dbus + config manager

This commit is contained in:
Luoxueyi 2022-09-09 11:41:40 +08:00
parent 2cfe6dee6c
commit d3b7626899
7 changed files with 526 additions and 0 deletions

View File

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

View File

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

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
<policyconfig>
<vendor>Kylin System Updater Config Manager</vendor>
<vendor_url>www.kylinos.cn</vendor_url>
<icon_name>kylin-system-updater-config</icon_name>
<action id="com.kylin.UpdaterCfgManager.action">
<_description>
system level settings
</_description>
<_message>
To Change the settings, you need to authenticate.
</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
</action>
</policyconfig>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!-- Only root can own the service -->
<policy user="root">
<allow own="com.kylin.UpdaterCfgManager"/>
<allow send_interface="com.kylin.updatercfgmanager.interface"/>
</policy>
<!-- Allow anyone to invoke methods on the interfaces -->
<policy context="default">
<allow send_destination="com.kylin.UpdaterCfgManager"
send_interface="com.kylin.updatercfgmanager.interface"/>
<allow send_destination="com.kylin.UpdaterCfgManager"
send_interface="org.freedesktop.DBus.Introspectable"/>
<allow send_destination="com.kylin.UpdaterCfgManager"
send_interface="org.freedesktop.DBus.Properties"/>
</policy>
</busconfig>

View File

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

View File

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

View File

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