Import Upstream version 2.0.5.15

This commit is contained in:
KevinDuan 2022-11-03 19:10:26 +08:00
parent d1b4a1e555
commit c9647c003b
62 changed files with 26016 additions and 0 deletions

4
Makefile Normal file
View File

@ -0,0 +1,4 @@
all:updater
updater:
cd backend && ./setup.py build

86
README.MD Normal file
View File

@ -0,0 +1,86 @@
### Software Updater for apt
- 目录架构:
```shell
backend debian Makefile plugin README.MD
```
- 其中分为控制面板插件目录`plugin` and 更新后端目录`backend`,两个模块相互隔离,公用一个包来安装
- GitLab分支介绍
- backend_dev:后端更新代码维护的分支
- plugin_dev:控制面板插件维护的分支
- dev:分支只要负责将后端更新代码和插件的代码进行合并编包测试的分支,最新的代码在此分支上
- master:负责最终出版本的分支dev上验证成功后将代码合并到master上进行编包
- 安装依赖
```
sudo apt install dh-python python3-all python3-distutils-extra gir1.2-snapd-1 apt-clone intltool at-spi2-core -y
```
-
### 后端服务:
- 后端服务主要负责更新、安装、升级等等各种安装和下载的过程处理
- 查看后端日志:`tail -f /var/log/kylin-system-updater/kylin-system-updater.log.1`
- 调试后端代码:
- 进入backend目录下直接运行`kylin-system-updater`
- 调试参数
```shell
-d 日志直接输出到终端不输出到log文件中
-n 不更新摸板不刷新source.list and important 列表
-r 替换已经运行的后端程序
-c 关闭源过滤等各种过滤代码
```
### 配置文件
- 名称:`system-updater.conf`
- 路径:`/var/lib/kylin-system-updater`
- 配置项:
```shell
#自动更新使用
[AutoUpgrade]
#升级列表,自动更新使用
upgradelist =
#系统状态
[SystemStatus]
#标志是否异常强制关闭
isabnormalreboot = False
```
### 文档
#### Aptdaemon
- https://pythonhosted.org/aptdaemon/aptdaemon.client.html?highlight=commit_packages#aptdaemon.client.AptClient.commit_packages
#### python-apt
- https://apt-team.pages.debiahn.net/python-apt/library/apt_pkg.html
#### 方法与信号接口文档
- 参考interface.md 文档

View File

@ -0,0 +1,103 @@
# AlertWatcher.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
#
# Copyright (c) 2010 Mohamed Amine IL Idrissi
#
# Author: Mohamed Amine IL Idrissi <ilidrissiamine@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 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 General Public License for more details.
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
from __future__ import absolute_import
import logging
from gi.repository import GObject
import dbus
from dbus.mainloop.glib import DBusGMainLoop
class AlertWatcher(GObject.GObject):
""" a class that checks for alerts and reports them, like a battery
or network warning """
__gsignals__ = {"network-alert": (GObject.SignalFlags.RUN_FIRST,
None,
(GObject.TYPE_INT,)),
"battery-alert": (GObject.SignalFlags.RUN_FIRST,
None,
(GObject.TYPE_BOOLEAN,)),
"network-3g-alert": (GObject.SignalFlags.RUN_FIRST,
None,
(GObject.TYPE_BOOLEAN,
GObject.TYPE_BOOLEAN,)),
}
def __init__(self):
GObject.GObject.__init__(self)
DBusGMainLoop(set_as_default=True)
self.bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
# make it always connected if NM isn't available
self.network_state = 3
def check_alert_state(self):
try:
#network
obj = self.bus.get_object("org.freedesktop.NetworkManager",
"/org/freedesktop/NetworkManager")
obj.connect_to_signal(
"StateChanged",
self._on_network_state_changed,
dbus_interface="org.freedesktop.NetworkManager")
interface = dbus.Interface(obj, "org.freedesktop.DBus.Properties")
self.network_state = interface.Get(
"org.freedesktop.NetworkManager", "State")
self._network_alert(self.network_state)
# power
# obj = self.bus.get_object('org.freedesktop.UPower',
# '/org/freedesktop/UPower')
# obj.connect_to_signal("Changed", self._power_changed,
# dbus_interface="org.freedesktop.UPower")
# self._power_changed()
# 3g
# self._update_3g_state()
except dbus.exceptions.DBusException as e:
logging.error(str(e))
pass
def _on_network_state_changed(self, state):
self._network_alert(state)
# self._update_3g_state()
# def _update_3g_state(self):
# from .roam import NetworkManagerHelper
# nm = NetworkManagerHelper()
# on_3g = nm.is_active_connection_gsm_or_cdma()
# is_roaming = nm.is_active_connection_gsm_or_cdma_roaming()
# self._network_3g_alert(on_3g, is_roaming)
# def _network_3g_alert(self, on_3g, is_roaming):
# self.emit("network-3g-alert", on_3g, is_roaming)
def _network_alert(self, state):
self.network_state = state
self.emit("network-alert", state)
# def _power_changed(self):
# obj = self.bus.get_object("org.freedesktop.UPower",
# "/org/freedesktop/UPower")
# interface = dbus.Interface(obj, "org.freedesktop.DBus.Properties")
# on_battery = interface.Get("org.freedesktop.UPower", "OnBattery")
# self.emit("battery-alert", on_battery)

View File

@ -0,0 +1,602 @@
# DataAcquisition.py
# supervisory control and data acquisition
#!/usr/bin/python3
import os
import json
import dbus
import uuid
import time
import socket
import base64
import shutil
import hashlib
import logging
import tarfile
import requests
import datetime
import threading
from email import message
from datetime import datetime
from binascii import a2b_hex
from Crypto.PublicKey import RSA
from urllib import parse, request
from PyQt5.QtCore import QSettings
from SystemUpdater.Core import enums
from Crypto.Cipher import PKCS1_OAEP
from json.decoder import JSONDecodeError
from dbus.exceptions import DBusException
from SystemUpdater.Core.UpdaterConfigParser import UpgradeConfig
LOCALTIDDIR = "/var/lib/kylin-system-updater/"
LOCALTIDFILE = "tidfile.conf"
MSGSNDDIR = "/var/lib/kylin-system-updater/sendinfos/"
SOURCE_PKGNAME = {
'Kylin System Updater': 'kylin-system-updater',
'Kylin Unattended Upgrade': 'unattended-upgrades'
}
class UpdateMsgCollector():
ACTION_DEFUALT_STATUS = -1
ACTION_UPDATE = 0
ACTION_INSTALL = 1
ACTION_INSTALL_DEB = 2
ACTION_CHECK_RESOLVER = 3
ACTION_DOWNLOADONLY = 4
ACTION_FIX_BROKEN = 5
ACTION_REMOVE_PACKAGES = 6
ACTION_FIX_INCOMPLETE = 7
ACTION_CLEAN = 8
ACTION_INSTALL_SHUTDOWN = 9
MODE_DEFAULT_STATUS = -1
#1、ACTION_INSTALL 安装的子类
#部分升级
MODE_INSTALL_PARTIAL = 0
#全部升级
MODE_INSTALL_ALL = 1
#系统全盘升级
MODE_INSTALL_SYSTEM = 2
#后端内部安装包使用 目前 更新配置包和升级本身使用
MODE_INSTALL_SINGLE = 3
#2、更新的子类
MODE_UPDATE_CACHE = 0
MODE_UPDATE_ALL = 1
mode_map = {
MODE_INSTALL_PARTIAL:"upgrade_system",
MODE_INSTALL_ALL:"upgrade_all",
MODE_INSTALL_PARTIAL:"upgrade_partial"
}
action_map = {
ACTION_CHECK_RESOLVER:enums.MONIT_DEPRESOLUT,
ACTION_INSTALL:enums.MONIT_INSTALL,
ACTION_INSTALL_DEB:enums.MONIT_INSTALLDEB,
10:enums.MONIT_FINISH,
"finish-update":enums.MONIT_FINISH,
"finish-install":enums.MONIT_FINISH
}
messageType_map = {
# InstallBackend.ACTION_CHECK_RESOLVER:"UpdateDetect",
ACTION_CHECK_RESOLVER:"DepResolution",
# InstallBackend.ACTION_CHECK_RESOLVER:"Downloading",
ACTION_INSTALL:"Installing",
# InstallBackend.ACTION_CHECK_RESOLVER:"UpgradeFinish",
ACTION_INSTALL_DEB:"InstallerInfo",
10:"Background-Upgrade",
"finish-update":"UpdateInfos",
"finish-install":"InstallInfos"
}
def __init__(self, manager=None):
self.uuid = ''
self.status = ''
self.upgrade_mode = ''
self.upgrade_action = ''
self.UploadMessage = {}
self.PackageInfo = {}
self.UpdateInfos = {}
self.background_version = {}
self.background_upgradable = []
self.background_list = []
self.upgrade_list = []
self.waitSendList = []
self.cache = None
self.updateManager = manager
# 转换 & 加密
self.convertor = FormatConvert(self)
# 发送器
self.sender = MessageSend(self)
logging.info("Initialize Update MessageSend Collector to success...")
def GenUploadMessage(self, dict_message, local_uuid = ''):
UploadMessage = {}
# 获取将要上传的数据,获取东八区时间
UploadMessage['createTimeStamp'] = get_east_8_time()
try:
if "packageName" in dict_message.keys():
dict_message.pop("packageName")
for key in dict_message.keys():
UploadMessage[key] = dict_message[key]
if local_uuid != '':
UploadMessage['UUID'] = str(local_uuid)
else:
UploadMessage['UUID'] = str(uuid.uuid1())
except Exception as e:
logging.error(str(e))
json_UploadMessage = self.convertor.dictConvertJson(UploadMessage)
logging.debug('Generate UploadMessage: %s.',json_UploadMessage)
self.UploadMessage = UploadMessage.copy()
UploadMessage.clear()
def GenPackageInfo(self, messageType, packageName):
PackageInfo = {}
PackageInfo['messageType'] = str(messageType)
PackageInfo['packageName'] = str(packageName)
key = str(packageName)+'_'+str(messageType)
# 获取本地tid
self.sender.GetLocalTid(key)
PackageInfo["tid"] = str(self.sender.localtid)
json_PackageInfo = self.convertor.dictConvertJson(PackageInfo)
logging.debug('Generate PackageInfo: %s.',json_PackageInfo)
self.PackageInfo = PackageInfo.copy()
PackageInfo.clear()
def setUploadMessage(self, KeyValue):
# FIXME:数据获取顺序问题
pass
def UpdateMsg(self, messageType, json_message, uuid = ''):
# para: messageType(消息类型): "UpdateInfos"、 "InstallInfos"、 "RemoveInfo"
# para: dict_message(数据内容): 必须包含 "packageName"、"source"", 采集器会进行检测
dict_message = self.convertor.JsonConvertDict(json_message)
if messageType == "":
messageType = "SystemUpdate"
if type(dict_message) != type(dict) and "appname" not in dict_message.keys():
raise AttributeError("'%s' object has no attribute '%s'" % ("dict message", "appname"))
# 生成UploadMessage与PackageInfo
try:
self.GenPackageInfo(messageType, "kylin-system-updater")
self.GenUploadMessage(dict_message, local_uuid = uuid)
except Exception as e:
logging.error(str(e))
# sha256
json_UploadMessage = self.convertor.dictConvertJson(self.UploadMessage)
json_PackageInfo = self.convertor.dictConvertJson(self.PackageInfo)
shaValue = self.convertor.Sha256Value(json_UploadMessage)
encodeMsg = self.convertor.EncodeRSAtoBase64(shaValue)
# dbus发送
try:
self.sender.MsgSendToServer(json_UploadMessage, json_PackageInfo, encodeMsg)
except Exception as e:
logging.error(e)
def Generate_Msg(self, upgrade_list, mode):
try:
self.upgrade_list = upgrade_list
self.upgrade_mode = mode
self.uuid = str(uuid.uuid1())
self.UpdateInfos.update({"upgradeMode":self.mode_map.get(self.upgrade_mode, "default-mode")})
except DBusException as e:
logging.error(e)
def Upgrade_Process_Msg(self, action, dict_msg = {}):
if self.updateManager.configs_uncover.getWithDefault("SystemStatus", "upload_upgrade_log", False) == True:
tmp_dict = {}
tmp_dict.update(dict_msg)
try:
self.UpdateInfos.update({"step":self.action_map.get(action, "")})
if self.upgrade_mode == self.MODE_INSTALL_SYSTEM:
self.UpdateInfos.update({"appname":"Upgrade System"})
tmp_dict.update(self.UpdateInfos)
json_file = json.dumps(tmp_dict.copy())
self.UpdateMsg(self.messageType_map.get(action, ""), json_file, self.uuid)
else:
if action == self.ACTION_INSTALL_DEB:
tmp_dict.update({"step":self.action_map.get(action, "")})
json_file = json.dumps(tmp_dict.copy())
self.UpdateMsg(self.messageType_map.get(action, ""), json_file, self.uuid)
else:
tmp_dict.update(self.UpdateInfos)
json_file = json.dumps(tmp_dict.copy())
self.UpdateMsg(self.messageType_map.get(action, ""), json_file, self.uuid)
except Exception as e:
logging.error(e)
tmp_dict.clear()
def make_background_version(self,pkg):
if pkg.is_installed == True:
self.background_version.update({pkg.name:pkg.installed.source_version})
else:
self.background_version.update({pkg.name:"Unknown"})
self.background_list.append(pkg.name)
def Msg_Clean(self):
self.UploadMessage = {}
self.PackageInfo = {}
self.UpdateInfos = {}
class FormatConvert():
def __init__(self, DataCollector):
#秘钥
self.publickey = UniqueKey()
self.collector = DataCollector
def dictConvertJson(self, dict_msg):
#字典转换为json格式字符串
json_file = ''
try:
json_file = json.dumps(dict_msg)
except JSONDecodeError as e:
logging.error(str(e))
return json_file
def JsonConvertDict(self, json_file):
# json格式字符串转换为字典
dict_file = {}
try:
dict_file = json.loads(json_file)
except JSONDecodeError as e:
logging.error(str(e))
return dict_file
def Sha256Value(self, json_file):
# 计算sha256值
hsobj = hashlib.sha256()
try:
hsobj.update(json_file.encode("utf-8"))
except ValueError as e:
logging.error("SHA256 value error: %s.",str(e))
except Exception as e:
logging.error(str(e))
return hsobj.hexdigest()
def EncodeRSAtoBase64(self, value):
# 将value进行RSA加密并base64转码
try:
# 计算hex值
value_hex = a2b_hex(value)
# 加载公钥,填充格式OAEP
uniqueKey = self.publickey.keyvalue.encode('utf-8')
uniqueKeyorig = base64.b64decode(uniqueKey) # 公钥文件
rsa_pubkey = RSA.importKey(uniqueKeyorig) # RSA公钥
oaep_pub = PKCS1_OAEP.new(rsa_pubkey) # OAEP填充
# 加密数据
encodemsg = oaep_pub.encrypt(value_hex)
# 加密数据Base64转码
enMsg = base64.b64encode(encodemsg)
except ValueError:
logging.error("Value error: %s.", value)
except TypeError:
logging.error("RSA key has no private half.")
return enMsg
class MessageSend():
ERR_PARA_FROMAT = 1
ERR_NO_LOACLTID = 2
ERR_ABNORMAL_SHA = 3
ERR_UPLOADMSG_SHA = 4
ERR_UPLOADMSG_CTS = 5
def __init__(self, DataCollector=None) -> None:
# self.convertor = FormatConvert()
if DataCollector == None:
self.collector = UpdateMsgCollector()
else:
self.collector = DataCollector
def MsgSendToServer(self, UploadMessage, PackageInfo, encodeMsg):
daqbus = dbus.SystemBus()
try:
daqobj = daqbus.get_object('com.kylin.daq', '/com/kylin/daq')
daqinterface = dbus.Interface(daqobj, dbus_interface='com.kylin.daq.interface')
except DBusException as e:
logging.error("kylin-daq service error: "+str(e))
return
try:
retval,retid = daqinterface.UploadMessage(PackageInfo, UploadMessage, encodeMsg)
except AttributeError:
logging.error("Call UploadMessage: Attribute Error.")
self.Send_finally(retval, retid, PackageInfo, UploadMessage, encodeMsg)
def Send_finally(self, retval, retid, json_PackageInfo, json_UploadMessage, encodeMsg):
# 根据发送结果进行处理
result = ''
PackageInfo = self.collector.convertor.JsonConvertDict(json_PackageInfo)
if retval != 0:
if retval == self.ERR_PARA_FROMAT:
result = "Parameter format error"
logging.debug("Sent Status: false - packageName: %s : result: %s.", PackageInfo['packageName'], result)
elif retval == self.ERR_NO_LOACLTID:
result = "The tid value in packageInfo is abnormal, but the message is saved successfully"
logging.debug("Sent Status: false - packageName: %s : result: %s.", PackageInfo['packageName'], result)
# 将返回的tid保存到本地
key = PackageInfo['packageName']+'_'+PackageInfo['messageType']
self.SaveTid(key, retid)
elif retval == self.ERR_ABNORMAL_SHA:
result = "Abnormal UploadedMessage Sha256"
logging.debug("Sent Status: false - packageName: %s : result: %s.", PackageInfo['packageName'], result)
elif retval == self.ERR_UPLOADMSG_SHA:
result = "Description The UploadedMessageSha256 was decrypted incorrectly"
logging.debug("Sent Status: false - packageName: %s : result: %s.", PackageInfo['packageName'], result)
elif retval == self.ERR_UPLOADMSG_CTS:
result = "The createTimeStamp field of UploadedMessage is abnormal"
logging.debug("Sent Status: false - packageName: %s : result: %s.", PackageInfo['packageName'], result)
elif retval == self.ERR_UPLOADMSG_CTS:
result = "Invalid key included in \"uploadedMessage\" or \"packageInfo\": <@timestamp>,<_id>,<_index>,<_type>,<createTime>,<highlight>,<sn>,<sort>, check upload field"
logging.debug("Sent Status: false - packageName: %s : result: %s.", PackageInfo['packageName'], result)
else:
logging.debug("Sent Status: false - packageName: %s : retval: %s.", PackageInfo['packageName'], retval)
# 上传失败写入本地json
if retval != self.ERR_NO_LOACLTID or retval == self.ERR_NO_LOACLTID:
self.WriteToJson(PackageInfo['messageType'], json_PackageInfo, json_UploadMessage, encodeMsg)
elif retval == 0:
result = "Send to server success"
logging.debug("Sent Status: True - packageName: %s : result: %s.", PackageInfo['packageName'], result)
def GetLocalTid(self, key):
# 试图获取本地tid
try:
# 存放至数据库
tid = self.collector.updateManager.sqlite3_server.select_from_tid("tid",key)
if tid == "None" or tid == None:
self.localtid = ""
else:
self.localtid = tid
except Exception as e:
logging.error(str(e))
def SaveTid(self, key, localtid):
if len(localtid) == 0:
return
_localtid = str(localtid)
try:
# 写入数据库
self.collector.updateManager.sqlite3_server.insert_into_tid(key, _localtid)
except Exception as e:
logging.error(str(e))
def WriteToJson(self, messageType, json_PackageInfo, json_UploadMessage, encodeMsg):
#发送失败时写入本地json中定时发送
Msg = {}
Msg["PackageInfo"] = json_PackageInfo
Msg["UploadMessage"] = json_UploadMessage
Msg["encodeMsg"] = str(encodeMsg)
json_file = self.collector.convertor.dictConvertJson(Msg)
# 保存信息
try:
if not os.path.exists(MSGSNDDIR):
os.mkdir(MSGSNDDIR)
# 根据messageType保存信息
with open(MSGSNDDIR+messageType+".json","a") as f:
f.write(json_file)
f.write("\n")
except Exception as e:
logging.error(str(e))
def _TimedTransmission(self, file_path = MSGSNDDIR):
classify_list = [name for name in os.listdir(file_path) if name.endswith(".json")]
for f in classify_list:
# 循环发送每一个文件
self._ReadFromFile(os.path.join(file_path, f))
def _ReadFromFile(self, json_path):
new_lines = []
# 从本地文件读取
if not os.path.exists(json_path):
return
with open(json_path, "r+") as f:
lines = f.readlines()
# file is empty and path is exit -> remove file
if len(lines) == 0 and os.path.exists(json_path):
os.remove(json_path)
return
#send installinfo or updateinfo
for line in lines:
(retval,retid) = self._file_send_server(line)
if retval != 0: # success
new_lines.append(line)
if os.path.exists(json_path):
os.remove(json_path)
if len(new_lines) != 0:
with open(json_path, "w+") as f:
for line in lines:
f.write(line)
def _file_send_server(self, json):
UploadMessage = {}
PackageInfo = {}
encodeMsg = ''
dict_msg = self.collector.convertor.JsonConvertDict(json)
if 'UploadMessage' in dict_msg.keys():
UploadMessage = dict_msg['UploadMessage']
UploadMessage = self.collector.convertor.dictConvertJson(UploadMessage)
if 'PackageInfo' in dict_msg.keys():
PackageInfo = dict_msg['PackageInfo']
PackageInfo = self.collector.convertor.dictConvertJson(PackageInfo)
if 'encodeMsg' in dict_msg.keys():
encodeMsg = str(dict_msg['encodeMsg'])
if len(UploadMessage) == 0 or len(PackageInfo) == 0 or encodeMsg == '':
logging.error("Msg error")
return 6, ''
daqbus = dbus.SystemBus()
try:
daqobj = daqbus.get_object('com.kylin.daq', '/com/kylin/daq')
daqinterface = dbus.Interface(daqobj, dbus_interface='com.kylin.daq.interface')
except DBusException as e:
logging.error(str(e))
try:
retval,retid = daqinterface.UploadMessage(PackageInfo, UploadMessage, encodeMsg)
except AttributeError:
logging.error("Call UploadMessage: Attribute Error.")
return (retval,retid)
class UniqueKey():
keyvalue = "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FR\
OEFNSUlCQ2dLQ0FRRUFzdW1NTFJEdlFNb0tEQkRJODRqSgpqc1A0Mk55V0pWVEZob2Jra3ZiT05j\
dExYTXVzRmo2TzJUblZYU3Z6VlVLSjRqZkpwT2l2WEphOVB5Z2wzYTRnClBzSU40enNCMEdOY0tr\
R3VsS2RrV2x6S3lWQ2xlTzhiQnN6SjkwbTc3cWF6YWg3a1A0TUl0WTVFczBpSkpiR0oKN1MxcERj\
MlJkNnVFQWJLaXJyRTFlNzlFTEd4am5VN2V5NWkyRDE2WWJoZEQwZ2lNa2RHR3piQXBKTWZWRVJR\
TQo1NXorMFVqdS8zSFJhNFY3b3p2TGRPRE5HUURaeWNJU0l3VHBLbFR3RjBxazdCNjVhTUlJenQ1\
dnhOK1lxYU1GClppZFRLNzcxNjdqNEExZ3F3MG45bjlybWVXUGRWZ3dudnRtVXp4Q1krNk05SXpK\
TDI3eWpRUTV1WGQ3RVdMT3IKbndJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="
class PHPServer(threading.Thread):
LOG_PATH = "/var/log/kylin-system-updater"
KYLIN_SOFTWARE_PROPERTIES_LOG = "/var/log/kylin-software-properties.log"
PINGBACK_INTERNET_URL = "http://archive1.kylinos.cn:32294/kylin-update-manager-server/main.php?"
PINGBACK_INTERNET_FILE_URL = "http://archive1.kylinos.cn:32294/kylin-update-manager-server/get_file.php?"
PINGBACK_INTRANET_URL = "http://archive.kylinos-intranet.cn/kylin-update-manager-server/main.php?"
PINGBACK_INTRANET_FILE_URL = "http://archive.kylinos-intranet.cn/kylin-update-manager-server/get_file.php?"
SYSTEM_VERSION_PATH = "/etc/kylin-version/kylin-system-version.conf"
def get_values(self, _appname="", _appversion="", _state='', _errorcode='', _errorstring=""):
self.appname = _appname
self.appversion = _appversion
self.status = _state
self.errorcode = _errorcode
self.errorstring = _errorstring
def run(self):
# 获取本机ip
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 80))
host_ip = s.getsockname()[0]
except:
host_ip = 'Ip failed to get'
# 获取系统版本
with open('/etc/lsb-release', 'r') as f:
for line in f.readlines():
if line.strip().startswith('DISTRIB_DESCRIPTION='):
versions = line.strip().split('=')
if "V10" in line and "SP1" in versions[1]:
version = "V10SP1"
else:
version = "V10Pro"
break
# 获取软件版本
output = os.popen('dpkg -l|grep kylin-system-updater').readlines()
if output:
soft_version = output[0].strip().split()[2]
# 获取时间
nowtime = time.strftime('%Y-%m-%d-%H:%M:%S', time.localtime(time.time()))
# 获取Mac
mac = uuid.UUID(int=uuid.getnode()).hex[-12:]
host_mac = ":".join([mac[e:e + 2] for e in range(0, 11, 2)])
# 获取序列号
if os.path.exists("/etc/.kyinfo"):
settings = QSettings("/etc/.kyinfo", QSettings.IniFormat)
settings.beginGroup("servicekey")
key = str(settings.value("key")).strip()
settings.endGroup()
else:
key = "0"
try:
# 用于收集源管理器的更新日志
if self.status != "success":
# nowtime = datetime.utcnow( ).strftime ( '%Y-%m-%d %H:%M:%S.%f' )[:-3]
nowtime = get_east_8_time()
log_dir = os.path.join(self.LOG_PATH, host_mac + "_" + nowtime)
log_file_gzip = log_dir + ".tar.gz"
os.makedirs(log_dir, exist_ok=True)
#get updater log
updater_path = os.path.join(log_dir,"kylin-system-updater")
os.makedirs(updater_path, exist_ok=True)
#get updater log
if os.path.exists("/var/log/kylin-system-updater/kylin-system-updater.log.1"):
shutil.copy("/var/log/kylin-system-updater/kylin-system-updater.log.1", log_dir)
if os.path.exists("/var/log/kylin-system-updater/kylin-system-updater.log.1.1.gz"):
shutil.copy("/var/log/kylin-system-updater/kylin-system-updater.log.1.1.gz", log_dir)
#get apt log
if os.path.exists("/var/log/apt/history.log"):
shutil.copy("/var/log/apt/history.log", log_dir)
if os.path.exists("/var/log/apt/term.log"):
shutil.copy("/var/log/apt/term.log", log_dir)
#get version file
if os.path.exists(self.SYSTEM_VERSION_PATH):
shutil.copy(self.SYSTEM_VERSION_PATH, log_dir)
gZipFile(log_dir, log_file_gzip)
header = {'Content-Type': "multipart/form-data", "Accept-Encoding": "gzip"}
try:
with open(log_file_gzip, "rb") as f:
requests.post(self.PINGBACK_INTRANET_FILE_URL + "filename=" + os.path.basename(log_file_gzip),
data=f.read(), headers=header)
except:
with open(log_file_gzip, "rb") as f:
requests.post(self.PINGBACK_INTERNET_FILE_URL + "filename=" + os.path.basename(log_file_gzip),
data=f.read(), headers=header)
shutil.rmtree(log_dir)
os.remove(log_file_gzip)
else:
log_file_gzip = ""
kmg_tmp = {'ip': host_ip, 'version': version, 'soft_version': soft_version, 'datetime': nowtime,
'host_mac': host_mac, 'appname': self.appname, 'appversion': self.appversion, 'serial_number': key,
'state': self.status, 'filename': log_file_gzip, 'errorcode': self.errorcode, 'errorstring': self.errorstring}
kmg = parse.urlencode(kmg_tmp)
logging.debug("PHPServer UpdateInfos: %s .", kmg_tmp)
# 优先使用内网服务器,再使用外网
try:
url = self.PINGBACK_INTRANET_URL + kmg
req = request.urlopen(url=url, timeout=3)
logging.info("The Intranet log server is successfully accessed, pkgname:%s .",self.appname)
except:
url = self.PINGBACK_INTERNET_URL + kmg
req = request.urlopen(url=url, timeout=3)
logging.info("The external log server is successfully accessed, pkgname:%s .",self.appname)
except Exception as e:
logging.error("Failed to access the external log server: %s, pkgname:%s .", e, self.appname)
if os.path.isfile(log_file_gzip):
os.remove(log_file_gzip)
def PHPSeverSend(_appname="", _appversion="", _statue="", _errorcode="", _errorstring=""):
send_thread = PHPServer()
send_thread.get_values(_appname=_appname, _appversion=_appversion, _state=_statue, _errorcode=_errorcode, _errorstring=_errorstring)
send_thread.start()
def gZipFile(src, dst):
with tarfile.open(dst, "w:gz") as tar:
tar.add(src, arcname=os.path.basename(src))
def get_east_8_time():
import time
# UTC时间
utc_time = datetime.utcnow()
# 转时间字符串
utc_time = utc_time.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
time_suffix = utc_time.split(".")[1]
# 字符串转时间元祖
utc_time = time.strptime(utc_time, "%Y-%m-%d %H:%M:%S.%f")
# 时间元祖转时间戳
utc_time = time.mktime(utc_time)
# 生成东八区时间时间戳
now_time = utc_time + 8*60*60
# 时间戳转时间元祖
now_time = time.localtime(now_time)
# 时间元祖转字符串
now_time = time.strftime("%Y-%m-%d %H:%M:%S",now_time)
now_time = now_time + "." +time_suffix
return now_time
# return 0
if __name__ == "__main__":
# 执行定时发送
ms = MessageSend()
ms._ReadFromFile("/var/lib/kylin-system-updater/sendinfos/testMsg.json")

View File

@ -0,0 +1,400 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import os
import logging
import sqlite3
from operator import itemgetter
from gettext import gettext as _
from optparse import OptionParser
DB_UPDATER = "/var/cache/kylin-update-manager/kylin-update-manager.db"
DB_UPGRADE = "/var/cache/kylin-system-updater/kylin-system-updater.db"
VER_DB = "/usr/share/kylin-system-updater/kylin-system-updater.db"
def dateMigration(options=None, old_db=None, old_db_cursor=None, new_db=None, new_db_cursor=None):
print(_("Loading Sqlite3Server..."))
if options==None:
old_path = DB_UPDATER
new_path = DB_UPGRADE
try:
if old_db==None and old_db_cursor==None:
old_db = sqlite3.connect(old_path, check_same_thread=False)
old_db_cursor = old_db.cursor()
if new_db==None and new_db_cursor==None:
new_db = sqlite3.connect(new_path, check_same_thread=False)
new_db_cursor = new_db.cursor()
except Exception as e:
print(e)
sql_commnd = ""
old_cfg_dict = {}
new_cfg_dict = {}
# step 1: 更新旧配置数据
try:
print("更新旧配置数据")
sql_commnd = "SELECT * FROM display where id=1"
old_db_cursor.execute(sql_commnd)
old_cfg = old_db_cursor.fetchone()
for od in old_db_cursor.description:
old_cfg_dict.update({str(od[0]):old_cfg[old_db_cursor.description.index(od)]})
new_db_cursor.execute(sql_commnd)
new_cfg = new_db_cursor.fetchone()
for od in new_db_cursor.description:
new_cfg_dict.update({str(od[0]):new_cfg[new_db_cursor.description.index(od)]})
if "download_limit" in new_cfg_dict.keys() and "download_limit_value" in new_cfg_dict.keys():
if new_cfg_dict['download_limit'] != None or new_cfg_dict['download_limit_value'] != None:
print("目标数据库有更新的配置项")
else:
sql_commnd = "UPDATE display set check_time='"+old_cfg_dict['check_time']+"' Where id=1"
new_db_cursor.execute(sql_commnd)
new_db.commit()
sql_commnd = "UPDATE display set update_time='"+old_cfg_dict['update_time']+"' Where id=1"
new_db_cursor.execute(sql_commnd)
new_db.commit()
sql_commnd = "UPDATE display set auto_check='"+old_cfg_dict['auto_check']+"' Where id=1"
new_db_cursor.execute(sql_commnd)
new_db.commit()
sql_commnd = "UPDATE display set system_version='"+old_cfg_dict['system_version']+"' Where id=1"
new_db_cursor.execute(sql_commnd)
new_db.commit()
if old_cfg_dict['auto_backup'] != None:
sql_commnd = "UPDATE display set auto_backup='"+old_cfg_dict['auto_backup']+"' Where id=1"
new_db_cursor.execute(sql_commnd)
new_db.commit()
if 'download_limit' in old_cfg_dict.keys() and old_cfg_dict['download_limit'] != None:
sql_commnd = "UPDATE display set download_limit='"+old_cfg_dict['download_limit']+"' Where id=1"
new_db_cursor.execute(sql_commnd)
new_db.commit()
if 'download_limit_value' in old_cfg_dict.keys() and old_cfg_dict['download_limit_value'] != None:
sql_commnd = "UPDATE display set download_limit_value='"+old_cfg_dict['download_limit_value']+"' Where id=1"
new_db_cursor.execute(sql_commnd)
new_db.commit()
except Exception as e:
print(e)
print("更新配置文件错误")
return
# step 2: 更新installed
try:
print("更新installed")
update_record_dict = {}
tmp_update_record_dict = []
sql_commnd = "SELECT * FROM installed"
old_db_cursor.execute(sql_commnd)
update_record = old_db_cursor.fetchall()
sql_commnd = "SELECT * FROM updateinfos"
new_db_cursor.execute(sql_commnd)
new_update_record = new_db_cursor.fetchall()
for ur in update_record:
id,appname,version,time,description,icon,statue,keyword,errorcode = ur
if errorcode in range(200):
errorcode = 'null'
update_record_dict.clear()
update_record_dict.update({"appname":appname})
update_record_dict.update({"version":version})
update_record_dict.update({"time":time})
update_record_dict.update({"description":description})
update_record_dict.update({"icon":icon})
update_record_dict.update({"statue":statue})
update_record_dict.update({"keyword":'1'})
update_record_dict.update({"errorcode":errorcode})
tmp_update_record_dict.append(update_record_dict.copy())
for ur in new_update_record:
id,appname,version,description,date,status,keyword,errorcode = ur
if errorcode in range(200):
errorcode = 'null'
update_record_dict.clear()
update_record_dict.update({"appname":appname})
update_record_dict.update({"version":version})
update_record_dict.update({"time":date})
update_record_dict.update({"description":description})
update_record_dict.update({"icon":None})
update_record_dict.update({"statue":status})
update_record_dict.update({"keyword":'1'})
update_record_dict.update({"errorcode":errorcode})
tmp_update_record_dict.append(update_record_dict.copy())
# 按时间排序
tmp_update_record_dict = sorted(tmp_update_record_dict, key=itemgetter('time'))
print("更新installed success")
except Exception as e:
print(e)
print("更新安装记录错误")
return
try:
# 删除 tmp
# DeleteTable(options.new_path+':'+'tmp')
# 创建表
sql_commnd = "create table IF NOT EXISTS tmp('id' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,\
'appname' TEXT,\
'version' TEXT,\
'description' TEXT,\
'date' TEXT,\
'status' TEXT,\
'keyword' TEXT,\
'errorcode' TEXT) "
new_db_cursor.execute(sql_commnd)
# 更新数据
for urd in tmp_update_record_dict:
new_db_cursor.execute(
"insert into tmp (appname, version, description, date, status, keyword, errorcode) values(?,"
"?,?,?,?,?,?)",
(urd['appname'], urd['version'], urd['description'], urd['time'], urd['statue'], urd['keyword'], urd['errorcode']))
new_db.commit()
# 删除updateinfos
sql_commnd = "drop table updateinfos"
new_db_cursor.execute(sql_commnd)
new_db.commit()
# 修改表名
sql_commnd = "alter table tmp rename to updateinfos"
new_db_cursor.execute(sql_commnd)
new_db.commit()
except Exception as e:
print(e)
print("安装记录迁移错误")
return
print("数据迁移成功.")
def CleanTable(db_table):
db_path, table_name = str(db_table).split(":")
if not os.path.isfile(db_path):
print("db path error.")
exit(-1)
print(_("Loading Sqlite3Server..."))
try:
db = sqlite3.connect(db_path, check_same_thread=False)
db_cursor = db.cursor()
sql_commnd = 'delete from '+table_name
db_cursor.execute(sql_commnd)
db.commit()
print("clean %s success."%table_name)
except Exception as e:
print(e)
print("clean %s error."%table_name)
def DeleteTable(db_table):
db_path, table_name = str(db_table).split(":")
if not os.path.isfile(db_path):
print("db path error.")
exit(-1)
print(_("Loading Sqlite3Server..."))
try:
db = sqlite3.connect(db_path, check_same_thread=False)
db_cursor = db.cursor()
sql_commnd = 'drop table '+table_name
db_cursor.execute(sql_commnd)
db.commit()
print("delete %s success."%table_name)
except Exception as e:
print("delete %s error: %s"%(table_name,e))
def _has_first_migration(new_db, new_db_cursor):
try:
sql_commnd = "select * from sqlite_master where type='table' and name='display';"
new_db_cursor.execute(sql_commnd)
retval = new_db_cursor.fetchone()
for rv in retval:
if "firstmigration" in str(rv):
return True
except Exception as e:
print(e)
return False
def _is_first_migration(new_db, new_db_cursor):
try:
sql_commnd = "select firstmigration from display;"
new_db_cursor.execute(sql_commnd)
retval = new_db_cursor.fetchone()
if "yes" in retval:
return True
else :
return False
except Exception as e:
print(e)
return False
def _is_display_exist_fields(field, db, db_cursor):
try:
sql_commnd = "select * from sqlite_master where type='table' and name='display';"
db_cursor.execute(sql_commnd)
retval = db_cursor.fetchone()
for rv in retval:
if field in str(rv):
return True
except Exception as e:
print(e)
return False
return False
def _is_updateinfos_exist_fields(field, db, db_cursor):
try:
sql_commnd = "select * from sqlite_master where type='table' and name='updateinfos';"
db_cursor.execute(sql_commnd)
retval = db_cursor.fetchone()
for rv in retval:
if field in str(rv):
return True
except Exception as e:
print(e)
return False
return False
def _add_display_fields(fields_default, default_table = True):
try:
if "=" not in fields_default:
print("format: field=value")
return False
field, value = fields_default.split('=')
# print(_("Loading Sqlite3Server..."))
db = sqlite3.connect(DB_UPGRADE, check_same_thread=False)
db_cursor = db.cursor()
if default_table:
if _is_display_exist_fields(field, db, db_cursor):
print("field %s is exist."%field)
return False
# 字段不存在,新增字段
sql_commnd = "alter table display add column "+field+" TEXT;"
db_cursor.execute(sql_commnd)
sql_commnd = "UPDATE display SET "+field+"='"+value+"'"
db_cursor.execute(sql_commnd)
db.commit()
else:
if _is_updateinfos_exist_fields(field, db, db_cursor):
print("field %s is exist."%field)
return False
# 字段不存在,新增字段
sql_commnd = "alter table updateinfos add column "+field+" TEXT;"
db_cursor.execute(sql_commnd)
db.commit()
except Exception as e:
print(e)
return False
print("Succeeded in adding field: '%s' "%field)
return True
def _add_new_table(table):
table = str(table).strip()
if "=" not in table:
return False
opt, fields = table.split('=')
try:
if fields == 'tid_search':
db = sqlite3.connect(DB_UPGRADE, check_same_thread=False)
db_cursor = db.cursor()
sql_commnd = "create table IF NOT EXISTS tid_search('id' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,\
'key' TEXT,\
'tid' TEXT) "
db_cursor.execute(sql_commnd)
db.commit()
db.close()
except Exception as e:
print(e)
return False
def CopyData():
try:
# 判断新字段是否存在
if (os.path.exists(VER_DB) and os.path.exists(DB_UPGRADE)):
print(_("Loading Sqlite3Server..."))
try:
new_db = sqlite3.connect(DB_UPGRADE, check_same_thread=False)
new_db_cursor = new_db.cursor()
ver_db = sqlite3.connect(VER_DB, check_same_thread=False)
ver_db_cursor = ver_db.cursor()
except Exception as e:
print(e)
if (_has_first_migration(new_db, new_db_cursor)): # 存在 firstmigration
if (_is_first_migration(new_db, new_db_cursor)):
# 数据迁移
dateMigration(new_db=new_db, new_db_cursor=new_db_cursor)
sql_commnd = "UPDATE display SET firstmigration='no';"
new_db_cursor.execute(sql_commnd)
new_db.commit()
else:
print("No data migration is required ...")
else:# 不存在firstmigration
# 新增 firstmigration 字段
sql_commnd = "alter table display add column firstmigration text;"
new_db_cursor.execute(sql_commnd)
sql_commnd = "UPDATE display SET firstmigration='yes';"
new_db_cursor.execute(sql_commnd)
#数据迁移
dateMigration(new_db=new_db, new_db_cursor=new_db_cursor)
sql_commnd = "UPDATE display SET firstmigration='no';"
new_db_cursor.execute(sql_commnd)
new_db.commit()
else :
print("Not found kylin-system-updater.db, ensure that \'kylin-system-updater\' is successfully installed ... ")
exit(-1)
except Exception as e:
print(e)
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 ("-o", "--old-path", dest="old_path",
help=_("Enter the old database address"))
parser.add_option ("-n", "--new-path", dest="new_path",
help=_("Enter the new database address"))
parser.add_option ("-c", "--clean-table", dest="clean_table",
help=_("Clear the table"))
parser.add_option ("-r", "--delete-table", dest="delete_table",
help=_("Delete the table"))
parser.add_option ("-m", "--data-migration", default=False, action="store_true",
dest="data_migration", help=_("data migration"))
parser.add_option ("-f", "--add-display-fields",
dest="add_display_fields", help=_("add display fields"))
parser.add_option ("-u", "--add-updateinfos-fields",
dest="add_updateinfos_fields", help=_("add updateinfos fields"))
parser.add_option ("-t", "--add-new-table",
dest="add_new_table", help=_("add new table"))
(options, args) = parser.parse_args()
if options.clean_table:
if ":" not in options.clean_table:
print("format error: <database:table>")
else:
CleanTable(str(options.clean_table))
if options.delete_table:
if ":" not in options.delete_table:
print("format error: <database:table>")
else:
DeleteTable(str(options.delete_table))
if options.add_display_fields:
_add_display_fields(str(options.add_display_fields))
if options.add_updateinfos_fields:
_add_display_fields(str(options.add_updateinfos_fields), default_table = False)
if options.add_new_table:
_add_new_table(str(options.add_new_table))
if options.data_migration:
CopyData()
exit(0)
if options.old_path or options.new_path:
# 检查文件
if not options.old_path or not options.new_path:
print("parameter error")
exit(-1)
if not os.path.isfile(options.old_path):
print("The source database file does not exist")
exit(-1)
if not os.path.isfile(options.new_path):
print("The destination database file does not exist")
exit(-1)
dateMigration(options)

View File

@ -0,0 +1,683 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from math import log10
import os
import re
import json
import yaml
import shutil
import sqlite3
import logging
import datetime
from gettext import gettext as _
from sys import exec_prefix
from SystemUpdater.Core.DataAcquisition import PHPSeverSend
from SystemUpdater.Core.UpdaterConfigParser import UpgradeConfig
from SystemUpdater.Core.utils import get_config_patch
import apt_pkg
from ..backend import InstallBackend
DB_FILE = os.path.join("/var/cache/kylin-system-updater/kylin-system-updater.db")
# UMDB_FILE = os.path.join("/var/cache/kylin-system-updater/kylin-system-updater.db")
INSTALLED_LIST = [{"item": "errorcode", "type": "int", "default": "0"}]
DISPALY_LIST = []
class Sqlite3Server(object):
def __init__(self, updateManager):
self.connect = None
self.window_main = updateManager
self.config_path = get_config_patch()
self.init_sqlit()
# uncoverable配置文件
self.ucconfigs = UpgradeConfig(datadir = "/etc/kylin-version", name = "kylin-system-version.conf")
self._system_version_config()
# 初始化连接数据库,修改为使用时连接
def init_sqlit(self):
try:
logging.info(_("Initialize database files ..."))
if not os.path.isfile(DB_FILE):
if not os.path.isdir(os.path.dirname(DB_FILE)):
os.makedirs(os.path.dirname(DB_FILE))
shutil.copy("/usr/share/kylin-system-updater/kylin-system-updater.db", os.path.dirname(DB_FILE))
except Exception as e:
logging.error("Failed to initialize database files: %s", str(e))
#connect连接数据库
def connect_database(self):
try:
logging.debug("Connect database ...")
self.connect = sqlite3.connect(DB_FILE, check_same_thread=False)
self.cursor = self.connect.cursor()
except Exception as e:
logging.error("Failed to connect database: %s", str(e))
#disconnect连接数据库
def disconnect_database(self):
try:
logging.debug("Disconnect database ...")
if self.connect != None:
self.connect.close()
if self.connect != None:
del self.cursor
except Exception as e:
logging.error("Failed to disconnect database: %s", str(e))
# 数据库表格中动态增加新的字段用于扩展
def insert_new_field(self):
if len(INSTALLED_LIST) == 0 and len(DISPALY_LIST) == 0:
return
self.cursor.execute("select sql from sqlite_master where name='installed'")
installed_sql = self.cursor.fetchone()[0]
pattern = re.compile(r'\"\w+\"')
installed_sql_list = pattern.findall(installed_sql)
for value in INSTALLED_LIST:
for field in installed_sql_list:
if value["item"] == str(field).strip("\""):
break
elif field == installed_sql_list[len(installed_sql_list) - 1]:
try:
if value["default"] != "":
sql = 'alter table installed add column "' + value["item"] + '" ' + value["type"] \
+ ' default ' + str(value["default"])
else:
sql = 'alter table installed add column "' + value["item"] + '" ' + value["type"]
self.cursor.execute(sql)
logging.info(_("installed table insert new field: %s"), value["item"])
except:
logging.error(_("installed table failed to insert a new field:"), value["item"], exc_info=True)
self.cursor.execute("select sql from sqlite_master where name='display'")
display_sql = self.cursor.fetchone()[0]
pattern = re.compile(r'\"\w+\"')
display_sql_list = pattern.findall(display_sql)
for value in DISPALY_LIST:
for field in display_sql_list:
if value["item"] == str(field).strip("\""):
break
elif field == display_sql_list[len(display_sql_list) - 1]:
try:
if value["default"] != "":
sql = 'alter table display add column "' + value["item"] + '" ' + value["type"] \
+ ' default ' + str(value["default"])
else:
sql = 'alter table installed add column "' + value["item"] + '" ' + value["type"]
self.cursor.execute(sql)
logging.info(_("display table insert new field %s"), value["item"])
except:
logging.error(_("display table failed to insert a new field %s"), value["item"], exc_info=True)
# 写入数据到installed表中
def insert_into_installed(self, *args, **kwargs):
self.connect_database()
self.cursor.execute(
"insert into installed (appname, version, time, description, icon, statue, keyword, errorcode) values(?,"
"?,?,?,?,?,?,?)", (args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]))
self.connect.commit()
logging.info("Database: Insert (%s=%s) To installed Complete ...", args[0], args[1])
self.disconnect_database()
# 写入数据到display表中
def insert_into_display(self, *args, **kwargs):
self.connect_database()
try:
sql = "update display set " + args[0] + "='" + args[1] + "' where id = 1"
self.cursor.execute(sql)
self.connect.commit()
except Exception as e:
logging.error("Insert error: %s.", str(e))
self.disconnect_database()
return False
logging.info("Database: Insert (%s=%s) To display Complete ...", args[0], args[1])
self.disconnect_database()
return True
# 写入数据到tid_search表中
def insert_into_tid(self, *args, **kwargs):
self.connect_database()
self.cursor.execute(
"insert into tid_search (key, tid) values(?,?)",
(args[0], args[1]))
self.connect.commit()
logging.info("Database: Insert (%s=%s) To tid_search Complete ...", args[0], args[1])
self.disconnect_database()
# 搜索tid_search表获取tid值
def select_from_tid(self, *args, **kwargs):
retval = ''
self.connect_database()
try:
sql = "select "+args[0]+" from tid_search where key='"+args[1]+"'"
self.cursor.execute(sql)
rets = self.cursor.fetchall()
if len(rets)!= 0:
if len(rets[0])!=0:
ret_first = rets[0]
retval = str(ret_first[0])
except Exception as e:
logging.error("Insert error: %s.", str(e))
self.disconnect_database()
logging.info("Database: Select tid_search data Complete...")
self.disconnect_database()
return retval
# 读出display表中数据
def select_from_display(self, *args, **kwargs):
self.connect_database()
try:
sql = "select "+args[0]+" from display"
self.cursor.execute(sql)
self.connect.commit()
retval = str(self.cursor.fetchone()[0])
except Exception as e:
logging.error("select error: %s.", str(e))
self.disconnect_database()
return "Error"
logging.info("Database: Search display Complete (%s) ...", args[0])
self.disconnect_database()
return retval
# 写入updateinfos表中
def insert_into_updateinfo(self, *args, **kwargs):
self.connect_database()
try:
self.cursor.execute(
"insert into updateinfos (appname, version, description, date, status, keyword, errorcode, appname_cn, status_cn, changelog) values(?,"
"?,?,?,?,?,?,?,?,?)",
(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]))
self.connect.commit()
except Exception as e:
logging.error("Insert error: %s.", str(e))
self.disconnect_database()
logging.info(_("Database: Insert To updateinfos Complete..."))
self.disconnect_database()
# 接收更新列表与信息,生成数据并插入数据库中
def insert_info(self, mode, pkg_list=[], pkg_group=[], adjust_pkg=[], success = False, error_string = '', error_desc = ''):
errstr = error_string + " " + error_desc
status = " "
status_cn = " "
appname_cn = ""
UpdateInfos = {}
InstallInfos = {}
time = datetime.datetime.now()
timestr = datetime.datetime.strftime(time, "%Y-%m-%d %H:%M:%S")
pkg_list = pkg_list.copy()
pkg_group = pkg_group.copy()
adjust_pkg = adjust_pkg.copy()
# 更新列表空,无更新
if not pkg_list and not pkg_group and mode != InstallBackend.MODE_INSTALL_SYSTEM:
logging.info("There is no update.")
return True
if success:
status = 'success'
status_cn = '成功'
else:
status = 'failed'
status_cn = '失败'
changeLog = ""
try:
# 判断更新方式
if mode == InstallBackend.MODE_INSTALL_PARTIAL: # 部分更新
pkg_adj = ""
# 判断更新包为单包或更新组
if pkg_group:
# 更新组
pkgname = pkg_group.pop(0)
pkgversion,pkgdescription,appname_cn = self.GetGroupmsg(pkgname)
#更新信息update-infos
UpdateInfos.update({"appname":str(pkgname)})
UpdateInfos.update({"source":"Kylin System Updater"})
UpdateInfos.update({"status":status})
UpdateInfos.update({"errorCode":str(error_string+" "+error_desc)})
self.window_main.collector.Upgrade_Process_Msg("finish-update", UpdateInfos.copy())
#安装信息install-infos
InstallInfos.update({"appname":str(pkgname)})
if pkgname in self.window_main.update_list.upgrade_meta.versoin_pkgs['groups_upgrade'].keys():
InstallInfos.update({"old_version":str(self.window_main.update_list.upgrade_meta.versoin_pkgs['groups_upgrade'][pkgname])})
else:
InstallInfos.update({"old_version":'UnKnown'})
InstallInfos.update({"new_version":str(pkgversion)})
InstallInfos.update({"status":status})
InstallInfos.update({"errorCode":str(error_string+" "+error_desc)})
self.window_main.collector.Upgrade_Process_Msg("finish-install", InstallInfos.copy())
# 系统升级完成 ..判断版本号
if status == "success" and "kylin-update-desktop-system" in pkgname:
# 更新版本号
if "=" in str(pkgversion):
pkgversion = str(pkgversion).split('=')[-1]
logging.info("Complete system upgrade, refresh system version ...")
self._refresh_system_version(pkgversion)
#移除step-two标记
self._removal_of_marker()
#FIXME: 临时方案 PHP
PHPSeverSend(_appname=pkgname, _appversion=pkgversion, _statue=status, _errorcode="10000100", _errorstring=errstr)
file_path = os.path.join(get_config_patch(), str(pkgname) + ".yaml")
with open(file_path, "r") as stream:
try:
data_yaml = yaml.safe_load(stream)
changeLog = data_yaml['changelog']
except yaml.YAMLError as exc:
logging.error(exc)
elif pkg_list:
changeLog = " "
# 单包更新 # 获取单包数据插入数据库
pkgname = pkg_list.pop(0)
for adj in adjust_pkg:
if pkgname in adj:
# 该部分升级的单包为调整版本,与候选版本不一致
pkg_adj = adj
break
if pkg_adj: # 有调整的情况
try:
pkg = self.window_main.cache[pkg_adj.split("=")[0]]
for ver in pkg.versions:
if ver.version == pkg_adj.split("=")[1]:
pkg_inst_ver = ver
break
pkgname = pkg_adj.split("=")[0]
pkgversion = str(pkg_inst_ver.source_version)
pkgdescription = str(pkg_inst_ver.description)
except Exception as e:
logging.error(_("%s could not be detected in the source because the source was changed or for other reasons."), \
str(pkgname))
logging.error(str(e))
else: # 没有调整的情况
try:
pkg = self.window_main.cache[pkgname]
pkgversion = str(pkg.candidate.version)
pkgdescription = str(pkg.candidate.raw_description)
except Exception as e:
logging.error(str(e))
#更新信息update-infos
UpdateInfos.update({"appname":str(pkgname)})
UpdateInfos.update({"source":"Kylin System Updater"})
UpdateInfos.update({"status":status})
UpdateInfos.update({"errorCode":str(error_string+" "+error_desc)})
self.window_main.collector.Upgrade_Process_Msg("finish-update", UpdateInfos.copy())
#安装信息install-infos
InstallInfos.update({"appname":str(pkgname)})
if pkgname in self.window_main.update_list.upgrade_meta.versoin_pkgs['single_upgrade'].keys():
InstallInfos.update({"old_version":str(self.window_main.update_list.upgrade_meta.versoin_pkgs['single_upgrade'][pkgname])})
else:
InstallInfos.update({"old_version":'UnKnown'})
InstallInfos.update({"new_version":str(pkgversion)})
InstallInfos.update({"status":status})
InstallInfos.update({"errorCode":str(error_string+" "+error_desc)})
self.window_main.collector.Upgrade_Process_Msg("finish-install", InstallInfos.copy())
# 软件商店获取中文名
appname_cn = self.get_cn_appname(str(pkgname))
#FIXME: 临时方案 PHP
PHPSeverSend(_appname=pkgname, _appversion=pkgversion, _statue=status, _errorcode="10000100", _errorstring=errstr)
try:
self.insert_into_updateinfo(pkgname, pkgversion, pkgdescription, timestr, status, "1", errstr, appname_cn, status_cn, changeLog)
# FIXME: 发送插入数据库成功的信号local_upgrade_list
self.window_main.dbusController.UpdateSqlitSingle(pkgname, timestr)
# 数据库文件被删除或者新增字段导致需要重新初始化数据库再写入
except Exception as e:
self.init_sqlit()
self.insert_into_updateinfo(pkgname, pkgversion, pkgdescription, timestr, status, "1", errstr, appname_cn, status_cn, changeLog)
# FIXME: 这里也需要, 发送插入数据库成功的信号
self.window_main.dbusController.UpdateSqlitSingle(pkgname, timestr)
elif mode == InstallBackend.MODE_INSTALL_ALL: # 系统全部升级
# # insert signal deb first
for i in pkg_list:
changeLog = ""
try:
pkg = self.window_main.cache[i]
except Exception as e:
logging.error(_("%s could not be detected in the source because the source was changed or for other reasons."), \
str(i))
continue
if not pkg:
continue
pkgversion = str(pkg.candidate.version)
pkgdescription = str(pkg.candidate.raw_description)
#更新信息update-infos
UpdateInfos.update({"appname":str(pkg.name)})
UpdateInfos.update({"source":"Kylin System Updater"})
UpdateInfos.update({"status":status})
UpdateInfos.update({"errorCode":str(error_string+" "+error_desc)})
self.window_main.collector.Upgrade_Process_Msg("finish-update", UpdateInfos.copy())
#安装信息install-infos
InstallInfos.update({"appname":str(pkg.name)})
if pkg.name in self.window_main.update_list.upgrade_meta.versoin_pkgs['groups_upgrade'].keys():
InstallInfos.update({"old_version":str(self.window_main.update_list.upgrade_meta.versoin_pkgs['groups_upgrade'][pkg.name])})
else:
InstallInfos.update({"old_version":'UnKnown'})
InstallInfos.update({"new_version":str(pkgversion)})
InstallInfos.update({"status":status})
InstallInfos.update({"errorCode":str(error_string+" "+error_desc)})
self.window_main.collector.Upgrade_Process_Msg("finish-install", InstallInfos.copy())
try:
# 软件商店获取中文名
appname_cn = self.get_cn_appname(str(i))
self.insert_into_updateinfo(str(i), pkgversion, pkgdescription, timestr, status, "1", errstr, appname_cn, status_cn, changeLog)
self.window_main.dbusController.UpdateSqlitSingle(str(i), timestr)
# 数据库文件被删除或者新增字段导致需要重新初始化数据库再写入
except Exception as e:
self.init_sqlit()
self.insert_into_updateinfo(str(i), pkgversion, pkgdescription, timestr, status, "1", errstr, appname_cn, status_cn, changeLog)
self.window_main.dbusController.UpdateSqlitSingle(str(i), timestr)
#FIXME: 临时方案 PHP
PHPSeverSend(_appname=pkg.name, _appversion=pkgversion, _statue=status, _errorcode="10000100", _errorstring=errstr)
# insert group deb next
for i in pkg_group:
# FIXME: 获取组信息
pkgversion,pkgdescription,appname_cn = self.GetGroupmsg(i)
#更新信息update-infos
UpdateInfos.update({"appname":str(i)})
UpdateInfos.update({"source":"Kylin System Updater"})
UpdateInfos.update({"status":status})
UpdateInfos.update({"errorCode":str(error_string+" "+error_desc)})
self.window_main.collector.Upgrade_Process_Msg("finish-update", UpdateInfos.copy())
#安装信息install-infos
InstallInfos.update({"appname":str(i)})
if i in self.window_main.update_list.upgrade_meta.versoin_pkgs['groups_upgrade'].keys():
InstallInfos.update({"old_version":str(self.window_main.update_list.upgrade_meta.versoin_pkgs['groups_upgrade'][i])})
else:
InstallInfos.update({"old_version":'UnKnown'})
InstallInfos.update({"new_version":str(pkgversion)})
InstallInfos.update({"status":status})
InstallInfos.update({"errorCode":str(error_string+" "+error_desc)})
json_file = json.dumps(InstallInfos.copy())
try:
self.window_main.collector.UpdateMsg("InstallInfos", json_file)
except:
pass
#FIXME: 临时方案 PHP
PHPSeverSend(_appname=i, _appversion=pkgversion, _statue=status, _errorcode="10000100", _errorstring=errstr)
file_path = os.path.join(get_config_patch(), str(i) + ".yaml")
with open(file_path, "r") as stream:
try:
data_yaml = yaml.safe_load(stream)
changeLog = data_yaml['changelog']
except yaml.YAMLError as exc:
logging.error(exc)
try:
self.insert_into_updateinfo(str(i), pkgversion, pkgdescription, timestr, status, "1", errstr, appname_cn, status_cn, changeLog)
self.window_main.dbusController.UpdateSqlitSingle(str(i), timestr)
# 数据库文件被删除或者新增字段导致需要重新初始化数据库再写入
except Exception as e:
self.init_sqlit()
self.insert_into_updateinfo(str(i), pkgversion, pkgdescription, timestr, status, "1", errstr, appname_cn, status_cn, changeLog)
self.window_main.dbusController.UpdateSqlitSingle(str(i), timestr)
# 系统升级完成 ..判断版本号
if status == "success" and "kylin-update-desktop-system" in pkg_group:
# 更新版本号
if "=" in str(pkgversion):
pkgversion = str(pkgversion).split('=')[-1]
logging.info("Complete system upgrade, refresh system version ...")
self._refresh_system_version(str(pkgversion))
#移除step-two标记
self._removal_of_marker()
elif mode == InstallBackend.MODE_INSTALL_SYSTEM: # 全盘升级
self.insert_into_updateinfo(_("Upgrade System"), "", "This is a complete system upgrade, equivalent to the implementation of apt dist-upgrade", timestr, status, "1", errstr, str("全盘升级"), status_cn, " ")
self.window_main.dbusController.UpdateSqlitSingle("Upgrade System", timestr)
# 全盘更新完成 ..判断版本号
if status == "success":
# 更新版本号
self._refresh_system_version(pseudo_version=True)
#移除step-two标记
self._removal_of_marker()
else:
logging.warning("Cache is None.")
except Exception as e:
logging.error("record update error: %s.",str(e))
# 获取group信息
def GetGroupmsg(self, appname):
jsonfile = appname+".json"
files = os.listdir(self.config_path) #获取文件夹中所有文件
if jsonfile in files: # 存在
# 读取组JSON文件
with open(self.config_path+jsonfile, "r") as f:
try :
data = json.load(f)
except json.JSONDecodeError as e:
logging.error(str(e))
try:
version = data['version']
if "=" in version:
version = version.split("=")[1].strip()
tmpdescription = data['description']
appname_cn = data['name']['zh_CN']
except Exception as e:
logging.error(str(e))
if "zh_CN" in tmpdescription and "en_US" in tmpdescription:
description = tmpdescription["zh_CN"] + ": " + tmpdescription["en_US"]
return (version,description,appname_cn)
else: # 不存在
return (None, None, None)
def refreshpkglist(self):
pkgs_install = []
pkgs_upgrade = []
pkgs_remove = []
for pkg in self.window_main.cache:
try:
if pkg.marked_install:
pkgs_install.append(pkg.name)
if pkg.marked_upgrade:
pkgs_upgrade.append(pkg.name)
elif pkg.marked_delete:
pkgs_remove.append(pkg.name)
except KeyError:
# pkg missing from fresh_cache can't be modified
pass
return pkgs_install,pkgs_upgrade,pkgs_remove
def _removal_of_marker(self):
try:
marker_path = "/var/cache/kylin-update-manager/ignoreOrDelay"
if os.path.exists(marker_path):
with open(marker_path, 'r+') as f:
line= f.readline()
if "2107" in line or "2203" in line:
f.seek(0)
f.truncate()
except Exception as e:
logging.error("Removing the upgrade success mark error: %s.",str(e))
# #查找数据库
# def find_msg_from_datebase(self, table, field, action = 'check', cid = 0):
# # 查询数据
# try:
# sql = "select "+field+" from "+table
# self.cursor.execute(sql)
# update_count = self.cursor.fetchone()[0]
# logging.info("%d history updates detected.", update_count)
# except Exception as e:
# logging.error("Check update error: %s", str(e))
def _system_version_config(self):
self.connect_database()
try:
sql = "select init_version from display where id=1"
self.cursor.execute(sql)
_is_init_verison = self.cursor.fetchone()[0]
if _is_init_verison == "yes":
update_version, os_version = self.get_default_version()
logging.info("Need to refresh version ...")
self._refresh_system_version(update_version, os_version)
sql = "update display set init_version = 'no'"
self.cursor.execute(sql)
self.connect.commit()
except Exception as e:
logging.error(str(e))
self.disconnect_database()
self.disconnect_database()
def _refresh_system_version(self, update_version='', os_version = '', pseudo_version = False):
try:
if "=" in update_version:
update_version = str(update_version).split('=')[-1]
if "=" in os_version:
os_version = str(os_version).split('=')[-1]
os_version = os_version.strip()
update_version = update_version.strip()
#刷新系统版本号:os_version
if update_version == '':
update_version, os_version = self.get_default_version()
if os_version == '' and os.path.isfile("/etc/os-release"):
with open("/etc/os-release", "r+") as f:
lines = f.readlines()
for line in lines:
if "KYLIN_RELEASE_ID" in line:
os_version = line.split('=')[1]
os_version = os_version.strip()
if not pseudo_version:
if len(os_version) != 0:
self.ucconfigs.setValue("SYSTEM","os_version",str(os_version),True)
if len(update_version) != 0:
self.ucconfigs.setValue("SYSTEM","update_version",str(update_version),True)
else:
current_update_version, current_os_version = self.get_current_version()
if current_os_version != os_version:
os_version+='*'
self.ucconfigs.setValue("SYSTEM","os_version",str(os_version),True)
if current_update_version != update_version:
update_version+='*'
self.ucconfigs.setValue("SYSTEM","update_version",str(update_version),True)
except Exception as e:
logging.error("Refresh system version error: %s.",str(e))
def get_default_version(self):
update_version = ""
os_version = ""
INPUT_CONFIG_PATH = self.config_path + 'kylin-update-desktop-system.json'
if os.path.isfile(INPUT_CONFIG_PATH): # 存在
# 读取JSON文件
with open(INPUT_CONFIG_PATH, "r") as f:
try :
data = json.load(f)
except json.JSONDecodeError as e:
logging.error(str(e))
try:
update_version = data['version']
if "=" in update_version:
update_version = update_version.split('=')[-1].strip()
except Exception as e:
logging.error("get_default_version error: %s .",str(e))
version_path = "/etc/os-release"
if os.path.isfile(version_path):
with open(version_path, "r+") as f:
lines = f.readlines()
for line in lines:
if "KYLIN_RELEASE_ID" in line:
os_version = line.split('=')[1]
os_version = eval(os_version.strip())
if update_version == "" and os_version != "":
update_version = os_version
elif update_version != "" and os_version == "":
os_version = update_version
return str(update_version),str(os_version)
def get_current_version(self):
os_version = ''
update_version = ''
try:
if not os.path.exists("/etc/kylin-version/kylin-system-version.conf"):
logging.warning("System version file doesn't exist.")
return os_version,update_version
os_version = eval(str(self.window_main.sqlite3_server.ucconfigs.get("SYSTEM","os_version")))
update_version = str(self.window_main.sqlite3_server.ucconfigs.get("SYSTEM","update_version"))
except Exception as e:
logging.error(str(e))
return update_version,os_version
logging.info('Current os_version: %s, release_id: %s .', os_version, update_version)
return str(update_version),str(os_version)
def get_cn_appname(self, name):
try:
SC_DB_FILE = "/usr/share/kylin-software-center/data/uksc.db"
if os.path.isfile(SC_DB_FILE):
connect = sqlite3.connect(SC_DB_FILE, check_same_thread=False)
cursor = connect.cursor()
else:
logging.warning("software center database isn't exist .")
return ""
sql = "select display_name_cn from application where display_name='"+name+"'"
cursor.execute(sql)
connect.commit()
retval = cursor.fetchone()
connect.close()
if retval != None and len(retval) != 0:
return str(retval[0])
else:
return ''
except Exception as e:
logging.error(_("Failed to initialize the database: %s"), str(e))
return ''
def insert_upgrade_history(self, args, caller):
caller_list = ['kylin-unattended-upgrade', "d-feet", "root"]
_in_list = False
for cl in caller_list:
if caller in cl:
_in_list = True
if _in_list == False:
logging.warning("Caller \" %s \": Operation without permission...", caller)
return False
# {"appname":GLib.Variant("s", "kylin-system-updater"), "version":GLib.Variant("s", "0.0")}
# "description":GLib.Variant("s", "Update Manager for Kylin"), "date":GLib.Variant("s", "2022-07-27 15:23:51")
# "status":GLib.Variant("s", "failed"), "keyword":GLib.Variant("s", "1")
# "errorcode":GLib.Variant("s", "System upgrade is complete. "), "appname_cn":GLib.Variant("s", "音乐")
upgrade_info = {}
try:
for it in args:
upgrade_info[str(it)] = str(args[str(it)])
logging.info("upgrade_info: %s", upgrade_info)
if "appname" in upgrade_info.keys() and "version" in upgrade_info.keys() \
and "description" in upgrade_info.keys() \
and "date" in upgrade_info.keys() \
and "status" in upgrade_info.keys() \
and "keyword" in upgrade_info.keys() \
and "errorcode" in upgrade_info.keys() \
and "appname_cn" in upgrade_info.keys() \
and "status_cn" in upgrade_info.keys() \
and "changelog" in upgrade_info.keys():
appname = upgrade_info["appname"]
if "kylin-unattended-upgrade" == appname:
upgrade_info["appname"] = self.get_cn_appname(appname)
if upgrade_info["appname"] == "":
upgrade_info["appname"] = _("kylin-unattended-upgrade")
if appname in self.window_main.cache and upgrade_info["description"] == "":
pkg = self.window_main.cache[appname]
if pkg.is_installed:
upgrade_info["description"] = pkg.installed.description
self.insert_into_updateinfo( upgrade_info["appname"], upgrade_info["version"], \
upgrade_info["description"], \
upgrade_info["date"], \
upgrade_info["status"], \
upgrade_info["keyword"], \
upgrade_info["errorcode"], \
upgrade_info["appname_cn"], \
upgrade_info["status_cn"], \
upgrade_info["changelog"] )
else:
logging.warning("Incomplete field.")
return False
except Exception as e:
logging.error(e)
return False
return True
def listtojsonstr(lists):
import json
jsonfile = json.dumps(lists)
return jsonfile

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,124 @@
import os
from gettext import gettext as _
import apt
import logging
import fcntl
import apt_pkg
class LogInstallProgress(apt.progress.base.InstallProgress):
""" Install progress that writes to self.progress_log
(/var/run/unattended-upgrades.progress by default)
"""
def __init__(self,file):
# type: (str) -> None
apt.progress.base.InstallProgress.__init__(self)
self.output_logfd = None # type: int
self.filename=file
self.error_pkg=""
self.errormsg=""
# raise Exception("for test!!!")
def error(self,pkg, errormsg):
logging.error(("Install mode - dpkg, Install error: %s"), errormsg)
self.error_pkg=self.filename
self.errormsg = errormsg
def status_change(self, pkg, percent, status):
# type: (str, float, str) -> None
logging.info(("pkg:%s,percent:%s,status:%s"),pkg,percent,status)
with open(self.progress_log, "w") as f:
f.write(("%s")%percent)
f.write(_("当前进度: %s ,正在安装:%s,当前状态:%s") % (percent, pkg,status))
f.write(_("Progress: %s %% (%s)") % (percent, pkg))
def _fixup_fds(self):
# () -> None
required_fds = [0, 1, 2, # stdin, stdout, stderr
self.writefd,
self.write_stream.fileno(),
self.statusfd,
self.status_stream.fileno()
]
# ensure that our required fds close on exec
for fd in required_fds[3:]:
old_flags = fcntl.fcntl(fd, fcntl.F_GETFD)
fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC)
# close all fds
try:
# PEP-446 implemented in Python 3.4 made all descriptors
# CLOEXEC, but we need to be able to pass writefd to dpkg
# when we spawn it
os.set_inheritable(self.writefd, True)
except AttributeError: # if we don't have os.set_inheritable()
pass
proc_fd = "/proc/self/fd"
if os.path.exists(proc_fd):
error_count = 0
for fdname in os.listdir(proc_fd):
try:
fd = int(fdname)
except Exception:
print("ERROR: can not get fd for %s" % fdname)
if fd in required_fds:
continue
try:
os.close(fd)
# print("closed: ", fd)
except OSError as e:
# there will be one fd that can not be closed
# as its the fd from pythons internal diropen()
# so its ok to ignore one close error
error_count += 1
if error_count > 1:
print("ERROR: os.close(%s): %s" % (fd, e))
def _redirect_stdin(self):
# type: () -> None
REDIRECT_INPUT = os.devnull
fd = os.open(REDIRECT_INPUT, os.O_RDWR)
os.dup2(fd, 0)
def _redirect_output(self):
# type: () -> None
# do not create log in dry-run mode, just output to stdout/stderr
if not apt_pkg.config.find_b("Debug::pkgDPkgPM", False):
logfd = self._get_logfile_dpkg_fd()
os.dup2(logfd, 1)
os.dup2(logfd, 2)
def _get_logfile_dpkg_fd(self):
# type: () -> int
logfd = os.open(
"/var/log/kylin-system-updater/kylin-system-updater.log.1", os.O_RDWR | os.O_APPEND | os.O_CREAT, 0o640)
try:
import grp
adm_gid = grp.getgrnam("adm").gr_gid
os.fchown(logfd, 0, adm_gid)
except (KeyError, OSError):
pass
return logfd
def update_interface(self):
# type: () -> None
# call super class first
apt.progress.base.InstallProgress.update_interface(self)
def _log_in_dpkg_log(self, msg):
# type: (str) -> None
logfd = self._get_logfile_dpkg_fd()
os.write(logfd, msg.encode("utf-8"))
os.close(logfd)
def finish_update(self):
# if error_status == 1:
# os._exit(1)
pass
def fork(self):
pid = os.fork()
if pid == 0:
self._fixup_fds()
self._redirect_stdin()
self._redirect_output()
return pid

View File

@ -0,0 +1,69 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
#参考文档: https://cuiqingcai.com/6080.html
import os
path = '/var/log/kylin-system-updater/'
numlist = []
def get_FileSize(filePath):
fsize = os.path.getsize(filePath)
fsize = fsize / float(1024 * 1024)
return round(fsize, 2)
#日志回滚
def get_logfile():
if not os.path.exists(path):
os.makedirs(path)
#优先获取当前未写满100M的日志编号文件
for i in os.listdir(path):
if "kylin-system-updater.log." in os.path.basename(path + i):
numlist.append((path + i).split(".")[2])
if get_FileSize(path + i) < 100:
return path + i
#获取1-5未使用的最小数字的标号作为日志文件
for i in range(1, 6):
if str(i) not in numlist:
return(os.path.join(path, ("kylin-system-updater.log.%s") % i))
try:
#编号1-5日志文件均写满时删除第一个往前移动日志编号获取最后一个编号作为日志文件
if len(numlist) != 0:
os.remove(os.path.join(path, "kylin-system-updater.log.1"))
for i in range(2, 6):
os.rename(os.path.join(path, ("kylin-system-updater.log.%s") % i),
os.path.join(path, ("kylin-system-updater.log.%s") % (i - 1)))
return os.path.join(path, "kylin-system-updater.log.5")
#默认情况下未生成任何日志时使用编号1的日志文件
else:
return os.path.join(path, "kylin-system-updater.log.1")
except:
return os.path.join(path, "kylin-system-updater.log.1")
def upgrade_strategies_logfile():
if not os.path.exists(path):
os.makedirs(path)
#优先获取当前未写满100M的日志编号文件
for i in os.listdir(path):
if "upgrade-strategies-daemon.log." in os.path.basename(path + i):
numlist.append((path + i).split(".")[2])
if get_FileSize(path + i) < 10:
return path + i
#获取1-5未使用的最小数字的标号作为日志文件
for i in range(1, 6):
if str(i) not in numlist:
return(os.path.join(path, ("upgrade-strategies-daemon.log.%s") % i))
try:
#编号1-5日志文件均写满时删除第一个往前移动日志编号获取最后一个编号作为日志文件
if len(numlist) != 0:
os.remove(os.path.join(path, "upgrade-strategies-daemon.log.1"))
for i in range(2, 6):
os.rename(os.path.join(path, ("upgrade-strategies-daemon.log.%s") % i),
os.path.join(path, ("upgrade-strategies-daemon.log.%s") % (i - 1)))
return os.path.join(path, "upgrade-strategies-daemon.log.5")
#默认情况下未生成任何日志时使用编号1的日志文件
else:
return os.path.join(path, "upgrade-strategies-daemon.log.1")
except:
return os.path.join(path, "upgrade-strategies-daemon.log.1")

View File

@ -0,0 +1,434 @@
# MyCache.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
#
# Copyright (c) 2004-2008 Canonical
#
# Author: Michael Vogt <mvo@debian.org>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 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 General Public License for more details.
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
from __future__ import absolute_import, print_function
import warnings
warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning)
import apt
import apt_pkg
import logging
import os
from urllib.error import HTTPError
from urllib.request import urlopen
from urllib.parse import urlsplit
from http.client import BadStatusLine
import socket
import re
import SystemUpdater.Core.DistUpgradeCache
from gettext import gettext as _
try:
from launchpadlib.launchpad import Launchpad
except ImportError:
Launchpad = None
CHANGELOGS_POOL = "https://changelogs.ubuntu.com/changelogs/pool/"
CHANGELOGS_URI = CHANGELOGS_POOL + "%s/%s/%s/%s_%s/%s"
class HttpsChangelogsUnsupportedError(Exception):
""" https changelogs with credentials are unsupported because of the
lack of certitifcation validation in urllib2 which allows MITM
attacks to steal the credentials
"""
pass
class MyCache(SystemUpdater.Core.DistUpgradeCache.MyCache):
CHANGELOG_ORIGIN = "Ubuntu"
def __init__(self, progress, rootdir=None):
apt.Cache.__init__(self, progress, rootdir)
# save for later
self.rootdir = rootdir
# raise if we have packages in reqreinst state
# and let the caller deal with that (runs partial upgrade)
assert len(self.req_reinstall_pkgs) == 0
# check if the dpkg journal is ok (we need to do that here
# too because libapt will only do it when it tries to lock
# the packaging system)
if self._dpkgJournalDirty() == True:
logging.info("dpkg was interrupted, you must manually run 'sudo dpkg --configure -a' to correct the problem.")
# init the regular cache
self._initDepCache()
self.all_changes = {}
self.all_news = {}
# on broken packages, try to fix via saveDistUpgrade()
# if self._depcache.broken_count > 0:
# self.saveDistUpgrade()
# assert (self._depcache.broken_count == 0
# and self._depcache.del_count == 0)
self.launchpad = None
def _dpkgJournalDirty(self):
"""
test if the dpkg journal is dirty
(similar to debSystem::CheckUpdates)
"""
d = os.path.dirname(
apt_pkg.config.find_file("Dir::State::status")) + "/updates"
for f in os.listdir(d):
if re.match("[0-9]+", f):
return True
return False
def _initDepCache(self):
self._depcache.read_pinfile()
self._depcache.init()
def clear(self):
self._initDepCache()
@property
def required_download(self):
""" get the size of the packages that are required to download """
pm = apt_pkg.PackageManager(self._depcache)
fetcher = apt_pkg.Acquire()
pm.get_archives(fetcher, self._list, self._records)
return fetcher.fetch_needed
@property
def install_count(self):
return self._depcache.inst_count
def keep_count(self):
return self._depcache.keep_count
@property
def del_count(self):
return self._depcache.del_count
def _check_dependencies(self, target, deps):
"""Return True if any of the dependencies in deps match target."""
# TODO: handle virtual packages
for dep_or in deps:
if not dep_or:
continue
match = True
for base_dep in dep_or:
if (base_dep.name != target.package.shortname
or not apt_pkg.check_dep(
target.version, base_dep.relation, base_dep.version)):
match = False
if match:
return True
return False
def find_removal_justification(self, pkg):
target = pkg.installed
if not target:
return False
for cpkg in self:
candidate = cpkg.candidate
if candidate is not None:
if (self._check_dependencies(
target, candidate.get_dependencies("Conflicts"))
and self._check_dependencies(
target, candidate.get_dependencies("Replaces"))):
logging.info(
"%s Conflicts/Replaces %s; allowing removal" % (
candidate.package.shortname, pkg.shortname))
return True
return False
def saveDistUpgrade(self):
""" this functions mimics a upgrade but will never remove anything """
#upgrade(True) 为True时使用dist-upgrade进行升级
self._depcache.upgrade(True)
wouldDelete = self._depcache.del_count
wouldDelete = 0
if wouldDelete > 0:
deleted_pkgs = [pkg for pkg in self if pkg.marked_delete]
assert wouldDelete == len(deleted_pkgs)
for pkg in deleted_pkgs:
if self.find_removal_justification(pkg):
wouldDelete -= 1
if wouldDelete > 0:
self.clear()
assert (self._depcache.broken_count == 0
and self._depcache.del_count == 0)
# else:
# assert self._depcache.broken_count == 0
self._depcache.upgrade()
return wouldDelete
def _strip_epoch(self, verstr):
" strip of the epoch "
vers_no_epoch = verstr.split(":")
if len(vers_no_epoch) > 1:
verstr = "".join(vers_no_epoch[1:])
return verstr
def _get_changelog_or_news(self, name, fname, strict_versioning=False,
changelogs_uri=None):
" helper that fetches the file in question "
# don't touch the gui in this function, it needs to be thread-safe
pkg = self[name]
# get the src package name
srcpkg = pkg.candidate.source_name
# assume "main" section
src_section = "main"
# use the section of the candidate as a starting point
section = pkg._pcache._depcache.get_candidate_ver(pkg._pkg).section
# get the source version
srcver_epoch = pkg.candidate.source_version
srcver = self._strip_epoch(srcver_epoch)
split_section = section.split("/")
if len(split_section) > 1:
src_section = split_section[0]
# lib is handled special
prefix = srcpkg[0]
if srcpkg.startswith("lib"):
prefix = "lib" + srcpkg[3]
# the changelogs_uri argument overrides the default changelogs_uri,
# this is useful for e.g. PPAs where we construct the changelogs
# path differently
if changelogs_uri:
uri = changelogs_uri
else:
uri = CHANGELOGS_URI % (src_section, prefix, srcpkg, srcpkg,
srcver, fname)
# https uris are not supported when they contain a username/password
# because the urllib2 https implementation will not check certificates
# and so its possible to do a man-in-the-middle attack to steal the
# credentials
res = urlsplit(uri)
if res.scheme == "https" and res.username:
raise HttpsChangelogsUnsupportedError(
"https locations with username/password are not"
"supported to fetch changelogs")
# print("Trying: %s " % uri)
changelog = urlopen(uri)
#print(changelog.read())
# do only get the lines that are new
alllines = ""
regexp = "^%s \\((.*)\\)(.*)$" % (re.escape(srcpkg))
while True:
line = changelog.readline().decode("UTF-8", "replace")
if line == "":
break
match = re.match(regexp, line)
if match:
# strip epoch from installed version
# and from changelog too
installed = getattr(pkg.installed, "version", None)
if installed and ":" in installed:
installed = installed.split(":", 1)[1]
changelogver = match.group(1)
if changelogver and ":" in changelogver:
changelogver = changelogver.split(":", 1)[1]
# we test for "==" here for changelogs
# to ensure that the version
# is actually really in the changelog - if not
# just display it all, this catches cases like:
# gcc-defaults with "binver=4.3.1" and srcver=1.76
#
# for NEWS.Debian we do require the changelogver > installed
if strict_versioning:
if (installed
and apt_pkg.version_compare(changelogver,
installed) < 0):
break
else:
if (installed
and apt_pkg.version_compare(changelogver,
installed) == 0):
break
alllines = alllines + line
return alllines
def _extract_ppa_changelog_uri(self, name):
"""Return the changelog URI from the Launchpad API
Return None in case of an error.
"""
if not Launchpad:
logging.warning("Launchpadlib not available, cannot retrieve PPA "
"changelog")
return None
cdt = self[name].candidate
for uri in cdt.uris:
if urlsplit(uri).hostname != 'ppa.launchpad.net':
continue
match = re.search('http.*/(.*)/(.*)/ubuntu/.*', uri)
if match is not None:
user, ppa = match.group(1), match.group(2)
break
else:
logging.error("Unable to find a valid PPA candidate URL.")
return
# Login on launchpad if we are not already
if self.launchpad is None:
self.launchpad = Launchpad.login_anonymously('update-manager',
'production',
version='devel')
archive = self.launchpad.archives.getByReference(
reference='~%s/ubuntu/%s' % (user, ppa)
)
if archive is None:
logging.error("Unable to retrieve the archive from the Launchpad "
"API.")
return
spphs = archive.getPublishedSources(source_name=cdt.source_name,
exact_match=True,
version=cdt.source_version)
if not spphs:
logging.error("No published sources were retrieved from the "
"Launchpad API.")
return
return spphs[0].changelogUrl()
def _guess_third_party_changelogs_uri_by_source(self, name):
pkg = self[name]
deb_uri = pkg.candidate.uri
if deb_uri is None:
return None
srcrec = pkg.candidate.record.get("Source")
if not srcrec:
return None
# srcpkg can be "apt" or "gcc-default (1.0)"
srcpkg = srcrec.split("(")[0].strip()
if "(" in srcrec:
srcver = srcrec.split("(")[1].rstrip(")")
else:
srcver = pkg.candidate.source_version
base_uri = deb_uri.rpartition("/")[0]
return base_uri + "/%s_%s.changelog" % (srcpkg, srcver)
def _guess_third_party_changelogs_uri_by_binary(self, name):
""" guess changelogs uri based on ArchiveURI by replacing .deb
with .changelog
"""
# there is always a pkg and a pkg.candidate, no need to add
# check here
pkg = self[name]
deb_uri = pkg.candidate.uri
if deb_uri:
return "%s.changelog" % deb_uri.rsplit(".", 1)[0]
return None
def get_news_and_changelog(self, name, lock):
self.get_news(name)
self.get_changelog(name)
try:
lock.release()
except Exception:
pass
def get_news(self, name):
" get the NEWS.Debian file from the changelogs location "
try:
news = self._get_changelog_or_news(name, "NEWS.Debian", True)
except Exception:
return
if news:
self.all_news[name] = news
def _fetch_changelog_for_third_party_package(self, name, origins):
# Special case for PPAs
changelogs_uri_ppa = None
for origin in origins:
if origin.origin.startswith('LP-PPA-'):
try:
changelogs_uri_ppa = self._extract_ppa_changelog_uri(name)
break
except Exception:
logging.exception("Unable to connect to the Launchpad "
"API.")
# Try non official changelog location
changelogs_uri_binary = \
self._guess_third_party_changelogs_uri_by_binary(name)
changelogs_uri_source = \
self._guess_third_party_changelogs_uri_by_source(name)
error_message = ""
for changelogs_uri in [changelogs_uri_ppa,
changelogs_uri_binary,
changelogs_uri_source]:
if changelogs_uri:
try:
changelog = self._get_changelog_or_news(
name, "changelog", False, changelogs_uri)
self.all_changes[name] += changelog
except (HTTPError, HttpsChangelogsUnsupportedError):
# no changelogs_uri or 404
error_message = _(
"This update does not come from a "
"source that supports changelogs.")
except (IOError, BadStatusLine, socket.error):
# network errors and others
logging.exception("error on changelog fetching")
error_message = _(
"Failed to download the list of changes. \n"
"Please check your Internet connection.")
self.all_changes[name] += error_message
def get_changelog(self, name):
" get the changelog file from the changelog location "
origins = self[name].candidate.origins
self.all_changes[name] = _("Changes for %s versions:\n"
"Installed version: %s\n"
"Available version: %s\n\n") % \
(name, getattr(self[name].installed, "version", None),
self[name].candidate.version)
if self.CHANGELOG_ORIGIN not in [o.origin for o in origins]:
self._fetch_changelog_for_third_party_package(name, origins)
return
# fixup epoch handling version
srcpkg = self[name].candidate.source_name
srcver_epoch = self[name].candidate.source_version.replace(':', '%3A')
try:
changelog = self._get_changelog_or_news(name, "changelog")
if len(changelog) == 0:
changelog = _("The changelog does not contain any relevant "
"changes.\n\n"
"Please use http://launchpad.net/ubuntu/+source/"
"%s/%s/+changelog\n"
"until the changes become available or try "
"again later.") % (srcpkg, srcver_epoch)
except HTTPError:
changelog = _("The list of changes is not available yet.\n\n"
"Please use http://launchpad.net/ubuntu/+source/"
"%s/%s/+changelog\n"
"until the changes become available or try again "
"later.") % (srcpkg, srcver_epoch)
except (IOError, BadStatusLine, socket.error) as e:
print("caught exception: ", e)
changelog = _("Failed to download the list "
"of changes. \nPlease "
"check your Internet "
"connection.")
self.all_changes[name] += changelog

View File

@ -0,0 +1,580 @@
#!/usr/bin/python3
import apt
import apt_pkg
import fnmatch
import logging
import logging.handlers
import re
import os
import sys
import string
import subprocess
import json
try:
from typing import List
from typing import Union
except ImportError:
pass
from gettext import gettext as _
SYSTEM_UPDATER_CORE_LIB_PATH="/usr/share/kylin-system-updater/"
sys.path.append(SYSTEM_UPDATER_CORE_LIB_PATH)
from SystemUpdater.Core.utils import get_config_patch
ImportantListPath="/var/lib/kylin-software-properties/template/important.list"
SOURCESLIST = "/etc/apt/sources.list"
# no py3 lsb_release in debian :/
DISTRO_CODENAME = subprocess.check_output(
["lsb_release", "-c", "-s"], universal_newlines=True).strip() # type: str
DISTRO_DESC = subprocess.check_output(
["lsb_release", "-d", "-s"], universal_newlines=True).strip() # type: str
DISTRO_ID = subprocess.check_output(
["lsb_release", "-i", "-s"], universal_newlines=True).strip() # type: str
ARCHITECTUREMAP = ['arm64','amd64','armhf','i386','loongarch64','mips64el','sw64']
RELEASEOFFSET = 1
ORIGINOFFSET = 2
HTTPTYPE = "HTTP"
FTPTYPE = "FTP"
class UpdateListFilterCache(apt.Cache):
def __init__(self, window_main):
self.window_main = window_main
# whitelist
self.upgradeList = []
# 必须升级的包
self.installList = []
self.config_path = get_config_patch()
# 获取源属性
self.origin_property = OriginProperty()
self.origin_property.get_allowed_sources()
self.origin_property.get_allowed_origin()
self.allowed_origins = get_allowed_origins(self.origin_property.allow_origin)
self.allowed_origins = deleteDuplicatedElementFromList(self.allowed_origins)
logging.info(_("Allowed origins: %s"),
self.allowed_origins)
# self.blacklist = apt_pkg.config.value_list(
# "Kylin-system-updater::Package-Blacklist")
# self.blacklist = deleteDuplicatedElementFromList(self.blacklist)
# self.whitelist = apt_pkg.config.value_list(
# "Kylin-system-updater::Package-Whitelist")
# self.whitelist = deleteDuplicatedElementFromList(self.whitelist)
# self.strict_whitelist = apt_pkg.config.find_b(
# "Kylin-system-updater::Package-Whitelist-Strict", False)
def checkInCache(self):
logging.info("start Check in cache")
tmplist = []
cache = apt.Cache()
for i in self.upgradeList:
try:
cache[i]
tmplist.append(i)
except Exception as e:
pass
self.upgradeList = tmplist
def initLocalPackagesList(self):
jsonfiles = []
tmplist = []
# 获取importantlist 本次更新推送
with open(ImportantListPath, 'r') as f:
text = f.read()
importantList = text.split()
logging.info("importantList: %s",importantList)
f.close()
if not importantList:
logging.error("importantList is empty")
exit(-1)
# 获取/usr/share/kylin-update-desktop-config/data/下所有json文件
for root,dirs,files in os.walk(self.config_path):
pass
for i in files:
if ".json" in i:
jsonfiles.append(i.split('.')[0])
# 找到importantlist中对应的json文件
for i in importantList:
if i not in jsonfiles:
# 说明这个是单独的包,不在分组中
# 加入更新列表
if i not in self.upgradeList:
self.upgradeList.append(i)
else:
# 在分组中
# 获取每个对应json文件中的upgrade_list
if i in jsonfiles:
filepath = os.path.join(self.config_path, i)
filepath = filepath+".json"
with open(filepath, 'r') as f:
pkgdict = f.read()
jsonfile = json.loads(pkgdict)
tmplist = jsonfile['install_list']
for j in tmplist:
if j not in self.upgradeList:
self.upgradeList.append(j)
f.close()
# 更改传入包列表经过源过滤返回的pkg中进行版本调整
def check_in_allowed_origin(self, pkg_lists, _is_adjust):
new_upgrade_pkgs = []
adjust_candidate_pkgs = []
for pkg in pkg_lists:
try:
new_ver = ver_in_allowed_origin(pkg, self.allowed_origins)
if _is_adjust and len(new_ver) == 0:
logging.warning("< %s > did not find a suitable version..." % pkg.name)
continue
if len(new_ver) == 0:
continue
if not pkg.installed: # 判断安装列表
if pkg.candidate == new_ver[0] and pkg not in new_upgrade_pkgs:
new_upgrade_pkgs.append(pkg)
elif new_ver[0] != pkg.candidate and pkg not in new_upgrade_pkgs:
logging.info("adjusting candidate version: %s" % new_ver[0])
if _is_adjust == True:
pkg.candidate = new_ver[0]
adjust_candidate_pkgs.append(pkg.name+"="+pkg.candidate.version)
new_upgrade_pkgs.append(pkg)
else: # 判断升级列表
for nv in new_ver:
if nv > pkg.installed and nv != pkg.candidate:
logging.info("adjusting candidate version: %s" % nv)
if _is_adjust == True:
pkg.candidate = nv
adjust_candidate_pkgs.append(pkg.name+"="+pkg.candidate.version)
break
elif nv > pkg.installed and nv == pkg.candidate:
new_upgrade_pkgs.append(pkg)
break
elif _is_adjust == True:
logging.warning("< %s > did not find a suitable version..." % pkg.name)
except NoAllowedOriginError:
logging.error("Cannot found allowed version: %s", pkg.name)
continue
return (new_upgrade_pkgs, adjust_candidate_pkgs)
def is_pkgname_in_blacklist(self, pkgs):
blacklist_filter_pkgs = []
for pkg in pkgs:
if pkg.name in self.blacklist:
pass
else :
blacklist_filter_pkgs.append(pkg)
return blacklist_filter_pkgs
def is_pkgname_in_whitelist(self, pkgs):
whitelist_filter_upgrade_pkgs = []
for pkg in pkgs:
if pkg.name in self.upgradeList:
whitelist_filter_upgrade_pkgs.append(pkg)
else :
pkg.mark_keep()
return whitelist_filter_upgrade_pkgs
class OriginProperty():
def __init__(self):
# 包含了本地所有源 http & ftp
self.local_sourcelist = {"http":[],"ftp":[]}
# 经过解析后的本地源,获取所有的分发属性
self.local_origin = {"http":[],"ftp":[]}
# 允许的源列表
self.allow_sources = []
# 允许的源+属性
self.allow_origin = {"http":[],"ftp":[]}
# 加载本地所有源
self.init_local_origin()
# 进行属性解析
self.analytic_properties(self.local_sourcelist)
def init_local_origin(self):
http_origin = {}
ftp_orgin = {}
#apt policy
sh_retval = os.popen("apt-cache policy").read().split("\n")
# policy = [ rv for rv in sh_retval if "http" in rv or "ftp" in rv or "release" in rv or "origin" in rv]
for rv in sh_retval:
if "http" in rv:
http_origin['sources'] = rv
http_origin['release'] = sh_retval[sh_retval.index(rv) + RELEASEOFFSET]
http_origin['origin'] = sh_retval[sh_retval.index(rv) + ORIGINOFFSET]
self.local_sourcelist['http'].append(http_origin.copy())
elif "ftp" in rv:
ftp_orgin['sources'] = rv
ftp_orgin['release'] = sh_retval[sh_retval.index(rv) + RELEASEOFFSET]
ftp_orgin['origin'] = sh_retval[sh_retval.index(rv) + ORIGINOFFSET]
self.local_sourcelist['ftp'].append(ftp_orgin.copy())
def merge_origin(self, source_type, source_origin):
is_append = True
if source_type == HTTPTYPE:
if self.local_origin['http']:
for lo in self.local_origin['http']:
if lo['origin_source'] == source_origin['origin_source'] and lo['dist'] == source_origin['dist']:
lo['component'] = list(set(lo['component']).union(set(source_origin['component'])))
is_append = False
if is_append:
self.local_origin['http'].append(source_origin.copy())
else:
self.local_origin['http'].append(source_origin.copy())
elif source_type == FTPTYPE:
if self.local_origin['ftp']:
for lo in self.local_origin['ftp']:
if lo['origin_source'] == source_origin['origin_source'] and lo['dist'] == source_origin['dist']:
lo['component'] = list(set(lo['component']).union(set(source_origin['component'])))
is_append = False
if is_append:
self.local_origin['ftp'].append(source_origin.copy())
else:
self.local_origin['ftp'].append(source_origin.copy())
def analytic_properties(self, local_sourcelist):
http_origin = {"component":[],"release":{}}
ftp_orgin = {"component":[],"release":{}}
dist_list = []
# 经过解析后的本地源,获取所有的分发属性
for ls in local_sourcelist['http']:
for item in filter(not_empty, ls['sources'].split(' ')):
if item.isdigit():
http_origin['policy_priority'] = item
elif "http" in item:
http_origin['origin_source'] = item
elif "/" in item:
dist_list = item.split("/")
dist_list.pop()
http_origin['dist'] = "/".join(dist_list)
http_origin['component'].append(item.split("/")[1])
elif item not in ARCHITECTUREMAP and item != "Packages":
http_origin['component'].append(item)
release_list = ls['release'].split(',')
release_list = [ rl.strip() for rl in release_list ]
if "release" in release_list[0]:
release_list[0] = release_list[0].lstrip("release").strip()
for rl in release_list:
if "=" in rl:
self.generate_dict(http_origin['release'], rl)
for item in filter(not_empty, ls['origin'].split(' ')):
if "origin" not in ls['origin']:
break
elif "origin" != item:
http_origin['origin'] = item
self.merge_origin(HTTPTYPE, http_origin)
http_origin = {"component":[],"release":{}}
for ls in local_sourcelist['ftp']:
for item in filter(not_empty, ls['sources'].split(' ')):
if item.isdigit():
ftp_orgin['policy_priority'] = item
elif "ftp" in item:
ftp_orgin['origin_source'] = item
elif "/" in item:
ftp_orgin['dist'] = item.split("/")[0]
ftp_orgin['component'].append(item.split("/")[1])
elif item not in ARCHITECTUREMAP and item != "Packages":
ftp_orgin['component'].append(item)
release_list = ls['release'].split(',')
if "release " in release_list[0]:
release_list[0] = release_list[0].lstrip("release ")
for rl in release_list:
if "=" in rl:
self.generate_dict(ftp_orgin['release'], rl)
for item in filter(not_empty, ls['origin'].split(' ')):
if "origin" not in ls['origin']:
break
elif "origin" != item:
ftp_orgin['origin'] = item
self.merge_origin(FTPTYPE, ftp_orgin)
ftp_orgin = {"component":[],"release":{}}
def generate_dict(self, dict, item):
item = item.strip()
if item == "":
logging.warning("empty match string matches nothing")
return False
(what, value) = [ s for s in item.split("=")]
if what in ('o', 'origin'):
dict['origin'] = value
elif what in ("l", "label"):
dict['label'] = value
elif what in ("a", "suite", "archive"):
dict['archive'] = value
elif what in ("c", "component"):
dict['component'] = value
elif what in ("site",):
dict['site'] = value
elif what in ("n", "codename",):
dict['codename'] = value
else:
dict[what] = value
# raise UnknownMatcherError(
# "Unknown whitelist entry for matcher %s (value %s)" % (
# what, value))
def get_allowed_sources(self):
# 源地址,在本地源列表中查找. 源服务器下发source.list为允许的源, 本模块屏蔽了sources.list.d下的源
# 获取允许的源
try:
old_sources_list = apt_pkg.config.find("Dir::Etc::sourcelist")
old_sources_list_d = apt_pkg.config.find("Dir::Etc::sourceparts")
old_cleanup = apt_pkg.config.find("APT::List-Cleanup")
apt_pkg.config.set("Dir::Etc::sourcelist",
os.path.abspath(SOURCESLIST))
apt_pkg.config.set("Dir::Etc::sourceparts", "xxx")
apt_pkg.config.set("APT::List-Cleanup", "0")
slist = apt_pkg.SourceList()
slist.read_main_list()
self.allow_sources = slist.list
except Exception as e:
logging.error(str(e))
finally:
apt_pkg.config.set("Dir::Etc::sourcelist",
old_sources_list)
apt_pkg.config.set("Dir::Etc::sourceparts",
old_sources_list_d)
apt_pkg.config.set("APT::List-Cleanup",
old_cleanup)
def get_allowed_origin(self):
# 获取允许的源
# 生成源与属性
self.local_origin
self.allow_sources
self.allow_origin
try:
for item in self.allow_sources:
for lo in self.local_origin['http']:
if item.uri.strip('/') == lo['origin_source'].strip('/') and item.dist == lo['dist']:
self.allow_origin['http'].append(lo)
for lo in self.local_origin['ftp']:
if item.uri.strip('/') == lo['origin_source'].strip('/') and item.dist == lo['dist']:
self.allow_origin['ftp'].append(lo)
except Exception as e:
logging.error(str(e))
class UnattendUpgradeFilter():
def __init__(self) -> None:
pass
def GetAllowOrigins(self):
# 获取源属性
self.origin_property = OriginProperty()
self.origin_property.get_allowed_sources()
self.origin_property.get_allowed_origin()
self.allowed_origins = get_allowed_origins(self.origin_property.allow_origin)
self.allowed_origins = deleteDuplicatedElementFromList(self.allowed_origins)
logging.info(_("Allowed origins: %s"),
self.allowed_origins)
return self.allowed_origins
def ver_in_allowed_origin(pkg, allow_origin):
# type: (apt.Package, List[str]) -> apt.package.Version
allown_versions = []
versions = _get_priority_order(pkg)
# 获取每个优先级别中 允许源的最高版本
allown_versions = _get_allowed_list(versions, allow_origin)
return allown_versions
def _get_priority_order(pkg):
versions = []
for ver in pkg.versions:
if versions:
for v in versions:
if v.policy_priority >= ver.policy_priority and v == versions[-1]:
break
elif v.policy_priority >= ver.policy_priority and v != versions[-1]:
continue
else:
index = versions.index(v)
versions.insert(index,ver)
break
if v == versions[-1] and versions[-1].policy_priority >= ver.policy_priority:
versions.append(ver)
else:
versions.append(ver)
return versions
def _get_allowed_list(versions, allow_origin):
current_priority = -100
allown_versions = []
for ver in versions:
if current_priority != ver.policy_priority:
if is_in_allowed_origin(ver, allow_origin):
allown_versions.append(ver)
current_priority = ver.policy_priority
else:
continue
return allown_versions
def get_allowed_origins(allow_origin):
""" return a list of allowed origins
"""
allowed_origins = []
origin = ''
archive = ''
uri = ''
label = ''
for ao in (allow_origin['http']+allow_origin['ftp']):
if 'origin' in ao['release']:
origin = 'o='+ao['release']['origin']
else:
origin = 'o='
if 'archive' in ao['release']:
archive = 'a='+ao['release']['archive']
else:
archive = 'a='
if 'label' in ao['release']:
label = 'l='+ao['release']['label']
else:
label = 'l='
if 'origin_source' in ao:
uri = 'uri='+ao['origin_source']
else:
uri = 'uri='
allowed_origins.append(origin+","+archive+","+label+","+uri)
return allowed_origins
def get_allowed_origins_legacy():
# type: () -> List[str]
""" legacy support for old Allowed-Origins var """
allowed_origins = [] # type: List[str]
key = "Kylin-system-updater::Allowed-Origins"
try:
for s in apt_pkg.config.value_list(key):
# if there is a ":" use that as seperator, else use spaces
if re.findall(r'(?<!\\):', s):
(distro_id, distro_codename) = re.split(r'(?<!\\):', s)
else:
(distro_id, distro_codename) = s.split()
# unescape "\:" back to ":"
distro_id = re.sub(r'\\:', ':', distro_id)
# escape "," (see LP: #824856) - can this be simpler?
distro_id = re.sub(r'([^\\]),', r'\1\\,', distro_id)
distro_codename = re.sub(r'([^\\]),', r'\1\\,', distro_codename)
# convert to new format
allowed_origins.append("o=%s,a=%s" % (substitute(distro_id),
substitute(distro_codename)))
except ValueError:
logging.error(_("Unable to parse %s." % key))
raise
return allowed_origins
def substitute(line):
# type: (str) -> str
""" substitude known mappings and return a new string
Currently supported ${distro-release}
"""
mapping = {"distro_codename": get_distro_codename(),
"distro_id": get_distro_id()}
return string.Template(line).substitute(mapping)
def get_distro_codename():
# type: () -> str
return DISTRO_CODENAME
def get_distro_id():
# type: () -> str
return DISTRO_ID
def is_in_allowed_origin(ver, allowed_origins):
# type: (apt.package.Version, List[str]) -> bool
if not ver:
return False
for origin in ver.origins:
if is_allowed_origin(origin, allowed_origins):
return True
return False
def is_allowed_origin(origin, allowed_origins):
# type: (Union[apt.package.Origin, apt_pkg.PackageFile], List[str]) -> bool
for allowed in allowed_origins:
if match_whitelist_string(allowed, origin):
return True
return False
def match_whitelist_string(whitelist, origin):
# type: (str, Union[apt.package.Origin, apt_pkg.PackageFile]) -> bool
"""
take a whitelist string in the form "origin=Debian,label=Debian-Security"
and match against the given python-apt origin. A empty whitelist string
never matches anything.
"""
whitelist = whitelist.strip()
if whitelist == "":
logging.warning("empty match string matches nothing")
return False
res = True
# make "\," the html quote equivalent
whitelist = whitelist.replace("\\,", "%2C")
for token in whitelist.split(","):
# strip and unquote the "," back
(what, value) = [s.strip().replace("%2C", ",")
for s in token.split("=")]
# logging.debug("matching %s=%s against %s" % (
# what, value, origin))
# support substitution here as well
value = substitute(value)
# first char is apt-cache policy output, send is the name
# in the Release file
if what in ("o", "origin"):
match = fnmatch.fnmatch(origin.origin, value)
elif what in ("l", "label"):
match = fnmatch.fnmatch(origin.label, value)
elif what in ("a", "suite", "archive"):
match = fnmatch.fnmatch(origin.archive, value)
elif what in ("c", "component"):
match = fnmatch.fnmatch(origin.component, value)
elif what in ("site",):
match = fnmatch.fnmatch(origin.site, value)
elif what in ("n", "codename",):
match = fnmatch.fnmatch(origin.codename, value)
elif what in ("uri",):
match = True
else:
raise UnknownMatcherError(
"Unknown whitelist entry for matcher %s (token %s)" % (
what, token))
# update res
res = res and match
# logging.debug("matching %s=%s against %s" % (
# what, value, origin))
return res
def deleteDuplicatedElementFromList(list):
resultList = []
for item in list:
if not item in resultList:
resultList.append(item)
return resultList
def not_empty(s):
return s and s.strip()
class UnknownMatcherError(ValueError):
pass
class NoAllowedOriginError(ValueError):
pass

View File

@ -0,0 +1,633 @@
# UpdateList.py
from gettext import gettext as _
import logging
import os
import json
import yaml
import shutil
from gi.repository import Gio
from .OriginFilter import UpdateListFilterCache
from .errors import *
from .enums import *
from SystemUpdater.Core.utils import get_config_patch
class LocalUpgradeDataList:
"""
Represent the (potentially partial) results of an unattended-upgrades
run
"""
def __init__(self,
groups_pkgs={},
upgrade_groups=[],
single_pkgs=[],
adjust_pkgs=[],
):
#可升级的组列表
self.upgrade_groups = upgrade_groups
#组列表中包含的包
self.groups_pkgs = groups_pkgs
#推送的可升级的单包
self.single_pkgs = single_pkgs
#调整版本列表 源过滤
self.adjust_pkgs = adjust_pkgs
#加版本号的升级包
self.versoin_pkgs = {'single_upgrade':{}, 'groups_upgrade':{}}
class UpdateList():
OUTPUT_CONFIG_PATH = '/var/lib/kylin-system-updater/json/'
IMPORTANT_LIST_PATH = '/var/lib/kylin-software-properties/template/important.list'
def __init__(self,parent):
self.parent = parent
#所有的组升级安装列表
self.upgrade_meta = LocalUpgradeDataList({},[],[],[])
if 'XDG_CURRENT_DESKTOP' in os.environ:
self.current_desktop = os.environ.get('XDG_CURRENT_DESKTOP')
else:
self.current_desktop = ''
if 'XDG_DATA_DIRS' in os.environ and os.environ['XDG_DATA_DIRS']:
data_dirs = os.environ['XDG_DATA_DIRS']
else:
data_dirs = '/usr/local/share/:/usr/share/'
self.application_dirs = [os.path.join(base, 'applications')
for base in data_dirs.split(':')]
self.config_path = get_config_patch()
if self.parent.install_mode.check_filter() == True:
#开启原过滤
self.fu = UpdateListFilterCache(self.parent)
else:
self.fu = None
logging.info("Close to Allowed origin fiter...")
#清空上次输出的分组JSON文件
def _empty_output_dir(self):
#清空 升级列表
if not os.path.exists(self.OUTPUT_CONFIG_PATH):
os.makedirs(self.OUTPUT_CONFIG_PATH)
logging.info('making the ConfigPath(%s) is complete...',self.OUTPUT_CONFIG_PATH)
else:
shutil.rmtree(self.OUTPUT_CONFIG_PATH)
os.makedirs(self.OUTPUT_CONFIG_PATH)
logging.info('Emptying the ConfigPath(%s) is complete...',self.OUTPUT_CONFIG_PATH)
#读取推送列表,判断分组和单包推送,再进行源过滤
def _make_important_list(self,cache,pkgs_upgrade,important_list = []):
upgradeable_pkgs = []
tmp = []
upgradeable_groups = []
logging.info("The Server Push List: %a",important_list)
for pkg_name in important_list:
#检查是否在cache 没有在cache中属于组
if pkg_name in cache:
pkg_obj = cache[pkg_name]
#在可升级的列表当中 此步骤为了排除已安装不需要升级的
if pkg_obj.is_installed:
if pkg_name in pkgs_upgrade:
pkgs_upgrade.remove(pkg_name)
tmp.append(pkg_obj)
else:
tmp.append(pkg_obj)
else:
upgradeable_groups.append(pkg_name)
if tmp != []:
install_list,upgrade_list,adjust_pkgs = self._make_fiter_origin(tmp,True)
self.upgrade_meta.adjust_pkgs.extend(adjust_pkgs)
upgradeable_pkgs = install_list + upgrade_list
logging.info("Push Single Packages: %a, Push Groups:%a",upgradeable_pkgs,upgradeable_groups)
return upgradeable_groups,upgradeable_pkgs
def _make_pkg_info_json(self,cache,pkgs_list):
total_download_size = 0
total_installed_size = 0
pkgs_info_json = {}
for pkg_name in pkgs_list:
pkg = cache[pkg_name]
#当前版本
cur_version = getattr(pkg.installed, "version", '')
new_version = getattr(pkg.candidate, "version", '')
#获取下载大小
download_size = getattr(pkg.candidate, "size", 0)
installed_size = getattr(pkg.candidate, "installed_size", 0)
total_download_size = total_download_size + download_size
total_installed_size = total_installed_size + installed_size
pkgs_info_json.update({pkg_name:{"cur_version":cur_version,"new_version":new_version,\
"download_size":str(download_size),"install_size":str(installed_size)}})
pkgs_info_json.update({"total_download_size":str(total_download_size)})
pkgs_info_json.update({"total_install_size":str(total_installed_size)})
return pkgs_info_json
#检查包是否在cache中 返回新得列表没 有安装的话才添加到列表
def _check_pkg_in_cache(self,cache,pkgs_list):
new_pkgs_list = []
for pkg_name in pkgs_list:
#检查是否在cache 以及 是否安装检查
if pkg_name in cache and not cache[pkg_name].is_installed:
new_pkgs_list.append(pkg_name)
else:
pass
return new_pkgs_list
def _make_group_output_json(self,data,data_yaml,upgrade_pkgs_json,install_pkgs_json):
groups_base_info = {}
output_json = {}
#FIXME: 确定输出文件的文件名 以及放置位置
output_config_name = self.OUTPUT_CONFIG_PATH + data['package'] + '.json'
#4、添加一些基础信息
groups_base_info.update({"package":data['package']})
groups_base_info.update({"new_version":data['version']})
groups_base_info.update({"name":data['name']})
groups_base_info.update({"description":data['description']})
groups_base_info.update({"icon":data['icon']})
#添加读yaml文件
groups_base_info.update({"changelog":data_yaml['changelog']})
#5、添加升级的内容
output_json.update(groups_base_info)
output_json.update({"upgrade_list":upgrade_pkgs_json})
output_json.update({"install_list":install_pkgs_json})
# output_json.update({"hold_list":hold_pkgs_list})
# output_json.update({"remove_list":remove_pkgs_list})
#6 产生JSON文件
with open(output_config_name, 'w', encoding='utf-8') as f:
json.dump(output_json, f, ensure_ascii=False, indent=4)
logging.info("Generate Jsonfile(%s) to complete... ",output_config_name)
#进行源过滤is_adjust 是否调整cache中的候选版本单包推送会调整保持控制面板显示正确的版本
def _make_fiter_origin(self,pkgs_list,adjust_versions):
install_pkgs = []
upgrade_pkgs = []
adjust_pkgs = []
#是否进行源过滤的选项
if self.fu != None:
try:
after_pkgs_list,adjust_pkgs = self.fu.check_in_allowed_origin(pkgs_list,adjust_versions)
except Exception as e:
after_pkgs_list = pkgs_list
logging.error("Check Allowed origin is occur error:" + str(e))
else:
after_pkgs_list = pkgs_list
adjust_pkgs = []
for pkg_obj in after_pkgs_list:
if pkg_obj.is_installed:
upgrade_pkgs.append(pkg_obj.name)
else:
install_pkgs.append(pkg_obj.name)
return install_pkgs,upgrade_pkgs,adjust_pkgs
#从本地中获取本次升级需要升级的包 部分升级和全部升级使用 全盘升级不适用
def _make_pkgs_list(self,cache,groups_pkgs,groups_list,pkg_list):
pkgs_install = []
pkgs_upgrade = []
#单包的升级方式
for pkg in pkg_list:
if cache[pkg].is_installed:
pkgs_upgrade.append(pkg)
else:
pkgs_install.append(pkg)
#遍历升级组列表
for group_name in groups_list:
pkgs_install += groups_pkgs.get(group_name,[]).get('pkgs_install',[])
pkgs_upgrade += groups_pkgs.get(group_name,[]).get('pkgs_upgrade',[])
return pkgs_install,pkgs_upgrade
#输出白名单的配置
def _make_autoupgrade_config(self,cache,upgrade_data,_adjust_pkgs):
pkgs_install,pkgs_upgrade = self._make_pkgs_list(cache,upgrade_data.groups_pkgs,upgrade_data.upgrade_groups,upgrade_data.single_pkgs)
split_adjust_pkgs = [i.split("=")[0] for i in _adjust_pkgs]
output_config_name = self.OUTPUT_CONFIG_PATH + 'auto-upgrade-list.json'
output_json = {}
install_info = {}
for pkg in pkgs_install:
pkg_cache = cache[pkg]
pkgs_json = {}
pkgs_json.update({"cur_version":getattr(pkg_cache.installed, "version", '')})
if pkg in split_adjust_pkgs:
version_adjust = _adjust_pkgs[split_adjust_pkgs.index(pkg)].split("=")[1]
pkgs_json.update({"new_version":version_adjust})
else:
pkgs_json.update({"new_version":getattr(pkg_cache.candidate, "version", '')})
install_info.update({pkg:pkgs_json})
upgrade_json = {}
for pkg in pkgs_upgrade:
pkg_cache = cache[pkg]
pkgs_json = {}
pkgs_json.update({"cur_version":getattr(pkg_cache.installed, "version", '')})
if pkg in split_adjust_pkgs:
version_adjust = _adjust_pkgs[split_adjust_pkgs.index(pkg)].split("=")[1]
pkgs_json.update({"new_version":version_adjust})
else:
pkgs_json.update({"new_version":getattr(pkg_cache.candidate, "version", '')})
upgrade_json.update({pkg:pkgs_json})
group_json = {}
for ug in self.upgrade_meta.groups_pkgs:
pkgs_json = {}
with open(self.config_path + str(ug) + ".yaml", "r") as stream:
try:
data_yaml = yaml.safe_load(stream)
pkgs_json.update({"cur_version":""})
pkgs_json.update({"new_version":data_yaml["version"]})
pkgs_json.update({"changelog":data_yaml["changelog"]})
except yaml.YAMLError as exc:
logging.error(exc)
group_json.update({ug:pkgs_json})
single_json = {}
for us in self.upgrade_meta.single_pkgs:
pkg_cache = cache[us]
pkgs_json = {}
pkgs_json.update({"cur_version":getattr(pkg_cache.installed, "version", '')})
if pkg in split_adjust_pkgs:
version_adjust = _adjust_pkgs[split_adjust_pkgs.index(pkg)].split("=")[1]
pkgs_json.update({"new_version":version_adjust})
else:
pkgs_json.update({"new_version":getattr(pkg_cache.candidate, "version", '')})
pkgs_json.update({"changelog":""})
single_json.update({us:pkgs_json})
output_json.update({"upgrade_list":upgrade_json})
output_json.update({"install_list":install_info})
output_json.update({"group_json":group_json})
output_json.update({"single_json":single_json})
#产生JSON文件
with open(output_config_name, 'w', encoding='utf-8') as f:
json.dump(output_json, f, ensure_ascii=False, indent=4)
logging.info("Generate AutoUpgrade Configfile to Complete and Jsonfile(%s) to complete... ",output_config_name)
def _split_package_id(self,package):
"""Return the name, the version number and the release of the
specified package."""
if "=" in package:
name, version = package.split("=", 1)
release = None
elif "/" in package:
name, release = package.split("/", 1)
version = None
else:
name = package
version = release = None
return name, version, release
def _make_downgrade(self,cache,downgrade_pkgs):
output_downgrade = []
adjust_pkgs = []
for pkg_name, pkg_ver, pkg_rel in [self._split_package_id(pkg)
for pkg in downgrade_pkgs]:
try:
pkg = cache[pkg_name]
except KeyError:
logging.warning("Package %s isn't available",pkg_name)
continue
if not pkg.is_installed:
logging.warning("Package %s isn't installed",pkg_name)
if pkg_ver:
if pkg.installed and pkg.installed.version < pkg_ver:
logging.warning("The former version %s of %s is already installed",pkg.installed.version, pkg.name)
continue
elif pkg.installed and pkg.installed.version == pkg_ver:
logging.warning("The version %s of %s is already installed",pkg.installed.version, pkg.name)
continue
try:
pkg.candidate = pkg.versions[pkg_ver]
except KeyError:
logging.warning("The version %s of %s isn't available",pkg_ver, pkg_name)
continue
output_downgrade.append(pkg_name)
adjust_pkgs.append(pkg_name+'='+pkg_ver)
return output_downgrade,adjust_pkgs
def _get_downgrade_list(self,cache,data):
downgrade_pkgs = []
try:
downgrade_raw = data['force_install_list']
except Exception as e:
downgrade_raw = []
for pkg_name, pkg_ver, pkg_rel in [self._split_package_id(pkg)
for pkg in downgrade_raw]:
if pkg_name in cache:
downgrade_pkgs.append(pkg_name)
else:
logging.warning("Package %s isn't available",pkg_name)
continue
return downgrade_raw,downgrade_pkgs
def _make_groups_pkgs(self,cache,data,pkgs_upgrade = []):
upgrade_pkgs_list = data['upgrade_list']
#检查包是否在cache中 以及是否已经安装 没有安装的话才添加到列表
new_install_list = self._check_pkg_in_cache(cache,data['install_list'])
downgrade_raw,downgrade_pkgs = self._get_downgrade_list(cache,data)
#被降级的软件包优先级最高
for pkg in downgrade_pkgs:
if pkg in upgrade_pkgs_list:
upgrade_pkgs_list.remove(pkg)
if pkg in new_install_list:
new_install_list.remove(pkg)
if pkg in self.upgrade_meta.single_pkgs:
self.upgrade_meta.single_pkgs.remove(pkg)
#进行交集 升级列表
new_upgrade_list = list(set(pkgs_upgrade) & set(upgrade_pkgs_list))
#进行源过滤
new_install_list,new_upgrade_list,adjust_pkgs = self._make_fiter_origin([cache[pkg] for pkg in new_install_list + new_upgrade_list],False)
self.upgrade_meta.adjust_pkgs.extend(adjust_pkgs)
#在总升级列表中移除这些包
for pkg in new_upgrade_list:
pkgs_upgrade.remove(pkg)
downgrade_pkg,adjust_pkgs = self._make_downgrade(cache,downgrade_raw)
self.upgrade_meta.adjust_pkgs.extend(adjust_pkgs)
new_upgrade_list.extend(downgrade_pkg)
#单包的优先级最高 从组中剔除此包
for pkg in self.upgrade_meta.single_pkgs:
if pkg in new_install_list:
new_install_list.remove(pkg)
return new_install_list,new_upgrade_list
def _make_groups_upgrade(self,cache,group_list,is_openkylin,pkgs_install,pkgs_upgrade):
upgrade_list = []
install_list = []
if os.path.isdir(self.config_path) == False:
logging.warning("configPath(%s) is not exists...",self.config_path)
return
files = os.listdir(self.config_path) #获得文件夹中所有文件的名称列表
for ifile in files:
#判是否是目录以及是否以JSON结尾
if ifile.endswith('.json'):
#读取组JSON文件
with open(self.config_path+ifile,'r') as f:
try:
data = json.load(f)
except Exception as exc:
logging.error(exc)
raise UpdateBaseError(ERROR_LOAD_CONFIG_FAILED)
group_name = data['package']
#读取组的yaml 文件的changelog的信息
with open(self.config_path + group_name + ".yaml", "r") as stream:
try:
data_yaml = yaml.safe_load(stream)
except Exception as exc:
logging.error(exc)
raise UpdateBaseError(ERROR_LOAD_CONFIG_FAILED)
#过滤没有推送的配置文件
if not group_name in group_list:
continue
if is_openkylin == True:
install_list,upgrade_list = pkgs_install,pkgs_upgrade
else:
install_list,upgrade_list = self._make_groups_pkgs(cache,data,pkgs_upgrade)
#判断当前是否可升级或者新装的包
if len(install_list) == 0 and len(upgrade_list) == 0:
continue
#3、生成升级的包列表JSON
upgrade_pkgs_json = self._make_pkg_info_json(cache,upgrade_list)
#2、生成安装的软件列表
install_pkgs_json = self._make_pkg_info_json(cache,install_list)
#输出JSON配置文件
self._make_group_output_json(data,data_yaml,upgrade_pkgs_json,install_pkgs_json)
#保存分组版本号,好像没有
self.upgrade_meta.versoin_pkgs['groups_upgrade'].update({group_name:''})
#添加到字典维护的升级列表
self.upgrade_meta.upgrade_groups.append(group_name)
self.upgrade_meta.groups_pkgs.update({group_name:{"pkgs_upgrade":upgrade_list,"pkgs_install":install_list}})
logging.info("Group(%s) upgrade:%d install:%d",group_name,len(upgrade_list),len(install_list))
else:
pass
def _make_openkylin_output_json(self,upgrade_pkgs_json,install_pkgs_json):
groups_base_info = {}
output_json = {}
#FIXME: 确定输出文件的文件名 以及放置位置
output_config_name = self.OUTPUT_CONFIG_PATH + "kylin-update-desktop-system.json"
#4、添加一些基础信息
groups_base_info.update({"package":"kylin-update-desktop-system"})
groups_base_info.update({"new_version":"33797.0001"})
groups_base_info.update({"name":{"zh_CN": "系统更新","en_US": "Kylin OS"}})
groups_base_info.update({"description":{"zh_CN": "Openkylin-系统更新包","en_US": "Openkylin-System Update Package"}})
groups_base_info.update({"icon":" "})
#添加读yaml文件
groups_base_info.update({"changelog":"Openkylin-系统更新包\n"})
#5、添加升级的内容
output_json.update(groups_base_info)
output_json.update({"upgrade_list":upgrade_pkgs_json})
output_json.update({"install_list":install_pkgs_json})
#6 产生JSON文件
with open(output_config_name, 'w', encoding='utf-8') as f:
json.dump(output_json, f, ensure_ascii=False, indent=4)
logging.info("Generate Jsonfile(%s) to complete... ",output_config_name)
def _rate_application_for_package(self, application, pkg):
score = 0
desktop_file = os.path.basename(application.get_filename())
application_id = os.path.splitext(desktop_file)[0]
if application.should_show():
score += 1
if application_id == pkg.name:
score += 5
return score
def _file_is_application(self, file_path):
# WARNING: This is called often if there's a lot of updates. A poor
# performing call here has a huge impact on the overall performance!
if not file_path.endswith(".desktop"):
# First the obvious case: If the path doesn't end in a .desktop
# extension, this isn't a desktop file.
return False
file_path = os.path.abspath(file_path)
for app_dir in self.application_dirs:
if file_path.startswith(app_dir):
return True
return False
def get_application_for_package(self, pkg):
desktop_files = []
rated_applications = []
for installed_file in pkg.installed_files:
if self._file_is_application(installed_file):
desktop_files.append(installed_file)
for desktop_file in desktop_files:
try:
application = Gio.DesktopAppInfo.new_from_filename(
desktop_file)
application.set_desktop_env(self.current_desktop)
except Exception as e:
logging.warning("Error loading .desktop file %s: %s" %
(desktop_file, e))
continue
score = self._rate_application_for_package(application, pkg)
if score > 0:
rated_applications.append((score, application))
rated_applications.sort(key=lambda app: app[0], reverse=True)
if len(rated_applications) > 0:
return rated_applications[0][1]
else:
return None
def _make_single_upgrade(self,cache,pkg_list):
for pkg in pkg_list:
zh_name = ''
base_info = {}
output_json = {}
output_config_name = self.OUTPUT_CONFIG_PATH + pkg + '.json'
pkg_cache = cache[pkg]
#获取包的软件名称,从主题中读取,只有可升级,是软件的包才可以读取
if pkg_cache.is_installed:
app = self.get_application_for_package(pkg_cache)
if app is not None:
zh_name = app.get_display_name()
pkgs_json = self._make_pkg_info_json(cache,[pkg])
en_name = getattr(pkg_cache.candidate, "summary", '')
description_str = getattr(pkg_cache.candidate, "description", '')
#4、添加一些基础信息
base_info.update({"package":pkg})
base_info.update({"cur_version":getattr(pkg_cache.installed, "version", '')})
base_info.update({"new_version":getattr(pkg_cache.candidate, "version", '')})
base_info.update({"name":{"zh_CN":zh_name,"en_US":en_name}})
base_info.update({"description":{"zh_CN":description_str,"en_US":description_str}})
base_info.update({"icon":''})
#5、添加升级的内容
output_json.update(base_info)
if pkg_cache.is_installed:
output_json.update({"upgrade_list":pkgs_json})
output_json.update({"install_list":{}})
else:
output_json.update({"upgrade_list":{}})
output_json.update({"install_list":pkgs_json})
#产生JSON文件
with open(output_config_name, 'w', encoding='utf-8') as f:
json.dump(output_json, f, ensure_ascii=False, indent=4)
logging.info("Generate Jsonfile(%s) to complete... ",output_config_name)
#6、保存单包版本号
self.upgrade_meta.versoin_pkgs['single_upgrade'].update({pkg_cache.name:getattr(pkg_cache.installed, "version", '')})
def _make_distupgrade(self,cache):
pkgs_upgrade = []
pkgs_install = []
if cache.get_changes():
cache.clear()
cache._depcache.upgrade(True)
#查找所有可升级的包
for pkg in cache:
try:
if pkg.marked_install:
pkgs_install.append(pkg.name)
elif pkg.marked_upgrade:
pkgs_upgrade.append(pkg.name)
except KeyError:
pass
return pkgs_install,pkgs_upgrade
def update_kylin(self,cache,important_data,is_openkylin = False):
pkgs_install = []
pkgs_upgrade = []
#查找所有可升级的包
if is_openkylin == True:
pkgs_install,pkgs_upgrade = self._make_distupgrade(cache)
else:
for pkg in cache:
if pkg.is_upgradable and pkg.is_installed:
pkgs_upgrade.append(pkg.name)
logging.info("System all upgradeable packages:upgrade:%d install:%d ",len(pkgs_upgrade),len(pkgs_install))
group_important_list,self.upgrade_meta.single_pkgs = self._make_important_list(cache,pkgs_upgrade,important_data)
#清空输出的目录
self._empty_output_dir()
#important_list 为空时此次不需要升级
if not group_important_list and not self.upgrade_meta.single_pkgs:
self.parent.dbusController.UpdateDetectFinished(True,[],'','')
return
#产生单包的JSON
self._make_single_upgrade(cache,self.upgrade_meta.single_pkgs)
#分组的包的JSON
self._make_groups_upgrade(cache,group_important_list,is_openkylin,pkgs_install,pkgs_upgrade)
self._make_autoupgrade_config(cache,self.upgrade_meta,self.upgrade_meta.adjust_pkgs)
self.parent.dbusController.UpdateDetectFinished(True,self.upgrade_meta.upgrade_groups + self.upgrade_meta.single_pkgs,'','')
return

View File

@ -0,0 +1,105 @@
#!/usr/bin/python3
# DistUpgradeConfigParser.py
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
from configparser import NoOptionError
from configparser import ConfigParser as SafeConfigParser
import os.path
import logging
class UpgradeConfig(SafeConfigParser):
def __init__(self, datadir="/var/lib/kylin-system-updater/", name="system-updater.conf",defaults_dir=None):
SafeConfigParser.__init__(self)
self.datadir = datadir
maincfg = os.path.join(datadir, name)
# defaults are read first
self.config_files = []
if defaults_dir:
self.config_files.append(os.path.join(datadir, defaults_dir))
# our config file
self.config_files += [maincfg]
self.read(self.config_files)
logging.info("Initialize Upgrade ConfigFile(%s) to success",str(self.config_files))
def optionxform(self, optionstr):
return optionstr
def reReadConfigFiles(self):
self.read(self.config_files)
def setValue(self, section, option, value=None,is_write = True):
if option != 'upgradelist':
logging.info("SetValue Section:%s Option:%s Value:%s",section, option, value)
try:
self.reReadConfigFiles()
self.set(section, option,value)
except Exception as e:
logging.error("Error: setValue section:%s option:%s value:%s",section, option, value)
logging.error(str(e))
return False
if is_write == True:
with open(self.config_files[-1], 'w+') as configfile:
self.write(configfile)
return True
def getWithDefault(self, section, option, default,re_read=False):
try:
if re_read == True:
self.reReadConfigFiles()
if type(default) == bool:
return self.getboolean(section, option)
elif type(default) == float:
return self.getfloat(section, option)
elif type(default) == int:
return self.getint(section, option)
return self.get(section, option)
except Exception as e:
logging.error(str(e))
return default
def getlist(self, section, option):
try:
tmp = self.get(section, option)
except Exception as e:
logging.error(str(e))
return []
items = [x.strip() for x in tmp.split(" ")]
if '' in items and len(items):
return []
return items
def setListValue(self, section, option, value=None,is_write = True):
tmp = str(value).replace('[', '').replace(']', '')
tmp = tmp.replace("'", '').replace(',', '')
try:
self.set(section, option,tmp)
except Exception as e:
logging.error("setListValue section:%s option:%s",section, option)
logging.error(str(e))
return
if is_write == True:
with open(self.config_files[-1], 'w+') as configfile:
self.write(configfile)
def getListFromFile(self, section, option):
try:
filename = self.get(section, option)
except NoOptionError:
return []
p = os.path.join(self.datadir, filename)
if not os.path.exists(p):
logging.error("getListFromFile: no '%s' found" % p)
with open(p) as f:
items = [x.strip() for x in f]
return [s for s in items if not s.startswith("#") and not s == ""]
if __name__ == "__main__":
# c = UpgradeConfig("/home/x/share/kylin-system-updater/backend/data/")
# print(c.setValue("SystemStatus", "abnormal_reboot", str(False)),True)
# print(c.getWithDefault("SystemStatus", "abnormal_reboot", False))
pass

View File

View File

@ -0,0 +1,169 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""enums - Enumerates for apt daemon dbus messages"""
__all__ = (
"ERROR_UPDATE_DEFAULT_FAILED",
"ERROR_UPDATE_SOURCE_FAILED","ERROR_NETWORK_FAILED","ERROR_NOT_GROUPS_CONFIG","ERROR_SOFTWARE_INDEX_RROKEN",
"ERROR_NOT_INIT_PACKAGESINFIO","ERROR_READ_IMPORTANTLIST_FAILED","ERROR_RESOLVER_FAILED","ERROR_NOT_UPGRADE_PACKAGES",
"ERROR_REMOVE_ESSENTIAL_PACKAGES","ERROR_NOT_DISK_SPACE","ERROR_NOT_CONFIGPKG_DEPENDENCIES","ERROR_NOT_SELFPKG_DEPENDENCIES",
"ERROR_NOT_FIX_SYSTEM","ERROR_READ_LOCAL_DEB","ERROR_LOCAL_DEB_FORMAT","ERROR_INSTALL_DEB_BASE","ERROR_LOAD_CONFIG_FAILED",
"ERROR_UPDATE_KEY_SIGNATURES","ERROR_UPDATE_NET_AUTHENTICATION","ERROR_UPDATE_NOTREAD_SOURCES","PRIORITY_UPGRADE_SUCCCESSED",
"ERROR_UPDATE_INVALID_TIME",
"get_error_description_from_enum", "get_error_string_from_enum", "get_source_name_from_enum", "get_caller_from_enum")
import gettext
gettext.bindtextdomain('kylin-system-updater', '/usr/share/locale')
gettext.textdomain('kylin-system-updater')
_ = gettext.gettext
PRIORITY_UPGRADE_SUCCCESSED = "priority-upgrade-successed"
#apt update阶段出现的错误解析
ERROR_UPDATE_DEFAULT_FAILED = "error-update-default-failed"
ERROR_UPDATE_KEY_SIGNATURES = "The following signatures"
ERROR_UPDATE_NET_AUTHENTICATION ="does the network require authentication?"
ERROR_UPDATE_NOTREAD_SOURCES = "The list of sources could not be read"
ERROR_UPDATE_INVALID_TIME = "(invalid for another"
ERROR_UPDATE_SOURCE_FAILED = "error-update-source-failed"
ERROR_NETWORK_FAILED = "error-network-failed"
ERROR_NOT_GROUPS_CONFIG = "error-not-groups-config"
ERROR_NOT_CONFIGPKG_DEPENDENCIES = "error-not-configpkg-dependencies"
ERROR_NOT_SELFPKG_DEPENDENCIES = "error-not-selfpkg-dependencies"
ERROR_NOT_FIX_SYSTEM = "error-not-fix-system"
ERROR_LOAD_CONFIG_FAILED = "error-load-config-failed"
#自己的
ERROR_SOFTWARE_INDEX_RROKEN = "error-software-index-broken"
ERROR_NOT_INIT_PACKAGESINFIO = "error-not-init-packagesinfo"
ERROR_READ_IMPORTANTLIST_FAILED = "error-read-importantlist-failed"
ERROR_RESOLVER_FAILED = "error-resolver-failed"
ERROR_NOT_UPGRADE_PACKAGES = "error-not-upgrade-packages"
ERROR_REMOVE_ESSENTIAL_PACKAGES = "error-remove-essential-packages"
ERROR_NOT_DISK_SPACE = "error-not-disk-space"
ERROR_READ_LOCAL_DEB = "error-read-local-deb"
ERROR_LOCAL_DEB_FORMAT = "error-local-deb-format"
ERROR_INSTALL_DEB_BASE = "error-install-deb-base"
_STRINGS_ERROR = {
PRIORITY_UPGRADE_SUCCCESSED: _("Update Manager upgrade is complete, please restart the setting panel before performing the system update."),
#update
ERROR_UPDATE_DEFAULT_FAILED: _("Check for update exceptions!"),
ERROR_UPDATE_SOURCE_FAILED: _("Check for update exceptions!"),
ERROR_NETWORK_FAILED: _("Network anomaly, can't check for updates!"),
ERROR_UPDATE_KEY_SIGNATURES: _("Check for update exceptions!"),
ERROR_READ_IMPORTANTLIST_FAILED: _("Check for update exceptions!"),
ERROR_SOFTWARE_INDEX_RROKEN: _("Check for update exceptions!"),
ERROR_NOT_INIT_PACKAGESINFIO: _("Check for update exceptions!"),
ERROR_NOT_FIX_SYSTEM: _("Check for update exceptions!"),
ERROR_LOAD_CONFIG_FAILED: _("Check for update exceptions!"),
#优先升级
ERROR_NOT_GROUPS_CONFIG: _("Upgrade configuration acquisition exception."),
ERROR_NOT_CONFIGPKG_DEPENDENCIES: _("Upgrade configuration acquisition exception."),
ERROR_NOT_SELFPKG_DEPENDENCIES: _("Priority upgrade status exception."),
#install
ERROR_RESOLVER_FAILED: _("Could not calculate the upgrade"),
ERROR_NOT_UPGRADE_PACKAGES: _("There is an exception in the update package."),
ERROR_REMOVE_ESSENTIAL_PACKAGES: _("There is an exception in the update package."),
ERROR_NOT_DISK_SPACE: _("Disk space is insufficient, please clean the disk and then upgrade"),
ERROR_READ_LOCAL_DEB:_(" "),
ERROR_LOCAL_DEB_FORMAT:_(" "),
ERROR_INSTALL_DEB_BASE:_(" ")}
_DESCS_ERROR = {
#update
ERROR_UPDATE_SOURCE_FAILED: _("Unable to access the source management server"),
ERROR_NETWORK_FAILED: _("Please check your network connection and retry."),
ERROR_UPDATE_KEY_SIGNATURES: _("Check your source public key signature"),
ERROR_UPDATE_NOTREAD_SOURCES: _("Please check your source list and retry."),
ERROR_UPDATE_INVALID_TIME: _("Please check the system time and synchronize the system time before updating."),
ERROR_UPDATE_NET_AUTHENTICATION: _("Check if your network requires authentication?"),
ERROR_NOT_GROUPS_CONFIG: _("Unable to get group configuration package, Please check if the configuration package exists in the software source repository."),
ERROR_NOT_INIT_PACKAGESINFIO: _("An unresolvable problem occurred while initializing the package."),
ERROR_SOFTWARE_INDEX_RROKEN: _("Software index is broken") + _("It is impossible to install or remove any software. "
"Please use the package manager \"Synaptic\" or run "
"\"sudo apt-get install -f\" in a terminal to fix "
"this issue at first."),
ERROR_READ_IMPORTANTLIST_FAILED: _("read important list failed"),
ERROR_NOT_CONFIGPKG_DEPENDENCIES: _("Unable to install group configuration package, Please check the configuration package related dependencies."),
ERROR_NOT_SELFPKG_DEPENDENCIES: _("Unable to perform priority upgrade, please check the dependency related to the priority upgrade package."),
ERROR_LOAD_CONFIG_FAILED: _("The system update configuration file is read abnormally, please check if the system update configuration file format is correct."),
ERROR_NOT_FIX_SYSTEM: _("The system APT environment is abnormal, please check the system APT environment."),
#install
ERROR_RESOLVER_FAILED: _("nothing"),
ERROR_NOT_UPGRADE_PACKAGES: _("This update cannot detect the upgradeable package."),
ERROR_REMOVE_ESSENTIAL_PACKAGES: _("You request the removal of a system-essential package."),
ERROR_NOT_DISK_SPACE: _("test"),
ERROR_READ_LOCAL_DEB:_("Deb format exception, read local deb file error."),
ERROR_LOCAL_DEB_FORMAT:_("Deb format exception, failed to parse package file."),
ERROR_INSTALL_DEB_BASE:_("Install deb error.")
}
#UPGRADE MONITOR STATUS
MONIT_DETECT = "step-updatedetect"
MONIT_DEPRESOLUT = "step-depresolution"
MONIT_DOWNLOAD = "step-downloading"
MONIT_INSTALL = "step-installing"
MONIT_FINISH = "step-finish"
MONIT_INSTALLDEB = "step-installdeb"
SOURCE_NAME = {
'kylin-installer':_("Kylin Installer"),
'kylin-uninstaller':_("Kylin Uninstaller"),
'kylin-background-upgrade':_("Kylin Background Upgrade"),
'kylin-software-center':_("Kylin Software Center"),
}
CALLER = {
'kylin-installer':"Kylin Installer",
'kylin-uninstaller':"Kylin Uninstaller",
'kylin-background-upgrade':"Kylin Background Upgrade",
'kylin-software-center':"Kylin Software Center",
}
def get_error_description_from_enum(enum):
"""Get a long description of an error.
:param enum: The transaction error enum, e.g. :data:`ERROR_NO_LOCK`.
:returns: The description string.
"""
try:
return _DESCS_ERROR[enum]
except KeyError:
return None
def get_error_string_from_enum(enum):
"""Get a short description of an error.
:param enum: The transaction error enum, e.g. :data:`ERROR_NO_LOCK`.
:returns: The description string.
"""
try:
return _STRINGS_ERROR[enum]
except KeyError:
return None
def get_source_name_from_enum(enum):
try:
return SOURCE_NAME[enum]
except KeyError:
return _("Kylin System Updater")
def get_caller_from_enum(enum):
try:
return CALLER[enum]
except KeyError:
return _("Kylin System Updater")
# vim:ts=4:sw=4:et

View File

@ -0,0 +1,65 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Exception classes"""
# __all__ = ("UpdateBaseError")
import logging
from selectors import EpollSelector
import sys
from .enums import *
PY3K = sys.version_info.major > 2
class UpdateBaseError(Exception):
"""Internal error if a transaction could not be processed successfully."""
_dbus_error_name = "org.debian.apt.TransactionFailed"
def __init__(self, code,header=None,desc=None,details="",*args):
if not args:
# Avoid string replacements if not used
details = details.replace("%", "%%")
args = tuple([_convert_unicode(arg) for arg in args])
details = _convert_unicode(details)
self.code = code
self.details = details
self.details_args = args
if header == None:
self.header = get_error_string_from_enum(self.code)
else:
self.header = header
if desc == None:
self.desc = get_error_description_from_enum(self.code)
else:
self.desc = desc
Exception.__init__(self, *args)
# AptDaemonError.__init__(self, "%s: %s" % (code, details % args))
def __unicode__(self):
return "%s" % \
(get_error_string_from_enum(self.code))
def __str__(self):
if PY3K:
return self.__unicode__()
else:
return self.__unicode__().encode("utf-8")
class UpdateProgressExit(Exception):
def __init__(self,*args):
Exception.__init__(self, *args)
logging.info("Update Progress wiil be Exit...")
def _convert_unicode(text, encoding="UTF-8"):
"""Always return an unicode."""
if PY3K and not isinstance(text, str):
text = str(text, encoding, errors="ignore")
elif not PY3K and not isinstance(text, unicode):
text = unicode(text, encoding, errors="ignore")
return text
# vim:ts=4:sw=4:et

View File

@ -0,0 +1,13 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""Main loop for aptdaemon."""
__all__ = ("mainloop", "get_main_loop")
from gi.repository import GLib
mainloop = GLib.MainLoop()
def get_main_loop():
"""Return the glib main loop as a singleton."""
return mainloop

View File

@ -0,0 +1,809 @@
# utils.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
#
# Copyright (c) 2004-2013 Canonical
#
# Authors: Michael Vogt <mvo@debian.org>
# Michael Terry <michael.terry@canonical.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 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 General Public License for more details.
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
from __future__ import print_function
from gettext import gettext as _
from gettext import ngettext
from stat import (S_IMODE, ST_MODE, S_IXUSR)
from math import ceil
import apt
import dbus
import apt_pkg
apt_pkg.init_config()
import shutil
import locale
import logging
import re
import os
import subprocess
import sys
import time
import fcntl
from urllib.request import (
ProxyHandler,
Request,
build_opener,
install_opener,
urlopen,
)
import dbus
from urllib.parse import urlsplit
from copy import copy
import psutil
import ctypes
from ctypes import *
import struct
# 禁止关机锁文件路径
VERIFY_SO = "libkylin_signtool.so"
class ExecutionTime(object):
"""
Helper that can be used in with statements to have a simple
measure of the timing of a particular block of code, e.g.
with ExecutionTime("db flush"):
db.flush()
"""
def __init__(self, info=""):
self.info = info
def __enter__(self):
self.now = time.time()
def __exit__(self, type, value, stack):
print("%s: %s" % (self.info, time.time() - self.now))
def get_string_with_no_auth_from_source_entry(entry):
tmp = copy(entry)
url_parts = urlsplit(tmp.uri)
if url_parts.username:
tmp.uri = tmp.uri.replace(url_parts.username, "hidden-u")
if url_parts.password:
tmp.uri = tmp.uri.replace(url_parts.password, "hidden-p")
return str(tmp)
def is_unity_running():
""" return True if Unity is currently running """
unity_running = False
try:
import dbus
bus = dbus.SessionBus()
unity_running = bus.name_has_owner("com.canonical.Unity")
except Exception:
logging.exception("could not check for Unity dbus service")
return unity_running
def is_child_of_process_name(processname, pid=None):
if not pid:
pid = os.getpid()
while pid > 0:
stat_file = "/proc/%s/stat" % pid
with open(stat_file) as stat_f:
stat = stat_f.read()
# extract command (inside ())
command = stat.partition("(")[2].rpartition(")")[0]
if command == processname:
return True
# get parent (second to the right of command) and check that next
pid = int(stat.rpartition(")")[2].split()[1])
return False
def inside_chroot():
""" returns True if we are inside a chroot
"""
# if there is no proc or no pid 1 we are very likely inside a chroot
if not os.path.exists("/proc") or not os.path.exists("/proc/1"):
return True
# if the inode is differnt for pid 1 "/" and our "/"
return os.stat("/") != os.stat("/proc/1/root")
def wrap(t, width=70, subsequent_indent=""):
""" helpers inspired after textwrap - unfortunately
we can not use textwrap directly because it break
packagenames with "-" in them into new lines
"""
out = ""
for s in t.split():
if (len(out) - out.rfind("\n")) + len(s) > width:
out += "\n" + subsequent_indent
out += s + " "
return out
def twrap(s, **kwargs):
msg = ""
paras = s.split("\n")
for par in paras:
s = wrap(par, **kwargs)
msg += s + "\n"
return msg
def lsmod():
" return list of loaded modules (or [] if lsmod is not found) "
modules = []
# FIXME raise?
if not os.path.exists("/sbin/lsmod"):
return []
p = subprocess.Popen(["/sbin/lsmod"], stdout=subprocess.PIPE,
universal_newlines=True)
lines = p.communicate()[0].split("\n")
# remove heading line: "Modules Size Used by"
del lines[0]
# add lines to list, skip empty lines
for line in lines:
if line:
modules.append(line.split()[0])
return modules
def check_and_fix_xbit(path):
" check if a given binary has the executable bit and if not, add it"
if not os.path.exists(path):
return
mode = S_IMODE(os.stat(path)[ST_MODE])
if not ((mode & S_IXUSR) == S_IXUSR):
os.chmod(path, mode | S_IXUSR)
def country_mirror():
" helper to get the country mirror from the current locale "
# special cases go here
lang_mirror = {'c': ''}
# no lang, no mirror
if 'LANG' not in os.environ:
return ''
lang = os.environ['LANG'].lower()
# check if it is a special case
if lang[:5] in lang_mirror:
return lang_mirror[lang[:5]]
# now check for the most comon form (en_US.UTF-8)
if "_" in lang:
country = lang.split(".")[0].split("_")[1]
if "@" in country:
country = country.split("@")[0]
return country + "."
else:
return lang[:2] + "."
return ''
def get_dist():
" return the codename of the current runing distro "
# then check the real one
from subprocess import Popen, PIPE
p = Popen(["lsb_release", "-i", "-s"], stdout=PIPE,
universal_newlines=True)
res = p.wait()
if res != 0:
sys.stderr.write("lsb_release returned exitcode: %i\n" % res)
return "unknown distribution"
dist = p.stdout.readline().strip()
p.stdout.close()
return dist
def get_dist_version():
" return the version of the current running distro "
# then check the real one
from subprocess import Popen, PIPE
p = Popen(["lsb_release", "-r", "-s"], stdout=PIPE,
universal_newlines=True)
res = p.wait()
if res != 0:
sys.stderr.write("lsb_release returned exitcode: %i\n" % res)
return "unknown distribution"
desc = p.stdout.readline().strip()
p.stdout.close()
return desc
class HeadRequest(Request):
def get_method(self):
return "HEAD"
def url_downloadable(uri, debug_func=None):
"""
helper that checks if the given uri exists and is downloadable
(supports optional debug_func function handler to support
e.g. logging)
Supports http (via HEAD) and ftp (via size request)
"""
if not debug_func:
lambda x: True
debug_func("url_downloadable: %s" % uri)
(scheme, netloc, path, querry, fragment) = urlsplit(uri)
debug_func("s='%s' n='%s' p='%s' q='%s' f='%s'" % (scheme, netloc, path,
querry, fragment))
if scheme in ("http", "https"):
try:
http_file = urlopen(HeadRequest(uri))
http_file.close()
if http_file.code == 200:
return True
return False
except Exception as e:
debug_func("error from httplib: '%s'" % e)
return False
elif scheme == "ftp":
import ftplib
try:
f = ftplib.FTP(netloc)
f.login()
f.cwd(os.path.dirname(path))
size = f.size(os.path.basename(path))
f.quit()
if debug_func:
debug_func("ftplib.size() returned: %s" % size)
if size != 0:
return True
except Exception as e:
if debug_func:
debug_func("error from ftplib: '%s'" % e)
return False
return False
def is_chinese(string):
"""
检查整个字符串是否包含中文
:param string: 需要检查的字符串
:return: bool
"""
for ch in string:
if u'\u4e00' <= ch <= u'\u9fff':
return True
return False
def init_proxy(gsettings=None):
""" init proxy settings
* use apt.conf http proxy if present,
* otherwise look into synaptics config file,
* otherwise the default behavior will use http_proxy environment
if present
"""
SYNAPTIC_CONF_FILE = "/root/.synaptic/synaptic.conf"
proxies = {}
# generic apt config wins
if apt_pkg.config.find("Acquire::http::Proxy") != '':
proxies["http"] = apt_pkg.config.find("Acquire::http::Proxy")
# then synaptic
elif os.path.exists(SYNAPTIC_CONF_FILE):
cnf = apt_pkg.Configuration()
apt_pkg.read_config_file(cnf, SYNAPTIC_CONF_FILE)
use_proxy = cnf.find_b("Synaptic::useProxy", False)
if use_proxy:
proxy_host = cnf.find("Synaptic::httpProxy")
proxy_port = str(cnf.find_i("Synaptic::httpProxyPort"))
if proxy_host and proxy_port:
proxies["http"] = "http://%s:%s/" % (proxy_host, proxy_port)
if apt_pkg.config.find("Acquire::https::Proxy") != '':
proxies["https"] = apt_pkg.config.find("Acquire::https::Proxy")
elif "http" in proxies:
proxies["https"] = proxies["http"]
# if we have a proxy, set it
if proxies:
# basic verification
for proxy in proxies.values():
if not re.match("https?://\\w+", proxy):
print("proxy '%s' looks invalid" % proxy, file=sys.stderr)
return
proxy_support = ProxyHandler(proxies)
opener = build_opener(proxy_support)
install_opener(opener)
if "http" in proxies:
os.putenv("http_proxy", proxies["http"])
if "https" in proxies:
os.putenv("https_proxy", proxies["https"])
return proxies
def on_battery():
"""
Check via dbus if the system is running on battery.
This function is using UPower per default, if UPower is not
available it falls-back to DeviceKit.Power.
"""
try:
import dbus
bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
try:
devobj = bus.get_object('org.freedesktop.UPower',
'/org/freedesktop/UPower')
dev = dbus.Interface(devobj, 'org.freedesktop.DBus.Properties')
return dev.Get('org.freedesktop.UPower', 'OnBattery')
except dbus.exceptions.DBusException as e:
error_unknown = 'org.freedesktop.DBus.Error.ServiceUnknown'
if e._dbus_error_name != error_unknown:
raise
devobj = bus.get_object('org.freedesktop.DeviceKit.Power',
'/org/freedesktop/DeviceKit/Power')
dev = dbus.Interface(devobj, "org.freedesktop.DBus.Properties")
return dev.Get("org.freedesktop.DeviceKit.Power", "on_battery")
except Exception:
#import sys
#print("on_battery returned error: ", e, file=sys.stderr)
return False
def str_to_bool(str):
if str == "0" or str.upper() == "FALSE":
return False
return True
def get_lang():
import logging
try:
(locale_s, encoding) = locale.getdefaultlocale()
return locale_s
except Exception:
logging.exception("gedefaultlocale() failed")
return None
def get_ubuntu_flavor(cache=None):
""" try to guess the flavor based on the running desktop """
# this will (of course) not work in a server environment,
# but the main use case for this is to show the right
# release notes.
pkg = get_ubuntu_flavor_package(cache=cache)
return pkg.split('-', 1)[0]
# def _load_meta_pkg_list():
# # This could potentially introduce a circular dependency, but the config
# # parser logic is simple, and doesn't rely on any UpdateManager code.
# from DistUpgrade.DistUpgradeConfigParser import DistUpgradeConfig
# parser = DistUpgradeConfig('/usr/share/ubuntu-release-upgrader')
# return parser.getlist('Distro', 'MetaPkgs')
def get_ubuntu_flavor_package(cache=None):
""" try to guess the flavor metapackage based on the running desktop """
# From spec, first if ubuntu-desktop is installed, use that.
# Second, grab first installed one from DistUpgrade.cfg.
# Lastly, fallback to ubuntu-desktop again.
meta_pkgs = ['ubuntu-desktop']
# try:
# meta_pkgs.extend(sorted(_load_meta_pkg_list()))
# except Exception as e:
# print('Could not load list of meta packages:', e)
if cache is None:
cache = apt.Cache()
for meta_pkg in meta_pkgs:
cache_pkg = cache[meta_pkg] if meta_pkg in cache else None
if cache_pkg and cache_pkg.is_installed:
return meta_pkg
return 'ubuntu-desktop'
def get_ubuntu_flavor_name(cache=None):
""" try to guess the flavor name based on the running desktop """
pkg = get_ubuntu_flavor_package(cache=cache)
lookup = {'ubuntustudio-desktop': 'Ubuntu Studio'}
if pkg in lookup:
return lookup[pkg]
elif pkg.endswith('-desktop'):
return capitalize_first_word(pkg.rsplit('-desktop', 1)[0])
elif pkg.endswith('-netbook'):
return capitalize_first_word(pkg.rsplit('-netbook', 1)[0])
else:
return 'Ubuntu'
# Unused by update-manager, but still used by ubuntu-release-upgrader
def error(parent, summary, message):
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk
d = Gtk.MessageDialog(parent=parent,
flags=Gtk.DialogFlags.MODAL,
type=Gtk.MessageType.ERROR,
buttons=Gtk.ButtonsType.CLOSE)
d.set_markup("<big><b>%s</b></big>\n\n%s" % (summary, message))
d.realize()
d.get_window().set_functions(Gdk.WMFunction.MOVE)
d.set_title("")
d.run()
d.destroy()
return False
def _split_package_id(package):
"""Return the name, the version number and the release of the
specified package."""
if ":" in package:
name, arch = package.split(":", 1)
# release = None
# elif "/" in package:
# name, release = package.split("/", 1)
# version = None
else:
name = package
arch = None
return name, arch
def get_config_patch():
#检查组配置文件当前的目录
NOW_UPDATE_CONFIG = '/usr/share/kylin-update-desktop-config/config/'
OLD_UPDATE_CONFIG = '/usr/share/kylin-update-desktop-config/data/'
if os.path.exists(NOW_UPDATE_CONFIG):
return NOW_UPDATE_CONFIG
elif os.path.exists(OLD_UPDATE_CONFIG):
return NOW_UPDATE_CONFIG
else:
return NOW_UPDATE_CONFIG
def get_broken_details(cache,now=True):
"""Return a message which provides debugging information about
broken packages.
This method is basically a Python implementation of apt-get.cc's
ShowBroken.
Keyword arguments:
trans -- the corresponding transaction
#表示当前系统apt已经破损的话是True 如果是安装软件包讲导致破损的话是False
now -- if we check currently broken dependecies or the installation
candidate
"""
msg = _("The following packages have unmet dependencies:")
msg += "\n\n"
for pkg in cache:
if not ((now and pkg.is_now_broken) or
(not now and pkg.is_inst_broken)):
continue
msg += "%s: " % pkg.name
#获取出现问题的包的版本
if now:
version = pkg.installed
else:
version = pkg.candidate
indent = " " * (len(pkg.name) + 2)
dep_msg = ""
#拿取依赖关系
for dep in version.dependencies:
or_msg = ""
for base_dep in dep.or_dependencies:
if or_msg:
or_msg += "or\n"
or_msg += indent
# Check if it's an important dependency
# See apt-pkg/depcache.cc IsImportantDep
# See apt-pkg/pkgcache.cc IsCritical()
if not (base_dep.rawtype in ["Depends","PreDepends",
"Obsoletes", "DpkgBreaks",
"Conflicts"] or
(apt_pkg.config.find_b("APT::Install-Recommends",
False) and
base_dep.rawtype == "Recommends") or
(apt_pkg.config.find_b("APT::Install-Suggests",
False) and
base_dep.rawtype == "Suggests")):
continue
# Get the version of the target package
try:
pkg_name,pkg_arch = _split_package_id(base_dep.name)
pkg_dep = cache[pkg_name]
except KeyError:
dep_version = None
else:
if now:
dep_version = pkg_dep.installed
else:
dep_version = pkg_dep.candidate
# We only want to display dependencies which cannot
# be satisfied
if dep_version and not apt_pkg.check_dep(base_dep.version,
base_dep.relation,
dep_version.version):
break
or_msg = "%s: %s " % (base_dep.rawtype, base_dep.name)
if base_dep.version:
or_msg += "(%s %s) " % (base_dep.relation,
base_dep.version)
if cache.is_virtual_package(base_dep.name):
or_msg += _("but it is a virtual package")
#表示这个依赖包没有安装 源里面没有
elif not dep_version:
if now:
or_msg += _("but it is not installed")
else:
#要依赖包 不存在时走此
or_msg += _("but it is not going to "
"be installed")
#表示安装的版本与需要的版本不一致 在这个地方来再进行递归安装判断 具体那些包造成的不能安装
elif now:
# TRANSLATORS: %s is a version number
or_msg += (_("but %s is installed") %
dep_version.version)
else:
#安装之后出现破损的话走这里
# TRANSLATORS: %s is a version number
or_msg += (_("but %s is to be installed") %
dep_version.version)
else:
# Only append an or-group if at least one of the
# dependencies cannot be satisfied
if dep_msg:
dep_msg += indent
dep_msg += or_msg
dep_msg += "\n"
msg += dep_msg
return msg
def humanize_size(bytes):
"""
Convert a given size in bytes to a nicer better readable unit
"""
if bytes < 1000 * 1000:
# to have 0 for 0 bytes, 1 for 0-1000 bytes and for 1 and above
# round up
size_in_kb = int(ceil(bytes / float(1000)))
# TRANSLATORS: download size of small updates, e.g. "250 kB"
return ngettext("%(size).0f kB", "%(size).0f kB", size_in_kb) % {
"size": size_in_kb}
else:
# TRANSLATORS: download size of updates, e.g. "2.3 MB"
return locale.format_string(_("%.1f MB"), bytes / 1000.0 / 1000.0)
def get_arch():
return apt_pkg.config.find("APT::Architecture")
def is_port_already_listening(port):
""" check if the current system is listening on the given tcp port """
# index in the line
INDEX_LOCAL_ADDR = 1
#INDEX_REMOTE_ADDR = 2
INDEX_STATE = 3
# state (st) that we care about
STATE_LISTENING = '0A'
# read the data
with open("/proc/net/tcp") as net_tcp:
for line in net_tcp.readlines():
line = line.strip()
if not line:
continue
# split, values are:
# sl local_address rem_address st tx_queue rx_queue tr
# tm->when retrnsmt uid timeout inode
values = line.split()
state = values[INDEX_STATE]
if state != STATE_LISTENING:
continue
local_port_str = values[INDEX_LOCAL_ADDR].split(":")[1]
local_port = int(local_port_str, 16)
if local_port == port:
return True
return False
def iptables_active():
""" Return True if iptables is active """
# FIXME: is there a better way?
iptables_empty = """Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
"""
if os.getuid() != 0:
raise OSError("Need root to check the iptables state")
if not os.path.exists("/sbin/iptables"):
return False
out = subprocess.Popen(["iptables", "-nL"],
stdout=subprocess.PIPE,
universal_newlines=True).communicate()[0]
if out == iptables_empty:
return False
return True
def capitalize_first_word(string):
""" this uppercases the first word's first letter
"""
if len(string) > 1 and string[0].isalpha() and not string[0].isupper():
return string[0].capitalize() + string[1:]
return string
def get_package_label(pkg):
""" this takes a package synopsis and uppercases the first word's
first letter
"""
name = getattr(pkg.candidate, "summary", "")
return capitalize_first_word(name)
# 查看uu进程是否需要kill
def kill_process(path):
try:
# 判断文件是否存在
if (os.path.exists(path)):
with open(path, "r") as f:
pid = f.read()
if len(pid) == 0:
return False
logging.info("Unattended Upgrades run path: %d. ", int(pid))
os.kill(int(pid), 9)
logging.info('Unattended Upgrades is running, kill pid: %d. ', int(pid))
else:
logging.warning('%s is not exist.', path)
except Exception as e:
logging.error(e)
return False
return True
def whether_to_quit_uu():
osreleasedict={}
try:
with open('/etc/os-release') as f:
lines = f.readlines()
for line in lines:
ls = line.strip().split('=',1)
osreleasedict.update({ls[0]:ls[1].strip('"')})
except Exception as e:
pass
if 'SUB_PROJECT_CODENAME' not in osreleasedict.keys():
osreleasedict.update({'SUB_PROJECT_CODENAME':''})
if 'PROJECT_CODENAME' in osreleasedict:
if osreleasedict['PROJECT_CODENAME']=='V10SP1-edu':
if 'SUB_PROJECT_CODENAME' in osreleasedict:
if osreleasedict['SUB_PROJECT_CODENAME']=='mavis':
return False
else:
logging.info("SUB_PROJECT_CODENAME != mavis")
else:
logging.info("no SUB_PROJECT_CODENAME")
else:
logging.info("PROJECT_CODENAME != V10SP1-edu")
else:
logging.info("no PROJECT_CODENAME")
return True
def get_proc_from_dbus_name(dbus_name, bus=None):
"""Return a deferred that gets the id of process owning the given
system D-Bus name.
"""
if not bus:
bus = dbus.SystemBus()
bus_obj = bus.get_object("org.freedesktop.DBus",
"/org/freedesktop/DBus/Bus")
pid = bus_obj.GetConnectionUnixProcessID(dbus_name,
dbus_interface="org.freedesktop.DBus")
proc = psutil.Process(int(pid))
# with open("/proc/%s/status" % pid) as process:
# values = [v for v in process.readlines() if v.startswith("Uid:")]
# uid = int(values[0].split()[1])
# #检查是否root用户执行
# if uid == 0:
# return "root"
return proc.name()
def deb_verify(deb_path, _isinstall = False):
logging.info("Verify pkg:%s.",deb_path)
_deb_path = str(deb_path)
try:
# # 加载验证签名库 , 验签接口暂时无法调用
# args = ["dpkg-architecture", "-qDEB_TARGET_MULTIARCH"]
# ret = subprocess.run(args, stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True)
# verifyso_path = os.path.join("/usr/lib/",str(ret.stdout).strip(),VERIFY_SO)
# logging.info("Load verify interface:%s.",verifyso_path)
# verifyso = ctypes.CDLL(verifyso_path)
# #环境初始化
# ret = verifyso.SOF_Initialize(ctx_obj)
# if (ret) :
# logging.info("SOF_InitializeEx error!")
# return 2
# if os.path.isfile(_deb_path):
# ret = verifyso.BJCA_dodebverify(None, bytes(_deb_path, encoding='utf8'), _isinstall)
if not os.path.isfile("/usr/bin/kylinsigntool"):
logging.error("SOF_InitializeEx error!")
return 1
args = ["/usr/bin/kylinsigntool", "-v", _deb_path]
ret = subprocess.run(args, stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True)
if "Signature Verified failed" in str(ret.stdout).strip() or "签名验证失败" in str(ret.stdout).strip():
logging.info("Signature Verified failed!")
return 2
elif "Signature Verified Ok" in str(ret.stdout).strip() or "签名验证成功" in str(ret.stdout).strip():
logging.info("Signature Verified Ok!")
return 0
except Exception as e:
logging.error(e)
return 3
def PolicyKit_Authority(details = '', sender = None):
_allow_kylinsign = False
_verify_kylinsign = False
try:
#获取未知来源应用安装策略Unknown sources apply installation policies
inst_policies_path = "/etc/dpkg/dpkg.cfg"
if os.path.isfile(inst_policies_path):
with open(inst_policies_path, "r") as f:
lines = f.readlines()
for line in lines:
if "allow-kylinsign" in line:
_allow_kylinsign = True
if "verify-kylinsign" in line:
_verify_kylinsign = True
if _allow_kylinsign == True and _verify_kylinsign == False: #策略: 阻止
logging.debug("unknown sources apply installation policies: deter")
return False,_("The package is unsigned, refuses to install.")
elif _allow_kylinsign == True and _verify_kylinsign == True: #策略: 警告
logging.debug("unknown sources apply installation policies: warning")
elif _allow_kylinsign == False and _verify_kylinsign == False: #策略: 关闭
logging.debug("unknown sources apply installation policies: close")
else:
logging.warning("Unknown sources apply installation policies get failed.")
#用户鉴权
details = {'polkit.message':details}
cancel_id = ''
action = "cn.kylinos.KylinSystemUpdater.action"
kit = dbus.SystemBus().get_object('org.freedesktop.PolicyKit1', '/org/freedesktop/PolicyKit1/Authority')
kit = dbus.Interface(kit, 'org.freedesktop.PolicyKit1.Authority')
(granted, notused , details) = kit.CheckAuthorization(
('system-bus-name', {'name': sender}),
action, details, dbus.UInt32(1),cancel_id, timeout=60*60*24*7)
if granted:
logging.info("Authentication success ...")
return True,_("Authentication success.")
else:
logging.info("Authentication failure ...")
return False,_("Authentication failure.")
except Exception as e:
logging.error(e)
return False,str(e)
if __name__ == "__main__":
#print(mirror_from_sources_list())
#print(on_battery())
#print(inside_chroot())
#print(iptables_active())
error(None, "bar", "baz")

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,101 @@
# UpdateManager.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
import os
import sys
import time
import dbus
import logging
import dbus.service
import traceback
from gettext import gettext as _
from dbus.mainloop.glib import DBusGMainLoop
from gi.repository import GLib
DBusGMainLoop(set_as_default=True)
from .UpgradeStrategiesDbus import UpgradeStrategiesDbusController,UPDATER_DBUS_INTERFACE,UPDATER_DBUS_PATH,UPDATER_DBUS_SERVICE
from .Core.Database import Sqlite3Server
from .Core.loop import mainloop
from SystemUpdater.Core.UpdaterConfigParser import UpgradeConfig
STRATEGY_IDLE_INTERVAL = 2*60
STRATEGY_IDLE_TIMEOUT = 6*60
class UpgradeStrategies():
def __init__(self,options):
try:
self.options = options
#dbus
self.dbusController = self._setup_dbus()
#config
self.uuconfigs = UpgradeConfig(datadir = "/var/lib/unattended-upgrades/", name = "unattended-upgrades-policy.conf")
self.sqlite3_server = Sqlite3Server(self)
#策略配置接口的超时退出机制
self.strategy_timestamp = 0
GLib.timeout_add_seconds(STRATEGY_IDLE_INTERVAL,
self._check_strategy_inactivity)
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 UpgradeStrategiesDbusController(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 UpgradeStrategiesDbusController(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)
def _check_strategy_inactivity(self):
logging.info("Checking for inactivity in Strategies daemon ...")
timestamp = self.strategy_timestamp
if timestamp == 0:
self.strategy_timestamp = time.time()
return True
#超时退出
if self.strategy_timestamp != 0 and time.time() - self.strategy_timestamp > STRATEGY_IDLE_TIMEOUT:
logging.warning("Quitting due to inactivity")
self.dbusController.Quit(None)
return False
return True

View File

@ -0,0 +1,604 @@
#!/usr/bin/python3
import os
import dbus
import dbus.service
import logging
import subprocess
from gettext import gettext as _
from .Core.loop import mainloop
from SystemUpdater.Core.utils import get_proc_from_dbus_name
UPDATER_DBUS_INTERFACE = 'com.kylin.UpgradeStrategies.interface'
UPDATER_DBUS_PATH = '/com/kylin/UpgradeStrategies'
UPDATER_DBUS_SERVICE = 'com.kylin.UpgradeStrategies'
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_AUTOMATIC_DOWNLOAD = 0
UU_UPGRADE_MODE_MANUAL = 1
UU_UPGRADE_MODE_AUTOMATIC_INSTALL = 2
UU_UPGRADE_MODE_BEFORE_SHUTDOWN = 3
#dbus 建立
class UpgradeStrategiesDbusController(dbus.service.Object):
""" this is a helper to provide the UpdateManagerIFace """
P2P_DEDAULT_PATH = "/etc/default/apt-p2p"
RETURN_SUCCESS_CODE = 0
RETURN_SUCCESS_DESC = ""
RETURN_UNKNOWN_CODE = -1
RETURN_UNKNOWN_DESC = ""
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 __check_change__(self, _config = None, _section = "", _option = "", _value = ""):
if _config == None:
return False
if _value == _config.getWithDefault(_section,_option,_value," "):
return True
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接口: 开启或关闭预下载功能
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='s', out_signature='is',sender_keyword='sender')
def ChangingP2PStatus(self,_status,sender = None):
status = str(_status)
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' ChangingP2PStatus, _status = %s , sender name: %s',status,sender_name)
if os.path.exists(self.P2P_DEDAULT_PATH):
if status == "enable":
with open(self.P2P_DEDAULT_PATH, 'w+') as configfile:
configfile.write("#enable=true\n")
#第一次进行检查是否已经开启
args = ["systemctl","is-enabled","apt-p2p.service"]
p = subprocess.run(args, stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True)
if p.returncode != 0:
logging.info("Apt-p2p service is not runing and will be enable...")
#第二次进行重启
args = ["systemctl","enable","apt-p2p.service"]
p = subprocess.run(args, stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True)
if p.returncode == 0:
logging.info("Service Enable Execute successfully")
#第三次进行重启
args = ["systemctl","restart","apt-p2p.service"]
p = subprocess.run(args, stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True)
if p.returncode == 0:
logging.info("Restart Execute successfully")
return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC
else:
logging.error(str(p.stdout))
logging.error("Failed to execute reboot")
return self.RETURN_UNKNOWN_CODE,str(p.stdout)
else:
logging.error(str(p.stdout))
logging.error("Failed to execute enable")
return self.RETURN_UNKNOWN_CODE,str(p.stdout)
else:
logging.info("Apt-p2p service has been enabled and not need to reabled...")
return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC
elif status == "disable":
with open(self.P2P_DEDAULT_PATH, 'w+') as configfile:
configfile.write("enable=false\n")
args = ["systemctl","is-enabled","apt-p2p.service"]
p = subprocess.run(args, stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True)
if p.returncode == 0:
logging.info("Apt-p2p service is runing and will be disable...")
args = ["systemctl","disable","apt-p2p.service"]
p = subprocess.run(args, stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True)
if p.returncode == 0:
logging.info("Service disable Execute successfully")
args = ["systemctl","stop","apt-p2p.service"]
p = subprocess.run(args, stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True)
if p.returncode == 0:
logging.info("Stop Execute successfully")
return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC
else:
logging.error(str(p.stdout))
logging.error("Failed to execute Stop")
return self.RETURN_UNKNOWN_CODE,str(p.stdout)
else:
logging.error(str(p.stdout))
logging.error("Failed to execute disable")
return self.RETURN_UNKNOWN_CODE,str(p.stdout)
else:
logging.info("Apt-p2p service has been disabled and not need to redisabled...")
return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC
else:
logging.waring("error: input value _status=%s",status)
else:
logging.waring("apt-p2p function is not install...")
## dbus接口: 开启或关闭预下载功能
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='bs', out_signature='b',sender_keyword='sender')
def SetPreDownloadState(self, _state, _time, sender = None):
state = bool(_state)
time = str(_time)
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' SetPreDownloadState, state is %r, time: %s, sender name: %s .',state,time,sender_name)
try:
if state:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "preDownload", "on"):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "preDownload", "on", True)
self.PropertyChanged("preDownload","on")
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "preDownloadTime", time):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "preDownloadTime", time, True)
self.PropertyChanged("preDownloadTime",time)
else:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "preDownload", "off"):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "preDownload", "off", True)
self.PropertyChanged("preDownload","off")
except Exception as e:
logging.error(str(e))
return False
return True
## 设置更新周期
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='i', out_signature='b',sender_keyword='sender')
def SetUpdateDays(self, days, sender = None):
_days = int(days)
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' SetUpdateDays, days: %d , sender:%s .'\
,_days,sender_name)
try:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "updateDays", _days):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "updateDays", str(_days), True)
self.PropertyChanged("updateDays",str(_days))
except Exception as e:
logging.error(str(e))
return False
return True
# 设置自动更新时间
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='s', out_signature='bs',sender_keyword='sender')
def SetAutoUpgradeRandomRange(self,randomRange,sender=None):
_randomRange = str(randomRange)
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' SetAutoUpgradeRandomRange will be set value %s, sender: %s .',\
_randomRange,sender_name)
try:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "randomRange", _randomRange):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "randomRange", _randomRange, True)
self.PropertyChanged("randomRange",_randomRange)
except Exception as e:
logging.error(str(e))
return True,"success"
## 设置是否开启自动重启,以及设置自动重启的时间
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='bs', out_signature='b',sender_keyword='sender')
def SetAutomaticReboot(self, status, reboot_time, sender = None):
_state = bool(status)
_reboot_time = str(reboot_time)
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' SetAutomaticReboot, status is %r, reboot_time: %s, sender name: %s .'\
,_state,_reboot_time,sender_name)
try:
if _state:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "automaticReboot", "on"):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "automaticReboot", "on", True)
self.PropertyChanged("automaticReboot","on")
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "automaticRebootTime", _reboot_time):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "automaticRebootTime", _reboot_time, True)
self.PropertyChanged("automaticRebootTime",_reboot_time)
else:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "automaticReboot", "off"):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "automaticReboot", "off", True)
self.PropertyChanged("automaticReboot","off")
except Exception as e:
logging.error(str(e))
return False
return True
## dbus接口: 开启关闭自动更新功能
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='b', out_signature='b')
def SetAutoUpgradeState(self, _state):
state = bool(_state)
logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' SetAutoUpgradeState, state is %r ...',state)
try:
if state:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "autoUpgradeState", "on"):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "autoUpgradeState", "on", True)
self.parent.sqlite3_server.insert_into_display("autoupdate_allow", "true")
self.PropertyChanged("autoUpgradeState","on")
self.ButtonStatusChange("autoUpgradeStatus", "true")
else :
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "autoUpgradeState", "off"):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "autoUpgradeState", "off", True)
self.parent.sqlite3_server.insert_into_display("autoupdate_allow", "false")
self.PropertyChanged("autoUpgradeState","off")
self.ButtonStatusChange("autoUpgradeStatus", "false")
except Exception as e:
logging.error(str(e))
return False
return True
## dbus接口: 设置自动更新策略
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='is', out_signature='b')
def SetAutoUpgradeMode(self, mode, time):
_time = str(time)
logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' SetAutoUpgradeMode, mode is %s, time is %s ...',mode,_time)
try:
if mode == UU_UPGRADE_MODE_AUTOMATIC_DOWNLOAD:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "downloadMode", "timing"):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "downloadMode", "timing", True)
self.PropertyChanged("downloadMode","timing")
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "downloadTime", str(_time)):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "downloadTime", str(_time), True)
self.PropertyChanged("downloadTime",str(_time))
self.ButtonStatusChange("autoUpgradeTime", str(_time))
elif mode == UU_UPGRADE_MODE_AUTOMATIC_INSTALL:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "installMode", "timing"):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "installMode", "timing", True)
self.PropertyChanged("installMode","timing")
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "installTime", str(_time)):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "installTime", str(_time), True)
self.PropertyChanged("installTime",str(_time))
elif mode == UU_UPGRADE_MODE_BEFORE_SHUTDOWN:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "installTime", "bshutdown"):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "installMode", "bshutdown", True)
self.PropertyChanged("installMode","bshutdown")
elif mode == UU_UPGRADE_MODE_MANUAL:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "downloadMode", "manual"):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "downloadMode", "manual", True)
self.PropertyChanged("downloadMode","manual")
except Exception as e:
logging.error(str(e))
return False
return True
# # dbus接口改变apt下载速度
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='sb', out_signature='b',sender_keyword='sender')
def SetDownloadspeedMax(self, speed, set,sender = None):
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' SetDownloadspeedMax, speed:%s, set:%r, sender name: %s .'%(speed, set, sender_name))
#来重启Aptdeamon
self.parent.init_config_aptdeamon = True
if set:
with open("/etc/apt/apt.conf.d/80apt-download", "w+") as f:
try:
f.write("Acquire::http::Dl-Limit" + " \"" + "%s" % str(speed) + "\";\n")
f.write("Acquire::https::Dl-Limit" + " \"" + "%s" % str(speed) + "\";\n")
#更改数据库值
self.parent.sqlite3_server.insert_into_display("download_limit","true")
self.parent.sqlite3_server.insert_into_display("download_limit_value",str(speed))
#发送信号
self.ButtonStatusChange("speed" , str(speed))
return True
except Exception as e:
logging.error(e)
return False
else:
if os.path.exists("/etc/apt/apt.conf.d/80apt-download"):
os.remove("/etc/apt/apt.conf.d/80apt-download")
self.parent.sqlite3_server.insert_into_display("download_limit","false")
self.ButtonStatusChange("speed", "0")
return True
else:
self.parent.sqlite3_server.insert_into_display("download_limit","false")
self.ButtonStatusChange("speed", "0")
return True
# # dbus接口获取apt下载速度
@dbus.service.method(UPDATER_DBUS_INTERFACE, out_signature='bs',sender_keyword='sender')
def GetDownloadspeedLimitValue(self,sender = None):
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' GetDownloadspeedLimitValue sender: %s .', sender_name)
try:
download_limit = self.parent.sqlite3_server.select_from_display("download_limit")
if download_limit == "true":
download_limit_value = self.parent.sqlite3_server.select_from_display("download_limit_value")
return True,str(download_limit_value)
else:
return False,str("0")
except:
return False, "0"
# 是否允许关机前更新
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='ss', out_signature='bs', sender_keyword='sender')
def UnattendedUpgradeValue(self, operation, value="false", sender=None):
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' UnattendedUpgradeValue sender:%s ', sender_name)
if operation.lower() != "get" and operation.lower() != "set":
return False, 'Please input [\"set\", \"value\"] to set. \nor [\"get\"] to get whether updates are allowed before shutdown.'
if operation == "set":
try:
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' UnattendedUpgradeValue is going to %s [allow_unattended_upgrades_shutdown] value to %s.'%(operation,value))
self.parent.sqlite3_server.insert_into_display("allow_unattended_upgrades_shutdown", value.lower())
except Exception as e:
logging.error(str(e))
return False,str(e)
else:
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' UnattendedUpgradeValue is going to %s [allow_unattended_upgrades_shutdown] value.'%(operation))
try:
value = self.parent.sqlite3_server.select_from_display("allow_unattended_upgrades_shutdown")
logging.info("[allow_unattended_upgrades_shutdown] value is %s."%(value))
except Exception as e:
logging.error(str(e))
return False,str(e)
return True,"success"
# 设置自动更新时间
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='s', out_signature='bs',sender_keyword='sender')
def SetAutoUpgradePeriod(self, period, sender = None):
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' SetAutoUpgradePeriod will be set value %s, SetAutoUpgradePeriod sender: %s.'%(period, sender_name))
try:
self.parent.sqlite3_server.insert_into_display("update_period", period.lower())
except Exception as e:
logging.error(str(e))
return True,"success"
# 获取数据库值
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='bss', out_signature='s')
def GetSetDatabaseInfo(self, gs, table, field):
Text = 'NULL'
try:
if gs: #get
if table == 'display':
Text = self.parent.sqlite3_server.select_from_display(str(field))
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' GetDatabaseInfo Table:%s Field:%s Text:%s',table,field,Text)
else: #set
if table == 'display' and "=" in field:
field, value = str(field).split("=")
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' SetDatabaseInfo Table:%s Field:%s', table, field)
self.parent.sqlite3_server.insert_into_display(field, value)
return "success"
except Exception as e:
logging.error(str(e))
return Text
return Text
## dbus接口: 发送立即更新的信号
@dbus.service.method(UPDATER_DBUS_INTERFACE, out_signature='b')
def AutoUpgradeAllNow(self):
logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' AutoUpgradeAllNow ...')
try:
self.UpgradeAllNow()
except Exception as e:
logging.error(str(e))
return False
return True
# kill 进程
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='i', out_signature='b')
def KillProcessSignal(self, pid):
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' KillProcessSignal is %d', pid)
try:
# 判断文件是否存在
if (os.path.exists(RUN_UNATTENDED_UPGRADE)):
os.kill(int(pid), 9)
logging.info('%s has been killed', pid)
else:
logging.warning('%s is not exist.', RUN_UNATTENDED_UPGRADE)
except Exception as e:
logging.error(str(e))
return False
return True
#设置数据库配置信息
@dbus.service.method(UPDATER_DBUS_INTERFACE,in_signature='ss',out_signature='b',sender_keyword='sender')
def DatabaseInfoSet(self,field_name,field_value,sender=None):
Status = False
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' SetDatabaseInfo,field_name:%s,field_value:%s,caller:%s .',\
field_name,field_value,sender_name)
Status = self.parent.sqlite3_server.insert_into_display(field_name,field_value)
return bool(Status)
#数据库获取配置信息
@dbus.service.method(UPDATER_DBUS_INTERFACE,in_signature='s',out_signature='s',sender_keyword='sender')
def DatabaseInfoGet(self,field_name,sender=None):
field_value = ''
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' GetDatabaseInfo field_name:%s caller:%s',field_name,sender_name)
field_value = self.parent.sqlite3_server.select_from_display(str(field_name))
logging.info("Get field_value:%s",field_value)
return field_value
#限速修改信号
@dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='ss')
def ButtonStatusChange(self, signal_types = '', value=''):
logging.info(COLORLOG_PREFIX + "Emitting"+ COLORLOG_SUFFIX + " ButtonStatusChange signal_types = %s, value = %s.",signal_types, value)
# dbus 信号:用于发送立即更新信号
@dbus.service.signal(UPDATER_DBUS_INTERFACE)
def UpgradeAllNow(self):
logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" UpgradeAllNow")
# dbus 信号:用于发送自动更新配置更改信号
@dbus.service.signal(UPDATER_DBUS_INTERFACE)
def ChangeUpgradePolicy(self):
logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" ChangeUpgradePolicy")
#限速修改信号
@dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='ss')
def ButtonStatusChange(self, signal_types = '', value=''):
logging.info(COLORLOG_PREFIX + "Emitting"+ COLORLOG_SUFFIX + " ButtonStatusChange signal_types = %s, value = %s.",signal_types, value)
# signal属性发生改变
@dbus.service.signal(dbus_interface=UPDATER_DBUS_INTERFACE,
signature="ss")
def PropertyChanged(self, property, value):
logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" PropertyChanged: ( %s, %s )" % (property, value))
# 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 == "UploadUpgradeLog":
# self.parent.configs_uncover.setValue("SystemStatus","upload_upgrade_log",str(bool(value)))
# elif name == "UploadInstallerLog":
# self.parent.configs_uncover.setValue("SystemStatus","upload_installer_log",str(bool(value)))
# else:
# raise dbus.exceptions.DBusException("Unknown or read only "
# "property: %s" % name)
# else:
# raise dbus.exceptions.DBusException("Unknown interface: %s" %
# iface)
# 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)),
# "UploadUpgradeLog": dbus.Boolean(
# self.parent.configs_uncover.getWithDefault("SystemStatus", "upload_upgrade_log", True)),
# "UploadInstallerLog": dbus.Boolean(
# self.parent.configs_uncover.getWithDefault("SystemStatus", "upload_installer_log", False))
# }
# else:
# return {}

View File

View File

@ -0,0 +1,529 @@
#!/usr/bin/env python
import aptdaemon.client as client
import aptdaemon.errors as errors
import aptdaemon.enums as enums
from aptdaemon.enums import (
EXIT_SUCCESS,
EXIT_FAILED,
EXIT_CANCELLED,
STATUS_FINISHED,
get_error_description_from_enum,
get_error_string_from_enum,
get_status_string_from_enum
)
from defer import inline_callbacks
from SystemUpdater.backend import InstallBackend
import logging
from gettext import gettext as _
from SystemUpdater.Core.utils import humanize_size
import dbus,time
from gi.repository import GLib
import traceback
from importlib import reload
# 超时检测 秒单位
UPDATER_IDLE_CHECK_INTERVAL = 5
#安装的超时检查20分钟 按照5秒检查一次
INSTALL_IDLE_TIMEOUT = 4 * 60
#更新超时检查 5分钟
UPDATE_IDLE_TIMEOUT = 5 * 60
class InstallBackendAptdaemon(InstallBackend):
"""Makes use of aptdaemon to refresh the cache and to install updates."""
def __init__(self, window_main, action,mode = InstallBackend.MODE_DEFAULT_STATUS):
InstallBackend.__init__(self, window_main, action,mode)
self.window_main = window_main
#切换aptdaemon的语言 重新导入模块就可以进行切换
if self.window_main.aptd_lang_switch == True:
self.window_main.aptd_lang_switch = False
reload(client)
reload(errors)
reload(enums)
#客户端连接aptdeamon的dbus接口
self.client = client.AptClient()
self.trans_failed_msg = None
#是否在安装状态 判断依据进度>50
self.on_install_stage = False
if self.action == self.ACTION_INSTALL_SHUTDOWN:
self.on_install_stage = True
if self.action == self.ACTION_UPDATE:
#更新的超时检查机制 超时时取消下载
self.update_timestamp = 0
GLib.timeout_add_seconds(UPDATER_IDLE_CHECK_INTERVAL,
self._check_update_inactivity)
#定时模拟发送进度 为了让进度更加线性
self.simulation_progress = 0
GLib.timeout_add_seconds(1,self._check_simulation_progress)
elif self.action == self.ACTION_INSTALL or self.action == self.ACTION_INSTALL_SHUTDOWN:
#安装的超时间检查 超时解除禁止关机
self.install_timestamp = INSTALL_IDLE_TIMEOUT
self.check_progress = 0
GLib.timeout_add_seconds(UPDATER_IDLE_CHECK_INTERVAL,
self._check_install_inactivity)
def _check_simulation_progress(self):
self.simulation_progress += 20
if self.aptd_base.progress >= 90 or self.simulation_progress > 80:
return False
else:
self._dist_status_changed(self.action,[],self.simulation_progress,self.aptd_base.status,self.aptd_base.details)
return True
def _check_install_inactivity(self):
"""Shutdown the daemon if it has been inactive for time specified
in INSTALL_IDLE_TIMEOUT.
"""
logging.info("Checking for inactivity in Installing Time:%d...",self.install_timestamp)
if self.window_main.now_working != self.ACTION_INSTALL and self.window_main.now_working != self.ACTION_INSTALL_SHUTDOWN:
logging.info("Installing to exit and timeout check quit...")
return False
# 进度不同时 更新时间戳
if self.aptd_base.progress != self.check_progress:
self.check_progress = self.aptd_base.progress
self.install_timestamp = INSTALL_IDLE_TIMEOUT
#只有安装的时候启用 下载时候不使用
if (self.install_timestamp <= 0 and self.on_install_stage == True):
logging.error("Quitting due to inactivity(%s)",self.aptd_base.details)
if self.action == self.ACTION_INSTALL_SHUTDOWN:
#关机安装模式 解除禁止关机锁
self.window_main.inhibit_lock.close()
logging.info("Installtion timeout to exit Due to inactivity and Releasing the shutdown lock...")
else:
#超时只单独进行解锁关机
self.inhibit_shutdown.unlock()
self._action_done(self.ACTION_INSTALL,
is_cancelled=False, success=False,
#FIXME: 安装超时退出
error_string=_("Could not install the upgrades"),
error_desc=_("Installtion timeout to exit Due to inactivity") + self.aptd_base.details)
# self.window_main.dbusController.Quit(None)
return False
else:
self.install_timestamp = self.install_timestamp - 1
return True
def _check_update_inactivity(self):
logging.info("Checking for inactivity in Updating...")
#退出定时器 当更新完毕的时候
if (self.aptd_base.cancelable == False and self.aptd_base.progress >= 90) or self.window_main.now_working != self.ACTION_UPDATE:
logging.info("Updating to exit and timeout check quit...")
return False
#当更新进入下载状态时 记录进去的时间
timestamp = self.update_timestamp
if self.aptd_base.cancelable == True and timestamp == 0:
self.update_timestamp = time.time()
return True
#超时设置
if self.update_timestamp != 0 and time.time() - self.update_timestamp > UPDATE_IDLE_TIMEOUT \
and self.aptd_base.cancelable == True:
logging.error("Quitting due to inactivity")
self.window_main.dbusController.transaction.cancel()
return False
return True
@inline_callbacks
def update(self):
"""刷新包cache"""
try:
trans = yield self.client.update_cache(defer=True)
self.window_main.dbusController.transaction = trans
# 注册回调函数 接收更新的状态
yield self._show_transaction(trans, self.ACTION_UPDATE,
_("Checking for updates…"), False)
except errors.NotAuthorizedError:
self._action_done(self.ACTION_UPDATE,
authorized=False, success=False,
error_string='', error_desc='')
except Exception:
self._action_done(self.ACTION_UPDATE,
is_cancelled=False, success=False,
error_string='', error_desc='')
raise
@inline_callbacks
def commit(self,model,pkgs_install, pkgs_upgrade, pkgs_remove,pkgs_downgrade = []):
"""Commit a list of package adds and removes"""
try:
reinstall = purge = []
trans = yield self.client.commit_packages(
pkgs_install, reinstall, pkgs_remove, purge = purge, upgrade = pkgs_upgrade,
downgrade = pkgs_downgrade,defer=True)
self.window_main.dbusController.transaction = trans
yield self._show_transaction(trans, self.action,
_("Installing updates…"), True)
except errors.NotAuthorizedError:
self._action_done(self.action,
authorized=False, success=False,
error_string='', error_desc='')
except errors.TransactionFailed as e:
self.trans_failed_msg = str(e)
except dbus.DBusException as e:
if e.get_dbus_name() != "org.freedesktop.DBus.Error.NoReply":
raise
self._action_done(self.action,
authorized=False, success=False,
error_string='', error_desc='')
except Exception:
self._action_done(self.action,
is_cancelled=False, success=False,
error_string='', error_desc='')
raise
@inline_callbacks
def commit_only(self,model,pkgs_install, pkgs_upgrade, pkgs_remove,pkgs_downgrade = []):
"""Commit a list of package adds and removes"""
try:
reinstall = purge = []
trans = yield self.client.commit_only(
pkgs_install, reinstall, pkgs_remove, purge = purge, upgrade = pkgs_upgrade,
downgrade = pkgs_downgrade,download = model, defer=True)
self.window_main.dbusController.transaction = trans
yield self._show_transaction(trans, self.action,
_("Installing updates…"), True)
except errors.NotAuthorizedError:
self._action_done(self.action,
authorized=False, success=False,
error_string='', error_desc='')
except errors.TransactionFailed as e:
self.trans_failed_msg = str(e)
except dbus.DBusException as e:
if e.get_dbus_name() != "org.freedesktop.DBus.Error.NoReply":
raise
self._action_done(self.action,
authorized=False, success=False,
error_string='', error_desc='')
except Exception:
self._action_done(self.action,
is_cancelled=False, success=False,
error_string='', error_desc='')
raise
@inline_callbacks
def install_deb(self,install_path,install_force):
"""安装deb包 """
try:
trans = yield self.client.install_file(path = install_path,force = install_force,defer=True)
# 注册回调函数 接收更新的状态
yield self._show_transaction(trans, self.ACTION_INSTALL_DEB,
_("Installing deb packages…"), False)
except errors.NotAuthorizedError:
self._action_done(self.ACTION_INSTALL_DEB,
authorized=False, success=False,
error_string='', error_desc='')
except Exception as e:
self._action_done(self.ACTION_INSTALL_DEB,
is_cancelled=False, success=False,
error_string=str(e), error_desc='')
# raise
@inline_callbacks
def fix_broken(self):
"""安装deb包 """
try:
trans = yield self.client.fix_broken_depends(defer=True)
self.window_main.dbusController.transaction = trans
# 注册回调函数 接收更新的状态
yield self._show_transaction(trans, self.ACTION_FIX_BROKEN,
_("Installing deb packages…"), False)
except errors.NotAuthorizedError:
self._action_done(self.ACTION_FIX_BROKEN,
authorized=False, success=False,
error_string='', error_desc='')
except Exception:
self._action_done(self.ACTION_FIX_BROKEN,
is_cancelled=False, success=False,
error_string='', error_desc='')
raise
@inline_callbacks
def fix_incomplete(self):
"""修复未完成的安装 """
try:
trans = yield self.client.fix_incomplete_install(defer=True)
self.window_main.dbusController.transaction = trans
# 注册回调函数 接收更新的状态
yield self._show_transaction(trans, self.ACTION_FIX_INCOMPLETE,
_("fix incomplete install"), False)
except errors.NotAuthorizedError:
self._action_done(self.ACTION_FIX_INCOMPLETE,
authorized=False, success=False,
error_string='', error_desc='')
except Exception:
self._action_done(self.ACTION_FIX_INCOMPLETE,
is_cancelled=False, success=False,
error_string='', error_desc='')
raise
@inline_callbacks
def clean(self):
"""清空所有下载的文件 """
try:
trans = yield self.client.clean(defer=True)
self.window_main.dbusController.transaction = trans
# 注册回调函数 接收更新的状态
yield self._show_transaction(trans, self.ACTION_CLEAN,
_("Remove all downloaded files."), False)
except errors.NotAuthorizedError:
self._action_done(self.ACTION_CLEAN,
authorized=False, success=False,
error_string='', error_desc='')
except Exception:
self._action_done(self.ACTION_CLEAN,
is_cancelled=False, success=False,
error_string='', error_desc='')
raise
@inline_callbacks
def purge_packages(self,pkgs_purge):
"""卸载deb包 """
try:
# trans = yield self.client.remove_packages(package_names = pkgs_purge,defer=True)
trans = yield self.client.commit_packages([],[],[],pkgs_purge,[],[],defer=True)
self.window_main.dbusController.transaction = trans
# 注册回调函数 接收更新的状态
yield self._show_transaction(trans, self.ACTION_REMOVE_PACKAGES,
_("Installing deb packages…"), False)
except errors.NotAuthorizedError:
self._action_done(self.ACTION_REMOVE_PACKAGES,
authorized=False, success=False,
error_string='', error_desc='')
except Exception as e:
logging.error(str(e))
# self._action_done(self.ACTION_REMOVE_PACKAGES,
# is_cancelled=False, success=False,
# error_string=str(e), error_desc='')
#进度回调
def _on_progress_changed(self, trans,progress,action):
#不要101这种未知状态
if progress == 101:
return
#过滤掉不是线性的进度
if progress > self.aptd_base.progress:
self.aptd_base.progress = progress
else:
return
self.aptd_base.progress = progress
self._dist_status_changed(action,self.now_upgrade.upgrade_content,self.aptd_base.progress,self.aptd_base.status,self.aptd_base.details)
#同步状态回调
def _on_status_changed(self, trans, status,action):
if action == self.ACTION_UPDATE and status == STATUS_FINISHED:
return
#转化词条
self.aptd_base.status = get_status_string_from_enum(status)
if self.aptd_base.status == None:
return
self._dist_status_changed(action,self.now_upgrade.upgrade_content,\
self.aptd_base.progress,self.aptd_base.status,self.aptd_base.details)
#分发进度状态和细节信息
def _dist_status_changed(self,action,upgrade_content = [],progress = 0,status = '',details = ''):
if action == self.ACTION_UPDATE: # 更新进度100后推迟发出100%的信号 -- 等待源过滤完成
if progress == 11:
progress = 15
if progress != 100:
self.window_main.dbusController.UpdateDetectStatusChanged(progress,status)
elif action == self.ACTION_INSTALL:
#50%时候 属于下载状态切换到安装状态的过程 下面的代码只执行一次
if progress >= 50 and progress < 90 and self.on_install_stage == False:
logging.info("The process is now in the installtion phase")
self.on_install_stage = True
self.safe_manager.shutdown_safe()
self._start_install_lock(_("Kylin System Updater"))
#只处理从下载切换到安装时出现的网络问题
#当网络波动时下载某些软件包失败时属于异常状态进行重试时 不发送后续进度 等待重试正常是 进行下载安装
if self.now_upgrade.version_upgrade == True and progress >= 48 and self.on_install_stage != True and 'Failed to fetch' in self.aptd_base.error_details:
logging.warning("Arise Failed to fetch and Need retry Upgrade...")
self.now_upgrade.need_retry = True
return
#在下载阶段发送取消信号
if self.on_install_stage == False:
self.window_main.dbusController.Cancelable(self.aptd_base.cancelable)
self.window_main.dbusController.UpdateDloadAndInstStaChanged(upgrade_content,progress,status,details)
elif action == self.ACTION_INSTALL_SHUTDOWN:
# 写入进度 到plymouth
self._progress_to_plymouth(progress)
self.window_main.dbusController.UpdateDloadAndInstStaChanged(upgrade_content,progress,status,details)
elif action == self.ACTION_DOWNLOADONLY:
#只处理从下载切换到安装时出现的网络问题
#当网络波动时下载某些软件包失败时属于异常状态进行重试时 不发送后续进度 等待重试正常是 进行下载安装
if self.now_upgrade.version_upgrade == True and progress >= 48 and 'Failed to fetch' in self.aptd_base.error_details:
logging.warning("Arise Failed to fetch and Need retry Upgrade...")
self.now_upgrade.need_retry = True
return
self.window_main.dbusController.Cancelable(self.aptd_base.cancelable)
self.window_main.dbusController.UpdateDloadAndInstStaChanged(upgrade_content,progress,status,details)
elif action == self.ACTION_FIX_BROKEN:
self.window_main.dbusController.FixBrokenStatusChanged(False,True,progress,status,'','')
elif action == self.ACTION_REMOVE_PACKAGES:
self.window_main.dbusController.PurgePkgStatusChanged(progress,status,details)
elif action == self.ACTION_INSTALL_DEB or action == self.ACTION_BACKGROUND_UPGRADE:
self.window_main.dbusController.InstalldebStatusChanged(progress,status,details)
else:
logging.info("Other Action:progress = %d , status = %s ,details = %s",progress,status,details)
def _on_details_changed(self, trans, details,action):
self.aptd_base.details = details
self._dist_status_changed(action,self.now_upgrade.upgrade_groups+self.now_upgrade.single_pkgs,\
self.aptd_base.progress,self.aptd_base.status,self.aptd_base.details)
def _on_download_changed(self, trans, details):
logging.info(details)
# eta 剩余时间不正确,取消掉
def _on_progress_download_changed(self,trans,current_items, total_items, currenty_bytes, total_bytes, current_cps, eta):
if self.action == self.ACTION_INSTALL or self.action == self.ACTION_DOWNLOADONLY or self.action == self.ACTION_BACKGROUND_UPGRADE:
self.window_main.dbusController.UpdateDownloadInfo(\
self.now_upgrade.upgrade_groups+self.now_upgrade.single_pkgs,\
current_items, total_items, \
currenty_bytes, total_bytes, \
current_cps)
else:
if self.action == self.ACTION_UPDATE or self.action == self.ACTION_REMOVE_PACKAGES:
return
logging.info("Other Action:current_items = %d, total_items = %d, currenty_bytes = %s, total_bytes = %s, current_cps = %s/s",\
current_items, total_items, \
humanize_size(currenty_bytes), humanize_size(total_bytes),\
humanize_size(current_cps))
def _on_cancellable_changed(self, trans, Cancelable):
#下面的这些 不发送取消信号
if self.action == self.ACTION_REMOVE_PACKAGES:
return
if self.action != self.ACTION_UPDATE:
logging.info("\033[1;32m" + "Emitting" + "\033[0m" +" Cancelable: %r",Cancelable)
self.window_main.dbusController.Cancelable(Cancelable)
#增加取消信号的频发机制
self.aptd_base.cancelable = Cancelable
def _on_config_file_conflict(self, transaction, old, new):
logging.info("Config file conflict oldconf = %s , newconf = %s...",str(old),str(new))
logging.info("Default To Replace Old Configfile...")
#默认替换旧的配置文件
transaction.resolve_config_file_conflict(old, "keep")
# transaction.resolve_config_file_conflict(old, "keep")
#增加记录当产生错误的时候 详细信息
def _on_error_changed(self, trans,error_code, error_details):
# error_string = get_error_string_from_enum(error_code)
self.aptd_base.error_details = str(error_details)
logging.error(str(error_details))
@inline_callbacks
def _show_transaction(self, trans, action, header, show_details):
#更新和升级最后完成和失败都会走此在此进行完成之后的处理
trans.connect("finished", self._on_finished, action)
#升级和更新的状态信息和进度
trans.connect("status-changed", self._on_status_changed,action)
trans.connect("progress-changed", self._on_progress_changed,action)
#取消升级
trans.connect("cancellable-changed", self._on_cancellable_changed)
#下载的进度信息
trans.connect("progress-details-changed", self._on_progress_download_changed)
trans.connect("status-details-changed", self._on_details_changed,action)
trans.connect("error", self._on_error_changed)
trans.connect("config-file-conflict", self._on_config_file_conflict)
# yield trans.set_debconf_frontend("ukui")
# yield trans.set_locale(os.environ["LANGUAGE"] + ".UTF-8")
yield trans.run()
def _on_finished(self, trans, status, action):
try:
error_string = ''
error_desc = ''
#退出
self.on_install_stage = False
if status == EXIT_FAILED:
# self.log_audit(str(trans.error.code))
error_string = get_error_string_from_enum(trans.error.code)
error_desc = get_error_description_from_enum(trans.error.code)
if self.trans_failed_msg:
error_desc = error_desc + "\n" + self.trans_failed_msg
#取消下载
elif status == EXIT_CANCELLED:
error_string = _("Failed to fetch")
error_desc = _("_Cancel Upgrade")
elif status == EXIT_SUCCESS and action == self.ACTION_INSTALL:
error_string = _("System upgrade is complete.")
elif status == EXIT_SUCCESS and action == self.ACTION_REMOVE_PACKAGES:
error_string = _("Uninstallation completed")
is_success = (status == EXIT_SUCCESS)
self._action_done(action,
is_cancelled=(status == EXIT_CANCELLED), success=is_success,
error_string=error_string, error_desc=error_desc)
except Exception as e:
logging.error(e)
traceback.print_exc()
# def log_audit(self,error_code):
# if error_code == "error-package-manager-failed":
# error_msg = self.check_install_error()
# with open("/var/log/kylin-system-updater/error_details.log", 'w+') as configfile:
# configfile.write(str(error_msg))
# def check_install_error(self):
# locate_error = ''
# with open("/var/log/apt/term.log", "r+") as f:
# log_start = None
# log_end = None
# aptterm_log = f.readlines()
# reverse_log = aptterm_log[::-1]
# for logstr in reverse_log:
# if log_end == None and "Log ended:" in logstr:
# log_end = aptterm_log.index(logstr)
# if log_start == None and "Log started:" in logstr:
# log_start = aptterm_log.index(logstr)
# if log_end != None and log_start != None:
# break
# latest_log = aptterm_log[log_start:log_end+1]
# error_deb = latest_log[-2].strip()
# error_msg_line = None
# for log_msg in latest_log:
# if error_deb in log_msg:
# error_msg_line = latest_log.index(log_msg)
# locate_error = latest_log[error_msg_line-5:error_msg_line+5]
# break
# return locate_error

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
Dir::Bin::Methods::ftp "ftp";

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</vendor>
<vendor_url>www.kylinos.cn</vendor_url>
<icon_name>kylin-system-updater</icon_name>
<action id="cn.kylinos.KylinSystemUpdater.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,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-upgrade-strategies</icon_name>
<action id="com.kylin.UpgradeStrategies.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.UpgradeStrategies"/>
<allow send_interface="com.kylin.UpgradeStrategies.interface"/>
</policy>
<!-- Allow anyone to invoke methods on the interfaces -->
<policy context="default">
<allow send_destination="com.kylin.UpgradeStrategies"
send_interface="com.kylin.UpgradeStrategies.interface"/>
<allow send_destination="com.kylin.UpgradeStrategies"
send_interface="org.freedesktop.DBus.Introspectable"/>
<allow send_destination="com.kylin.UpgradeStrategies"
send_interface="org.freedesktop.DBus.Properties"/>
</policy>
</busconfig>

View File

@ -0,0 +1,4 @@
[D-BUS Service]
Name=com.kylin.UpgradeStrategies
Exec=/bin/python3 /usr/share/kylin-system-updater/kylin-upgrade-strategies -r -d
User=root

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.systemupgrade"/>
<allow send_interface="com.kylin.systemupgrade.interface"/>
</policy>
<!-- Allow anyone to invoke methods on the interfaces -->
<policy context="default">
<allow send_destination="com.kylin.systemupgrade"
send_interface="com.kylin.systemupgrade.interface"/>
<allow send_destination="com.kylin.systemupgrade"
send_interface="org.freedesktop.DBus.Introspectable"/>
<allow send_destination="com.kylin.systemupgrade"
send_interface="org.freedesktop.DBus.Properties"/>
</policy>
</busconfig>

View File

@ -0,0 +1,14 @@
[whitelist]
key1 = /usr/bin/kylin-background-upgrade
key2 = /usr/bin/ukui-control-center
key3 = /usr/bin/kylin-installer
key4 = /usr/bin/kylin-uninstaller
key5 = /usr/bin/kylin-software-properties-service
key6 = /usr/bin/kylin-source-update
key7 = /usr/bin/kylin-source-manager
key8 = /usr/bin/kylin-unattended-upgrade
key9 = /usr/bin/kylin-software-center
key10 = /usr/bin/kylin-printer
key11 = /usr/bin/kylin-printer-applet
key12 = /usr/bin/hedron-client
key13 = /usr/bin/kylin-software-center-plugin-synchrodata

View File

@ -0,0 +1,26 @@
#!/bin/sh
# a) it breaks if its not available
# b) the string we have here does not need it (because it has no vars)
eval_gettext() {
if [ -x /usr/bin/gettext ]; then
echo $(gettext "$1")
else
echo "$1"
fi
}
export TEXTDOMAIN=update-notifier
export TEXTDOMAINDIR=/usr/share/locale
case "$DPKG_MAINTSCRIPT_PACKAGE::$DPKG_MAINTSCRIPT_NAME" in
linux-image-extra*::postrm)
exit 0;;
esac
if [ "$0" = "/etc/kernel/postinst.d/update-notifier" ]; then
DPKG_MAINTSCRIPT_PACKAGE=linux-base
fi
# Wake the applet up
echo "*** $(eval_gettext "System logout required") ***" > /var/run/logout-required
echo "$DPKG_MAINTSCRIPT_PACKAGE" >> /var/run/logout-required.pkgs

View File

@ -0,0 +1,26 @@
#!/bin/sh
# a) it breaks if its not available
# b) the string we have here does not need it (because it has no vars)
eval_gettext() {
if [ -x /usr/bin/gettext ]; then
echo $(gettext "$1")
else
echo "$1"
fi
}
export TEXTDOMAIN=update-notifier
export TEXTDOMAINDIR=/usr/share/locale
case "$DPKG_MAINTSCRIPT_PACKAGE::$DPKG_MAINTSCRIPT_NAME" in
linux-image-extra*::postrm)
exit 0;;
esac
if [ "$0" = "/etc/kernel/postinst.d/update-notifier" ]; then
DPKG_MAINTSCRIPT_PACKAGE=linux-base
fi
# Wake the applet up
echo "*** $(eval_gettext "System restart required") ***" > /var/run/reboot-required
echo "$DPKG_MAINTSCRIPT_PACKAGE" >> /var/run/reboot-required.pkgs

View File

@ -0,0 +1,10 @@
/var/log/kylin-system-updater/kylin-system-updater.log.1
{
weekly
missingok
rotate 3
compress
notifempty
minsize 10M
copytruncate
}

Binary file not shown.

View File

@ -0,0 +1,13 @@
[Unit]
Description=kylin-system-updater dbus daemon
StartLimitIntervalSec=0
[Service]
Type=dbus
Restart=always
RestartSec=3
BusName=com.kylin.systemupgrade
ExecStart=/usr/share/kylin-system-updater/kylin-system-updater
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,3 @@
[SYSTEM]
os_version =
update_version =

View File

@ -0,0 +1,12 @@
#此配置文件内的所有配置项,在新装时都会被替换掉
[SystemStatusCover]
close_source_filter = False
priority_upgrade_restart = True
[ConfigPkgStatus]
check_resover_remove = False
check_frontend_pkg = True
[AutoUpgradeConfig]
upgradeInterval = 7
downloadRandom = 180

View File

@ -0,0 +1,9 @@
[SystemStatus]
abnormal_reboot = False
upload_upgrade_log = True
upload_installer_log = False
[InstallMode]
shutdown_install = False
manual_install = False
auto_install = False

View File

@ -0,0 +1,30 @@
[autoUpgradePolicy]
#自动更新的开关
autoUpgradeState = off
#预下载开关
preDownload = off
# 预下载的时间为时间段 例如10:00-11:00
preDownloadTime = 10:00
#添加检查更新的周期 以天为单位
updateDays = 1
# timing 为定时下载 manaual手动下载
downloadMode = timing
# 下载的时间为时间段 例如10:00-11:00
downloadTime = 10:00
#安装存在定时timing 手动:manual 关机安装bshutdown
installMode = timing
#安装也为时间段 例如00:00
installTime = 10:00
#是否开启自动重启 以及自动重启时间可以调节
automaticReboot = off
#自动重启时间的调节 now为立即重启 重启时间调节 例如00:00
automaticRebootTime = now

View File

@ -0,0 +1 @@
0

657
backend/interface.md Normal file
View File

@ -0,0 +1,657 @@
## DBUS接口
[TOC]
### 对应版本信息
| 软件包 | 目前版本 | 备注 |
| :------------------: | :-----------------------------: | :--: |
| kylin-system-updater | kylin-system-updater 1.4.16kord | |
| aptdaemon | 1.1.1+bzr982-0kylin32.3 | |
| | | |
### 描述
实现系统升级以python apt库和aptdeamon的形式
### Dbus接口信息
| 名称 | 含义 |
| -------------- | --------------------------------- |
| BUS类型 | SYSTEM BUS |
| DBUS名称 | com.kylin.systemupgrade |
| OBJECT路径 | /com/kylin/systemupgrade |
| INTERFACES名称 | com.kylin.systemupgrade.interface |
| 属性名称 | org.freedesktop.DBus.Properties |
### Apt-p2p配置项设置
### Dbus接口信息
| 名称 | 含义 |
| -------------- | --------------------------------- |
| BUS类型 | SYSTEM BUS |
| DBUS名称 | com.kylin.systemupgrade |
| OBJECT路径 | /com/kylin/systemupgrade |
| INTERFACES名称 | com.kylin.systemupgrade.interface |
| 属性名称 | org.freedesktop.DBus.Properties |
#### Get
- `简介:`获取属性的值
- `入参:` `s`iface要设置的属性的接口 `s`property要设置的属性名称
- `出参:` `Variant`变量
- `示例:`
```
#获取p2p的配置
Get(com.kylin.systemupgrade.interface,P2pBootstrap)
```
#### Set
- `简介:`设置属性的值
- `入参:` `s`iiface要设置的属性的接口 `s`iproperty要设置的属性名称 `Variant`value要设置的值
- `出参:`
- `示例:`
```
#设置p2p的配置
set("com.kylin.systemupgrade.interface","P2pBootstrap"GLib.Variant('s', "test"))
```
### 方法列表
| Method Name | Input Args | Output Args | means | 异步任务 |
| ------------------ | ---------- | ----------- | --------------------------------- | ------------------ |
| UpdateDetect | 无 | b | 更新cache产生组升级列表JSON文件 | |
| DistUpgradeAll | b | b | 全部升级 | |
| DistUpgradePartial | b,as | b | 部分升级 | |
| DistUpgradeSystem | b | b | 全盘升级 | |
| CancelDownload | 无 | b | 取消升级 | |
| InsertInstallState | ss | b | 向display表插入数据 | |
| GtDownloadspeedLimitValue | 无 | b | 获取当前限速 | |
| SetDownloadspeedMax | sb | b | 设置限速 | |
| GetBackendStatus | 无 | i | 控制获取后端状态 | |
| UnattendedUpgradeValue | ss | bs | 获取是否允许关机前更新 | |
| PurgePackages | as | b | 卸载软件包 | |
| InstalldebFile | ssbb | b | 安装本地deb包 | |
| DataBackendCollect | ss | b | | |
| CheckRebootRequired | s | b | 检查是否需要重启的方法,以及弹窗提示 | |
### Method分析
#### UpdateDetect
- `简介:`更新cache对象完成从之后拿到系统中所有可升级的包再经过源过滤、白名单等等的过滤最后输出当前`可升级的包以及分组JSON配置 输出目录: /var/lib/kylin-system-updater
- `入参:`
- `出参:`True or False 注意:不通过返回值来判断有没有执行成功 通过下面的信号
- `对应信号:`
- `UpdateDetectStatusChanged:` 更新的进度信息和状态信息
- `UpdateDetectFinished:`更新的完成的信号
#### DistUpgradePartial
- `简介:` 升级部分软件包或者分组
- `入参:` `b:` False模式只进行获取升级列表以及计算修复依赖关系以及计算是否存在删除的包`True模式:`直接进行安装的操作 注意必须选使用False模式获取升级列表以及计算依赖关系再进行True模式
`as:` 输入需要升级或者安装的分组 例如 升级系统组:["kylin-update-desktop-system"]
- `出参:`True or False 注意:不通过返回值来判断有没有执行成功 通过下面的信号
- `对应信号:`
- `UpdateDependResloveStatus:` 升级计算依赖修复反馈信号
- `UpdateDloadAndInstStaChanged:`升级安装过程的进度信号以及状态
- `UpdateInstallFinished:` 升级安装完成的信号
#### DistUpgradeAll
- `简介:`升级全部可升级的分组
- `入参:` `b:` False模式只进行获取升级列表以及计算修复依赖关系以及计算是否存在删除的包`True模式:`直接进行安装的操作 注意必须选使用False模式获取升级列表以及计算依赖关系再进行True模式
- `出参:`True or False 注意:不通过返回值来判断有没有执行成功 通过下面的信号
- `对应信号:`
- `UpdateDependResloveStatus:` 升级计算依赖修复反馈信号
- `UpdateDloadAndInstStaChanged:`升级安装过程的进度信号以及状态
- `UpdateInstallFinished:` 升级安装完成的信号
#### UpdateDownloadInfo
- `介绍:` 发送下载包信息信号
- `出参`: `i:`当前正在下载的项,`i:`所有下载的项,`i:`当前下载的字节,`i:`总的需要下载的字节,`i:`下载速度
- `示例:`
```sh
current_items = 1, total_items = 1, currenty_bytes = 45 kB, total_bytes = 45 kB, current_cps = 0 kB/s
```
#### GetBackendStatus
- `介绍:` 获取后端的状态,现在正在处理那些任务
- `入参:` `s:`当前用户的语言变量 例如传入语言环境变量`LANG` 的值`zh_CN.UTF-8` 就会将升级的语言切换为中文 同理其他也能相应设置
- `出参`: `i:`当前任务ID整型数字
- `状态示例列表:`
```python
ACTION_DEFUALT_STATUS = -1 #默认状态空闲状态
ACTION_UPDATE = 0 #处于更新cache状态
ACTION_INSTALL = 1 #包括部分升级、全部升级、q
ACTION_INSTALL_DEB = 2 #处于安装deb的状态
ACTION_CHECK_RESOLVER = 3 #处于计算依赖过程
ACTION_DOWNLOADONLY = 4 #单独下载软件包过程
ACTION_FIX_BROKEN = 5 #修复依赖的过程
ACTION_REMOVE_PACKAGES = 6 #卸载包的状态中
```
#### UnattendedUpgradeValue
- `介绍:` 设置或获取是否允许关机前更新
- `入参`: `s:`operation("get"/"set")`s:`value将要设置的值
- `示例:`
```sh
operation = "set", value = "false"
```
#### InstalldebFile
- `简介:`安装本地deb包
- `入参:` `source:(string)` 安装来源,`path:(string)`本地deb包绝对路径`_check_local_dep:(bool)`出现依赖问题时是否查询本路径下是否存在满足的包,`_auto_satisfy:(bool)`出现依赖问题时是否通过网络下载并安装依赖包
- `出参:`True or False
- `对应信号:`
- `InstalldebStatusChanged`:安装过程的进度信号以及状态
- `InstalldebFinished`:安装完成的信号
- `示例:`
```sh
source = 'kylin-installer', path = '/home/kylin/kylin-video_3.1.0-94.5_amd64.deb', _check_local_dep = 0, _auto_satisfy = 1
```
#### PurgePackages
- `简介:`卸载系统中的软件包
- `入参:` `as:` 需要卸载的包列表 `s:`当前用户的用户名 例如:kylin用户就传入`kylin`字符串
- `出参:`True or False 出参值不做任何参考意义 `注意:`其中False的时候表示后端正在处理其他任务会报错其中完成信号也会反馈结果故不采用方法的返回值来判断错误类型
- `对应信号:`
- `PurgePkgStatusChanged:`卸载过程的进度信号以及状态
- `PurgePackagesFinished:` 卸载完成的信号
- `示例:`
```sh
_purge_list = ['kylin-video','tree'] cur_user = 'kylin'
```
#### DataBackendCollect
- `介绍:` 后端数据采集
- `入参`: `messageType: ` 消息埋点(string) `uploadMessage: ` 上传数据(json格式字符串),必须包含的字段: "packageName"
- `示例:`
```sh
messageType = "UpdateInfos", uploadMessage = "{\"packageName\":\"kylin-system-updater\",\"source\":\"kylin-system-updater\",\"status\":\"True\",\"errorCode\":\"\",\"versionOld\":\"1.2.13.2kord\",\"versionNew\":\"1.2.17.1kord\"}"
messageType: 消息埋点(string) "UpdateInfos"、"InstallInfos"、"RemoveInfos"...
source: 安装来源 "kylin-installer"、"unattented-upgrade"、"kylin-software-center"、"kylin-system-updater"...
status: 安装或卸载状态 "True"/"False"
errorCode: 错误信息 ""/"..."
versionOld: 旧版本号 "1.2.13.2kord"
versionNew: 新版本号 "1.2.17.1kord"
```
#### CheckRebootRequired
- `介绍:` 检查是否需要重启的方法,以及弹窗提示
- `入参`: `s:`标识那个应用调的此接口如自动更新可填入字符串“autoyupgrade”
- `出参:`True or False 执行的结果
#### SetConfigValue
- `简介:`设置配置文件的值 配置文件的目录`/var/lib/kylin-system-updater/system-updater.conf`
- `入参:` `section`, `option`,` value` 不管布尔、列表的数据类型都转化成字符串类型来写入
- 例如传入"InstallModel","shutdown_install","False"
```
[InstallMode]
shutdown_install = True
manual_install = False
auto_install = False
pkgs_install =
pkgs_upgrade =
pkgs_remove =
```
- `出参:`True :设置值成功 False: 设置失败
#### GetConfigValue
- `简介:`获取配置文件的值 配置文件的目录`/var/lib/kylin-system-updater/system-updater.conf`
- `入参:` `section`,` option` 例如传入"InstallModel","shutdown_install"
- `出参:` `bool:`True :获取值成功 False: 获取失败 `Value:`值都以字符串类型来返回
```
[InstallMode]
shutdown_install = True
manual_install = False
auto_install = False
pkgs_install =
pkgs_upgrade =
pkgs_remove =
```
#### CheckInstallRequired
- `简介:`检查当前系统是否需要关机安装或者重启安装
- `入参:`
- `出参:` `int:` 类型返回值 表示当前系统是否需要安装 返回值数值含义如下。简要为0时不需要安装不为零时需要进行提示安装
- `1` 手动更新请求当前系统在关机时进行安装软件包
- `2` 自动更新请求当前系统在关机时进行安装软件包
- `0` 当前系统不需要进行安装
#### FixBrokenDepends
- `简介:` 修复当前的系统Apt环境收到`UpdateFixBrokenStatus`后进行调用类似于调用apt install -f 来进行修复
- `入参:`
- `出参:` True or False 执行的结果 无实际意义
- `对应信号:`
- `FixBrokenStatusChanged:` 修复依赖的状态信号 可不使用
#### MountSquashfsSource
- `简介:` 挂载离线源squashfs
- `入参:` `s:` 挂载文件的位置
- `出参:` `b:` True or False 执行的结果,`s:` 字符类型错误信息描述
### Signal列表
| Signal Name | Output Args | means |
| ---------------------------- | ----------- | ------------------------ |
| UpdateDetectStatusChanged | i,s | 更新进度信息以及状态信息 |
| UpdateDetectFinished | b,as,s,s | 更新完成信号 |
| UpdateDloadAndInstStaChanged | as,i,s,s | 升级的进度信号以及状态 |
| UpdateInstallFinished | b,as,s,s | 升级完成的信号 |
| UpdateDownloadInfo | i,i,u,u,i | 发送下载包信息信号 |
| UpdateDependResloveStatus | b,b,s | 更新依赖修复信息 |
| DistupgradeDependResloveStatus | b,s | 更新全盘修复信息 |
| Cancelable | b | 是否可取消 |
| UpdateSqlitSingle | | |
| FixBrokenStatusChanged | iiisss | 修复依赖的状态信号 |
| PurgePackagesFinished | iss | 卸载完成信号 |
| PurgePkgStatusChanged | bss | 卸载进度信息以及状态信息 |
| RebootLogoutRequired | s | 请求重启或者注销的信号 |
| UpdateInstallFinished | b,as,s,s | 下载完成的信号 |
### Signal分析
#### UpdateDetectStatusChanged
- `介绍:`更新的进度信息和状态信息
- `出参`:`i:`更新的进度信息从0-100%`s:`更新的状态信息,
- `示例:`
```sh
progress = 9 , status = 正在解决依赖关系
progress = 92 , status = 正在载入软件列表
progress = 92 , status = 完成
```
#### UpdateDetectFinished
- `介绍:`更新的完成的信号
- `出参`: `b:`更新是否成功,`as:`可升级的组列表,`s:`产生错误的结果,`s:`产生错误的原因
- `示例:`
```sh
success = True , upgrade_group = ['kylin-update-desktop-system', 'tree', 'texinfo', 'kylin-update-manager', 'dnsmasq-base', 'vino', 'dpkg-dev', 'ghostscript', 'atril', 'wpasupplicant', 'eom', 'eom-common', 'fcitx-bin', 'fcitx-data', 'fcitx-frontend-gtk2', 'wps-office'], error_string = , error_desc =
error_string = 获取更新软件推送失败,请稍后再进行尝试更新 , error_desc = 推送服务器连接异常
```
#### UpdateDependResloveStatus
- `介绍:`升级计算依赖修复反馈信号
- `出参`: `b:`修复依赖关系是否成功,`b:`是否存在升级需要卸载的包,`as:`卸载的包列表,`as:`卸载的包的描述信息,`as:`卸载此包的原因 升级安装那些包导致的,`s:`产生错误的结果,`s:`产生错误的原因
- `示例:`
```sh
UpdateDependResloveStatus:resolver_status = True , remove_status = True , remove_pkgs = ['kylin-burner-i18n'],pkg_raw_description = ['Sophisticated CD/DVD burning application - localizations files'] ,delete_desc = ['kylin-burner-i18n 将要被删除,由于升级 kylin-burner'],error_string = , error_desc =
```
#### UpdateFixBrokenStatus
- `介绍:`更新检查过程中是否需要修复系统Apt的环境提示是否存在卸载软件包的情况
- `出参`: `b:`是否能修复系统环境,`b:`是否存在修复需要卸载的包,`as:`卸载的包列表,`as:`卸载的包的描述信息,`as:`卸载此包的原因,`s:`产生错误的结果,`s:`产生错误的原因
- `示例:`
```sh
UpdateDependResloveStatus:resolver_status = True , remove_status = True , remove_pkgs = ['kylin-burner-i18n'],pkg_raw_description = ['Sophisticated CD/DVD burning application - localizations files'] ,delete_desc = ['kylin-burner-i18n 将要被删除,由于升级 kylin-burner'],error_string = , error_desc =
```
#### UpdateDloadAndInstStaChanged
- `介绍:` 升级安装过程的进度信号以及状态
- `出参`: `as:`当前那些组在升级安装 `i:`更新的进度信息从0-100%`s:`更新的状态信息 `s:`下载的细节信息
- ` 示例:`
```sh
groups_list = ['kylin-update-desktop-system'] progress = 15 , status = 下载中 current_details = 下载中 tree
```
#### UpdateInstallFinished
- `介绍:` 升级安装完成的信号
- `出参`: `b:`升级是否成功,`as:`可升级的组列表,`s:`产生错误的结果,`s:`产生错误的原因
- `示例:`
```sh
pdateInstallFinished success = True , upgrade_group = ['tree'], error_string = 系统升级完成。 , error_desc =
```
#### UpdateDownloadFinished
- `介绍:` 下载完成的信号
- `出参`: `b:`下载是否成功,`as:`可升级的组列表,`s:`产生错误的结果,`s:`产生错误的原因
- `示例:`
```sh
pdateInstallFinished success = True , upgrade_group = ['tree'], error_string = 系统升级完成。 , error_desc =
```
#### FixBrokenStatusChanged
- `介绍:` 修复依赖过程中的状态反馈信号
- `出参`: `i:`修复依赖是否完成 `i:`修复依赖过程是否成功`注意:只有修复完成时,修复依赖是否成功才有意义``i:`修复的进度信息 `s:`修复的状态信息 `s:`产生错误的结果,`s:`产生错误的原因
- ` 示例:`
```sh
emit FixBrokenStatusChanged finished = False , success = True,progress = 66 , status = 正在应用更改,error_string = , error_desc =
```
-
#### PurgePkgStatusChanged
- `介绍:`卸载的进度信息和状态信息以及状态的细节信息
- `出参`:`i:`卸载的进度信息从0-100%`s:`卸载的状态信息,`s:`卸载的细节信息
- `示例:`
```sh
INFO:emit PurgePkgStatusChanged progress = 63 , status = 正在应用更改 ,current_details = 正在准备删除 kylin-video
INFO:emit PurgePkgStatusChanged progress = 76 , status = 正在应用更改 ,current_details = 正在卸载 kylin-video
```
#### PurgePackagesFinished
- `介绍:`卸载的完成的信号
- `出参`: `b:`卸载是否成功,`s:`产生错误的结果,`s:`产生错误的原因
- `示例:`
```sh
#卸载完成
PurgePackagesFinished success = True , error_string = 卸载完成。 , error_desc =
#卸载失败
PurgePackagesFinished success = False , error_string = 软件包不存在 , error_desc = 检查包名的拼写是否正确,以及是否启用了相应的仓库。
PurgePackagesFinished success = False , error_string = 软件包没有安装 , error_desc = 不需要进行卸载。
#卸载失败 由于正在处理其他任务也同样会报错
PurgePackagesFinished success = False , error_string = 其他任务正在更新升级中,请稍后再卸载。 , error_desc =
```
#### InstalldebStatusChanged
- `介绍:`安装的进度信息和状态信息以及状态的细节信息
- `出参`:`i:`安装的进度信息从0-100%`s:`安装的状态信息,`s:`安装的细节信息
- `示例:`
```sh
InstalldebStatusChanged progress = 57 , status = 正在应用更改 ,current_details = 正在配置 python3-bandit
InstalldebStatusChanged progress = 57 , status = 正在应用更改 ,current_details = python3-bandit 已安装
```
#### InstalldebFinished
- `介绍:`安装的完成的信号
- `出参`: `b:`安装是否成功,`s:`产生错误的结果,`s:`产生错误的原因
- `示例:`
```sh
#安装完成
InstalldebFinished success = True , error_string = , error_desc =
#安装失败 缺少依赖的
InstalldebFinished success = False , error_string = bandit dependency is not satisfied , error_desc = python3-bandit
#安装失败 选择从网络拉依赖 网络断开 报网络错误
InstalldebFinished success = False , error_string = 下载软件包文件失败 , error_desc = 检查您的网络连接。
```
#### RebootLogoutRequired
- `介绍:`请求重启和注销的信号
- `出参`: `s:` "reboot" 表示重启 "logout"表示注销
- `示例:`
```sh
Emitting RebootLogoutRequired required_status = reboot
```
#### InstallDetectStatus
- `介绍:`下载安装前的状态检查
- `出参`: `b:`检查出错时为`False`,没有错误`success``s:`产生错误的码
- 错误码示例:
```python
ERROR_NOT_DISK_SPACE = "error-not-disk-space"
```
- `示例:`
```sh
#表示出现磁盘已满的错误z
InstallDetectStatus success = False , error_code = "error-not-disk-space"
```
后端日志:`/var/log/kylin-system-updater/kylin-system-updater.log.1`
### 更新过程报错信息总结
| 错误信息 | 错误原因 | 解决办法 |
| -------------------- | ------------------------------------------------------------ | ---------------------------------------- |
| 下载软件仓库信息失败 | 源存在问题使用apt update检查若存在报错则更新管理器无问题 | 检查源是否可以使用 |
| 无法访问源管理服务器 | 源管理服务器存在问题 | 源管理服务器是否可用或者检查源服务器配置 |
| 软件索引已经损坏 | 当前系统中cache被破坏apt无法使用 | 终端检查错误原因进行解决 |
| 无法初始化软件包信息 | apt存在某些问题 | 具体错误原因查看日志相应解决 |
| 无法获取组配置软件包 | 源中不存在kylin-update-desktop-config | 将此包放入源仓库或者写配置文件不强制安装 |
| 无法读取推送升级列表 | 读取推送列表出现问题 | 检查important.list是否存在 |
| 获取软件推送失败 | 老版本文案同 无法访问源管理服务器解决 | |
### 安装过程报错信息总结
| 错误信息 | 错误原因 | 解决办法 |
| ------------------ | ------------------------------ | ---------------------------------- |
| 软件包操作失败 | 被升级的软件包有问题 | 检查后端log日志查看那个包存在问题 |
| 下载软件包文件失败 | 网络原因的或者这个软件包的仓库 | 检查网络以及源仓库 |
| 磁盘空间不足 | 磁盘的空间不足 | 查看日志那些目录空间不足 |
| 不能计算升级 | 无法计算依赖关系 | 检查日志那个包出现的问题,相应解决 |
| | | |

103
backend/kylin-system-updater Executable file
View File

@ -0,0 +1,103 @@
#!/usr/bin/python3
from SystemUpdater.UpdateManager import UpdateManager
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"))
parser.add_option ("-n","--no-update-source", action="store_true",
dest="no_update_source", default=False,
help=_("Do not check for updates source when updating"))
parser.add_option ("--no-update", action="store_true",
dest="no_update", default=False,
help="Do not check for updates when starting")
parser.add_option("-c", "--close-filter",
default=False,
action="store_true", dest="close_filter",
help=_("Quit and close allowed origin"))
parser.add_option("--no-check-network",
default=False,
action="store_true", dest="no_check_network",
help=_("Quit and close check network"))
(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('kylin-system-updater(LANGUAGE:%s LANG:%s) starting ...',os.environ["LANGUAGE"],os.environ["LANG"])
app = UpdateManager(options)
#当出现安装过程中异常的重启时 开机直接进行修复操作
# if app.configs_cover.getWithDefault("ConfigPkgStatus", "check_frontend_pkg", False) == True:
# app.configs_cover.setValue("ConfigPkgStatus","check_frontend_pkg",str(False),True)
# app.check_frontend_pkg()
#当出现安装过程中异常的重启时 开机直接进行修复操作
if app.configs_uncover.getWithDefault("SystemStatus", "abnormal_reboot", False) == True:
app.start_update()
app.run()

View File

@ -0,0 +1,81 @@
#!/usr/bin/python3
from SystemUpdater.UpgradeStrategies import UpgradeStrategies
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 upgrade_strategies_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 = UpgradeStrategies(options)
app.run()

6
backend/po/ChangeLog Normal file
View File

@ -0,0 +1,6 @@
2021-09-16 XueYi Luo <luoxueyi@kylinos.cn>
* zh_CN.po: Updated Simplified Chinese translation.
* zh_HK.po: Updated translation for HongKong,china.
* zh_TW.po: Updated translation for Taiwan,China.

28
backend/po/Makefile Normal file
View File

@ -0,0 +1,28 @@
top_srcdir=`pwd`/..
DOMAIN=kylin-system-updater
PO_FILES := $(wildcard *.po)
CONTACT=sebastian.heinlein@web.de
XGETTEXT_ARGS = --msgid-bugs-address=$(CONTACT)
XGETTEXT_ARGS += --keyword=unicode_gettext:2 --keyword=unicode_ngettext:2,3
XGETTEXT_ARGS += --language=python
all: update-po
# update the pot
$(DOMAIN).pot:
XGETTEXT_ARGS="$(XGETTEXT_ARGS)" intltool-update -p -g $(DOMAIN)
# merge the new stuff into the po files
merge-po: $(PO_FILES)
XGETTEXT_ARGS="$(XGETTEXT_ARGS)" intltool-update -r -g $(DOMAIN);
# create mo from the pos
%.mo : %.po
mkdir -p mo/$(subst .po,,$<)/LC_MESSAGES/
msgfmt $< -o mo/$(subst .po,,$<)/LC_MESSAGES/$(DOMAIN).mo
# dummy target
update-po: $(DOMAIN).pot merge-po $(patsubst %.po,%.mo,$(wildcard *.po))

11
backend/po/POTFILES.in Normal file
View File

@ -0,0 +1,11 @@
[encoding: UTF-8]
SystemUpdater/backend/InstallBackendAptdaemon.py
SystemUpdater/backend/__init__.py
SystemUpdater/UpdateManager.py
SystemUpdater/Core/MyCache.py
SystemUpdater/Core/UpdateList.py
SystemUpdater/Core/OriginFilter.py
SystemUpdater/Core/Database.py
SystemUpdater/UpdateManagerDbus.py
SystemUpdater/Core/utils.py
SystemUpdater/Core/enums.py

0
backend/po/POTFILES.skip Normal file
View File

2763
backend/po/zh_CN.po Normal file

File diff suppressed because it is too large Load Diff

2696
backend/po/zh_HK.po Normal file

File diff suppressed because it is too large Load Diff

2728
backend/po/zh_TW.po Normal file

File diff suppressed because it is too large Load Diff

50
backend/report-updater-bug Executable file
View File

@ -0,0 +1,50 @@
#!/bin/sh
#系统升级收集bug日志使用
echo "系统升级收集BUG日志使用..."
#建立收集的log目录
mkdir updaterlog
#记录一些基本信息
date >> updaterlog/base-info
dpkg -l | grep kylin-system-updater >> updaterlog/base-info
dpkg -l | grep ukui-control-center >> updaterlog/base-info
dpkg -l | grep aptdaemon >> updaterlog/base-info
echo $1 >> updaterlog/base-info
echo "记录BUG产生时间系统当前时间以及升级相关的版本信息"
cat updaterlog/base-info
cp /etc/apt/sources.list updaterlog || true
cp -r /usr/share/kylin-update-desktop-config/config/ updaterlog || true
#复制后端的日志
cp -r /var/log/kylin-system-updater/ updaterlog || true
#收集apt的日志
cp -r /var/log/apt/term.log updaterlog || true
cp -r /var/log/apt/history.log updaterlog || true
#收集aptdamon的日志
cp -r /var/log/kylin-unattended-upgrades/ updaterlog || true
outputName="$(date +%m-%d,%H-%M-%S)-updaterLog.tar.gz"
#将所有的日志进行打包
tar -czvf updaterLog.tar.gz updaterlog >/dev/null
#删除收集的日志目录
rm -rf updaterlog
#将文件存储到桌面
if [ ! -d ~/桌面 ]; then
mv updaterLog.tar.gz ~/Desktop/$outputName
echo 输出位置:~/Desktop/$outputName
else
mv updaterLog.tar.gz ~/桌面/$outputName
echo 输出位置:~/桌面/$outputName
fi
echo "系统更新日志收集完毕..."
echo "\033[1;31m注意\033[0m 1、请确保Bug复现的时间与执行脚本收集日志时间相近以此能根据脚本执行时间快速定位到问题的相关日志..."
echo " 2、若Bug复现的时间与现在时间相差较远时请手动输入大概复现时间。例如 report-updater-bug 月-日,时-分"
echo "请将桌面下\033[5;32;49;1m $outputName \033[0m日志文件提交到禅道... "

15
backend/setup.cfg Normal file
View File

@ -0,0 +1,15 @@
[build_i18n]
domain=kylin-system-updater
# xml_files=[("share/metainfo/",
# ("data/update-manager.appdata.xml.in",)),
# ]
[sdist]
formats = bztar
[nosetests]
match=test
# [install]
# skip-build=0

21
backend/setup.py Executable file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env python3
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
from distutils.core import setup
from DistUtilsExtra.command import (
build_extra, build_i18n, build_help)
disabled = []
class CustomBuild(build_extra.build_extra):
def run(self):
build_extra.build_extra.run(self)
setup(
packages=[ 'SystemUpdater',
'SystemUpdater.backend',
'SystemUpdater.Core'
],
cmdclass={ "build": CustomBuild,
"build_i18n": build_i18n.build_i18n
# "build_help": build_help.build_help
}
)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,153 @@
// Automatically upgrade packages from these (origin:archive) pairs
//
// Note that in Ubuntu security updates may pull in new dependencies
// from non-security sources (e.g. chromium). By allowing the release
// pocket these get automatically pulled in.
Unattended-Upgrade::Allowed-Origins {
"Kylin:10.1";
":default";
//"${distro_id}:${distro_codename}";
//"${distro_id}:${distro_codename}-security";
// Extended Security Maintenance; doesn't necessarily exist for
// every release and this system may not have it installed, but if
// available, the policy for updates is such that unattended-upgrades
// should also install from here by default.
//"${distro_id}ESMApps:${distro_codename}-apps-security";
//"${distro_id}ESM:${distro_codename}-infra-security";
// "${distro_id}:${distro_codename}-updates";
// "${distro_id}:${distro_codename}-proposed";
// "${distro_id}:${distro_codename}-backports";
};
// Python regular expressions, matching packages to exclude from upgrading
Unattended-Upgrade::Package-Blacklist {
// The following matches all packages starting with linux-
// "linux-";
// Use $ to explicitely define the end of a package name. Without
// the $, "libc6" would match all of them.
// "libc6$";
// "libc6-dev$";
// "libc6-i686$";
// Special characters need escaping
// "libstdc\+\+6$";
// The following matches packages like xen-system-amd64, xen-utils-4.1,
// xenstore-utils and libxenstore3.0
// "(lib)?xen(store)?";
// For more information about Python regular expressions, see
// https://docs.python.org/3/howto/regex.html
};
// This option controls whether the development release of Ubuntu will be
// upgraded automatically. Valid values are "true", "false", and "auto".
Unattended-Upgrade::DevRelease "false";
// This option allows you to control if on a unclean dpkg exit
// unattended-upgrades will automatically run
// dpkg --force-confold --configure -a
// The default is true, to ensure updates keep getting installed
//Unattended-Upgrade::AutoFixInterruptedDpkg "true";
//tell dpkg not to cause conffile prompts
DPkg:Options{
"--force-confnew";
};
// Split the upgrade into the smallest possible chunks so that
// they can be interrupted with SIGTERM. This makes the upgrade
// a bit slower but it has the benefit that shutdown while a upgrade
// is running is possible (with a small delay)
//Unattended-Upgrade::MinimalSteps "false";
// Install all updates when the machine is shutting down
// instead of doing it in the background while the machine is running.
// This will (obviously) make shutdown slower.
// Unattended-upgrades increases logind's InhibitDelayMaxSec to 30s.
// This allows more time for unattended-upgrades to shut down gracefully
// or even install a few packages in InstallOnShutdown mode, but is still a
// big step back from the 30 minutes allowed for InstallOnShutdown previously.
// Users enabling InstallOnShutdown mode are advised to increase
// InhibitDelayMaxSec even further, possibly to 30 minutes.
//Unattended-Upgrade::InstallOnShutdown "False";
// Send email to this address for problems or packages upgrades
// If empty or unset then no email is sent, make sure that you
// have a working mail setup on your system. A package that provides
// 'mailx' must be installed. E.g. "user@example.com"
//Unattended-Upgrade::Mail "";
// Set this value to one of:
// "always", "only-on-error" or "on-change"
// If this is not set, then any legacy MailOnlyOnError (boolean) value
// is used to chose between "only-on-error" and "on-change"
//Unattended-Upgrade::MailReport "on-change";
// Remove unused automatically installed kernel-related packages
// (kernel images, kernel headers and kernel version locked tools).
//Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
// Do automatic removal of newly unused dependencies after the upgrade
//Unattended-Upgrade::Remove-New-Unused-Dependencies "true";
// Do automatic removal of unused packages after the upgrade
// (equivalent to apt-get autoremove)
//Unattended-Upgrade::Remove-Unused-Dependencies "false";
// Automatically reboot *WITHOUT CONFIRMATION* if
// the file /var/run/reboot-required is found after the upgrade
//Unattended-Upgrade::Automatic-Reboot "false";
// Automatically reboot even if there are users currently logged in
// when Unattended-Upgrade::Automatic-Reboot is set to true
//Unattended-Upgrade::Automatic-Reboot-WithUsers "true";
// If automatic reboot is enabled and needed, reboot at the specific
// time instead of immediately
// Default: "now"
//Unattended-Upgrade::Automatic-Reboot-Time "02:00";
// Use apt bandwidth limit feature, this example limits the download
// speed to 70kb/sec
//Acquire::http::Dl-Limit "70";
//Retry times after each download failure
Unattended-Upgrade::RetryTimes 3;
// Enable logging to syslog. Default is False
//Unattended-Upgrade::SyslogEnable "True";
// Specify syslog facility. Default is daemon
// Unattended-Upgrade::SyslogFacility "daemon";
// Download and install upgrades only on AC power
// (i.e. skip or gracefully stop updates on battery)
Unattended-Upgrade::OnlyOnACPower "True";
// Download and install upgrades only on non-metered connection
// (i.e. skip or gracefully stop updates on a metered connection)
// Unattended-Upgrade::Skip-Updates-On-Metered-Connections "true";
// Verbose logging
//Unattended-Upgrade::Verbose "True";
// Print debugging information both in unattended-upgrades and
// in unattended-upgrade-shutdown
Unattended-Upgrade::Debug "True";
// Allow package downgrade if Pin-Priority exceeds 1000
// Unattended-Upgrade::Allow-downgrade "false";
// When APT fails to mark a package to be upgraded or installed try adjusting
// candidates of related packages to help APT's resolver in finding a solution
// where the package can be upgraded or installed.
// This is a workaround until APT's resolver is fixed to always find a
// solution if it exists. (See Debian bug #711128.)
// The fallback is enabled by default, except on Debian's sid release because
// uninstallable packages are frequent there.
// Disabling the fallback speeds up unattended-upgrades when there are
// uninstallable packages at the expense of rarely keeping back packages which
// could be upgraded or installed.
// Unattended-Upgrade::Allow-APT-Mark-Fallback "true";

View File

@ -0,0 +1,14 @@
[Unit]
Description=Unattended Upgrades Shutdown
After=network.target local-fs.target systemd-logind.service kylin-system-updater.service
RequiresMountsFor=/run /var/log /var/run /var/lib /boot
Documentation=man:unattended-upgrade(8)
[Service]
ExecStart=/usr/bin/kylin-unattended-upgrade-shutdown --wait-for-signal
Type=idle
#KillMode=process
#TimeoutStopSec=1800
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,10 @@
/var/log/unattended-upgrades/unattended-upgrades.log
/var/log/unattended-upgrades/unattended-upgrades-dpkg.log
/var/log/unattended-upgrades/unattended-upgrades-shutdown.log
{
rotate 6
monthly
compress
missingok
notifempty
}

72
unattended-upgrades/notify Executable file
View File

@ -0,0 +1,72 @@
#!/usr/bin/python3
import os
import sys
import subprocess
import dbus
import re
import atexit
import logging
import signal
NOTIFICATION_PIPE = '/tmp/notification.pipe'
PID_FILE = '/tmp/notify.pid'
def signal_handler_term():
logging.warning("SIGTERM received, will stop")
sys.exit(1)
def signal_handler_int():
logging.warning("SIGINT received, will stop")
sys.exit(1)
def translate(text):
env = os.environ['LANG']
if re.match('zh_CN',env):
if text == 'install start':
return '安装开始'
elif text == 'install finish':
return '安装结束'
else:
return text
def notify(iface,msg):
msg = msg.rstrip()
title = ' '
content = ' '
if msg =='install start':
title = translate('install start')
content = translate('install start')
elif msg == 'install finish':
title = translate('install finish')
content = translate('install start')
#print(title,content)
iface.Notify(' ',1,' ',title,content,[],{},3)
def main():
bus = dbus.SessionBus()
proxy_object = bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications')
iface = dbus.Interface(proxy_object,dbus_interface='org.freedesktop.Notifications')
f=open(NOTIFICATION_PIPE,'r')
while True:
msg = f.read()
if len(msg)>0:
bus = dbus.SessionBus()
proxy_object = bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications')
iface = dbus.Interface(proxy_object,dbus_interface='org.freedesktop.Notifications')
notify(iface,msg)
f.flush()
if __name__ == "__main__":
signal.signal(signal.SIGTERM, signal_handler_term)
signal.signal(signal.SIGINT,signal_handler_int)
if os.path.exists(PID_FILE):
logging.info("notify already exists, exiting...")
sys.exit(0)
else:
# clean up pid file on exit
with open(PID_FILE, "w+") as fp:
fp.write("%s" % os.getpid())
atexit.register(os.remove, PID_FILE)
main()

View File

@ -0,0 +1,7 @@
[Desktop Entry]
Name=update-notify
Exec=/usr/bin/notify
Type=Application
NoDisplay=true
Comment=update-notify