mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-21 07:55:00 +00:00
dbus: Add new module for D-Bus services
- Implement listening for CacheUpdated notification. - Configuration to allow only root to trigger the notification. - Trigger the notification from an apt update hook. - Retrieve the list of packages available for upgrade and print them to log. - Add dependency on libglib2.0-bin for the gdbus command line tool. Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
parent
94255806cf
commit
2df02b059c
8
data/etc/apt/apt.conf.d/20freedombox
Normal file
8
data/etc/apt/apt.conf.d/20freedombox
Normal file
@ -0,0 +1,8 @@
|
||||
// This configuration is installed by FreedomBox.
|
||||
//
|
||||
// When Apt's cache is updated (i.e. apt-cache update) notify FreedomBox service
|
||||
// via it's D-Bus API. FreedomBox may then handle upgrade of some packages.
|
||||
//
|
||||
APT::Update::Post-Invoke-Success {
|
||||
"/usr/bin/test -S /var/run/dbus/system_bus_socket && /usr/bin/gdbus call --system --dest org.freedombox.Service --object-path /org/freedombox/Service/PackageHandler --timeout 10 --method org.freedombox.Service.PackageHandler.CacheUpdated 2> /dev/null > /dev/null; /bin/echo > /dev/null";
|
||||
};
|
||||
19
data/usr/share/dbus-1/system.d/org.freedombox.Service.conf
Normal file
19
data/usr/share/dbus-1/system.d/org.freedombox.Service.conf
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
|
||||
<!--
|
||||
This file is part of FreedomBox.
|
||||
|
||||
Allow only root user to send any notifications to FreedomBox.
|
||||
-->
|
||||
|
||||
<busconfig>
|
||||
|
||||
<policy user="root">
|
||||
<allow own="org.freedombox.Service"/>
|
||||
<allow send_destination="org.freedombox.Service"
|
||||
send_path="/org/freedombox/Service/PackageHandler"/>
|
||||
</policy>
|
||||
|
||||
</busconfig>
|
||||
2
debian/control
vendored
2
debian/control
vendored
@ -70,6 +70,8 @@ Depends:
|
||||
gir1.2-nm-1.0,
|
||||
javascript-common,
|
||||
ldapscripts,
|
||||
# For gdbus used to call hooks into service
|
||||
libglib2.0-bin,
|
||||
libjs-bootstrap,
|
||||
libjs-jquery,
|
||||
libjs-modernizr,
|
||||
|
||||
@ -23,7 +23,7 @@ import sys
|
||||
|
||||
import axes
|
||||
|
||||
from . import (cfg, frontpage, log, menu, module_loader, service, setup,
|
||||
from . import (cfg, dbus, frontpage, log, menu, module_loader, service, setup,
|
||||
web_framework, web_server)
|
||||
|
||||
axes.default_app_config = "plinth.axes_app_config.AppConfig"
|
||||
@ -129,6 +129,7 @@ def adapt_config(arguments):
|
||||
def on_web_server_stop():
|
||||
"""Stop all other threads since web server is trying to exit."""
|
||||
setup.stop()
|
||||
dbus.stop()
|
||||
|
||||
|
||||
def main():
|
||||
@ -177,6 +178,8 @@ def main():
|
||||
|
||||
setup.run_setup_in_background()
|
||||
|
||||
dbus.run()
|
||||
|
||||
web_server.init()
|
||||
web_server.run(on_web_server_stop)
|
||||
|
||||
|
||||
154
plinth/dbus.py
Executable file
154
plinth/dbus.py
Executable file
@ -0,0 +1,154 @@
|
||||
#
|
||||
# This file is part of FreedomBox.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
"""
|
||||
Expose some API over D-Bus.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import threading
|
||||
|
||||
from plinth.utils import import_from_gi
|
||||
|
||||
from . import setup
|
||||
|
||||
glib = import_from_gi('GLib', '2.0')
|
||||
gio = import_from_gi('Gio', '2.0')
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_thread = None
|
||||
_server = None
|
||||
_main_loop = None
|
||||
|
||||
|
||||
class PackageHandler():
|
||||
"""D-Bus service to listen for messages when apt cache is updated."""
|
||||
|
||||
introspection_xml = '''
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node name="/org/freedombox/Service/PackageHandler">
|
||||
<interface name="org.freedombox.Service.PackageHandler">
|
||||
<method name="CacheUpdated"/>
|
||||
</interface>
|
||||
</node>
|
||||
'''
|
||||
|
||||
def register(self, connection):
|
||||
"""Register the object in D-Bus connection."""
|
||||
introspection_data = gio.DBusNodeInfo.new_for_xml(
|
||||
self.introspection_xml)
|
||||
interface_info = gio.DBusNodeInfo.lookup_interface(
|
||||
introspection_data, 'org.freedombox.Service.PackageHandler')
|
||||
connection.register_object('/org/freedombox/Service/PackageHandler',
|
||||
interface_info, self.on_method_call, None,
|
||||
None)
|
||||
|
||||
def on_method_call(self, _connection, _sender, _object_path,
|
||||
_interface_name, method_name, _parameters, invocation):
|
||||
"""Handle method being called.
|
||||
|
||||
No need to check all the incoming parameters as D-Bus will validate all
|
||||
the incoming parameters using introspection data.
|
||||
|
||||
"""
|
||||
if method_name == 'CacheUpdated':
|
||||
self.on_cache_updated()
|
||||
invocation.return_value()
|
||||
|
||||
@staticmethod
|
||||
def on_cache_updated():
|
||||
"""Called when system package cache is updated."""
|
||||
logger.info('Apt package cache updated.')
|
||||
|
||||
# Run in a new thread because we don't want to block the thread running
|
||||
# Glib main loop.
|
||||
threading.Thread(target=setup.on_package_cache_updated).start()
|
||||
|
||||
|
||||
class DBusServer():
|
||||
"""Abstraction over a connection to D-Bus."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the server object."""
|
||||
self.package_handler = None
|
||||
|
||||
def connect(self):
|
||||
"""Connect to bus with well-known name."""
|
||||
gio.bus_own_name(gio.BusType.SYSTEM, 'org.freedombox.Service',
|
||||
gio.BusNameOwnerFlags.NONE, self.on_bus_acquired,
|
||||
self.on_name_acquired, self.on_name_lost)
|
||||
|
||||
def on_bus_acquired(self, connection, name):
|
||||
"""Callback when connection to D-Bus has been acquired."""
|
||||
logger.info('D-Bus connection acquired: %s', name)
|
||||
self.package_handler = PackageHandler()
|
||||
self.package_handler.register(connection)
|
||||
|
||||
@staticmethod
|
||||
def on_name_acquired(_connection, name):
|
||||
"""Callback when service name on D-Bus has been acquired."""
|
||||
logger.info('D-Bus name acquired: %s', name)
|
||||
|
||||
@staticmethod
|
||||
def on_name_lost(_connection, name):
|
||||
"""Callback when service name or DBus connection is closed."""
|
||||
logger.info('D-Bus connection lost: %s', name)
|
||||
|
||||
# XXX: Reconnect after a while
|
||||
#
|
||||
# Such as by doing:
|
||||
# connection.set_exit_on_close(False)
|
||||
# gio.timeout_add(10000, self.connect)
|
||||
#
|
||||
# However, perhaps due to some cleanup issues, reconnection is not
|
||||
# happening if it is does after an incoming method call.
|
||||
#
|
||||
# If D-Bus connection is lost due to daemon restart, FreedomBox service
|
||||
# will receive a SIGTERM and exit. systemd should then restart the
|
||||
# service again.
|
||||
|
||||
|
||||
def run():
|
||||
"""Run a glib main loop forever in a thread."""
|
||||
global _thread
|
||||
_thread = threading.Thread(target=_run)
|
||||
_thread.start()
|
||||
|
||||
|
||||
def stop():
|
||||
"""Exit glib main loop and end the thread."""
|
||||
if _main_loop:
|
||||
logger.info('Exiting main loop for D-Bus services')
|
||||
_main_loop.quit()
|
||||
|
||||
|
||||
def _run():
|
||||
"""Connect to D-Bus and run main loop."""
|
||||
logger.info('Started new thread for D-Bus services')
|
||||
|
||||
global _server
|
||||
_server = DBusServer()
|
||||
_server.connect()
|
||||
|
||||
global _main_loop
|
||||
_main_loop = glib.MainLoop()
|
||||
_main_loop.run()
|
||||
_main_loop = None
|
||||
|
||||
logger.info('D-Bus services thread exited.')
|
||||
@ -37,6 +37,8 @@ _is_first_setup = False
|
||||
is_first_setup_running = False
|
||||
_is_shutting_down = False
|
||||
|
||||
_force_upgrader = None
|
||||
|
||||
|
||||
class Helper(object):
|
||||
"""Helper routines for modules to show progress."""
|
||||
@ -335,3 +337,31 @@ def run_setup_on_modules(module_list, allow_install=True):
|
||||
except Exception as exception:
|
||||
logger.error('Error running setup - %s', exception)
|
||||
raise
|
||||
|
||||
|
||||
class ForceUpgrader():
|
||||
"""Find and upgrade packages by force when conffile prompt is needed."""
|
||||
|
||||
def on_package_cache_updated(self):
|
||||
"""Find an upgrade packages."""
|
||||
packages = self.get_list_of_upgradeable_packages()
|
||||
if packages:
|
||||
logger.info('Packages available for upgrade: %s',
|
||||
', '.join([package.name for package in packages]))
|
||||
|
||||
# XXX: Implement force upgrading of selected packages
|
||||
|
||||
@staticmethod
|
||||
def get_list_of_upgradeable_packages():
|
||||
"""Return list of packages that can be upgraded."""
|
||||
cache = apt.cache.Cache()
|
||||
return [package for package in cache if package.is_upgradable]
|
||||
|
||||
|
||||
def on_package_cache_updated():
|
||||
"""Called by D-Bus service when apt package cache is updated."""
|
||||
global _force_upgrader
|
||||
if not _force_upgrader:
|
||||
_force_upgrader = ForceUpgrader()
|
||||
|
||||
_force_upgrader.on_package_cache_updated()
|
||||
|
||||
18
setup.py
18
setup.py
@ -236,10 +236,11 @@ setuptools.setup(
|
||||
glob.glob('data/etc/apache2/sites-available/*.conf')),
|
||||
('/etc/apache2/includes',
|
||||
glob.glob('data/etc/apache2/includes/*.conf')),
|
||||
('/etc/apt/apt.conf.d',
|
||||
glob.glob('data/etc/apt/apt.conf.d/60unattended-upgrades')),
|
||||
('/etc/avahi/services/',
|
||||
glob.glob('data/etc/avahi/services/*.service')),
|
||||
('/etc/apt/apt.conf.d', [
|
||||
'data/etc/apt/apt.conf.d/60unattended-upgrades',
|
||||
'data/etc/apt/apt.conf.d/20freedombox'
|
||||
]), ('/etc/avahi/services/',
|
||||
glob.glob('data/etc/avahi/services/*.service')),
|
||||
('/etc/ikiwiki', glob.glob('data/etc/ikiwiki/*.setup')),
|
||||
('/etc/NetworkManager/dispatcher.d/', [
|
||||
'data/etc/NetworkManager/dispatcher.d/10-freedombox-batman'
|
||||
@ -247,9 +248,10 @@ setuptools.setup(
|
||||
'data/etc/sudoers.d/plinth'
|
||||
]), ('/lib/systemd/system',
|
||||
glob.glob('data/lib/systemd/system/*.service')),
|
||||
('/lib/systemd/system/mldonkey-server.service.d',
|
||||
['data/lib/systemd/system/mldonkey-server.service.d/freedombox.conf']),
|
||||
('/lib/systemd/system', glob.glob('data/lib/systemd/system/*.timer')),
|
||||
('/lib/systemd/system/mldonkey-server.service.d', [
|
||||
'data/lib/systemd/system/mldonkey-server.service.d/freedombox.conf'
|
||||
]), ('/lib/systemd/system',
|
||||
glob.glob('data/lib/systemd/system/*.timer')),
|
||||
('/etc/mediawiki',
|
||||
glob.glob('data/etc/mediawiki/*.php')), ('/etc/update-motd.d/', [
|
||||
'data/etc/update-motd.d/50-freedombox'
|
||||
@ -265,6 +267,8 @@ setuptools.setup(
|
||||
glob.glob('data/usr/share/augeas/lenses/*.aug')),
|
||||
('/usr/share/augeas/lenses/tests',
|
||||
glob.glob('data/usr/share/augeas/lenses/tests/test_*.aug')),
|
||||
('/usr/share/dbus-1/system.d',
|
||||
glob.glob('data/usr/share/dbus-1/system.d/*.conf')),
|
||||
('/usr/share/pam-configs/',
|
||||
glob.glob('data/usr/share/pam-configs/*-freedombox')),
|
||||
('/etc/fail2ban/jail.d', glob.glob('data/etc/fail2ban/jail.d/*.conf')),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user