[tracker/sam/sandbox-rewrite: 27/27] Simplify the tracker-sandbox tool and improve log output



commit e1616d2c38695cb0e045621a9cd9dfea419280a2
Author: Sam Thursfield <sam afuera me uk>
Date:   Tue Aug 6 12:10:02 2019 +0200

    Simplify the tracker-sandbox tool and improve log output
    
    The `tracker-sandbox` tool is now a thin wrapper around the `tracker`
    CLI. Effectively, this commit removes all functionality except the old
    '--shell' mode. This way we avoid duplicating code paths and we make
    sure that everyone is using and testing the `tracker` CLI.
    
    The sandbox tool now captures all log output from the Tracker daemons,
    instead of having them print directly to stdout. The old behaviour is
    now enabled with the `--verbosity=` argument.

 README.md                        |  37 +--
 utils/sandbox/tracker-sandbox.py | 632 ++++++++++++++-------------------------
 2 files changed, 248 insertions(+), 421 deletions(-)
---
diff --git a/README.md b/README.md
index 48c374e6f..f1ec0f855 100644
--- a/README.md
+++ b/README.md
@@ -98,34 +98,37 @@ in the top of the tracker source tree and type this to see the --help output:
     ./utils/sandbox/tracker-sandbox.py --help
 
 You should always pass the `--prefix` option, which should be the same as the
---prefix argument you passed to Meson. You also need to use `--index` which
-controls where internal state files like the database are kept. You may also
-want to pass `--debug` to see detailed log output.
+--prefix argument you passed to Meson. You may pass `--index` which to controls
+where Tracker's database is kept. You may also want to pass `--debug` to see
+detailed log output.
 
-Now you can index some files using `--update` mode. Here's how to index files
-in `~/Documents` for example:
+The remaining arguments you pass to `tracker-sandbox` are shell commands which
+get run inside the sandbox. Use the `--` sentinel to ensure that the
+commandline arguments are processed correctly. First, let's see the status of
+the Tracker daemons:
 
-    ./utils/sandbox/tracker-sandbox.py  --prefix ~/opt/tracker --index ~/tracker-content \
-        --update --content ~/Documents
+    ./utils/sandbox/tracker-sandbox.py  --prefix ~/opt/tracker -- tracker daemon status
 
-You can then list the files that have been indexed...
+Let's try and index some content...
 
-    ./utils/sandbox/tracker-sandbox.py  --prefix ~/opt/tracker --index ~/tracker-content \
-        --list-files
+    ./utils/sandbox/tracker-sandbox.py  --prefix ~/opt/tracker -- tracker index ~/Music
+
+... let's see what files were found ...
+
+    ./utils/sandbox/tracker-sandbox.py  --prefix ~/opt/tracker -- tracker sparql --list-files
 
 ... run a full-text search ...
 
-    ./utils/sandbox/tracker-sandbox.py  --prefix ~/opt/tracker --index ~/tracker-content \
-        --search "bananas"
+    ./utils/sandbox/tracker-sandbox.py  --prefix ~/opt/tracker -- tracker search "bananas"
 
 ... or run a SPARQL query on the content:
 
-    ./utils/sandbox/tracker-sandbox.py  --prefix ~/opt/tracker --index ~/tracker-content \
-        --sparql "SELECT ?url { ?resource a nfo:FileDataObject ; nie:url ?url . }"
+    ./utils/sandbox/tracker-sandbox.py  --prefix ~/opt/tracker -- tracker sparql
+        --query "SELECT ?url { ?resource a nfo:FileDataObject ; nie:url ?url . }"
 
-You can also open a shell inside the sandbox environment. From here you can run
-the `tracker` commandline tool, and you can run the Tracker daemons manually
-under a debugger such as GDB.
+If you run `tracker-sandbox` without a command argument, it will open an
+interactive shell inside the sandbox. From here you can use debugging tools
+such as GDB.
 
 For more information about developing Tracker, look at
 https://wiki.gnome.org/Projects/Tracker.
diff --git a/utils/sandbox/tracker-sandbox.py b/utils/sandbox/tracker-sandbox.py
index 06bc6afb0..cc8ebd786 100755
--- a/utils/sandbox/tracker-sandbox.py
+++ b/utils/sandbox/tracker-sandbox.py
@@ -2,33 +2,11 @@
 #
 # Copyright (C) 2012-2013 Martyn Russell <martyn lanedo com>
 # Copyright (C) 2012      Sam Thursfield <sam thursfield codethink co uk>
-# Copyright (C) 2016      Sam Thursfield <sam afuera me uk>
+# Copyright (C) 2016,2019 Sam Thursfield <sam afuera me uk>
 #
-# This script allows a user to utilise Tracker for local instances by
-# specifying an index directory location where the Tracker data is
-# stored and a content directory location where the content to be
-# indexed is kept. From there, queries or a shell can be launched to
-# use that data.
-#
-# This was initially a shell script by Sam and later converted into a
-# more comprehensive python script by Martyn.
-#
-# Usage:
-#  - Create or update an index stored in tracker/ subdir with content in html/
-#      tracker-sandbox.py -i tracker -c html -u
-#  - Query for 'foo'
-#      tracker-sandbox.py -i tracker -c html -q foo
-#  - List files in index
-#      tracker-sandbox.py -i tracker -c html -l
-#  - Start shell with environment set up
-#      tracker-sandbox.py -i tracker -c html -s
-#  - Test with different prefixes, e.g. /usr/local installs
-#      tracker-sandbox.py -i tracker -c html -s -p /usr/local
-#  ...
-#
-# Changes:
-#  - If you make _ANY_ changes, please send them in so I can incorporate them.
+# This is a tool for running development versions of Tracker.
 #
+# See README.md for usage information.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -45,34 +23,27 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 #
 
+import argparse
 import locale
+import logging
 import os
-import subprocess
-import optparse
+import shlex
 import signal
+import subprocess
 import sys
-import errno
+import threading
 
 import configparser
 
-import gi
-gi.require_version('Tracker', '2.0')
-from gi.repository import Tracker, GLib
+from gi.repository import GLib
 
 # Script
 script_name = 'tracker-sandbox'
-script_version = '0.1'
-script_about = 'Localised Tracker sandbox for content indexing and search'
-
-index_location_abs = ''
+script_version = '1.0'
+script_about = "Tracker Sandbox developer tool."
 
 default_prefix = '/usr'
-default_debug_verbosity = 2
-
-# Session
-dbus_session_pid = -1
-dbus_session_address = ''
-dbus_session_file = ''
+default_index_location = '/tmp/tracker-sandbox'
 
 store_pid = -1
 store_proc = None
@@ -108,212 +79,138 @@ removable-days-threshold=3
 enable-writeback=false
 """
 
-# Utilities
-
-
-def mkdir_p(path):
-    try:
-        os.makedirs(path)
-    except OSError as exc:
-        if exc.errno == errno.EEXIST:
-            pass
-        else:
-            raise
-
-
-def debug(message):
-    if opts.debug:
-        print(message)
-
-# DB functions (sync for now)
-
-
-def db_query_have_files():
-    # Set this here in case we used 'bus' for an update() before this.
-    # os.environ['TRACKER_SPARQL_BACKEND'] = 'direct'
-
-    print('Using query to check index has data in it...')
-
-    conn = Tracker.SparqlConnection.get(None)
-    cursor = conn.query(
-        'select count(?urn) where { ?urn a nfo:FileDataObject }', None)
-
-    # Only expect one result here...
-    while (cursor.next(None)):
-        print('  Currently %d file(s) exist in our index' %
-              (cursor.get_integer(0)))
 
+log = logging.getLogger('sandbox')
+dbuslog = logging.getLogger('dbus')
 
-def db_query_list_files():
-    # Set this here in case we used 'bus' for an update() before this.
-    # os.environ['TRACKER_SPARQL_BACKEND'] = 'direct'
 
-    print('Using query to list files indexed...')
+# Private DBus daemon
 
-    conn = Tracker.SparqlConnection.get(None)
-    cursor = conn.query(
-        'select nie:url(?urn) where { ?urn a nfo:FileDataObject }', None)
+class DBusDaemon:
+    """The private D-Bus instance that provides the sandbox's session bus.
 
-    # Only expect one result here...
-    while (cursor.next(None)):
-        print('  ' + cursor.get_string(0)[0])
+    We support reading and writing the session information to a file. This
+    means that if the user runs two sandbox instances on the same data
+    directory at the same time, they will share the same message bus.
+    """
 
+    def __init__(self, session_file=None):
+        self.session_file = session_file
+        self.existing_session = False
+        self.process = None
 
-def db_search(search_text):
-    conn = Tracker.SparqlConnection.get(None)
-    query = ('select nie:url(?urn) where { ?urn a nfo:FileDataObject . '
-             '?urn fts:match "%s" }')
-    cursor = conn.query(query % (search_text), None)
-
-    print('Found:')
-
-    while (cursor.next(None)):
-        print('  ' + cursor.get_string(0)[0])
-
-
-def db_sparql_query(sparql):
-    conn = Tracker.SparqlConnection.get(None)
-    cursor = conn.query(sparql)
-
-    print('Results:')
-
-    while (cursor.next(None)):
-        row = []
-        for column in range(0, cursor.get_n_columns()):
-            row.append(cursor.get_string(column)[0])
-        print('  ' + '\t'.join(row))
-
-
-# Index functions
-
-def index_clean():
-    # tracker reset --hard
-    debug('Cleaning index, FIXME: Does nothing.')
+        try:
+            self.address, self.pid = self.read_session_file(session_file)
+            self.existing_session = True
+        except FileNotFoundError:
+            log.debug("No existing D-Bus session file was found.")
 
+            self.address = None
+            self.pid = None
 
-def find_libexec_binaries(command):
-    binary = os.path.join(opts.prefix, 'libexec', command)
-    if not os.path.exists(binary):
-        binary = os.path.join(opts.prefix, 'libexec', command)
-        if not os.path.exists(binary):
+    def get_session_file(self):
+        """Returns the path to the session file if we created it, or None."""
+        if self.existing_session:
             return None
+        return self.session_file
 
-    return binary
-
-
-def index_update():
-    debug('Updating index ...')
-    debug('--')
-
-    # FIXME: Need to start tracker-extract to make sure extended
-    # metadata is created, but the problem is, after miner-fs
-    # stops, we return to the prompt, so how do we handle that?
-    #
-    # We need to listen to signals from tracker-extract and then
-    # quit after some inactivity I think ... OR listen to
-    # GraphUpdated and when there are no more objects without a
-    # data-source, we know all data was indexed.
-
-    # Start tracker-miner-fs
-    binary = find_libexec_binaries('tracker-miner-fs')
-    if binary is None:
-        print('Could not find "tracker-miner-fs" in $prefix/lib{exec} '
-              'directories', file=sys.stderr)
-        print('Is Tracker installed properly?', file=sys.stderr)
-        sys.exit(1)
-
-    try:
-        # Mine data WITHOUT being a daemon, exit when done. Ignore desktop
-        # files
-        subprocess.check_output([binary, "--no-daemon"]).decode('utf-8')
-    except subprocess.CalledProcessError as e:
-        print('Could not run %s, %s' % (binary, e.output))
-        sys.exit(1)
-
-    debug('--')
-
-    # We've now finished updating the index now OR we completely failed
-    print('Index now up to date!')
-
-    # Check we have data in our index...
-    db_query_have_files()
+    def get_address(self):
+        return self.address
 
-
-def index_shell():
-    print('Starting shell... (type "exit" to finish)')
-    print()
-
-    os.system("/bin/bash")
-
-# Environment / Clean up
-
-
-def dbus_session_get_from_content(content):
-    global dbus_session_address
-    global dbus_session_pid
-
-    if len(content) < 1:
-        print('Content was empty ... can not get DBus session information from'
-              ' empty string', file=sys.stderr)
-        return False
-
-    dbus_session_address = content.splitlines()[0]
-    dbus_session_pid = int(content.splitlines()[1])
-
-    err_msg = 'DBus session file was corrupt (%s), please remove "%s"'
-    if dbus_session_address == '':
-        print(err_msg % ("no address", dbus_session_file), file=sys.stderr)
-        sys.exit(1)
-    if dbus_session_pid < 0:
-        print(err_msg % ("no PID", dbus_session_file), file=sys.stderr)
-        sys.exit(1)
-
-    return True
-
-
-def dbus_session_file_get():
-    try:
-        with open(dbus_session_file, 'r') as f:
+    @staticmethod
+    def read_session_file(session_file):
+        with open(session_file, 'r') as f:
             content = f.read()
-            return dbus_session_get_from_content(content)
-    except FileNotFoundError as e:
-        # Expect this if we have a new session to set up
-        return False
-
-
-def dbus_session_file_set():
-    mkdir_p(os.environ['XDG_RUNTIME_DIR'])
 
-    content = '%s\n%s' % (dbus_session_address, dbus_session_pid)
-    with open(dbus_session_file, 'w') as f:
-        f.write(content)
+        try:
+            address = content.splitlines()[0]
+            pid = int(content.splitlines()[1])
+        except ValueError:
+            raise RuntimeError(f"D-Bus session file {session_file} is not valid. "
+                                "Remove this file to start a new session.")
+
+        return address, pid
+
+    @staticmethod
+    def write_session_file(session_file, address, pid):
+        os.makedirs(os.path.dirname(session_file), exist_ok=True)
+
+        content = '%s\n%s' % (address, pid)
+        with open(session_file, 'w') as f:
+            f.write(content)
+
+    def start_if_needed(self):
+        if self.existing_session:
+            log.debug('Using existing D-Bus session from file "%s" with address "%s"'
+                      ' with PID %d' % (self.session_file, self.address, self.pid))
+        else:
+            dbus_command = ['dbus-daemon', '--session', '--print-address=1', '--print-pid=1']
+            self.process = subprocess.Popen(dbus_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+            try:
+                self.address = self.process.stdout.readline().strip().decode('ascii')
+                self.pid = int(self.process.stdout.readline().strip().decode('ascii'))
+            except ValueError:
+                error = self.process.stderr.read().strip().decode('unicode-escape')
+                raise RuntimeError(f"Failed to start D-Bus daemon.\n{error}")
+
+            log.debug("Using new D-Bus session with address '%s' with PID %d",
+                      self.address, self.pid)
+
+            self.write_session_file(self.session_file, self.address, self.pid)
+            log.debug("Wrote D-Bus session file at %s", self.session_file)
+
+            # We must read from the pipes continuously, otherwise the daemon
+            # process will block.
+            self._threads=[threading.Thread(target=self.pipe_to_log, args=(self.process.stdout, 'stdout'), 
daemon=True),
+                           threading.Thread(target=self.pipe_to_log, args=(self.process.stderr, 'stderr'), 
daemon=True)]
+            self._threads[0].start()
+            self._threads[1].start()
+
+    def stop(self):
+        if self.process:
+            log.debug("  Stopping DBus daemon")
+            self.process.terminate()
+            self.process.wait()
+
+    def pipe_to_log(self, pipe, source):
+        """This function processes the output from our dbus-daemon instance."""
+        while True:
+            line_raw = pipe.readline()
+
+            if len(line_raw) == 0:
+                break
+
+            line = line_raw.decode('utf-8').rstrip()
+
+            if line.startswith('(tracker-'):
+                # We set G_MESSAGES_PREFIXED=all, meaning that all log messages
+                # output by Tracker processes have a prefix. Note that
+                # g_print() will NOT be captured here.
+                dbuslog.info(line)
+            else:
+                # Log messages from other daemons, including the dbus-daemon
+                # itself, go here. Any g_print() messages also end up here.
+                dbuslog.debug(line)
 
 
-def environment_unset():
-    debug('Cleaning up files ...')
+# Environment / Clean up
 
-    if not dbus_session_file == '':
-        debug('  Removing DBus session file')
-        os.unlink(dbus_session_file)
+def environment_unset(dbus):
+    log.debug('Cleaning up files ...')
 
-    debug('Cleaning up processes ...')
+    if dbus.get_session_file():
+        log.debug('  Removing DBus session file')
+        os.unlink(dbus.get_session_file())
 
-    if dbus_session_pid > 0:
-        debug('  Killing DBus session')
-        try:
-            os.kill(dbus_session_pid, signal.SIGTERM)
-        # (3, 'No such process') old python-schedutils incorrectly
-        # raised SystemError
-        except (SystemError, OSError):
-            debug('    Process %d not found', dbus_session_pid)
+    log.debug('Cleaning up processes ...')
 
-    if not opts.update:
-        return
+    dbus.stop()
 
     # FIXME: clean up tracker-store, can't use 'tracker daemon ...' for this,
     #        that kills everything it finds in /proc sadly.
     if store_pid > 0:
-        debug('  Killing Tracker store')
+        log.debug('  Killing Tracker store')
         os.kill(store_pid, signal.SIGTERM)
 
 
@@ -329,27 +226,22 @@ def environment_set_and_add_path(env, prefix, suffix):
     os.environ[env] = full
 
 
-def environment_set():
+def environment_set(index_location, prefix, verbosity=0):
     # Environment
-    global dbus_session_address
-    global dbus_session_pid
-    global dbus_session_file
-    global index_location_abs
-    global default_debug_verbosity
-
-    index_location_abs = os.path.abspath(opts.index_location)
+    index_location = os.path.abspath(index_location)
+    prefix = os.path.abspath(os.path.expanduser(prefix))
 
     # Data
-    os.environ['XDG_DATA_HOME'] = '%s/data/' % index_location_abs
-    os.environ['XDG_CONFIG_HOME'] = '%s/config/' % index_location_abs
-    os.environ['XDG_CACHE_HOME'] = '%s/cache/' % index_location_abs
-    os.environ['XDG_RUNTIME_DIR'] = '%s/run/' % index_location_abs
+    os.environ['XDG_DATA_HOME'] = '%s/data/' % index_location
+    os.environ['XDG_CONFIG_HOME'] = '%s/config/' % index_location
+    os.environ['XDG_CACHE_HOME'] = '%s/cache/' % index_location
+    os.environ['XDG_RUNTIME_DIR'] = '%s/run/' % index_location
 
     # Prefix - only set if non-standard
-    if opts.prefix != default_prefix:
-        environment_set_and_add_path('PATH', opts.prefix, 'bin')
-        environment_set_and_add_path('LD_LIBRARY_PATH', opts.prefix, 'lib')
-        environment_set_and_add_path('XDG_DATA_DIRS', opts.prefix, 'share')
+    if prefix != default_prefix:
+        environment_set_and_add_path('PATH', prefix, 'bin')
+        environment_set_and_add_path('LD_LIBRARY_PATH', prefix, 'lib')
+        environment_set_and_add_path('XDG_DATA_DIRS', prefix, 'share')
 
     # Preferences
     os.environ['TRACKER_USE_CONFIG_FILES'] = 'yes'
@@ -357,69 +249,57 @@ def environment_set():
     # if opts.debug:
     #     os.environ['TRACKER_USE_LOG_FILES'] = 'yes'
 
-    if opts.debug:
-        os.environ['G_MESSAGES_DEBUG'] = 'all'
-        os.environ['TRACKER_VERBOSITY'] = '%d' % default_debug_verbosity
-        os.environ['DBUS_VERBOSE'] = '1'
-    else:
-        os.environ['TRACKER_VERBOSITY'] = '0'
+    os.environ['G_MESSAGES_PREFIXED'] = 'all'
+    os.environ['TRACKER_VERBOSITY'] = str(verbosity)
 
-    debug('Using prefix location "%s"' % opts.prefix)
-    debug('Using index location "%s"' % index_location_abs)
+    log.debug('Using prefix location "%s"' % prefix)
+    log.debug('Using index location "%s"' % index_location)
 
     # Ensure directory exists
     # DBus specific instance
     dbus_session_file = os.path.join(
         os.environ['XDG_RUNTIME_DIR'], 'dbus-session')
 
-    if dbus_session_file_get() is False:
-        output = subprocess.check_output(["dbus-daemon",
-                                          "--session",
-                                          "--print-address=1",
-                                          "--print-pid=1",
-                                          "--fork"]).decode('utf-8')
-
-        dbus_session_get_from_content(output)
-        dbus_session_file_set()
-        debug('Using new D-Bus session with address "%s" with PID %d' %
-              (dbus_session_address, dbus_session_pid))
-    else:
-        debug('Using existing D-Bus session from file "%s" with address "%s"'
-              ' with PID %d' %
-              (dbus_session_file, dbus_session_address, dbus_session_pid))
+    dbus = DBusDaemon(dbus_session_file)
+    dbus.start_if_needed()
 
     # Important, other subprocesses must use our new bus
-    os.environ['DBUS_SESSION_BUS_ADDRESS'] = dbus_session_address
+    os.environ['DBUS_SESSION_BUS_ADDRESS'] = dbus.get_address()
+
+    # So tests can detect if they are run under sandbox or not.
+    os.environ['TRACKER_SANDBOX'] = '1'
+
+    return dbus
 
 
-def config_set():
+def config_set(content_locations_recursive=None, content_locations_single=None):
     # Make sure File System miner is configured correctly
     config_dir = os.path.join(os.environ['XDG_CONFIG_HOME'], 'tracker')
     config_filename = os.path.join(config_dir, 'tracker-miner-fs.cfg')
 
-    debug('Using config file "%s"' % config_filename)
+    log.debug('Using config file "%s"' % config_filename)
 
     # Only update config if we're updating the database
-    mkdir_p(config_dir)
+    os.makedirs(config_dir, exist_ok=True)
 
     if not os.path.exists(config_filename):
         f = open(config_filename, 'w')
         f.write(config_template)
         f.close()
 
-        debug('  Miner config file written')
+        log.debug('  Miner config file written')
 
     # Set content path
     config = configparser.ConfigParser()
     config.optionxform = str
     config.read(config_filename)
 
-    if opts.content_locations_recursive:
-        debug("Using content locations: %s" %
-              opts.content_locations_recursive)
-    if opts.content_locations_single:
-        debug("Using non-recursive content locations: %s" %
-              opts.content_locations_single)
+    if content_locations_recursive:
+        log.debug("Using content locations: %s" %
+              content_locations_recursive)
+    if content_locations_single:
+        log.debug("Using non-recursive content locations: %s" %
+              content_locations_single)
 
     def locations_gsetting(locations):
         locations = [dir if dir.startswith('&') else os.path.abspath(dir)
@@ -430,9 +310,9 @@ def config_set():
         config.add_section('General')
 
     config.set('General', 'index-recursive-directories',
-               locations_gsetting(opts.content_locations_recursive or []))
+               locations_gsetting(content_locations_recursive or []))
     config.set('General', 'index-single-directories',
-               locations_gsetting(opts.content_locations_single or []))
+               locations_gsetting(content_locations_single or []))
 
     with open(config_filename, 'w') as f:
         config.write(f)
@@ -452,150 +332,94 @@ def link_to_mime_data():
         new_mime_dir = os.path.join(new_xdg_data_home, 'mime')
         if (not os.path.exists(new_mime_dir)
                 and not os.path.islink(new_mime_dir)):
-            mkdir_p(new_xdg_data_home)
+            os.makedirs(new_xdg_data_home, exist_ok=True)
             os.symlink(
                 os.path.join(original_xdg_data_home, 'mime'), new_mime_dir)
 
 
+def argument_parser():
+    parser = argparse.ArgumentParser(description=script_about)
+    parser.add_argument('--version', action='store_true',
+                        help="show version information")
+    parser.add_argument('--debug-dbus', action='store_true',
+                        help="show stdout and stderr messages from every daemon "
+                             "running on the sandbox session bus. By default we "
+                             "only show messages logged by Tracker daemons.")
+    parser.add_argument('--debug-sandbox', action='store_true',
+                        help="show debugging info from tracker-sandbox")
+    parser.add_argument('-v', '--verbosity', default='0',
+                        choices=['0', '1', '2', '3', 'errors', 'minimal', 'detailed', 'debug'],
+                        help="show debugging info from Tracker processes")
+    parser.add_argument('-p', '--prefix', metavar='DIR', type=str, default=default_prefix,
+                        help=f"run Tracker from the given install prefix (default={default_prefix})")
+    parser.add_argument('-i', '--index', metavar='DIR', type=str,
+                        default=default_index_location, dest='index_location',
+                        help=f"directory to the index (default={default_index_location})")
+    parser.add_argument('command', type=str, nargs='*', help="Command to run inside the shell")
+
+    return parser
+
+
+def verbosity_as_int(verbosity):
+    verbosity_map = {
+        'errors': 0,
+        'minimal': 1,
+        'detailed': 2,
+        'debug': 3
+    }
+    return verbosity_map.get(verbosity, int(args.verbosity))
+
+
+def init_logging(debug_sandbox, debug_dbus):
+    SANDBOX_FORMAT = "sandbox: %(message)s"
+    DBUS_FORMAT = "|%(message)s"
+
+    if debug_sandbox:
+        sandbox_log_handler = logging.StreamHandler()
+        sandbox_log_handler.setFormatter(logging.Formatter(SANDBOX_FORMAT))
+        log.setLevel(logging.DEBUG)
+        log.addHandler(sandbox_log_handler)
+
+    dbus_log_handler = logging.StreamHandler()
+    dbus_log_handler.setFormatter(logging.Formatter(DBUS_FORMAT))
+    if debug_dbus:
+        dbuslog.setLevel(logging.DEBUG)
+    else:
+        dbuslog.setLevel(logging.INFO)
+    dbuslog.addHandler(dbus_log_handler)
+
+
 # Entry point/start
 if __name__ == "__main__":
     locale.setlocale(locale.LC_ALL, '')
 
-    # Parse command line
-    usage_oneline = '%s -i <DIR> -c <DIR> [OPTION...]' % (
-        os.path.basename(sys.argv[0]))
-    usage = '\n  %s - %s' % (usage_oneline, script_about)
-    usage_invalid = 'Usage:\n  %s' % (usage_oneline)
-
-    popt = optparse.OptionParser(usage)
-    popt.add_option('-v', '--version',
-                    action='count',
-                    dest='version',
-                    help='show version information')
-    popt.add_option('-d', '--debug',
-                    action='count',
-                    dest='debug',
-                    help='show additional debugging')
-    popt.add_option('-p', '--prefix',
-                    action='store',
-                    metavar='PATH',
-                    dest='prefix',
-                    default=default_prefix,
-                    help='use a non-standard prefix (default="%s")' %
-                         (default_prefix))
-    popt.add_option('-i', '--index',
-                    action='store',
-                    metavar='DIR',
-                    dest='index_location',
-                    help='directory storing the index')
-    popt.add_option('-c', '--content',
-                    action='append',
-                    metavar='DIR',
-                    dest='content_locations_recursive',
-                    help='directory storing the content which is indexed (can '
-                         'be specified multiple times)')
-    popt.add_option('-C', '--content-non-recursive',
-                    action='append',
-                    metavar='DIR',
-                    dest='content_locations_single',
-                    help='directory storing the content which is indexed, '
-                    'non-recursive variant (can be specified multiple times)')
-    popt.add_option('-u', '--update',
-                    action='count',
-                    dest='update',
-                    help='update index/database from content')
-    popt.add_option('-l', '--list-files',
-                    action='count',
-                    dest='list_files',
-                    help='list files indexed')
-    popt.add_option('-s', '--shell',
-                    action='count',
-                    dest='shell',
-                    help='start a shell with the environment set up')
-    popt.add_option('--search',
-                    action='store',
-                    metavar='CRITERIA',
-                    dest='search',
-                    help='what content to look for in files')
-    popt.add_option('-q', '--sparql',
-                    action='store',
-                    metavar='CRITERIA',
-                    dest='sparql_query',
-                    help='SPARQL query to execute')
-
-    (opts, args) = popt.parse_args()
-
-    if opts.version:
-        print('%s %s\n%s\n' % (script_name, script_version, script_about))
+    args = argument_parser().parse_args()
+
+    if args.version:
+        print(f"{script_name} {script_version}\n{script_about}\n")
         sys.exit(0)
 
-    if not opts.index_location:
-        if not opts.content_locations_recursive and not \
-                opts.content_locations_single:
-            print('Expected index (-i) or content (-c) locations to be '
-                  'specified', file=sys.stderr)
-            print(usage_invalid)
-            sys.exit(1)
-
-    if opts.update:
-        if not opts.index_location or not (opts.content_locations_recursive or
-                                           opts.content_locations_single):
-            print('Expected index (-i) and content (-c) locations to be '
-                  'specified', file=sys.stderr)
-            print('These arguments are required to update the index databases',
-                  file=sys.stderr)
-            sys.exit(1)
-
-    if ((opts.sparql_query or opts.search or opts.list_files or opts.shell)
-            and not opts.index_location):
-        print('Expected index location (-i) to be specified', file=sys.stderr)
-        print('This arguments is required to use the content that has been '
-              'indexed', file=sys.stderr)
-        sys.exit(1)
-
-    if (not opts.update
-            and not opts.sparql_query
-            and not opts.search
-            and not opts.list_files
-            and not opts.shell):
-        print('No action specified (e.g. update (-u), shell (-s), '
-              'list files (-l), etc)\n', file=sys.stderr)
-        print('%s %s\n%s\n' % (script_name, script_version, script_about),
-              file=sys.stderr)
-        print(usage_invalid, file=sys.stderr)
-        sys.exit(1)
+    init_logging(args.debug_sandbox, args.debug_dbus)
+
+    shell = os.environ.get('SHELL', '/bin/bash')
+
+    verbosity = verbosity_as_int(args.verbosity)
 
     # Set up environment variables and foo needed to get started.
-    environment_set()
+    dbus = environment_set(args.index_location, args.prefix, verbosity)
     config_set()
 
     link_to_mime_data()
 
     try:
-        if opts.update:
-            index_update()
-
-        if opts.list_files:
-            db_query_list_files()
-
-        if opts.shell:
-            index_shell()
-            sys.exit(0)
-
-        if opts.search or opts.sparql_query:
-            if not os.path.exists(index_location_abs):
-                print('Can not query yet, index has not been created, see '
-                      '--update or -u', file=sys.stderr)
-                print(usage_invalid, file=sys.stderr)
-                sys.exit(1)
-
-        if opts.search:
-            db_search(opts.search)
-
-        if opts.sparql_query:
-            db_sparql_query(opts.sparql_query)
-
-    except KeyboardInterrupt:
-        print('Handling Ctrl+C')
+        if args.command:
+            command = [shell, '-c', ' '.join(shlex.quote(c) for c in args.command)]
+            log.debug("Running: %s", command)
+            subprocess.run(command)
+        else:
+            print('Starting shell... (type "exit" to finish)')
+            print()
 
-    environment_unset()
+            os.system(shell)
+    finally:
+        environment_unset(dbus)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]