[magneto] migrate to RigoDaemon, kill entropy-updates-service (RIP!)

This commit is contained in:
Fabio Erculiani
2012-04-12 19:40:20 +02:00
parent a639e417d9
commit c43e589276
10 changed files with 147 additions and 946 deletions

View File

@@ -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:

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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()

View File

@@ -1,14 +0,0 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.entropy.Client">
<method name="notifier"/>
<method name="get_repo_status"/>
<method name="trigger_check"/>
<method name="trigger_startup_check"/>
<method name="get_updates"/>
<method name="get_updates_atoms"/>
<method name="is_system_changed"/>
<method name="close_connection"/>
<method name="client_ping"/>
</interface>
</node>

View File

@@ -1,4 +0,0 @@
[D-BUS Service]
Name=org.entropy.Client
Exec=/usr/sbin/client-updates-daemon
User=root

View File

@@ -1,20 +0,0 @@
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy user="root">
<allow own="org.entropy.Client"/>
<allow send_destination="org.entropy.Client"/>
<allow send_requested_reply="true"/>
</policy>
<policy group="entropy">
<allow own="org.entropy.Client"/>
<allow send_destination="org.entropy.Client"/>
<allow send_requested_reply="true"/>
</policy>
<policy context="default">
<allow send_requested_reply="true"/>
<deny send_destination="org.entropy.Client"/>
</policy>
</busconfig>

View File

@@ -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 ${@}

View File

@@ -1,735 +0,0 @@
#!/usr/bin/python2 -O
# -*- coding: utf-8 -*-
"""
@author: Fabio Erculiani <lxnay@sabayon.org>
@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)