diff --git a/rigo/RigoDaemon/app.py b/rigo/RigoDaemon/app.py
index 89fdb46b3..2492d3722 100755
--- a/rigo/RigoDaemon/app.py
+++ b/rigo/RigoDaemon/app.py
@@ -14,7 +14,6 @@ import os
import sys
import time
import signal
-import hashlib
from threading import Lock, Timer
# this makes the daemon to not write the entropy pid file
@@ -249,7 +248,6 @@ class RigoDaemonService(dbus.service.Object):
self._current_activity_mutex = Lock()
self._current_activity = ActivityStates.AVAILABLE
- self._activity_token = None
self._activity_mutex = Lock()
self._acquired_exclusive = False
@@ -266,17 +264,11 @@ class RigoDaemonService(dbus.service.Object):
"""
Switch to busy activity state, if possible.
Raise RigoDaemonService.BusyError if already busy.
- Returns an Activity Token (float) that can be used
- with acquire_exclusive() and release_exclusive().
"""
with self._current_activity_mutex:
if self._current_activity != ActivityStates.AVAILABLE:
raise RigoDaemonService.BusyError()
self._current_activity = activity
- token = hashlib.sha1()
- token.update(str(time.time()))
- self._activity_token = token.hexdigest()
- return self._activity_token
def _unbusy(self, activity, _force=False):
"""
@@ -285,7 +277,6 @@ class RigoDaemonService(dbus.service.Object):
AVAILABLE.
Raise RigoDaemonService.UnbusyFromDifferentActivity if
provided activity differs from the current one.
- Return the current activity_token and atomically resets it.
"""
with self._current_activity_mutex:
if activity != self._current_activity and not _force:
@@ -293,9 +284,6 @@ class RigoDaemonService(dbus.service.Object):
if activity == ActivityStates.AVAILABLE and not _force:
raise RigoDaemonService.AlreadyAvalabileError()
self._current_activity = ActivityStates.AVAILABLE
- token = self._activity_token
- self._activity_token = None
- return token
def stop(self):
"""
@@ -315,27 +303,32 @@ class RigoDaemonService(dbus.service.Object):
entropy.tools.kill_threads()
os.kill(os.getpid(), signal.SIGTERM)
- def _update_repositories(self, repositories, force):
+ def _update_repositories(self, repositories, force, activity):
"""
Repositories Update execution code.
"""
with self._activity_mutex:
- self._close_local_resources()
-
- if not repositories:
- repositories = list(
- SystemSettings()['repositories']['available'])
-
- if DAEMON_DEBUG:
- write_output("_update_repositories(): %s" % (
- repositories,))
+ # ask clients to release their locks
+ self._acquire_exclusive(activity)
result = 99
msg = ""
try:
+ self._close_local_resources()
+ self._entropy_setup()
+ self.activity_started(activity)
+
+ if not repositories:
+ repositories = list(
+ SystemSettings()['repositories']['available'])
+ if DAEMON_DEBUG:
+ write_output("_update_repositories(): %s" % (
+ repositories,))
+
updater = self._entropy.Repositories(
repositories, force = force)
result = updater.unlocked_sync()
+
except AttributeError as err:
write_output("_update_repositories error: %s" % (err,))
result = 1
@@ -346,9 +339,17 @@ class RigoDaemonService(dbus.service.Object):
result = 2
msg = _("Unhandled Exception")
return
+
finally:
- self.repositories_updated(
- result, msg, self._activity_token)
+ try:
+ self._unbusy(activity)
+ except RigoDaemonService.AlreadyAvailableError:
+ write_output("_update_repositories._unbusy: already "
+ "available, wtf !?!?")
+ # wtf??
+ self._release_exclusive(activity)
+ self.activity_completed(activity, result == 0)
+ self.repositories_updated(result, msg)
def _close_local_resources(self):
"""
@@ -378,6 +379,8 @@ class RigoDaemonService(dbus.service.Object):
blocking=False,
shared=False)
if not acquired:
+ if DAEMON_DEBUG:
+ write_output("_acquire_exclusive: asking to unlock")
self.resources_unlock_request(activity)
self._entropy.lock_resources(
blocking=True,
@@ -385,61 +388,66 @@ class RigoDaemonService(dbus.service.Object):
if DAEMON_DEBUG:
write_output("_acquire_exclusive: just acquired lock")
- self.exclusive_acquired()
-
- def _release_exclusive(self):
+ def _release_exclusive(self, activity):
"""
Release Exclusive access to Entropy Resources.
"""
- with self._activity_mutex:
- # make sure to not release locks as long
- # as there is activity
- with self._acquired_exclusive_mutex:
- if self._acquired_exclusive:
- self._entropy.unlock_resources()
- # now we got the exclusive lock
- self._acquired_exclusive = False
+ # make sure to not release locks as long
+ # as there is activity
+ with self._acquired_exclusive_mutex:
+ if self._acquired_exclusive:
+ self.resources_lock_request(activity)
+ self._entropy.unlock_resources()
+ # now we got the exclusive lock
+ self._acquired_exclusive = False
+
+ def _entropy_setup(self):
+ """
+ Notify us that a new client is now connected.
+ Here we reload Entropy configuration and other resources.
+ """
+ if DAEMON_DEBUG:
+ write_output("_entropy_setup(): called")
+ acquired = self._ping_sched_startup.acquire(False)
+ if acquired:
+ if DAEMON_DEBUG:
+ write_output("_entropy_setup(): starting ping() signaling")
+ self._ping_sched.start()
+
+ initconfig_entropy_constants(etpConst['systemroot'])
+ self._entropy.Settings().clear()
+ self._entropy._validate_repositories()
+ self._close_local_resources()
+ if DAEMON_DEBUG:
+ write_output("_entropy_setup(): complete")
### DBUS METHODS
@dbus.service.method(BUS_NAME, in_signature='asb',
- out_signature='')
+ out_signature='b')
def update_repositories(self, repositories, force):
"""
Request RigoDaemon to update the given repositories.
At the end of the execution, the "repositories_updated"
signal will be raised.
"""
+ activity = ActivityStates.UPDATING_REPOSITORIES
+ try:
+ self._busy(activity)
+ except RigoDaemonService.BusyError:
+ # I am already busy doing other stuff, cannot
+ # satisfy request
+ return False
+
if DAEMON_DEBUG:
write_output("update_repositories called: %s" % (
repositories,))
task = ParallelTask(self._update_repositories, repositories,
- force)
+ force, activity)
task.daemon = True
task.name = "UpdateRepositoriesThread"
task.start()
-
- @dbus.service.method(BUS_NAME, in_signature='',
- out_signature='')
- def connect(self):
- """
- Notify us that a new client is now connected.
- Here we reload Entropy configuration and other resources.
- """
- if DAEMON_DEBUG:
- write_output("connect(): called")
- acquired = self._ping_sched_startup.acquire(False)
- if acquired:
- if DAEMON_DEBUG:
- write_output("connect(): starting ping() signaling")
- self._ping_sched.start()
- with self._activity_mutex:
- initconfig_entropy_constants(etpConst['systemroot'])
- self._entropy.Settings().clear()
- self._entropy._validate_repositories()
- self._close_local_resources()
- if DAEMON_DEBUG:
- write_output("A new client is now connected !")
+ return True
@dbus.service.method(BUS_NAME, in_signature='',
out_signature='i')
@@ -450,69 +458,6 @@ class RigoDaemonService(dbus.service.Object):
"""
return self._current_activity
- @dbus.service.method(BUS_NAME, in_signature='i',
- out_signature='b')
- def acquire_exclusive(self, activity):
- """
- Start the rendezvous that will give us (this process)
- exclusive access to Entropy Resources, released by
- Rigo.
- """
- if DAEMON_DEBUG:
- write_output("acquire_exclusive: called for activity %s" % (
- activity,))
-
- try:
- token = self._busy(activity)
- except RigoDaemonService.BusyError:
- # I am already busy doing other stuff, cannot
- # satisfy request
- return False
-
- task = ParallelTask(self._acquire_exclusive, activity)
- task.daemon = True
- task.name = "AcquireExclusive"
- task.start()
- return True
-
- @dbus.service.method(BUS_NAME, in_signature='is',
- out_signature='b')
- def release_exclusive(self, activity, token):
- """
- Release exclusive access to Entropy Resources.
- """
- if DAEMON_DEBUG:
- write_output("release_exclusive: called for activity: %s" % (
- activity,))
-
- try:
- current_token = self._unbusy(activity)
- except RigoDaemonService.AlreadyAvailableError:
- write_output("release_exclusive: already "
- "available, ignoring")
- return True
- except RigoDaemonService.UnbusyFromDifferentActivity:
- write_output("release_exclusive: unbusy "
- "from different activity: %s"
- ", current: %s"% (
- activity, self._current_activity,))
- return False
-
- # determine if token is still valid or belongs to
- # a previous run.
- if token != current_token:
- write_output("release_exclusive: unbusy "
- "with different token: %s"
- ", current: %s"% (
- token, current_token,))
- return False
-
- task = ParallelTask(self._release_exclusive)
- task.daemon = True
- task.name = "ReleaseExclusive"
- task.start()
- return True
-
@dbus.service.method(BUS_NAME, in_signature='',
out_signature='b')
def is_exclusive(self):
@@ -575,8 +520,8 @@ class RigoDaemonService(dbus.service.Object):
pass
@dbus.service.signal(dbus_interface=BUS_NAME,
- signature='iss')
- def repositories_updated(self, result, message, token):
+ signature='is')
+ def repositories_updated(self, result, message):
"""
Repositories have been updated.
"result" is an integer carrying execution return status.
@@ -585,16 +530,6 @@ class RigoDaemonService(dbus.service.Object):
write_output("repositories_updated() issued, args:"
" %s" % (locals(),))
- @dbus.service.signal(dbus_interface=BUS_NAME,
- signature='')
- def exclusive_acquired(self):
- """
- Entropy Resources have been eventually acquired in
- blocking mode.
- """
- if DAEMON_DEBUG:
- write_output("exclusive_acquired() issued")
-
@dbus.service.signal(dbus_interface=BUS_NAME,
signature='i')
def resources_unlock_request(self, activity):
@@ -608,6 +543,42 @@ class RigoDaemonService(dbus.service.Object):
write_output("resources_unlock_request() issued for %d" % (
activity,))
+ @dbus.service.signal(dbus_interface=BUS_NAME,
+ signature='i')
+ def resources_lock_request(self, activity):
+ """
+ Signal all the connected Clients to re-acquire shared
+ Entropy Resources Lock. This is actually a mandatory
+ request in order to run into really bad, inconsistent states.
+ """
+ if DAEMON_DEBUG:
+ write_output("resources_lock_request() issued for %d" % (
+ activity,))
+
+ @dbus.service.signal(dbus_interface=BUS_NAME,
+ signature='i')
+ def activity_started(self, activity):
+ """
+ Signal all the connected Clients that a scheduled activity
+ has begun (and we're running with exclusive Entropy Resources
+ access).
+ """
+ if DAEMON_DEBUG:
+ write_output("activity_started() issued for %d" % (
+ activity,))
+
+ @dbus.service.signal(dbus_interface=BUS_NAME,
+ signature='ib')
+ def activity_completed(self, activity, success):
+ """
+ Signal all the connected Clients that a scheduled activity
+ has been carried out.
+ """
+ if DAEMON_DEBUG:
+ write_output("activity_completed() issued for %d,"
+ " success: %s" % (
+ activity, success,))
+
@dbus.service.signal(dbus_interface=BUS_NAME,
signature='')
def ping(self):
@@ -620,7 +591,10 @@ class RigoDaemonService(dbus.service.Object):
if DAEMON_DEBUG:
write_output("time is up! issuing _release_exclusive()")
self._unbusy(None, _force=True)
- self._release_exclusive()
+ with self._activity_mutex:
+ self._release_exclusive()
+ if DAEMON_DEBUG:
+ write_output("issued _release_exclusive()")
if DAEMON_DEBUG:
write_output("ping() issued")
diff --git a/rigo/RigoDaemon/config.py b/rigo/RigoDaemon/config.py
index 39b0585ff..069568cdd 100644
--- a/rigo/RigoDaemon/config.py
+++ b/rigo/RigoDaemon/config.py
@@ -20,4 +20,4 @@ class PolicyActions:
# PolicyKit update action
UPDATE_REPOSITORIES = "org.sabayon.RigoDaemon.update"
UPGRADE_SYSTEM = "org.sabayon.RigoDaemon.upgrade"
- MANAGE_APP = "org.sabayon.RigoDaemon.manage"
+ MANAGE_APPLICATIONS = "org.sabayon.RigoDaemon.manage"
diff --git a/rigo/RigoDaemon/dbus/org.sabayon.Rigo.xml b/rigo/RigoDaemon/dbus/org.sabayon.Rigo.xml
index 7bd0f9627..b20607ecb 100644
--- a/rigo/RigoDaemon/dbus/org.sabayon.Rigo.xml
+++ b/rigo/RigoDaemon/dbus/org.sabayon.Rigo.xml
@@ -4,20 +4,11 @@
-
-
-
-
-
-
-
-
-
-
+
diff --git a/rigo/RigoDaemon/enums.py b/rigo/RigoDaemon/enums.py
index c32c4656d..dcc3f36e6 100644
--- a/rigo/RigoDaemon/enums.py
+++ b/rigo/RigoDaemon/enums.py
@@ -15,7 +15,7 @@ class ActivityStates:
AVAILABLE,
NOT_AVAILABLE,
UPDATING_REPOSITORIES,
- INSTALLING_APPLICATION,
+ MANAGING_APPLICATIONS,
UPGRADING_SYSTEM,
INTERNAL_ROUTINES
) = range(6)
diff --git a/rigo/rigo/enums.py b/rigo/rigo/enums.py
index 21e334605..918783390 100644
--- a/rigo/rigo/enums.py
+++ b/rigo/rigo/enums.py
@@ -75,16 +75,21 @@ class RigoViewStates:
class LocalActivityStates:
(
READY,
- UPDATING_REPOSITORIES_MASTER,
- UPDATING_REPOSITORIES_SLAVE,
- INSTALLING_APPLICATIONS,
- ) = range(4)
+ UPDATING_REPOSITORIES,
+ MANAGING_APPLICATIONS,
+ ) = range(3)
class BusyError(Exception):
"""
Cannot acknowledge a Local Activity change.
"""
+ class SameError(Exception):
+ """
+ Cannot set the same Local Activity.
+ The proposed activity equals the current one.
+ """
+
class AlreadyReadyError(Exception):
"""
Cannot acknowledge a Local Activity change to
diff --git a/rigo/rigo/ui/gtk3/controllers/application.py b/rigo/rigo/ui/gtk3/controllers/application.py
index 3f40b8387..8903682a2 100644
--- a/rigo/rigo/ui/gtk3/controllers/application.py
+++ b/rigo/rigo/ui/gtk3/controllers/application.py
@@ -83,7 +83,7 @@ class ApplicationViewController(GObject.Object):
"application-request-action" : (GObject.SignalFlags.RUN_LAST,
None,
(GObject.TYPE_PYOBJECT,
- str),
+ GObject.TYPE_PYOBJECT),
),
}
@@ -724,7 +724,7 @@ class ApplicationViewController(GObject.Object):
Remove the given Application.
"""
self.emit("application-request-action",
- app, AppActions.REMOVE)
+ app.get_installed(), AppActions.REMOVE)
def _on_app_install(self, widget, app):
"""
diff --git a/rigo/rigo/ui/gtk3/widgets/apptreeview.py b/rigo/rigo/ui/gtk3/widgets/apptreeview.py
index 9623e2c39..726a864c6 100644
--- a/rigo/rigo/ui/gtk3/widgets/apptreeview.py
+++ b/rigo/rigo/ui/gtk3/widgets/apptreeview.py
@@ -502,8 +502,6 @@ class AppTreeView(Gtk.TreeView):
else:
perform_action = AppActions.INSTALL
- store.notify_action_request(app, path)
-
self._apc.emit("application-request-action",
self.appmodel.get_application(app),
perform_action)
diff --git a/rigo/rigo_app.py b/rigo/rigo_app.py
index 90d057a08..96623c9be 100644
--- a/rigo/rigo_app.py
+++ b/rigo/rigo_app.py
@@ -27,6 +27,7 @@ import time
from threading import Lock, Semaphore
import dbus
+import dbus.exceptions
sys.path.insert(0, "../lib")
sys.path.insert(1, "../client")
@@ -165,9 +166,10 @@ class RigoServiceController(GObject.Object):
_OUTPUT_SIGNAL = "output"
_REPOSITORIES_UPDATED_SIGNAL = "repositories_updated"
_TRANSFER_OUTPUT_SIGNAL = "transfer_output"
- _EXCLUSIVE_ACQUIRED_SIGNAL = "exclusive_acquired"
_PING_SIGNAL = "ping"
_RESOURCES_UNLOCK_REQUEST_SIGNAL = "resources_unlock_request"
+ _RESOURCES_LOCK_REQUEST_SIGNAL = "resources_lock_request"
+ _ACTIVITY_STARTED_SIGNAL = "activity_started"
def __init__(self, rigo_app, activity_rwsem, auth,
entropy_client, entropy_ws):
@@ -176,8 +178,10 @@ class RigoServiceController(GObject.Object):
self._activity_rwsem = activity_rwsem
self._auth = auth
self._nc = None
+ self._bottom_nc = None
self._wc = None
self._avc = None
+ self._apc = None
self._terminal = None
self._entropy = entropy_client
self._entropy_ws = entropy_ws
@@ -185,8 +189,6 @@ class RigoServiceController(GObject.Object):
self.__system_bus = None
self.__entropy_bus = None
self.__entropy_bus_mutex = Lock()
- self._connected_mutex = Lock()
- self._connected = False
self._registered_signals = {}
self._registered_signals_mutex = Lock()
@@ -196,12 +198,20 @@ class RigoServiceController(GObject.Object):
self._please_wait_box = None
self._please_wait_mutex = Lock()
+ self._application_request_mutex = Lock()
+
def set_applications_controller(self, avc):
"""
Bind ApplicationsViewController object to this class.
"""
self._avc = avc
+ def set_application_controller(self, apc):
+ """
+ Bind ApplicationViewController object to this class.
+ """
+ self._apc = apc
+
def set_terminal(self, terminal):
"""
Bind a TerminalWidget to this object, in order to be used with
@@ -222,15 +232,56 @@ class RigoServiceController(GObject.Object):
"""
self._nc = nc
+ def set_bottom_notification_controller(self, bottom_nc):
+ """
+ Bind a BottomNotificationViewController to this object.
+ """
+ self._bottom_nc = bottom_nc
+
+ def setup(self, shared_locked):
+ """
+ Execute object setup once initialization phase is complete.
+ This phase is comprehensive of all the set_* method calls.
+ """
+ if self._apc is not None:
+ # connect application request events
+ self._apc.connect(
+ "application-request-action",
+ self._on_application_request_action)
+
+ # since we handle the lock/unlock of entropy
+ # resources here, we need to know what's the
+ # initial state
+ self._resources_locked = Lock()
+ if not shared_locked:
+ self._resources_locked.acquire()
+
+ def service_available(self):
+ """
+ Return whether the RigoDaemon dbus service is
+ available.
+ """
+ try:
+ self._entropy_bus
+ return True
+ except dbus.exceptions.DBusException:
+ return False
+
def busy(self, local_activity):
"""
Become busy, switch to some local activity.
If an activity is already taking place,
LocalActivityStates.BusyError is raised.
+ If the active activity equals the requested one,
+ LocalActivityStates.SameError is raised.
"""
with self._local_activity_mutex:
+ if self._local_activity == local_activity:
+ raise LocalActivityStates.SameError()
if self._local_activity != LocalActivityStates.READY:
raise LocalActivityStates.BusyError()
+ GLib.idle_add(self._bottom_nc.set_activity,
+ local_activity)
self._local_activity = local_activity
def unbusy(self, current_activity):
@@ -244,6 +295,8 @@ class RigoServiceController(GObject.Object):
raise LocalActivityStates.AlreadyReadyError()
if self._local_activity != current_activity:
raise LocalActivityStates.UnbusyFromDifferentActivity()
+ GLib.idle_add(self._bottom_nc.set_activity,
+ LocalActivityStates.READY)
self._local_activity = LocalActivityStates.READY
def local_activity(self):
@@ -298,10 +351,40 @@ class RigoServiceController(GObject.Object):
self._resources_unlock_request_signal,
dbus_interface=self.DBUS_INTERFACE)
+ # RigoDaemon Entropy Resources lock requests
+ self.__entropy_bus.connect_to_signal(
+ self._RESOURCES_LOCK_REQUEST_SIGNAL,
+ self._resources_lock_request_signal,
+ dbus_interface=self.DBUS_INTERFACE)
+
+ # RigoDaemon tells 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)
+
return self.__entropy_bus
- def _repositories_updated_signal(self, result, message,
- token, local_activity):
+ ### GOBJECT EVENTS
+
+ def _on_application_request_action(self, apc, app, app_action):
+ """
+ This event comes from ApplicationViewController notifying
+ that user would like to schedule the given action for App.
+ "app" is an Application object, "app_action" is an AppActions
+ enum value.
+ """
+ if const_debug_enabled():
+ const_debug_write(
+ __name__,
+ "_on_application_request_action: "
+ "%s -> %s" % (app, app_action))
+ self.application_request(app, app_action)
+
+ ### DBUS SIGNALS
+
+ def _repositories_updated_signal(self, result, message):
"""
Signal coming from RigoDaemon notifying us that repositories have
been updated.
@@ -324,33 +407,20 @@ class RigoServiceController(GObject.Object):
"already consumed")
return
- # Since we might sleep here, and we're in the
- # MainThread, better spawning a new thread
- # The sleep is inside _scale_down (wait on a
- # semaphore)
- def _scale_down():
- # we don't expect to fail here, it would
- # mean programming error.
- self.unbusy(local_activity)
+ local_activity = LocalActivityStates.UPDATING_REPOSITORIES
+ # we don't expect to fail here, it would
+ # mean programming error.
+ self.unbusy(local_activity)
- activity = DaemonActivityStates.UPDATING_REPOSITORIES
- self._scale_down(activity, token)
- self._release_local_resources()
+ # 1 -- ACTIVITY CRIT :: OFF
+ self._activity_rwsem.writer_release()
- # 1 -- ACTIVITY CRIT :: OFF
- self._activity_rwsem.writer_release()
+ self.emit("repositories-updated",
+ result, message)
- GLib.idle_add(self.emit, "repositories-updated",
- result, message)
- if const_debug_enabled():
- const_debug_write(
- __name__,
- "_repositories_updated_signal: repositories-updated")
-
- task = ParallelTask(_scale_down)
- task.name = "RepositoriesUpdatedSignalScalerDown"
- task.daemon = True
- task.start()
+ const_debug_write(
+ __name__,
+ "_repositories_updated_signal: repositories-updated")
def _output_signal(self, text, header, footer, back, importance, level,
count_c, count_t, percent):
@@ -433,6 +503,44 @@ class RigoServiceController(GObject.Object):
self._entropy_bus,
dbus_interface=self.DBUS_INTERFACE).pong()
+ def _resources_lock_request_signal(self, activity):
+ """
+ RigoDaemon is asking us to acquire a shared Entropy Resources
+ lock. First we check if we have released it.
+ """
+ const_debug_write(
+ __name__,
+ "_resources_lock_request_signal: "
+ "called, with remote activity: %s" % (activity,))
+
+ def _resources_lock():
+ const_debug_write(
+ __name__,
+ "_resources_lock_request_signal._resources_lock: "
+ "enter (sleep)")
+
+ self._entropy.lock_resources(
+ blocking=True,
+ shared=True)
+ self._release_local_resources()
+
+ const_debug_write(
+ __name__,
+ "_resources_lock_request_signal._resources_lock: "
+ "regained shared lock")
+
+ # it's a muted, ask the MainLoop to release it
+ # or it will explode
+ GLib.idle_add(self._resources_locked.release)
+
+ # check if we actually released them
+ locked = self._resources_locked.acquire(False)
+ if not locked:
+ task = ParallelTask(_resources_lock)
+ task.name = "ResourceLockAfterRelease"
+ task.daemon = True
+ task.start()
+
def _resources_unlock_request_signal(self, activity):
"""
RigoDaemon is asking us to release our Entropy Resources Lock.
@@ -446,7 +554,12 @@ class RigoServiceController(GObject.Object):
if activity == DaemonActivityStates.UPDATING_REPOSITORIES:
# did we ask that or is it another client?
- if self.local_activity() == LocalActivityStates.READY:
+ local_activity = self.local_activity()
+ if local_activity == LocalActivityStates.READY:
+ locked = self._resources_locked.acquire(False)
+ if locked:
+ self._entropy.unlock_resources()
+
# another client, bend over XD
# LocalActivityStates value will be atomically
# switched in the above thread.
@@ -461,13 +574,63 @@ class RigoServiceController(GObject.Object):
__name__,
"_resources_unlock_request_signal: "
"somebody called repo update, starting here too")
- else:
+ elif local_activity == \
+ LocalActivityStates.UPDATING_REPOSITORIES:
+ locked = self._resources_locked.acquire(False)
+ if locked:
+ self._entropy.unlock_resources()
+
+ self._entropy.unlock_resources()
+
const_debug_write(
__name__,
"_resources_unlock_request_signal: "
"it's been us calling repositories update")
# it's been us calling it, ignore request
return
+ else:
+ const_debug_write(
+ __name__,
+ "_resources_unlock_request_signal: "
+ "not accepting RigoDaemon resources unlock request, "
+ "local activity: %s" % (local_activity,))
+
+ 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.
+ """
+ const_debug_write(
+ __name__,
+ "_activity_started_signal: "
+ "called, with remote activity: %s" % (activity,))
+ # reset please wait notification then
+ self._please_wait(None)
+
+ ### GP PUBLIC METHODS
+
+ def application_request(self, app, app_action):
+ """
+ Start Application Action (install/remove).
+ """
+ task = ParallelTask(self._application_request,
+ app, app_action)
+ task.name = "ApplicationRequest{%s, %s}" % (
+ app, app_action,)
+ task.daemon = True
+ task.start()
+
+ def update_repositories(self, repositories, force):
+ """
+ Start Entropy Repositories Update
+ """
+ task = ParallelTask(self._update_repositories,
+ repositories, force)
+ task.name = "UpdateRepositoriesThread"
+ task.daemon = True
+ task.start()
def activity(self):
"""
@@ -514,21 +677,6 @@ class RigoServiceController(GObject.Object):
"""
GLib.idle_add(self.output_test)
- def _connect(self):
- """
- Inform RigoDaemon that a new Client is now connected.
- This MUST be called after Entropy Resources Lock is
- acquired in exclusive mode.
- """
- with self._connected_mutex:
- if self._connected:
- return
- # inform daemon that a new instance is now connected
- dbus.Interface(
- self._entropy_bus,
- dbus_interface=self.DBUS_INTERFACE).connect()
- self._connected = True
-
def _release_local_resources(self):
"""
Release all the local resources (like repositories)
@@ -597,6 +745,10 @@ class RigoServiceController(GObject.Object):
action_id = None
if daemon_activity == DaemonActivityStates.UPDATING_REPOSITORIES:
action_id = PolicyActions.UPDATE_REPOSITORIES
+ elif daemon_activity == DaemonActivityStates.MANAGING_APPLICATIONS:
+ action_id = PolicyActions.MANAGE_APPLICATIONS
+ elif daemon_activity == DaemonActivityStates.UPGRADING_SYSTEM:
+ action_id = PolicyActions.UPGRADE_SYSTEM
if action_id is None:
raise AttributeError("unsupported daemon activity")
@@ -614,111 +766,23 @@ class RigoServiceController(GObject.Object):
def _scale_up(self, activity):
"""
- Acquire (in blocking mode) the Entropy Resources Lock
- in exclusive mode. Scale up privileges, and ask for
- root password if not done yet.
+ Make sure User is authorized to perform a privileged
+ operation.
"""
granted = self._authorize(activity)
if not granted:
- return False
-
- acquired_sem = Semaphore(0)
-
- const_debug_write(__name__, "RigoServiceController: "
- "_scale_up: enter")
-
- def _acquirer():
const_debug_write(__name__, "RigoServiceController: "
- "_scale_up: acquired!")
- acquired_sem.release()
-
- # start the rendezvous
- sig_match = self._entropy_bus.connect_to_signal(
- self._EXCLUSIVE_ACQUIRED_SIGNAL,
- _acquirer,
- dbus_interface=self.DBUS_INTERFACE)
-
- accepted = dbus.Interface(
- self._entropy_bus,
- dbus_interface=self.DBUS_INTERFACE
- ).acquire_exclusive(activity)
- if not accepted:
- def _notify():
- if self._nc is not None:
- box = self.ServiceNotificationBox(
- _("Another activity is currently in progress"),
- Gtk.MessageType.ERROR)
- box.add_destroy_button(_("K thanks"))
- self._nc.append(box)
- GLib.idle_add(_notify)
+ "_scale_up: abort")
return False
- self._entropy.unlock_resources()
- # lock down UI here and show a please wait
- # state, or the user won't understand what's happening
- # we assume we already have the activity_rwsem writer
- # lock here
- ms = _("Waiting for Entropy Resources availability, please wait")
+ ms = _("Waiting for RigoDaemon, please wait...")
+ # this will be reset when activity_started() arrives
self._please_wait(ms)
- try:
- acquired_sem.acquire() # CANBLOCK
- finally:
- self._please_wait(None)
- sig_match.remove()
-
- # we successfully passed the resource to RigoDaemon
- self._connect()
const_debug_write(__name__, "RigoServiceController: "
"_scale_up: leave")
return True
- def _scale_down(self, activity, release_exclusive_token):
- """
- Release RigoDaemon Entropy Resources and regain
- control here.
- """
- acquired_sem = Semaphore(0)
- # start the rendezvous
-
- const_debug_write(__name__, "RigoServiceController: "
- "_scale_down: enter, for activity: %s" % (
- activity,))
-
- def _acquirer(sem):
- const_debug_write(__name__, "RigoServiceController: "
- "_scale_down._acquirer: enter")
- self._entropy.lock_resources(
- blocking=True,
- shared=True)
- const_debug_write(__name__, "RigoServiceController: "
- "_scale_down._acquirer: leave")
- sem.release()
-
- task = ParallelTask(_acquirer, acquired_sem)
- task.name = "RigoDaemonResourcesReleaser"
- task.daemon = True
- task.start()
-
- # call this unconditionally and ignore any error.
- # multiple calls are harmless since the token
- # ensures that we actually release locks only once,
- # without the risk of running into race conditions
- accepted = dbus.Interface(
- self._entropy_bus,
- dbus_interface=self.DBUS_INTERFACE
- ).release_exclusive(
- activity,
- release_exclusive_token)
-
- # ignore accepted, unbusy() protects us from
- # releasing during other activities.
- acquired_sem.acquire()
- # back with shared lock!
-
- const_debug_write(__name__, "RigoServiceController: "
- "_scale_down: leave")
-
def _update_repositories(self, repositories, force,
master=True):
"""
@@ -728,10 +792,7 @@ class RigoServiceController(GObject.Object):
# 1 -- ACTIVITY CRIT :: ON
self._activity_rwsem.writer_acquire() # CANBLOCK
- if master:
- local_activity = LocalActivityStates.UPDATING_REPOSITORIES_MASTER
- else:
- local_activity = LocalActivityStates.UPDATING_REPOSITORIES_SLAVE
+ local_activity = LocalActivityStates.UPDATING_REPOSITORIES
try:
self.busy(local_activity)
# will be unlocked when we get the signal back
@@ -741,6 +802,12 @@ class RigoServiceController(GObject.Object):
# 1 -- ACTIVITY CRIT :: OFF
self._activity_rwsem.writer_release()
return
+ except LocalActivityStates.SameError:
+ const_debug_write(__name__, "_update_repositories: "
+ "LocalActivityStates.SameError!")
+ # 1 -- ACTIVITY CRIT :: OFF
+ self._activity_rwsem.writer_release()
+ return
if master:
scaled = self._scale_up(
@@ -750,16 +817,23 @@ class RigoServiceController(GObject.Object):
# 1 -- ACTIVITY CRIT :: OFF
self._activity_rwsem.writer_release()
return
- else:
- # if we don't need to scale, just unlock
- # local resources.
- # No need to connect() because other clients have
- # already done that
- self._entropy.unlock_resources()
- self._update_repositories_unlocked(
+ accepted = self._update_repositories_unlocked(
repositories, force, master)
+ if not accepted:
+ self.unbusy(local_activity)
+ # 1 -- ACTIVITY CRIT :: OFF
+ self._activity_rwsem.writer_release()
+
+ def _notify():
+ box = self.ServiceNotificationBox(
+ _("Another activity is currently in progress"),
+ Gtk.MessageType.ERROR)
+ box.add_destroy_button(_("K thanks"))
+ self._nc.append(box)
+ GLib.idle_add(_notify)
+
def _update_repositories_unlocked(self, repositories, force,
master):
"""
@@ -787,21 +861,16 @@ class RigoServiceController(GObject.Object):
const_debug_write(__name__, "RigoServiceController: "
"rigo UI now locked!")
- if master:
- loc_activity = LocalActivityStates.UPDATING_REPOSITORIES_MASTER
- else:
- loc_activity = LocalActivityStates.UPDATING_REPOSITORIES_SLAVE
-
signal_sem = Semaphore(1)
- def _repositories_updated_signal(result, message, token):
+ def _repositories_updated_signal(result, message):
if not signal_sem.acquire(False):
# already called, no need to call again
return
# this is done in order to have it called
# only once by two different code paths
self._repositories_updated_signal(
- result, message, token, loc_activity)
+ result, message)
with self._registered_signals_mutex:
# connect our signal
@@ -824,8 +893,9 @@ class RigoServiceController(GObject.Object):
self._terminal.reset()
self._release_local_resources()
+ accepted = True
if master:
- dbus.Interface(
+ accepted = dbus.Interface(
self._entropy_bus,
dbus_interface=self.DBUS_INTERFACE
).update_repositories(repositories, force)
@@ -834,6 +904,8 @@ class RigoServiceController(GObject.Object):
self._update_repositories_signal_check(
sig_match, signal_sem)
+ return accepted
+
def _update_repositories_signal_check(self, sig_match, signal_sem):
"""
Called via _update_repositories_unlocked() in order to handle
@@ -863,16 +935,63 @@ class RigoServiceController(GObject.Object):
GLib.idle_add(self._repositories_updated_signal,
0, "", activity)
- def update_repositories(self, repositories, force):
+ def _application_request(self, app, app_action, master=True):
"""
- Local method used to start Entropy repositories
- update.
+ Forward Application Request (install or remove) to RigoDaemon.
+ Make sure there isn't any other ongoing activity.
+ """
+ # Need to serialize access to this method because
+ # we're going to acquire several resources in a non-atomic
+ # way wrt access to this method.
+ with self._application_request_mutex:
+
+ acquire_writer = True
+ # since we need to writer_acquire(), which is blocking
+ # better try to allocate the local activity first
+ local_activity = LocalActivityStates.MANAGING_APPLICATIONS
+ try:
+ self.busy(local_activity)
+ except LocalActivityStates.BusyError:
+ const_debug_write(__name__, "_application_request: "
+ "LocalActivityStates.BusyError!")
+ # doing other stuff, cannot go ahead
+ return
+ except LocalActivityStates.SameError:
+ const_debug_write(__name__, "_application_request: "
+ "LocalActivityStates.SameError, "
+ "no need to acquire writer")
+ # we're already doing this activity, do not acquire
+ # activity_rwsem
+ acquire_writer = False
+
+ if acquire_writer:
+ # 2 -- ACTIVITY CRIT :: ON
+ const_debug_write(__name__, "_application_request: "
+ "about to acquire writer end of "
+ "activity rwsem")
+ self._activity_rwsem.writer_acquire() # CANBLOCK
+
+ # acquire_exclusive should be removed and the same
+ # done by RigoDaemon, see the other window
+ """
+ if master:
+ scaled = self._scale_up(
+ DaemonActivityStates.MANAGING_APPLICATIONS)
+ if not scaled:
+ self.unbusy(local_activity)
+ # 1 -- ACTIVITY CRIT :: OFF
+ self._activity_rwsem.writer_release()
+ return
+ else:
+ # if we don't need to scale, just unlock
+ # local resources.
+ # No need to connect() because other clients have
+ # already done that
+ self._entropy.unlock_resources()
+
+ self._update_repositories_unlocked(
+ repositories, force, master)
"""
- task = ParallelTask(self._update_repositories,
- repositories, force)
- task.name = "UpdateRepositoriesThread"
- task.daemon = True
- task.start()
class WorkViewController(GObject.Object):
@@ -1029,6 +1148,85 @@ class WorkViewController(GObject.Object):
class NotificationViewController(GObject.Object):
+ """
+ Base Class for NotificationBox Controller.
+ """
+
+ def __init__(self, notification_box):
+ GObject.Object.__init__(self)
+ self._box = notification_box
+ self._context_id_map = {}
+
+ def setup(self):
+ """
+ Controller Setup code
+ """
+
+ def append(self, box, timeout=None, context_id=None):
+ """
+ Append a notification to the Notification area.
+ context_id is used to automatically drop any other
+ notification exposing the same context identifier.
+ """
+ context_id = box.get_context_id()
+ if context_id is not None:
+ old_box = self._context_id_map.get(context_id)
+ if old_box is not None:
+ old_box.destroy()
+ self._context_id_map[context_id] = box
+ box.render()
+ self._box.pack_start(box, False, False, 0)
+ box.show()
+ self._box.show()
+ if timeout is not None:
+ GLib.timeout_add_seconds(timeout, self.remove, box)
+
+ def append_safe(self, box, timeout=None):
+ """
+ Thread-safe version of append().
+ """
+ def _append():
+ self.append(box, timeout=timeout)
+ GLib.idle_add(_append)
+
+ def remove(self, box):
+ """
+ Remove a NotificationBox from this notification
+ area, if there.
+ """
+ if box in self._box.get_children():
+ context_id = box.get_context_id()
+ if context_id is not None:
+ self._context_id_map.pop(context_id, None)
+ box.destroy()
+
+ def remove_safe(self, box):
+ """
+ Thread-safe version of remove().
+ """
+ GLib.idle_add(self.remove, box)
+
+ def clear(self, managed=True):
+ """
+ Clear all the notifications.
+ """
+ for child in self._box.get_children():
+ if child.is_managed() and not managed:
+ continue
+ context_id = child.get_context_id()
+ if context_id is not None:
+ self._context_id_map.pop(context_id, None)
+ child.destroy()
+
+ def clear_safe(self, managed=True):
+ """
+ Thread-safe version of clear().
+ """
+ GLib.idle_add(self.clear, managed)
+
+
+class UpperNotificationViewController(NotificationViewController):
+
"""
Notification area widget controller.
This class features the handling of some built-in
@@ -1036,20 +1234,24 @@ class NotificationViewController(GObject.Object):
but also accepts external NotificationBox instances as well.
"""
- def __init__(self, activity_rwsem, entropy_client, entropy_ws, rigo_service,
- avc, notification_box):
- GObject.Object.__init__(self)
+ def __init__(self, activity_rwsem, entropy_client, entropy_ws,
+ rigo_service, avc, notification_box):
+
+ NotificationViewController.__init__(
+ self, notification_box)
+
+ self._avc = avc
+ self._service = rigo_service
self._activity_rwsem = activity_rwsem
self._entropy = entropy_client
self._entropy_ws = entropy_ws
- self._service = rigo_service
- self._avc = avc
- self._box = notification_box
self._updates = None
self._security_updates = None
- self._context_id_map = {}
def setup(self):
+ """
+ Reimplemented from NotificationViewController.
+ """
GLib.timeout_add_seconds(1, self._calculate_updates)
GLib.idle_add(self._check_connectivity)
@@ -1164,8 +1366,6 @@ class NotificationViewController(GObject.Object):
"""
# FIXME, lxnay complete
print("On Upgrade Request Received", args)
- # FIXME, this is for testing, REMOVE !!!!
- self._service.update_repositories([], True)
def _on_update(self, box):
"""
@@ -1181,67 +1381,78 @@ class NotificationViewController(GObject.Object):
"""
self._avc.set_many_safe(self._updates)
- def append(self, box, timeout=None, context_id=None):
- """
- Append a notification to the Notification area.
- context_id is used to automatically drop any other
- notification exposing the same context identifier.
- """
- context_id = box.get_context_id()
- if context_id is not None:
- old_box = self._context_id_map.get(context_id)
- if old_box is not None:
- old_box.destroy()
- self._context_id_map[context_id] = box
- box.render()
- self._box.pack_start(box, False, False, 0)
- box.show()
- self._box.show()
- if timeout is not None:
- GLib.timeout_add_seconds(timeout, self.remove, box)
+class BottomNotificationViewController(NotificationViewController):
- def append_safe(self, box, timeout=None):
- """
- Thread-safe version of append().
- """
- def _append():
- self.append(box, timeout=timeout)
- GLib.idle_add(_append)
+ """
+ Bottom Notification Area.
+ This area is only used to show Activity controls to User.
+ For example, during repositories update, this area just
+ shows one notification box stating that the above activity is in
+ progress, making possible to switch to the Work View anytime.
+ """
- def remove(self, box):
- """
- Remove a NotificationBox from this notification
- area, if there.
- """
- if box in self._box.get_children():
- context_id = box.get_context_id()
- if context_id is not None:
- self._context_id_map.pop(context_id, None)
- box.destroy()
+ UNIQUE_CONTEXT_ID = "BottomNotificationBoxContextId"
- def remove_safe(self, box):
- """
- Thread-safe version of remove().
- """
- GLib.idle_add(self.remove, box)
+ __gsignals__ = {
+ "show-work-view" : (GObject.SignalFlags.RUN_LAST,
+ None,
+ tuple(),
+ ),
+ }
- def clear(self, managed=True):
- """
- Clear all the notifications.
- """
- for child in self._box.get_children():
- if child.is_managed() and not managed:
- continue
- context_id = child.get_context_id()
- if context_id is not None:
- self._context_id_map.pop(context_id, None)
- child.destroy()
+ def __init__(self, notification_box):
- def clear_safe(self, managed=True):
+ NotificationViewController.__init__(
+ self, notification_box)
+
+ def _on_work_view_show(self, widget):
"""
- Thread-safe version of clear().
+ User is asking to show the Work View.
"""
- GLib.idle_add(self.clear, managed)
+ self.emit("show-work-view")
+
+ def _append_repositories_update(self):
+ """
+ Add a NotificationBox related to Repositories Update
+ Activity in progress.
+ """
+ msg = _("Repositories Update in progress...")
+ box = NotificationBox(
+ msg, message_type=Gtk.MessageType.INFO,
+ context_id=self.UNIQUE_CONTEXT_ID)
+ box.add_button(_("Show me"), self._on_work_view_show)
+ self.append(box)
+
+ def _append_installing_apps(self):
+ """
+ Add a NotificationBox related to Applications Install
+ Activity in progress.
+ """
+ msg = _("Application Install in progress...")
+ box = NotificationBox(
+ msg, message_type=Gtk.MessageType.INFO,
+ context_id=self.UNIQUE_CONTEXT_ID)
+ box.add_button(_("Show me"), self._on_work_view_show)
+ self.append(box)
+
+ def set_activity(self, local_activity):
+ """
+ Set a current local Activity, showing the
+ most appropriate NotificationBox.
+ This method must be called from the MainLoop.
+ """
+ if local_activity == LocalActivityStates.READY:
+ self.clear()
+ return
+
+ if local_activity == LocalActivityStates.UPDATING_REPOSITORIES:
+ self._append_repositories_update()
+ return
+ elif local_activity == LocalActivityStates.MANAGING_APPLICATIONS:
+ self._append_installing_apps()
+ return
+
+ raise NotImplementedError()
class Rigo(Gtk.Application):
@@ -1314,14 +1525,17 @@ class Rigo(Gtk.Application):
self._search_entry = self._builder.get_object("searchEntry")
self._static_view = self._builder.get_object("staticViewVbox")
self._notification = self._builder.get_object("notificationBox")
+ self._bottom_notification = \
+ self._builder.get_object("bottomNotificationBox")
self._work_view = self._builder.get_object("workViewVbox")
self._work_view.set_name("rigo-view")
self._app_view_c = ApplicationViewController(
self._entropy, self._entropy_ws, self._builder)
- self._view = AppTreeView(self._entropy, self._app_view_c, icons, True,
- AppListStore.ICON_SIZE, store=None)
+ self._view = AppTreeView(
+ self._entropy, self._app_view_c, icons,
+ True, AppListStore.ICON_SIZE, store=None)
self._scrolled_view.add(self._view)
self._app_store = AppListStore(
@@ -1357,15 +1571,25 @@ class Rigo(Gtk.Application):
self._avc.connect("view-filled", self._on_view_filled)
self._avc.connect("view-want-change", self._on_view_change)
- self._nc = NotificationViewController(
+ self._nc = UpperNotificationViewController(
self._activity_rwsem, self._entropy,
self._entropy_ws, self._service,
self._avc, self._notification)
+ # Bottom NotificationBox controller.
+ # Bottom notifications are only used for
+ # providing Activity control to User during
+ # the Activity itself.
+ self._bottom_nc = BottomNotificationViewController(
+ self._bottom_notification)
+ self._service.set_bottom_notification_controller(
+ self._bottom_nc)
+
self._app_view_c.set_notification_controller(self._nc)
self._app_view_c.set_applications_controller(self._avc)
self._service.set_applications_controller(self._avc)
+ self._service.set_application_controller(self._app_view_c)
self._service.set_notification_controller(self._nc)
self._service.connect("repositories-updating", self._on_repo_updating)
@@ -1375,6 +1599,8 @@ class Rigo(Gtk.Application):
self._service, self._work_view)
self._service.set_work_controller(self._work_view_c)
+ self._bottom_nc.connect("show-work-view", self._on_show_work_view)
+
def is_ui_locked(self):
"""
Return whether the UI is currently locked.
@@ -1389,6 +1615,13 @@ class Rigo(Gtk.Application):
self._search_entry.set_sensitive(False)
self._change_view_state(state, lock=True)
+ def _on_show_work_view(self, widget):
+ """
+ We've been explicitly asked to switch to WORK_VIEW_STATE
+ """
+ self._change_view_state(RigoViewStates.WORK_VIEW_STATE,
+ _ignore_lock=True)
+
def _on_repo_updated(self, widget, result, message):
"""
Emitted by RigoServiceController when we're allowed to
@@ -1496,14 +1729,14 @@ class Rigo(Gtk.Application):
"""
self._work_view.hide()
- def _change_view_state(self, state, lock=False):
+ def _change_view_state(self, state, lock=False, _ignore_lock=False):
"""
Change Rigo Application UI state.
You can pass a custom widget that will be shown in case
of static view state.
"""
with self._state_mutex:
- if self._current_state_lock:
+ if self._current_state_lock and not _ignore_lock:
const_debug_write(
__name__,
"cannot change view state, UI locked")
@@ -1579,6 +1812,15 @@ class Rigo(Gtk.Application):
Gtk.main_quit()
return
+ if not self._service.service_available():
+ self._show_ok_dialog(
+ None,
+ escape_markup(_("Rigo")),
+ escape_markup(_("RigoDaemon service is not available")))
+ entropy.tools.kill_threads()
+ Gtk.main_quit()
+ return
+
acquired = not self._entropy.wait_resources(
max_lock_count=1,
shared=True)
@@ -1619,7 +1861,7 @@ class Rigo(Gtk.Application):
task.name = "UpdateRepositoriesUnlocked"
task.start()
- elif activity == DaemonActivityStates.INSTALLING_APPLICATION:
+ elif activity == DaemonActivityStates.MANAGING_APPLICATIONS:
# FIXME, jump to WORK_VIEW and show the progress.
msg = _("Background Service is installing Applications")
elif activity == DaemonActivityStates.UPGRADING_SYSTEM:
@@ -1654,6 +1896,7 @@ class Rigo(Gtk.Application):
self._avc.setup()
self._nc.setup()
self._work_view_c.setup()
+ self._service.setup(acquired)
self._window.show()
def run(self):
diff --git a/rigo/test_polkit.py b/rigo/test_polkit.py
new file mode 100644
index 000000000..d346fb4d2
--- /dev/null
+++ b/rigo/test_polkit.py
@@ -0,0 +1,71 @@
+import os
+from threading import Semaphore
+from gi.repository import GLib, Polkit, GObject
+
+class AuthenticationController(object):
+
+ """
+ This class handles User authentication required
+ for privileged activies, like Repository updates
+ and Application management.
+ """
+
+ def __init__(self, mainloop):
+ self._authenticated = False
+ self._authenticated_sem = Semaphore(1)
+ self._mainloop = mainloop
+
+ def authenticate(self, action_id, authentication_callback):
+ """
+ Authenticate current User asking Administrator
+ passwords.
+ authentication_callback is the function that
+ is called after the authentication procedure,
+ providing one boolean argument describing the
+ process result: True for authenticated, False
+ for not authenticated.
+ This method must be called from the MainLoop.
+ If authentication has been already
+ """
+ self._authenticated_sem.acquire()
+ if self._authenticated:
+ try:
+ authentication_callback(True)
+ finally:
+ self._authenticated_sem.release()
+ return
+
+ def _polkit_auth_callback(authority, res, loop):
+ authenticated = False
+ try:
+ result = authority.check_authorization_finish(res)
+ if result.get_is_authorized():
+ authenticated = True
+ elif result.get_is_challenge():
+ authenticated = True
+ except GObject.GError as err:
+ raise err
+ finally:
+ self._authenticated = authenticated
+ self._authenticated_sem.release()
+ authentication_callback(authenticated)
+
+ # authenticated_sem will be released in the callback
+ authority = Polkit.Authority.get()
+ subject = Polkit.UnixProcess.new(os.getppid())
+ authority.check_authorization(
+ subject,
+ action_id,
+ None,
+ Polkit.CheckAuthorizationFlags.ALLOW_USER_INTERACTION,
+ None, # Gio.Cancellable()
+ _polkit_auth_callback,
+ self._mainloop)
+
+def callback(result):
+ print "Auth status", result
+
+mainloop = GLib.MainLoop()
+ctrl = AuthenticationController(mainloop)
+GLib.idle_add(ctrl.authenticate, "org.sabayon.RigoDaemon.update", callback)
+mainloop.run()