From d3b762689990c87d2a88f6d452c5870e604f6e60 Mon Sep 17 00:00:00 2001 From: Luoxueyi Date: Fri, 9 Sep 2022 11:41:40 +0800 Subject: [PATCH] dbus + config manager --- backend/SystemUpdater/ConfigManager.py | 114 ++++++++ backend/SystemUpdater/ConfigManagerDaemon.py | 264 ++++++++++++++++++ .../data/cn.kylinos.UpdaterCfgManager.policy | 25 ++ backend/data/com.kylin.UpdaterCfgManager.conf | 23 ++ ...kylin-system-updater-configmanager.service | 13 + backend/kylin-system-updater-ConfigManager | 81 ++++++ debian/kylin-system-updater.install | 6 + 7 files changed, 526 insertions(+) create mode 100644 backend/SystemUpdater/ConfigManager.py create mode 100644 backend/SystemUpdater/ConfigManagerDaemon.py create mode 100644 backend/data/cn.kylinos.UpdaterCfgManager.policy create mode 100644 backend/data/com.kylin.UpdaterCfgManager.conf create mode 100644 backend/data/kylin-system-updater-configmanager.service create mode 100644 backend/kylin-system-updater-ConfigManager diff --git a/backend/SystemUpdater/ConfigManager.py b/backend/SystemUpdater/ConfigManager.py new file mode 100644 index 0000000..3bf0112 --- /dev/null +++ b/backend/SystemUpdater/ConfigManager.py @@ -0,0 +1,114 @@ +# UpdateManager.py +# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*- +import os +import apt_pkg +import sys +import time +import shutil +import dbus +import logging +import dbus.service +import threading +import subprocess +import traceback +from apt import Cache +from gettext import gettext as _ +from apt.debfile import DebPackage +from dbus.mainloop.glib import DBusGMainLoop +DBusGMainLoop(set_as_default=True) + +from .Core.errors import * +from .Core.enums import * +from .Core.MyCache import MyCache +from .ConfigManagerDaemon import ConfigManagerDbusController,UPDATER_DBUS_INTERFACE,UPDATER_DBUS_PATH,UPDATER_DBUS_SERVICE +from .Core.UpdateList import UpdateList +from .backend import InstallBackend,get_backend +from .Core.Database import Sqlite3Server +from .Core.loop import mainloop +from .Core.DataAcquisition import UpdateMsgCollector + +from SystemUpdater.Core.UpdaterConfigParser import UpgradeConfig +from SystemUpdater.Core.utils import kill_process +from SystemUpdater.Core.DpkgInstallProgress import LogInstallProgress +from SystemUpdater.Core.utils import deb_verify,PolicyKit_Authority,get_proc_from_dbus_name,whether_to_quit_uu,get_dist + +class UpdateConfigManager(): + BACKEND_PKG_NAME = 'kylin-system-updater' + FRONTEND_PKG_NAME = "kylin-update-frontend" + GROUPS_PKG_NAME = 'kylin-update-desktop-config' + SOURCES_UPDATE_NAME = "kylin-software-properties" + APTD_PKG_NAME = "aptdaemon" + RUN_UNATTENDED_UPGRADE = '/var/run/unattended-upgrades.pid' + RETRY_LIMIT_NUM = 2 + + def __init__(self,options): + try: + self.options = options + self.cache = None + self.update_list = None + self.init_config_aptdeamon = False + self.aptd_lang_switch = False + self.retry_limit = self.RETRY_LIMIT_NUM + #dbus + self.dbusController = self._setup_dbus() + # #config + # self.configs_uncover = UpgradeConfig(defaults_dir="system-updater-defaults.conf") + # self.configs_cover = UpgradeConfig(name = "system-updater-coverable.conf") + # self.uuconfigs = UpgradeConfig(datadir = "/var/lib/unattended-upgrades/", name = "unattended-upgrades-policy.conf") + # #数据采集器 + # self.collector = UpdateMsgCollector(self) + + except Exception as e: + logging.error(e) + traceback.print_exc() + + def run(self): + """Start the daemon and listen for calls.""" + logging.info("Waiting for calls...") + try: + mainloop.run() + except KeyboardInterrupt: + self.dbusController.Quit(None) + + def _setup_dbus(self): + # check if there is another g-a-i already and if not setup one + # listening on dbus + bus = dbus.SystemBus() + try: + bus_name = dbus.service.BusName(UPDATER_DBUS_SERVICE, + bus, + do_not_queue=True) + logging.info("Initiate dbus success ...") + return ConfigManagerDbusController(self, bus_name) + except dbus.exceptions.NameExistsException: + if self.options.replace is False: + logging.critical("Another daemon is already running") + sys.exit(1) + logging.warning("Replacing already running daemon") + + retry_reboot_times = 0 + the_other_guy = bus.get_object(UPDATER_DBUS_SERVICE, + UPDATER_DBUS_PATH) + the_other_guy.Quit(dbus_interface=UPDATER_DBUS_INTERFACE, + timeout=300) + time.sleep(1) + while True: + retry_reboot_times = retry_reboot_times + 1 + #当重试次数超过5次时退出程序 + if retry_reboot_times > 5: + logging.critical("Reboot backend is Failed...") + sys.exit(1) + try: + bus_name = dbus.service.BusName(UPDATER_DBUS_SERVICE, + bus, + do_not_queue=True) + logging.warning("Replacing already running daemon to Success...") + return ConfigManagerDbusController(self, bus_name) + except dbus.exceptions.NameExistsException: + the_other_guy = bus.get_object(UPDATER_DBUS_SERVICE, + UPDATER_DBUS_PATH) + the_other_guy.Quit(dbus_interface=UPDATER_DBUS_INTERFACE, + timeout=300) + logging.error("Dbus has not withdrawn and retry reboot times:%d...",retry_reboot_times) + time.sleep(1) + diff --git a/backend/SystemUpdater/ConfigManagerDaemon.py b/backend/SystemUpdater/ConfigManagerDaemon.py new file mode 100644 index 0000000..c249d2a --- /dev/null +++ b/backend/SystemUpdater/ConfigManagerDaemon.py @@ -0,0 +1,264 @@ +#!/usr/bin/python3 +import os +import dbus +import time +import dbus.service +import logging + +import apt_pkg +from gettext import gettext as _ +from .Core.loop import mainloop +import SystemUpdater.Core.enums as enums +from .Core.errors import * +from xml.etree import ElementTree + +UPDATER_DBUS_INTERFACE = 'com.kylin.updatercfgmanager.interface' +UPDATER_DBUS_PATH = '/com/kylin/UpdaterCfgManager' +UPDATER_DBUS_SERVICE = 'com.kylin.UpdaterCfgManager' +RUN_UNATTENDED_UPGRADE = '/var/run/unattended-upgrades.pid' +SYSTEM_VERSION = '/etc/kylin-version/kylin-system-version.conf' + +#颜色设置 +COLORLOG_SUFFIX = "\033[0m" + +# Define some foreground colors +BLACK = 30 +RED = 31 +GREEN = 32 +YELLOW = 33 +BLUE = 34 +MAGENTA = 35 +CYAN = 36 +WHITE = 37 + +#字体颜色 +FRONT_COLOR_SEQ = "\033[1;%dm" +#背景颜色 +BACK_COLOR_SEQ = "\033[%d;1m" + +COLORLOG_PREFIX = FRONT_COLOR_SEQ % GREEN +COLORMETHOR_PREFIX = FRONT_COLOR_SEQ % CYAN + +UU_UPGRADE_MODE_TIMING = 0 +UU_UPGRADE_MODE_BEFORE_SHUTDOWN = 1 + +#dbus 建立 +class ConfigManagerDbusController(dbus.service.Object): + """ this is a helper to provide the UpdateManagerIFace """ + + def __init__(self, parent, bus_name, + object_path=UPDATER_DBUS_PATH): + dbus.service.Object.__init__(self, bus_name, object_path) + self.parent = parent + self.bus = dbus.SystemBus() + + self.transaction = None + + # def _update_important_reply(self,retval): + # if bool(retval) == False: + # self.UpdateDetectFinished(False,[''],enums.get_error_string_from_enum(enums.ERROR_UPDATE_SOURCE_FAILED),\ + # enums.get_error_description_from_enum(enums.ERROR_UPDATE_SOURCE_FAILED)) + # else: + # self.parent.start_update_backend() + + # def _update_important_error(self,retval): + # logging.error(str(retval)) + # self.UpdateDetectFinished(False,[''],enums.get_error_string_from_enum(enums.ERROR_UPDATE_SOURCE_FAILED),\ + # enums.get_error_description_from_enum(enums.ERROR_UPDATE_SOURCE_FAILED)) + + # #更新important.list的本次升级的列表 + # def on_update_important_list(self): + # self.UpdateDetectStatusChanged(10,_("Updating Source Template")) + # obj = self.bus.get_object('com.kylin.software.properties', '/com/kylin/software/properties') + # interface = dbus.Interface(obj, dbus_interface='com.kylin.software.properties.interface') + # interface.updateSourceTemplate(timeout=20,reply_handler=self._update_important_reply,error_handler=self._update_important_error) + + # def is_reboot_required(self): + # """If a reboot is required to get all changes into effect.""" + # return os.path.exists(os.path.join(apt_pkg.config.find_dir("Dir"), + # "var/run/reboot-required")) + + # def is_logout_required(self): + # """If a logout is required to get all changes into effect.""" + # return os.path.exists(os.path.join(apt_pkg.config.find_dir("Dir"), + # "var/run/logout-required")) + + # #重启aptdeamon后台服务 + # def make_aptdeamon_restart(self): + # try: + # obj = self.bus.get_object('org.debian.apt', '/org/debian/apt') + # interface = dbus.Interface(obj, dbus_interface='org.debian.apt') + # logging.info("Now start to restart Aptdeamon...") + # interface.Quit() + # except Exception as e: + # logging.error(str(e)) + + # #设置aptdeamon的环境变量 + # def set_aptdeamon_environ(self,key,value): + # try: + # logging.info("Set aptdeaom environment variables %s = %s...",key,value) + # obj = self.bus.get_object('org.debian.apt', '/org/debian/apt') + # interface = dbus.Interface(obj, dbus_interface='org.debian.apt') + # retval = interface.SetEnviron(key,value) + # return retval + # except Exception as e: + # logging.error(str(e)) + # if key == "init" and value == "config": + # self.make_aptdeamon_restart() + # time.sleep(0.5) + # return False + + @dbus.service.method(UPDATER_DBUS_INTERFACE, + in_signature="", out_signature="", + sender_keyword="caller_name") + def Quit(self, caller_name): + """Request a shutdown of the daemon.""" + #如果在下载就请求 取消 + logging.info("Quitting was requested") + logging.debug("Quitting main loop...") + mainloop.quit() + logging.debug("Exit") + + @dbus.service.method(UPDATER_DBUS_INTERFACE, + in_signature="", out_signature="", + sender_keyword="caller_name") + def interface1(self, caller_name): + logging.info("interface1") + +''' + #更新进度信息 0~100 进度信息 101为非预期的信号 + @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='is') + def UpdateDetectStatusChanged(self,progress,status): + logging.info(COLORLOG_PREFIX+"Emitting"+COLORLOG_SUFFIX+" UpdateDetectStatusChanged progress = %d , status = %s",progress,status) + + WRITABLE_PROPERTIES = () + + # pylint: disable-msg=C0103,C0322 + @dbus.service.signal(dbus_interface=dbus.PROPERTIES_IFACE, + signature="sa{sv}as") + def PropertiesChanged(self, interface, changed_properties, + invalidated_properties): + """The signal gets emitted if a property of the object's + interfaces changed. + + :param property: The name of the interface. + :param changed_properties: A dictrionary of changed + property/value pairs + :param invalidated_properties: An array of property names which + changed but the value isn't conveyed. + + :type interface: s + :type changed_properties: a{sv} + :type invalidated_properties: as + """ + logging.info("Emitting PropertiesChanged: %s, %s, %s" % + (interface, changed_properties, invalidated_properties)) + + # pylint: disable-msg=C0103,C0322 + @dbus.service.method(dbus.INTROSPECTABLE_IFACE, + in_signature='', out_signature='s', + path_keyword='object_path', + connection_keyword='connection') + def Introspect(self, object_path, connection): + # Inject the properties into the introspection xml data + data = dbus.service.Object.Introspect(self, object_path, connection) + xml = ElementTree.fromstring(data) + for iface in xml.findall("interface"): + props = self._get_properties(iface.attrib["name"]) + for key, value in props.items(): + attrib = {"name": key} + if key in self.WRITABLE_PROPERTIES: + attrib["access"] = "readwrite" + else: + attrib["access"] = "read" + if isinstance(value, dbus.String): + attrib["type"] = "s" + elif isinstance(value, dbus.UInt32): + attrib["type"] = "u" + elif isinstance(value, dbus.Int32): + attrib["type"] = "i" + elif isinstance(value, dbus.UInt64): + attrib["type"] = "t" + elif isinstance(value, dbus.Int64): + attrib["type"] = "x" + elif isinstance(value, dbus.Boolean): + attrib["type"] = "b" + elif isinstance(value, dbus.Struct): + attrib["type"] = "(%s)" % value.signature + elif isinstance(value, dbus.Dictionary): + attrib["type"] = "a{%s}" % value.signature + elif isinstance(value, dbus.Array): + attrib["type"] = "a%s" % value.signature + else: + raise Exception("Type %s of property %s isn't " + "convertable" % (type(value), key)) + iface.append(ElementTree.Element("property", attrib)) + new_data = ElementTree.tostring(xml, encoding="UTF-8") + return new_data + + # pylint: disable-msg=C0103,C0322 + @dbus.service.method(dbus.PROPERTIES_IFACE, + in_signature="ssv", out_signature="", + sender_keyword="sender") + def Set(self, iface, name, value, sender): + """Set a property. + + Only the user who intiaited the transaction is + allowed to modify it. + + :param iface: The interface which provides the property. + :param name: The name of the property which should be modified. + :param value: The new value of the property. + + :type iface: s + :type name: s + :type value: v + """ + logging.info("Set() was called: %s, %s" % (name, value)) + return self._set_property(iface, name, value, sender) + + # pylint: disable-msg=C0103,C0322 + @dbus.service.method(dbus.PROPERTIES_IFACE, + in_signature="s", out_signature="a{sv}") + def GetAll(self, iface): + """Get all available properties of the given interface.""" + logging.info("GetAll() was called: %s" % iface) + return self._get_properties(iface) + + # pylint: disable-msg=C0103,C0322 + @dbus.service.method(dbus.PROPERTIES_IFACE, + in_signature="ss", out_signature="v") + def Get(self, iface, property): + """Return the value of the given property provided by the given + interface. + """ + logging.info("Get() was called: %s, %s" % (iface, property)) + return self._get_properties(iface)[property] + + def _set_property(self, iface, name, value, sender): + """Helper to set a property on the properties D-Bus interface.""" + # if iface == UPDATER_DBUS_INTERFACE: + # if name == "ShutdownInstall": + # self.parent.configs_uncover.setValue("InstallMode","shutdown_install",str(bool(value))) + # elif name == "P2pBootstrap": + # self.parent.apt_p2p_config.set_bootstrap(str(value)) + # else: + # raise dbus.exceptions.DBusException("Unknown or read only " + # "property: %s" % name) + # else: + # raise dbus.exceptions.DBusException("Unknown interface: %s" % + # iface) + pass + def _get_properties(self, iface): + """Helper get the properties of a D-Bus interface.""" + # if iface == UPDATER_DBUS_INTERFACE: + # return { + # "ShutdownInstall": dbus.Boolean( + # self.parent.configs_uncover.getWithDefault("InstallMode", "shutdown_install", False)), + + # "P2pBootstrap": dbus.String(self.parent.apt_p2p_config.get_bootstrap()) + # } + # else: + # return {} + pass +''' \ No newline at end of file diff --git a/backend/data/cn.kylinos.UpdaterCfgManager.policy b/backend/data/cn.kylinos.UpdaterCfgManager.policy new file mode 100644 index 0000000..82a0f7e --- /dev/null +++ b/backend/data/cn.kylinos.UpdaterCfgManager.policy @@ -0,0 +1,25 @@ + + + + + Kylin System Updater Config Manager + www.kylinos.cn + kylin-system-updater-config + + + <_description> + system level settings + + <_message> + To Change the settings, you need to authenticate. + + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + diff --git a/backend/data/com.kylin.UpdaterCfgManager.conf b/backend/data/com.kylin.UpdaterCfgManager.conf new file mode 100644 index 0000000..8893b99 --- /dev/null +++ b/backend/data/com.kylin.UpdaterCfgManager.conf @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + diff --git a/backend/data/kylin-system-updater-configmanager.service b/backend/data/kylin-system-updater-configmanager.service new file mode 100644 index 0000000..2e33bd5 --- /dev/null +++ b/backend/data/kylin-system-updater-configmanager.service @@ -0,0 +1,13 @@ +[Unit] +Description=kylin-system-updater config manager dbus daemon +StartLimitIntervalSec=0 + +[Service] +Type=dbus +#Restart=always +#RestartSec=3 +BusName=com.kylin.UpdaterCfgManager +ExecStart=/bin/python3 /usr/share/kylin-system-updater/kylin-system-updater-ConfigManager -r -d + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/backend/kylin-system-updater-ConfigManager b/backend/kylin-system-updater-ConfigManager new file mode 100644 index 0000000..b484dec --- /dev/null +++ b/backend/kylin-system-updater-ConfigManager @@ -0,0 +1,81 @@ +#!/usr/bin/python3 + +from SystemUpdater.ConfigManager import UpdateConfigManager +from gettext import gettext as _ +import logging +from optparse import OptionParser +import dbus +dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) +import signal +import os +import sys +import gettext + +from SystemUpdater.Core.LogManager import get_logfile as logfile + +gettext.bindtextdomain('kylin-system-updater', '/usr/share/locale') +gettext.textdomain('kylin-system-updater') +_ = gettext.gettext + +#定义日志的格式 +FORMAT = "%(asctime)s [%(levelname)s]: %(message)s" + +FORMAT_DEBUG = '%(asctime)-15s %(levelname)s(%(filename)s:%(lineno)d):%(message)s' + +def signal_handler_term(signal, frame): + # type: (int, object) -> None + logging.warning("SIGTERM received, will stop") + app.dbusController.Quit(None) + +if __name__ == "__main__": + # Begin parsing of options + parser = OptionParser() + parser.add_option ("-d", "--debug", action="store_true", default=False, + help=_("Show debug messages")) + parser.add_option("-r", "--replace", + default=False, + action="store_true", dest="replace", + help=_("Quit and replace an already running " + "daemon")) + + + (options, args) = parser.parse_args() + + if os.getuid() != 0: + print(_("You need to be root to run this application")) + sys.exit(1) + + # set debconf to NON_INTERACTIVE + os.environ["DEBIAN_FRONTEND"] = "noninteractive" + os.environ["TERM"] = "xterm" + os.environ["PATH"] = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + + #当不存在语言变量时 默认显示中文 + if not "LANGUAGE" in os.environ: + os.environ["LANGUAGE"] = "zh_CN.UTF-8" + + #当不存在语言变量时 默认显示中文 + if not "LANG" in os.environ: + os.environ["LANG"] = "zh_CN.UTF-8" + + #做一些规范处理 + if os.environ["LANGUAGE"] == "en": + os.environ["LANGUAGE"] = "en_US.UTF-8" + if os.environ["LANGUAGE"] == "zh_CN:en" or os.environ["LANGUAGE"] == "zh_CN:zh": + os.environ["LANGUAGE"] = "zh_CN.UTF-8" + + # ensure that we are not killed when the terminal goes away e.g. on + # shutdown + signal.signal(signal.SIGHUP, signal.SIG_IGN) + signal.signal(signal.SIGINT,signal_handler_term) + + if options.debug: + logging.basicConfig(format=FORMAT,level=logging.INFO,datefmt='%m-%d,%H:%M:%S') + else: + logging.basicConfig(format=FORMAT,level=logging.DEBUG,datefmt='%m-%d,%H:%M:%S',filename = logfile(),filemode = 'a') + + + logging.info('Updater Config Manager Daemon(LANGUAGE:%s LANG:%s) starting ...',os.environ["LANGUAGE"],os.environ["LANG"]) + + app = UpdateConfigManager(options) + app.run() \ No newline at end of file diff --git a/debian/kylin-system-updater.install b/debian/kylin-system-updater.install index bd169b7..a1238fa 100644 --- a/debian/kylin-system-updater.install +++ b/debian/kylin-system-updater.install @@ -19,6 +19,12 @@ backend/data/unattended-upgrades-policy.conf /var/lib/unattended-upgrades/ backend/data/cn.kylinos.KylinSystemUpdater.policy /usr/share/polkit-1/actions/ backend/data/kylin-system-version.conf /etc/kylin-version/ +#configDaemon +backend/kylin-system-updater-ConfigManager /usr/share/kylin-system-updater/ +backend/data/com.kylin.UpdaterCfgManager.conf /etc/dbus-1/system.d/ +backend/data/kylin-system-updater-configmanager.service /usr/lib/systemd/system/ +backend/data/cn.kylinos.UpdaterCfgManager.policy /usr/share/polkit-1/actions/ + #uu unattended-upgrades/*.service /lib/systemd/system/ unattended-upgrades/notify /usr/bin/