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



commit bfcea20f7231a22e62ec4eed935738ebd5bc4e75
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. This way we avoid duplicating code paths and we make sure that
    everyone is using and testing the `tracker` CLI.
    
    Effectively, this commit removes all functionality except the old
    '--shell' mode.
    
    Log handling is much improved. The tool now captures all output from
    the Tracker daemons that run inside the sandbox and can be told to
    write it to stdout using the `--verbosity` argument.

 README.md                        |  37 +--
 utils/sandbox/tracker-sandbox.py | 617 ++++++++++++++-------------------------
 2 files changed, 241 insertions(+), 413 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..d178c1ec8 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,29 @@
 # 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'
+script_version = '1.0'
+script_about = "Tracker Sandbox developer tool."
 
 index_location_abs = ''
 
 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 +81,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!')
+    def get_address(self):
+        return self.address
 
-    # Check we have data in our index...
-    db_query_have_files()
-
-
-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,15 +228,12 @@ 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_abs = os.path.abspath(index_location)
+    prefix = os.path.abspath(os.path.expanduser(prefix))
 
     # Data
     os.environ['XDG_DATA_HOME'] = '%s/data/' % index_location_abs
@@ -346,10 +242,10 @@ def environment_set():
     os.environ['XDG_RUNTIME_DIR'] = '%s/run/' % index_location_abs
 
     # 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 +253,54 @@ 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_abs)
 
     # 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()
+
+    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 +311,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 +333,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]