diff --git a/magneto/Makefile b/magneto/Makefile index 496eb7028..7210e2609 100644 --- a/magneto/Makefile +++ b/magneto/Makefile @@ -27,7 +27,7 @@ magneto-loader-install: mkdir -p $(DESTDIR)/$(LIBDIR)/entropy/magneto install -m644 $(MISCDIR)/*.desktop $(DESTDIR)/etc/xdg/autostart/. install -m644 $(MISCDIR)/*.desktop $(DESTDIR)$(PREFIX)/share/applications/. - install -m755 src/magneto.py $(DESTDIR)/$(LIBDIR)/entropy/magneto/. + install -m755 src/magneto_app.py $(DESTDIR)/$(LIBDIR)/entropy/magneto/magneto.py install -m755 $(MISCDIR)/magneto $(DESTDIR)$(BINDIR)/. magneto-gtk-install: diff --git a/magneto/src/magneto/core/interfaces.py b/magneto/src/magneto/core/interfaces.py index 203c2c08d..9a4f695b9 100644 --- a/magneto/src/magneto/core/interfaces.py +++ b/magneto/src/magneto/core/interfaces.py @@ -17,6 +17,7 @@ import time import subprocess import dbus import dbus.exceptions +from threading import Lock # Entropy imports from entropy.output import nocolor @@ -29,6 +30,10 @@ from entropy.misc import ParallelTask # Magneto imports from magneto.core import config +# RigoDaemon imports +from RigoDaemon.config import DbusConfig +from RigoDaemon.enums import ActivityStates + class MagnetoCoreUI: """ @@ -97,12 +102,26 @@ class MagnetoCore(MagnetoCoreUI): Magneto base UI interface, must be subclassed to make it support Qt/GTK interfaces. """ + + _PING_SIGNAL = "ping" + _UPDATES_AVAILABLE_SIGNAL = "updates_available" + _ACTIVITY_STARTED_SIGNAL = "activity_started" + _REPOSITORIES_UPDATED_SIGNAL = "repositories_updated" + + DBUS_INTERFACE = DbusConfig.BUS_NAME + DBUS_PATH = DbusConfig.OBJECT_PATH + def __init__(self, icon_loader_class, main_loop_class): if "--debug" not in sys.argv: import signal signal.signal(signal.SIGINT, signal.SIG_DFL) + self.__dbus_main_loop = None + self.__system_bus = None + self.__entropy_bus = None + self.__entropy_bus_mutex = Lock() + # Set this to True when DBus service is up self._dbus_service_available = False # Notice Window Widget status @@ -131,11 +150,6 @@ class MagnetoCore(MagnetoCoreUI): # Dbus variables self._dbus_init_error_msg = 'Unknown error' - self._dbus_interface = "org.entropy.Client" - self._dbus_path = "/notifier" - self._signal_name = "signal_updates" - self._updating_signal_name = "signal_updating" - self._integrity_signal_name = "signal_integrity_problem" self._menu_item_list = ( ("disable_applet", _("_Disable Notification Applet"), @@ -156,38 +170,118 @@ class MagnetoCore(MagnetoCoreUI): self._main_loop_class = main_loop_class + @property + def _dbus_main_loop(self): + if self.__dbus_main_loop is None: + self.__dbus_main_loop = self._main_loop_class( + set_as_default=True) + return self.__dbus_main_loop + + @property + def _system_bus(self): + if self.__system_bus is None: + self.__system_bus = dbus.SystemBus( + mainloop=self._dbus_main_loop) + return self.__system_bus + + @property + def _entropy_bus(self): + with self.__entropy_bus_mutex: + if self.__entropy_bus is None: + self.__entropy_bus = self._system_bus.get_object( + self.DBUS_INTERFACE, self.DBUS_PATH + ) + + # ping/pong signaling, used to let + # RigoDaemon release exclusive locks + # when no client is connected + self.__entropy_bus.connect_to_signal( + self._PING_SIGNAL, self._ping_signal, + dbus_interface=self.DBUS_INTERFACE) + + # RigoDaemon is telling us that a new activity + # has just begun + self.__entropy_bus.connect_to_signal( + self._ACTIVITY_STARTED_SIGNAL, + self._activity_started_signal, + dbus_interface=self.DBUS_INTERFACE) + + # RigoDaemon tells us that there are app updates + # available + self.__entropy_bus.connect_to_signal( + self._UPDATES_AVAILABLE_SIGNAL, + self._updates_available_signal, + dbus_interface=self.DBUS_INTERFACE) + + self.__entropy_bus.connect_to_signal( + self._REPOSITORIES_UPDATED_SIGNAL, + self._repositories_updated_signal, + dbus_interface=self.DBUS_INTERFACE) + + return self.__entropy_bus + def setup_dbus(self): """ Dbus Setup method. """ - tries = 5 - while tries: - dbus_loop = self._main_loop_class(set_as_default = True) - self._system_bus = dbus.SystemBus(mainloop = dbus_loop) - try: - self._entropy_dbus_object = self._system_bus.get_object( - self._dbus_interface, self._dbus_path - ) - self._entropy_dbus_object.connect_to_signal( - self._signal_name, self.new_updates_signal, - dbus_interface = self._dbus_interface - ) - self._entropy_dbus_object.connect_to_signal( - self._updating_signal_name, self.updating_signal, - dbus_interface = self._dbus_interface - ) - self._entropy_dbus_object.connect_to_signal( - self._integrity_signal_name, self.integrity_signal, - dbus_interface = self._dbus_interface) - except dbus.exceptions.DBusException as e: - self._dbus_init_error_msg = repr(e) - # service not avail - tries -= 1 - time.sleep(2) - continue + try: + self._entropy_bus + self._dbus_service_available = True return True - entropy.tools.print_traceback() - return False + except dbus.exceptions.DBusException as err: + self._dbus_service_available = False + self._dbus_init_error_msg = "%s" % (err,) + return False + + def _ping_signal(self): + """ + Need to call pong() as soon as possible to hold all Entropy + Resources allocated by RigoDaemon. + """ + dbus.Interface( + self._entropy_bus, + dbus_interface=self.DBUS_INTERFACE).pong() + + def _activity_started_signal(self, activity): + """ + RigoDaemon is telling us that the scheduled activity, + either by us or by another Rigo, has just begun and + that it, RigoDaemon, has now exclusive access to + Entropy Resources. + """ + if activity == ActivityStates.UPDATING_REPOSITORIES: + self.updating_signal() + + def _repositories_updated_signal(self, result, message): + """ + Repositories have been updated, ask for info + """ + self._hello() + + def _hello(self): + """ + Say hello to RigoDaemon. This causes the sending of + several welcome signals, such as updates notification. + """ + return dbus.Interface( + self._entropy_bus, + dbus_interface=self.DBUS_INTERFACE).hello() + + def _update_repositories(self): + """ + Spawn Repositories Update on RigoDaemon. + """ + accepted = dbus.Interface( + self._entropy_bus, + dbus_interface=self.DBUS_INTERFACE).update_repositories( + [], False) + return accepted + + def _updates_available_signal(self, update, update_atoms, + remove, remove_atoms): + if not update_atoms: + return + self.new_updates_signal(update_atoms) def show_service_not_available(self): # inform user about missing Entropy service @@ -210,16 +304,14 @@ class MagnetoCore(MagnetoCoreUI): ) ) - def new_updates_signal(self): + def new_updates_signal(self, update_atoms): if not config.settings['APPLET_ENABLED']: return - iface = dbus.Interface( - self._entropy_dbus_object, dbus_interface="org.entropy.Client") - updates = iface.get_updates_atoms() - avail = [str(x) for x in updates] + + avail = [str(x) for x in update_atoms] del self.package_updates[:] self.package_updates.extend(avail) - upd_len = len(updates) + upd_len = len(update_atoms) if upd_len: self.update_tooltip(ngettext("There is %s update available", @@ -256,15 +348,6 @@ class MagnetoCore(MagnetoCoreUI): _("Repositories are being updated automatically") ) - def integrity_signal(self): - # all fine, no updates - self.update_tooltip( - _("Installed Packages Repository Integrity Problem")) - self.show_alert( - _("Installed Packages Repository Integrity"), - _("Integrity corruption detected, this might indicate a filesystem or system issue") - ) - def is_system_on_batteries(self): """ Return whether System is running on batteries. @@ -280,30 +363,6 @@ class MagnetoCore(MagnetoCoreUI): return True return False - def is_system_changed(self): - - # enable applet if disabled - if not config.settings['APPLET_ENABLED']: - return False - - # dbus daemon not available - if not self._dbus_service_available: - return False - - iface = dbus.Interface( - self._entropy_dbus_object, dbus_interface="org.entropy.Client") - return iface.is_system_changed() - - def send_keepalive(self): - """ - MagnetoCore users must spawn this function every 60 seconds (with a - timer). - """ - if self._dbus_service_available: - iface = dbus.Interface( - self._entropy_dbus_object, dbus_interface="org.entropy.Client") - iface.client_ping() - def send_check_updates_signal(self, widget=None, startup_check=False): # enable applet if disabled @@ -320,12 +379,10 @@ class MagnetoCore(MagnetoCoreUI): self.last_trigger_check_t = cur_t if self._dbus_service_available: - iface = dbus.Interface( - self._entropy_dbus_object, dbus_interface="org.entropy.Client") if startup_check: - iface.trigger_check() + self._hello() else: - iface.trigger_startup_check() + self._update_repositories() self.manual_check_triggered = True def set_state(self, new_state, use_busy_icon = 0): @@ -371,17 +428,15 @@ class MagnetoCore(MagnetoCoreUI): self.load_url(etpConst['distro_website_url']) def load_url(self, url): - subprocess.call(['xdg-open', url]) + task = ParallelTask( + subprocess.call, ['xdg-open', url]) + task.daemon = True + task.start() def launch_package_manager(self, *data): - if os.access("/usr/bin/rigo", os.X_OK | os.R_OK): - task = ParallelTask(subprocess.call, ["/usr/bin/rigo"]) - task.daemon = True - task.start() - elif os.access("/usr/bin/sulfur", os.X_OK | os.R_OK): - task = ParallelTask(subprocess.call, ["/usr/bin/sulfur"]) - task.daemon = True - task.start() + task = ParallelTask(subprocess.call, ["/usr/bin/rigo"]) + task.daemon = True + task.start() def disable_applet(self): self.update_tooltip(_("Updates Notification Applet Disabled")) @@ -418,49 +473,4 @@ class MagnetoCore(MagnetoCoreUI): raise SystemExit(0) def close_service(self): - if self._dbus_service_available: - iface = dbus.Interface( - self._entropy_dbus_object, dbus_interface="org.entropy.Client") - try: - iface.close_connection() - except dbus.exceptions.DBusException: - pass entropy.tools.kill_threads() - -class Entropy(Client): - - """ - @deprecated - """ - - def init_singleton(self, magneto): - Client.init_singleton(self, installed_repo = False) - self.__magneto = magneto - self.progress_tooltip = self.__magneto.update_tooltip - self.progress_tooltip_message_title = _("Updates Notification") - self.applet_last_message = '' - nocolor() - - def output(self, text, header = "", footer = "", back = False, - importance = 0, level = "info", count = [], percent = False): - - count_str = "" - if count: - if percent: - count_str = str(int(round((float(count[0])/count[1])*100, 1)))+"% " - else: - count_str = "(%s/%s) " % (str(count[0]), str(count[1]),) - - message = count_str+_(text) - #if importance in (1,2): - if importance == 2: - self.progress_tooltip_message_title = message - self.__do_applet_print(self.applet_last_message) - else: - self.__do_applet_print(message) - - def __do_applet_print(self, message): - self.applet_last_message = message - self.__magneto.show_alert(self.progress_tooltip_message_title, - message) -Client.__singleton_class__ = Entropy diff --git a/magneto/src/magneto/gtk/interfaces.py b/magneto/src/magneto/gtk/interfaces.py index 94c326626..783820fea 100644 --- a/magneto/src/magneto/gtk/interfaces.py +++ b/magneto/src/magneto/gtk/interfaces.py @@ -72,17 +72,12 @@ class Magneto(MagnetoCore): def __do_first_check(self): - # if system is running on batteries, - # first check is skipped - if self.is_system_on_batteries(): - return - def _do_check(): - self.send_check_updates_signal(startup_check=True) + self.send_check_updates_signal(startup_check = True) return False if self._dbus_service_available: - # after 20 seconds + # after 10 seconds gobject.timeout_add(10000, _do_check) def startup(self): @@ -99,9 +94,6 @@ class Magneto(MagnetoCore): gobject.timeout_add(30000, self.show_service_available) self.__do_first_check() - # send Keep Alive signal - self.__send_keepalive() - # Notice Window instance self._notice_window = AppletNoticeWindow(self) @@ -111,14 +103,6 @@ class Magneto(MagnetoCore): gtk.main() gtk.gdk.threads_leave() - def __send_keepalive(self): - """ - As per MagnetoCore specs. - """ - gobject.timeout_add(60*1000, self.__send_keepalive) - self.send_keepalive() - return False - def close_service(self): MagnetoCore.close_service(self) gobject.timeout_add(0, gtk.main_quit) diff --git a/magneto/src/magneto/kde/interfaces.py b/magneto/src/magneto/kde/interfaces.py index 67295df97..137826fe8 100644 --- a/magneto/src/magneto/kde/interfaces.py +++ b/magneto/src/magneto/kde/interfaces.py @@ -94,25 +94,13 @@ class Magneto(MagnetoCore): def __do_first_check(self): - # if system is running on batteries, - # first check is skipped - if self.is_system_on_batteries(): - return - def _do_check(): - self.send_check_updates_signal(startup_check=True) + self.send_check_updates_signal(startup_check = True) return False if self._dbus_service_available: QTimer.singleShot(10000, _do_check) - def __send_keepalive(self): - """ - As per MagnetoCore specs. - """ - QTimer.singleShot(60*1000, self.__send_keepalive) - self.send_keepalive() - def startup(self): """ Start user interface. @@ -130,9 +118,6 @@ class Magneto(MagnetoCore): QTimer.singleShot(30000, self.show_service_available) self.__do_first_check() - # send Keep Alive signal - self.__send_keepalive() - # Notice Window instance self._notice_window = AppletNoticeWindow(self) diff --git a/magneto/src/magneto.py b/magneto/src/magneto_app.py similarity index 95% rename from magneto/src/magneto.py rename to magneto/src/magneto_app.py index 9355a02dd..44ab46220 100755 --- a/magneto/src/magneto.py +++ b/magneto/src/magneto_app.py @@ -18,12 +18,14 @@ import time # point and will cause lock stealing. def _startup(): + sys.path.insert(0, "/usr/lib/rigo") sys.path.insert(0, '/usr/lib/entropy/client') sys.path.insert(0, '/usr/lib/entropy/lib') sys.path.insert(0, '/usr/lib/entropy/magneto') + sys.path.insert(0, '../rigo') sys.path.insert(0, '../../client') sys.path.insert(0, '../../lib') - sys.path.insert(0, '../') + sys.path.insert(0, './') startup_delay = None for arg in sys.argv[1:]: @@ -57,6 +59,7 @@ def _startup(): # load GTK from magneto.gtk.interfaces import Magneto + import entropy.tools magneto = Magneto() try: magneto.startup() diff --git a/misc/dbus/interfaces/org.entropy.Client.xml b/misc/dbus/interfaces/org.entropy.Client.xml deleted file mode 100644 index a1b5c2926..000000000 --- a/misc/dbus/interfaces/org.entropy.Client.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/misc/dbus/system-services/org.entropy.Client.service b/misc/dbus/system-services/org.entropy.Client.service deleted file mode 100644 index 1c785a210..000000000 --- a/misc/dbus/system-services/org.entropy.Client.service +++ /dev/null @@ -1,4 +0,0 @@ -[D-BUS Service] -Name=org.entropy.Client -Exec=/usr/sbin/client-updates-daemon -User=root diff --git a/misc/dbus/system.d/org.entropy.Client.conf b/misc/dbus/system.d/org.entropy.Client.conf deleted file mode 100644 index c2d2f30d6..000000000 --- a/misc/dbus/system.d/org.entropy.Client.conf +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/services/client-updates-daemon b/services/client-updates-daemon deleted file mode 100755 index caf50d208..000000000 --- a/services/client-updates-daemon +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -lang_id="en_US.UTF-8" -export LC_ALL="${lang_id}" -export LANG="${lang_id}" -export LANGUAGE="${lang_id}" - -exec /usr/libexec/entropy-updates-service ${@} diff --git a/services/entropy-updates-service b/services/entropy-updates-service deleted file mode 100755 index e69683ded..000000000 --- a/services/entropy-updates-service +++ /dev/null @@ -1,735 +0,0 @@ -#!/usr/bin/python2 -O -# -*- coding: utf-8 -*- -""" - - @author: Fabio Erculiani - @contact: lxnay@sabayon.org - @copyright: Fabio Erculiani - @license: GPL-2 - - B{Entropy Package Manager Client service}. - -""" -import os -import sys -# this makes the daemon to not write the entropy pid file -# avoiding to lock other instances -sys.argv.append('--no-pid-handling') -import time -import gobject -import dbus -import dbus.service -import dbus.mainloop.glib -import signal -import gc -from threading import Lock, RLock - -DAEMON_DEBUG = False -if "--debug" in sys.argv: - DAEMON_DEBUG = True - -# Entropy imports -sys.path.insert(0,'/usr/lib/entropy/lib') -sys.path.insert(0,'/usr/lib/entropy/server') -sys.path.insert(0,'/usr/lib/entropy/client') -sys.path.insert(0,'../lib') -sys.path.insert(0,'../server') -sys.path.insert(0,'../client') - -from entropy.cache import EntropyCacher -# update default writeback timeout -EntropyCacher.WRITEBACK_TIMEOUT = 120 - -from entropy.misc import LogFile -from entropy.i18n import _ -from entropy.exceptions import PermissionDenied, RepositoryError, \ - SystemDatabaseError -import entropy.tools as entropy_tools -from entropy.client.interfaces import Client -from entropy.fetchers import UrlFetcher -from entropy.const import etpConst, const_setup_entropy_pid, \ - const_unsetup_entropy_pid, const_convert_to_rawstring, \ - const_drop_privileges, const_regain_privileges -from entropy.core.settings.base import SystemSettings as SysSet -from entropy.misc import ParallelTask -from entropy.output import TextInterface, nocolor -nocolor() - - -SYS_SETTINGS = SysSet() -TEXT = TextInterface() -DAEMON_LOGFILE = os.path.join(etpConst['syslogdir'],"client-updater.log") -DAEMON_LOG = LogFile(SYS_SETTINGS['system']['log_level']+1, - DAEMON_LOGFILE, header = "[client-updater]") -PREVIOUS_PROGRESS = '' -CHECK_DELAY_SECS = 3600*4 -for xopt in sys.argv: - if xopt.startswith("--secs=") and (len(xopt.split("=")) > 1): - try: - delay_secs = int(xopt.split("=")[1]) - CHECK_DELAY_SECS = delay_secs - except ValueError: - continue - -if DAEMON_DEBUG: - # redirect possible exception tracebacks to log file - sys.stderr = DAEMON_LOG - sys.stdout = DAEMON_LOG - -def write_output(*args, **kwargs): - message = time.strftime('[%H:%M:%S %d/%m/%Y %Z]') + " " + args[0] - global PREVIOUS_PROGRESS - if PREVIOUS_PROGRESS == message: - return - PREVIOUS_PROGRESS = message - DAEMON_LOG.write(message) - DAEMON_LOG.flush() - if DAEMON_DEBUG: - TEXT.output(*args, **kwargs) - -def install_exception_handler(): - sys.excepthook = handle_exception - -def uninstall_exception_handler(): - sys.excepthook = sys.__excepthook__ - -def handle_exception(exc_class, exc_instance, exc_tb): - - t_back = entropy_tools.get_traceback(tb_obj = exc_tb) - - # restore original exception handler, to avoid loops - uninstall_exception_handler() - # write exception to log file - write_output(const_convert_to_rawstring(t_back)) - raise exc_instance - -install_exception_handler() - -class Privileges: - - def __init__(self, drop_privs = True): - self.__drop_privs = drop_privs - self.__drop_privs_lock = RLock() - self.__with_stmt = 0 - - def __enter__(self): - """ - Hold the lock. - """ - self.__drop_privs_lock.acquire() - if self.__with_stmt < 1: - self.regain() - self.__with_stmt += 1 - - def __exit__(self, exc_type, exc_value, traceback): - """ - Drop the lock. - """ - if self.__with_stmt == 1: - self.drop() - self.__with_stmt -= 1 - self.__drop_privs_lock.release() - - def drop(self): - """ - Drop process privileges. Setting unpriv_gid to etpConst['entropygid'] - makes Entropy UGC/Data cache handling working. - """ - if self.__drop_privs: - with self.__drop_privs_lock: - const_drop_privileges(unpriv_gid = etpConst['entropygid']) - - def regain(self): - """ - Regain previously dropped process privileges. - """ - if self.__drop_privs: - with self.__drop_privs_lock: - const_regain_privileges() - -class DaemonUrlFetcher(UrlFetcher): - - daemon_last_avg = 100 - __average = 0 - __downloadedsize = 0 - __remotesize = 0 - __datatransfer = 0 - - def handle_statistics(self, th_id, downloaded_size, total_size, - average, old_average, update_step, show_speed, data_transfer, - time_remaining, time_remaining_secs): - self.__average = average - self.__downloadedsize = downloaded_size - self.__remotesize = total_size - self.__datatransfer = data_transfer - - def update(self): - myavg = abs(int(round(float(self.__average), 1))) - if abs((myavg - self.daemon_last_avg)) < 1: - return - if int(myavg) % 10 == 0: - DAEMON_LOG.write("fetch @ %s%s" % (myavg, "%",)) - self.daemon_last_avg = myavg - - -class Entropy(Client): - - def init_singleton(self): - Client.init_singleton(self, load_ugc = False, - url_fetcher = DaemonUrlFetcher, repo_validation = False) - # validate currently available repos - # manually, to not taint logs - self._validate_repositories(quiet = True) - self.output( - "Loading Entropy Updates daemon: check every %ss, logfile: %s" % ( - CHECK_DELAY_SECS, DAEMON_LOGFILE,) - ) - - def output(self, *args, **kwargs): - return write_output(*args, **kwargs) -Client.__singleton_class__ = Entropy - -class UpdatesDaemon(dbus.service.Object): - - def __init__(self): - # start dbus service - object_path = "/notifier" - dbus_loop = dbus.mainloop.glib.DBusGMainLoop(set_as_default = True) - system_bus = dbus.SystemBus(mainloop = dbus_loop) - name = dbus.service.BusName("org.entropy.Client", - bus = system_bus) - dbus.service.Object.__init__(self, name, object_path) - write_output("__init__: dbus service loaded") - - # do not load/unload every time, this avoids - # memleaks: - # https://forum.sabayon.org/viewtopic.php?f=24&t=26171 - self._entropy = Entropy() - self._privileges = Privileges() - self._privileges.drop() - self.__alive = False - self.__is_working_mutex = Lock() - self.__updater = None - self.__entropy_cache_cleaner = None - self.__entropy_integrity_checker = None - self.__system_changes_checker = None - self.__client_ping_checker = None - self.__oncall_updater = None - self.__quit_service_wd = None - self.__quit_service_trigger = False - self.__trigger_oncall_updater = False - self.__trigger_disable_repos_update = False - self.__trigger_startup_check = False - self.__fetch_mutex = Lock() - self.__updates = [] - self.__updates_atoms = None - self.__system_db_hash = None - self.__last_system_repo_checksum = None - self.__got_client_ping = True - - def start(self): - self.stop() - self.__alive = True - self.__updater = gobject.timeout_add_seconds( - CHECK_DELAY_SECS, self.run_fetcher) - self.__oncall_updater = gobject.timeout_add_seconds( - 3, self.run_oncall_fetcher) - self.__quit_service_wd = gobject.timeout_add_seconds( - 60, self.quit_service_watchdog) - self.__system_changes_checker = gobject.timeout_add_seconds( - 120, self.check_system_changes) - self.__entropy_cache_cleaner = gobject.timeout_add_seconds( - 3600, self.cleanup_entropy_cache) - self.__entropy_integrity_checker = gobject.timeout_add_seconds( - 300, self.entropy_integrity_check) - if "--disable-timer" not in sys.argv: - self.__client_ping_checker = gobject.timeout_add_seconds( - 120, self.check_client_ping) - - def stop(self): - if self.__alive: - self.__alive = False - - if self.__updater is not None: - gobject.source_remove(self.__updater) - - if self.__system_changes_checker is not None: - gobject.source_remove(self.__system_changes_checker) - - if self.__oncall_updater is not None: - gobject.source_remove(self.__oncall_updater) - - if self.__quit_service_wd is not None: - gobject.source_remove(self.__quit_service_wd) - - if self.__client_ping_checker is not None: - gobject.source_remove(self.__client_ping_checker) - - if self.__entropy_cache_cleaner is not None: - gobject.source_remove(self.__entropy_cache_cleaner) - - if self.__entropy_integrity_checker is not None: - gobject.source_remove(self.__entropy_integrity_checker) - - def do_alert(self, string, msg, urgency = "critical"): - write_output('alert: %s, %s, urgency: %s' % (string, msg, urgency,)) - - def is_system_on_batteries(self): - """ - Return whether System is running on batteries. - - @return: True, if running on batteries - @rtype: bool - """ - ac_powa_exec = "/usr/bin/on_ac_power" - if not os.access(ac_powa_exec, os.X_OK): - return False - ex_rc = os.system(ac_powa_exec) - if ex_rc: - return True - return False - - def check_client_ping(self): - if self.__got_client_ping: - self.__got_client_ping = False - else: # timeout! - if DAEMON_DEBUG: - write_output("check_client_ping: NO client ping, quitting") - self.__quit_service_trigger = True - return self.__alive - - def cleanup_entropy_cache(self): - - with self.__is_working_mutex: - - with self._privileges: - acquired = False - try: - acquired = self.__acquire_entropy_locks(self._entropy, - pid_lock = False) - if not acquired: - return True # respawn later - - if hasattr(self._entropy, 'clean_downloaded_packages'): - self._entropy.clean_downloaded_packages() - return False - - finally: - if acquired: - self.__release_entropy_locks(self._entropy, pid_lock = False) - - def entropy_integrity_check(self): - - if self.is_system_on_batteries(): - # try again later - return self.__alive - - with self.__is_working_mutex: - - with self._privileges: - acquired = False - try: - acquired = self.__acquire_entropy_locks(self._entropy, - pid_lock = False) - if not acquired: - return self.__alive # respawn later - - inst_repo = self._entropy.installed_repository() - if inst_repo is not None: - try: - inst_repo.integrity_check() - except SystemDatabaseError: - self.signal_integrity_problem() - - # no need to run this further - return False - - finally: - if acquired: - self.__release_entropy_locks(self._entropy, pid_lock = False) - - def check_system_changes(self): - if self.__trigger_oncall_updater: - return self.__alive - - changed = self._is_system_changed() - if changed: - # this will disable repositories download, we don't really want - # that in this case. - self.__trigger_disable_repos_update = True - # trigger check and push - self.__trigger_oncall_updater = True - # keep alive - return self.__alive - - def run_oncall_fetcher(self): - - if not self.__trigger_oncall_updater: - return self.__alive - with self.__fetch_mutex: - self.__trigger_oncall_updater = False - update_repos = True - if self.__trigger_disable_repos_update: - update_repos = False - self.__trigger_disable_repos_update = False - task = ParallelTask(self.__run_fetcher, update_repos = update_repos) - task.daemon = True - task.start() - #self.__run_fetcher() - return self.__alive - - def quit_service_watchdog(self): - if self.__quit_service_trigger: - self.stop() - with self.__is_working_mutex: - entropy_tools.kill_threads() - raise SystemExit(0) - return self.__alive - - def run_fetcher(self): - - if self.is_system_on_batteries(): - # running on batteries, then skip - return self.__alive - - with self.__fetch_mutex: - task = ParallelTask(self.__run_fetcher) - task.daemon = True - task.start() - #self.__run_fetcher() - return self.__alive - - def __run_sync(self, repos, entropy): - - try: - repo_conn = entropy.Repositories( - repo_identifiers = repos, fetch_security = False, - entropy_updates_alert = True) - if DAEMON_DEBUG: - write_output("__run_sync: repository interface loaded") - except Exception, err: - if DAEMON_DEBUG: - write_output( - "__run_sync: Unhandled exception, error: %s" % (err,)) - else: - - # 128: sync error, something bad happened - # 2: repositories not available (all) - # 1: not able to update all the repositories - if DAEMON_DEBUG: - write_output("__run_sync: preparing to run sync") - rc_res = repo_conn.sync() - del repo_conn - if DAEMON_DEBUG: - write_output("__run_sync: sync done") - - if rc_res == 1: - err = _("Not all the repositories have been fetched") - self.do_alert( _("Updates: repository issues"), err ) - return 0 # fine anyway - elif rc_res == 2: - err = _("No repositories found online") - self.do_alert( _("Updates: repository issues"), err ) - return 0 # try to calculate updates anyway - elif rc_res == 128: - err = _("Synchronization errors. Cannot update repositories.") - self.do_alert( _("Updates: sync issues"), err ) - return rc_res - elif isinstance(rc_res, basestring): - self.do_alert( _("Updates: unhandled error"), rc_res ) - return 1 - - return 0 - - def __acquire_entropy_locks(self, entropy, pid_lock = True): - - if pid_lock: - acquired, locked = const_setup_entropy_pid(force_handling = True) - if (not acquired) or locked: - if DAEMON_DEBUG: - if locked: - write_output( - "__acquire_entropy_locks: app. locked, skipping") - else: - write_output( - "__acquire_entropy_locks: app. locked during acquire") - return False - - # entropy resources locked? - acquired = entropy.lock_resources(blocking = False) - if not acquired: - if DAEMON_DEBUG: - write_output( - "__acquire_entropy_locks: resources locked, skipping") - # reload installed packages repository - entropy.close_repositories() - entropy.reopen_installed_repository() - # garbage collect - count = gc.collect() - if DAEMON_DEBUG: - write_output("__acquire_entropy_locks: GC collected %s" % (count,)) - return acquired - - def __release_entropy_locks(self, entropy, pid_lock = True): - # unlock resources - entropy.unlock_resources() - if pid_lock: - # remove application lock - const_unsetup_entropy_pid() - - def __run_fetcher(self, update_repos = True): - - if self.__updater == None: - return 0 - - with self.__is_working_mutex: - - with self._privileges: - - rc_fetch = 0 - acquired = False - - try: - - acquired = self.__acquire_entropy_locks(self._entropy, - pid_lock = update_repos) - if not acquired: - return rc_fetch - - if DAEMON_DEBUG: - write_output("__run_fetcher: called %s" % ( - time.time(),)) - - if update_repos: - repos_to_up = self.get_repo_status() - if repos_to_up: - - self.do_alert( - _("Repositories to update"), - unicode(repos_to_up), - urgency = 'critical' - ) - - if not self.__trigger_startup_check: - gobject.timeout_add(0, self.signal_updating) - repos = repos_to_up.keys() - rc_fetch = self.__run_sync(repos, self._entropy) - if rc_fetch != 0: - return rc_fetch - if DAEMON_DEBUG: - write_output("__run_fetcher: sync closed, rc: %s" % ( - rc_fetch,)) - else: - self.__trigger_startup_check = False - if DAEMON_DEBUG: - write_output("__run_fetcher: not syncing atm, " - "trigger startup check is ON, waiting next " - "round, repos: %s" % (repos_to_up,)) - - try: - update, remove, fine, spm_fine = \ - self._entropy.calculate_updates(use_cache = False) - del fine, remove - except Exception, err: - entropy_tools.print_traceback(f = DAEMON_LOG) - msg = "%s: %s" % (_("Updates: error"), err,) - self.do_alert(_("Updates: error"), msg) - del self.__updates[:] - self.__updates_atoms = None - return 1 - - self.__system_db_hash = \ - self._entropy.installed_repository().checksum( - do_order = True, strict = False) - self.__updates = update[:] - self.__updates_atoms = None - - if update: - self.do_alert( - _("Updates available"), - "%s %d %s" % ( - _("There are"), len(update), - _("updates available."),), - urgency = 'critical' - ) - gobject.timeout_add(0, self.signal_updates) - else: - self.do_alert( - _("No updates"), - "%s" % (update,), - urgency = 'critical' - ) - gobject.timeout_add(0, self.signal_updates) - - return 0 - - finally: - if acquired: - self.__release_entropy_locks(self._entropy, - pid_lock = update_repos) - - # compare repos status for updates - @dbus.service.method ( "org.entropy.Client", in_signature = '', - out_signature = 'a{si}') - def get_repo_status(self): - - repos = {} - # now get remote - sys_set = SysSet() - for repoid in sys_set['repositories']['available']: - - if DAEMON_DEBUG: - write_output( - "get_repo_status: scanning repository: %s" % ( - repoid,)) - - repo_rev = Entropy.get_repository(repoid).revision(repoid) - online_rev = Entropy.get_repository(repoid).remote_revision(repoid) - - if DAEMON_DEBUG: - write_output( - "get_repo_status: local rev: %s, remote rev: %s" % ( - repo_rev, online_rev,)) - - if (online_rev == -1) or (repo_rev != online_rev): - - if DAEMON_DEBUG: - write_output( - "get_repo_status: repo needs to be updated: %s" % ( - repoid,)) - - sys_set._clear_repository_cache(repoid = repoid) - repos[repoid] = { - 'local': repo_rev, - 'remote': online_rev, - } - - return repos - - @dbus.service.method ( "org.entropy.Client", in_signature = '', - out_signature = '') - def trigger_check(self): - self.__trigger_oncall_updater = True - - @dbus.service.method ( "org.entropy.Client", in_signature = '', - out_signature = '') - def trigger_startup_check(self): - self.__trigger_oncall_updater = True - self.__trigger_startup_check = True - - @dbus.service.method ( "org.entropy.Client", in_signature = '', - out_signature = 'av') - def get_updates(self): - - curr_hash = self._entropy.installed_repository().checksum( - do_order = True, strict = False) - if curr_hash == self.__system_db_hash: - return self.__updates - - try: - update, remove, fine, spm_fine = \ - self._entropy.calculate_updates(use_cache = False) - except Exception, err: - entropy_tools.print_traceback(f = DAEMON_LOG) - msg = "get_updates: %s: %s" % (_("Updates: error"), err,) - self.do_alert(_("Updates: error"), msg) - return self.__updates - - self.__updates = update[:] - self.__system_db_hash = curr_hash - return self.__updates - - def _is_system_changed(self): - with self.__is_working_mutex: - - acquired = False - try: - acquired = self._entropy.lock_resources(blocking = False) - if not acquired: - if DAEMON_DEBUG: - write_output( - "_is_system_changed: resources locked!") - # resources are locked, nothing changed yet :P - return False - - last_checksum = self.__last_system_repo_checksum - cur_checksum = self._entropy.installed_repository().checksum( - strict = False, strings = True) - - changed = last_checksum != cur_checksum - if DAEMON_DEBUG and changed: - write_output( - "_is_system_changed: system db checksum changed!") - self.__last_system_repo_checksum = cur_checksum - return changed - finally: - if acquired: - self._entropy.unlock_resources() - - @dbus.service.method("org.entropy.Client", in_signature = '', - out_signature = 'b') - def is_system_changed(self): - return self._is_system_changed() - - @dbus.service.method("org.entropy.Client", in_signature = '', - out_signature = 'av') - def get_updates_atoms(self): - - if self.__updates_atoms is not None: - return self.__updates_atoms - - with self.__is_working_mutex: - atoms = [] - self.__updates_atoms = [] - for idpackage, repoid in self.__updates: - try: - dbc = self._entropy.open_repository(repoid) - atoms.append(dbc.retrieveAtom(idpackage)) - except RepositoryError: - continue - self.__updates_atoms.extend(atoms) - return self.__updates_atoms - - @dbus.service.method("org.entropy.Client", in_signature = '', - out_signature = '') - def client_ping(self): - self.__got_client_ping = True - if DAEMON_DEBUG: - write_output("client_ping: got client ping") - - # signal sent when updates are available for retrive - @dbus.service.signal(dbus_interface = 'org.entropy.Client', - signature = '') - def signal_updates(self): - if DAEMON_DEBUG: - write_output("signal_updates: updates available!") - return False - - # signal sent when installed packages repository is corrupted - @dbus.service.signal(dbus_interface = 'org.entropy.Client', - signature = '') - def signal_integrity_problem(self): - if DAEMON_DEBUG: - write_output("signal_integrity_problem: corrupted repository!") - return False - - # signal sent when daemon is updating the repositories - @dbus.service.signal(dbus_interface = 'org.entropy.Client', - signature = '') - def signal_updating(self): - if DAEMON_DEBUG: - write_output("signal_updating: updating repos!") - return False - -if __name__ == "__main__": - signal.signal(signal.SIGINT, signal.SIG_DFL) - try: - ud_i = UpdatesDaemon() - except dbus.exceptions.DBusException: - raise SystemExit(1) - gobject.threads_init() - ud_i.start() - main_loop = gobject.MainLoop() - main_loop.run() - ud_i.stop() - raise SystemExit(0)