From e68268abe2a3e7757f330ad19c5d2b03d8887f57 Mon Sep 17 00:00:00 2001 From: Fabio Erculiani Date: Mon, 9 Apr 2012 15:38:31 +0200 Subject: [PATCH] [RigoDaemon/Rigo] Add support for .etp and .tbz2 package files installation --- rigo/RigoDaemon/app.py | 141 ++++++++++++++++-- rigo/RigoDaemon/dbus/org.sabayon.Rigo.xml | 1 + rigo/rigo/controllers/daemon.py | 94 +++++++++++- rigo/rigo/models/application.py | 21 ++- rigo/rigo/ui/gtk3/controllers/applications.py | 136 +++++++++++------ rigo/rigo_app.py | 62 +++++++- 6 files changed, 388 insertions(+), 67 deletions(-) diff --git a/rigo/RigoDaemon/app.py b/rigo/RigoDaemon/app.py index 89ad99b26..c3e36e917 100755 --- a/rigo/RigoDaemon/app.py +++ b/rigo/RigoDaemon/app.py @@ -59,7 +59,8 @@ EntropyCacher.WRITEBACK_TIMEOUT = 120 from entropy.const import etpConst, const_convert_to_rawstring, \ initconfig_entropy_constants, const_debug_write from entropy.exceptions import DependenciesNotFound, \ - DependenciesCollision, DependenciesNotRemovable, SystemDatabaseError + DependenciesCollision, DependenciesNotRemovable, SystemDatabaseError, \ + EntropyPackageException from entropy.i18n import _ from entropy.misc import LogFile, ParallelTask, TimeScheduled, \ ReadersWritersSemaphore, DirectoryMonitor @@ -367,7 +368,7 @@ class RigoDaemonService(dbus.service.Object): BUS_NAME = DbusConfig.BUS_NAME OBJECT_PATH = DbusConfig.OBJECT_PATH - API_VERSION = 0 + API_VERSION = 1 """ RigoDaemon is the dbus service Object in charge of executing @@ -380,10 +381,11 @@ class RigoDaemonService(dbus.service.Object): class ActionQueueItem(object): - def __init__(self, package_id, repository_id, action, - simulate, authorized): + def __init__(self, package_id, repository_id, package_path, + action, simulate, authorized): self._package_id = package_id self._repository_id = repository_id + self._path = package_path self._action = action self._simulate = simulate self._authorized = authorized @@ -401,6 +403,12 @@ class RigoDaemonService(dbus.service.Object): """ return self._repository_id + def path(self): + """ + Return Application Package file path, if any. + """ + return self._path + def action(self): """ Return AppActions Action for this ActionQueueItem. @@ -637,6 +645,12 @@ class RigoDaemonService(dbus.service.Object): 'org.freedesktop.DBus').GetConnectionUnixUser( sender) + def _dbus_to_unicode(self, dbus_string): + """ + Convert dbus.String() to unicode object + """ + return dbus_string.decode(etpConst['conf_encoding']) + def _authorize(self, pid, action_id): """ Authorize privileged Activity. @@ -770,6 +784,66 @@ class RigoDaemonService(dbus.service.Object): GLib.idle_add( self.repositories_updated, result, msg) + def _maybe_setup_package_repository(self, app_item): + """ + Setup the Entropy Repository for the Application in + the ActionQueueItem if path() is valid. + """ + path = app_item.path() + if app_item.action() == AppActions.INSTALL \ + and path is not None: + write_output("_maybe_setup_package_repository: " + "this is a package file, generating " + "repo", debug=True) + + self._rwsem.writer_acquire() + try: + pkg_matches = self._entropy.add_package_repository( + path) + return pkg_matches, True + except EntropyPackageException as err: + write_output("_maybe_setup_package_repository: " + "invalid package file, " + "%s" % (err,), debug=True) + return None, False + except Exception as err: + write_output("_maybe_setup_package_repository: " + "error during repository setup (err), " + "%s" % (repr(err),), debug=True) + return None, False + finally: + self._rwsem.writer_release() + + return None, True + + def _maybe_dismantle_package_repository(self, pkg_matches): + """ + Dismantle the Entropy Repository previously configured + by _maybe_setup_package_repository() + """ + repository_ids = set((x[1] for x in pkg_matches)) + write_output( + "_maybe_dismantle_package_repository: " + "about to acquire rwsem for %s" % (pkg_matches,), + debug=True) + self._rwsem.writer_acquire() + try: + write_output( + "_maybe_dismantle_package_repository: " + "acquired rwsem for %s" % (pkg_matches,), + debug=True) + for repository_id in repository_ids: + try: + self._entropy.remove_repository(repository_id) + except Exception as err: + write_output( + "_maybe_dismantle_package_repository: " + "error during repository removal (err), " + "%s" % (repr(err),), debug=True) + finally: + self._rwsem.writer_release() + + def _action_queue_worker_thread(self): """ Worker thread that handles Application Action requests @@ -873,6 +947,15 @@ class RigoDaemonService(dbus.service.Object): "doing %s" % ( item,), debug=True) + # Package file installation support + generated = True + if is_app: + pkg_matches, generated = \ + self._maybe_setup_package_repository(item) + if not generated: + outcome = AppTransactionOutcome.INSTALL_ERROR + return + self._rwsem.reader_acquire() try: outcome = self._process_action(item, activity, is_app) @@ -881,6 +964,8 @@ class RigoDaemonService(dbus.service.Object): outcome,), debug=True) finally: self._rwsem.reader_release() + if is_app and pkg_matches: + self._maybe_dismantle_package_repository(pkg_matches) finally: _action_queue_finally(activity, outcome) @@ -892,10 +977,12 @@ class RigoDaemonService(dbus.service.Object): if is_app: package_id, repository_id = item.pkg action = item.action() + path = item.path() else: # upgrade package_id, repository_id = None, None action = None + path = None simulate = item.simulate() self._txs.reset() @@ -905,13 +992,17 @@ class RigoDaemonService(dbus.service.Object): if is_app: if action == AppActions.REMOVE: + if path is not None: + # error, cannot remove an app from + # package path + return outcome outcome = self._process_remove_action( activity, action, simulate, package_id, repository_id) elif action == AppActions.INSTALL: outcome = self._process_install_action( activity, action, simulate, - package_id, repository_id) + package_id, repository_id, path) else: # upgrade outcome = self._process_upgrade_action( @@ -1192,7 +1283,7 @@ class RigoDaemonService(dbus.service.Object): self._entropy.installed_repository().commit() def _process_install_action(self, activity, action, simulate, - package_id, repository_id): + package_id, repository_id, path): """ Process Application Install Action. """ @@ -1760,10 +1851,11 @@ class RigoDaemonService(dbus.service.Object): task.start() return True - @dbus.service.method(BUS_NAME, in_signature='issb', + @dbus.service.method(BUS_NAME, in_signature='isssb', out_signature='b', sender_keyword='sender') def enqueue_application_action(self, package_id, repository_id, - action, simulate, sender=None): + package_path, action, + simulate, sender=None): """ Request RigoDaemon to enqueue a new Application Action, if possible. @@ -1771,7 +1863,8 @@ class RigoDaemonService(dbus.service.Object): pid = self._get_caller_pid(sender) write_output("enqueue_application_action called: " "%s, %s, client pid: %s" % ( - (package_id, repository_id), action, pid), debug=True) + (package_id, repository_id, package_path), + action, pid), debug=True) self._enqueue_action_busy_hold_sem.acquire() try: @@ -1789,15 +1882,27 @@ class RigoDaemonService(dbus.service.Object): "already busy, just enqueue", debug=True) - def _enqueue(): + def _enqueue(package_id, repository_id, package_path, + action, simulate): + + if not package_path: + package_path = None + else: + self._dbus_to_unicode(package_path) + repository_id = self._dbus_to_unicode(repository_id) + package_id = int(package_id) + simulate = bool(simulate) + action = self._dbus_to_unicode(action) + try: authorized = self._authorize( pid, PolicyActions.MANAGE_APPLICATIONS) item = self.ActionQueueItem( - int(package_id), - str(repository_id), + package_id, + repository_id, + package_path, action, - bool(simulate), + simulate, authorized) self._action_queue.append(item) if authorized: @@ -1810,7 +1915,9 @@ class RigoDaemonService(dbus.service.Object): finally: self._enqueue_action_busy_hold_sem.release() - task = ParallelTask(_enqueue) + task = ParallelTask(_enqueue, package_id, + repository_id, package_path, + action, simulate) task.name = "EnqueueApplicationActionThread" task.daemon = True task.start() @@ -2166,7 +2273,7 @@ class RigoDaemonService(dbus.service.Object): Load a new ConfigurationFiles object. """ write_output("reload_configuration_updates called", debug=True) - def _reload(): + def _reload(pid): authorized = self._authorize( pid, PolicyActions.MANAGE_CONFIGURATION) if not authorized: @@ -2181,7 +2288,9 @@ class RigoDaemonService(dbus.service.Object): finally: self._rwsem.reader_release() - task = ParallelTask(_reload) + task = ParallelTask( + _reload, + self._get_caller_pid(sender)) task.name = "ReloadConfigurationUpdates" task.daemon = True task.start() diff --git a/rigo/RigoDaemon/dbus/org.sabayon.Rigo.xml b/rigo/RigoDaemon/dbus/org.sabayon.Rigo.xml index b77c02a0e..cc6259672 100644 --- a/rigo/RigoDaemon/dbus/org.sabayon.Rigo.xml +++ b/rigo/RigoDaemon/dbus/org.sabayon.Rigo.xml @@ -15,6 +15,7 @@ + diff --git a/rigo/rigo/controllers/daemon.py b/rigo/rigo/controllers/daemon.py index 3273d175b..cb5564d7a 100644 --- a/rigo/rigo/controllers/daemon.py +++ b/rigo/rigo/controllers/daemon.py @@ -21,6 +21,7 @@ this program; if not, write to the Free Software Foundation, Inc., import time from threading import Lock, Semaphore, current_thread +from collections import deque import dbus @@ -46,6 +47,7 @@ from RigoDaemon.config import DbusConfig as DaemonDbusConfig from entropy.const import const_debug_write, \ const_debug_enabled, etpConst from entropy.misc import ParallelTask +from entropy.exceptions import EntropyPackageException from entropy.i18n import _, ngettext from entropy.output import darkgreen, brown, darkred, red, blue @@ -62,6 +64,7 @@ class RigoServiceController(GObject.Object): NOTIFICATION_CONTEXT_ID = "RigoServiceControllerContextId" ANOTHER_ACTIVITY_CONTEXT_ID = "AnotherActivityNotificationContextId" SYSTEM_UPGRADE_CONTEXT_ID = "SystemUpgradeContextId" + PKG_INSTALL_CONTEXT_ID = "PackageInstallContextId" class ServiceNotificationBox(NotificationBox): @@ -169,7 +172,7 @@ class RigoServiceController(GObject.Object): _UNSUPPORTED_APPLICATIONS_SIGNAL = "unsupported_applications" _RESTARTING_UPGRADE_SIGNAL = "restarting_system_upgrade" _CONFIGURATION_UPDATES_SIGNAL = "configuration_updates_available" - _SUPPORTED_APIS = [0] + _SUPPORTED_APIS = [1] def __init__(self, rigo_app, activity_rwsem, entropy_client, entropy_ws): @@ -192,6 +195,8 @@ class RigoServiceController(GObject.Object): self._registered_signals = {} self._registered_signals_mutex = Lock() + self._package_repositories = deque() + self._local_transactions = {} self._local_activity = LocalActivityStates.READY self._local_activity_mutex = Lock() @@ -702,6 +707,7 @@ class RigoServiceController(GObject.Object): # This way repository in-RAM caches are reset # otherwise installed repository metadata becomes # inconsistent + self._release_local_package_repositories() self._release_local_resources(clear_avc=False) # reset progress bar, we're done with it @@ -1211,6 +1217,17 @@ class RigoServiceController(GObject.Object): task.daemon = True task.start() + def package_install_request(self, package_path, simulate=False): + """ + Start Application Install Action for package file. + """ + task = ParallelTask(self._package_install_request, + package_path, simulate=simulate) + task.name = "PackageInstallRequest{%s, %s}" % ( + package_path, simulate,) + task.daemon = True + task.start() + def upgrade_system(self, simulate=False): """ Start a System Upgrade. @@ -1450,6 +1467,22 @@ class RigoServiceController(GObject.Object): raise sem_data['exc'] return sem_data['res'] + def _release_local_package_repositories(self): + """ + Release all the local package file repositories. + """ + self._entropy.rwsem().writer_acquire() + try: + while True: + try: + repository_id = self._package_repositories.popleft() + except IndexError: + break + self._entropy.remove_repository( + repository_id) + finally: + self._entropy.rwsem().writer_release() + def _release_local_resources(self, clear_avc=True): """ Release all the local resources (like repositories) @@ -1936,8 +1969,12 @@ class RigoServiceController(GObject.Object): """ if app is not None: package_id, repository_id = app.get_details().pkg + package_path = app.path else: package_id, repository_id = None, None + package_path = None + if package_path is None: + package_path = "" sig_match = None if busied: @@ -1999,8 +2036,9 @@ class RigoServiceController(GObject.Object): self._entropy_bus, dbus_interface=self.DBUS_INTERFACE ).enqueue_application_action( - package_id, repository_id, daemon_action, - simulate) + package_id, repository_id, + package_path, daemon_action, + simulate) def _enqueue_callback(): return self._execute_mainloop(_enqueue) @@ -2063,6 +2101,56 @@ class RigoServiceController(GObject.Object): DaemonAppTransactionOutcome.SUCCESS, local_activity) + def _package_install_request(self, package_path, simulate=False): + """ + Forward Application Package Install request to RigoDaemon. + """ + self._entropy.rwsem().writer_acquire() + try: + package_matches = self._entropy.add_package_repository( + package_path) + except EntropyPackageException as exc: + def _notify(): + msg = _("Package Install Error: %s") % (exc,) + box = self.ServiceNotificationBox( + prepare_markup(msg), + Gtk.MessageType.ERROR, + context_id=self.PKG_INSTALL_CONTEXT_ID) + box.add_destroy_button(_("Okay")) + self._nc.append(box) + GLib.idle_add(_notify) + return + finally: + self._entropy.rwsem().writer_release() + + accepted = False + accepted_count = 0 + repository_ids = set() + for package_match in package_matches: + pkg_id, pkg_repo = package_match + repository_ids.add(pkg_repo) + app = Application(self._entropy, self._entropy_ws, + package_match, + package_path=package_path) + accepted = self._application_request( + app, AppActions.INSTALL, simulate=simulate) + if not accepted: + break + accepted_count += 1 + + if accepted_count == 0: + self._entropy.rwsem().writer_acquire() + try: + for repository_id in repository_ids: + self._entropy.remove_repository( + repository_id) + finally: + self._entropy.rwsem().writer_release() + else: + for repository_id in repository_ids: + self._package_repositories.append( + repository_id) + def _application_request(self, app, app_action, simulate=False, master=True): """ diff --git a/rigo/rigo/models/application.py b/rigo/rigo/models/application.py index b5c50dab6..61d6d82dd 100644 --- a/rigo/rigo/models/application.py +++ b/rigo/rigo/models/application.py @@ -821,11 +821,12 @@ class Application(object): return self._licenses def __init__(self, entropy_client, entropy_ws, package_match, - redraw_callback=None): + redraw_callback=None, package_path=None): self._entropy = entropy_client self._entropy_ws = entropy_ws self._pkg_match = package_match self._pkg_id, self._repo_id = package_match + self._path = package_path self._redraw_callback = redraw_callback @property @@ -843,6 +844,14 @@ class Application(object): finally: self._entropy.rwsem().reader_release() + @property + def path(self): + """ + If this Application comes from a single package file, + return the package path. + """ + return self._path + def is_installed(self): """ Return if Application is currently installed. @@ -1102,9 +1111,15 @@ class Application(object): return app_install, app_remove - except DependenciesNotFound: + except DependenciesNotFound as err: + const_debug_write( + __name__, + "get_install_queue: DependenciesNotFound: %s" % (err,)) return None - except DependenciesCollision: + except DependenciesCollision as err: + const_debug_write( + __name__, + "get_install_queue: DependenciesCollision: %s" % (err,)) return None finally: diff --git a/rigo/rigo/ui/gtk3/controllers/applications.py b/rigo/rigo/ui/gtk3/controllers/applications.py index a062b1b32..51b427415 100644 --- a/rigo/rigo/ui/gtk3/controllers/applications.py +++ b/rigo/rigo/ui/gtk3/controllers/applications.py @@ -30,7 +30,8 @@ from rigo.paths import CONF_DIR from rigo.enums import RigoViewStates, AppActions from rigo.models.application import Application, ApplicationMetadata from rigo.utils import escape_markup, prepare_markup -from rigo.ui.gtk3.widgets.notifications import OrphanedAppsNotificationBox +from rigo.ui.gtk3.widgets.notifications import \ + NotificationBox from entropy.cache import EntropyCacher from entropy.const import etpConst, const_debug_write, \ @@ -92,6 +93,7 @@ class ApplicationsViewController(GObject.Object): self._nf_box = nf_box self._not_found_search_box = None self._not_found_label = None + self._nc = None self._cacher = EntropyCacher() self._search_thread_mutex = Lock() @@ -101,6 +103,12 @@ class ApplicationsViewController(GObject.Object): self._search_writeback_mutex = Lock() self._search_writeback_thread = None + def set_notification_controller(self, nc): + """ + Bind a NotificationViewController to this object. + """ + self._nc = nc + def _search_icon_release(self, search_entry, icon_pos, _other): """ Event associated to the Search bar icon click. @@ -178,15 +186,17 @@ class ApplicationsViewController(GObject.Object): finally: self._entropy.rwsem().reader_release() - def __simulate_app_install(self, text): - + def install(self, dependency, simulate=False): + """ + Try to match dependency to an Application and then install + it, if possible. + """ const_debug_write( __name__, - "__simulate_app_install: " - "%s" % (text,)) + "install: %s" % (dependency,)) self._entropy.rwsem().reader_acquire() try: - pkg_match = self._entropy.atom_match(text) + pkg_match = self._entropy.atom_match(dependency) finally: self._entropy.rwsem().reader_release() @@ -194,60 +204,102 @@ class ApplicationsViewController(GObject.Object): if pkg_id == -1: const_debug_write( __name__, - "__simulate_app_install: " - "no match for: %s" % (text,)) + "install: " + "no match for: %s" % (dependency,)) + def _notify(): + msg = _("Application %s not found") + msg = msg % (dependency,) + box = NotificationBox( + prepare_markup(msg), Gtk.MessageType.ERROR, + context_id="AppInstallNotFoundContextId") + self._nc.append(box, timeout=10) + if self._nc is not None: + GLib.idle_add(_notify) return app = Application( self._entropy, self._entropy_ws, pkg_match) self._service.application_request( - app, AppActions.INSTALL, simulate=True) + app, AppActions.INSTALL, simulate=simulate) const_debug_write( __name__, - "__simulate_app_install: " + "install: " "application_request() sent for: %s, %s" % ( - text, app,)) + dependency, app,)) - def __simulate_app_removal(self, text): + def install_package(self, package_path, simulate=False): + """ + Install Entropy Package file. + """ + const_debug_write( + __name__, + "install_package: %s" % (package_path,)) + + self._service.package_install_request( + package_path, simulate=simulate) const_debug_write( __name__, - "__simulate_app_removal: " - "%s" % (text,)) + "install_package: " + "package_install_request() sent for: %s" % ( + package_path,)) + + def remove(self, dependency, simulate=False): + """ + Try to match dependency to an Application and then remove + it, if possible. + """ + const_debug_write( + __name__, + "remove: %s" % (dependency,)) self._entropy.rwsem().reader_acquire() try: - pkg_match = self._entropy.atom_match(text) + inst_repo = self._entropy.installed_repository() + pkg_repo = inst_repo.repository_id() + pkg_id, rc = inst_repo.atomMatch(dependency) finally: self._entropy.rwsem().reader_release() - pkg_id, pkg_repo = pkg_match if pkg_id == -1: const_debug_write( __name__, - "__simulate_app_removal: " - "no match for: %s" % (text,)) + "remove: " + "no match for: %s" % (dependency,)) + def _notify(): + msg = _("Application %s not found") + msg = msg % (dependency,) + box = NotificationBox( + prepare_markup(msg), Gtk.MessageType.ERROR, + context_id="AppRemoveNotFoundContextId") + self._nc.append(box, timeout=10) + if self._nc is not None: + GLib.idle_add(_notify) return app = Application( self._entropy, self._entropy_ws, - pkg_match) - app = app.get_installed() - if app is None: - const_debug_write( - __name__, - "__simulate_app_removal: " - "not installed: %s" % (text,)) - return + (pkg_id, pkg_repo)) self._service.application_request( - app, AppActions.REMOVE, simulate=True) + app, AppActions.REMOVE, simulate=simulate) const_debug_write( __name__, - "__simulate_app_removal: " + "remove: " "application_request() sent for: %s, %s" % ( - text, app,)) + dependency, app,)) + + def upgrade(self, simulate=False): + """ + Launch a System Upgrade activity. + """ + const_debug_write( + __name__, "upgrade") + self._service.upgrade_system(simulate=simulate) + const_debug_write( + __name__, "upgrade:" + " upgrade_system() sent") def __simulate_orphaned_apps(self, text): @@ -272,38 +324,34 @@ class ApplicationsViewController(GObject.Object): __name__, "__simulate_orphaned_apps: completed") - def __simulate_system_upgrade(self): - - const_debug_write( - __name__, "__simulate_system_upgrade") - self._service.upgrade_system(simulate=True) - const_debug_write( - __name__, "__simulate_system_upgrade:" - " upgrade_system() sent") - def __search_thread(self, text): ## special keywords hook if text == "rigo:update": self._update_repositories_safe() return + if text == "rigo:upgrade": + self.upgrade() + return + elif text == "rigo:confupdate": + self._service.configuration_updates() + return + + # debug, simulation elif text == "rigo:vte": GLib.idle_add(self.emit, "view-want-change", RigoViewStates.WORK_VIEW_STATE, None) return - elif text == "rigo:confupdate": - self._service.configuration_updates() - return elif text.startswith("rigo:simulate:i:"): sim_str = text[len("rigo:simulate:i:"):].strip() if sim_str: - self.__simulate_app_install(sim_str) + self.install(sim_str, simulate=True) return elif text.startswith("rigo:simulate:r:"): sim_str = text[len("rigo:simulate:r:"):].strip() if sim_str: - self.__simulate_app_removal(sim_str) + self.remove(sim_str, simulate=True) return elif text.startswith("rigo:simulate:o:"): sim_str = text[len("rigo:simulate:o:"):].strip() @@ -311,7 +359,7 @@ class ApplicationsViewController(GObject.Object): self.__simulate_orphaned_apps(sim_str) return if text == "rigo:simulate:u": - self.__simulate_system_upgrade() + self.upgrade(simulate=True) return # serialize searches to avoid segfaults with sqlite3 diff --git a/rigo/rigo_app.py b/rigo/rigo_app.py index 8f10969b0..9c01d3bdd 100644 --- a/rigo/rigo_app.py +++ b/rigo/rigo_app.py @@ -21,6 +21,7 @@ this program; if not, write to the Free Software Foundation, Inc., """ import os +import argparse # entropy.i18n will pick this up os.environ['ETP_GETTEXT_DOMAIN'] = "rigo" @@ -220,6 +221,7 @@ class Rigo(Gtk.Application): self._activity_rwsem, self._entropy, self._entropy_ws, self._service, self._avc, self._notification) + self._avc.set_notification_controller(self._nc) # Bottom NotificationBox controller. # Bottom notifications are only used for @@ -264,7 +266,7 @@ class Rigo(Gtk.Application): If --dumper is in argv, a recurring thread dump function will be spawned every 30 seconds. """ - dumper_enable = "--no-dumper" not in sys.argv + dumper_enable = self._nsargs.dumper if dumper_enable: task = None @@ -658,6 +660,34 @@ class Rigo(Gtk.Application): Gtk.main_quit() return + parser = argparse.ArgumentParser( + description=_("Rigo Application Browser")) + parser.add_argument( + "--package", type=file, + metavar="", help="package path") + parser.add_argument( + "--install", + metavar="", help="install given dependency") + parser.add_argument( + "--remove", + metavar="", help="remove given dependency") + parser.add_argument( + "--dumper", help="enable the main thread dumper (debug)", + action="store_true", default=False) + parser.add_argument( + "--debug", help="enable Entropy Library debug mode", + action="store_true", default=False) + try: + self._nsargs = parser.parse_args(sys.argv[1:]) + except IOError as err: + self._show_ok_dialog( + None, + escape_markup(_("Rigo")), + escape_markup("%s" % (err,))) + entropy.tools.kill_threads() + Gtk.main_quit() + return + self._thread_dumper() self._config_view_c.setup() self._app_view_c.setup() @@ -667,6 +697,7 @@ class Rigo(Gtk.Application): self._service.setup(acquired) self._easter_eggs() self._window.show() + self._start_managing() def _easter_eggs(self): """ @@ -686,6 +717,35 @@ class Rigo(Gtk.Application): box.add_destroy_button(_("Woot, thanks")) self._nc.append(box) + def _start_managing(self): + """ + Start managing applications passed via argv. + """ + if self._nsargs.install: + dependency = self._nsargs.install + task = ParallelTask( + self._avc.install, dependency) + task.name = "AppInstall-%s" % (dependency,) + task.daemon = True + task.start() + + if self._nsargs.remove: + dependency = self._nsargs.remove + task = ParallelTask( + self._avc.remove, dependency) + task.name = "AppRemove-%s" % (dependency,) + task.daemon = True + task.start() + + if self._nsargs.package: + path = self._nsargs.package.name + self._nsargs.package.close() # no need, unfortunately + task = ParallelTask( + self._avc.install_package, path) + task.name = "AppInstallPackage-%s" % (path,) + task.daemon = True + task.start() + def run(self): """ Run Rigo ;-)