[RigoDaemon] feed App Management notes to clients, bump API
Update Rigo as well and let it push the notes to the Terminal Widget (they come from stdout and stderr, sorry)
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
|
||||
"""
|
||||
import os
|
||||
import stat
|
||||
|
||||
# entropy.i18n will pick this up
|
||||
os.environ['ETP_GETTEXT_DOMAIN'] = "rigo"
|
||||
@@ -265,8 +266,10 @@ class FakeOutFile(object):
|
||||
Fake Standard Output / Error file object
|
||||
"""
|
||||
|
||||
def __init__(self, entropy_client):
|
||||
def __init__(self, entropy_client, app_mgmt_mutex, app_mgmt_notes):
|
||||
self._entropy = entropy_client
|
||||
self._app_mgmt_mutex = app_mgmt_mutex
|
||||
self._app_mgmt_notes = app_mgmt_notes
|
||||
self._rfd, self._wfd = os.pipe()
|
||||
task = ParallelTask(self._pusher)
|
||||
task.name = "FakeOutFilePusher"
|
||||
@@ -282,6 +285,16 @@ class FakeOutFile(object):
|
||||
if err.errno == errno.EINTR:
|
||||
continue
|
||||
raise
|
||||
# record to App Management Log, if enabled
|
||||
with self._app_mgmt_mutex:
|
||||
fobj = self._app_mgmt_notes['fobj']
|
||||
if fobj is not None:
|
||||
try:
|
||||
fobj.write(chunk)
|
||||
except (OSError, IOError) as err:
|
||||
write_output("_pusher thread: "
|
||||
"cannot write to app log: "
|
||||
"%s" % (repr(err),))
|
||||
self._entropy.output(chunk, _raw=True)
|
||||
|
||||
def close(self):
|
||||
@@ -376,7 +389,7 @@ class RigoDaemonService(dbus.service.Object):
|
||||
BUS_NAME = DbusConfig.BUS_NAME
|
||||
OBJECT_PATH = DbusConfig.OBJECT_PATH
|
||||
|
||||
API_VERSION = 3
|
||||
API_VERSION = 4
|
||||
|
||||
"""
|
||||
RigoDaemon is the dbus service Object in charge of executing
|
||||
@@ -548,9 +561,18 @@ class RigoDaemonService(dbus.service.Object):
|
||||
self._deferred_shutdown = False
|
||||
self._deferred_shutdown_mutex = Lock()
|
||||
|
||||
self._app_mgmt_mutex = Lock()
|
||||
self._app_mgmt_notes = {
|
||||
'fobj': None,
|
||||
'path': None
|
||||
}
|
||||
|
||||
Entropy.set_daemon(self)
|
||||
self._entropy = Entropy()
|
||||
self._fakeout = FakeOutFile(self._entropy)
|
||||
self._fakeout = FakeOutFile(
|
||||
self._entropy,
|
||||
self._app_mgmt_mutex,
|
||||
self._app_mgmt_notes)
|
||||
|
||||
executable_path = sys.argv[0]
|
||||
write_output(
|
||||
@@ -1177,6 +1199,41 @@ class RigoDaemonService(dbus.service.Object):
|
||||
# put it back
|
||||
self._action_queue_waiter.release()
|
||||
else:
|
||||
|
||||
# before unbusy, read App Management Notes
|
||||
with self._app_mgmt_mutex:
|
||||
# 0o644
|
||||
perms = stat.S_IREAD | stat.S_IWRITE \
|
||||
| stat.S_IRGRP | stat.S_IROTH
|
||||
|
||||
fobj = self._app_mgmt_notes['fobj']
|
||||
app_log_path = self._app_mgmt_notes['path']
|
||||
if fobj is not None:
|
||||
try:
|
||||
fobj.close()
|
||||
except OSError:
|
||||
write_output(
|
||||
"_action_queue_finally: "
|
||||
"unexpected close() error %s" % (
|
||||
repr(err),))
|
||||
self._app_mgmt_notes['fobj'] = None
|
||||
|
||||
# root is already owning it
|
||||
try:
|
||||
os.chmod(app_log_path, perms)
|
||||
except OSError as err:
|
||||
if err.errno == errno.ENOENT:
|
||||
# wtf, file vanished?
|
||||
app_log_path = ""
|
||||
elif err.errno == errno.EPERM:
|
||||
# somebody changed the permissions
|
||||
app_log_path = ""
|
||||
else:
|
||||
write_output(
|
||||
"_action_queue_finally: "
|
||||
"unexpected error %s" % (repr(err),))
|
||||
app_log_path = ""
|
||||
|
||||
try:
|
||||
self._unbusy(activity)
|
||||
except ActivityStates.AlreadyAvailableError:
|
||||
@@ -1193,7 +1250,7 @@ class RigoDaemonService(dbus.service.Object):
|
||||
GLib.idle_add(
|
||||
self.activity_completed, activity, success)
|
||||
GLib.idle_add(
|
||||
self.applications_managed, outcome)
|
||||
self.applications_managed, outcome, app_log_path)
|
||||
self._maybe_signal_configuration_updates()
|
||||
|
||||
is_app = True
|
||||
@@ -2403,8 +2460,10 @@ class RigoDaemonService(dbus.service.Object):
|
||||
self._enqueue_action_busy_hold_sem.acquire()
|
||||
try:
|
||||
activity = ActivityStates.MANAGING_APPLICATIONS
|
||||
busied = False
|
||||
try:
|
||||
self._busy(activity)
|
||||
busied = True
|
||||
except ActivityStates.BusyError:
|
||||
# I am already busy doing other stuff, cannot
|
||||
# satisfy request
|
||||
@@ -2416,6 +2475,39 @@ class RigoDaemonService(dbus.service.Object):
|
||||
"already busy, just enqueue",
|
||||
debug=True)
|
||||
|
||||
if busied:
|
||||
# Setup Application Management
|
||||
# Install/Remove notes
|
||||
tmp_fd, tmp_path = tempfile.mkstemp(
|
||||
prefix="RigoDaemonAppMgmt",
|
||||
suffix=".notes")
|
||||
with self._app_mgmt_mutex:
|
||||
fobj = self._app_mgmt_notes['fobj']
|
||||
path = self._app_mgmt_notes['path']
|
||||
if fobj is not None:
|
||||
try:
|
||||
fobj.close()
|
||||
except (OSError, IOError):
|
||||
write_output(
|
||||
"enqueue_application_action: "
|
||||
"busied, but cannot close previous fd")
|
||||
if path is not None:
|
||||
try:
|
||||
os.remove(path)
|
||||
except (OSError, IOError):
|
||||
write_output(
|
||||
"enqueue_application_action: "
|
||||
"busied, but cannot remove previous path")
|
||||
try:
|
||||
fobj = os.fdopen(tmp_fd, "w")
|
||||
except OSError as err:
|
||||
write_output(
|
||||
"enqueue_application_action: "
|
||||
"cannot open tmp_fd: %s" % (repr(err),))
|
||||
fobj = None
|
||||
self._app_mgmt_notes['fobj'] = fobj
|
||||
self._app_mgmt_notes['path'] = tmp_path
|
||||
|
||||
task = ParallelTask(
|
||||
self._enqueue_application_action_internal,
|
||||
pid, package_id, repository_id, package_path,
|
||||
@@ -2902,8 +2994,8 @@ class RigoDaemonService(dbus.service.Object):
|
||||
" %s" % (locals(),), debug=True)
|
||||
|
||||
@dbus.service.signal(dbus_interface=BUS_NAME,
|
||||
signature='s')
|
||||
def applications_managed(self, outcome):
|
||||
signature='ss')
|
||||
def applications_managed(self, outcome, app_log_path):
|
||||
"""
|
||||
Enqueued Application actions have been completed.
|
||||
"""
|
||||
|
||||
@@ -18,8 +18,9 @@ You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
|
||||
import sys
|
||||
import time
|
||||
import codecs
|
||||
from threading import Lock, Semaphore, current_thread
|
||||
from collections import deque
|
||||
|
||||
@@ -178,7 +179,7 @@ class RigoServiceController(GObject.Object):
|
||||
_UNAVAILABLE_REPOSITORIES_SIGNAL = "unavailable_repositories"
|
||||
_OLD_REPOSITORIES_SIGNAL = "old_repositories"
|
||||
_NOTICEBOARDS_AVAILABLE_SIGNAL = "noticeboards_available"
|
||||
_SUPPORTED_APIS = [3]
|
||||
_SUPPORTED_APIS = [4]
|
||||
|
||||
def __init__(self, rigo_app, activity_rwsem,
|
||||
entropy_client, entropy_ws):
|
||||
@@ -709,7 +710,8 @@ class RigoServiceController(GObject.Object):
|
||||
box.add_button(_("Show me"), _show_me)
|
||||
self._nc.append(box)
|
||||
|
||||
def _applications_managed_signal(self, outcome, local_activity):
|
||||
def _applications_managed_signal(self, outcome, app_log_path,
|
||||
local_activity):
|
||||
"""
|
||||
Signal coming from RigoDaemon notifying us that the
|
||||
MANAGING_APPLICATIONS is over.
|
||||
@@ -759,6 +761,28 @@ class RigoServiceController(GObject.Object):
|
||||
if outcome != DaemonAppTransactionOutcome.SUCCESS:
|
||||
self._notify_app_management_outcome(None, outcome)
|
||||
|
||||
# Send Application Management notes to Terminal.
|
||||
if app_log_path:
|
||||
enc = etpConst['conf_encoding']
|
||||
app_notes = None
|
||||
try:
|
||||
with codecs.open(app_log_path, encoding=enc) as log_f:
|
||||
app_notes = log_f.read()
|
||||
except (IOError, OSError,) as err:
|
||||
const_debug_write(
|
||||
__name__,
|
||||
"_applications_managed_signal: "
|
||||
"cannot read app_log_path: %s" % (repr(err),))
|
||||
if app_notes is not None:
|
||||
if len(app_notes) > 3: # chars
|
||||
if self._terminal is not None:
|
||||
self._terminal.reset()
|
||||
self._output_signal(
|
||||
app_notes, None, None, False, 0, "info",
|
||||
0, 0, False, True)
|
||||
if self._wc is not None:
|
||||
self._wc.expand_terminal()
|
||||
|
||||
# we don't expect to fail here, it would
|
||||
# mean programming error.
|
||||
self.unbusy(local_activity)
|
||||
@@ -2154,14 +2178,15 @@ class RigoServiceController(GObject.Object):
|
||||
|
||||
signal_sem = Semaphore(1)
|
||||
|
||||
def _applications_managed_signal(outcome):
|
||||
def _applications_managed_signal(outcome, app_log_path):
|
||||
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._applications_managed_signal(
|
||||
outcome, LocalActivityStates.MANAGING_APPLICATIONS)
|
||||
outcome, app_log_path,
|
||||
LocalActivityStates.MANAGING_APPLICATIONS)
|
||||
|
||||
with self._registered_signals_mutex:
|
||||
# connect our signal
|
||||
@@ -2252,7 +2277,7 @@ class RigoServiceController(GObject.Object):
|
||||
# callback in random threads.
|
||||
GLib.idle_add(self._applications_managed_signal,
|
||||
DaemonAppTransactionOutcome.SUCCESS,
|
||||
local_activity)
|
||||
"", local_activity)
|
||||
|
||||
def _package_install_request(self, package_path, simulate=False):
|
||||
"""
|
||||
@@ -2492,14 +2517,15 @@ class RigoServiceController(GObject.Object):
|
||||
|
||||
signal_sem = Semaphore(1)
|
||||
|
||||
def _applications_managed_signal(outcome):
|
||||
def _applications_managed_signal(outcome, app_log_path):
|
||||
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._applications_managed_signal(
|
||||
outcome, LocalActivityStates.UPGRADING_SYSTEM)
|
||||
outcome, app_log_path,
|
||||
LocalActivityStates.UPGRADING_SYSTEM)
|
||||
|
||||
with self._registered_signals_mutex:
|
||||
# connect our signal
|
||||
|
||||
Reference in New Issue
Block a user