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)