Import Upstream version 2.0.5.15
This commit is contained in:
parent
d1b4a1e555
commit
c9647c003b
|
@ -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 文档
|
||||
|
|
@ -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)
|
|
@ -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")
|
|
@ -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)
|
|
@ -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
|
@ -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
|
|
@ -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")
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
@ -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
|
|
@ -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 {}
|
|
@ -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
|
@ -0,0 +1 @@
|
|||
Dir::Bin::Methods::ftp "ftp";
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
|
@ -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>
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
@ -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
|
|
@ -0,0 +1,3 @@
|
|||
[SYSTEM]
|
||||
os_version =
|
||||
update_version =
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
0
|
|
@ -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日志查看那个包存在问题 |
|
||||
| 下载软件包文件失败 | 网络原因的或者这个软件包的仓库 | 检查网络以及源仓库 |
|
||||
| 磁盘空间不足 | 磁盘的空间不足 | 查看日志那些目录空间不足 |
|
||||
| 不能计算升级 | 无法计算依赖关系 | 检查日志那个包出现的问题,相应解决 |
|
||||
| | | |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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()
|
|
@ -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()
|
|
@ -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.
|
||||
|
|
@ -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))
|
||||
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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日志文件提交到禅道... "
|
|
@ -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
|
|
@ -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
|
@ -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";
|
|
@ -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
|
|
@ -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
|
||||
}
|
|
@ -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()
|
|
@ -0,0 +1,7 @@
|
|||
[Desktop Entry]
|
||||
Name=update-notify
|
||||
Exec=/usr/bin/notify
|
||||
Type=Application
|
||||
NoDisplay=true
|
||||
Comment=update-notify
|
||||
|
Loading…
Reference in New Issue