diff -Nru kylin-system-updater-3.0.0.0/debian/changelog kylin-system-updater-3.0.0.0/debian/changelog --- kylin-system-updater-3.0.0.0/debian/changelog 2024-11-27 13:56:00.000000000 +0800 +++ kylin-system-updater-3.0.0.0/debian/changelog 2025-02-25 14:13:19.000000000 +0800 @@ -1,3 +1,21 @@ +kylin-system-updater (3.0.0.0-ok34) nile; urgency=medium + + * BUG: 无 + * 需求号: 无 + * 其他改动说明: 修复编译报错 + * 其他改动影响域:无 + + -- wangsong <wangsong@kylinos.cn> Tue, 25 Feb 2025 14:13:19 +0800 + +kylin-system-updater (3.0.0.0-ok33) nile; urgency=medium + + * BUG: 无 + * 需求号: 无 + * 其他改动说明: 同步主线V11改动 + * 其他改动影响域:无 + + -- wangsong <wangsong@kylinos.cn> Thu, 13 Feb 2025 10:04:42 +0800 + kylin-system-updater (3.0.0.0-ok32) nile; urgency=medium * BUG: diff -Nru kylin-system-updater-3.0.0.0/debian/kylin-system-updater-immutable.install kylin-system-updater-3.0.0.0/debian/kylin-system-updater-immutable.install --- kylin-system-updater-3.0.0.0/debian/kylin-system-updater-immutable.install 2024-11-27 13:56:00.000000000 +0800 +++ kylin-system-updater-3.0.0.0/debian/kylin-system-updater-immutable.install 2025-02-25 14:13:19.000000000 +0800 @@ -1,30 +1,33 @@ #!/usr/bin/dh-exec -#backend -backend-immutable/data/kylin-system-updater.db /usr/share/kylin-system-updater/ -backend-immutable/report-updater-bug /usr/bin -backend-immutable/data/30kylin-system-updater /etc/apt/apt.conf.d/ + +# dbus服务配置 backend-immutable/data/com.kylin.systemupgrade.conf /etc/dbus-1/system.d/ -#backend-immutable/data/com.kylin.systemupgrade.limit /etc/dbus-1/conf/ backend-immutable/data/kylin-system-updater.service /usr/lib/systemd/system/ -#backend-immutable/data/kylin-upgrade-poweroff.service /usr/lib/systemd/system/ -backend-immutable/data/kylin-logout-required /usr/share/kylin-system-updater/ -backend-immutable/data/kylin-reboot-required /usr/share/kylin-system-updater/ +backend-immutable/data/cn.kylinos.KylinSystemUpdater.policy /usr/share/polkit-1/actions/ + +# 配置文件安装 +backend-immutable/data/settings-default.json /etc/kylin-system-updater/ + +# 程序代码安装 backend-immutable/kylin-system-updater /usr/share/kylin-system-updater/ -backend-immutable/monitor-reboot /usr/share/kylin-system-updater/ backend-immutable/SystemUpdater/*.py /usr/share/kylin-system-updater/SystemUpdater/ backend-immutable/SystemUpdater/backend/*.py /usr/share/kylin-system-updater/SystemUpdater/backend/ backend-immutable/SystemUpdater/Core/*.py /usr/share/kylin-system-updater/SystemUpdater/Core/ +backend-immutable/SystemUpdater/packageManager/*.py /usr/share/kylin-system-updater/SystemUpdater/packageManager/ backend-immutable/build/mo/* /usr/share/locale/ +backend-immutable/report-updater-bug /usr/bin -backend-immutable/data/system-updater-defaults.conf /etc/kylin-system-updater/ +# 自动更新配置安装 backend-immutable/data/unattended-upgrades-policy.conf /var/lib/unattended-upgrades/ backend-immutable/data/unattended-upgrades-timestamp /var/lib/unattended-upgrades/ -backend-immutable/data/cn.kylinos.KylinSystemUpdater.policy /usr/share/polkit-1/actions/ + +# 版本配置文件 backend-immutable/data/kylin-system-version.conf /etc/kylin-version/ +backend-immutable/data/kylin-system-updater.db /usr/share/kylin-system-updater/ #configDaemon backend-immutable/kylin-upgrade-strategies /usr/share/kylin-system-updater/ backend-immutable/data/com.kylin.UpgradeStrategies.conf /etc/dbus-1/system.d/ backend-immutable/data/com.kylin.UpgradeStrategies.service /usr/share/dbus-1/system-services/ backend-immutable/data/cn.kylinos.UpgradeStrategies.policy /usr/share/polkit-1/actions/ -backend-immutable/data/kylin-system-updater /etc/logrotate.d +backend-immutable/data/kylin-system-updater /etc/logrotate.d \ No newline at end of file diff -Nru kylin-system-updater-3.0.0.0/debian/patches/0026-changelog-3.0.0.0-ok33.patch kylin-system-updater-3.0.0.0/debian/patches/0026-changelog-3.0.0.0-ok33.patch --- kylin-system-updater-3.0.0.0/debian/patches/0026-changelog-3.0.0.0-ok33.patch 1970-01-01 08:00:00.000000000 +0800 +++ kylin-system-updater-3.0.0.0/debian/patches/0026-changelog-3.0.0.0-ok33.patch 2025-02-25 14:13:19.000000000 +0800 @@ -0,0 +1,17031 @@ +From: wangsong <wangsong@kylinos.cn> +Date: Thu, 13 Feb 2025 10:05:10 +0800 +Subject: changelog:3.0.0.0-ok33 + +--- + .../SystemUpdater/Core/DataAcquisition.py | 50 +- + backend-immutable/SystemUpdater/Core/Database.py | 34 +- + .../SystemUpdater/Core/DbusControl.py | 87 + + .../SystemUpdater/Core/PluginManager.py | 397 --- + backend-immutable/SystemUpdater/Core/UpdateList.py | 8 +- + .../SystemUpdater/Core/diskManager.py | 123 + + backend-immutable/SystemUpdater/Core/enums.py | 96 +- + .../SystemUpdater/Core/quitStrategiesService.py | 60 + + .../SystemUpdater/Core/space_rules.py | 175 ++ + backend-immutable/SystemUpdater/UpdateManager.py | 132 +- + .../SystemUpdater/UpdateManagerDbus.py | 225 +- + .../SystemUpdater/UpdatesAvailable.py | 129 +- + .../SystemUpdater/UpgradeStrategies.py | 39 +- + .../SystemUpdater/UpgradeStrategiesDbus.py | 268 +- + .../SystemUpdater/backend/BackendOstreeNext.py | 292 +- + backend-immutable/SystemUpdater/configs.py | 67 + + backend-immutable/SystemUpdater/constants.py | 12 + + .../packageManager/UpdaterSDKInterface.py | 584 ++++ + .../SystemUpdater/packageManager/__init__.py | 0 + .../SystemUpdater/packageManager/pmEnums.py | 370 +++ + .../SystemUpdater/packageManager/pmUtils.py | 932 +++++++ + .../SystemUpdater/packageManager/pmWorker.py | 201 ++ + backend-immutable/SystemUpdater/pluginbase.py | 449 ++++ + .../SystemUpdater/plugins/update/randomstr.py | 11 + + backend-immutable/SystemUpdater/push_update.py | 83 + + .../data/com.kylin.UpgradeStrategies.limit | 33 + + .../data/com.kylin.UpgradeStrategies.limit.verify | 1 + + backend-immutable/data/settings-default.json | 3 + + backend-immutable/kylin-system-updater | 13 +- + backend-immutable/kylin-upgrade-strategies | 2 +- + backend-immutable/po/bo_CN.po | 4 + + backend-immutable/po/zh_CN.po | 17 +- + backend-immutable/po/zh_HK.po | 4 + + backend-immutable/po/zh_TW.po | 4 + + .../data/cn.kylinos.KylinSystemUpdater.policy | 129 + + .../tests/data/cn.kylinos.UpgradeStrategies.policy | 25 + + .../tests/data/com.kylin.UpgradeStrategies.conf | 23 + + .../tests/data/com.kylin.UpgradeStrategies.limit | 33 + + .../data/com.kylin.UpgradeStrategies.limit.verify | 1 + + .../tests/data/com.kylin.UpgradeStrategies.service | 4 + + .../tests/data/com.kylin.systemupgrade.conf | 23 + + .../tests/data/com.kylin.systemupgrade.limit | 14 + + backend-immutable/tests/data/inhibit-sleep.conf | 25 + + backend-immutable/tests/data/kylin-system-updater | 10 + + .../tests/data/kylin-system-updater.db | Bin 0 -> 40960 bytes + .../tests/data/kylin-system-updater.service | 13 + + .../tests/data/kylin-system-version.conf | 3 + + backend-immutable/tests/data/settings-default.json | 3 + + .../tests/data/unattended-upgrades-policy.conf | 44 + + .../tests/data/unattended-upgrades-timestamp | 4 + + backend-immutable/tests/diskManager_unittest.py | 30 + + backend-immutable/tests/enums.sh | 12 +- + backend-immutable/tests/interface.md | 657 +++++ + backend-immutable/tests/kylin-system-updater | 80 + + backend-immutable/tests/kylin-upgrade-strategies | 81 + + backend-immutable/tests/libtest-core.sh | 33 - + backend-immutable/tests/libtest.sh | 144 +- + backend-immutable/tests/po/ChangeLog | 6 + + backend-immutable/tests/po/Makefile | 28 + + backend-immutable/tests/po/POTFILES.in | 9 + + backend-immutable/tests/po/POTFILES.skip | 0 + backend-immutable/tests/po/bo_CN.po | 242 ++ + backend-immutable/tests/po/zh_CN.po | 2821 ++++++++++++++++++++ + backend-immutable/tests/po/zh_HK.po | 2712 +++++++++++++++++++ + backend-immutable/tests/po/zh_TW.po | 2744 +++++++++++++++++++ + backend-immutable/tests/report-updater-bug | 62 + + backend-immutable/tests/setup.cfg | 15 + + backend-immutable/tests/setup.py | 21 + + backend-immutable/tests/sqlite_tests.py | 13 + + backend-immutable/tests/test-configs.py | 86 + + backend-immutable/tests/test-for-frontend.sh | 45 + + backend-immutable/tests/test-offline-upgrade.sh | 202 ++ + backend-immutable/tests/test-pull.sh | 39 + + backend-immutable/tests/test-push-update.sh | 201 ++ + backend-immutable/tests/test-upgrade-endoflife.sh | 54 + + 75 files changed, 14820 insertions(+), 776 deletions(-) + create mode 100644 backend-immutable/SystemUpdater/Core/DbusControl.py + create mode 100644 backend-immutable/SystemUpdater/Core/diskManager.py + create mode 100755 backend-immutable/SystemUpdater/Core/quitStrategiesService.py + create mode 100644 backend-immutable/SystemUpdater/Core/space_rules.py + create mode 100644 backend-immutable/SystemUpdater/configs.py + create mode 100644 backend-immutable/SystemUpdater/constants.py + create mode 100644 backend-immutable/SystemUpdater/packageManager/UpdaterSDKInterface.py + create mode 100644 backend-immutable/SystemUpdater/packageManager/__init__.py + create mode 100644 backend-immutable/SystemUpdater/packageManager/pmEnums.py + create mode 100644 backend-immutable/SystemUpdater/packageManager/pmUtils.py + create mode 100644 backend-immutable/SystemUpdater/packageManager/pmWorker.py + create mode 100644 backend-immutable/SystemUpdater/pluginbase.py + create mode 100644 backend-immutable/SystemUpdater/plugins/update/randomstr.py + create mode 100644 backend-immutable/SystemUpdater/push_update.py + create mode 100644 backend-immutable/data/com.kylin.UpgradeStrategies.limit + create mode 100644 backend-immutable/data/com.kylin.UpgradeStrategies.limit.verify + create mode 100644 backend-immutable/data/settings-default.json + create mode 100644 backend-immutable/tests/data/cn.kylinos.KylinSystemUpdater.policy + create mode 100644 backend-immutable/tests/data/cn.kylinos.UpgradeStrategies.policy + create mode 100644 backend-immutable/tests/data/com.kylin.UpgradeStrategies.conf + create mode 100644 backend-immutable/tests/data/com.kylin.UpgradeStrategies.limit + create mode 100644 backend-immutable/tests/data/com.kylin.UpgradeStrategies.limit.verify + create mode 100644 backend-immutable/tests/data/com.kylin.UpgradeStrategies.service + create mode 100644 backend-immutable/tests/data/com.kylin.systemupgrade.conf + create mode 100644 backend-immutable/tests/data/com.kylin.systemupgrade.limit + create mode 100644 backend-immutable/tests/data/inhibit-sleep.conf + create mode 100644 backend-immutable/tests/data/kylin-system-updater + create mode 100644 backend-immutable/tests/data/kylin-system-updater.db + create mode 100644 backend-immutable/tests/data/kylin-system-updater.service + create mode 100644 backend-immutable/tests/data/kylin-system-version.conf + create mode 100644 backend-immutable/tests/data/settings-default.json + create mode 100644 backend-immutable/tests/data/unattended-upgrades-policy.conf + create mode 100644 backend-immutable/tests/data/unattended-upgrades-timestamp + create mode 100644 backend-immutable/tests/diskManager_unittest.py + create mode 100644 backend-immutable/tests/interface.md + create mode 100755 backend-immutable/tests/kylin-system-updater + create mode 100755 backend-immutable/tests/kylin-upgrade-strategies + create mode 100644 backend-immutable/tests/po/ChangeLog + create mode 100644 backend-immutable/tests/po/Makefile + create mode 100644 backend-immutable/tests/po/POTFILES.in + create mode 100644 backend-immutable/tests/po/POTFILES.skip + create mode 100644 backend-immutable/tests/po/bo_CN.po + create mode 100644 backend-immutable/tests/po/zh_CN.po + create mode 100644 backend-immutable/tests/po/zh_HK.po + create mode 100644 backend-immutable/tests/po/zh_TW.po + create mode 100755 backend-immutable/tests/report-updater-bug + create mode 100644 backend-immutable/tests/setup.cfg + create mode 100755 backend-immutable/tests/setup.py + create mode 100644 backend-immutable/tests/sqlite_tests.py + create mode 100644 backend-immutable/tests/test-configs.py + create mode 100755 backend-immutable/tests/test-for-frontend.sh + create mode 100755 backend-immutable/tests/test-offline-upgrade.sh + create mode 100755 backend-immutable/tests/test-pull.sh + create mode 100755 backend-immutable/tests/test-push-update.sh + create mode 100755 backend-immutable/tests/test-upgrade-endoflife.sh + +diff --git a/backend-immutable/SystemUpdater/Core/DataAcquisition.py b/backend-immutable/SystemUpdater/Core/DataAcquisition.py +index ab6fe09..32fa611 100644 +--- a/backend-immutable/SystemUpdater/Core/DataAcquisition.py ++++ b/backend-immutable/SystemUpdater/Core/DataAcquisition.py +@@ -13,13 +13,13 @@ import shutil + import hashlib + import logging + import tarfile +-import requests ++import datetime + import threading + +-from datetime import datetime,timezone,timedelta ++from datetime import datetime + from binascii import a2b_hex + from urllib import parse, request +-from SystemUpdater.Core import enums ++from SystemUpdater.packageManager.pmEnums import * + from json.decoder import JSONDecodeError + from dbus.exceptions import DBusException + from SystemUpdater.Core.UpdaterConfigParser import UpgradeConfig +@@ -72,7 +72,15 @@ class UpdateMsgCollector(): + MODE_INSTALL_PARTIAL:"upgrade_partial" + } + action_map = { +- ++ ACTION_CHECK_RESOLVER:MONIT_DEPRESOLUT, ++ ACTION_INSTALL:MONIT_INSTALL, ++ ACTION_INSTALL_DEB:MONIT_INSTALLDEB, ++ 10:MONIT_FINISH, ++ "finish-updatedetect":MONIT_DETECT, ++ "finish-download":MONIT_DOWNLOAD, ++ "finish-downfinish":MONIT_DOWNLOAD, ++ "finish-update":MONIT_FINISH, ++ "finish-install":MONIT_FINISH + } + messageType_map = { + "finish-updatedetect":"UpdateDetect", +@@ -660,13 +668,13 @@ class PHPServer(threading.Thread): + gZipFile(log_dir, log_file_gzip) + header = {'Content-Type': "multipart/form-data", "Accept-Encoding": "gzip"} + try: ++ import requests ++ + 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, timeout = (3,3)) +- 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, timeout = (3,3)) ++ except Exception as e: ++ logging.error("The external log server error: %s .", e) + else: + log_file_gzip = "" + kmg_tmp = {'ip': host_ip, 'version': version, 'soft_version': soft_version, 'datetime': nowtime, +@@ -700,15 +708,25 @@ def gZipFile(src, dst): + tar.add(src, arcname=os.path.basename(src)) + + def get_east_8_time(): +- now_time = "" +- +- try: +- now_time = datetime.now(timezone.utc) + timedelta(hours=8) +- now_time = now_time.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] +- except: +- pass +- ++ 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 + + def GetActivationCode(): + Code = '' +diff --git a/backend-immutable/SystemUpdater/Core/Database.py b/backend-immutable/SystemUpdater/Core/Database.py +index 82a35d2..4b17082 100644 +--- a/backend-immutable/SystemUpdater/Core/Database.py ++++ b/backend-immutable/SystemUpdater/Core/Database.py +@@ -26,7 +26,7 @@ INSTALLED_LIST = [{"item": "errorcode", "type": "int", "default": "0"}] + DISPALY_LIST = [] + + class Sqlite3Server(object): +- def __init__(self, updateManager, _no_DataMigration=False): ++ def __init__(self, updateManager=None, _no_DataMigration=False): + self.connect = None + self.window_main = updateManager + self.init_sqlit() +@@ -60,6 +60,10 @@ class Sqlite3Server(object): + self.deb_metadata.update({"new_version":''}) + self.deb_metadata.update({"old_update_version":''}) + self.deb_metadata.update({"new_update_version":''}) ++ self.deb_policy_timestamp = 0 ++ self.deb_policy_keep = False ++ self.purge_policy_timestamp = 0 ++ self.purge_policy_keep = False + + # Initialize the connection database and modify it to connect when using + def init_sqlit(self): +@@ -146,10 +150,24 @@ class Sqlite3Server(object): + + # 写入数据到display表中 + def insert_into_display(self, *args, **kwargs): ++ display_filed_lists = ["check_time","update_time","auto_check","system_version","auto_backup","download_limit", ++ "download_limit_value","allow_unattended_upgrades_shutdown","update_period", ++ "firstmigration","autoupdate_allow","tid","init_version"] ++ if args[0] not in display_filed_lists: ++ return "Error" ++ ++ def is_valid_string(s): ++ pattern = r'^[a-zA-Z0-9=:_\-\s]+$' ++ return bool(re.fullmatch(pattern, s)) ++ ++ if not is_valid_string(args[1]): ++ logging.error("Invalid args: %s", args[1]) ++ return "Error" ++ + self.connect_database() + try: +- sql = "update display set " + args[0] + "='" + args[1] + "' where id = 1" +- self.cursor.execute(sql) ++ sql = f"update display set {args[0]} = ? where id = 1" ++ self.cursor.execute(sql, (str(args[1]), )) + self.connect.commit() + except Exception as e: + logging.error("Insert error: %s.", str(e)) +@@ -274,12 +292,7 @@ class Sqlite3Server(object): + try: + output_config_name = '/var/cache/kylin-system-updater/json/kylin-update-desktop-system.json' + +- if success: +- status = 'success' +- status_cn = '成功' +- else: +- status = 'failed' +- status_cn = '失败' ++ status, status_cn = ('success', '成功') if success else ('failed', '失败') + + try: + with open(output_config_name,'r') as f: +@@ -303,6 +316,9 @@ class Sqlite3Server(object): + + errstr = get_error_description_from_enum(error_code) + ++ if not success and not errstr: ++ errstr = _('System upgrade error') ++ + self.insert_into_updateinfo(pkgappname, pkgversion, pkgdescription, pkgtimestr, \ + pkgstatus, pkgkeyword, errstr, pkgappnamecn, pkgstatuscn, pkgchangelog) + +diff --git a/backend-immutable/SystemUpdater/Core/DbusControl.py b/backend-immutable/SystemUpdater/Core/DbusControl.py +new file mode 100644 +index 0000000..f7c7e3c +--- /dev/null ++++ b/backend-immutable/SystemUpdater/Core/DbusControl.py +@@ -0,0 +1,87 @@ ++#!/usr/bin/python3 ++ ++import os ++import configparser ++import logging ++import dbus ++from gettext import gettext as _ ++ ++from SystemUpdater.Core.errors import * ++from SystemUpdater.Core.enums import * ++ ++class DbusControl(): ++ def __init__(self,dbus,debug): ++ self.dbus = dbus ++ self.debug = debug ++ self.prohibit_env = ["LD_PRELOAD","LD_LIBRARY_PATH","LD_AUDIT"] ++ self.config = configparser.ConfigParser() ++ self.whitelist_values = [] ++ try: ++ self.config.read("/etc/dbus-1/conf/com.kylin.UpgradeStrategies.limit") ++ whitelist = self.config['whitelist'] ++ for key in whitelist: ++ self.whitelist_values.append(whitelist[key]) ++ except Exception as e: ++ logging.warning("Skip dbus control and dsc:%s",str(e)) ++ ++ def _get_environment_from_pid(self, pid): ++ try: ++ with open(f'/proc/{pid}/environ', 'r') as f: ++ environ = f.read() ++ # 环境变量用 '\0' 分隔,替换为换行符便于读取 ++ env_vars = environ.split('\0') ++ env_dict = {var.split('=')[0]: var.split('=')[1] for var in env_vars if '=' in var} ++ return env_dict ++ except Exception as e: ++ logging.error("Error reading environment variables: %s",str(e)) ++ return None ++ ++ def _get_process_path_from_pid(self, pid): ++ try: ++ # 读取 /proc/[pid]/exe 符号链接获取进程路径 ++ process_path = os.readlink(f'/proc/{pid}/exe') ++ if process_path.startswith('/usr/bin/python'): ++ cmd_file = '/proc/%u/cmdline' % pid ++ with open(cmd_file, 'r') as cf: ++ cmd_line = cf.readline() ++ cmds = cmd_line.split('\0') ++ if len(cmds) >= 2: ++ process_path = cmds[1] ++ return process_path ++ except Exception as e: ++ return str(e) ++ ++ def _get_pid_from_dbus_name(self,dbus_name, bus=None): ++ """Return a deferred that gets the id of process owning the given ++ system D-Bus name. ++ """ ++ bus_obj = self.dbus.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)) ++ ++ return int(pid) ++ ++ def check_sender(self,sender,skip_check=False): ++ pid = self._get_pid_from_dbus_name(sender) ++ ++ process_path = self._get_process_path_from_pid(pid) ++ ++ if self.debug or skip_check: ++ return process_path ++ ++ # 检查运行白名单 ++ if self.whitelist_values and process_path not in self.whitelist_values: ++ logging.error("dbus method control,operation not permitted. sender:%s",process_path) ++ raise dbus.exceptions.DBusException("dbus method control,operation not permitted.") ++ ++ # # 检查环境变量 ++ # env_vars = self._get_environment_from_pid(pid) ++ ++ # if env_vars: ++ # for env in self.prohibit_env: ++ # if env in env_vars: ++ # logging.error("dbus method control,env forbidden. sender:%s env:%s",process_path,env) ++ # raise dbus.exceptions.DBusException("dbus method control,env forbidden.") ++ return process_path +\ No newline at end of file +diff --git a/backend-immutable/SystemUpdater/Core/PluginManager.py b/backend-immutable/SystemUpdater/Core/PluginManager.py +index 19eda71..752c5dd 100644 +--- a/backend-immutable/SystemUpdater/Core/PluginManager.py ++++ b/backend-immutable/SystemUpdater/Core/PluginManager.py +@@ -1,402 +1,5 @@ + #!/usr/bin/python3 + # -*- coding: UTF-8 -*- +-# 脚本插件化执行管理 +-# TODO: +- # 使能/失能 --配置文件--ok +- # 错误码 --脚本/本程序--wait/add +- # 进度同步 --线程获取--add +- # 输出规范 --脚本/本程序(重定向日志文件、输出格式)--ok/ok +- # 元数据类型 --描述信息、翻译(配置文件)--ok +- # 运行等级 --(root/user)--wait + +-import subprocess +-import os +-from sre_compile import isstring +-import threading +-import yaml +-import logging +-from gi.repository import GObject + +-class pluginState(): +- PLUGIN_SUCCESS = 0 +- PLUGINERR_PLUGIN_NOT_EXIST = PLUGIN_SUCCESS - 1 +- PLUGINERR_PLUGIN_NOT_COMPLETED = PLUGINERR_PLUGIN_NOT_EXIST - 1 +- PLUGINERR_NO_AVAILABLE_YAML = PLUGINERR_PLUGIN_NOT_COMPLETED - 1 +- PLUGINERR_NOT_LOAD_ALL = PLUGINERR_NO_AVAILABLE_YAML - 1 +- PLUGINERR_PLUGIN_NOT_IN_LIST = PLUGINERR_NOT_LOAD_ALL - 1 +- PLUGINERR_PLUGIN_NOT_ENABLED = PLUGINERR_PLUGIN_NOT_IN_LIST - 1 +- PLUGINERR_PLUGIN_CONFIG_FAILED = PLUGINERR_PLUGIN_NOT_ENABLED - 1 +- PLUGINERR_LOG_PATH_NOT_EXIT = PLUGINERR_PLUGIN_CONFIG_FAILED - 1 +- PLUGINERR_CONFIG_NOT_COMPLETED = PLUGINERR_LOG_PATH_NOT_EXIT - 1 +- PLUGINERR_CMD_IS_NONE = PLUGINERR_CONFIG_NOT_COMPLETED - 1 +- PLUGINERR_LANGUAGE_NOT_SUPPORT = PLUGINERR_CMD_IS_NONE - 1 +- +- _numToInfo = { +- PLUGIN_SUCCESS: 'success', +- PLUGINERR_PLUGIN_NOT_EXIST: 'plugin path not exist', +- PLUGINERR_PLUGIN_NOT_COMPLETED: 'plugin folder not completed', +- PLUGINERR_NO_AVAILABLE_YAML: 'there is no available yaml', +- PLUGINERR_NOT_LOAD_ALL: 'not run load_all', +- PLUGINERR_PLUGIN_NOT_IN_LIST: 'plugin not in loaded plugin list', +- PLUGINERR_PLUGIN_NOT_ENABLED: 'plugin not enabled', +- PLUGINERR_PLUGIN_CONFIG_FAILED: 'plugin config failed', +- PLUGINERR_LOG_PATH_NOT_EXIT: 'log path not exists', +- PLUGINERR_CONFIG_NOT_COMPLETED: 'config not completed', +- PLUGINERR_CMD_IS_NONE: 'cmd is none', +- PLUGINERR_LANGUAGE_NOT_SUPPORT: 'not support this language', +- } +- _infoToNum = { +- 'success': PLUGIN_SUCCESS, +- 'plugin path not exist': PLUGINERR_PLUGIN_NOT_EXIST, +- 'plugin folder not completed': PLUGINERR_PLUGIN_NOT_COMPLETED, +- 'there is no available yaml': PLUGINERR_NO_AVAILABLE_YAML, +- 'not run load_all': PLUGINERR_NOT_LOAD_ALL, +- 'plugin not in loaded plugin list': PLUGINERR_PLUGIN_NOT_IN_LIST, +- 'plugin not enabled': PLUGINERR_PLUGIN_NOT_ENABLED, +- 'plugin config failed': PLUGINERR_PLUGIN_CONFIG_FAILED, +- 'log path not exists': PLUGINERR_LOG_PATH_NOT_EXIT, +- 'config not completed': PLUGINERR_CONFIG_NOT_COMPLETED, +- 'cmd is none': PLUGINERR_CMD_IS_NONE, +- 'not support this language': PLUGINERR_LANGUAGE_NOT_SUPPORT, +- } +- +- +-PLUGIN_MANAGER_PATH = "./" # 可修改 +- +-# 目录结构 FILE PATH +-CFG_FILE = "conf.yaml" +-CFG_EX_DIR = "conf.d/" +-CFG_PATH = PLUGIN_MANAGER_PATH + CFG_FILE +-CFG_EX_PATH = PLUGIN_MANAGER_PATH + CFG_EX_DIR +-MOD_DIR = "modules/" +-MOD_PATH = PLUGIN_MANAGER_PATH + MOD_DIR +-MOD_AVAILABLE = "modules-available/" +-MOD_ENABLED = "modules-enabled/" +-MOD_AVAILABLE_PATH = MOD_PATH + MOD_AVAILABLE +-MOD_ENABLED_PATH = MOD_PATH + MOD_ENABLED +-PLUGIN_DIR = "script/" +-PLUGIN_PATH = PLUGIN_MANAGER_PATH + PLUGIN_DIR +- +-# 配置 日志路径 +-LOG_DIR_ROOT = '/var/log/kylin-system-updater/' +-# 默认插件日志路径 +-PLUGIN_LOG_DIR = '/var/log/kylin-system-updater/plugin/' +- +-# PLUGIN.YAML +-PLUGIN_CONF_KEY_NAME = 'name' +-PLUGIN_CONF_KEY_DESC = 'desc' +-PLUGIN_CONF_KEY_EXEC = 'exec' +-PLUGIN_CONF_KEY_LOGLEVEL = 'loglevel' +-PLUGIN_CONF_KEY_RUNLEVEL = 'runlevel' +-PLUGIN_CONF_KEY_LIST = [PLUGIN_CONF_KEY_NAME,PLUGIN_CONF_KEY_DESC,PLUGIN_CONF_KEY_EXEC,PLUGIN_CONF_KEY_LOGLEVEL,PLUGIN_CONF_KEY_RUNLEVEL] +- +-# CONF.YAML AND CONF.D +-MANAGER_CONF_KEY_LOGDIR = "logdir" +-MANAGER_CONF_KEY_LIST = [MANAGER_CONF_KEY_LOGDIR, ] +- +- +-FORMAT = "%(asctime)s [%(levelname)s]: %(message)s" +-RUNLEVEL_LIST = ['ROOT', 'USER'] +-LOGLEVEL_LIST = ['DEBUG', 'INFO', 'NOTIFY', 'WARNING', 'ERROR', 'CRITICAL'] +- +-class LOADSTATE(): +- PLGNAME = 0x01 +- EXECCMD = 0x02 +- STATESUM = PLGNAME + EXECCMD +- +- +-LANG_KEY_ZH_CN = 'zh_CN' +-LANG_KEY_EN = 'en' +-class LANGLIST(): +- LANG_EN = 0 +- LANG_ZH_CN = 1 +- +- +-class pluginClass(pluginState): +- +- def __init__(self): +- # 必须配置项 +- self.pluginName = None +- self.execCmd = None +- # 可选配置项 +- self.descList = [] # en / zh +- self.logLevel = LOGLEVEL_LIST.index('DEBUG') +- self.runLevel = RUNLEVEL_LIST.index('ROOT') +- self.enabled = False +- # 合成变量 +- self.cmd = None +- self.logDir = PLUGIN_LOG_DIR # 插件日志路径 +- self.logPath = os.path.join(self.logDir, "default.log") # 插件日志文件 +- self.fifoName = "default-fifo" # 插件进度文件 +- # self.fifoPath = PLUGIN_PATH + self.fifoName +- self.fifoPath = "/var/log/kylin-system-updater"+self.fifoName +- # 记录变量 +- self.running = False # 是否在运行 +- self.process = 0 # 执行进度 +- self.loadState = 0 # 插件配置完成 +- logging.info("init finished.") +- +- ###################### 内部函数 ###################### +- # 1-配置指定字段 +- # 2-更新进度 (1/0.5s) +- # 3- +- ###################### +- def _config_key(self, cfg, key): +- if cfg == None or key == None or key not in cfg: +- logging.warning("[PLUGIN]: key[%s] not in yaml.", key) +- +- if key == PLUGIN_CONF_KEY_NAME: +- if isstring(cfg[key]): +- self.pluginName = cfg[key] +- self.fifoName = cfg[key] + "-fifo" +- self.loadState += LOADSTATE.PLGNAME +- else: +- logging.error("[PLUGIN]: name[%s] not string.", cfg[key]) +- +- elif key == PLUGIN_CONF_KEY_DESC: +- langList = cfg[key] +- descDict = {} +- if langList == None or len(langList) == 0: +- return +- for i in range(len(langList)): +- descDict = langList[i] +- if LANG_KEY_EN in descDict: +- self.descList.insert(LANGLIST.LANG_EN, descDict.pop(LANG_KEY_EN)) +- continue +- elif LANG_KEY_ZH_CN in descDict: +- self.descList.insert(LANGLIST.LANG_ZH_CN, descDict.pop(LANG_KEY_ZH_CN)) +- continue +- +- elif key == PLUGIN_CONF_KEY_EXEC: +- if isstring(cfg[key]): +- self.execCmd = cfg[key] +- self.loadState += LOADSTATE.EXECCMD +- else: +- logging.error("[PLUGIN]: execCmd[%s] not string.", cfg[key]) +- +- elif key == PLUGIN_CONF_KEY_LOGLEVEL: +- loglevel = cfg[key].upper() +- if loglevel in LOGLEVEL_LIST: +- self.logLevel = LOGLEVEL_LIST.index(loglevel) +- +- elif key == PLUGIN_CONF_KEY_RUNLEVEL: +- runlevel = cfg[key].upper() +- if runlevel in RUNLEVEL_LIST: +- self.runLevel = RUNLEVEL_LIST.index(runlevel) +- else: +- logging.warning("[PLUGIN]: key[%s] not need config.", key) +- +- def _update_process(self): +- if not self.running: +- logging.info("[PLUGIN]: plugin [%s] is not running.", self.pluginName) +- return +- +- if os.path.exists(self.fifoPath): +- try: +- fd = open(self.fifoPath, 'r', 1) +- process = fd.readline() +- self.process = int(process.strip("\n")) +- except Exception as e: +- logging.info("[PLUGIN]: get process err[%s].",e) +- else: +- logging.info("[PLUGIN]: fifo[%s] not exists.", self.fifoPath) +- +- if self.process >= 100 or self.process < 0: +- return +- +- tmptimer = threading.Timer(0.5, function=self._update_process) +- tmptimer.start() +- +- ###################### 外部函数 ###################### +- # 1-读取配置文件,并配置该插件 +- # 2-使能插件 +- # 3-失能插件 +- # 4-获取插件名称 +- # 5-获取进度 +- # 6-注册进度跟新回调 +- # 7-执行插件 +- # 8-获取描述信息 +- # 9-设置脚本日志路径 +- +- # TODO: +- # 重配置该插件 +- ###################### +- +- # 配置该插件 +- def plg_config(self, filePath): +- if not os.path.exists(filePath): +- logging.error("[PLUGIN]: [%s] not exist.", filePath) +- return self.PLUGINERR_PLUGIN_CONFIG_FAILED +- +- def plg_enable(self): +- self.enabled = True +- def plg_disable(self): +- self.enabled = False +- +- def plg_get_name(self): +- return self.pluginName +- +- def plg_get_process(self): +- return self.process +- +- def plg_get_desc(self): +- # 获得语言变量 +- #TODO: 例如:中文繁体,如果不存在的话,显示中文简体 +- lang=os.getenv("LANG") +- if LANG_KEY_EN in lang: +- if len(self.descList) > LANGLIST.LANG_EN: +- return self.descList[LANGLIST.LANG_EN] +- else: +- logging.error("[PLUGIN]: There is not a desc of the language[%s].", lang) +- elif LANG_KEY_ZH_CN in lang: +- if len(self.descList) > LANGLIST.LANG_ZH_CN: +- return self.descList[LANGLIST.LANG_ZH_CN] +- else: +- logging.error("[PLUGIN]: There is not a desc of the language[%s].", lang) +- else: +- logging.error("[PLUGIN]: There is not a desc of the language[%s].", lang) +- return +- +- # 添加 update cmd +- # 设置脚本日志路径 +- def plg_set_logDir(self, logPath): +- if not os.path.exists(logPath): +- try: +- os.makedirs(logPath, mode=0o755) +- except Exception as e: +- logging.error("[PLUGIN]: create plugin log dir failed.[%s]", e) +- return self.PLUGINERR_LOG_PATH_NOT_EXIT +- self.logDir = logPath +- if self.pluginName != None: +- self.logPath = os.path.join(self.logDir, self.pluginName + ".log") +- self.cmd = "bash " + self.execCmd + " fifoname=" + self.fifoName + " logpath=" + self.logPath + " loglevel=" + str(self.logLevel) + " modename=" + self.pluginName +- +- def plg_run(self): +- if not self.enabled: +- logging.error("[PLUGIN]: [%s] not enabled.", self.pluginName) +- return self.PLUGINERR_PLUGIN_NOT_ENABLED +- +- self.running = True +- tmptimer = threading.Timer(0.5, function=self._update_process) +- tmptimer.start() +- +- if self.cmd == None: +- logging.error("[PLUGIN]: cmd is None.") +- return self.PLUGINERR_CMD_IS_NONE, self._numToInfo(self.PLUGINERR_CMD_IS_NONE), self._numToInfo(self.PLUGINERR_CMD_IS_NONE) +- +- logging.debug("[PLUGIN]: cmd[%s].",self.cmd) +- try: +- ret = subprocess.run(self.cmd, shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE) +- except Exception as e: +- logging.error("[PLUGIN]: subprocess run err[%s].", e) +- +- self.running = False +- logging.debug("[PLUGIN]: [%s] run finished ret[%d].",self.pluginName, ret.returncode) +- return ret.returncode, ret.stdout.decode(), ret.stderr.decode() +- +- def plg_reconfig(self): +- pass +- +- +- +-class pluginManagerClass(pluginState): +- def __init__(self): +- # 变量初始化 +- self.plgClassList = [] # 插件句柄 +- self.loaded = False +- +- self.managerLogDir = LOG_DIR_ROOT # 管理器日志路径 +- # 日志配置初始化,试用updater的logger +- # if not os.path.exists(self.managerLogDir): +- # os.mkdir(self.managerLogDir, mode=0o755) +- # logfile = os.path.join(self.managerLogDir, 'PluginManager.log.' + str(self.classNum)) +- # logging.basicConfig(format=FORMAT, level='DEBUG', datefmt='%m-%d,%H:%M:%S', filename=logfile, filemode='a') +- # self.pluginLogDir = PLUGIN_LOG_DIR # 插件日志路径 +- +- # 将单个插件句柄添加到全局记录, 并使能 +- def _add_single_plugin(self, filePath, enable): +- if not os.path.exists(filePath): +- logging.debug("[PLUGIN]: [%s] not exist.", filePath) +- return +- +- singlePlgClass = pluginClass() +- singlePlgClass.plg_config(filePath) +- self.plgClassList.append(singlePlgClass) +- if enable: +- singlePlgClass.plg_enable() +- singlePlgClass.plg_set_logDir(self.pluginLogDir) +- +- def _remove_single_plugin(self, pluginClass): +- if pluginClass in self.plgClassList: +- logging.debug("[PLUGIN]: remove [%s].", pluginClass.plg_get_name()) +- pluginClass.remove(pluginClass) +- pluginClass.plg_disable() +- +- +- # 加载所有插件,读取所有配置 +- # 第一个执行 +- # 返回插件句柄列表 +- # TODO:加载指定插件, 读取指定配置 +- def reload_plugin(self, pluginName): +- pass +- +- # 通过句柄获取插件名称 +- def get_plugin_name(self, pluginClass): +- if not self.loaded: +- logging.error("[PLUGIN]: please run load_all first.") +- return self.PLUGINERR_NOT_LOAD_ALL +- if pluginClass not in self.plgClassList: +- logging.error("[PLUGIN]: there is no this plugin in pluginList.") +- return self.PLUGINERR_PLUGIN_NOT_IN_LIST +- +- return pluginClass.plg_get_name() +- +- # 运行指定插件 +- # pluginName, pluginClass 都指定时,以名称为准 +- def run_plugin(self, pluginName = None): +- self.running = True +- +- if pluginName == None or not os.path.isfile(pluginName): +- logging.error("[PLUGIN]: [%s] Cann't found.",pluginName) +- return True +- cmd = "bash " + pluginName +- try: +- ret = subprocess.run(cmd, shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE) +- logging.info("[PLUGIN]: script[%s].",pluginName) +- except Exception as e: +- logging.error("[PLUGIN]: subprocess run err[%s].", e) +- return True +- +- self.running = False +- +- if ret.returncode != 0: +- logging.error("[PLUGIN]: code:%d, out:%s, err:%s",ret.returncode, ret.stdout.decode(), ret.stderr.decode()) +- logging.debug("[PLUGIN]: run finished returncode[%d], out[%s], err[%s]",ret.returncode, ret.stdout.decode(), ret.stderr.decode()) +- return (ret.returncode==0) +- +- def connect_signal(self, plgclass, signal, handler): +- if plgclass not in self.plgClassList: +- logging.error("[PLUGIN]: there is no this plugin in pluginList.") +- return self.PLUGINERR_PLUGIN_NOT_IN_LIST +- return plgclass.connect(signal, handler) +- +- +-def plugin_process_handler(obj, process): +- logging.info("[PLUGIN]: ******* process [%d].", process) +- +-# if __name__ == "__main__": +- +-# pMClass = pluginManagerClass() +-# plgList = pMClass.load_all("./") +- +-# for everyPlg in iter(plgList): +-# name = pMClass.get_plugin_name(everyPlg) +-# print("name:", name) +-# desc = pMClass.get_desc(everyPlg) +-# print("desc:", desc) +-# pMClass.connect_signal(everyPlg, "processChanged", plugin_process_handler) +- +-# ret = pMClass.run_plugin(name) +- +-# exit(0) + +diff --git a/backend-immutable/SystemUpdater/Core/UpdateList.py b/backend-immutable/SystemUpdater/Core/UpdateList.py +index b0213fd..e8f7e85 100644 +--- a/backend-immutable/SystemUpdater/Core/UpdateList.py ++++ b/backend-immutable/SystemUpdater/Core/UpdateList.py +@@ -1,17 +1,17 @@ + # UpdateList.py + +-from gettext import gettext as _ ++ + import logging + import os + import json + import yaml +-from gi.repository import Gio + from .errors import * + from .enums import * ++from gi.repository import Gio ++from gettext import gettext as _ ++from SystemUpdater.constants import OUTPUT_JSON_PATH + + class UpdateList(): +- OUTPUT_JSON_PATH = '/var/lib/kylin-system-updater/json/' +- + def __init__(self,parent): + self.parent = parent + self.uuconfigs = self.parent.uuconfigs +diff --git a/backend-immutable/SystemUpdater/Core/diskManager.py b/backend-immutable/SystemUpdater/Core/diskManager.py +new file mode 100644 +index 0000000..5e71509 +--- /dev/null ++++ b/backend-immutable/SystemUpdater/Core/diskManager.py +@@ -0,0 +1,123 @@ ++import os ++import logging ++import apt_pkg ++from gettext import gettext as _ ++from SystemUpdater.Core.errors import * ++from SystemUpdater.Core.enums import * ++ ++class diskManager(): ++ def __init__(self) -> None: ++ self.mounted_list = None ++ self.mounted_point_free_space = None ++ self.init_finish = False ++ self._start() ++ ++ def _start(self): ++ try: ++ self.mounted_list = self._get_mount_points() ++ self.mounted_point_free_space = self._get_mounted_point_free_space() ++ self.init_finish = True ++ except Exception as e: ++ logging.error(e) ++ ++ def _get_mount_points(self): ++ # 获得系统的挂载点列表 ++ # get the mount points list from /proc/mounts ++ mounted = [] ++ with open("/proc/mounts") as mounts: ++ for line in mounts: ++ try: ++ (what, where, fs, options, a, b) = line.split() ++ except ValueError as e: ++ logging.debug("line '%s' in /proc/mounts not understood (%s)" % (line, e)) ++ continue ++ if not where in mounted: ++ mounted.append(where) ++ # make sure mounted is sorted by longest path ++ mounted.sort(key=len, reverse=True) ++ return mounted ++ ++ def _get_mounted_point_free_space(self): ++ # 获取系统每个挂载点的剩余空间 ++ fs_free = {} ++ for mount_point in self.mounted_list: ++ statvfs = os.statvfs(mount_point) ++ free = statvfs.f_frsize * statvfs.f_bavail # 剩余空间,单位 GB ++ fs_free[mount_point] = free ++ return fs_free ++ ++ def _get_mounted_point_by_dir(self, archivedir): ++ """ return 'id' of a directory so that directories on the ++ same filesystem get the same id (simply the mount_point) ++ """ ++ for mount_point in self.mounted_list: ++ if archivedir.startswith(mount_point): ++ return mount_point ++ return "/" ++ ++ # External interface ++ def check_space_size_by_size(self, download_size, additional_size): ++ ''' ++ download_size: 下载包的总大小 ++ additional_size: 除下载空间外还需要额外的空间大小 ++ 通过输入的需求安装列表,计算挂载点大小 ++ 安装下载的地址通过 archivedir = apt_pkg.config.find_dir("Dir::Cache::archives") 自动获取 ++ ''' ++ if self.init_finish is True: ++ result = True ++ install_req_size = {} ++ notfree_mounted_pointed = [] ++ try: ++ # 下载路径 ++ archivedir = apt_pkg.config.find_dir("Dir::Cache::archives") ++ # V11上使用ostree,安装路径为/ostree/repo ++ # 除安装空间外还预留一些安全空间缓冲区,设置为固定值 ++ for (dir, size) in [(archivedir, download_size), ++ # 需要检测/ostree下是否足够安装,安装空间需求=下载大小+额外需要空间 ++ ("/ostree", download_size + additional_size), ++ ("/", 500*1024*1024), # small safety buffer / ++ ]: ++ mounted_point = self._get_mounted_point_by_dir(dir) ++ if size < 0: ++ # 兼容出现负数的BUG ++ continue ++ if mounted_point in install_req_size: ++ install_req_size[mounted_point] += size ++ else: ++ ++ install_req_size[mounted_point] = size ++ logging.debug(f"dir {dir} needs {size} disk size") ++ ++ for mounted_point in install_req_size: ++ if self.mounted_point_free_space[mounted_point] > install_req_size[mounted_point]: ++ continue ++ else: ++ result = False ++ notfree_mounted_pointed.append(mounted_point) ++ ++ if result is False: ++ logging.error(f"Not enough free space:{notfree_mounted_pointed}") ++ else: ++ logging.info("Check: Enough free space For system update...") ++ except Exception as e: ++ logging.error(e) ++ result = False ++ ++ else: ++ result = False ++ logging.error("diskManager Initialization is not complete") ++ return result ++ ++ return result ++ ++def check_free_space(download_size, addition_size=0): ++ try: ++ _diskManager = diskManager() ++ result =_diskManager.check_space_size_by_size(download_size, addition_size) ++ if result is False: ++ raise UpdateBaseError(ERROR_NOT_DISK_SPACE) ++ except Exception as e: ++ logging.error(e) ++ raise UpdateBaseError(ERROR_NOT_DISK_SPACE) ++ return False ++ return True +\ No newline at end of file +diff --git a/backend-immutable/SystemUpdater/Core/enums.py b/backend-immutable/SystemUpdater/Core/enums.py +index 2b7aeaa..602943f 100644 +--- a/backend-immutable/SystemUpdater/Core/enums.py ++++ b/backend-immutable/SystemUpdater/Core/enums.py +@@ -2,19 +2,32 @@ + # -*- coding: utf-8 -*- + """enums - Enumerates for apt daemon dbus messages""" + +-__all__ = ( +- #错误枚举 +- "ERROR_OTHER_PROGRESS_OCCUPATION","ERROR_NETWORK_FAILED","ERROR_CANCELLED","ERROR_NOT_ROLLBAK_DEPLOYMENT", +- "ERROR_ORIGIN_IS_NONE","INSTALL_FAILED_FALG","INSTALL_SUCCESS_FLAG","INSTALL_DEFAULT_FLAG", +- # 方法 +- "get_error_description_from_enum" +- ) ++# __all__ = ( ++# #错误枚举 ++# "ERROR_OTHER_PROGRESS_OCCUPATION","ERROR_NETWORK_FAILED","EXIT_CANCELLED","ERROR_NOT_ROLLBAK_DEPLOYMENT", ++# "ERROR_ORIGIN_IS_NONE","INSTALL_FAILED_FALG","INSTALL_SUCCESS_FLAG","INSTALL_DEFAULT_FLAG","ERROR_UPDATE_SOURCE_FAILED", ++# "ERROR_PROGRAM_EXCEPTION","ERROR_NOT_DISK_SPACE","ERROR_PUSH_BRANCH_EXCEPTION","ERROR_REPO_IS_NONE", ++ ++# # 方法 ++# "get_error_description_from_enum","get_error_num_from_enum" ++# ) + + import gettext + gettext.bindtextdomain('kylin-system-updater', '/usr/share/locale') + gettext.textdomain('kylin-system-updater') + _ = gettext.gettext + ++ ++# 更新事务进行过程中的角色 ++ROLE_UNSET = "role-unset" ++# 检查更新 ++ROLE_UPDATE_CACHE = "role-update-cache" ++#更新下载 ++ROLE_DOWNLOAD_ONLY = "role-download-only" ++# 更新部署 ++ROLE_DEPLOY_ONLY = "role-deploy-only" ++ ++ + #系统更新的所有枚举 + PRIORITY_UPGRADE_SUCCCESSED = "priority-upgrade-successed" + INSTALL_DEFAULT_FLAG = 'none' +@@ -22,24 +35,25 @@ INSTALL_FAILED_FALG = 'failed' + INSTALL_SUCCESS_FLAG = 'success' + #apt update阶段出现的错误解析 + +- +- +- +- + #通用错误 +-ERROR_PROGRAM_EXCEPTION = "#0000" +-ERROR_OTHER_PROGRESS_OCCUPATION = "#0001" +-ERROR_NETWORK_FAILED = "#0002" ++ERROR_PROGRAM_EXCEPTION = "error-program-exception" ++ERROR_OTHER_PROGRESS_OCCUPATION = "error-other-progress-occupation" ++ERROR_NETWORK_FAILED = "error-network-failed" + +-#检查更新错误 1 +-ERROR_ORIGIN_IS_NONE = "#0100" ++#检查更新错误 ++ERROR_ORIGIN_IS_NONE = "error-origin-is-none" ++ERROR_REPO_IS_NONE = "error-repo-is-none" ++ERROR_UPDATE_SOURCE_FAILED = "error-update-source-failed" ++ERROR_PUSH_BRANCH_EXCEPTION = "error-push-branch-exception" + + +-#下载错误 2 +-ERROR_CANCELLED = "#0200" ++#下载错误 ++EXIT_CANCELLED = "exit-cancelled" ++ERROR_NOT_DISK_SPACE = "error-not-disk-space" + +-#部署错误 3 +-ERROR_NOT_ROLLBAK_DEPLOYMENT = "#0300" ++#部署错误 ++ERROR_NOT_DEPLOYMENT = "error-not-deployment" ++ERROR_NOT_ROLLBAK_DEPLOYMENT = "error-not-rollback-deployment" + + + GLIB_ERROR_NETWORK_FAILED = "While fetching mirrorlist" +@@ -47,12 +61,50 @@ GLIB_ERROR_NETWORK_FAILED = "While fetching mirrorlist" + _DESCS_ERROR = { + ERROR_OTHER_PROGRESS_OCCUPATION: _("Other tasks are being updated and upgraded, please try again later."), + ERROR_NETWORK_FAILED: _("Network anomaly, can't check for updates!"), +- ERROR_CANCELLED: _("_Cancel Upgrade"), ++ ERROR_REPO_IS_NONE: _("Name of the repository deployed by the current system is empty."), ++ ++ ERROR_UPDATE_SOURCE_FAILED: _("Unable to access the source management server, please try again later"), ++ EXIT_CANCELLED: _("_Cancel Upgrade"), + ERROR_NOT_ROLLBAK_DEPLOYMENT: _("There is no way to rollback to the previous version, there is nothing to rollback."), +- ERROR_ORIGIN_IS_NONE: _("Check for update exceptions! Please verify that the system deployment is complete.") ++ ERROR_ORIGIN_IS_NONE: _("Check for update exceptions! Please verify that the system deployment is complete."), ++ ERROR_NOT_DISK_SPACE: _("Disk space is insufficient, please clean the disk and then upgrade"), ++ ERROR_PUSH_BRANCH_EXCEPTION: _("Update push is abnormal, and the corresponding branch information cannot be found on the server."), + ++ # 更新部署错误 ++ ERROR_NOT_DEPLOYMENT: _("The system has not been deployed, please check the system deployment status.") + } ++_CODE_ERROR = { ++ #通用错误码 ++ ERROR_PROGRAM_EXCEPTION: "#0000", ++ ERROR_NETWORK_FAILED: "#0001", ++ ERROR_OTHER_PROGRESS_OCCUPATION: "#0002", ++ ERROR_PUSH_BRANCH_EXCEPTION: "#0003", ++ ERROR_REPO_IS_NONE: "#0004", ++ ++ #检查更新错误 ++ ERROR_UPDATE_SOURCE_FAILED: "#0100", ++ ERROR_ORIGIN_IS_NONE: "#0101", ++ ++ #下载错误 2 ++ EXIT_CANCELLED: "#0200", ++ ERROR_NOT_DISK_SPACE: "#0201", ++ ++ #部署错误 3 ++ ERROR_NOT_DEPLOYMENT: "#0300", ++ ERROR_NOT_ROLLBAK_DEPLOYMENT: "#0301" ++} ++ ++def get_error_num_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 _CODE_ERROR[enum] ++ except KeyError: ++ return enum ++ + def get_error_description_from_enum(enum): + """Get a long description of an error. + +diff --git a/backend-immutable/SystemUpdater/Core/quitStrategiesService.py b/backend-immutable/SystemUpdater/Core/quitStrategiesService.py +new file mode 100755 +index 0000000..ee5f629 +--- /dev/null ++++ b/backend-immutable/SystemUpdater/Core/quitStrategiesService.py +@@ -0,0 +1,60 @@ ++#!/usr/bin/python3 ++ ++import dbus ++import psutil ++import signal ++import logging ++ ++STRATEGIES_DBUS_PATH = '/com/kylin/UpgradeStrategies' ++STRATEGIES_DBUS_SERVICE = 'com.kylin.UpgradeStrategies' ++STRATEGIES_DBUS_INTERFACE = 'com.kylin.UpgradeStrategies.interface' ++ ++process_names = [ ++ "kylin-upgrade-strategies", ++ "kconf2 monitor kylin-update-frontend."] ++ ++def _quit_strategies_service(): ++ try: ++ bus = dbus.SystemBus() ++ the_other_guy = bus.get_object(STRATEGIES_DBUS_SERVICE, ++ STRATEGIES_DBUS_PATH) ++ the_other_guy.Quit(dbus_interface=STRATEGIES_DBUS_INTERFACE, ++ timeout=2) ++ except Exception as e: ++ pass ++ ++def kill_process_tree(pid, sig=signal.SIGTERM): ++ try: ++ parent = psutil.Process(pid) ++ except psutil.NoSuchProcess: ++ return ++ ++ children = parent.children(recursive=True) ++ for child in children: ++ try: ++ child.send_signal(sig) ++ except psutil.NoSuchProcess: ++ pass ++ ++ try: ++ parent.send_signal(sig) ++ except psutil.NoSuchProcess: ++ pass ++ ++def _quit_force_strategies_service(): ++ try: ++ for proc in psutil.process_iter(['pid', 'name', 'cmdline']): ++ if isinstance(proc.info['cmdline'], list): ++ cmdline_str = ' '.join(proc.info['cmdline']) ++ ++ if any(name in cmdline_str for name in process_names): ++ print(f"正在终止进程 {cmdline_str} - {proc}") ++ kill_process_tree(proc.info['pid']) ++ except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): ++ pass ++ except Exception as e: ++ logging.error("_quit_force_strategies_service err:%s", e) ++ ++if __name__ == "__main__": ++ _quit_strategies_service() ++ _quit_force_strategies_service() +\ No newline at end of file +diff --git a/backend-immutable/SystemUpdater/Core/space_rules.py b/backend-immutable/SystemUpdater/Core/space_rules.py +new file mode 100644 +index 0000000..b45fffa +--- /dev/null ++++ b/backend-immutable/SystemUpdater/Core/space_rules.py +@@ -0,0 +1,175 @@ ++import os ++import re ++import glob ++import locale ++import logging ++import apt_pkg ++from gettext import gettext as _ ++from SystemUpdater.Core.errors import * ++from SystemUpdater.Core.enums import * ++# from SystemUpdater.Core.DistUpgradeCache import FreeSpaceRequired ++ ++def estimate_kernel_initrd_size_in_boot(): ++ """estimate the amount of space used by the kernel and initramfs in /boot, ++ including a safety margin ++ """ ++ kernel = 0 ++ initrd = 0 ++ kver = os.uname()[2] ++ for f in glob.glob("/boot/*%s*" % kver): ++ if f == '/boot/initrd.img-%s' % kver: ++ initrd += os.path.getsize(f) ++ # don't include in the estimate any files that are left behind by ++ # an interrupted package manager run ++ elif (f.find('initrd.img') >= 0 or f.find('.bak') >= 0 ++ or f.find('.dpkg-') >= 0): ++ continue ++ else: ++ kernel += os.path.getsize(f) ++ if kernel == 0: ++ # logging.warning( ++ # "estimate_kernel_initrd_size_in_boot() returned '0' for kernel?") ++ kernel = 16*1024*1024 ++ if initrd == 0: ++ # logging.warning( ++ # "estimate_kernel_initrd_size_in_boot() returned '0' for initrd?") ++ initrd = 100*1024*1024 ++ # add small safety buffer ++ kernel += 1*1024*1024 ++ # safety buffer as a percentage of the existing initrd's size ++ initrd_buffer = 1*1024*1024 ++ if initrd * 0.05 > initrd_buffer: ++ initrd_buffer = initrd * 0.05 ++ initrd += initrd_buffer ++ return kernel,initrd ++KERNEL_SIZE, INITRD_SIZE = estimate_kernel_initrd_size_in_boot() ++ ++def kernel_spaces_requires(pkgs_list): ++ # now calculate the space that is required on /boot ++ # we do this by checking how many linux-image-$ver packages ++ # are installed or going to be installed ++ kernel_count = 0 ++ for pkg_name in pkgs_list: ++ # we match against everything that looks like a kernel ++ # and add space check to filter out metapackages ++ if re.match("^linux-(image|image-debug)-[0-9.]*-.*", pkg_name): ++ # upgrade because early in the release cycle the major version ++ # may be the same or they might be -lts- kernels ++ logging.info("%s (new-install) added with %s to boot space" % (pkg_name, KERNEL_SIZE)) ++ kernel_count += 1 ++ # space calculated per LP: #1646222 ++ kernel_space = (kernel_count * KERNEL_SIZE ++ + (kernel_count + 1) * INITRD_SIZE) ++ return kernel_space ++ ++def check_free_space(required_download=0,additional_required_space=0,kernel_space=0): ++ """ ++ this checks if we have enough free space on /var, /boot and /usr ++ with the given cache ++ ++ Note: this can not be fully accurate if there are multiple ++ mountpoints for /usr, /var, /boot ++ """ ++ ++ class FreeSpace(object): ++ " helper class that represents the free space on each mounted fs " ++ def __init__(self, initialFree): ++ self.free = initialFree ++ self.need = 0 ++ ++ def make_fs_id(d): ++ """ return 'id' of a directory so that directories on the ++ same filesystem get the same id (simply the mount_point) ++ """ ++ for mount_point in mounted: ++ if d.startswith(mount_point): ++ return mount_point ++ return "/" ++ ++ # this is all a bit complicated ++ # 1) check what is mounted (in mounted) ++ # 2) create FreeSpace objects for the dirs we are interested in ++ # (mnt_map) ++ # 3) use the mnt_map to check if we have enough free space and ++ # if not tell the user how much is missing ++ mounted = [] ++ mnt_map = {} ++ fs_free = {} ++ try: ++ with open("/proc/mounts") as mounts: ++ for line in mounts: ++ try: ++ (what, where, fs, options, a, b) = line.split() ++ except ValueError as e: ++ logging.debug("line '%s' in /proc/mounts not understood (%s)" % (line, e)) ++ continue ++ if not where in mounted: ++ mounted.append(where) ++ # make sure mounted is sorted by longest path ++ mounted.sort(key=len, reverse=True) ++ ++ archivedir = apt_pkg.config.find_dir("Dir::Cache::archives") ++ for d in ["/", "/usr", "/var", "/boot", archivedir, "/home", "/tmp/"]: ++ d = os.path.realpath(d) ++ fs_id = make_fs_id(d) ++ if os.path.exists(d): ++ st = os.statvfs(d) ++ free = st.f_bavail * st.f_frsize ++ else: ++ logging.warning("directory '%s' does not exists" % d) ++ free = 0 ++ if fs_id in mnt_map: ++ logging.debug("Dir %s mounted on %s" % ++ (d, mnt_map[fs_id])) ++ fs_free[d] = fs_free[mnt_map[fs_id]] ++ else: ++ logging.debug("Free space on %s: %s" % ++ (d, free)) ++ mnt_map[fs_id] = d ++ fs_free[d] = FreeSpace(free) ++ del mnt_map ++ ++ # sum up space requirements ++ for (dir, size) in [(archivedir, required_download), ++ ("/usr", additional_required_space), ++ # plus 50M safety buffer in /usr ++ ("/usr", 50*1024*1024), ++ ("/boot", 160*1024*1024), ++ ("/tmp", 5*1024*1024), # /tmp for dkms LP: #427035 ++ ("/", 500*1024*1024), # small safety buffer / ++ ]: ++ # we are ensuring we have more than enough free space not less ++ if size < 0: ++ continue ++ dir = os.path.realpath(dir) ++ logging.debug("dir '%s' needs '%s' of '%s' (%f)" % (dir, size, fs_free[dir], fs_free[dir].free)) ++ fs_free[dir].free -= size ++ fs_free[dir].need += size ++ ++ # check for space required violations ++ required_list = {} ++ for dir in fs_free: ++ if fs_free[dir].free < 0: ++ # ensure unicode here (LP: #1172740) ++ free_at_least = apt_pkg.size_to_str(float(abs(fs_free[dir].free)+1)) ++ if isinstance(free_at_least, bytes): ++ free_at_least = free_at_least.decode( ++ locale.getpreferredencoding()) ++ free_needed = apt_pkg.size_to_str(fs_free[dir].need) ++ if isinstance(free_needed, bytes): ++ free_needed = free_needed.decode( ++ locale.getpreferredencoding()) ++ # make_fs_id ensures we only get stuff on the same ++ # mountpoint, so we report the requirements only once ++ # per mountpoint ++ required_list[make_fs_id(dir)] = FreeSpaceRequired(free_needed, make_fs_id(dir), free_at_least) ++ except Exception as e: ++ logging.error("free space check failed %s",str(e)) ++ ++ if len(required_list) > 0: ++ logging.error("Not enough free space: %s" % [str(i) for i in required_list]) ++ raise UpdateBaseError(ERROR_NOT_DISK_SPACE) ++ else: ++ logging.info("Check: Enough free space For system update...") ++ ++ return True +\ No newline at end of file +diff --git a/backend-immutable/SystemUpdater/UpdateManager.py b/backend-immutable/SystemUpdater/UpdateManager.py +index eb55736..31affd5 100644 +--- a/backend-immutable/SystemUpdater/UpdateManager.py ++++ b/backend-immutable/SystemUpdater/UpdateManager.py +@@ -4,7 +4,9 @@ import os + import json + import subprocess + import dbus,time,sys ++import traceback + import logging ++import uuid + from .Core.errors import * + from .Core.enums import * + from .Core.loop import mainloop +@@ -12,25 +14,23 @@ from gettext import gettext as _ + from dbus.mainloop.glib import DBusGMainLoop + DBusGMainLoop(set_as_default=True) + from .Core.Database import Sqlite3Server ++from .Core.MyCache import MyCache ++from SystemUpdater.constants import OUTPUT_JSON_PATH,SYSTEM_UPDATE_GROUPS ++from .UpdatesAvailable import Transaction ++from .push_update import update_important ++from SystemUpdater.configs import settings + from .backend.BackendOstreeNext import UpdateBackend,DownloadBackend,DeployBackend,RollbackBackend,LoadCacheBackend + from .UpdateManagerDbus import UpdateManagerDbusController,UPDATER_DBUS_INTERFACE,UPDATER_DBUS_PATH,UPDATER_DBUS_SERVICE +-from SystemUpdater.Core.UpdaterConfigParser import UpgradeConfig +-from .Core.MyCache import MyCache +-from .UpdatesAvailable import UpdatesAvailable + from SystemUpdater.Core.DataAcquisition import get_east_8_time + +-OUTPUT_JSON_PATH = '/var/cache/kylin-system-updater/json/' +-SYSTEM_UPDATE_GROUPS = "kylin-update-desktop-system" +- + class UpdateManager(): + def __init__(self,options): + try: + self.options = options + self._check_conf_dir() + self.cache = MyCache() +- self.trans = UpdatesAvailable() ++ self.trans = Transaction() + self.sqlite3_server = Sqlite3Server(self) +- self.configs_uncover = UpgradeConfig(defaults_conf="/etc/kylin-system-updater/system-updater-defaults.conf") + self.bus = dbus.SystemBus() + self.dbus_send = self._setup_dbus(self.bus) + +@@ -47,12 +47,42 @@ class UpdateManager(): + except KeyboardInterrupt: + self.dbus_send.Quit(None) + ++ def create_trans(self, role, sender): ++ tid = uuid.uuid4().hex ++ trans = Transaction(tid, role, sender) ++ self.trans = trans ++ return trans ++ + def start_cancel(self): + if self.trans: + self.trans.cancellable.cancel() + + #进行更新的操作 +- def start_update(self): ++ def start_update(self,trans): ++ try: ++ # if settings.get("update_source",True): ++ # def _update_important_success(push_list): ++ # trans.push_list = push_list ++ # self.start_update_cache(trans) ++ ++ # update_important(self.bus,reply_handler=_update_important_success,error_handler=self._update_error_handler) ++ # return ++ self.start_update_cache(trans) ++ except UpdateBaseError as excep: ++ self._update_error_handler(excep.code) ++ except UpdateProgressExit: ++ pass ++ except Exception as e: ++ logging.error(e) ++ tbk = traceback.format_exc() ++ logging.error(str(tbk)) ++ self._update_error_handler(ERROR_PROGRAM_EXCEPTION) ++ ++ def _update_error_handler(self,error_code): ++ self.dbus_send.UpdateDetectFinishedNext(False,[''],error_code) ++ ++ #进行更新的操作 ++ def start_update_cache(self,trans): + try: + def _download_info(parent,fetched,requested,bytes_transferred,download_all,bytes_sec): + self.dbus_send.UpdateDownloadInfo(fetched,requested,bytes_transferred,download_all,bytes_sec) +@@ -60,10 +90,15 @@ class UpdateManager(): + def _update_finished(parent,trans): + if parent.exit: + # 检查是否可以升级 +- if trans.from_revision != trans.new_revision: +- logging.info("Update available refs:%s old-revision:%s to new-revision:%s ",trans.available_refs,trans.from_revision,trans.new_revision) +- self._make_meta_for_panel(trans.available_metadata,OUTPUT_JSON_PATH) +- self.dbus_send.UpdateDetectFinishedNext(True,[SYSTEM_UPDATE_GROUPS],'') ++ if trans.from_revision and trans.new_revision: ++ if (trans.from_revision != trans.new_revision and trans.available_refs != trans.deployed_refs) \ ++ or (trans.from_revision != trans.new_revision and trans.push_list): ++ logging.info("Update available refs:%s old-revision:%s to new-revision:%s ",trans.available_refs,trans.from_revision,trans.new_revision) ++ self._make_meta_for_panel(trans.available_metadata,OUTPUT_JSON_PATH) ++ self.dbus_send.UpdateDetectFinishedNext(True,[SYSTEM_UPDATE_GROUPS],'') ++ else: ++ logging.info("System is up-to-date refs:%s revision:%s",trans.available_refs,trans.new_revision) ++ self.dbus_send.UpdateDetectFinishedNext(parent.exit,[],'') + else: + logging.info("System is up-to-date refs:%s revision:%s",trans.available_refs,trans.new_revision) + self.dbus_send.UpdateDetectFinishedNext(parent.exit,[],'') +@@ -72,10 +107,11 @@ class UpdateManager(): + + self.sqlite3_server.insert_into_display("check_time", get_east_8_time()[0:-4]) + ++ trans.role = ROLE_UPDATE_CACHE + update_backend = UpdateBackend(self,self.options.sysroot,self.options.os) + update_backend.connect("transaction-done", _update_finished) + update_backend.connect("download-info", _download_info) +- update_backend.run(self.trans) ++ update_backend.run(trans) + except Exception as e: + logging.error(e) + +@@ -92,6 +128,7 @@ class UpdateManager(): + else: + trans.deployable = False + ++ self.trans.role = ROLE_DOWNLOAD_ONLY + download_backend = DownloadBackend(self,self.options.sysroot,self.options.os) + download_backend.connect("transaction-done", _download_finished) + download_backend.connect("download-info", _download_info) +@@ -106,13 +143,15 @@ class UpdateManager(): + + trans.deployable = False + if parent.exit: +- self.configs_uncover.setValue("statusForFinishPrompt","finished_status",INSTALL_SUCCESS_FLAG) ++ settings["status"] = INSTALL_SUCCESS_FLAG + else: +- self.configs_uncover.setValue("statusForFinishPrompt","finished_status",parent.error_code) ++ settings["status"] = parent.error_code ++ # 更新内容写入到配置文件 ++ settings.save() + +- if parent.exit and self.options.sysroot == None: +- self._system_reboot_shutdown(device_status,self.options.prohibit_shutdown) ++ self._system_reboot_shutdown(device_status,self.options.prohibit_shutdown) + ++ self.trans.role = ROLE_DEPLOY_ONLY + deploy_backend = DeployBackend(self,self.options.sysroot,self.options.os) + deploy_backend.connect("transaction-done", _deploy_finished) + deploy_backend.run(self.trans) +@@ -124,8 +163,7 @@ class UpdateManager(): + def _rollback_finished(parent,trans): + self.dbus_send.RollbackSysFinishedNext(parent.exit,trans.available_refs,parent.error_code) + +- if parent.exit and self.options.sysroot == None: +- self._system_reboot_shutdown("reboot",self.options.prohibit_shutdown) ++ self._system_reboot_shutdown("reboot",self.options.prohibit_shutdown) + + rollback_backend = RollbackBackend(self,self.options.sysroot,self.options.os) + rollback_backend.connect("transaction-done", _rollback_finished) +@@ -133,38 +171,12 @@ class UpdateManager(): + except Exception as e: + logging.error(e) + +- def start_deb_install(self, deb_path = "", _check_local_dep = False, _auto_satisfy = False, source = '', sender=None): +- try: +- kareiInterface = dbus.Interface(dbus.SystemBus().get_object('org.kylin.kare', '/org/kylin/kare'), +- dbus_interface='org.kylin.kare') +- logging.info("kare install:%s", deb_path) +- kareRet = kareiInterface.install ("openkylin2.0", deb_path) +- +- if kareRet: +- logging.info("kare install call success:%s", deb_path) +- self.sqlite3_server.transaction_manager.start_transaction(deb_path, 30) +- else: +- self.dbus_send.InstalldebFinished(False,'kare install call error','') +- except Exception as e: +- logging.error("kare install error:%s", str(e)) +- self.dbus_send.InstalldebFinished(False, str(e), 'kare install call error') +- +- def start_purge_pkgs(self,pkgs_list,sender=None): +- try: +- kareiInterface = dbus.Interface(dbus.SystemBus().get_object('org.kylin.kare', '/org/kylin/kare'), +- dbus_interface='org.kylin.kare') +- for pl in pkgs_list: +- logging.info("kare remove: %s", pl) +- kareRet = kareiInterface.remove(pl) +- +- if kareRet: +- logging.info("kare remove call success:%s", pl) +- self.sqlite3_server.transaction_manager.start_transaction(pl, 10) +- else: +- self.dbus_send.PurgePackagesFinished (False,'kare remove call error','') +- except Exception as e: +- logging.error("kare remove error:%s", str(e)) +- self.dbus_send.PurgePackagesFinished (False,'kare remove error','') ++ def require_shutdown_install(self): ++ # 检测当前是否准备安装更新 ++ if self.trans.deployable is True: ++ return 1,0 ++ else: ++ return 0,0 + + def _make_meta_for_panel(self,data,output_path): + groups_base_info = {} +@@ -179,12 +191,16 @@ class UpdateManager(): + groups_base_info.update({"update-type":data.setdefault("update-type",SYSTEM_UPDATE_GROUPS)}) + + update_name = {"zh_CN":data.setdefault("update-name_zh_CN","系统更新"),"en_US":data.setdefault("update-name_en_US","System update")} +- + groups_base_info.update({"update-name":update_name}) +- groups_base_info.update({"description":data.setdefault("description","")}) +- groups_base_info.update({"changelog":data.setdefault("changelog","")}) +- groups_base_info.update({"total_download_size":int(data.setdefault("ostree.incrementsize",0))}) +- groups_base_info.update({"total_install_size":int(data.setdefault("ostree.incrementsize",0))}) ++ ++ description_name = {"zh_CN":data.setdefault("description",""),"en_US":data.setdefault("description","")} ++ groups_base_info.update({"description":description_name}) ++ ++ changlog = {"zh_CN":data.setdefault("changelog",""),"en_US":data.setdefault("changelog","")} ++ groups_base_info.update({"changelog":changlog}) ++ ++ groups_base_info.update({"total_download_size":int(data.setdefault("download-size",0))}) ++ groups_base_info.update({"total_install_size":int(data.setdefault("install-size",0))}) + groups_base_info.update({"icon":data.setdefault("icon","/usr/share/upgrade/icon/kylin-update-desktop-system.png")}) + + output_json.update(groups_base_info) +@@ -225,7 +241,7 @@ class UpdateManager(): + logging.info("Initiate dbus success ...") + return UpdateManagerDbusController(self,bus,bus_name) + except dbus.exceptions.NameExistsException: +- if self.options.replace is False: ++ if not settings.get("replace",False): + logging.critical("Another daemon is already running") + sys.exit(1) + logging.warning("Replacing already running daemon") +diff --git a/backend-immutable/SystemUpdater/UpdateManagerDbus.py b/backend-immutable/SystemUpdater/UpdateManagerDbus.py +index f525069..b5ede1d 100755 +--- a/backend-immutable/SystemUpdater/UpdateManagerDbus.py ++++ b/backend-immutable/SystemUpdater/UpdateManagerDbus.py +@@ -6,13 +6,16 @@ import dbus.service + import logging + from gettext import gettext as _ + from .Core.loop import mainloop +-import SystemUpdater.Core.enums as enums + from SystemUpdater.Core.errors import * + from SystemUpdater.Core.enums import * + from xml.etree import ElementTree + import locale + from gettext import ngettext + from math import ceil ++import subprocess ++from SystemUpdater.configs import settings ++from SystemUpdater.packageManager.pmWorker import * ++ + UPDATER_DBUS_INTERFACE = 'com.kylin.systemupgrade.interface' + UPDATER_DBUS_PATH = '/com/kylin/systemupgrade' + UPDATER_DBUS_SERVICE = 'com.kylin.systemupgrade' +@@ -107,7 +110,6 @@ class UpdateManagerDbusController(dbus.service.Object): + self.cache = parent.cache + self.bus = bus + self.prohibit_list = [] +- self.configs_uncover = parent.configs_uncover + self.database = self.parent.sqlite3_server + + self.now_working = self.ACTION_DEFUALT_STATUS +@@ -143,8 +145,8 @@ class UpdateManagerDbusController(dbus.service.Object): + sender_name = get_proc_from_dbus_name(sender) + + logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' UpdateDetect sender:%s...',sender_name) +- self._check_prohibit_user(sender_name) +- self.parent.start_update() ++ trans = self.parent.create_trans(ACTION_DEFUALT_STATUS,sender_name) ++ self.parent.start_update(trans) + self.now_working = self.ACTION_UPDATE + return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC + except Exception as e: +@@ -153,7 +155,7 @@ class UpdateManagerDbusController(dbus.service.Object): + + #更新 信号 + def UpdateDetectFinishedNext(self,success,upgrade_group,error_code=''): +- self.UpdateDetectFinished(success,upgrade_group,error_code,get_error_description_from_enum(error_code)) ++ self.UpdateDetectFinished(success,upgrade_group,get_error_num_from_enum(error_code),get_error_description_from_enum(error_code)) + + @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='basss') + def UpdateDetectFinished(self, success, upgrade_group,error_code='',error_desc=''): +@@ -170,7 +172,7 @@ class UpdateManagerDbusController(dbus.service.Object): + sender_name = get_proc_from_dbus_name(sender) + + logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' UpdateDownloadAll sender:%s...',sender_name) +- self._check_prohibit_user(sender_name) ++ # self._check_prohibit_user(sender_name) + self.parent.start_download() + self.now_working = self.ACTION_DOWNLOADONLY + return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC +@@ -180,7 +182,7 @@ class UpdateManagerDbusController(dbus.service.Object): + + # 下载信号 + def UpdateDownloadFinishedNext(self,success,upgrade_group,error_code=''): +- self.UpdateDownloadFinished(success,upgrade_group,error_code,get_error_description_from_enum(error_code)) ++ self.UpdateDownloadFinished(success,upgrade_group,get_error_num_from_enum(error_code),get_error_description_from_enum(error_code)) + + @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='basss') + def UpdateDownloadFinished(self, success, upgrade_group,error_code='',error_desc=''): +@@ -189,7 +191,7 @@ class UpdateManagerDbusController(dbus.service.Object): + success,upgrade_group, error_code,error_desc) + + #发送下载包信息 fix bug 字节大小改成u 无符号32位 +- @dbus.service.signal(UPDATER_DBUS_INTERFACE, signature='iiuui') ++ @dbus.service.signal(UPDATER_DBUS_INTERFACE, signature='iitti') + def UpdateDownloadInfo(self,current_items, total_items, currenty_bytes, total_bytes, current_cps): + logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" UpdateDownloadInfo current_items = %d, total_items = %d, currenty_bytes = %s, total_bytes = %s, current_cps = %s/s", + current_items, total_items, \ +@@ -205,6 +207,26 @@ class UpdateManagerDbusController(dbus.service.Object): + + # =============================== 部署 =============================== */ + ++ # V10版本旧接口名称,内容和DeployLatestUpdate一样 ++ @dbus.service.method(UPDATER_DBUS_INTERFACE,in_signature='s',out_signature='is',sender_keyword='sender') ++ def TriggerInstallOnShutdown(self,mode,sender=None): ++ try: ++ #处于更新和升级中的话 不进行更新 ++ if self.now_working != ACTION_DEFUALT_STATUS: ++ logging.warning('DeployLatestUpdate In the process of Updating or Upgrading...') ++ return self.RETURN_BUSY_STATE,self.RETURN_BUSY_DESC ++ else: ++ sender_name = get_proc_from_dbus_name(sender) ++ ++ logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' DeployLatestUpdate sender:%s...',sender_name) ++ # self._check_prohibit_user(sender_name) ++ self.parent.start_deploy(str(mode)) ++ self.now_working = self.ACTION_DEPLOY ++ return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC ++ except Exception as e: ++ logging.error(str(e)) ++ return self.RETURN_UNKNOWN_CODE,str(e) ++ + @dbus.service.method(UPDATER_DBUS_INTERFACE,in_signature='s',out_signature='is',sender_keyword='sender') + def DeployLatestUpdate(self,mode,sender=None): + try: +@@ -216,7 +238,7 @@ class UpdateManagerDbusController(dbus.service.Object): + sender_name = get_proc_from_dbus_name(sender) + + logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' DeployLatestUpdate sender:%s...',sender_name) +- self._check_prohibit_user(sender_name) ++ # self._check_prohibit_user(sender_name) + self.parent.start_deploy(str(mode)) + self.now_working = self.ACTION_DEPLOY + return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC +@@ -226,7 +248,7 @@ class UpdateManagerDbusController(dbus.service.Object): + + def DeployUpdatFinishedNext(self,success,upgrade_group,error_code=''): + self.database.insert_info(success,error_code) +- self.DeployUpdatFinished(success,upgrade_group,error_code,get_error_description_from_enum(error_code)) ++ self.DeployUpdatFinished(success,upgrade_group,get_error_num_from_enum(error_code),get_error_description_from_enum(error_code)) + + @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='basss') + def DeployUpdatFinished(self, success, upgrade_group,error_code='',error_desc=''): +@@ -300,10 +322,11 @@ class UpdateManagerDbusController(dbus.service.Object): + try: + sender_name = get_proc_from_dbus_name(sender) + +- status = self.configs_uncover.getWithDefault("statusForFinishPrompt", "finished_status",INSTALL_DEFAULT_FLAG) ++ status = settings.get("status") + logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' ChcekPopupOnPoweroffInstalled sender:%s status:%s ...',sender_name,status) + if status != INSTALL_DEFAULT_FLAG: +- self.configs_uncover.setValue("statusForFinishPrompt","finished_status",INSTALL_DEFAULT_FLAG) ++ settings["status"] = INSTALL_DEFAULT_FLAG ++ settings.save() + return status + except Exception as e: + logging.error(str(e)) +@@ -324,71 +347,161 @@ class UpdateManagerDbusController(dbus.service.Object): + # sender_name,os_version, update_version) + return os_version,update_version + +- @dbus.service.method(UPDATER_DBUS_INTERFACE,in_signature='ssbbs',out_signature='is',sender_keyword='sender') ++ # =============================== 包安装卸载 =============================== */ ++ @dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='ssbbs', out_signature='is', sender_keyword='sender') + def InstallDebFile(self,source = "unKnown", path = "", _check_local_dep = False, _auto_satisfy = False, user_lang = '', sender=None): + try: +- if self.now_working != ACTION_DEFUALT_STATUS: +- logging.warning('PurgePackages In the process of Updating or Upgrading...') ++ sender_name = get_proc_from_dbus_name(sender) ++ ++ if self.now_working != self.ACTION_DEFUALT_STATUS: ++ logging.warning('Updater In the process of Upgrading, ignore \'%s\' InstallDebFile(%s) request ...', \ ++ sender_name, path) + return self.RETURN_BUSY_STATE,self.RETURN_BUSY_DESC + +- #当传入为空时 直接返回 + if str(user_lang) == '': + logging.info("The incoming language is null...") + +- check_local_dep = bool(_check_local_dep) +- auto_satisfy = bool(_auto_satisfy) +- deb_path = str(path) ++ check_local_dep = bool(_check_local_dep) ++ auto_satisfy = bool(_auto_satisfy) ++ deb_path = str(path) + +- sender_name = get_proc_from_dbus_name(sender) +- self._check_prohibit_user(sender_name) + logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' InstallDebFile and check_local_dep:%r, auto_satisfy:%r, current_lang:%s , InstallDebFile sender: %s .',\ +- check_local_dep,auto_satisfy,user_lang,sender_name) +- logging.info("Will install: %s.",path) +- self.parent.start_deb_install(deb_path, _check_local_dep, _auto_satisfy, source, sender) ++ check_local_dep, auto_satisfy, user_lang, sender_name) ++ logging.info("Will install: %s.", path) ++ ++ if not hasattr(self.parent, 'pmworker') or self.parent.pmworker is None: ++ self.parent.pmworker = PmWorker(self.parent) ++ ++ self.parent.pmworker.start_deb_install(deb_path, _check_local_dep, _auto_satisfy, source, sender) + return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC + except Exception as e: + logging.error(str(e)) + return self.RETURN_UNKNOWN_CODE,str(e) + +- @dbus.service.method(UPDATER_DBUS_INTERFACE,in_signature='asss',out_signature='is',sender_keyword='sender') +- def PurgePackages(self,_purge_list,cur_user,user_lang = '',sender=None): ++ @dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='as', out_signature='is', sender_keyword='sender') ++ def InstallPackages(self, pkg_list = [], sender=None): + try: +- if self.now_working != ACTION_DEFUALT_STATUS: +- logging.warning('PurgePackages In the process of Updating or Upgrading...') ++ sender_name = get_proc_from_dbus_name(sender) ++ ++ if self.now_working != self.ACTION_DEFUALT_STATUS: ++ logging.warning('Updater In the process of Upgrading, ignore \'%s\' InstallPackages request ...', \ ++ sender_name) + return self.RETURN_BUSY_STATE,self.RETURN_BUSY_DESC + +- if str(user_lang) == '': +- logging.info("The incoming language is null...") ++ logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' InstallPackages: %s .',\ ++ ",".join(pkg_list)) + +- purge_list = [str(pkg) for pkg in _purge_list] ++ if not hasattr(self.parent, 'pmworker') or self.parent.pmworker is None: ++ self.parent.pmworker = PmWorker(self.parent) ++ ++ self.parent.pmworker.start_back_upgrade(pkg_list, sender) ++ ++ return self.RETURN_SUCCESS_CODE, self.RETURN_SUCCESS_DESC ++ except Exception as e: ++ logging.error(str(e)) ++ return self.RETURN_UNKNOWN_CODE, str(e) ++ ++ @dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='asss', out_signature='is', sender_keyword='sender') ++ def PurgePackages(self, _purge_list, cur_user, user_lang = '', sender=None): ++ try: + sender_name = get_proc_from_dbus_name(sender) ++ purge_list = [str(pkg) for pkg in _purge_list] ++ ++ if self.now_working != self.ACTION_DEFUALT_STATUS: ++ logging.warning('Updater In the process of Upgrading, ignore \'%s\' PurgePackages(%s) request ...', \ ++ sender_name, purge_list) ++ return self.RETURN_BUSY_STATE,self.RETURN_BUSY_DESC ++ + logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' PurgePackages Sender:%s and purge list is:%s...',sender_name, purge_list) +- self._check_prohibit_user(sender_name) + +- # if True: +- # #需要对aptdeamon加这两个环境变量 才可以提示弹窗 +- # self.set_aptdeamon_environ("XAUTHORITY","/home/"+str(cur_user)+"/.Xauthority") +- # self.set_aptdeamon_environ("DISPLAY",":0") ++ if not hasattr(self.parent, 'pmworker') or self.parent.pmworker is None: ++ self.parent.pmworker = PmWorker(self.parent) + +- self.parent.sqlite3_server.current_purge_pkgs = purge_list +- self.parent.start_purge_pkgs(purge_list, sender) ++ self.parent.pmworker.start_purge_pkgs(purge_list, sender) + return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC + except Exception as e: + logging.error(str(e)) + return self.RETURN_UNKNOWN_CODE,str(e) +- ++ ++ @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='basss') ++ def UpdateInstallFinished(self, success, upgrade_group, error_string='', error_desc=''): ++ logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" UpdateInstallFinished success = %r , upgrade_group = %a, error_string = %s , error_desc = %s ",\ ++ success, upgrade_group, error_string, error_desc) ++ ++ #卸载完成的信号 + @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='bss') +- def InstalldebFinished(self, success,error_string='',error_desc=''): +- logging.info(COLORLOG_PREFIX + "Emitting"+ COLORLOG_SUFFIX + " InstalldebFinished success = %r , error_string = %s , error_desc = %s ",\ ++ def PurgePackagesFinished(self, success,error_string='',error_desc=''): ++ logging.info(COLORLOG_PREFIX + "Emitting"+ COLORLOG_SUFFIX + " PurgePackagesFinished success = %r , error_string = %s , error_desc = %s ",\ + success,error_string,error_desc) ++ ++ #卸载进度信息 0~100 进度信息 101为非预期的信号 ++ @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='iss') ++ def PurgePkgStatusChanged(self,progress,status,current_details): ++ logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" PurgePkgStatusChanged progress = %d , status = %s ,current_details = %s",\ ++ progress,status,current_details) + ++ #卸载完成的信号,包含当前正在卸载的包名 ++ @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='bsss') ++ def PurgePackagesFinishedWithPkgname(self, success,error_string='',error_desc='',current_pkg=''): ++ logging.info(COLORLOG_PREFIX + "Emitting"+ COLORLOG_SUFFIX + " PurgePackagesFinishedWithPkgname success = %r , error_string = %s , error_desc = %s , current purge package = %s" ,\ ++ success,error_string,error_desc,current_pkg) ++ ++ #卸载完成的信号 ++ @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='bssss') ++ def PurgePackagesFinishedWithErrCode(self, success,error_string='',error_desc='',current_pkg='',error_code=''): ++ logging.info(COLORLOG_PREFIX + "Emitting"+ COLORLOG_SUFFIX + " PurgePackagesFinishedWithErrCode success = %r , error_string = %s , error_desc = %s , current purge package = %s , error_code = %s",\ ++ success,error_string,error_desc,current_pkg,error_code) ++ ++ #卸载进度信息 0~100 进度信息 101为非预期的信号,包含当前正在卸载的包名 ++ @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='isss') ++ def PurgePkgStatusChangedWithPkgname(self,progress,status,current_details,current_pkg=''): ++ logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" PurgePkgStatusChangedWithPkgname progress = %d , status = %s ,current_details = %s, current purge package = %s",\ ++ progress,status,current_details,current_pkg) ++ ++ #安装deb包完成的信号 + @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='bss') +- def PurgePackagesFinished(self, success,error_string='',error_desc=''): +- logging.info(COLORLOG_PREFIX + "Emitting"+ COLORLOG_SUFFIX + " PurgePackagesFinished success = %r , error_string = %s , error_desc = %s ",\ ++ def InstalldebFinished(self, success,error_string='',error_desc=''): ++ logging.info(COLORLOG_PREFIX + "Emitting"+ COLORLOG_SUFFIX + " InstalldebFinished success = %r , error_string = %s , error_desc = %s ",\ + success,error_string,error_desc) + ++ #安装deb包完成的信号 ++ @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='bsss') ++ def InstalldebFinishedWithErrCode(self, success,error_string='',error_desc='',error_code=''): ++ logging.info(COLORLOG_PREFIX + "Emitting"+ COLORLOG_SUFFIX + " InstalldebFinishedWithErrCode success = %r , error_string = %s , error_desc = %s , error_code = %s",\ ++ success,error_string,error_desc,error_code) ++ ++ #安装进度信息 0~100 进度信息 101为非预期的信号 ++ @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='iss') ++ def InstalldebStatusChanged(self,progress,status,current_details): ++ logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" InstalldebStatusChanged progress = %d , status = %s ,current_details = %s",\ ++ progress,status,current_details) ++ ++ @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='is') ++ def EnvBuildStatusChanged(self, progress, status): ++ logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" EnvBuildStatusChanged progress = %d , status = %s",\ ++ progress,status) ++ ++ @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='bs') ++ def EnvBuildStatusFinished(self, progress, status): ++ logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" EnvBuildStatusFinished progress = %d , status = %s",\ ++ progress,status) ++ + WRITABLE_PROPERTIES = () + ++ #检查是否需要安装或者重启安装的请求 ++ @dbus.service.method(UPDATER_DBUS_INTERFACE,out_signature='is',sender_keyword='sender') ++ def CheckInstallRequired(self,sender=None): ++ try: ++ sender_name = get_proc_from_dbus_name(sender) ++ need_install,install_time = self.parent.require_shutdown_install() ++ logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' CheckInstallRequired need_install:%s install_time:%s sender_name: %s...',\ ++ need_install,install_time,sender_name) ++ ++ return need_install,str(install_time) ++ except Exception as e: ++ logging.error(str(e)) ++ return 0,"0" ++ + # pylint: disable-msg=C0103,C0322 + @dbus.service.signal(dbus_interface=dbus.PROPERTIES_IFACE, + signature="sa{sv}as") +@@ -498,7 +611,6 @@ class UpdateManagerDbusController(dbus.service.Object): + ''' + if iface == UPDATER_DBUS_INTERFACE: + if name == "ShutdownInstall": +- self.parent.configs_uncover.setValue("InstallMode","shutdown_install",str(bool(value))) + elif name == "P2pBootstrap": + self.parent.apt_p2p_config.set_bootstrap(str(value)) + else: +@@ -518,3 +630,28 @@ class UpdateManagerDbusController(dbus.service.Object): + } + else: + return {} ++ ++ @dbus.service.method(UPDATER_DBUS_INTERFACE,in_signature='s',out_signature='b',sender_keyword='sender') ++ def MountSquashfs(self,squashfs,sender=None): ++ logging.info("mount squashfs:%s"%squashfs) ++ ret = True ++ if(os.path.exists(squashfs)): ++ name=os.path.splitext(squashfs) ++ logging.info("file extension name:%s"%name[1]) ++ if(name[1].lower()==".squashfs"): ++ try: ++ mountpath = "/media/OfflineSource" ++ cmd="cat /proc/mounts|grep -q %s && umount %s"%(mountpath,mountpath) ++ ret1=subprocess.run(cmd,shell=True).returncode ++ logging.info("umount return code:%d"%ret1) ++ cmd1 = "mkdir -p %s && mount -o loop %s %s"%(mountpath,squashfs,mountpath) ++ ret2=subprocess.run(cmd1,shell=True).returncode ++ logging.info("mount ret:%d"%ret2) ++ if (ret2==0): ++ pass ++ else: ++ ret=False ++ except Exception as e: ++ logging.error(e) ++ ret = False ++ return ret +\ No newline at end of file +diff --git a/backend-immutable/SystemUpdater/UpdatesAvailable.py b/backend-immutable/SystemUpdater/UpdatesAvailable.py +index 3ed3076..ecf276b 100644 +--- a/backend-immutable/SystemUpdater/UpdatesAvailable.py ++++ b/backend-immutable/SystemUpdater/UpdatesAvailable.py +@@ -3,26 +3,28 @@ + import os + import json + import logging ++import collections + from gi.repository import Gio + from gettext import gettext as _ + from SystemUpdater.Core.errors import * + from SystemUpdater.Core.enums import * ++from gi.repository import GObject, GLib + +-class UpdatesAvailable(): +- """ +- Represent the (potentially partial) results of an unattended-upgrades +- run +- """ +- ++class Transaction(): + # 更新的缓存数据保存位置 + UPDATE_AVAILABLE_DATA = "/var/cache/kylin-system-updater/update_available_data.json" + +- def __init__(self): ++ def __init__(self,tid=None, role=None, sender=None): + self.from_revision = None + self.new_revision = None + self.available_refs = None +- self.available_metadata = None +- ++ self.deployed_refs = None ++ self.available_repo = None ++ self.push_list = [] ++ self.available_metadata = {} ++ self.tid = tid ++ self.role = ROLE_UNSET ++ self.sender = sender + self._deployable = False + + if os.path.exists(self.UPDATE_AVAILABLE_DATA): +@@ -36,8 +38,13 @@ class UpdatesAvailable(): + def _write_local(self): + try: + output_upgrade = {} ++ output_upgrade.update({"from_revision":self.from_revision}) + output_upgrade.update({"new_revision":self.new_revision}) + output_upgrade.update({"available_refs":self.available_refs}) ++ output_upgrade.update({"deployed_refs":self.deployed_refs}) ++ output_upgrade.update({"available_repo":self.available_repo}) ++ # output_upgrade.update({"push_list":self.push_list}) ++ output_upgrade.update({"available_metadata":self.available_metadata}) + output_upgrade.update({"_deployable":self._deployable}) + + #6 产生JSON文件 +@@ -51,14 +58,18 @@ class UpdatesAvailable(): + try: + with open(self.UPDATE_AVAILABLE_DATA,'r') as f: + data = json.load(f) +- self.new_revision = data["new_revision"] +- self.available_refs = data["available_refs"] +- self._deployable = data["_deployable"] ++ self.from_revision = data.get("from_revision",None) ++ self.new_revision = data.get("new_revision",None) ++ self.available_refs = data.get("available_refs",None) ++ self.deployed_refs = data.get("deployed_refs",None) ++ self.available_repo = data.get("available_repo",None) ++ # self.push_list = data.get("push_list",[]) ++ self.available_metadata = data.get("available_metadata",{}) ++ self._deployable = data.get("_deployable",False) + logging.info("Finished: reading upgrade data from the local file...") + except Exception as exc: + logging.error(exc) + +- + def _get_deployable(self): + return self._deployable + +@@ -66,4 +77,94 @@ class UpdatesAvailable(): + self._deployable = state + self._write_local() + +- deployable = property(_get_deployable, _set_deployable) +\ No newline at end of file ++ def _set_default(self,state): ++ pass ++ ++ def _get_download_size(self): ++ return self.available_metadata.get("download-size",0) ++ ++ def _get_install_size(self): ++ return self.available_metadata.get("install-size",0) ++ ++ deployable = property(_get_deployable, _set_deployable) ++ dowanload_size = property(_get_download_size, _set_default) ++ install_size = property(_get_install_size, _set_default) ++ ++class TransactionQueue(GObject.GObject): ++ ++ """Queue for transactions.""" ++ ++ __gsignals__ = {"queue-changed": (GObject.SignalFlags.RUN_FIRST, ++ None, ++ ())} ++ ++ def __init__(self, worker): ++ """Intialize a new TransactionQueue instance.""" ++ GObject.GObject.__init__(self) ++ self._queue = collections.deque() ++ self._proc_count = 0 ++ self.worker = worker ++ # Used to keep track of not yet queued transactions ++ self.limbo = {} ++ # self.worker.connect("transaction-done", self._on_transaction_done) ++ ++ def __len__(self): ++ return len(self._queue) ++ ++ def _emit_queue_changed(self): ++ """Emit the queued-changed signal.""" ++ logging.debug("emitting queue changed") ++ self.emit("queue-changed") ++ ++ def put(self, tid): ++ """Add an item to the queue.""" ++ trans = self.limbo.pop(tid) ++ if trans._idle_watch is not None: ++ GLib.source_remove(trans._idle_watch) ++ if self.worker.trans: ++ trans.status = enums.STATUS_WAITING ++ self._queue.append(trans) ++ else: ++ self.worker.run(trans) ++ self._emit_queue_changed() ++ ++ # def _on_transaction_done(self, worker, trans): ++ # """Mark the last item as done and request a new item.""" ++ # # FIXME: Check if the transaction failed because of a broken system or ++ # # if dpkg journal is dirty. If so allready queued transactions ++ # # except the repair transactions should be removed from the queue ++ # if trans.exit in [enums.EXIT_FAILED, enums.EXIT_CANCELLED]: ++ # if trans.exit == enums.EXIT_FAILED: ++ # exit = enums.EXIT_PREVIOUS_FAILED ++ # else: ++ # exit = enums.EXIT_CANCELLED ++ # _trans = trans.after ++ # while _trans: ++ # self.remove(_trans) ++ # _trans.exit = exit ++ # msg = enums.get_role_error_from_enum(trans.role) ++ # _trans.status_details = msg ++ # _trans = _trans.after ++ # try: ++ # next_trans = self._queue.popleft() ++ # except IndexError: ++ # logging.debug("There isn't any queued transaction") ++ # else: ++ # self.worker.run(next_trans) ++ # self._emit_queue_changed() ++ ++ def remove(self, transaction): ++ """Remove the specified item from the queue.""" ++ self._queue.remove(transaction) ++ self._emit_queue_changed() ++ ++ def clear(self): ++ """Remove all items from the queue.""" ++ for transaction in self._queue: ++ transaction._remove_from_connection_no_raise() ++ self._queue.clear() ++ ++ @property ++ def items(self): ++ """Return a list containing all queued items.""" ++ return list(self._queue) +\ No newline at end of file +diff --git a/backend-immutable/SystemUpdater/UpgradeStrategies.py b/backend-immutable/SystemUpdater/UpgradeStrategies.py +index 6d18d78..954744f 100644 +--- a/backend-immutable/SystemUpdater/UpgradeStrategies.py ++++ b/backend-immutable/SystemUpdater/UpgradeStrategies.py +@@ -1,38 +1,40 @@ + # 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 ++from .UpgradeStrategiesDbus import UpgradeStrategiesDbusController, \ ++ UPDATER_DBUS_INTERFACE, \ ++ UPDATER_DBUS_PATH, \ ++ UPDATER_DBUS_SERVICE ++from SystemUpdater.packageManager.pmUtils import * + + 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, _no_DataMigration=True) +- #策略配置接口的超时退出机制 ++ self.options = options ++ self.bus = dbus.SystemBus () ++ self.dbus_send = self._setup_dbus ( self.bus ) ++ self.uuconfigs = UpgradeConfig ( ++ datadir = "/var/lib/unattended-upgrades/", ++ name = "unattended-upgrades-policy.conf" ) ++ self.configs_uncover = UpgradeConfig ( ++ defaults_conf = "/etc/kylin-system-updater/system-updater-defaults.conf" ) ++ self.sqlite3_server = Sqlite3Server ( self, _no_DataMigration=True ) ++ + 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() +@@ -43,12 +45,11 @@ class UpgradeStrategies(): + try: + mainloop.run() + except KeyboardInterrupt: +- self.dbusController.Quit(None) ++ self.dbus_send.Quit(None) + +- def _setup_dbus(self): ++ def _setup_dbus(self, bus): + # 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, +@@ -96,6 +97,6 @@ class UpgradeStrategies(): + #超时退出 + if self.strategy_timestamp != 0 and time.time() - self.strategy_timestamp > STRATEGY_IDLE_TIMEOUT: + logging.warning("Quitting due to inactivity") +- self.dbusController.Quit(None) ++ self.dbus_send.Quit(None) + return False +- return True +\ No newline at end of file ++ return True +diff --git a/backend-immutable/SystemUpdater/UpgradeStrategiesDbus.py b/backend-immutable/SystemUpdater/UpgradeStrategiesDbus.py +index 1691052..1ab788d 100644 +--- a/backend-immutable/SystemUpdater/UpgradeStrategiesDbus.py ++++ b/backend-immutable/SystemUpdater/UpgradeStrategiesDbus.py +@@ -6,7 +6,9 @@ import logging + import subprocess + from gettext import gettext as _ + from .Core.loop import mainloop +-from SystemUpdater.Core.utils import get_proc_from_dbus_name ++from SystemUpdater.Core.DbusControl import DbusControl ++from SystemUpdater.packageManager.pmUtils import * ++from SystemUpdater.packageManager.pmWorker import * + + UPDATER_DBUS_INTERFACE = 'com.kylin.UpgradeStrategies.interface' + UPDATER_DBUS_PATH = '/com/kylin/UpgradeStrategies' +@@ -45,20 +47,40 @@ UU_UPGRADE_MODE_BEFORE_SHUTDOWN = 3 + 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 = "" ++ ACTION_DEFUALT_STATUS =-1 ++ ACTION_UPDATE = 0 ++ ACTION_DOWNLOADONLY = 1 ++ ACTION_DEPLOY = 2 ++ ACTION_ROLLBACK = 3 + +- RETURN_UNKNOWN_CODE = -1 +- RETURN_UNKNOWN_DESC = "" ++ RETURN_BUSY_STATE = 1 ++ RETURN_BUSY_DESC = "In the process of updating or Upgrading..." ++ ++ 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.parent = parent ++ self.bus = dbus.SystemBus() + +- self.transaction = None ++ self.transaction = None ++ self.now_working = self.ACTION_DEFUALT_STATUS ++ ++ self.prohibit_list = [] ++ if not self.parent.options.debug: ++ self.prohibit_list = ["dbus-send","gdbus"] ++ ++ self.dbus_control = DbusControl(self.bus,parent.options.debug) ++ ++ def _check_prohibit_user(self, sender_name): ++ if sender_name in self.prohibit_list: ++ raise dbus.exceptions.DBusException("ERROR: You are not allowed to perform this action.") + + def __check_change__(self, _config = None, _section = "", _option = "", _value = ""): + if _config == None: +@@ -73,8 +95,7 @@ class UpgradeStrategiesDbusController(dbus.service.Object): + def Quit(self, caller_name): + """Request a shutdown of the daemon.""" + #如果在下载就请求 取消 +- logging.info("Quitting was requested") +- logging.debug("Quitting main loop...") ++ logging.info("UpgradeStrategies service quitting...") + mainloop.quit() + logging.debug("Exit") + +@@ -82,7 +103,7 @@ class UpgradeStrategiesDbusController(dbus.service.Object): + @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) ++ sender_name = self.dbus_control.check_sender(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): +@@ -155,7 +176,7 @@ class UpgradeStrategiesDbusController(dbus.service.Object): + @dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='b', out_signature='b',sender_keyword='sender') + def SetUpgradeStrategyState(self,state,sender=None): + state=bool(state) +- sender_name = get_proc_from_dbus_name(sender) ++ sender_name = self.dbus_control.check_sender(sender) + logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' SetUpdateStrategyState, upgrade strategy state is %r, sender name: %s .',state,sender_name) + try: + if state: +@@ -175,7 +196,7 @@ class UpgradeStrategiesDbusController(dbus.service.Object): + @dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='s', out_signature='b',sender_keyword='sender') + def SetUpgradeMode(self, mode, sender = None): + _mode = str(mode) +- sender_name = get_proc_from_dbus_name(sender) ++ sender_name = self.dbus_control.check_sender(sender) + logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' SetUpgradeMode, mode: %s , sender:%s .',_mode,sender_name) + try: + if not self.__check_change__(self.parent.uuconfigs, "updateStrategiesManager", "installType", _mode): +@@ -191,7 +212,7 @@ class UpgradeStrategiesDbusController(dbus.service.Object): + def SetPreDownloadState(self, _state, _time, sender = None): + state = bool(_state) + time = str(_time) +- sender_name = get_proc_from_dbus_name(sender) ++ sender_name = self.dbus_control.check_sender(sender) + logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' SetPreDownloadState, state is %r, time: %s, sender name: %s .',state,time,sender_name) + try: + if state: +@@ -214,7 +235,7 @@ class UpgradeStrategiesDbusController(dbus.service.Object): + @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) ++ sender_name = self.dbus_control.check_sender(sender) + logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' SetUpdateDays, days: %d , sender:%s .'\ + ,_days,sender_name) + try: +@@ -230,7 +251,7 @@ class UpgradeStrategiesDbusController(dbus.service.Object): + @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) ++ sender_name = self.dbus_control.check_sender(sender) + logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' SetAutoUpgradeRandomRange will be set value %s, sender: %s .',\ + _randomRange,sender_name) + try: +@@ -246,7 +267,7 @@ class UpgradeStrategiesDbusController(dbus.service.Object): + def SetAutomaticReboot(self, status, reboot_time, sender = None): + _state = bool(status) + _reboot_time = str(reboot_time) +- sender_name = get_proc_from_dbus_name(sender) ++ sender_name = self.dbus_control.check_sender(sender) + logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' SetAutomaticReboot, status is %r, reboot_time: %s, sender name: %s .'\ + ,_state,_reboot_time,sender_name) + try: +@@ -267,9 +288,10 @@ class UpgradeStrategiesDbusController(dbus.service.Object): + return True + + ## dbus接口: 开启关闭自动更新功能 +- @dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='b', out_signature='b') +- def SetAutoUpgradeState(self, _state): ++ @dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='b', out_signature='b', sender_keyword='sender') ++ def SetAutoUpgradeState(self, _state, sender): + state = bool(_state) ++ sender_name = self.dbus_control.check_sender(sender) + logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' SetAutoUpgradeState, state is %r ...',state) + try: + if state: +@@ -290,9 +312,10 @@ class UpgradeStrategiesDbusController(dbus.service.Object): + return True + + ## dbus接口: 设置自动更新策略 +- @dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='is', out_signature='b') +- def SetAutoUpgradeMode(self, mode, time): ++ @dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='is', out_signature='b', sender_keyword='sender') ++ def SetAutoUpgradeMode(self, mode, time, sender): + _time = str(time) ++ sender_name = self.dbus_control.check_sender(sender) + logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' SetAutoUpgradeMode, mode is %s, time is %s ...',mode,_time) + try: + if mode == UU_UPGRADE_MODE_AUTOMATIC_DOWNLOAD: +@@ -326,7 +349,7 @@ class UpgradeStrategiesDbusController(dbus.service.Object): + # # 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) ++ sender_name = self.dbus_control.check_sender(sender) + logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' SetDownloadspeedMax, speed:%s, set:%r, sender name: %s .'%(speed, set, sender_name)) + #来重启Aptdeamon + if set: +@@ -357,7 +380,7 @@ class UpgradeStrategiesDbusController(dbus.service.Object): + # # 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) ++ sender_name = self.dbus_control.check_sender(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") +@@ -372,7 +395,7 @@ class UpgradeStrategiesDbusController(dbus.service.Object): + # 是否允许关机前更新 + @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) ++ sender_name = self.dbus_control.check_sender(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.' +@@ -405,9 +428,10 @@ class UpgradeStrategiesDbusController(dbus.service.Object): + # return True,"success" + + # 获取数据库值 +- @dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='bss', out_signature='s') +- def GetSetDatabaseInfo(self, gs, table, field): ++ @dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='bss', out_signature='s', sender_keyword='sender') ++ def GetSetDatabaseInfo(self, gs, table, field, sender): + Text = 'NULL' ++ sender_name = self.dbus_control.check_sender(sender) + try: + if gs: #get + if table == 'display': +@@ -425,8 +449,9 @@ class UpgradeStrategiesDbusController(dbus.service.Object): + return Text + + ## dbus接口: 发送立即更新的信号 +- @dbus.service.method(UPDATER_DBUS_INTERFACE, out_signature='b') +- def AutoUpgradeAllNow(self): ++ @dbus.service.method(UPDATER_DBUS_INTERFACE, out_signature='b', sender_keyword='sender') ++ def AutoUpgradeAllNow(self, sender): ++ sender_name = self.dbus_control.check_sender(sender) + logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' AutoUpgradeAllNow ...') + try: + self.UpgradeAllNow() +@@ -435,27 +460,11 @@ class UpgradeStrategiesDbusController(dbus.service.Object): + 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) ++ sender_name = self.dbus_control.check_sender(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) +@@ -465,12 +474,173 @@ class UpgradeStrategiesDbusController(dbus.service.Object): + @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) ++ sender_name = self.dbus_control.check_sender(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.method(UPDATER_DBUS_INTERFACE, in_signature='ssbbs', out_signature='is', sender_keyword='sender') ++ def InstallDebFile(self,source = "unKnown", path = "", _check_local_dep = False, _auto_satisfy = False, user_lang = '', sender=None): ++ try: ++ sender_name = self.dbus_control.check_sender(sender) ++ ++ if self.now_working != self.ACTION_DEFUALT_STATUS: ++ logging.warning('Updater In the process of Upgrading, ignore \'%s\' InstallDebFile(%s) request ...', \ ++ sender_name, path) ++ return self.RETURN_BUSY_STATE,self.RETURN_BUSY_DESC ++ ++ if str(user_lang) == '': ++ logging.info("The incoming language is null...") ++ ++ check_local_dep = bool(_check_local_dep) ++ auto_satisfy = bool(_auto_satisfy) ++ deb_path = str(path) ++ ++ logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' InstallDebFile and check_local_dep:%r, auto_satisfy:%r, current_lang:%s , InstallDebFile sender: %s .',\ ++ check_local_dep, auto_satisfy, user_lang, sender_name) ++ logging.info("Will install: %s.", path) ++ ++ if not hasattr(self.parent, 'pmworker') or self.parent.pmworker is None: ++ self.parent.pmworker = PmWorker(self.parent) ++ ++ self.parent.pmworker.start_deb_install(deb_path, _check_local_dep, _auto_satisfy, source, sender) ++ return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC ++ except Exception as e: ++ logging.error(str(e)) ++ return self.RETURN_UNKNOWN_CODE,str(e) ++ ++ @dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='as', out_signature='is', sender_keyword='sender') ++ def InstallPackages(self, pkg_list = [], sender=None): ++ try: ++ sender_name = self.dbus_control.check_sender(sender) ++ ++ if self.now_working != self.ACTION_DEFUALT_STATUS: ++ logging.warning('Updater In the process of Upgrading, ignore \'%s\' InstallPackages request ...', \ ++ sender_name) ++ return self.RETURN_BUSY_STATE,self.RETURN_BUSY_DESC ++ ++ logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' InstallPackages: %s .',\ ++ ",".join(pkg_list)) ++ ++ if not hasattr(self.parent, 'pmworker') or self.parent.pmworker is None: ++ self.parent.pmworker = PmWorker(self.parent) ++ ++ self.parent.pmworker.start_back_upgrade(pkg_list, sender) ++ ++ return self.RETURN_SUCCESS_CODE, self.RETURN_SUCCESS_DESC ++ except Exception as e: ++ logging.error(str(e)) ++ return self.RETURN_UNKNOWN_CODE, str(e) ++ ++ @dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='asss', out_signature='is', sender_keyword='sender') ++ def PurgePackages(self, _purge_list, cur_user, user_lang = '', sender=None): ++ try: ++ sender_name = self.dbus_control.check_sender(sender) ++ purge_list = [str(pkg) for pkg in _purge_list] ++ ++ if self.now_working != self.ACTION_DEFUALT_STATUS: ++ logging.warning('Updater In the process of Upgrading, ignore \'%s\' PurgePackages(%s) request ...', \ ++ sender_name, purge_list) ++ return self.RETURN_BUSY_STATE,self.RETURN_BUSY_DESC ++ ++ logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' PurgePackages Sender:%s and purge list is:%s...',sender_name, purge_list) ++ ++ if not hasattr(self.parent, 'pmworker') or self.parent.pmworker is None: ++ self.parent.pmworker = PmWorker(self.parent) ++ ++ self.parent.pmworker.start_purge_pkgs(purge_list, sender) ++ return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC ++ except Exception as e: ++ logging.error(str(e)) ++ return self.RETURN_UNKNOWN_CODE,str(e) ++ ++ @dbus.service.method(UPDATER_DBUS_INTERFACE,in_signature='ss',out_signature='i',sender_keyword='sender') ++ def PackageInstallStatus(self, name, version, sender=None): ++ try: ++ sender_name = self.dbus_control.check_sender(sender) ++ logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' PackageInstallStatus, name:%s, version:%s, sender:%s .', ++ name, version, sender_name) ++ ++ return self.parent.check_package_status(name, version, sender) ++ except Exception as e: ++ logging.error("PackageInstallStatus err:%s", e) ++ return -100 ++ ++ @dbus.service.method(UPDATER_DBUS_INTERFACE,out_signature='b',sender_keyword='sender') ++ def GetAutocheckStatus(self,sender=None): ++ sender_name = self.dbus_control.check_sender(sender) ++ logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' GetAutocheckStatus sender: %s .', sender_name) ++ ++ autocheckStatus = True ++ ++ try: ++ autocheckStatus = self.parent.sqlite3_server.select_from_display("auto_check") ++ logging.info("[auto_check] value is %s.", autocheckStatus) ++ except Exception as e: ++ logging.error("GetAutocheckStatus: %s.", str(e)) ++ ++ return autocheckStatus ++ ++ #卸载完成的信号 ++ @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='bss') ++ def PurgePackagesFinished(self, success,error_string='',error_desc=''): ++ logging.info(COLORLOG_PREFIX + "Emitting"+ COLORLOG_SUFFIX + " PurgePackagesFinished success = %r , error_string = %s , error_desc = %s ",\ ++ success,error_string,error_desc) ++ ++ #卸载进度信息 0~100 进度信息 101为非预期的信号 ++ @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='iss') ++ def PurgePkgStatusChanged(self,progress,status,current_details): ++ logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" PurgePkgStatusChanged progress = %d , status = %s ,current_details = %s",\ ++ progress,status,current_details) ++ ++ #卸载完成的信号,包含当前正在卸载的包名 ++ @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='bsss') ++ def PurgePackagesFinishedWithPkgname(self, success,error_string='',error_desc='',current_pkg=''): ++ logging.info(COLORLOG_PREFIX + "Emitting"+ COLORLOG_SUFFIX + " PurgePackagesFinishedWithPkgname success = %r , error_string = %s , error_desc = %s , current purge package = %s" ,\ ++ success,error_string,error_desc,current_pkg) ++ ++ #卸载完成的信号 ++ @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='bssss') ++ def PurgePackagesFinishedWithErrCode(self, success,error_string='',error_desc='',current_pkg='',error_code=''): ++ logging.info(COLORLOG_PREFIX + "Emitting"+ COLORLOG_SUFFIX + " PurgePackagesFinishedWithErrCode success = %r , error_string = %s , error_desc = %s , current purge package = %s , error_code = %s",\ ++ success,error_string,error_desc,current_pkg,error_code) ++ ++ #卸载进度信息 0~100 进度信息 101为非预期的信号,包含当前正在卸载的包名 ++ @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='isss') ++ def PurgePkgStatusChangedWithPkgname(self,progress,status,current_details,current_pkg=''): ++ logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" PurgePkgStatusChangedWithPkgname progress = %d , status = %s ,current_details = %s, current purge package = %s",\ ++ progress,status,current_details,current_pkg) ++ ++ #安装deb包完成的信号 ++ @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='bss') ++ def InstalldebFinished(self, success,error_string='',error_desc=''): ++ logging.info(COLORLOG_PREFIX + "Emitting"+ COLORLOG_SUFFIX + " InstalldebFinished success = %r , error_string = %s , error_desc = %s ",\ ++ success,error_string,error_desc) ++ ++ #安装deb包完成的信号 ++ @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='bsss') ++ def InstalldebFinishedWithErrCode(self, success,error_string='',error_desc='',error_code=''): ++ logging.info(COLORLOG_PREFIX + "Emitting"+ COLORLOG_SUFFIX + " InstalldebFinishedWithErrCode success = %r , error_string = %s , error_desc = %s , error_code = %s",\ ++ success,error_string,error_desc,error_code) ++ ++ #安装进度信息 0~100 进度信息 101为非预期的信号 ++ @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='iss') ++ def InstalldebStatusChanged(self,progress,status,current_details): ++ logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" InstalldebStatusChanged progress = %d , status = %s ,current_details = %s",\ ++ progress,status,current_details) ++ ++ @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='is') ++ def EnvBuildStatusChanged(self, progress, status): ++ logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" EnvBuildStatusChanged progress = %d , status = %s",\ ++ progress,status) ++ ++ @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='bs') ++ def EnvBuildStatusFinished(self, progress, status): ++ logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" EnvBuildStatusFinished progress = %d , status = %s",\ ++ progress,status) ++ + #限速修改信号 + @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='ss') + def ButtonStatusChange(self, signal_types = '', value=''): +@@ -492,6 +662,10 @@ class UpgradeStrategiesDbusController(dbus.service.Object): + def ButtonStatusChange(self, signal_types = '', value=''): + logging.info(COLORLOG_PREFIX + "Emitting"+ COLORLOG_SUFFIX + " ButtonStatusChange signal_types = %s, value = %s.",signal_types, value) + ++ @dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='basss') ++ def UpdateInstallFinished(self, success, upgrade_group, error_string='', error_desc=''): ++ logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" UpdateInstallFinished success = %r , upgrade_group = %a, error_string = %s , error_desc = %s ",\ ++ success, upgrade_group, error_string, error_desc) + + # signal:属性发生改变 + @dbus.service.signal(dbus_interface=UPDATER_DBUS_INTERFACE, +diff --git a/backend-immutable/SystemUpdater/backend/BackendOstreeNext.py b/backend-immutable/SystemUpdater/backend/BackendOstreeNext.py +index 3d03c40..52f363b 100644 +--- a/backend-immutable/SystemUpdater/backend/BackendOstreeNext.py ++++ b/backend-immutable/SystemUpdater/backend/BackendOstreeNext.py +@@ -7,10 +7,13 @@ gi.require_version('OSTree', '1.0') + from gi.repository import GLib, Gio, OSTree,GObject + from SystemUpdater.Core.errors import * + from SystemUpdater.Core.enums import * ++from SystemUpdater.constants import OFFLINE_REPO_POST,DEFAULT_OS_NAME + from gettext import gettext as _ ++from SystemUpdater.Core.diskManager import check_free_space ++from SystemUpdater.pluginbase import PluginBase + + RETRY_LIMIT = 5 +-ORIGIN_REMOTE_NAME = "openkylin" ++DEFAULT_REPO_NAME = "kylin" + + class BackendBaseOstree(GObject.GObject): + __gsignals__ = { +@@ -21,13 +24,13 @@ class BackendBaseOstree(GObject.GObject): + GObject.TYPE_NONE, + ( GObject.TYPE_INT, + GObject.TYPE_INT, +- GObject.TYPE_INT64, +- GObject.TYPE_INT64, +- GObject.TYPE_INT64 ++ GObject.TYPE_ULONG, ++ GObject.TYPE_ULONG, ++ GObject.TYPE_ULONG + )) + } + +- def __init__(self,parent,sysroot_path=None,osname=None): ++ def __init__(self,parent,sysroot_path=None,osname=DEFAULT_OS_NAME): + GObject.GObject.__init__(self) + self._async_progress = None + self.error_code = '' +@@ -44,19 +47,18 @@ class BackendBaseOstree(GObject.GObject): + if booted_deployment: + self.osname = booted_deployment.get_osname() + else: ++ logging.warning("Booted deployment is None and use to defalut os name(%s)...",osname) + self.osname = osname + +- self.merge_deployment = self.sysroot.get_merge_deployment(self.osname) +- if not self.merge_deployment: +- pass +- self.origin = self.merge_deployment.get_origin() ++ self.cur_deployment = self.sysroot.get_merge_deployment(self.osname) ++ self.origin = self.cur_deployment.get_origin() + +- # 异常情况下origin文件origin_remote为空 默认强制openkylin +- refspec = self.origin.get_string("origin","refspec") +- ret,origin_remote,origin_ref = OSTree.parse_refspec(refspec) +- if origin_remote == None: +- origin_refspec = ORIGIN_REMOTE_NAME+":"+origin_ref +- self.origin.set_string("origin","refspec",origin_refspec) ++ # 加载插件机制 ++ # 获取当前运行程序的插件目录位置 ++ # self.plugins_dir = os.path.join(os.getcwd(),"SystemUpdater/plugins") ++ # # 基础插件 所有插件的位置 ++ # test = os.path.join(self.plugins_dir,"base") ++ # self.plugin_base = PluginBase(package='SystemUpdater.plugins',searchpath=[os.path.join(self.plugins_dir,"base")]) + + def run(self,trans=None): + logging.info("Processing transaction") +@@ -137,74 +139,151 @@ class BackendBaseOstree(GObject.GObject): + logging.error(str(tbk)) + + def _make_error(self,error,trans): +- if error.code in (0,37): +- self.error_code = ERROR_NETWORK_FAILED +- #取消 +- elif error.code == 19: +- trans.cancellable.reset() +- self.error_code = ERROR_CANCELLED +- elif error.code == 1: +- #Remote "kylin" not found (1) +- self.exit = True ++ if trans.role == ROLE_DEPLOY_ONLY: ++ self.error_code = get_error_num_from_enum(ERROR_NOT_DEPLOYMENT) ++ else: ++ if error.code in (0,37): ++ self.error_code = get_error_num_from_enum(ERROR_NETWORK_FAILED) ++ #取消 ++ elif error.code == 19: ++ trans.cancellable.reset() ++ self.error_code = get_error_num_from_enum(EXIT_CANCELLED) ++ elif error.code == 1: ++ # 在远程分支 未找到 对应的 分支 ++ if "Server returned status 404" in error.message: ++ self.error_code = get_error_num_from_enum(ERROR_PUSH_BRANCH_EXCEPTION) ++ # 未找到 对应的仓库信息 ++ elif "Remote" in error.message and "not found" in error.message: ++ self.exit = True ++ ++ def _open_offline_repo(self): ++ # 检查是否存在离线仓库 ++ offline_flag = OFFLINE_REPO_POST + "/config" ++ if os.path.exists(offline_flag): ++ g_file= Gio.File.new_for_path(OFFLINE_REPO_POST) ++ offline_repo = OSTree.Repo.new(g_file) ++ ret = offline_repo.open() ++ return offline_repo ++ else: ++ return None + +-class UpdateBackend(BackendBaseOstree): ++ def register_formatter(self, name, formatter): ++ """A function a plugin can use to register a formatter.""" ++ self.formatters[name] = formatter ++ ++ def _load_plugins(self,identifier): ++ # 每次formatters将被刷新 ++ self.formatters = {} ++ plugin_dir = os.path.join(self.plugins_dir,identifier) ++ plugins_sources = self.plugin_base.make_plugin_source(searchpath=[plugin_dir],identifier=identifier) ++ ++ # 初始化插件 ++ for plugin_name in plugins_sources.list_plugins(): ++ plugin = plugins_sources.load_plugin(plugin_name) ++ plugin.setup(self) ++ return self.formatters + ++class UpdateBackend(BackendBaseOstree): + def __init__(self,parent,sysroot_path=None,osname=None): + """Initialize a new AptWorker instance.""" + BackendBaseOstree.__init__(self,parent,sysroot_path,osname) ++ ++ #更新前插件 ++ # self.update_formatters = self._load_plugins("update") + + def _run_transaction(self, sysroot,trans): ++ g_options = { ++ "n-network-retries":GLib.Variant("u",RETRY_LIMIT),\ ++ "flags":GLib.Variant("i",OSTree.RepoPullFlags.COMMIT_ONLY),\ ++ # "timestamp-check":GLib.Variant("b",True),\ ++ "depth":GLib.Variant("i",1) ++ } ++ push_revision = None + ostree_repo = sysroot.repo() +- from_revision = self.merge_deployment.get_csum() +- +- refspec = self.origin.get_string("origin","refspec") +- ret,origin_remote,origin_ref = OSTree.parse_refspec(refspec) +- +- g_options = GLib.Variant("a{sv}", +- { "refs":GLib.Variant("as",[origin_ref]),\ +- "n-network-retries":GLib.Variant("u",RETRY_LIMIT),\ +- "flags":GLib.Variant("i",OSTree.RepoPullFlags.COMMIT_ONLY),\ +- # "timestamp-check":GLib.Variant("b",True),\ +- "depth":GLib.Variant("i",1) +- }) +- +- logging.info("Start pull metadata refs:%s...",origin_ref) +- ret = ostree_repo.pull_with_options(origin_remote,g_options,self._async_progress,trans.cancellable) + +- origin_refspec = origin_remote+":"+origin_ref +- ret,new_revision = ostree_repo.resolve_rev(origin_refspec,True) +- new_ref = origin_ref ++ # 获取当前部署分支的checksum与当前部署的分支 ++ from_revision = self.cur_deployment.get_csum() ++ refspec = self.origin.get_string("origin","refspec") ++ ret,remote_repo,deployed_refs = OSTree.parse_refspec(refspec) ++ if not remote_repo: ++ logging.error("Now system deployment repo name is None...") ++ raise UpdateBaseError(ERROR_REPO_IS_NONE) ++ ++ logging.info("Now system deployment info(%s:%s@%s)...",remote_repo,deployed_refs,from_revision) ++ ++ # 加载推送更新 当推送为空时 直接去服务端 拉取当前分支最新的commit ++ if trans.push_list: ++ for push in trans.push_list: ++ remote_repo = push.repo_name ++ pull_ref = push.branch ++ # 指定拉取的commit id,不指定时拉取当前分支最新的 ++ if push.revision: ++ g_options["override-commit-ids"] = GLib.Variant("as",[push.revision]) ++ push_revision = push.revision ++ break ++ else: ++ # 未推送情况下 获取当前部署的分支 ++ pull_ref = deployed_refs ++ ++ g_options["refs"] = GLib.Variant("as",[pull_ref]) ++ logging.info("Start pull metadata and options:%s...",g_options) ++ ostree_repo.pull_with_options(remote_repo,GLib.Variant("a{sv}",g_options),self._async_progress,trans.cancellable) ++ ++ # 没有指定commit情况下 检索当前分支最新commit ++ if not push_revision: ++ ret,new_revision = ostree_repo.resolve_rev(remote_repo+":"+pull_ref,True) ++ else: ++ new_revision = push_revision + + ret,metadata = ostree_repo.load_variant(OSTree.ObjectType.COMMIT,new_revision) + + n_metadata = metadata[0] + + # 检查是否当前分支已经终止,切换到新的分支 +- if OSTree.COMMIT_META_KEY_ENDOFLIFE_REBASE in n_metadata: +- new_ref = n_metadata[OSTree.COMMIT_META_KEY_ENDOFLIFE_REBASE] ++ if OSTree.COMMIT_META_KEY_ENDOFLIFE_REBASE in n_metadata and not trans.push_list: + # 拉取新分支的元数据 +- g_options = GLib.Variant("a{sv}", +- { "refs":GLib.Variant("as",[new_ref]),\ +- "n-network-retries":GLib.Variant("u",RETRY_LIMIT),\ +- "flags":GLib.Variant("i",OSTree.RepoPullFlags.COMMIT_ONLY),\ +- # "timestamp-check":GLib.Variant("b",True),\ +- "depth":GLib.Variant("i",1) +- }) +- logging.info("From origin ref:%s To new refs:%s...",origin_ref,new_ref) +- logging.info("Start pull new metadata refs:%s...",new_ref) +- ret = ostree_repo.pull_with_options(origin_remote,g_options,self._async_progress,trans.cancellable) +- +- origin_refspec = origin_remote+":"+new_ref +- ret,new_revision = ostree_repo.resolve_rev(origin_refspec,True) ++ new_ref = n_metadata[OSTree.COMMIT_META_KEY_ENDOFLIFE_REBASE] ++ logging.info("From origin ref:%s To new refs:%s...",pull_ref,new_ref) + +- ret,metadata = ostree_repo.load_variant(OSTree.ObjectType.COMMIT,new_revision) +- +- # 判断是否有新的更新 ++ pull_ref = new_ref ++ logging.info("Start pull new metadata refs:%s...",pull_ref) ++ g_options["refs"] = GLib.Variant("as",[pull_ref]) ++ ret = ostree_repo.pull_with_options(remote_repo,GLib.Variant("a{sv}",g_options),self._async_progress,trans.cancellable) ++ ++ ret,new_revision = ostree_repo.resolve_rev(remote_repo+":"+pull_ref,True) ++ ++ ret,metadata = ostree_repo.load_variant(OSTree.ObjectType.COMMIT,new_revision) ++ ++ logging.info("new_revision:%s and all metadata:%s...",new_revision,metadata[0]) ++ ++ trans.available_metadata = metadata[0] + trans.from_revision = from_revision ++ trans.available_repo = remote_repo + trans.new_revision = new_revision +- trans.available_refs = new_ref ++ trans.available_refs = pull_ref ++ trans.deployed_refs = deployed_refs ++ ++ def _get_commit_recursive(self,ostree_repo,checksum): ++ # 如果 start 不在 all_deltas 中,返回空列表 ++ ret,metadata = ostree_repo.load_variant(OSTree.ObjectType.COMMIT,checksum) ++ if not metadata: ++ return [] ++ parent = OSTree.commit_get_parent(metadata) ++ if parent: ++ return [parent] + self._get_commit_recursive(ostree_repo, parent) ++ else: ++ return [] + +- trans.available_metadata = metadata[0] ++ def _get_all_commit(self,ostree_repo): ++ commit_and_refs = {} ++ ret,out_all_refs = ostree_repo.list_refs(None,None) ++ ++ for refs in out_all_refs: ++ commit_list = self._get_commit_recursive(ostree_repo,out_all_refs[refs]) ++ for commit in commit_list: ++ commit_and_refs[commit] = refs ++ ++ logging.info(commit_and_refs) + + class DownloadBackend(BackendBaseOstree): + def __init__(self,parent,sysroot_path=None,osname=None): +@@ -212,29 +291,65 @@ class DownloadBackend(BackendBaseOstree): + BackendBaseOstree.__init__(self,parent,sysroot_path,osname) + self.download_size = 0 + ++ self.g_options = { ++ "n-network-retries":GLib.Variant("u",RETRY_LIMIT),\ ++ # "timestamp-check":GLib.Variant("b",True),\ ++ # "low-speed-limit-bytes":GLib.Variant("u",10),\ ++ # "low-speed-limit-seconds":GLib.Variant("u",20),\ ++ "flags":GLib.Variant("i",OSTree.RepoPullFlags.NONE),\ ++ "depth":GLib.Variant("i",1) ++ } ++ ++ def _get_offline_deltas(self): ++ all_deltas = [] ++ offline_repo = self._open_offline_repo() ++ if offline_repo: ++ all_deltas = {} ++ ret,deltas_list = offline_repo.list_static_delta_names(None) ++ logging.info("Offline Repo(%s) has delta files:%r",OFFLINE_REPO_POST,deltas_list) ++ for delta in deltas_list: ++ from_rev = delta.split("-")[0] ++ to_rev = delta.split("-")[1] ++ all_deltas[from_rev] = to_rev ++ ++ return all_deltas ++ + def _run_transaction(self, sysroot,trans): + ostree_repo = sysroot.repo() +- refspec = self.origin.get_string("origin","refspec") +- ret,origin_remote,origin_ref = OSTree.parse_refspec(refspec) ++ ++ remote_repo = trans.available_repo + if trans.available_metadata: +- self.download_size = int(trans.available_metadata.setdefault("ostree.incrementsize",0)) +- +- logging.info("start pull data from available_refs:%s...",trans.available_refs) +- g_options = GLib.Variant("a{sv}", +- { +- "refs":GLib.Variant("as",[trans.available_refs]),\ +- "n-network-retries":GLib.Variant("u",RETRY_LIMIT),\ +- # "timestamp-check":GLib.Variant("b",True),\ +- # "low-speed-limit-bytes":GLib.Variant("u",10),\ +- # "low-speed-limit-seconds":GLib.Variant("u",20),\ +- "flags":GLib.Variant("i",OSTree.RepoPullFlags.NONE),\ +- "depth":GLib.Variant("i",1) +- }) +- +- ostree_repo.pull_with_options(origin_remote,g_options,self._async_progress,trans.cancellable) ++ self.download_size = int(trans.dowanload_size) ++ ++ check_free_space(self.download_size) ++ ++ self.g_options["refs"] = GLib.Variant("as",[trans.available_refs]) ++ ++ all_deltas = self._get_offline_deltas() ++ if all_deltas: ++ pull_deltas = self._get_deltas_recursive(trans.from_revision,all_deltas) ++ self.g_options["require-static-deltas"] = GLib.Variant("b",True) ++ ++ # 循环拉取所有的 deltas ++ for delta in pull_deltas: ++ self.g_options["override-commit-ids"] = GLib.Variant("as",[delta]) ++ logging.info("Start pull data and options:%s...",self.g_options) ++ ostree_repo.pull_with_options(remote_repo,GLib.Variant("a{sv}",self.g_options),self._async_progress,trans.cancellable) ++ else: ++ self.g_options["override-commit-ids"] = GLib.Variant("as",[trans.new_revision]) ++ logging.info("Start pull data and options:%s...",self.g_options) ++ ostree_repo.pull_with_options(remote_repo,GLib.Variant("a{sv}",self.g_options),self._async_progress,trans.cancellable) + + # 下载完成后检查下载的元数据的时间戳 + # upgrader.check_timestamps(ostree_repo,from_revision,trans.new_revision) ++ ++ def _get_deltas_recursive(self,start, all_deltas): ++ # 如果 start 不在 all_deltas 中,返回空列表 ++ if start not in all_deltas: ++ return [] ++ # 获取下一个值并递归处理 ++ next_value = all_deltas[start] ++ return [next_value] + self._get_deltas_recursive(next_value, all_deltas) + + def _on_async_progress(self,obj): + try: +@@ -287,11 +402,10 @@ class DeployBackend(BackendBaseOstree): + refs = trans.available_refs + + # 获取当前origin文件 +- refspec = self.origin.get_string("origin","refspec") +- ret,origin_remote,origin_ref = OSTree.parse_refspec(refspec) ++ remote_repo = trans.available_repo + + # 修改origin 为当前部署新分支 +- origin_refspec = origin_remote+":"+refs ++ origin_refspec = remote_repo+":"+refs + # 创建新的origin文件 + deploy_origin = sysroot.origin_new_from_refspec(origin_refspec) + +@@ -306,12 +420,12 @@ class DeployBackend(BackendBaseOstree): + + self._progress_to_plymouth(60) + +- logging.info("start deploy available_refs:%s new_revision:%s...",refs,new_revision) +- ret,upgrade_deployment = sysroot.deploy_tree(self.osname,new_revision,deploy_origin,self.merge_deployment,None,None) ++ logging.info("Start deploy and available_refs:%s new_revision:%s...",refs,new_revision) ++ ret,upgrade_deployment = sysroot.deploy_tree(self.osname,new_revision,deploy_origin,self.cur_deployment,None,None) + + # 进行部署时 保留历史部署 + flags = OSTree.SysrootSimpleWriteDeploymentFlags.RETAIN +- sysroot.simple_write_deployment(self.osname,upgrade_deployment,self.merge_deployment,flags,None) ++ sysroot.simple_write_deployment(self.osname,upgrade_deployment,self.cur_deployment,flags,None) + + self._progress_to_plymouth(90) + +@@ -402,7 +516,7 @@ class RollbackBackend(BackendBaseOstree): + + origin = out_rollback.get_origin() + refspec = origin.get_string("origin","refspec") +- ret,origin_remote,origin_ref = OSTree.parse_refspec(refspec) ++ ret,remote_repo,origin_ref = OSTree.parse_refspec(refspec) + + trans.available_refs = origin_ref + +@@ -417,7 +531,7 @@ class LoadCacheBackend(BackendBaseOstree): + + def _get_refs(self): + refspec = self.origin.get_string("origin","refspec") +- ret,origin_remote,origin_ref = OSTree.parse_refspec(refspec) ++ ret,remote_repo,origin_ref = OSTree.parse_refspec(refspec) + if origin_ref == None: + origin_ref = '' + return origin_ref +@@ -428,7 +542,7 @@ class LoadCacheBackend(BackendBaseOstree): + if out_rollback and not out_rollback.is_pinned(): + rollback_origin = out_rollback.get_origin() + refspec = rollback_origin.get_string("origin","refspec") +- ret,origin_remote,origin_ref = OSTree.parse_refspec(refspec) ++ ret,remote_repo,origin_ref = OSTree.parse_refspec(refspec) + + return [origin_ref] + else: +diff --git a/backend-immutable/SystemUpdater/configs.py b/backend-immutable/SystemUpdater/configs.py +new file mode 100644 +index 0000000..34b43fe +--- /dev/null ++++ b/backend-immutable/SystemUpdater/configs.py +@@ -0,0 +1,67 @@ ++import json ++import logging ++DEFAULT_SETTINGS = "/etc/kylin-system-updater/settings-default.json" ++RUN_SETTINGS = "/var/cache/kylin-system-updater/settings.json" ++class JSONConfig: ++ def __init__(self, settings_files = []): ++ self.configs = {} ++ ++ # 循环读取所有的配置文件,并将配置进行合并,最后的配置文件优先级最高 ++ for config_file in settings_files: ++ try: ++ with open(config_file, "r") as f: ++ config_data = json.load(f) ++ self.configs = {**self.configs, **config_data} # 使用字典解包进行合并 ++ except Exception as e: ++ pass ++ ++ self.settings_files = settings_files ++ ++ def get(self, key, default=None): ++ return self.configs.get(key, default) ++ ++ def set(self, key, value): ++ self.configs[key] = value ++ ++ def __getitem__(self, key): ++ return self.configs[key] ++ ++ def __setitem__(self, key, value): ++ logging.info(f"Settings: setting '{key}' to '{value}'") ++ self.configs[key] = value ++ ++ def update(self, new_data): ++ if isinstance(new_data, dict): ++ self.configs.update(new_data) ++ else: ++ raise TypeError("update() expects a dictionary") ++ ++ def save(self): ++ with open(self.settings_files[-1], 'w') as f: ++ json.dump(self.configs, f, indent=4) ++ ++""" ++ 使用方式: ++ 获取值 ++ 1、settings.get("name") == "Bob" ++ 2、settings.get("name1", "default") == "default" ++ 3、settings.get("local",{}).get("pre",{}).get("en") == "xxxxx" ++ ++ 设置值 ++ 1、settings.set("name", "Bob") ++ # 仅支持一级设置 ++ 2、settings["name"] = "Bob" ++ 3、支持多级设置 ++ a、locals = settings.get("local",{}) ++ b、locals["pre"]["en"] = "xxxxx" ++ ++ 保存 ++ settings.save() ++""" ++ ++settings = JSONConfig( ++ settings_files=[ ++ DEFAULT_SETTINGS, ++ RUN_SETTINGS, ++ ] ++) +\ No newline at end of file +diff --git a/backend-immutable/SystemUpdater/constants.py b/backend-immutable/SystemUpdater/constants.py +new file mode 100644 +index 0000000..fcb227f +--- /dev/null ++++ b/backend-immutable/SystemUpdater/constants.py +@@ -0,0 +1,12 @@ ++ ++PUTSH_CONTENT_LOCATION = "/opt/kylin-software-properties/ostree-important.list" ++ ++OFFLINE_REPO_POST = "/tmp/ostree-auto-test/offline-repo" ++ ++OUTPUT_JSON_PATH = '/var/cache/kylin-system-updater/json/' ++ ++SYSTEM_UPDATE_GROUPS = "kylin-update-desktop-system" ++ ++DEFAULT_OS_NAME = "kylin" ++ ++DEFAULT_REPO_NAME = "kylin" +\ No newline at end of file +diff --git a/backend-immutable/SystemUpdater/packageManager/UpdaterSDKInterface.py b/backend-immutable/SystemUpdater/packageManager/UpdaterSDKInterface.py +new file mode 100644 +index 0000000..8bbf813 +--- /dev/null ++++ b/backend-immutable/SystemUpdater/packageManager/UpdaterSDKInterface.py +@@ -0,0 +1,584 @@ ++import os ++import ctypes ++import logging ++import subprocess ++ ++from enum import Enum ++from SystemUpdater.packageManager.pmUtils import * ++ ++class KSettingsPrivate(ctypes.Structure): ++ GMainContext = ctypes.c_void_p ++ KSettingsSchema = ctypes.c_void_p ++ GDBusConnection = ctypes.c_void_p ++ ++ _fields_ = [ ++ ("main_context", ctypes.POINTER(GMainContext)), ++ ("schema", ctypes.POINTER(KSettingsSchema)), ++ ("conn", ctypes.POINTER(GDBusConnection)), ++ ("sub_id", ctypes.c_uint * 2) ++ ] ++ ++class KSettings(ctypes.Structure): ++ _fields_ = [ ++ ("parent_instance", ctypes.c_void_p), # GObject parent_instance ++ ("priv", ctypes.POINTER(KSettingsPrivate)) ++ ] ++ ++class UpdaterSDKInterface: ++ def __init__(self, libraryPath = '', archRelatedLib = ''): ++ ++ self.sdkLib = None ++ self.sdkLoaded = False ++ ++ try: ++ if archRelatedLib: ++ libraryPath = self.arch_related_library_path(archRelatedLib) ++ ++ if os.path.exists(libraryPath): ++ self.sdkLib = ctypes.cdll.LoadLibrary(libraryPath) ++ self.sdkLoaded = True ++ else: ++ logging.warning("%s dose not exist.", libraryPath) ++ except Exception as e: ++ logging.error("UpdaterSDKInterface init error: %s", e) ++ ++ def arch_related_library_path(self, archRelatedLib): ++ archRelatedLibPath = '' ++ ++ try: ++ multiarch = subprocess.check_output(['dpkg-architecture', '-qDEB_HOST_MULTIARCH'], timeout=3).decode().strip() ++ archRelatedLibPath = os.path.join("/usr/lib", multiarch, archRelatedLib) ++ except Exception as e: ++ logging.error("arch_related_library_path error: %s", e) ++ ++ return archRelatedLibPath ++ ++ def function_exists(self, funcName): ++ try: ++ getattr(self.sdkLib, funcName) ++ return True ++ except AttributeError: ++ return False ++ ++ ++class UpdaterSDKConf2: ++ KCONF2_LIBRARY = 'libkyconf2.so' ++ APPLICATION_NAME = 'kylin-update-frontend' ++ GROUP_TO_LISTEN = [ ++ 'kylin-update-frontend.notification', ++ 'kylin-update-frontend.downloadlimit', ++ 'kylin-update-frontend.autodownload', ++ 'kylin-update-frontend.autobackup', ++ 'kylin-update-frontend.downloadsettings' ++ ] ++ GROUP_SETTINGS = {} ++ ++ def __init__(self, parent = None): ++ self.conf2Loaded = False ++ self.strategieService = parent ++ self.sdkInterface = UpdaterSDKInterface(archRelatedLib = self.KCONF2_LIBRARY) ++ ++ if not self.sdkInterface.sdkLoaded: ++ logging.warning("'%s' load error", self.KCONF2_LIBRARY) ++ else: ++ self.conf2_interface_init() ++ ++ def conf2_interface_init(self): ++ try: ++ if self.sdkInterface.function_exists("kdk_conf2_new"): ++ self.sdkInterface.sdkLib.kdk_conf2_new.argtypes = [ctypes.c_char_p, ctypes.c_char_p] ++ self.sdkInterface.sdkLib.kdk_conf2_new.restype = ctypes.POINTER(KSettings) ++ ++ for gtl in self.GROUP_TO_LISTEN: ++ self.GROUP_SETTINGS.update({gtl:self.sdkInterface.sdkLib.kdk_conf2_new(gtl.encode('utf-8'), None)}) ++ ++ else: ++ logging.warning("kdk_conf2_new do not find in '%s'", self.KCONF2_LIBRARY) ++ self.conf2Loaded = False ++ return ++ ++ if self.sdkInterface.function_exists("kdk_conf2_get_string"): ++ self.sdkInterface.sdkLib.kdk_conf2_get_string.argtypes = [ ctypes.POINTER(KSettings), ++ ctypes.c_char_p] ++ self.sdkInterface.sdkLib.kdk_conf2_get_string.restype = ctypes.c_char_p ++ else: ++ self.conf2Loaded = False ++ return ++ ++ if self.sdkInterface.function_exists("kdk_conf2_set_value"): ++ self.sdkInterface.sdkLib.kdk_conf2_set_value.argtypes = [ ctypes.POINTER(KSettings), ++ ctypes.c_char_p, ++ ctypes.c_char_p] ++ self.sdkInterface.sdkLib.kdk_conf2_set_value.restype = ctypes.c_int ++ else: ++ self.conf2Loaded = False ++ return ++ ++ if self.sdkInterface.function_exists("kdk_conf2_ksettings_destroy"): ++ pass ++ else: ++ self.conf2Loaded = False ++ return ++ ++ ++ # TODO: ctypes使用kdk_conf2_connect_signal时回调无法触发... ++ # KCallBack = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_char_p, ctypes.c_void_p) ++ ++ # @KCallBack ++ # def on_key_changed(setting, key, user_data): ++ # key_str = key.decode('utf-8') ++ # logging.info("'%s' change detected", key_str) ++ ++ # self.sdkInterface.sdkLib.kdk_conf2_connect_signal.argtypes = [ ctypes.POINTER(KSettings), ++ # ctypes.c_char_p, ++ # KCallBack, ++ # ctypes.c_void_p] ++ # self.sdkInterface.sdkLib.kdk_conf2_connect_signal.restype = ctypes.c_ulong ++ # self.sdkInterface.sdkLib.kdk_conf2_connect_signal(ksettings_ptr, b"changed", on_key_changed, None) ++ ++ self.conf2_listen_key_changed() ++ ++ self.conf2Loaded = True ++ except Exception as e: ++ logging.error("conf2_interface_init error: %s", e) ++ ++ def conf2_listen_key_changed(self): ++ try: ++ def conf2_key_changed_handler(schema, version, key): ++ if schema in self.GROUP_TO_LISTEN: ++ changed_value = self.conf2_get_key_value(schema, key).decode('utf-8') ++ ++ logging.info("key_changed signal: %s(%s):%s -> %s", ++ schema, version, key, changed_value) ++ ++ if schema == 'kylin-update-frontend.notification': ++ ++ if "enable" == key: ++ self.strategieService.dbus_send.AutocheckStatusChanged( ++ True if str(changed_value).lower() == 'true' else False) ++ ++ elif schema == 'kylin-update-frontend.downloadlimit': ++ ++ if "enable" == key: ++ downloadlimitValue = self.conf2_get_key_value(schema, 'value') ++ self.strategieService.dbus_send.DownloadStatusChanged( ++ True if str(changed_value).lower() == 'true' else False, ++ int(downloadlimitValue)) ++ ++ elif "value" == key: ++ downloadspeedLimitStatus = self.conf2_get_key_value(schema, 'enable') ++ self.strategieService.dbus_send.DownloadStatusChanged( ++ True if downloadspeedLimitStatus.lower() == 'true' else False, ++ int(str(changed_value)) if str(changed_value).isdigit() else int(0)) ++ ++ elif schema == 'kylin-update-frontend.autodownload': ++ ++ if "enable" == key: ++ self.strategieService.dbus_send.AutoUpgradeStatusChanged( ++ True if str(changed_value).lower() == 'true' else False) ++ ++ # conf2_service_proxy = self.strategieService.bus.get_object('com.kylin.kysdk.conf2', ++ # '/com/kylin/kysdk/conf2', ++ # follow_name_owner_changes=True) ++ # conf2_service_proxy.connect_to_signal("key_changed", conf2_key_changed_handler) ++ self.strategieService.bus.add_signal_receiver( ++ conf2_key_changed_handler, ++ dbus_interface='com.kylin.kysdk.conf2', ++ signal_name='key_changed' ++ ) ++ ++ except Exception as e: ++ logging.error("conf2_listen_key_changed error: %s", e) ++ ++ def conf2_get_key_value(self, schema, key): ++ updater_kconf2_value = '' ++ ++ try: ++ updater_kconf2_value = self.sdkInterface.sdkLib.kdk_conf2_get_string( ++ self.GROUP_SETTINGS[schema], ++ key.encode('utf-8')) ++ ++ logging.info("conf2_get_key_value: %s:%s -> %s.", schema, key, updater_kconf2_value) ++ except Exception as e: ++ logging.error("conf2_get_key_value: %s",str(e)) ++ ++ return updater_kconf2_value ++ ++ def conf2_set_key_value(self, schema, key, updater_kconf2_value): ++ setStatus = True ++ try: ++ setStatus = self.sdkInterface.sdkLib.kdk_conf2_set_value( ++ self.GROUP_SETTINGS[schema], ++ key.encode('utf-8'), ++ updater_kconf2_value.encode('utf-8')) ++ setStatus = True if setStatus == int(0) else False ++ logging.info("conf2_set_key_value: %s:%s -> %s.", schema, key, updater_kconf2_value) ++ except Exception as e: ++ logging.error("conf2_set_key_value: %s",str(e)) ++ setStatus = False ++ ++ return setStatus ++ ++ def quitKConf2Manager(self): ++ pass ++ ++class InstallMode(Enum): ++ KDK_PACKAGE_AUTO = 0 ++ KDK_PACKAGE_KARE = 1 ++ KDK_PACKAGE_KAIMING = 2 ++ KDK_PACKAGE_DEBIAN = 3 ++ ++ ++class UpdaterSDKPkgInstall: ++ SDK_LIBRARY = 'libkypackage.so' ++ KCallBack = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_int, ctypes.c_char_p) ++ ++ KDK_PACKAGE_INSTALL_ERROR = 0 ++ KDK_PACKAGE_INSTALL_SUCCESS = 1 ++ ++ def __init__(self, parent = None): ++ self.installLoaded = False ++ self.upgradeService = parent ++ self.sdkInterface = UpdaterSDKInterface(archRelatedLib = self.SDK_LIBRARY) ++ ++ if not self.sdkInterface.sdkLoaded: ++ logging.warning("'%s' load error", self.SDK_LIBRARY) ++ else: ++ self.package_interface_init() ++ ++ self.purge_status_changed = self.KCallBack(self._purge_callback_wrapper) ++ self.install_status_changed = self.KCallBack(self._install_callback_wrapper) ++ self.batch_install_status_changed = self.KCallBack(self._batch_install_callback_wrapper) ++ ++ self.purgeFinished = False ++ self.installFinished = False ++ self.batchInstFinished = False ++ ++ self.progress_restore = 0 ++ self.batchInstPkgs = [] ++ self.batchInstPkgsNum = 0 ++ self.batchInstPkgsIndex = 0 ++ ++ # state状态 ++ # 0: 卸载成功 ++ # 1: 正在卸载包 ++ # other: 错误 ++ def _purge_callback_wrapper(self, progress, state, loginfo): ++ try: ++ _loginfo = loginfo.decode("utf-8") ++ # logging.info("purge_status_changed, progress: %d, state: %d, loginfo: %s.", progress, state, _loginfo) ++ ++ if state == 0: ++ if progress == 100: ++ self.purgeFinished = True ++ self.upgradeService.dbus_send.PurgePackagesFinished(True, _loginfo,'') ++ self.upgradeService.dbus_send.PurgePackagesFinishedWithPkgname(True, _loginfo, '', '') ++ self.upgradeService.dbus_send.PurgePackagesFinishedWithErrCode(True, _loginfo, '', '', get_error_num_from_enum(SUCCESS_PURGE_PKG)) ++ ++ elif state == 1: ++ if progress == 100: ++ self.purgeFinished = True ++ self.upgradeService.dbus_send.PurgePackagesFinished(True, _loginfo,'') ++ self.upgradeService.dbus_send.PurgePackagesFinishedWithPkgname(True, _loginfo, '', '') ++ self.upgradeService.dbus_send.PurgePackagesFinishedWithErrCode(True, _loginfo, '', '', get_error_num_from_enum(SUCCESS_PURGE_PKG)) ++ else: ++ self.upgradeService.dbus_send.PurgePkgStatusChanged(progress, str(state), _loginfo) ++ ++ elif state < 0: ++ self.purgeFinished = True ++ self.upgradeService.dbus_send.PurgePackagesFinished(False, _loginfo,'') ++ self.upgradeService.dbus_send.PurgePackagesFinishedWithPkgname(False, _loginfo, '', '') ++ self.upgradeService.dbus_send.PurgePackagesFinishedWithErrCode(False, _loginfo, '', '', get_error_num_from_enum(ERROR_PURGE_PKG_DPKG)) ++ ++ else: ++ logging.info("purge_status_changed other, progress: %d, state: %d, loginfo: %s.", progress, state, _loginfo) ++ ++ except Exception as e: ++ logging.error("_install_callback_wrapper error: %s", e) ++ ++ # state状态 ++ # 0: 安装成功 ++ # 1: 正在安装包 ++ # 2: 正在构建环境 ++ # other: 错误 ++ def _install_callback_wrapper(self, progress, state, loginfo): ++ try: ++ _loginfo = loginfo.decode("utf-8") ++ # logging.info("install_status_changed, progress: %d, state: %d, loginfo: %s.", progress, state, _loginfo) ++ ++ if state == 0: ++ if progress == 100: ++ self.installFinished = True ++ self.upgradeService.dbus_send.InstalldebFinished (True, '', _loginfo) ++ self.upgradeService.dbus_send.InstalldebFinishedWithErrCode (True, '', _loginfo, '') ++ ++ elif state == 1: ++ if progress == 100: ++ self.installFinished = True ++ self.upgradeService.dbus_send.InstalldebFinished (True, '', _loginfo) ++ self.upgradeService.dbus_send.InstalldebFinishedWithErrCode (True, '', _loginfo, '') ++ else: ++ self.upgradeService.dbus_send.InstalldebStatusChanged(progress, str(state), _loginfo) ++ ++ elif state == 2: ++ if progress == 100: ++ self.upgradeService.dbus_send.EnvBuildStatusChanged(progress, str(_loginfo)) ++ self.upgradeService.dbus_send.EnvBuildStatusFinished(True, str(_loginfo)) ++ else: ++ self.upgradeService.dbus_send.EnvBuildStatusChanged(progress, str(_loginfo)) ++ ++ elif state < 0: ++ self.installFinished = True ++ self.upgradeService.dbus_send.InstalldebFinished (False, '', _loginfo) ++ self.upgradeService.dbus_send.InstalldebFinishedWithErrCode (False, '', _loginfo, '') ++ ++ else: ++ logging.info("install_status_changed other, progress: %d, state: %d, loginfo: %s.", progress, state, _loginfo) ++ ++ except Exception as e: ++ logging.error("_install_callback_wrapper error: %s", e) ++ ++ def _batch_install_callback_wrapper(self, progress, state, loginfo): ++ try: ++ _loginfo = loginfo.decode("utf-8") ++ _progress = int((progress * 100.0 / self.batchInstPkgsNum + self.batchInstPkgsIndex * 100 * 100.0 / self.batchInstPkgsNum) / 100) ++ ++ if _progress < self.progress_restore: ++ _progress = self.progress_restore ++ else: ++ self.progress_restore = _progress ++ ++ logging.info("_batch_install_callback_wrapper(%d/%d[%d]), progress: %d, state: %d, loginfo: %s.", ++ self.batchInstPkgsIndex + 1, self.batchInstPkgsNum, _progress, progress, state, _loginfo) ++ ++ if state == 0: ++ if progress == 100: ++ if self.batchInstPkgsIndex == self.batchInstPkgsNum: ++ self.batchInstFinished = True ++ self.upgradeService.dbus_send.UpdateInstallFinished(True, self.batchInstPkgs, '', _loginfo) ++ else: ++ self.upgradeService.dbus_send.InstalldebStatusChanged(_progress, str(state), _loginfo) ++ ++ elif state == 1: ++ if progress == 100: ++ if self.batchInstPkgsIndex == self.batchInstPkgsNum: ++ self.batchInstFinished = True ++ self.upgradeService.dbus_send.UpdateInstallFinished(True, self.batchInstPkgs, '', _loginfo) ++ else: ++ self.upgradeService.dbus_send.InstalldebStatusChanged(_progress, str(state), _loginfo) ++ ++ else: ++ self.upgradeService.dbus_send.InstalldebStatusChanged(_progress, str(state), _loginfo) ++ ++ elif state == 2: ++ if progress == 100: ++ self.upgradeService.dbus_send.EnvBuildStatusChanged(progress, str(_loginfo)) ++ self.upgradeService.dbus_send.EnvBuildStatusFinished(True, str(_loginfo)) ++ else: ++ self.upgradeService.dbus_send.EnvBuildStatusChanged(progress, str(_loginfo)) ++ ++ elif state < 0: ++ if self.batchInstPkgsIndex == self.batchInstPkgsNum: ++ self.batchInstFinished = True ++ self.upgradeService.dbus_send.UpdateInstallFinished(False, self.batchInstPkgs, '', _loginfo) ++ else: ++ self.upgradeService.dbus_send.InstalldebStatusChanged(_progress, str(state), _loginfo) ++ ++ else: ++ logging.info("_batch_install_callback_wrapper other, progress: %d, state: %d, loginfo: %s.", progress, state, _loginfo) ++ ++ except Exception as e: ++ logging.error("_batch_install_callback_wrapper error: %s", e) ++ ++ def package_interface_init(self): ++ try: ++ if self.sdkInterface.function_exists("kdk_package_install_package"): ++ self.sdkInterface.sdkLib.kdk_package_install_package.argtypes = [ctypes.c_char_p, ctypes.c_int, self.KCallBack] ++ self.sdkInterface.sdkLib.kdk_package_install_package.restype = ctypes.c_int ++ else: ++ logging.warning("kdk_package_install_package do not find in '%s'", self.SDK_LIBRARY) ++ self.conf2Loaded = False ++ return ++ ++ if self.sdkInterface.function_exists("kdk_package_install_package_online"): ++ self.sdkInterface.sdkLib.kdk_package_install_package_online.argtypes = [ctypes.c_char_p, ctypes.c_int, self.KCallBack] ++ self.sdkInterface.sdkLib.kdk_package_install_package_online.restype = ctypes.c_int ++ else: ++ logging.warning("kdk_package_install_package_online do not find in '%s'", self.SDK_LIBRARY) ++ self.conf2Loaded = False ++ return ++ ++ if self.sdkInterface.function_exists("kdk_package_install_package_offline"): ++ self.sdkInterface.sdkLib.kdk_package_install_package_offline.argtypes = [ctypes.c_char_p, ctypes.c_int, self.KCallBack] ++ self.sdkInterface.sdkLib.kdk_package_install_package_offline.restype = ctypes.c_int ++ else: ++ logging.warning("kdk_package_install_package_offline do not find in '%s'", self.SDK_LIBRARY) ++ self.conf2Loaded = False ++ return ++ ++ if self.sdkInterface.function_exists("kdk_package_remove_package"): ++ self.sdkInterface.sdkLib.kdk_package_remove_package.argtypes = [ctypes.c_char_p, self.KCallBack] ++ self.sdkInterface.sdkLib.kdk_package_remove_package.restype = ctypes.c_int ++ else: ++ logging.warning("kdk_package_remove_package do not find in '%s'", self.SDK_LIBRARY) ++ self.conf2Loaded = False ++ return ++ ++ # 以下接口不影响sdk安装卸载操作 ++ if self.sdkInterface.function_exists("kdk_package_get_name"): ++ self.sdkInterface.sdkLib.kdk_package_get_name.argtypes = [ctypes.c_char_p] ++ self.sdkInterface.sdkLib.kdk_package_get_name.restype = ctypes.c_char_p ++ else: ++ logging.warning("kdk_package_get_name do not find in '%s'", self.SDK_LIBRARY) ++ ++ if self.sdkInterface.function_exists("kdk_package_is_installed"): ++ self.sdkInterface.sdkLib.kdk_package_is_installed.argtypes = [ctypes.c_char_p, ctypes.c_char_p] ++ self.sdkInterface.sdkLib.kdk_package_is_installed.restype = ctypes.c_int ++ else: ++ logging.warning("kdk_package_is_installed do not find in '%s'", self.SDK_LIBRARY) ++ ++ self.installLoaded = True ++ except Exception as e: ++ logging.error("conf2_interface_init error: %s", e) ++ ++ def package_interface_install(self, uri): ++ _success = False ++ ++ try: ++ if not self.installLoaded: ++ logging.error("kdk_package_install_package load library error") ++ return _success ++ ++ logging.info("kdk_package_install_package try to install: %s", uri) ++ ++ iRet = self.sdkInterface.sdkLib.kdk_package_install_package (uri.encode('utf-8'), ++ InstallMode.KDK_PACKAGE_AUTO.value, ++ self.install_status_changed) ++ ++ logging.info("kdk_package_install_package iRet:%d", iRet) ++ ++ if iRet == self.KDK_PACKAGE_INSTALL_ERROR: ++ logging.info("kdk_package_install_package KDK_PACKAGE_INSTALL_ERROR") ++ elif iRet == self.KDK_PACKAGE_INSTALL_SUCCESS: ++ _success = True ++ logging.info("kdk_package_install_package KDK_PACKAGE_INSTALL_SUCCESS") ++ ++ except Exception as e: ++ logging.error("package_interface_install error: %s", e) ++ ++ return _success ++ ++ def package_interface_batch_install(self, pkgs_list): ++ _success = False ++ iRet = self.KDK_PACKAGE_INSTALL_ERROR ++ ++ try: ++ if not self.installLoaded: ++ logging.error("kdk_package_install_package load library error") ++ return _success ++ ++ logging.info("kdk_package_install_package try to install: %s", ",".join(pkgs_list)) ++ self.batchInstPkgsNum = len(pkgs_list) ++ ++ for pl in pkgs_list: ++ iRet = self.sdkInterface.sdkLib.kdk_package_install_package (pl.encode('utf-8'), ++ InstallMode.KDK_PACKAGE_AUTO.value, ++ self.batch_install_status_changed) ++ self.batchInstPkgsIndex += 1 ++ ++ if iRet == self.KDK_PACKAGE_INSTALL_ERROR: ++ break ++ ++ logging.info("kdk_package_install_package iRet:%d", iRet) ++ ++ if iRet == self.KDK_PACKAGE_INSTALL_ERROR: ++ logging.info("kdk_package_install_package KDK_PACKAGE_INSTALL_ERROR") ++ elif iRet == self.KDK_PACKAGE_INSTALL_SUCCESS: ++ _success = True ++ logging.info("kdk_package_install_package KDK_PACKAGE_INSTALL_SUCCESS") ++ ++ self.package_interface_install_clear() ++ except Exception as e: ++ logging.error("package_interface_install error: %s", e) ++ ++ return _success ++ ++ def package_interface_remove(self, uri): ++ _success = False ++ _package_name = '' ++ ++ try: ++ if not self.installLoaded: ++ logging.error("kdk_package_install_package load library error") ++ return _success ++ ++ if os.path.exists(uri) and uri.endswith('.desktop'): ++ _package_name = self.package_name_from_desktop (uri) ++ else: ++ _package_name = uri ++ ++ logging.info("kdk_package_remove_package try to remove: %s", _package_name) ++ iRet = self.sdkInterface.sdkLib.kdk_package_remove_package (_package_name.encode('utf-8'), ++ self.purge_status_changed) ++ ++ logging.info("kdk_package_remove_package iRet:%d", iRet) ++ ++ if iRet == self.KDK_PACKAGE_INSTALL_ERROR: ++ logging.info("kdk_package_remove_package KDK_PACKAGE_REMOVE_ERROR") ++ elif iRet == self.KDK_PACKAGE_INSTALL_SUCCESS: ++ _success = True ++ logging.info("kdk_package_remove_package KDK_PACKAGE_REMOVE_SUCCESS") ++ ++ except Exception as e: ++ logging.error("package_interface_remove error: %s", e) ++ ++ return _success ++ ++ def package_name_from_desktop(self, desktop_path): ++ _package_name = '' ++ ++ try: ++ if self.sdkInterface.sdkLib.kdk_package_get_name: ++ logging.info("package_name_from_desktop try to get _package_name: %s", desktop_path) ++ _package_name = self.sdkInterface.sdkLib.kdk_package_get_name (desktop_path.encode('utf-8')).decode('utf-8').strip() ++ else: ++ logging.error("kdk_package_get_name load library error") ++ ++ logging.info("package_name_from_desktop _package_name: %s", _package_name) ++ ++ except Exception as e: ++ logging.error("package_name_from_desktop error: %s", e) ++ ++ return _package_name ++ ++ def package_interface_check_status(self, _name, _version): ++ install_status = -100 ++ ++ try: ++ if self.sdkInterface.sdkLib.kdk_package_is_installed: ++ if _name: ++ _name = _name.encode('utf-8') ++ if _version: ++ _version = _version.encode('utf-8') ++ logging.info("package_interface_check_status try to check _name: %s, _version:%s""", _name, _version) ++ install_status = self.sdkInterface.sdkLib.kdk_package_is_installed (_name, _version) ++ else: ++ logging.error("kdk_package_is_installed load library error") ++ ++ logging.info("package_interface_check_status _name: %s, _version:%s, install_status:%s", ++ _name, _version, install_status) ++ ++ except Exception as e: ++ logging.error("package_interface_check_status error: %s", e) ++ ++ return install_status ++ ++ def package_interface_install_clear(self): ++ try: ++ self.progress_restore = 0 ++ self.batchInstPkgs = [] ++ self.batchInstPkgsNum = 0 ++ self.batchInstPkgsIndex = 0 ++ ++ except Exception as e: ++ logging.error("package_interface_check_status error: %s", e) +diff --git a/backend-immutable/SystemUpdater/packageManager/__init__.py b/backend-immutable/SystemUpdater/packageManager/__init__.py +new file mode 100644 +index 0000000..e69de29 +diff --git a/backend-immutable/SystemUpdater/packageManager/pmEnums.py b/backend-immutable/SystemUpdater/packageManager/pmEnums.py +new file mode 100644 +index 0000000..7b375e6 +--- /dev/null ++++ b/backend-immutable/SystemUpdater/packageManager/pmEnums.py +@@ -0,0 +1,370 @@ ++#!/usr/bin/env python ++# -*- coding: utf-8 -*- ++"""enums - Enumerates for apt daemon dbus messages""" ++ ++import gettext ++gettext.bindtextdomain('kylin-system-updater', '/usr/share/locale') ++gettext.textdomain('kylin-system-updater') ++_ = gettext.gettext ++ ++from aptdaemon.enums import ( ++ ERROR_REPO_DOWNLOAD_FAILED, ++ ERROR_NO_PACKAGE, ++ ERROR_NO_CACHE, ++ EXIT_CANCELLED, ++ ERROR_PACKAGE_MANAGER_FAILED, ++ ERROR_PACKAGE_DOWNLOAD_FAILED, ++ ERROR_DAEMON_DIED, ++ ERROR_CACHE_BROKEN, ++ ERROR_NO_LOCK ++ ) ++ ++from aptdaemon.enums import get_error_description_from_enum as get_other_error_description_from_enum ++from aptdaemon.enums import get_error_string_from_enum as get_other_error_string_from_enum ++ ++SHOW_DETAILES_CHECK_FLAG = "check-show-details-log" ++ ++# 静态变量 ++RUNTIME_INSTALL_TYPE = "runtime" ++POWEROFF_INSTALL_TYPE = "pre-poweroff" ++DEFAULT_INSTALL_TYPE = "default" ++DEFAULT_EMPTY_CHATACTER = '' ++INSTALL_DEFAULT_FLAG = 'none' ++INSTALL_FAILED_FALG = 'failed' ++INSTALL_SUCCESS_FLAG = 'success' ++ ++CUSTOMIZED_SOURCES = "customized-sources" ++STANDARD_SOURCES = "standard-sources" ++ ++CUSTOMIZED_SYSTEM = "customized-system" ++INTERMEDIATE_SYSTEM = "intermediate-system" ++ ++STANDARD_SYSTEM = "standard-system" ++ ++APT_PRIVATE_PATH = "/etc/kylin-system-updater/apt_private.conf" ++APT_STANDARD_PATH = "/etc/kylin-system-updater/apt_standard.conf" ++ ++#系统更新的所有枚举 ++PRIORITY_UPGRADE_SUCCCESSED = "priority-upgrade-successed" ++ ++#apt update阶段出现的错误解析 ++ERROR_PROGRAM_EXCEPTION = "error-program-exception" ++ERROR_UPDATE_DEFAULT_FAILED = "error-update-default-failed" ++ERROR_NOT_SOURCES_PATH = ERROR_NO_LOCK ++ ++ERROR_UPDATE_SOURCE_FAILED = "error-update-source-failed" ++ERROR_UPDATE_SOURCE_TIMEOUT = "error-update-source-timeout" ++ERROR_SOURCE_WRITE_DENIED = "error-source-write-denied" ++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_CONFIG_PATH_MISSING = "error-config-path-missing" ++ERROR_SYSTEM_SANITY_FAILED = "error-system-snaity-failed" ++ERROR_SYSTEM_SANITY_WARNING = "error-system-snaity-warning" ++ERROR_NOT_NECESSARY_LIB = "error-not-necessary-lib" ++ERROR_DOWNGRADE_PKGS_LIMIT="error-downgrade-pkgs-limit" ++ERROR_INCOMP_THIRDS_PKGS="error-incomp-thirds-pkgs" ++ERROR_CHECK_UPDATE_TIMEOUT="error-check-update-timeout" ++ ++#安装错误 ++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_RUN_SCRIPTS_FAILED = "error-run-scripts-failed" ++ERROR_NOT_DISK_SPACE = "error-not-disk-space" ++ERROR_DEVICE_LOW_BATTERY = "error-device-low-battery" ++ERROR_READ_LOCAL_DEB = "error-read-local-deb" ++ERROR_LOCAL_DEB_FORMAT = "error-local-deb-format" ++ERROR_INSTALL_DEB_BASE = "error-install-deb-base" ++ERROR_INSTALL_DEB_VERIFY = "error-install-deb-verify" ++ERROR_INSTALL_DEB_SECURITY = "error-install-deb-security" ++ERROR_INSTALL_DEB_DPKG = "error-install-deb-dpkg" ++ERROR_INSTALL_DEB_DEPEND = "error-install-deb-depend" ++SUCCESS_INSTALL_DEB = "success-install-deb" ++ERROR_INSTALL_DEB_OTHER = "error-install-deb-other" ++SUCCESS_PURGE_PKG = "success-purge-pkg" ++ERROR_PURGE_PKG_DPKG = "error-purge-pkg-dpkg" ++ERROR_PURGE_PKG_VERIFY = "error-purge-pkg-verify" ++ERROR_PURGE_PKG_TOREMOVE = "error-purge-pkg-toremove" ++ERROR_INSTALL_POWER_OUTAGE = "error-install-power-outage" ++ERROR_INSTALL_TIMEOUT_FAILED = "error-install-timeout-failed" ++ ++ERROR_NO_PACKAGE_CACHE = "error-no-package-cache" ++ERROR_NO_OFFLINE_SOURCE = "error-no-offline-source" ++ ++_STRINGS_ERROR = { ++ DEFAULT_EMPTY_CHATACTER: '', ++ PRIORITY_UPGRADE_SUCCCESSED: _("Update Manager upgrade is complete, please restart the setting panel before performing the system update."), ++ ERROR_DAEMON_DIED: _("Update Manager exception, 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_UPDATE_SOURCE_TIMEOUT: _("Check for update exceptions!"), ++ ERROR_NETWORK_FAILED: _("Network anomaly, can't check for updates!"), ++ 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_PROGRAM_EXCEPTION: _("Check for update exceptions!"), ++ ERROR_CONFIG_PATH_MISSING: _("Check for update exceptions!"), ++ ERROR_NOT_SOURCES_PATH: _("Check for update exceptions!"), ++ ERROR_DOWNGRADE_PKGS_LIMIT: _("Check for update exceptions!"), ++ ERROR_SYSTEM_SANITY_FAILED: _("Update conditions not met: Patches do not match"), ++ ERROR_SYSTEM_SANITY_WARNING: _("Update conditions not met: Patches do not match"), ++ ERROR_NOT_NECESSARY_LIB: _("Check for update exceptions!"), ++ ERROR_INCOMP_THIRDS_PKGS: _("Check for update exceptions!"), ++ ERROR_CHECK_UPDATE_TIMEOUT: _("Check for update exceptions!"), ++ ERROR_SOURCE_WRITE_DENIED: _("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 ++ EXIT_CANCELLED: _("Failed to fetch"), ++ 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_RUN_SCRIPTS_FAILED: _("There is an exception in the update package."), ++ ERROR_NOT_DISK_SPACE: _("Disk space is insufficient, please clean the disk and then upgrade"), ++ ERROR_DEVICE_LOW_BATTERY: _("If the battery of the device is insufficient, please plug in the power supply before updating the system."), ++ ERROR_INSTALL_TIMEOUT_FAILED: _(" "), ++ ERROR_READ_LOCAL_DEB:_(" "), ++ ERROR_INSTALL_POWER_OUTAGE:_(" "), ++ ERROR_LOCAL_DEB_FORMAT:_(" "), ++ ERROR_INSTALL_DEB_BASE:_(" "), ++ ERROR_INSTALL_DEB_VERIFY:_(" "), ++ ERROR_INSTALL_DEB_SECURITY:_(" "), ++ ERROR_INSTALL_DEB_DPKG:_(" "), ++ ERROR_INSTALL_DEB_DEPEND:_(" "), ++ ERROR_CACHE_BROKEN:_(" "), ++ SUCCESS_INSTALL_DEB:_(" "), ++ ERROR_INSTALL_DEB_OTHER:_(" "), ++ SUCCESS_PURGE_PKG:_(" "), ++ ERROR_PURGE_PKG_DPKG:_(" "), ++ ERROR_PURGE_PKG_VERIFY:_(" "), ++ ERROR_PURGE_PKG_TOREMOVE:_(" "), ++ ++ ERROR_NO_OFFLINE_SOURCE:_(" "), ++ ERROR_NO_PACKAGE_CACHE: _(" ") ++ } ++ ++_DESCS_ERROR = { ++ PRIORITY_UPGRADE_SUCCCESSED: '', ++ ++ DEFAULT_EMPTY_CHATACTER: '', ++ ERROR_PROGRAM_EXCEPTION: _("Program exception, please contact the administrator to solve."), ++ #update ++ ERROR_NOT_SOURCES_PATH: _(" "), ++ ERROR_UPDATE_SOURCE_FAILED: _("Unable to access the source management server, please try again later"), ++ ERROR_UPDATE_SOURCE_TIMEOUT: _("Access to the source management server timed out, please try again later"), ++ ERROR_NETWORK_FAILED: _("Please check your network connection and retry."), ++ 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_CONFIG_PATH_MISSING: _("Patch configuration file directory does not exitst."), ++ 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_DOWNGRADE_PKGS_LIMIT: _("Update patchs do not match and too many downgraded packages."), ++ 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."), ++ ERROR_SYSTEM_SANITY_FAILED: _("The version does not match the patch check."), ++ ERROR_SYSTEM_SANITY_WARNING: _(" "), ++ ERROR_NOT_NECESSARY_LIB: _("The system necessary library matching failed."), ++ ERROR_INCOMP_THIRDS_PKGS: _("Third-party application is not compatible with the patch."), ++ ERROR_CHECK_UPDATE_TIMEOUT: _("Check the update timeout, please try again later."), ++ ERROR_SOURCE_WRITE_DENIED: _("Write Source file was denied, please repair the file permission."), ++ ++ #install ++ EXIT_CANCELLED: _("_Cancel Upgrade"), ++ ERROR_RESOLVER_FAILED: _(" "), ++ 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_RUN_SCRIPTS_FAILED:_("Exceptions to running the check script."), ++ ERROR_CACHE_BROKEN:_("The package system is broken, please contact the administrator to solve."), ++ ERROR_NOT_DISK_SPACE: _(" "), ++ ERROR_DEVICE_LOW_BATTERY: _(" "), ++ 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."), ++ ERROR_INSTALL_POWER_OUTAGE: _("The installation process is abnormally shut down or down."), ++ ERROR_INSTALL_DEB_VERIFY:_(" "), ++ ERROR_INSTALL_DEB_SECURITY:_(" "), ++ ERROR_INSTALL_DEB_DPKG:_(" "), ++ ERROR_INSTALL_DEB_DEPEND:_(" "), ++ SUCCESS_INSTALL_DEB:_(" "), ++ ERROR_INSTALL_DEB_OTHER:_(" "), ++ SUCCESS_PURGE_PKG:_(" "), ++ ERROR_PURGE_PKG_DPKG:_(" "), ++ ERROR_PURGE_PKG_VERIFY:_(" "), ++ ERROR_PURGE_PKG_TOREMOVE:_(" "), ++ ERROR_NO_OFFLINE_SOURCE:_("If the offline source is not detected, please remount it and update it."), ++ ++ ERROR_NO_PACKAGE_CACHE: _("Package is not in cache"), ++ ERROR_INSTALL_TIMEOUT_FAILED: _("Installation timeout and the installation is exited.") ++ } ++ ++_CODE_ERROR = { ++ DEFAULT_EMPTY_CHATACTER: '', ++ # other 仅测试使用 ++ ERROR_PROGRAM_EXCEPTION: "#0000", ++ ERROR_RESOLVER_FAILED:"#0001", ++ ++ #update ++ ERROR_UPDATE_SOURCE_FAILED: "#0100", ++ ERROR_UPDATE_SOURCE_TIMEOUT: "#0101", ++ ERROR_NETWORK_FAILED: "#0102", ++ ERROR_NOT_GROUPS_CONFIG: "#0107", ++ ERROR_NOT_INIT_PACKAGESINFIO: "#0108", ++ ERROR_SOFTWARE_INDEX_RROKEN: "#0109", ++ ERROR_READ_IMPORTANTLIST_FAILED: "#0110", ++ ERROR_NOT_CONFIGPKG_DEPENDENCIES: "#0111", ++ ERROR_NOT_SELFPKG_DEPENDENCIES: "#0112", ++ ERROR_LOAD_CONFIG_FAILED: "#0113", ++ ERROR_NOT_FIX_SYSTEM: "#0114", ++ ERROR_REPO_DOWNLOAD_FAILED: "#0115", ++ ERROR_NO_CACHE: "#0116", ++ ERROR_SYSTEM_SANITY_FAILED: "#0117", ++ ERROR_SYSTEM_SANITY_WARNING: "#0118", ++ ERROR_NOT_NECESSARY_LIB:"#0119", ++ ERROR_INCOMP_THIRDS_PKGS:"#0120", ++ ERROR_CHECK_UPDATE_TIMEOUT:"#0121", ++ ERROR_SOURCE_WRITE_DENIED:"#0122", ++ ERROR_CONFIG_PATH_MISSING:"#0123", ++ ++ #install ++ ERROR_PACKAGE_MANAGER_FAILED: "#0200", ++ ERROR_NOT_UPGRADE_PACKAGES: "#0201", ++ ERROR_REMOVE_ESSENTIAL_PACKAGES: "#0202", ++ ERROR_PACKAGE_DOWNLOAD_FAILED: "#0203", ++ ERROR_NOT_DISK_SPACE: "#0204", ++ ERROR_READ_LOCAL_DEB: "#0205", ++ ERROR_LOCAL_DEB_FORMAT: "#0206", ++ ERROR_INSTALL_DEB_BASE: "#0207", ++ ERROR_DEVICE_LOW_BATTERY: "#0208", ++ ERROR_NO_PACKAGE: "#0209", ++ ERROR_NO_PACKAGE_CACHE: "#0210", ++ ERROR_INSTALL_DEB_VERIFY:"#0211", ++ ERROR_INSTALL_DEB_SECURITY:"#0212", ++ ERROR_INSTALL_DEB_DPKG:"#0213", ++ ERROR_INSTALL_DEB_DEPEND:"#0214", ++ ERROR_CACHE_BROKEN:"#0215", ++ ERROR_NO_OFFLINE_SOURCE:"#0216", ++ ERROR_INSTALL_POWER_OUTAGE:"#0217", ++ ERROR_INSTALL_TIMEOUT_FAILED: "#0218", ++ ++ SUCCESS_INSTALL_DEB:"#0219", ++ ERROR_INSTALL_DEB_OTHER:"#0220", ++ ERROR_PURGE_PKG_DPKG:"#0221", ++ ERROR_PURGE_PKG_VERIFY:"#0222", ++ ERROR_PURGE_PKG_TOREMOVE:"#0223", ++ SUCCESS_PURGE_PKG:"#0230", ++ ++ # other ++ EXIT_CANCELLED: "#0300" ++ } ++ ++ ++#UPGRADE MONITOR STATUS ++MONIT_DETECT = "step-updatedetect" ++MONIT_DEPRESOLUT = "step-depresolution" ++MONIT_DOWNLOAD = "step-downfinish" ++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", ++} ++ ++PolicyKit_Authority_Action = { ++ 'kylin-installer':"cn.kylin.installer.action", ++ 'kylin-uninstaller':"cn.kylin.uninstaller.action", ++ 'kylin-software-center':"cn.kylin.software.center.action", ++ 'kylin-system-updater':"cn.kylinos.KylinSystemUpdater.action", ++ 'kylin-installer-self':"cn.kylin.installer.self.action", ++ 'kylin-uninstaller-self':"cn.kylin.uninstaller.self.action", ++ 'kylin-software-center-self':"cn.kylin.software.center.self.action", ++ 'kylin-system-updater-self':"cn.kylinos.KylinSystemUpdater.self.action", ++} ++ ++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 get_other_error_description_from_enum(enum) ++ ++def get_error_num_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 _CODE_ERROR[enum] ++ except KeyError: ++ return enum ++ ++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 get_other_error_string_from_enum(enum) ++ ++ ++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") ++ ++def get_policykit_authority_action_enum(enum): ++ try: ++ return PolicyKit_Authority_Action[enum] ++ except KeyError: ++ if enum.endswith("-self"): ++ return "cn.kylinos.KylinSystemUpdater.self.action" #默认配置 ++ else: ++ return "cn.kylinos.KylinSystemUpdater.action" #默认配置 ++ ++# vim:ts=4:sw=4:et +diff --git a/backend-immutable/SystemUpdater/packageManager/pmUtils.py b/backend-immutable/SystemUpdater/packageManager/pmUtils.py +new file mode 100644 +index 0000000..872988f +--- /dev/null ++++ b/backend-immutable/SystemUpdater/packageManager/pmUtils.py +@@ -0,0 +1,932 @@ ++# 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 ++import re ++import os ++import sys ++import apt ++import dbus ++import time ++import shutil ++import locale ++import psutil ++import logging ++import subprocess ++from ctypes import * ++from copy import copy ++from math import ceil ++from defer import Deferred ++from gettext import gettext as _ ++from gettext import ngettext ++from urllib.parse import urlsplit ++from apt.debfile import DebPackage ++from SystemUpdater.Core.errors import * ++from stat import (S_IMODE, ST_MODE, S_IXUSR) ++from SystemUpdater.packageManager.pmEnums import * ++ ++import apt_pkg ++apt_pkg.init_config() ++ ++from urllib.request import ( ++ ProxyHandler, ++ Request, ++ build_opener, ++ install_opener, ++ urlopen, ++ ) ++ ++# 禁止关机锁文件路径 ++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 ++ ++#清空上次输出的分组JSON文件 ++def make_empty_dir(path): ++ #清空 升级列表 ++ if not os.path.exists(path): ++ os.makedirs(path) ++ logging.info('making the ConfigPath(%s) is complete...',path) ++ else: ++ shutil.rmtree(path) ++ os.makedirs(path) ++ logging.info('Emptying the ConfigPath(%s) is complete...',path) ++ ++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 = [] ++ 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_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 """ ++ 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() ++ ++#判断是否为光盘源 ++#光盘源格式 deb file:///home/someone/packs/ ++def is_offline_source(): ++ try: ++ DIR_MRDIA = "/media/OfflineSource/" ++ if os.path.exists(DIR_MRDIA): ++ for second_dir in os.listdir(DIR_MRDIA): ++ #到/media/OfflineSource/ ++ check_dir_two = DIR_MRDIA + second_dir + "/" ++ ++ if not os.path.isdir(check_dir_two): ++ continue ++ check_file = check_dir_two + "ss.map" ++ ++ if os.path.exists(check_file): ++ return True ++ #没有返回存在光盘源就说明不存在 ++ return False ++ else: ++ return False ++ except Exception as e: ++ logging.warning(str(e)) ++ return False ++ ++def deb_format_check(deb_uri): ++ format_check = False ++ ++ try: ++ if not os.path.isfile(deb_uri): ++ return format_check ++ ++ DebPackage(deb_uri) ++ format_check = True ++ logging.info("deb_format_check '%s' legal", deb_uri) ++ except Exception as e: ++ logging.error(e) ++ ++ return format_check ++ ++def deb_verify(deb_path): ++ logging.info("Verify pkg:%s.",deb_path) ++ _deb_path = str(deb_path) ++ _verify_status = False ++ try: ++ if not deb_format_check(_deb_path) or not kysec_authority(): ++ _verify_status = True ++ return _verify_status ++ ++ # # 加载验证签名库 , 验签接口暂时无法调用 ++ if not os.path.isfile("/usr/bin/kylinsigntool"): ++ logging.error("SOF_InitializeEx error!") ++ return _verify_status ++ 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() \ ++ or "Deb signature does not exist" in str(ret.stdout).strip() or "签名不存在" in str(ret.stdout).strip() \ ++ or "证书验证失败" in str(ret.stdout).strip(): ++ logging.info("Signature Verified failed!") ++ elif "Signature Verified Ok" in str(ret.stdout).strip() or "签名验证成功" in str(ret.stdout).strip() \ ++ or "Certificate verification is successful" in str(ret.stdout).strip() or "证书验证成功" in str(ret.stdout).strip(): ++ logging.info("Signature Verified Ok!") ++ _verify_status = True ++ else: ++ logging.error("Signature Verified failed:%s.",ret) ++ except Exception as e: ++ logging.error(e) ++ return _verify_status ++ ++ ++def PolicyKit_Authority(details = '', sender = None, authentication = False, source=''): ++ deferred = Deferred() ++ try: ++ #用户鉴权 ++ logging.info("Authentication via PolicyKit .") ++ details = {'polkit.message':details} ++ cancel_id = '' ++ ++ if False: ++ source=source+'-self' ++ ++ logging.info('authentication status: %r.',authentication) ++ if True == authentication: ++ subject = ('system-bus-name', {'name': sender}) ++ action = get_policykit_authority_action_enum(source) ++ ++ def policykit_done(xxx_todo_changeme): ++ (authorized, challenged, auth_details) = xxx_todo_changeme ++ if authorized: ++ deferred.callback(auth_details) ++ elif challenged: ++ deferred.errback(AuthorizationFailed(subject, action)) ++ else: ++ deferred.errback(NotAuthorizedError(subject, action)) ++ ++ logging.info("PolicyKit source: %s, action: %s.",source,action) ++ kit = dbus.SystemBus().get_object('org.freedesktop.PolicyKit1', '/org/freedesktop/PolicyKit1/Authority') ++ kit = dbus.Interface(kit, 'org.freedesktop.PolicyKit1.Authority') ++ kit.CheckAuthorization(subject, action, details, ++ dbus.UInt32(1), action, ++ timeout=60*60*24*7, ++ reply_handler=policykit_done, ++ error_handler=deferred.errback) ++ else: ++ deferred.callback(_("Authentication success.")) ++ except Exception as e: ++ logging.error(e) ++ deferred.errback(AuthorizationFailed(subject, action)) ++ return deferred ++ ++def kysec_authority(): ++ _allow_kylinsign = False ++ _verify_kylinsign = False ++ _kysec_authority_status = True ++ ++ 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 and "#allow-kylinsign" not in line: ++ _allow_kylinsign = True ++ if "verify-kylinsign" in line and "#verify-kylinsign" not in line: ++ _verify_kylinsign = True ++ ++ if _allow_kylinsign == True and _verify_kylinsign == False: #策略: 阻止 ++ logging.info("unknown sources apply installation policies: deter") ++ _kysec_authority_status = False ++ ++ elif _allow_kylinsign == True and _verify_kylinsign == True: #策略: 警告 ++ logging.info("unknown sources apply installation policies: warning") ++ ++ elif _allow_kylinsign == False and _verify_kylinsign == False: #策略: 关闭 ++ logging.info("unknown sources apply installation policies: close") ++ _kysec_authority_status = False ++ ++ except Exception as e: ++ logging.error(e) ++ ++ return _kysec_authority_status ++ ++ ++class NotAuthorizedError(dbus.DBusException): ++ ++ _dbus_error_name = "org.freedesktop.PolicyKit.Error.NotAuthorized" ++ ++ def __init__(self, subject, action_id): ++ dbus.DBusException.__init__(self, "%s: %s" % (subject, action_id)) ++ self.action_id = action_id ++ self.subject = subject ++ ++ ++class AuthorizationFailed(NotAuthorizedError): ++ _dbus_error_name = "org.freedesktop.PolicyKit.Error.Failed" ++ ++ ++def ApplicationSecInstall(debname = ''): ++ ++ SEC_MODE_NORMAL = 0 ++ SEC_MODE_WHITELIST = 1 ++ SEC_MODE_BLACKLIST = 2 ++ ++ try: ++ logging.debug("Security Application Install Check .") ++ ++ sec = dbus.SystemBus().get_object('com.kylin.kysdk.applicationsec', '/com/kylin/kysdk/applicationsec') ++ secinterface = dbus.Interface(sec, dbus_interface='com.kylin.kysdk.applicationsec.antiinstall') ++ secMode = secinterface.GetMode() ++ ++ if SEC_MODE_NORMAL == secMode: ++ logging.info("Application installation control policy not enabled .") ++ return True,_("Application installation control policy not enabled .") ++ ++ elif SEC_MODE_WHITELIST == secMode: ++ logging.info("Application installation in whitelist mode .") ++ ++ if secinterface.PackageIsInWhilteList(debname) : ++ logging.info("Application in whitelist .") ++ return True,"" ++ else: ++ logging.info("Application not in whitelist .") ++ return False,_("Installation failed! Application is not in the software whitelist list!") ++ ++ elif SEC_MODE_BLACKLIST == secMode: ++ logging.info("Application installation in blacklist mode .") ++ ++ if secinterface.PackageIsInBlackList(debname) : ++ logging.info("Application in blacklist .") ++ return False,_("Installation failed! Application is in the software blacklist list!") ++ else: ++ logging.info("Application not in blacklist .") ++ return True,"" ++ ++ else: ++ logging.info("Application installation in unknown mode .") ++ return True,_("Application installation in unknown mode .") ++ ++ except dbus.exceptions.DBusException as e: ++ dbus_error = str(e._dbus_error_name) ++ compare_error = str("org.freedesktop.DBus.Error.ServiceUnknown") ++ if dbus_error == compare_error: ++ logging.info("Application installation control policy not enabled .") ++ return True,"Application installation control policy not enabled ." ++ raise ++ ++ 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") +diff --git a/backend-immutable/SystemUpdater/packageManager/pmWorker.py b/backend-immutable/SystemUpdater/packageManager/pmWorker.py +new file mode 100644 +index 0000000..03c7ba8 +--- /dev/null ++++ b/backend-immutable/SystemUpdater/packageManager/pmWorker.py +@@ -0,0 +1,201 @@ ++#!/usr/bin/env python ++# -*- coding: utf-8 -*- ++ ++import logging ++from gettext import gettext as _ ++ ++from SystemUpdater.packageManager.pmUtils import * ++from SystemUpdater.Core.UpdaterConfigParser import UpgradeConfig ++from SystemUpdater.packageManager.UpdaterSDKInterface import UpdaterSDKPkgInstall ++ ++class PmWorker(): ++ def __init__(self, parent = None): ++ self.parent = parent ++ self.dbus_send = parent.dbus_send ++ self.sqlite3_server = parent.sqlite3_server ++ self.updater_package = UpdaterSDKPkgInstall ( parent ) ++ ++ if not hasattr(parent, 'configs_uncover') or parent.configs_uncover is None: ++ self.configs_uncover = UpgradeConfig ( ++ defaults_conf = "/etc/kylin-system-updater/system-updater-defaults.conf" ) ++ else: ++ self.configs_uncover = parent.configs_uncover ++ ++ def start_deb_install(self, deb_uri = "", _check_local_dep = False, _auto_satisfy = False, source = '', sender=None): ++ sender_name = get_proc_from_dbus_name(sender) ++ caller_trans = get_source_name_from_enum(sender_name) ++ ++ try: ++ def _run_install_handler(): ++ _success = self.updater_package.package_interface_install(deb_uri) ++ if self.updater_package.installFinished: ++ self.updater_package.installFinished = False ++ else: ++ self.dbus_send.InstalldebFinished(_success, '', '') ++ self.dbus_send.InstalldebFinishedWithErrCode(_success, '', '', '') ++ ++ def on_authorization_success(result): ++ logging.info("Authorization success ...") ++ ++ self.sqlite3_server.deb_policy_keep = True ++ def _check_deb_policy(): ++ if self.sqlite3_server.deb_policy_timestamp % 10 == 0: ++ logging.info("Checking for deb policy timeout(%d)...",self.sqlite3_server.deb_policy_timestamp) ++ if (self.sqlite3_server.deb_policy_timestamp <= 0): ++ logging.warning("Deb policy timeout") ++ self.sqlite3_server.deb_policy_keep = False ++ return False ++ else: ++ self.sqlite3_server.deb_policy_timestamp = self.sqlite3_server.deb_policy_timestamp - 1 ++ return True ++ ++ from gi.repository import GLib ++ self.sqlite3_server.deb_policy_timestamp = 60 * 5 ++ GLib.timeout_add_seconds(1,_check_deb_policy) ++ ++ _run_install_handler() ++ ++ def on_authorization_failure(error): ++ try: ++ logging.info("Authorization failed ...") ++ ++ self.sqlite3_server.deb_policy_keep = False ++ self.sqlite3_server.deb_policy_timestamp = 0 ++ ++ error.raise_exception() ++ except Exception as error: ++ logging.error(error) ++ self.dbus_send.InstalldebFinished(False,_("Cancel authentication."),'') ++ self.dbus_send.InstalldebFinishedWithErrCode(False,_("Cancel authentication."),'','') ++ ++ def _policykit_authority_handler(install_handler = None, authority_failed_handler = None): ++ if not self.sqlite3_server.deb_policy_keep: ++ deferred = PolicyKit_Authority(caller_trans+_(" requires authentication to install software packages."), ++ sender = sender, ++ authentication = self.configs_uncover.getWithDefault( ++ "InstallAndPurge", ++ "install_authority",True), ++ source=source) ++ ++ deferred.add_callback(on_authorization_success) ++ deferred.add_errback(on_authorization_failure) ++ else: ++ self.sqlite3_server.deb_policy_timestamp = 60 * 5 ++ logging.info("Deb policy keep, ignore...") ++ ++ _run_install_handler() ++ ++ if not deb_verify(deb_uri): ++ _policykit_authority_handler() ++ else: ++ _run_install_handler() ++ ++ except Exception as e: ++ logging.info("start_deb_install error: %s", e) ++ self.dbus_send.InstalldebFinished(False, str(e), '') ++ self.dbus_send.InstalldebFinishedWithErrCode(False, str(e), '', '') ++ ++ def start_purge_pkgs(self,pkgs_list,sender=None): ++ try: ++ sender_name = get_proc_from_dbus_name(sender) ++ ++ def _run_purge_handler(): ++ for pl in pkgs_list: ++ _success = self.updater_package.package_interface_remove(pl) ++ if not _success: ++ break ++ ++ if self.updater_package.purgeFinished: ++ self.updater_package.purgeFinished = False ++ else: ++ self.dbus_send.PurgePackagesFinished (_success, '', '') ++ self.dbus_send.PurgePackagesFinishedWithPkgname (_success, '', '', ",".join(pkgs_list)) ++ self.dbus_send.PurgePackagesFinishedWithErrCode (_success, '', '', ",".join(pkgs_list), ++ get_error_num_from_enum (SUCCESS_PURGE_PKG)) ++ ++ def on_authorization_success(result): ++ logging.info("Authorization success ...") ++ ++ self.sqlite3_server.purge_policy_keep = True ++ def _check_purge_policy(): ++ if self.sqlite3_server.purge_policy_timestamp % 10 == 0: ++ logging.info("Checking for deb policy timeout(%d)...",self.sqlite3_server.purge_policy_timestamp) ++ if (self.sqlite3_server.purge_policy_timestamp <= 0): ++ logging.warning("Deb policy timeout") ++ self.sqlite3_server.purge_policy_keep = False ++ return False ++ else: ++ self.sqlite3_server.purge_policy_timestamp = self.sqlite3_server.purge_policy_timestamp - 1 ++ return True ++ ++ from gi.repository import GLib ++ self.sqlite3_server.purge_policy_timestamp = 60 * 5 ++ GLib.timeout_add_seconds(1,_check_purge_policy) ++ ++ _run_purge_handler() ++ ++ def on_authorization_failure(error): ++ """Convert the DeferredException to a normal exception.""" ++ try: ++ logging.info("Authorization failed ...") ++ ++ self.sqlite3_server.purge_policy_keep = False ++ self.sqlite3_server.purge_policy_timestamp = 0 ++ ++ error.raise_exception() ++ except Exception as error: ++ logging.error(error) ++ self.dbus_send.PurgePackagesFinished(False,_("Cancel authentication."),'') ++ self.dbus_send.PurgePackagesFinishedWithPkgname(False,_("Cancel authentication."),'',",".join(pkgs_list)) ++ self.dbus_send.PurgePackagesFinishedWithErrCode(False,_("Cancel authentication."),'',",".join(pkgs_list),get_error_num_from_enum(ERROR_PURGE_PKG_VERIFY)) ++ ++ def _policykit_authority_handler(install_handler = None, authority_failed_handler = None): ++ if not self.sqlite3_server.purge_policy_keep: ++ deferred = PolicyKit_Authority( ++ get_source_name_from_enum(sender_name)+_(" requires authentication to uninstall software packages."), ++ sender = sender, ++ authentication = self.configs_uncover.getWithDefault("InstallAndPurge","purge_authority",False), ++ source=sender_name) ++ ++ deferred.add_callback(on_authorization_success) ++ deferred.add_errback(on_authorization_failure) ++ else: ++ self.sqlite3_server.purge_policy_timestamp = 60 * 5 ++ logging.info("Purge policy keep, ignore...") ++ ++ _run_purge_handler() ++ ++ _policykit_authority_handler() ++ ++ except Exception as e: ++ logging.error("start_purge_pkgs error: %s", e) ++ ++ def start_back_upgrade(self, pkgs_list, sender=None): ++ _success = False ++ ++ try: ++ self.updater_package.batchInstPkgs = [str(item) for item in pkgs_list] ++ ++ _success = self.updater_package.package_interface_batch_install(pkgs_list) ++ if self.updater_package.batchInstFinished: ++ self.updater_package.batchInstFinished = False ++ else: ++ self.dbus_send.UpdateInstallFinished(_success, self.updater_package.batchInstPkgs, '', '') ++ self.updater_package.package_interface_install_clear() ++ ++ except Exception as e: ++ logging.error("start_back_upgrade err: %s", e) ++ ++ def check_package_status(self, _name, _version = '', sender = None): ++ install_status = -100 ++ ++ try: ++ if not _version: ++ _version = 'NULL' ++ ++ install_status = self.updater_package.package_interface_check_status(_name, _version) ++ ++ except Exception as e: ++ logging.error("check_package_status err: %s", e) ++ ++ return install_status +diff --git a/backend-immutable/SystemUpdater/pluginbase.py b/backend-immutable/SystemUpdater/pluginbase.py +new file mode 100644 +index 0000000..0370f17 +--- /dev/null ++++ b/backend-immutable/SystemUpdater/pluginbase.py +@@ -0,0 +1,449 @@ ++# -*- coding: utf-8 -*- ++""" ++ pluginbase ++ ~~~~~~~~~~ ++ ++ Pluginbase is a module for Python that provides a system for building ++ plugin based applications. ++ ++ :copyright: (c) Copyright 2014 by Armin Ronacher. ++ :license: BSD, see LICENSE for more details. ++""" ++import os ++import sys ++import uuid ++import errno ++import pkgutil ++import hashlib ++import threading ++ ++from types import ModuleType ++from weakref import ref as weakref ++ ++ ++PY2 = sys.version_info[0] == 2 ++if PY2: ++ text_type = unicode ++ string_types = (unicode, str) ++ from cStringIO import StringIO as NativeBytesIO ++else: ++ text_type = str ++ string_types = (str,) ++ from io import BytesIO as NativeBytesIO ++ ++ ++__version__ = '1.0.1' ++_local = threading.local() ++ ++_internalspace = ModuleType(__name__ + '._internalspace') ++_internalspace.__path__ = [] ++sys.modules[_internalspace.__name__] = _internalspace ++ ++ ++def get_plugin_source(module=None, stacklevel=None): ++ """Returns the :class:`PluginSource` for the current module or the given ++ module. The module can be provided by name (in which case an import ++ will be attempted) or as a module object. ++ ++ If no plugin source can be discovered, the return value from this method ++ is `None`. ++ ++ This function can be very useful if additional data has been attached ++ to the plugin source. For instance this could allow plugins to get ++ access to a back reference to the application that created them. ++ ++ :param module: optionally the module to locate the plugin source of. ++ :param stacklevel: defines how many levels up the module should search ++ for before it discovers the plugin frame. The ++ default is 0. This can be useful for writing wrappers ++ around this function. ++ """ ++ if module is None: ++ frm = sys._getframe((stacklevel or 0) + 1) ++ name = frm.f_globals['__name__'] ++ glob = frm.f_globals ++ elif isinstance(module, string_types): ++ frm = sys._getframe(1) ++ name = module ++ glob = __import__(module, frm.f_globals, ++ frm.f_locals, ['__dict__']).__dict__ ++ else: ++ name = module.__name__ ++ glob = module.__dict__ ++ return _discover_space(name, glob) ++ ++ ++def get_searchpath(path, depth=float('inf'), followlinks=False): ++ """This utility function returns a list directories suitable for use as the ++ *searchpath* argument to :class:`PluginSource`. This will recursively add ++ directories up to the specified depth. ++ ++ :param str path: The directory on the file system to start the search path ++ at. It will be included in the result. ++ :param int depth: The number of directories to recurse into while building ++ the search path. By default the function will iterate into ++ all child directories. ++ :param bool followlinks: Whether or not to recurse into directories which ++ are symbolic links. ++ :return: A list of directories, including *path* and child directories. ++ :rtype: list ++ """ ++ # os.walk implements a depth-first approach which results in unnecessarily ++ # slow execution when *path* is a large tree and *depth* is a small number ++ paths = [path] ++ for dir_entry in os.listdir(path): ++ sub_path = os.path.join(path, dir_entry) ++ if not os.path.isdir(sub_path): ++ continue ++ if not followlinks and os.path.islink(sub_path): ++ continue ++ if depth: ++ paths.extend(get_searchpath(sub_path, depth - 1, followlinks)) ++ return paths ++ ++ ++def _discover_space(name, globals): ++ try: ++ return _local.space_stack[-1] ++ except (AttributeError, IndexError): ++ pass ++ ++ if '__pluginbase_state__' in globals: ++ return globals['__pluginbase_state__'].source ++ ++ mod_name = globals.get('__name__') ++ if mod_name is not None and \ ++ mod_name.startswith(_internalspace.__name__ + '.'): ++ end = mod_name.find('.', len(_internalspace.__name__) + 1) ++ space = sys.modules.get(mod_name[:end]) ++ if space is not None: ++ return space.__pluginbase_state__.source ++ ++ ++def _shutdown_module(mod): ++ members = list(mod.__dict__.items()) ++ for key, value in members: ++ if key[:1] != '_': ++ setattr(mod, key, None) ++ for key, value in members: ++ setattr(mod, key, None) ++ ++ ++def _to_bytes(s): ++ if isinstance(s, text_type): ++ return s.encode('utf-8') ++ return s ++ ++ ++class _IntentionallyEmptyModule(ModuleType): ++ ++ def __getattr__(self, name): ++ try: ++ return ModuleType.__getattr__(self, name) ++ except AttributeError: ++ if name[:2] == '__': ++ raise ++ raise RuntimeError( ++ 'Attempted to import from a plugin base module (%s) without ' ++ 'having a plugin source activated. To solve this error ' ++ 'you have to move the import into a "with" block of the ' ++ 'associated plugin source.' % self.__name__) ++ ++ ++class _PluginSourceModule(ModuleType): ++ ++ def __init__(self, source): ++ modname = '%s.%s' % (_internalspace.__name__, source.spaceid) ++ ModuleType.__init__(self, modname) ++ self.__pluginbase_state__ = PluginBaseState(source) ++ ++ @property ++ def __path__(self): ++ try: ++ ps = self.__pluginbase_state__.source ++ except AttributeError: ++ return [] ++ return ps.searchpath + ps.base.searchpath ++ ++ ++def _setup_base_package(module_name): ++ try: ++ mod = __import__(module_name, None, None, ['__name__']) ++ except ImportError: ++ mod = None ++ if '.' in module_name: ++ parent_mod = __import__(module_name.rsplit('.', 1)[0], ++ None, None, ['__name__']) ++ else: ++ parent_mod = None ++ ++ if mod is None: ++ mod = _IntentionallyEmptyModule(module_name) ++ if parent_mod is not None: ++ setattr(parent_mod, module_name.rsplit('.', 1)[-1], mod) ++ sys.modules[module_name] = mod ++ ++ ++class PluginBase(object): ++ """The plugin base acts as a control object around a dummy Python ++ package that acts as a container for plugins. Usually each ++ application creates exactly one base object for all plugins. ++ ++ :param package: the name of the package that acts as the plugin base. ++ Usually this module does not exist. Unless you know ++ what you are doing you should not create this module ++ on the file system. ++ :param searchpath: optionally a shared search path for modules that ++ will be used by all plugin sources registered. ++ """ ++ ++ def __init__(self, package, searchpath=None): ++ #: the name of the dummy package. ++ self.package = package ++ if searchpath is None: ++ searchpath = [] ++ #: the default search path shared by all plugins as list. ++ self.searchpath = searchpath ++ _setup_base_package(package) ++ ++ def make_plugin_source(self, *args, **kwargs): ++ """Creates a plugin source for this plugin base and returns it. ++ All parameters are forwarded to :class:`PluginSource`. ++ """ ++ return PluginSource(self, *args, **kwargs) ++ ++ ++class PluginSource(object): ++ """The plugin source is what ultimately decides where plugins are ++ loaded from. Plugin bases can have multiple plugin sources which act ++ as isolation layer. While this is not a security system it generally ++ is not possible for plugins from different sources to accidentally ++ cross talk. ++ ++ Once a plugin source has been created it can be used in a ``with`` ++ statement to change the behavior of the ``import`` statement in the ++ block to define which source to load the plugins from:: ++ ++ plugin_source = plugin_base.make_plugin_source( ++ searchpath=['./path/to/plugins', './path/to/more/plugins']) ++ ++ with plugin_source: ++ from myapplication.plugins import my_plugin ++ ++ :param base: the base this plugin source belongs to. ++ :param identifier: optionally a stable identifier. If it's not defined ++ a random identifier is picked. It's useful to set this ++ to a stable value to have consistent tracebacks ++ between restarts and to support pickle. ++ :param searchpath: a list of paths where plugins are looked for. ++ :param persist: optionally this can be set to `True` and the plugins ++ will not be cleaned up when the plugin source gets ++ garbage collected. ++ """ ++ # Set these here to false by default so that a completely failing ++ # constructor does not fuck up the destructor. ++ persist = False ++ mod = None ++ ++ def __init__(self, base, identifier=None, searchpath=None, ++ persist=False): ++ #: indicates if this plugin source persists or not. ++ self.persist = persist ++ if identifier is None: ++ identifier = str(uuid.uuid4()) ++ #: the identifier for this source. ++ self.identifier = identifier ++ #: A reference to the plugin base that created this source. ++ self.base = base ++ #: a list of paths where plugins are searched in. ++ self.searchpath = searchpath ++ #: The internal module name of the plugin source as it appears ++ #: in the :mod:`pluginsource._internalspace`. ++ self.spaceid = '_sp' + hashlib.md5( ++ _to_bytes(self.base.package) + b'|' + ++ _to_bytes(identifier), ++ ).hexdigest() ++ #: a reference to the module on the internal ++ #: :mod:`pluginsource._internalspace`. ++ self.mod = _PluginSourceModule(self) ++ ++ if hasattr(_internalspace, self.spaceid): ++ raise RuntimeError('This plugin source already exists.') ++ sys.modules[self.mod.__name__] = self.mod ++ setattr(_internalspace, self.spaceid, self.mod) ++ ++ def __del__(self): ++ if not self.persist: ++ self.cleanup() ++ ++ def list_plugins(self): ++ """Returns a sorted list of all plugins that are available in this ++ plugin source. This can be useful to automatically discover plugins ++ that are available and is usually used together with ++ :meth:`load_plugin`. ++ """ ++ rv = [] ++ for _, modname, ispkg in pkgutil.iter_modules(self.mod.__path__): ++ rv.append(modname) ++ return sorted(rv) ++ ++ def load_plugin(self, name): ++ """This automatically loads a plugin by the given name from the ++ current source and returns the module. This is a convenient ++ alternative to the import statement and saves you from invoking ++ ``__import__`` or a similar function yourself. ++ ++ :param name: the name of the plugin to load. ++ """ ++ if '.' in name: ++ raise ImportError('Plugin names cannot contain dots.') ++ with self: ++ return __import__(self.base.package + '.' + name, ++ globals(), {}, ['__name__']) ++ ++ def open_resource(self, plugin, filename): ++ """This function locates a resource inside the plugin and returns ++ a byte stream to the contents of it. If the resource cannot be ++ loaded an :exc:`IOError` will be raised. Only plugins that are ++ real Python packages can contain resources. Plain old Python ++ modules do not allow this for obvious reasons. ++ ++ .. versionadded:: 0.3 ++ ++ :param plugin: the name of the plugin to open the resource of. ++ :param filename: the name of the file within the plugin to open. ++ """ ++ mod = self.load_plugin(plugin) ++ fn = getattr(mod, '__file__', None) ++ if fn is not None: ++ if fn.endswith(('.pyc', '.pyo')): ++ fn = fn[:-1] ++ if os.path.isfile(fn): ++ return open(os.path.join(os.path.dirname(fn), filename), 'rb') ++ buf = pkgutil.get_data(self.mod.__name__ + '.' + plugin, filename) ++ if buf is None: ++ raise IOError(errno.ENOENT, 'Could not find resource') ++ return NativeBytesIO(buf) ++ ++ def cleanup(self): ++ """Cleans up all loaded plugins manually. This is necessary to ++ call only if :attr:`persist` is enabled. Otherwise this happens ++ automatically when the source gets garbage collected. ++ """ ++ self.__cleanup() ++ ++ def __cleanup(self, _sys=sys, _shutdown_module=_shutdown_module): ++ # The default parameters are necessary because this can be fired ++ # from the destructor and so late when the interpreter shuts down ++ # that these functions and modules might be gone. ++ if self.mod is None or self.mod.__name__ is None: ++ return ++ modname = self.mod.__name__ ++ self.mod.__pluginbase_state__ = None ++ self.mod = None ++ try: ++ delattr(_internalspace, self.spaceid) ++ except AttributeError: ++ pass ++ prefix = modname + '.' ++ # avoid the bug described in issue #6 ++ if modname in _sys.modules: ++ del _sys.modules[modname] ++ for key, value in list(_sys.modules.items()): ++ if not key.startswith(prefix): ++ continue ++ mod = _sys.modules.pop(key, None) ++ if mod is None: ++ continue ++ _shutdown_module(mod) ++ ++ def __assert_not_cleaned_up(self): ++ if self.mod is None: ++ raise RuntimeError('The plugin source was already cleaned up.') ++ ++ def __enter__(self): ++ self.__assert_not_cleaned_up() ++ _local.__dict__.setdefault('space_stack', []).append(self) ++ return self ++ ++ def __exit__(self, exc_type, exc_value, tb): ++ try: ++ _local.space_stack.pop() ++ except (AttributeError, IndexError): ++ pass ++ ++ def _rewrite_module_path(self, modname): ++ self.__assert_not_cleaned_up() ++ if modname == self.base.package: ++ return self.mod.__name__ ++ elif modname.startswith(self.base.package + '.'): ++ pieces = modname.split('.') ++ return self.mod.__name__ + '.' + '.'.join( ++ pieces[self.base.package.count('.') + 1:]) ++ ++ ++class PluginBaseState(object): ++ __slots__ = ('_source',) ++ ++ def __init__(self, source): ++ if source.persist: ++ self._source = lambda: source ++ else: ++ self._source = weakref(source) ++ ++ @property ++ def source(self): ++ rv = self._source() ++ if rv is None: ++ raise AttributeError('Plugin source went away') ++ return rv ++ ++ ++class _ImportHook(ModuleType): ++ ++ def __init__(self, name, system_import): ++ ModuleType.__init__(self, name) ++ self._system_import = system_import ++ self.enabled = True ++ ++ def enable(self): ++ """Enables the import hook which drives the plugin base system. ++ This is the default. ++ """ ++ self.enabled = True ++ ++ def disable(self): ++ """Disables the import hook and restores the default import system ++ behavior. This effectively breaks pluginbase but can be useful ++ for testing purposes. ++ """ ++ self.enabled = False ++ ++ def plugin_import(self, name, globals=None, locals=None, ++ fromlist=None, level=None): ++ if level is None: ++ # set the level to the default value specific to this python version ++ level = -1 if PY2 else 0 ++ import_name = name ++ if self.enabled: ++ ref_globals = globals ++ if ref_globals is None: ++ ref_globals = sys._getframe(1).f_globals ++ space = _discover_space(name, ref_globals) ++ if space is not None: ++ actual_name = space._rewrite_module_path(name) ++ if actual_name is not None: ++ import_name = actual_name ++ ++ return self._system_import(import_name, globals, locals, ++ fromlist, level) ++ ++ ++try: ++ import __builtin__ as builtins ++except ImportError: ++ import builtins ++import_hook = _ImportHook(__name__ + '.import_hook', builtins.__import__) ++builtins.__import__ = import_hook.plugin_import ++sys.modules[import_hook.__name__] = import_hook ++del builtins +diff --git a/backend-immutable/SystemUpdater/plugins/update/randomstr.py b/backend-immutable/SystemUpdater/plugins/update/randomstr.py +new file mode 100644 +index 0000000..268b4c1 +--- /dev/null ++++ b/backend-immutable/SystemUpdater/plugins/update/randomstr.py +@@ -0,0 +1,11 @@ ++import random ++import string ++import logging ++ ++def make_random(parent,trans): ++ logging.info("Processing transaction") ++ logging.info("%sdddddddddddddd",trans.sender) ++ return trans ++ ++def setup(app): ++ app.register_formatter('random', make_random) +diff --git a/backend-immutable/SystemUpdater/push_update.py b/backend-immutable/SystemUpdater/push_update.py +new file mode 100644 +index 0000000..d4b006d +--- /dev/null ++++ b/backend-immutable/SystemUpdater/push_update.py +@@ -0,0 +1,83 @@ ++import logging ++import dbus ++from dataclasses import dataclass ++from .Core.errors import * ++from .Core.enums import * ++from SystemUpdater.constants import PUTSH_CONTENT_LOCATION,DEFAULT_REPO_NAME ++from gettext import gettext as _ ++ ++@dataclass ++class UpdateContent: ++ repo_name: str = DEFAULT_REPO_NAME ++ branch: str = None ++ revision: str = None ++ ++#更新important.list的本次升级的列表 ++def update_important(bus,reply_handler,error_handler): ++ def _update_important_reply(retval): ++ if bool(retval) == False: ++ error_handler(ERROR_UPDATE_SOURCE_FAILED) ++ else: ++ push_content = _get_push_content() ++ reply_handler(push_content) ++ def _update_important_error(retval): ++ logging.error(str(retval)) ++ error_handler(ERROR_UPDATE_SOURCE_FAILED) ++ ++ try: ++ obj = bus.get_object('com.kylin.software.properties', '/com/kylin/software/properties') ++ ++ interface = dbus.Interface(obj, dbus_interface='com.kylin.software.properties.interface') ++ interface.updateSourceTemplate(timeout=20,reply_handler=_update_important_reply,error_handler=_update_important_error) ++ except Exception as e: ++ logging.warning(str(e)) ++ raise UpdateBaseError(ERROR_UPDATE_SOURCE_FAILED) ++ ++def _read_important_data(): ++ # 当存在源管理推送内容时 优先从源管理获取当前推送的分支信息 ++ try: ++ push_list = [] ++ with open(PUTSH_CONTENT_LOCATION, 'r') as f: ++ data = f.read() ++ push_list = data.split() ++ ++ #去除重复内容 ++ push_list = list(set(push_list)) ++ ++ logging.info("Server push list: %r",push_list) ++ return push_list ++ except Exception as e: ++ logging.warning(str(e)) ++ return [] ++ ++def _parse_push_content(push_content): ++ # 推送的内容格式: ++ # 1、带有commit号时拉取分支指定commit,仓库名称:分支名称@commit号 示例:kylin:kylin/desktop/v11/2503/xc/general/all/beta2/amd64@e46fb4558c5e0d2724af ++ # 2、不带有commit号时拉取分支最新commit,kylin:kylin/2203/230 示例:kylin:kylin/desktop/v11/2503/xc/general/all/beta2/amd64 ++ update_content = UpdateContent() ++ try: ++ refspec = push_content.split(":") ++ if len(refspec) == 2: ++ update_content.repo_name = refspec[0] ++ ref_and_rev = refspec[1] ++ if "@" in ref_and_rev: ++ refs_details = ref_and_rev.split("@") ++ update_content.branch = refs_details[0] ++ update_content.revision = refs_details[1] ++ else: ++ update_content.branch = ref_and_rev ++ return [update_content] ++ else: ++ logging.error("Push updates do not contain the repo name...") ++ return [] ++ except Exception as e: ++ logging.warning(str(e)) ++ return [] ++ ++def _get_push_content(): ++ update_list = [] ++ push_list = _read_important_data() ++ for cont in push_list: ++ update_cont = _parse_push_content(cont) ++ update_list = update_cont + update_list ++ return update_list +diff --git a/backend-immutable/data/com.kylin.UpgradeStrategies.limit b/backend-immutable/data/com.kylin.UpgradeStrategies.limit +new file mode 100644 +index 0000000..148dccd +--- /dev/null ++++ b/backend-immutable/data/com.kylin.UpgradeStrategies.limit +@@ -0,0 +1,33 @@ ++[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 ++key14 = /usr/share/kylin-system-updater/kylin-system-updater ++key15 = /usr/bin/kylin-updateresult-notify-new ++key16 = /usr/bin/ukui-menu ++key17 = /usr/bin/ukui-session ++key18 = /usr/bin/NotifySend ++key19 = /usr/bin/kylin-scanner ++key20 = /usr/bin/kylin-os-manager ++key21 = /usr/sbin/ksc-scan-daemon ++key22 = /usr/sbin/preinstall-daemon ++key23 = /usr/bin/ukui-session-tools ++key24 = /usr/bin/kylin-unattended-upgrade-shutdown ++key25 = /usr/sbin/kimcd ++key26 = /usr/bin/hedron-domain-hook.py ++key27 = /usr/bin/NotifySend (deleted) ++key28 = /usr/bin/kylin-updatefinish-notify-new ++key29 = /usr/bin/UpgradeRebootNotify ++key30 = /usr/bin/peony-qt-desktop ++key31 = /usr/bin/ukui-panel ++key32 = /usr/bin/kylin-police +\ No newline at end of file +diff --git a/backend-immutable/data/com.kylin.UpgradeStrategies.limit.verify b/backend-immutable/data/com.kylin.UpgradeStrategies.limit.verify +new file mode 100644 +index 0000000..1666589 +--- /dev/null ++++ b/backend-immutable/data/com.kylin.UpgradeStrategies.limit.verify +@@ -0,0 +1 @@ ++7fa6c5b19ae662c3221d42ef41193e90bffd6a34 +\ No newline at end of file +diff --git a/backend-immutable/data/settings-default.json b/backend-immutable/data/settings-default.json +new file mode 100644 +index 0000000..28036d1 +--- /dev/null ++++ b/backend-immutable/data/settings-default.json +@@ -0,0 +1,3 @@ ++{ ++ "status": "none" ++} +\ No newline at end of file +diff --git a/backend-immutable/kylin-system-updater b/backend-immutable/kylin-system-updater +index 607ba04..baf60ca 100755 +--- a/backend-immutable/kylin-system-updater ++++ b/backend-immutable/kylin-system-updater +@@ -10,16 +10,15 @@ import signal + import os + import sys + import gettext +- ++from SystemUpdater.configs import settings + from SystemUpdater.Core.LogManager import get_logfile as logfile +- ++from SystemUpdater.constants import DEFAULT_OS_NAME + 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): +@@ -40,13 +39,16 @@ if __name__ == "__main__": + parser.add_option("", "--sysroot", default=None, + action="store", type="string", dest="sysroot", + help=_("Import sysroot path in the given path")) +- parser.add_option("", "--os", default=None, ++ parser.add_option("", "--os", default=DEFAULT_OS_NAME, + action="store", type="string", dest="os", + help=_("Import os name in the given string")) + parser.add_option("-p", "--prohibit-shutdown", + default=False, + action="store_true", dest="prohibit_shutdown", + help=_("close auto shutdown")) ++ parser.add_option ("-n","--no-update-source", action="store_false", ++ dest="update_source", default=True, ++ help=_("Do not check for updates source when updating")) + parser.add_option("--no-check-network", + default=True, + action="store_false", dest="check_network", +@@ -54,6 +56,9 @@ if __name__ == "__main__": + + (options, args) = parser.parse_args() + ++ # 将命令行参数 加载到settings的配置项中 ++ settings.update(vars(options)) ++ + if os.getuid() != 0: + print(_("You need to be root to run this application")) + sys.exit(1) +diff --git a/backend-immutable/kylin-upgrade-strategies b/backend-immutable/kylin-upgrade-strategies +index a343f46..c60e461 100755 +--- a/backend-immutable/kylin-upgrade-strategies ++++ b/backend-immutable/kylin-upgrade-strategies +@@ -25,7 +25,7 @@ FORMAT_DEBUG = '%(asctime)-15s %(levelname)s(%(filename)s:%(lineno)d):%(message) + def signal_handler_term(signal, frame): + # type: (int, object) -> None + logging.warning("SIGTERM received, will stop") +- app.dbusController.Quit(None) ++ app.dbus_send.Quit(None) + + if __name__ == "__main__": + # Begin parsing of options +diff --git a/backend-immutable/po/bo_CN.po b/backend-immutable/po/bo_CN.po +index c945e0c..be65457 100644 +--- a/backend-immutable/po/bo_CN.po ++++ b/backend-immutable/po/bo_CN.po +@@ -236,3 +236,7 @@ msgstr "སྒྲིག་སྦྱོར་ཕམ་པ། མཉེན་ཆ + + msgid "Application installation in unknown mode ." + msgstr "ཉེར་སྤྱོད་སྒྲིག་སྦྱོར་དང་དོ་དམ་ཚོད་འཛིན་གྱི་ཐབས་ཇུས་མི་ཤེས་པ།" ++ ++#: ../SystemUpdater/Core/utils.py:753 ++msgid "Cancel authentication." ++msgstr "ཕྱིར་འབུད་བྱ་རྒྱུ།" +diff --git a/backend-immutable/po/zh_CN.po b/backend-immutable/po/zh_CN.po +index 0e0faa1..9a401f2 100644 +--- a/backend-immutable/po/zh_CN.po ++++ b/backend-immutable/po/zh_CN.po +@@ -2795,8 +2795,6 @@ msgstr "安装失败!软件包在黑名单列表!" + msgid "Application installation in unknown mode ." + msgstr "应用安装管控策略未知." + +- +- + msgid "Other tasks are being updated and upgraded, please try again later." + msgstr "其他任务正在更新升级中,请稍后再试" + +@@ -2807,4 +2805,17 @@ msgid "There is no way to rollback to the previous version, there is nothing to + msgstr "无法回滚到上一个版本,没有可回滚内容。" + + msgid "Check for update exceptions! Please verify that the system deployment is complete." +-msgstr "检查更新异常!请检查系统部署是否完整。" +\ No newline at end of file ++msgstr "检查更新异常!请检查系统部署是否完整。" ++ ++msgid "Update push is abnormal, and the corresponding branch information cannot be found on the server." ++msgstr "更新推送异常,未在服务端找到相应分支信息。" ++ ++msgid "Name of the repository deployed by the current system is empty." ++msgstr "更新检查异常,当前系统部署分支仓库所属信息为空。" ++ ++#: ../SystemUpdater/Core/utils.py:753 ++msgid "Cancel authentication." ++msgstr "取消认证" ++ ++msgid "System upgrade error" ++msgstr "系统更新异常" +diff --git a/backend-immutable/po/zh_HK.po b/backend-immutable/po/zh_HK.po +index 0ff5ead..4b147fd 100644 +--- a/backend-immutable/po/zh_HK.po ++++ b/backend-immutable/po/zh_HK.po +@@ -2706,3 +2706,7 @@ msgstr "安裝失敗!軟件包在黑名單列表!" + + msgid "Application installation in unknown mode ." + msgstr "應用安裝管控策略未知." ++ ++#: ../SystemUpdater/Core/utils.py:753 ++msgid "Cancel authentication." ++msgstr "取消認證" +diff --git a/backend-immutable/po/zh_TW.po b/backend-immutable/po/zh_TW.po +index 2a55e08..412d059 100644 +--- a/backend-immutable/po/zh_TW.po ++++ b/backend-immutable/po/zh_TW.po +@@ -2738,3 +2738,7 @@ msgstr "安裝失敗!軟件包在黑名單列表!" + + msgid "Application installation in unknown mode ." + msgstr "應用安裝管控策略未知." ++ ++#: ../SystemUpdater/Core/utils.py:753 ++msgid "Cancel authentication." ++msgstr "取消認證" +diff --git a/backend-immutable/tests/data/cn.kylinos.KylinSystemUpdater.policy b/backend-immutable/tests/data/cn.kylinos.KylinSystemUpdater.policy +new file mode 100644 +index 0000000..2195a39 +--- /dev/null ++++ b/backend-immutable/tests/data/cn.kylinos.KylinSystemUpdater.policy +@@ -0,0 +1,129 @@ ++<?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> ++ ++ <!--Kylin Installer Config--> ++ <action id="cn.kylin.installer.action"> ++ <_description> ++ Configuration items added for Kirin Installer ++ </_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> ++ ++ <action id="cn.kylin.uninstaller.action"> ++ <_description> ++ Configuration items added for Kirin Uninstaller ++ </_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> ++ ++ <!--Kylin System Updater Config--> ++ <action id="cn.kylinos.KylinSystemUpdater.action"> ++ <_description> ++ Configuration items added for Kirin Installer ++ </_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> ++ ++ <!--Kylin Software Center Config--> ++ <action id="cn.kylin.software.center.action"> ++ <_description> ++ Configuration items added for Kylin Software Center ++ </_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> ++ ++ <!--Kylin Installer Config--> ++ <action id="cn.kylin.installer.self.action"> ++ <_description> ++ Configuration items added for Kirin Installer ++ </_description> ++ <_message> ++ To Change the settings, you need to authenticate. ++ </_message> ++ <defaults> ++ <allow_any>auth_self_keep</allow_any> ++ <allow_inactive>auth_self_keep</allow_inactive> ++ <allow_active>auth_self_keep</allow_active> ++ </defaults> ++ </action> ++ ++ <action id="cn.kylin.uninstaller.self.action"> ++ <_description> ++ Configuration items added for Kirin Uninstaller ++ </_description> ++ <_message> ++ To Change the settings, you need to authenticate. ++ </_message> ++ <defaults> ++ <allow_any>auth_self_keep</allow_any> ++ <allow_inactive>auth_self_keep</allow_inactive> ++ <allow_active>auth_self_keep</allow_active> ++ </defaults> ++ </action> ++ ++ <!--Kylin System Updater Config--> ++ <action id="cn.kylinos.KylinSystemUpdater.self.action"> ++ <_description> ++ Configuration items added for Kirin Installer ++ </_description> ++ <_message> ++ To Change the settings, you need to authenticate. ++ </_message> ++ <defaults> ++ <allow_any>auth_self_keep</allow_any> ++ <allow_inactive>auth_self_keep</allow_inactive> ++ <allow_active>auth_self_keep</allow_active> ++ </defaults> ++ </action> ++ ++ <!--Kylin Software Center Config--> ++ <action id="cn.kylin.software.center.self.action"> ++ <_description> ++ Configuration items added for Kylin Software Center ++ </_description> ++ <_message> ++ To Change the settings, you need to authenticate. ++ </_message> ++ <defaults> ++ <allow_any>auth_self_keep</allow_any> ++ <allow_inactive>auth_self_keep</allow_inactive> ++ <allow_active>auth_self_keep</allow_active> ++ </defaults> ++ </action> ++ ++</policyconfig> +diff --git a/backend-immutable/tests/data/cn.kylinos.UpgradeStrategies.policy b/backend-immutable/tests/data/cn.kylinos.UpgradeStrategies.policy +new file mode 100644 +index 0000000..debbe26 +--- /dev/null ++++ b/backend-immutable/tests/data/cn.kylinos.UpgradeStrategies.policy +@@ -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> +diff --git a/backend-immutable/tests/data/com.kylin.UpgradeStrategies.conf b/backend-immutable/tests/data/com.kylin.UpgradeStrategies.conf +new file mode 100644 +index 0000000..9142c8c +--- /dev/null ++++ b/backend-immutable/tests/data/com.kylin.UpgradeStrategies.conf +@@ -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> +diff --git a/backend-immutable/tests/data/com.kylin.UpgradeStrategies.limit b/backend-immutable/tests/data/com.kylin.UpgradeStrategies.limit +new file mode 100644 +index 0000000..148dccd +--- /dev/null ++++ b/backend-immutable/tests/data/com.kylin.UpgradeStrategies.limit +@@ -0,0 +1,33 @@ ++[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 ++key14 = /usr/share/kylin-system-updater/kylin-system-updater ++key15 = /usr/bin/kylin-updateresult-notify-new ++key16 = /usr/bin/ukui-menu ++key17 = /usr/bin/ukui-session ++key18 = /usr/bin/NotifySend ++key19 = /usr/bin/kylin-scanner ++key20 = /usr/bin/kylin-os-manager ++key21 = /usr/sbin/ksc-scan-daemon ++key22 = /usr/sbin/preinstall-daemon ++key23 = /usr/bin/ukui-session-tools ++key24 = /usr/bin/kylin-unattended-upgrade-shutdown ++key25 = /usr/sbin/kimcd ++key26 = /usr/bin/hedron-domain-hook.py ++key27 = /usr/bin/NotifySend (deleted) ++key28 = /usr/bin/kylin-updatefinish-notify-new ++key29 = /usr/bin/UpgradeRebootNotify ++key30 = /usr/bin/peony-qt-desktop ++key31 = /usr/bin/ukui-panel ++key32 = /usr/bin/kylin-police +\ No newline at end of file +diff --git a/backend-immutable/tests/data/com.kylin.UpgradeStrategies.limit.verify b/backend-immutable/tests/data/com.kylin.UpgradeStrategies.limit.verify +new file mode 100644 +index 0000000..1666589 +--- /dev/null ++++ b/backend-immutable/tests/data/com.kylin.UpgradeStrategies.limit.verify +@@ -0,0 +1 @@ ++7fa6c5b19ae662c3221d42ef41193e90bffd6a34 +\ No newline at end of file +diff --git a/backend-immutable/tests/data/com.kylin.UpgradeStrategies.service b/backend-immutable/tests/data/com.kylin.UpgradeStrategies.service +new file mode 100644 +index 0000000..7d913e8 +--- /dev/null ++++ b/backend-immutable/tests/data/com.kylin.UpgradeStrategies.service +@@ -0,0 +1,4 @@ ++[D-BUS Service] ++Name=com.kylin.UpgradeStrategies ++Exec=/usr/share/kylin-system-updater/kylin-upgrade-strategies ++User=root +\ No newline at end of file +diff --git a/backend-immutable/tests/data/com.kylin.systemupgrade.conf b/backend-immutable/tests/data/com.kylin.systemupgrade.conf +new file mode 100644 +index 0000000..0408835 +--- /dev/null ++++ b/backend-immutable/tests/data/com.kylin.systemupgrade.conf +@@ -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> +diff --git a/backend-immutable/tests/data/com.kylin.systemupgrade.limit b/backend-immutable/tests/data/com.kylin.systemupgrade.limit +new file mode 100644 +index 0000000..2d80c3a +--- /dev/null ++++ b/backend-immutable/tests/data/com.kylin.systemupgrade.limit +@@ -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 +\ No newline at end of file +diff --git a/backend-immutable/tests/data/inhibit-sleep.conf b/backend-immutable/tests/data/inhibit-sleep.conf +new file mode 100644 +index 0000000..a4b5275 +--- /dev/null ++++ b/backend-immutable/tests/data/inhibit-sleep.conf +@@ -0,0 +1,25 @@ ++# This file is part of systemd. ++# ++# systemd is free software; you can redistribute it and/or modify it ++# under the terms of the GNU Lesser General Public License as published by ++# the Free Software Foundation; either version 2.1 of the License, or ++# (at your option) any later version. ++# ++# Entries in this file show the compile time defaults. ++# You can change settings by editing this file. ++# Defaults can be restored by simply deleting this file. ++# ++# See systemd-sleep.conf(5) for details ++ ++[Sleep] ++AllowSuspend=no ++AllowHibernation=no ++#AllowSuspendThenHibernate=yes ++#AllowHybridSleep=yes ++#SuspendMode= ++#SuspendState=mem standby freeze ++#HibernateMode=platform shutdown ++#HibernateState=disk ++#HybridSleepMode=suspend platform shutdown ++#HybridSleepState=disk ++#HibernateDelaySec=180min +diff --git a/backend-immutable/tests/data/kylin-system-updater b/backend-immutable/tests/data/kylin-system-updater +new file mode 100644 +index 0000000..3ce1e87 +--- /dev/null ++++ b/backend-immutable/tests/data/kylin-system-updater +@@ -0,0 +1,10 @@ ++/var/log/kylin-system-updater/kylin-system-updater.log.1 ++{ ++ weekly ++ missingok ++ rotate 3 ++ compress ++ notifempty ++ maxsize 10M ++ copytruncate ++} +diff --git a/backend-immutable/tests/data/kylin-system-updater.db b/backend-immutable/tests/data/kylin-system-updater.db +new file mode 100644 +index 0000000..934a075 +--- /dev/null ++++ b/backend-immutable/tests/data/kylin-system-updater.db +@@ -0,0 +1,36 @@ ++SQLite format 3���@ ���g��� ++��������u�������������������������������������������������g�.?� �� ++t O� �� ++t � �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������5������������������ ��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������g&�%tabledisplaydisplayCREATE TABLE "display"( ++ id INT, ++ check_time TEXT, ++ update_time TEXT, ++ auto_check TEXT, ++ system_version TEXT, ++ auto_backup TEXT, ++ download_limit TEXT, ++ download_limit_value TEXT, ++ allow_unattended_upgrades_shutdown TEXT, ++ update_period TEXT, ++ firstmigration TEXT, ++ autoupdate_allow TEXT, ++ tid TEXT, ++ init_version TEXT)5%I#�indexsqlite_autoindex_updateinfos_1updateinfos�7$##�5tableupdateinfosupdateinfosCREATE TABLE "updateinfos"( ++ id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, ++ appname TEXT NOT NULL, ++ version TEXT Not NULL, ++ description TEXT, ++ date TEXT, ++ status TEXT, ++ keyword TEXT, ++ errorcode TEXT, ++ appname_cn TEXT, ++ status_cn TEXT, ++ changelog TEXT)P++Ytablesqlite_sequencesqlite_sequenceCREATE TABLE sqlite_sequence(name,seq)!�K�����������������������������������������������������������������������3!G!�indexsqlite_autoindex_tid_search_1tid_search�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������} !!�Etabletid_searchtid_searchCREATE TABLE tid_search( ++id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, ++key TEXT, ++tid TEXT ++) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������) �������falsefalsetruefalseNoneyes �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ++������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ��� ++������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#������* updateinfos��� displayPinstalled �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ++���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +\ No newline at end of file +diff --git a/backend-immutable/tests/data/kylin-system-updater.service b/backend-immutable/tests/data/kylin-system-updater.service +new file mode 100644 +index 0000000..df4c1f2 +--- /dev/null ++++ b/backend-immutable/tests/data/kylin-system-updater.service +@@ -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 +\ No newline at end of file +diff --git a/backend-immutable/tests/data/kylin-system-version.conf b/backend-immutable/tests/data/kylin-system-version.conf +new file mode 100644 +index 0000000..1d12ee6 +--- /dev/null ++++ b/backend-immutable/tests/data/kylin-system-version.conf +@@ -0,0 +1,3 @@ ++[SYSTEM] ++os_version = ++update_version = +\ No newline at end of file +diff --git a/backend-immutable/tests/data/settings-default.json b/backend-immutable/tests/data/settings-default.json +new file mode 100644 +index 0000000..28036d1 +--- /dev/null ++++ b/backend-immutable/tests/data/settings-default.json +@@ -0,0 +1,3 @@ ++{ ++ "status": "none" ++} +\ No newline at end of file +diff --git a/backend-immutable/tests/data/unattended-upgrades-policy.conf b/backend-immutable/tests/data/unattended-upgrades-policy.conf +new file mode 100644 +index 0000000..a95a67c +--- /dev/null ++++ b/backend-immutable/tests/data/unattended-upgrades-policy.conf +@@ -0,0 +1,44 @@ ++[autoUpgradePolicy] ++#自动更新的开关 ++autoUpgradeState = off ++ ++#预下载开关 ++preDownload = off ++ ++# 预下载的时间为时间段 例如:10:00-11:00 ++preDownloadTime = 09:00-10:00 ++ ++#添加检查更新的周期 以天为单位 ++updateDays = 30 ++ ++# timing 为定时下载 manaual手动下载 ++downloadMode = timing ++ ++# 下载的时间为时间段 例如:10:00-11:00 ++downloadTime = 20:00-08:00 ++ ++#安装存在定时timing 手动:manual 关机安装bshutdown ++installMode = bshutdown ++ ++#安装也为时间段 例如:00:00 ++installTime = 08:00-20:00 ++ ++#立即更新随机波动范围(单位:分钟) ++randomRange = 60 ++ ++#是否开启自动重启 以及自动重启时间可以调节 ++automaticReboot = off ++ ++#自动重启时间的调节 now为立即重启 重启时间调节 例如00:00 ++automaticRebootTime = now ++ ++#更新前是否进行备份 ++backupbeforeinstall = on ++ ++#下发的策略管控 ++[updateStrategiesManager] ++# 策略的开关 False:关 True:开 ++strategiesState = False ++ ++# 安装策略 default:默认模式 runtime:运行时安装 pre-poweroff:关机安装 ++installType = default +diff --git a/backend-immutable/tests/data/unattended-upgrades-timestamp b/backend-immutable/tests/data/unattended-upgrades-timestamp +new file mode 100644 +index 0000000..47eec8e +--- /dev/null ++++ b/backend-immutable/tests/data/unattended-upgrades-timestamp +@@ -0,0 +1,4 @@ ++[TimeStamp] ++predownload = 2023-7-1 00:00:00 ++download = 2023-7-1 00:00:00 ++install = 2023-7-1 00:00:00 +diff --git a/backend-immutable/tests/diskManager_unittest.py b/backend-immutable/tests/diskManager_unittest.py +new file mode 100644 +index 0000000..b4e8b13 +--- /dev/null ++++ b/backend-immutable/tests/diskManager_unittest.py +@@ -0,0 +1,30 @@ ++import unittest ++import sys ++import os ++from SystemUpdater.Core.errors import * ++ ++# 添加项目根目录到 sys.path ++from SystemUpdater.Core.diskManager import check_free_space ++class TestCalculator(unittest.TestCase): ++ # def test_diskManagerClassInit(self): ++ # testclass = diskManager() ++ # self.assertIsInstance(testclass, diskManager) ++ ++ def test_spaceCheckTrue(self): ++ result = check_free_space(1000,1000) ++ self.assertEqual(True, result) ++ ++ def test_spaceCheckFalse(self): ++ with self.assertRaises(UpdateBaseError): ++ result = check_free_space(9999999999999999,100000) ++ ++ def test_spaceCheckaddis0(self): ++ result = check_free_space(100000) ++ self.assertEqual(True, result) ++ ++ def test_spaceCheckaddnotis0(self): ++ with self.assertRaises(UpdateBaseError): ++ result = check_free_space(9999999999999999) ++ ++if __name__ == "__main__": ++ unittest.main() +\ No newline at end of file +diff --git a/backend-immutable/tests/enums.sh b/backend-immutable/tests/enums.sh +index 3c9c25a..c73eb29 100644 +--- a/backend-immutable/tests/enums.sh ++++ b/backend-immutable/tests/enums.sh +@@ -22,8 +22,16 @@ CMD_PREFIX="" + test_tmpdir="/tmp/ostree-auto-test" + + BUILD_MASTER_BRACH="testos/buildmaster/newbranch" +- ++file_to_monitor="/var/log/kylin-system-updater/kylin-system-updater.log.1" + http_port="8889" + process_name="python3 -m http.server "$http_port + +-metadata_string="--add-metadata=ostree.incrementsize='1220202' --add-metadata=update-name_zh_CN='系统更新' --add-metadata=update-name_en_US='dddddddddd' --add-metadata=description='GalaxyKylinV10SP1SystemUpdate' --add-metadata-string='icon=/usr/share/kylin-update-desktop-config/icon/kylin-update-desktop-system.png' --add-metadata-string=changelog='initialcommit' --add-metadata-string=total_download_size='800MB' --add-metadata-string=total_install_size='1.2GB' --add-metadata-string=update-type='kylin-update-desktop-system'" ++metadata_string="--add-metadata=download-size='8000000000' --add-metadata=install-size='111220202' --add-metadata=update-name_zh_CN='系统更新' --add-metadata=update-name_en_US='dddddddddd' --add-metadata=description='GalaxyKylinV10SP1SystemUpdate' --add-metadata-string='icon=/usr/share/kylin-update-desktop-config/icon/kylin-update-desktop-system.png' --add-metadata-string=changelog='initialcommit' --add-metadata-string=total_download_size='800MB' --add-metadata-string=total_install_size='1.2GB' --add-metadata-string=update-type='kylin-update-desktop-system'" ++ ++TAIL_LINE_NUM="3" ++ ++updater_program=$(pwd)/../kylin-system-updater ++ ++PUSH_DIR="/opt/kylin-software-properties" ++PUSH_FILE="$PUSH_DIR/ostree-important.list" ++OS_DATA="/tmp/ostree-auto-test/osdata" +\ No newline at end of file +diff --git a/backend-immutable/tests/interface.md b/backend-immutable/tests/interface.md +new file mode 100644 +index 0000000..addf4bc +--- /dev/null ++++ b/backend-immutable/tests/interface.md +@@ -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日志查看那个包存在问题 | ++| 下载软件包文件失败 | 网络原因的或者这个软件包的仓库 | 检查网络以及源仓库 | ++| 磁盘空间不足 | 磁盘的空间不足 | 查看日志那些目录空间不足 | ++| 不能计算升级 | 无法计算依赖关系 | 检查日志那个包出现的问题,相应解决 | ++| | | | ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/backend-immutable/tests/kylin-system-updater b/backend-immutable/tests/kylin-system-updater +new file mode 100755 +index 0000000..baf60ca +--- /dev/null ++++ b/backend-immutable/tests/kylin-system-updater +@@ -0,0 +1,80 @@ ++#!/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.configs import settings ++from SystemUpdater.Core.LogManager import get_logfile as logfile ++from SystemUpdater.constants import DEFAULT_OS_NAME ++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.dbus_send.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("", "--sysroot", default=None, ++ action="store", type="string", dest="sysroot", ++ help=_("Import sysroot path in the given path")) ++ parser.add_option("", "--os", default=DEFAULT_OS_NAME, ++ action="store", type="string", dest="os", ++ help=_("Import os name in the given string")) ++ parser.add_option("-p", "--prohibit-shutdown", ++ default=False, ++ action="store_true", dest="prohibit_shutdown", ++ help=_("close auto shutdown")) ++ parser.add_option ("-n","--no-update-source", action="store_false", ++ dest="update_source", default=True, ++ help=_("Do not check for updates source when updating")) ++ parser.add_option("--no-check-network", ++ default=True, ++ action="store_false", dest="check_network", ++ help=_("Quit and close check network")) ++ ++ (options, args) = parser.parse_args() ++ ++ # 将命令行参数 加载到settings的配置项中 ++ settings.update(vars(options)) ++ ++ if os.getuid() != 0: ++ print(_("You need to be root to run this application")) ++ sys.exit(1) ++ ++ # 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.INFO,datefmt='%m-%d,%H:%M:%S',filename = logfile(),filemode = 'a') ++ ++ logging.info('kylin-system-updater starting ...') ++ ++ app = UpdateManager(options) ++ ++ app.run() +\ No newline at end of file +diff --git a/backend-immutable/tests/kylin-upgrade-strategies b/backend-immutable/tests/kylin-upgrade-strategies +new file mode 100755 +index 0000000..c60e461 +--- /dev/null ++++ b/backend-immutable/tests/kylin-upgrade-strategies +@@ -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.dbus_send.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() +\ No newline at end of file +diff --git a/backend-immutable/tests/libtest-core.sh b/backend-immutable/tests/libtest-core.sh +index 945d285..0d8f9bf 100644 +--- a/backend-immutable/tests/libtest-core.sh ++++ b/backend-immutable/tests/libtest-core.sh +@@ -1,30 +1,3 @@ +-# Core source library for shell script tests; the +-# canonical version lives in: +-# +-# https://github.com/ostreedev/ostree +-# +-# Known copies are in the following repos: +-# +-# - https://github.com/projectatomic/rpm-ostree +-# +-# Copyright (C) 2017 Colin Walters <walters@verbum.org> +-# +-# SPDX-License-Identifier: LGPL-2.0+ +-# +-# This library is free software; you can redistribute it and/or +-# modify it under the terms of the GNU Lesser General Public +-# License as published by the Free Software Foundation; either +-# version 2 of the License, or (at your option) any later version. +-# +-# This library 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 +-# Lesser General Public License for more details. +-# +-# You should have received a copy of the GNU Lesser General Public +-# License along with this library; if not, write to the +-# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +-# Boston, MA 02111-1307, USA. + + fatal() { + echo $@ 1>&2; exit 1 +@@ -34,12 +7,6 @@ assert_not_reached () { + fatal "$@" + } + +-# Some tests look for specific English strings. Use a UTF-8 version +-# of the C (POSIX) locale if we have one, or fall back to en_US.UTF-8 +-# (https://sourceware.org/glibc/wiki/Proposals/C.UTF-8) +-# +-# If we can't find the locale command assume we have support for C.UTF-8 +-# (e.g. musl based systems) + if type -p locale >/dev/null; then + export LC_ALL=$(locale -a | grep -iEe '^(C|en_US)\.(UTF-8|utf8)$' | head -n1 || true) + if [ -z "${LC_ALL}" ]; then fatal "Can't find suitable UTF-8 locale"; fi +diff --git a/backend-immutable/tests/libtest.sh b/backend-immutable/tests/libtest.sh +index c04cbe9..d4b7459 100644 +--- a/backend-immutable/tests/libtest.sh ++++ b/backend-immutable/tests/libtest.sh +@@ -1,16 +1,128 @@ + . ./enums.sh + . ./libtest-core.sh + +-if [ ! -d "$test_tmpdir" ]; then +- mkdir -p "$test_tmpdir" +- echo "创建测试目录: $test_tmpdir" +-else +- echo "创建测试目录: $test_tmpdir" +- sudo chattr -R -i $test_tmpdir/* >/dev/null 2>&1 || true +- chmod -R 777 $test_tmpdir/* >/dev/null 2>&1 || true +- rm -rf $test_tmpdir +- mkdir -p "$test_tmpdir" +-fi ++TRUE=true ++FALSE=false ++ ++DEBUG="$FALSE" ++ ++for arg in "$@"; do ++ if [ "$arg" == "-d" ]; then ++ DEBUG="$TRUE" ++ # 如果需要执行某些操作,可以放在这里 ++ fi ++done ++ ++BOLD="$(tput bold 2>/dev/null || printf '')" ++GREY="$(tput setaf 0 2>/dev/null || printf '')" ++UNDERLINE="$(tput smul 2>/dev/null || printf '')" ++RED="$(tput setaf 1 2>/dev/null || printf '')" ++GREEN="$(tput setaf 2 2>/dev/null || printf '')" ++YELLOW="$(tput setaf 3 2>/dev/null || printf '')" ++BLUE="$(tput setaf 4 2>/dev/null || printf '')" ++MAGENTA="$(tput setaf 5 2>/dev/null || printf '')" ++NO_COLOR="$(tput sgr0 2>/dev/null || printf '')" ++ ++info() { ++ printf '\n%s\n\n' "${BOLD}${YELLOW}! $*${NO_COLOR}" ++} ++ ++warn() { ++ printf '%s\n' "${YELLOW}! $*${NO_COLOR}" ++} ++ ++error() { ++ printf '%s\n\n' "${BOLD}${RED}>${NO_COLOR} ${BOLD}${RED}$*${NO_COLOR}" ++} ++ ++fn_simulate_ostree_system() { ++ # 模拟ostree系统的环境 进行测试 ++ # 主要做的: ++ # - 初始化一个ostree仓库:testos-repo ++ # - 在osdata目录创建模拟一些系统文件 and 提交两次到testos-repo 仓库 ++ # - 复制osdata数据到osdata-devel,做出修改然后再次提交 ++ # - 用init-fs创建sysroot 和 os-init 和 syslinux ++ # - 提交一个新的commit用空目录,标记初始分支($BUILD_MASTER_BRACH)为EOL 重新定向到新分支(testos/buildmaster/newbranch) ++ ++ # 删除所有历史测试数据 重新创建测试 ++ if [ ! -d "$test_tmpdir" ]; then ++ mkdir -p "$test_tmpdir" ++ echo "创建测试目录: $test_tmpdir" ++ else ++ echo "创建测试目录: $test_tmpdir" ++ sudo chattr -R -i $test_tmpdir/* >/dev/null 2>&1 || true ++ chmod -R 777 $test_tmpdir/* >/dev/null 2>&1 || true ++ rm -rf $test_tmpdir ++ mkdir -p "$test_tmpdir" ++ fi ++ ++ if [ ! -d "$PUSH_DIR" ]; then ++ mkdir -p "$PUSH_DIR" ++ fi ++ ++ # 创建ostree仓库 仓库类型 and boot类型 包括默认开启一个远程 ++ setup_os_repository "archive" "syslinux" ++ ++ cd ${test_tmpdir} ++ ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo remote add --set=gpg-verify=false testos $(cat httpd-address)/ostree/testos-repo ++ ++ ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo pull testos $BUILD_MASTER_BRACH > /dev/null ++ rev=$(${CMD_PREFIX} ostree --repo=sysroot/ostree/repo rev-parse $BUILD_MASTER_BRACH) ++ echo "拉取分支:$BUILD_MASTER_BRACH 从远程仓库testos-repo and revision=${rev}" ++ ++ echo "部署分支:$BUILD_MASTER_BRACH" ++ ${CMD_PREFIX} ostree admin deploy --karg=root=LABEL=MOO --karg=quiet --os=testos testos:$BUILD_MASTER_BRACH > /dev/null ++ assert_has_dir sysroot/boot/ostree/testos-${bootcsum} ++} ++ ++moniter_signal_success () { ++ signal=$1 ++ ++ tail -F -n $TAIL_LINE_NUM "$file_to_monitor" | while read -r line; do ++ if echo -e "$line" | grep -q $signal" success = True"; then ++ echo -e "$line \n" ++ break ++ fi ++ ++ if echo "$line" | grep -q 'ERROR'; then ++ echo -e "\033[91m$line\033[0m" ++ else ++ echo "$line" ++ fi ++ done ++} ++ ++moniter_signal_failed () { ++ signal=$1 ++ ++ tail -F -n $TAIL_LINE_NUM "$file_to_monitor" | while read -r line; do ++ if echo -e "$line" | grep -q $signal" success = False"; then ++ echo -e "$line \n" ++ break ++ fi ++ ++ if echo "$line" | grep -q 'ERROR'; then ++ echo -e "\033[91m$line\033[0m" ++ else ++ echo "$line" ++ fi ++ done ++} ++ ++moniter_string () { ++ tail -F -n $TAIL_LINE_NUM "$file_to_monitor" | while read -r line; do ++ if echo -e "$line" | grep "$1"; then ++ echo -e "$line \n" ++ break ++ fi ++ ++ if echo "$line" | grep -q 'ERROR'; then ++ echo -e "\033[91m$line\033[0m" ++ else ++ echo "$line" ++ fi ++ done ++} + + os_repository_new_commit () + { +@@ -50,8 +162,6 @@ os_repository_new_commit () + mkdir -p usr/etc/new-default-dir + echo "a new default dir and file" > usr/etc/new-default-dir/moo + +- # dd if=/dev/zero of=usr/etc/new-default-dir/bigfile bs=1M count=1000 +- + echo "content iteration ${content_iteration}" > usr/bin/content-iteration + + echo -n "创建新分支:$branch and commit revision:" +@@ -198,7 +308,7 @@ EOF + + $process_name >/dev/null 2>&1 & + child_pid=$! +- trap "kill -9 $child_pid" EXIT ++ trap "kill -9 $child_pid >/dev/null 2>&1 | true" EXIT + # 延迟一段时间 等待服务开启 + sleep 2 + +@@ -206,4 +316,10 @@ EOF + port=$(cat ${test_tmpdir}/httpd-port) + echo "http://localhost:${port}" > ${test_tmpdir}/httpd-address + cd ${oldpwd} +-} +\ No newline at end of file ++} ++ ++if ! [ "$DEBUG" = "$TRUE" ]; then ++ info "正在启动系统更新的后台服务... 参数:$@" ++ $updater_program -r --sysroot=$test_tmpdir/sysroot -p --os=testos "$@" & ++ sleep 3 ++fi +\ No newline at end of file +diff --git a/backend-immutable/tests/po/ChangeLog b/backend-immutable/tests/po/ChangeLog +new file mode 100644 +index 0000000..123b7dd +--- /dev/null ++++ b/backend-immutable/tests/po/ChangeLog +@@ -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. ++ +diff --git a/backend-immutable/tests/po/Makefile b/backend-immutable/tests/po/Makefile +new file mode 100644 +index 0000000..dacf43d +--- /dev/null ++++ b/backend-immutable/tests/po/Makefile +@@ -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)) ++ +diff --git a/backend-immutable/tests/po/POTFILES.in b/backend-immutable/tests/po/POTFILES.in +new file mode 100644 +index 0000000..5b8c84f +--- /dev/null ++++ b/backend-immutable/tests/po/POTFILES.in +@@ -0,0 +1,9 @@ ++[encoding: UTF-8] ++SystemUpdater/backend/BackendOstreeNext.py ++SystemUpdater/backend/__init__.py ++SystemUpdater/UpdateManager.py ++SystemUpdater/Core/MyCache.py ++SystemUpdater/Core/UpdateList.py ++SystemUpdater/Core/Database.py ++SystemUpdater/UpdateManagerDbus.py ++SystemUpdater/Core/enums.py +diff --git a/backend-immutable/tests/po/POTFILES.skip b/backend-immutable/tests/po/POTFILES.skip +new file mode 100644 +index 0000000..e69de29 +diff --git a/backend-immutable/tests/po/bo_CN.po b/backend-immutable/tests/po/bo_CN.po +new file mode 100644 +index 0000000..be65457 +--- /dev/null ++++ b/backend-immutable/tests/po/bo_CN.po +@@ -0,0 +1,242 @@ ++# SOME DESCRIPTIVE TITLE. ++# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER ++# This file is distributed under the same license as the PACKAGE package. ++# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. ++# ++#, fuzzy ++msgid "" ++msgstr "" ++"Project-Id-Version: PACKAGE VERSION\n" ++"Report-Msgid-Bugs-To: kylinos.cn\n" ++"POT-Creation-Date: 2012-06-14 00:53+0100\n" ++"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" ++"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" ++"Language-Team: LANGUAGE <LL@li.org>\n" ++"Language: \n" ++"MIME-Version: 1.0\n" ++"Content-Type: text/plain; charset=UTF-8\n" ++"Content-Transfer-Encoding: 8bit\n" ++ ++msgid "Unable to access the source management server, please try again later" ++msgstr "འདྲི་རྩད་ཀྱི་འབྱུང་ཁུངས་དོ་དམ་ཞབས་ཞུའི་ཡོ་བྱད་ལ་འཚམས་འདྲི་བྱེད་ཐབས་མེད་པས་རྗེས་སུ་ཡང་བསྐྱར" ++ ++msgid "Access to the source management server timed out, please try again later" ++msgstr "འཚམས་འདྲིའི་འབྱུང་ཁུངས་ཀྱི་དོ་དམ་ཞབས་ཞུའི་ཡོ་བྱད་དུས་ལས་བརྒལ་བས་རྗེས་སུ་ཡང་བསྐྱར་ཚོད་ལྟ་བྱེད་རོགས" ++ ++msgid "Check if your network requires authentication?" ++msgstr "ཁྱེད་ཀྱི་དྲ་རྒྱར་ཞིབ་བཤེར་བྱེད་དགོས་སམ།" ++ ++msgid "Check your source public key signature" ++msgstr "ཁྱེད་ཀྱི་འབྱུང་ཁུངས་ལ་ཞིབ་བཤེར་བྱས་ནས་མིང་རྟགས་བཀོད།" ++ ++msgid "update important list occur Exception" ++msgstr "ལག་ཏུ་བླངས་ནས་རྒྱུན་ལྡན་མིན་པ་བྱུང་ན་རྗེས་སུ་ཡང་བསྐྱར་ཚོད་ལྟ་ཞིག་གནང་རོགས།" ++ ++msgid "You need to be root to run this application" ++msgstr "ཁྱོད་ལ་rootཡི་དབང་ཚད་ལྟར་འཁོར་སྐྱོད་བྱེད་དགོས།" ++ ++msgid "There is an exception in the update package." ++msgstr "ཁུག་མ་གསར་སྒྱུར་བྱས་པ་རྒྱུན་ལྡན་མིན་པ་ཞིག་རེད" ++ ++msgid "You request the removal of a system-essential package." ++msgstr "ཁྱེད་ཀྱིས་མ་ལག་ཅིག་གི་དགོས་ངེས་ཀྱི་མཉེན་ཆས་ཁུག་མ་ཞིག་བསུབ་དགོས་པའི་བླང་བྱ་" ++ ++msgid "This update cannot detect the upgradeable package." ++msgstr "ཐེངས་འདིའི་གསར་སྒྱུར་གྱིས་རིམ་འཕར་ཐུབ་པའི་མཉེན་ཆས་ཁུག་མར་ཞིབ་དཔྱད་ཚད་ལེན་བྱེད་ཐབས་མེད།" ++ ++msgid "read important list failed" ++msgstr "རིམ་པ་སྤོར་བའི་རེའུ་མིག་ཀློག་ཐབས་བྲལ་བས་རྗེས་སུ་ཡང་བསྐྱར་ཚོད་ལྟ་ཞིག་བྱེད་རོགས།" ++ ++msgid "Priority Upgrade Package being updated" ++msgstr "ཁག་བགོས་ཀྱི་བཀོད་སྒྲིག་གསར་སྒྱུར་བྱེད་བཞིན་ཡོད།" ++ ++msgid "Exceptions of Priority Upgrade." ++msgstr "དམིགས་སུ་བཀར་ནས་དམིགས་སུ་བཀར་ནས་རིམ་པ" ++ ++msgid "Due to the presence of deleted packages." ++msgstr "བསུབ་པའི་མཉེན་ཆས་ཁུག་མ་ཡོད་པའི་རྐྱེན་གྱིས།" ++ ++msgid "The system update configuration file is read abnormally, please check if the system update configuration file format is correct." ++msgstr "མ་ལག་གསར་སྒྱུར་བཀོད་སྒྲིག་ཡིག་ཆ་རྒྱུན་ལྡན་མིན་པས། ཞིབ་བཤེར་མ་ལག་གི་བཀོད་སྒྲིག་ཡིག་ཆའི་རྣམ་གཞག་ཡང་དག་ཡིན་མིན་ལ་ཞིབ་བཤེར་གནང་རོགས།" ++ ++msgid "Installation progress: " ++msgstr "སྒྲིག་སྦྱོར་མྱུར་ཚད་གཤམ་གསལ། " ++ ++msgid "Installation successful, about to shut down" ++msgstr "སྒྲིག་སྦྱོར་ལེགས་འགྲུབ་བྱུང་བ་དང་འགག་སྒོ་ལས་སྒྲོལ་གྲབས་ཡོད་" ++ ++msgid "Installation failed, about to shut down" ++msgstr "སྒྲིག་སྦྱོར་བྱས་ནས་ཕམ་ཉེས་བྱུང་ན་སྒོ་རྒྱག་ལ་ཉེ།" ++ ++msgid "groups JSON ConfigPkgs install failed" ++msgstr "ཁག་བགོས་ཀྱིས་ཡིག་ཆ་སྒྲིག་སྦྱོར་བྱེད་ཐབས་མེད།" ++ ++msgid "Installtion timeout to exit Due to inactivity" ++msgstr "སྒྲིག་སྦྱོར་བྱེད་སྐབས་ཕྱིར་འཐེན་བྱས་པའི་རྐྱེན་གྱིས་རེད།" ++ ++msgid "Command execution error" ++msgstr "ཚགས་པར་འདོན་ནོར་ཤོར་བའི་བཀའ་ཕབ་པ།" ++ ++msgid "Unsupported architecture" ++msgstr "སྒྲོམ་གཞི་དང་མི་མཐུན་པ།" ++ ++msgid "Other Error" ++msgstr "ནོར་འཁྲུལ་གཞན་དག་བཅས་ཡིན" ++ ++msgid "dependency is not satisfied" ++msgstr "འབྲེལ་བ་མི་ཚིམ་པར་བརྟེན་དགོས།" ++ ++msgid "dependency is not satisfied will download" ++msgstr "འབྲེལ་བ་མི་ཚིམ་པར་བརྟེན་དགོས།" ++ ++msgid "Disk space is insufficient, please clean the disk and then upgrade" ++msgstr "ཁབ་ལེན་གྱི་བར་སྟོང་མི་འདང་བས་ཁབ་ལེན་སྡེར་མ་གཙང་བཤེར་བྱས་རྗེས་རིམ་སྤར་གསར་སྒྱུར་བྱེད་རོགས།" ++ ++msgid "Network anomaly, can't check for updates!" ++msgstr "དྲ་རྒྱ་རྒྱུན་ལྡན་མིན་པས་ཞིབ་བཤེར་གསར་སྒྱུར་བྱེད་ཐབས་མེད།" ++ ++msgid "Check for update exceptions!" ++msgstr "རྒྱུན་ལྡན་མིན་པར་ཞིབ་བཤེར་བྱེད་པ།" ++ ++msgid "Check for update exceptions,fix system APT environment error." ++msgstr "ཞིབ་བཤེར་གསར་སྒྱུར་མ་ལག་APTཡི་ཁོར་ཡུག་ལ་ནོར་འཁྲུལ་བྱུང་བ་རེད།" ++ ++msgid "The system APT environment is abnormal, please check the system APT environment." ++msgstr "མ་ལག་APTཡི་ཁོར་ཡུག་རྒྱུན་ལྡན་མིན་པར་ཉམས་གསོ་བྱེད་པར་མ་ལག་APTཡི་ཁོར་ཡུག་ལ་ཞིབ་བཤེར་གནང་རོགས།" ++ ++msgid "Priority upgrade status exception." ++msgstr "དམིགས་སུ་བཀར་ནས་རིམ་པ་འཕར་བའི་རྣམ་པ་རྒྱུན་ལྡན་མིན་པ" ++ ++msgid "Upgrade configuration acquisition exception." ++msgstr "རིམ་སྤར་བཀོད་སྒྲིག་ལ་རྒྱུན་ལྡན་མིན་པའི་གྲུབ་འབྲས་ཐོབ་པ་རེད།" ++ ++msgid "Please check your network connection and retry." ++msgstr "ཁྱེད་ཀྱི་དྲ་རྒྱ་འབྲེལ་མཐུད་བྱས་རྗེས་ཡང་བསྐྱར་ཚོད་ལྟ་ཞིག་བྱེད་རོགས།" ++ ++msgid "Please check your source list and retry." ++msgstr "ཁྱེད་ཀྱི་འབྱུང་ཁུངས་རེའུ་མིག་ལ་ཞིབ་བཤེར་བྱས་རྗེས་ཡང་བསྐྱར་ཚོད་ལྟ་བྱོས།" ++ ++msgid "Checking network connection" ++msgstr "དྲ་རྒྱ་སྦྲེལ་མཐུད་བྱེད་པར་ཞིབ་བཤེར་བྱ་དགོས།" ++ ++msgid "Updating Source Template" ++msgstr "འབྱུང་ཁུངས་གསར་སྒྱུར་བྱེད་པའི་མ་དཔེའི་ནང་།" ++ ++msgid "Update Manager upgrade is complete, please restart the setting panel before performing the system update." ++msgstr "དོ་དམ་ཡོ་བྱད་རིམ་སྤར་ལེགས་འགྲུབ་བྱུང་བ་དང་། གསར་བཅོས་བྱས་རྗེས་སླར་ཡང་མ་ལག་གསར་སྒྱུར་བྱེད་རོགས།" ++ ++msgid "Uninstallation completed" ++msgstr "ཕབ་ལེན་ལེགས་འགྲུབ་བྱུང་བ།" ++ ++msgid "Package validation failed and installation was rejected." ++msgstr "མཉེན་ཆས་ཚོད་ལྟས་ར་སྤྲོད་བྱས་ནས་ཕམ་ཁ་བྱུང་བས་སྒྲིག་སྦྱོར་དང་ལེན་མ་བྱས" ++ ++msgid "Other tasks are being updated and upgraded, please uninstall them later." ++msgstr "ལས་འགན་གཞན་དག་གསར་སྒྱུར་རིམ་སྤོར་བྱེད་བཞིན་པའི་སྒང་ཡིན་" ++ ++#: ../aptdaemon/worker/aptworker.py:1353 ++msgid "The following packages have unmet dependencies:" ++msgstr "གཤམ་གསལ་གྱི་མཉེན་ཆས་ཁུག་མ་ཡིད་ཚིམ་པའི་གཞན་རྟེན་གྱི་འབྲེལ་བ།" ++ ++#: ../aptdaemon/worker/aptworker.py:1406 ++msgid "but it is a virtual package" ++msgstr "འོན་ཀྱང་དེ་ནི་རྟོག་བཟོའི་མཉེན་ཆས་ཁུག་མ་རེད།" ++ ++#: ../aptdaemon/worker/aptworker.py:1409 ++msgid "but it is not installed" ++msgstr "但是 %s 没有安装" ++ ++#: ../aptdaemon/worker/aptworker.py:1411 ++msgid "but it is not going to be installed" ++msgstr "但是无法安装 %s" ++ ++#. TRANSLATORS: %s is a version number ++#: ../aptdaemon/worker/aptworker.py:1415 ++#, python-format ++msgid "but %s is installed" ++msgstr "但是 %s 已经安装" ++ ++#. TRANSLATORS: %s is a version number ++#: ../aptdaemon/worker/aptworker.py:1419 ++#, python-format ++msgid "but %s is to be installed" ++msgstr "但是将要安装 %s" ++ ++#: ../SystemUpdater/Core/enums.py:763 ++msgid "Kylin System Updater" ++msgstr "ཝེ།སྒྲིག་ཆས་གསར་སྒྱུར་བྱེད་དགོས།" ++ ++#: ../SystemUpdater/Core/enums.py:609 ++msgid "Kylin Installer" ++msgstr "ཝེ།སྒྲིག་ཆས་སྒྲིག་སྦྱོར་བྱེད་དགོས།" ++ ++#: ../SystemUpdater/Core/enums.py:610 ++msgid "Kylin Uninstaller" ++msgstr "ཝེ།བཏགས་ཆས་" ++ ++#: ../SystemUpdater/Core/enums.py:611 ++msgid "Kylin Background Upgrade" ++msgstr "ཁ་རོག་གེར་གསར་སྒྱུར་བྱེད་པ།" ++ ++#: ../SystemUpdater/Core/enums.py:612 ++msgid "Kylin Software Center" ++msgstr "མཉེན་ཆས་ཚོང་ཁང་།" ++ ++#: ../SystemUpdater/UpdateManagerDbus.py:355 ++msgid " requires authentication to uninstall software packages." ++msgstr "མཉེན་ཆས་ཀྱི་ཁུག་མ་འདོན་པར་བདེན་དཔང་ར་སྤྲོད་བྱེད་དགོས།" ++ ++#. 验签失败,提权 ++#: ../SystemUpdater/UpdateManager.py:463 ++msgid " requires authentication to install software packages." ++msgstr "མཉེན་ཆས་ཀྱི་ཁུག་མ་སྒྲིག་སྦྱོར་བྱེད་པར་བདེན་དཔང་ར་སྤྲོད་བྱེད" ++ ++#: ../SystemUpdater/Core/utils.py:750 ++msgid "Authentication success." ++msgstr "བདེན་དཔང་ར་སྤྲོད་ལེགས་འགྲུབ་བྱུང་" ++ ++#: ../SystemUpdater/Core/utils.py:753 ++msgid "Authentication failure." ++msgstr "བདེན་དཔང་ར་སྤྲོད་ཕམ་སོང་།" ++ ++#: ../SystemUpdater/Core/enums.py:101 ++msgid "Deb format exception, read local deb file error." ++msgstr "མཉེན་ཆས་ཀྱི་ཁུག་མའི་རྣམ་གཞག་རྒྱུན་ལྡན་མིན་པས་ཕམ་ཁ་བླངས།" ++ ++#: ../SystemUpdater/Core/enums.py:102 ++msgid "Install deb error." ++msgstr "མཉེན་ཆས་སྒྲིག་སྦྱོར་བྱས་པ་ཕམ་སོང་།" ++ ++msgid "Upgrade System" ++msgstr "ཁྱོན་ཡོངས་ནས་རིམ་སྤར་བྱ་དགོས།" ++ ++msgid "kylin-unattended-upgrade" ++msgstr "རང་འགུལ་གྱིས་གསར་སྒྱུར་བྱེད་དགོས།" ++ ++msgid "Please check the system time and synchronize the system time before updating." ++msgstr "མ་ལག་གི་དུས་ཚོད་ལ་ཞིབ་བཤེར་གནང་རོགས། དུས་མཉམ་དུ་མ་ལག་གི་དུས་ཚོད་རྗེས་སུ་གསར་སྒྱུར་བྱེད་དགོས" ++ ++msgid "The package is unsigned, refuses to install." ++msgstr "མཉེན་ཆས་ཀྱི་ཁུག་མར་མིང་རྟགས་མ་བཀོད་པས་སྒྲིག་སྦྱོར་དང་ལེན་མི་བྱེད།" ++ ++msgid "Program exception, please contact the administrator to solve." ++msgstr "གོ་རིམ་ལག་བསྟར་རྒྱུན་ལྡན་མིན་པར་དོ་དམ་པ་དང་འབྲེལ་གཏུག་བྱས་ནས་ཐག་གཅོད" ++ ++msgid "Exceptions to running the check script." ++msgstr "འཁོར་སྐྱོད་ཞིབ་བཤེར་གྱི་རྐང་པར་རྒྱུན་ལྡན་མིན་པ་བྱུང་།" ++ ++msgid "Application installation control policy not enabled ." ++msgstr "ཉེར་སྤྱོད་སྒྲིག་སྦྱོར་དོ་དམ་ཚོད་འཛིན་བྱེད་འགོ་ཚུགས་མེད་པ།" ++ ++msgid "Installation failed! Application is not in the software whitelist list!" ++msgstr "སྒྲིག་སྦྱོར་ཕམ་པ། མཉེན་ཆས་ནི་མིང་ཐོའི་རེའུ་མིག་དཀར་པོ་ན་ཡོད་དམ།" ++ ++msgid "Installation failed! Application is in the software blacklist list!" ++msgstr "སྒྲིག་སྦྱོར་ཕམ་པ། མཉེན་ཆས་མིང་ཐོའི་ནང་དུ་ཡོད།" ++ ++msgid "Application installation in unknown mode ." ++msgstr "ཉེར་སྤྱོད་སྒྲིག་སྦྱོར་དང་དོ་དམ་ཚོད་འཛིན་གྱི་ཐབས་ཇུས་མི་ཤེས་པ།" ++ ++#: ../SystemUpdater/Core/utils.py:753 ++msgid "Cancel authentication." ++msgstr "ཕྱིར་འབུད་བྱ་རྒྱུ།" +diff --git a/backend-immutable/tests/po/zh_CN.po b/backend-immutable/tests/po/zh_CN.po +new file mode 100644 +index 0000000..9a401f2 +--- /dev/null ++++ b/backend-immutable/tests/po/zh_CN.po +@@ -0,0 +1,2821 @@ ++# SOME DESCRIPTIVE TITLE. ++# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER ++# This file is distributed under the same license as the PACKAGE package. ++# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. ++# ++#, fuzzy ++msgid "" ++msgstr "" ++"Project-Id-Version: PACKAGE VERSION\n" ++"Report-Msgid-Bugs-To: sebastian.heinlein@web.de\n" ++"POT-Creation-Date: 2012-06-14 00:53+0100\n" ++"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" ++"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" ++"Language-Team: LANGUAGE <LL@li.org>\n" ++"Language: \n" ++"MIME-Version: 1.0\n" ++"Content-Type: text/plain; charset=UTF-8\n" ++"Content-Transfer-Encoding: 8bit\n" ++ ++#. TRANSLATORS: download size of small updates, e.g. "250 kB" ++#: ../DistUpgrade/utils.py:433 ../UpdateManager/Core/utils.py:433 ++#, python-format ++msgid "%(size).0f kB" ++msgid_plural "%(size).0f kB" ++msgstr[0] "%(size).0f kB" ++ ++#. TRANSLATORS: download size of updates, e.g. "2.3 MB" ++#: ../DistUpgrade/utils.py:436 ../UpdateManager/Core/utils.py:436 ++#, python-format ++msgid "%.1f MB" ++msgstr "%.1f MB" ++ ++#. TRANSLATORS: %s is a country ++#: ../DistUpgrade/distro.py:206 ../DistUpgrade/distro.py:436 ++#, python-format ++msgid "Server for %s" ++msgstr "%s 的服务器" ++ ++#. More than one server is used. Since we don't handle this case ++#. in the user interface we set "custom servers" to true and ++#. append a list of all used servers ++#: ../DistUpgrade/distro.py:224 ../DistUpgrade/distro.py:230 ++#: ../DistUpgrade/distro.py:246 ++msgid "Main server" ++msgstr "主服务器" ++ ++#: ../DistUpgrade/distro.py:250 ++msgid "Custom servers" ++msgstr "自定义服务器" ++ ++#: ../DistUpgrade/DistUpgradeAptCdrom.py:142 ++msgid "Could not calculate sources.list entry" ++msgstr "无法计算 sources.list 条目" ++ ++#: ../DistUpgrade/DistUpgradeAptCdrom.py:251 ++msgid "" ++"Unable to locate any package files, perhaps this is not a Ubuntu Disc or the " ++"wrong architecture?" ++msgstr "无法定位任何软件包文件,也许这张不是 Ubuntu 光盘,或者其架构错误?" ++ ++#: ../DistUpgrade/DistUpgradeAptCdrom.py:294 ++msgid "Failed to add the CD" ++msgstr "添加 CD 失败" ++ ++#: ../DistUpgrade/DistUpgradeAptCdrom.py:295 ++#, python-format ++msgid "" ++"There was a error adding the CD, the upgrade will abort. Please report this " ++"as a bug if this is a valid Ubuntu CD.\n" ++"\n" ++"The error message was:\n" ++"'%s'" ++msgstr "" ++"添加 CD 时出错,升级中止。如果这是一张有效的 Ubuntu CD,请您报告这个错误。\n" ++"\n" ++"错误信息是:\n" ++"'%s'" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:151 ++msgid "Remove package in bad state" ++msgid_plural "Remove packages in bad state" ++msgstr[0] "卸载状态异常的软件包" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:154 ++#, python-format ++msgid "" ++"The package '%s' is in an inconsistent state and needs to be reinstalled, " ++"but no archive can be found for it. Do you want to remove this package now " ++"to continue?" ++msgid_plural "" ++"The packages '%s' are in an inconsistent state and need to be reinstalled, " ++"but no archives can be found for them. Do you want to remove these packages " ++"now to continue?" ++msgstr[0] "" ++"软件包“%s”处于不一致的状态,需要重新安装,但是没有找到对应的存档。您希望现在" ++"删除这个软件包以进行下一步吗?" ++ ++#. FIXME: not ideal error message, but we just reuse a ++#. existing one here to avoid a new string ++#: ../DistUpgrade/DistUpgradeCache.py:255 ++msgid "The server may be overloaded" ++msgstr "服务器可能已过载" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:368 ++msgid "Broken packages" ++msgstr "破损的软件包" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:369 ++msgid "" ++"Your system contains broken packages that couldn't be fixed with this " ++"software. Please fix them first using synaptic or apt-get before proceeding." ++msgstr "" ++"您的系统包含有本软件不能修复的破损软件包,在您继续前请先用新立得或者 apt-get " ++"修复它们。" ++ ++#. FIXME: change the text to something more useful ++#: ../DistUpgrade/DistUpgradeCache.py:693 ++#, python-format ++msgid "" ++"An unresolvable problem occurred while calculating the upgrade:\n" ++"%s\n" ++"\n" ++" This can be caused by:\n" ++" * Upgrading to a pre-release version of Ubuntu\n" ++" * Running the current pre-release version of Ubuntu\n" ++" * Unofficial software packages not provided by Ubuntu\n" ++"\n" ++msgstr "" ++"在准备升级时发生了一个无法解决的问题:\n" ++"%s\n" ++"\n" ++" 这可能是由以下原因引起的:\n" ++" * 升级到了预发行 Ubuntu 版本\n" ++" * 正在运行当前的预发行 Ubuntu 版本\n" ++" * 非 Ubuntu 提供的非官方软件包\n" ++"\n" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:703 ++msgid "This is most likely a transient problem, please try again later." ++msgstr "很可能发生了一个传输问题,请稍后重试。" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:706 ++msgid "" ++"If none of this applies, then please report this bug using the command " ++"'ubuntu-bug update-manager' in a terminal." ++msgstr "" ++"如果没有应用任何变更,您可以在终端里输入命令‘ubuntu-bug update-manager’来报告" ++"这个 bug。" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:711 ++#: ../UpdateManager/UpdateManager.py:1031 ++msgid "Could not calculate the upgrade" ++msgstr "不能计算升级" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:762 ++msgid "Error authenticating some packages" ++msgstr "一些软件包认证出错" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:763 ++msgid "" ++"It was not possible to authenticate some packages. This may be a transient " ++"network problem. You may want to try again later. See below for a list of " ++"unauthenticated packages." ++msgstr "" ++"一些软件包无法通过签名验证。这可能是暂时的网络问题,您可以在稍后再试。以下是" ++"未认证软件包的列表。" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:783 ++#, python-format ++msgid "" ++"The package '%s' is marked for removal but it is in the removal blacklist." ++msgstr "软件包“%s”标记为可移除,但它已在移除黑名单中。" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:787 ++#, python-format ++msgid "The essential package '%s' is marked for removal." ++msgstr "必要的软件包“%s”被标记为移除。" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:796 ++#, python-format ++msgid "Trying to install blacklisted version '%s'" ++msgstr "尝试安装黑名单版本“%s”" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:914 ++#, python-format ++msgid "Can't install '%s'" ++msgstr "无法安装 '%s'" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:915 ++msgid "" ++"It was impossible to install a required package. Please report this as a bug " ++"using 'ubuntu-bug update-manager' in a terminal." ++msgstr "" ++"无法安装一个必需的软件包。请在终端里输入命令‘ubuntu-bug update-manager’来报告" ++"这个 bug。" ++ ++#. FIXME: provide a list ++#: ../DistUpgrade/DistUpgradeCache.py:926 ++msgid "Can't guess meta-package" ++msgstr "无法猜出元软件包" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:927 ++msgid "" ++"Your system does not contain a ubuntu-desktop, kubuntu-desktop, xubuntu-" ++"desktop or edubuntu-desktop package and it was not possible to detect which " ++"version of Ubuntu you are running.\n" ++" Please install one of the packages above first using synaptic or apt-get " ++"before proceeding." ++msgstr "" ++"您的系统没有安装 ubuntu-desktop,kubuntu-desktop 或 eubuntu-desktop 软件包所" ++"以无法确定运行的 ubuntu 的版本。\n" ++" 请先用新立得或 APT 安装以上所举软件包中的一个。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:114 ++msgid "Reading cache" ++msgstr "正在读取缓存" ++ ++#: ../DistUpgrade/DistUpgradeController.py:223 ++msgid "Unable to get exclusive lock" ++msgstr "无法获得排它锁" ++ ++#: ../DistUpgrade/DistUpgradeController.py:224 ++msgid "" ++"This usually means that another package management application (like apt-get " ++"or aptitude) already running. Please close that application first." ++msgstr "" ++"这通常意味着另一个软件包管理程序(如 apt-get 或 aptitude)正在运行。请先关闭那" ++"个程序。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:257 ++msgid "Upgrading over remote connection not supported" ++msgstr "不支持通过远程连接升级" ++ ++#: ../DistUpgrade/DistUpgradeController.py:258 ++msgid "" ++"You are running the upgrade over a remote ssh connection with a frontend " ++"that does not support this. Please try a text mode upgrade with 'do-release-" ++"upgrade'.\n" ++"\n" ++"The upgrade will abort now. Please try without ssh." ++msgstr "" ++"您正在通过远程 SSH 升级,而前端程序不支持这种方式。请尝试在文本模式下通过 " ++"'do-release-upgrade' 命令进行升级。\n" ++"\n" ++"现在将退出升级。请试试不使用 ssh 的方式。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:272 ++msgid "Continue running under SSH?" ++msgstr "继续在 SSH 下执行?" ++ ++#: ../DistUpgrade/DistUpgradeController.py:273 ++#, python-format ++msgid "" ++"This session appears to be running under ssh. It is not recommended to " ++"perform a upgrade over ssh currently because in case of failure it is harder " ++"to recover.\n" ++"\n" ++"If you continue, an additional ssh daemon will be started at port '%s'.\n" ++"Do you want to continue?" ++msgstr "" ++"此会话似乎是在 SSH 下运行。目前不推荐通过 SSH 执行升级,因为升级失败时较难恢" ++"复。\n" ++"\n" ++"如果您选择继续,将在 '%s' 端口上建立额外的 SSH 守护进程。\n" ++"您想要继续吗?" ++ ++#: ../DistUpgrade/DistUpgradeController.py:287 ++msgid "Starting additional sshd" ++msgstr "正在启用额外的 ssh 守护进程" ++ ++#: ../DistUpgrade/DistUpgradeController.py:288 ++#, python-format ++msgid "" ++"To make recovery in case of failure easier, an additional sshd will be " ++"started on port '%s'. If anything goes wrong with the running ssh you can " ++"still connect to the additional one.\n" ++msgstr "" ++"为了在失败时更容易恢复,将在端口“%s”开启一个额外的 ssh 守护进程。如果当前运行" ++"的 ssh 发生错误,您仍能够通过该额外的 ssh 进行连接。\n" ++ ++#: ../DistUpgrade/DistUpgradeController.py:296 ++#, python-format ++msgid "" ++"If you run a firewall, you may need to temporarily open this port. As this " ++"is potentially dangerous it's not done automatically. You can open the port " ++"with e.g.:\n" ++"'%s'" ++msgstr "" ++"如果您运行了一个防火墙,可能需要临时打开这个端口。这可能有些危险,因此没有自" ++"动进行这个操作。您可以通过类似这样的命令打开端口:\n" ++"%s" ++ ++#: ../DistUpgrade/DistUpgradeController.py:368 ++#: ../DistUpgrade/DistUpgradeController.py:413 ++msgid "Can not upgrade" ++msgstr "不能升级" ++ ++#: ../DistUpgrade/DistUpgradeController.py:369 ++#, python-format ++msgid "An upgrade from '%s' to '%s' is not supported with this tool." ++msgstr "此工具不支持从 '%s' 到 ‘%s' 的升级。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:378 ++msgid "Sandbox setup failed" ++msgstr "安装沙盒失败" ++ ++#: ../DistUpgrade/DistUpgradeController.py:379 ++msgid "It was not possible to create the sandbox environment." ++msgstr "不能创建沙盒环境" ++ ++#: ../DistUpgrade/DistUpgradeController.py:385 ++msgid "Sandbox mode" ++msgstr "沙盒模式" ++ ++#: ../DistUpgrade/DistUpgradeController.py:386 ++#, python-format ++msgid "" ++"This upgrade is running in sandbox (test) mode. All changes are written to " ++"'%s' and will be lost on the next reboot.\n" ++"\n" ++"*No* changes written to a system directory from now until the next reboot " ++"are permanent." ++msgstr "" ++"本次更新运行在沙盘(测试)模式,所有变更都将写入 '%s' 并会在重启后丢失。\n" ++"\n" ++"从现在起对系统目录的变更重启后都将 *不复存在* 。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:414 ++msgid "" ++"Your python install is corrupted. Please fix the '/usr/bin/python' symlink." ++msgstr "您的 python 安装错误,请修复“/usr/bin/python”符号链接。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:440 ++msgid "Package 'debsig-verify' is installed" ++msgstr "已安装软件包“debsig-verify”" ++ ++#: ../DistUpgrade/DistUpgradeController.py:441 ++msgid "" ++"The upgrade can not continue with that package installed.\n" ++"Please remove it with synaptic or 'apt-get remove debsig-verify' first and " ++"run the upgrade again." ++msgstr "" ++"由于安装了上述软件包而无法继续升级。\n" ++"请使用新立得软件包管理器来移除它,或者先使用 “sudo apt-get remove debsig-" ++"verify”卸载后再重新尝试升级。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:453 ++#, python-format ++msgid "Can not write to '%s'" ++msgstr "无法写入 '%s'" ++ ++#: ../DistUpgrade/DistUpgradeController.py:454 ++#, python-format ++msgid "" ++"Its not possible to write to the system directory '%s' on your system. The " ++"upgrade can not continue.\n" ++"Please make sure that the system directory is writable." ++msgstr "" ++"无法写入您的系统目录 %s ,升级无法继续。\n" ++"请确保系统目录可写。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:465 ++msgid "Include latest updates from the Internet?" ++msgstr "包括网络上的最新更新?" ++ ++#: ../DistUpgrade/DistUpgradeController.py:466 ++msgid "" ++"The upgrade system can use the internet to automatically download the latest " ++"updates and install them during the upgrade. If you have a network " ++"connection this is highly recommended.\n" ++"\n" ++"The upgrade will take longer, but when it is complete, your system will be " ++"fully up to date. You can choose not to do this, but you should install the " ++"latest updates soon after upgrading.\n" ++"If you answer 'no' here, the network is not used at all." ++msgstr "" ++"升级系统能够从网络上自动下载最新的更新文件并自动安装。如果网络连接可用,强烈" ++"建议启用此选项。\n" ++"\n" ++"升级时间可能加长,但升级结束后,您的系统将是最新的。您也可以选择在升级结束后" ++"再进行系统更新。\n" ++"如果选择\"否\",就不会使用网络。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:686 ++#, python-format ++msgid "disabled on upgrade to %s" ++msgstr "已禁止升级到 %s" ++ ++#: ../DistUpgrade/DistUpgradeController.py:713 ++msgid "No valid mirror found" ++msgstr "未找到可用的镜像" ++ ++#: ../DistUpgrade/DistUpgradeController.py:714 ++#, python-format ++msgid "" ++"While scanning your repository information no mirror entry for the upgrade " ++"was found. This can happen if you run a internal mirror or if the mirror " ++"information is out of date.\n" ++"\n" ++"Do you want to rewrite your 'sources.list' file anyway? If you choose 'Yes' " ++"here it will update all '%s' to '%s' entries.\n" ++"If you select 'No' the upgrade will cancel." ++msgstr "" ++"扫描仓库时未发现升级信息。可能您使用的是内部镜像或者镜像信息已经过期。\n" ++"\n" ++"是否依然重写 \"sources.list\" 文件?如果选择“是”,将会把所有 “%s” 升级到 " ++"“%s”。" ++ ++#. hm, still nothing useful ... ++#: ../DistUpgrade/DistUpgradeController.py:734 ++msgid "Generate default sources?" ++msgstr "生成默认的源?" ++ ++#: ../DistUpgrade/DistUpgradeController.py:735 ++#, python-format ++msgid "" ++"After scanning your 'sources.list' no valid entry for '%s' was found.\n" ++"\n" ++"Should default entries for '%s' be added? If you select 'No', the upgrade " ++"will cancel." ++msgstr "" ++"扫描“sources.list”后未发现用于“%s”的可用项。\n" ++"\n" ++"是否为“%s”添加默认项?如果选择“否”,将会取消升级。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:770 ++msgid "Repository information invalid" ++msgstr "仓库信息无效" ++ ++#: ../DistUpgrade/DistUpgradeController.py:771 ++msgid "" ++"Upgrading the repository information resulted in a invalid file so a bug " ++"reporting process is being started." ++msgstr "更新软件源时返回了无效文件,已启动错误报告进程。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:778 ++msgid "Third party sources disabled" ++msgstr "第三方源被禁用" ++ ++#: ../DistUpgrade/DistUpgradeController.py:779 ++msgid "" ++"Some third party entries in your sources.list were disabled. You can re-" ++"enable them after the upgrade with the 'software-properties' tool or your " ++"package manager." ++msgstr "" ++"您的 sources.list 中的一些第三方源被禁用。您可以在升级后用\"软件源\"工具或包" ++"管理器来重新启用它们。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:819 ++msgid "Package in inconsistent state" ++msgid_plural "Packages in inconsistent state" ++msgstr[0] "软件包存在冲突" ++ ++#: ../DistUpgrade/DistUpgradeController.py:822 ++#, python-format ++msgid "" ++"The package '%s' is in an inconsistent state and needs to be reinstalled, " ++"but no archive can be found for it. Please reinstall the package manually or " ++"remove it from the system." ++msgid_plural "" ++"The packages '%s' are in an inconsistent state and need to be reinstalled, " ++"but no archive can be found for them. Please reinstall the packages manually " ++"or remove them from the system." ++msgstr[0] "" ++"软件包“%s”处于冲突状态,需要重新安装, 但是没能找到它的存档。请重新手动安装这" ++"个软件包或者将其从系统中删除。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:870 ++msgid "Error during update" ++msgstr "升级时出错" ++ ++#: ../DistUpgrade/DistUpgradeController.py:871 ++msgid "" ++"A problem occurred during the update. This is usually some sort of network " ++"problem, please check your network connection and retry." ++msgstr "升级过程中出错。这通常是一些网络问题,请检查您的网络连接后再试" ++ ++#. print("on_button_install_clicked") ++#: ../DistUpgrade/DistUpgradeController.py:880 ++#: ../UpdateManager/UpdateManager.py:757 ++msgid "Not enough free disk space" ++msgstr "磁盘空间不足" ++ ++#: ../DistUpgrade/DistUpgradeController.py:881 ++#, python-format ++msgid "" ++"The upgrade has aborted. The upgrade needs a total of %s free space on disk " ++"'%s'. Please free at least an additional %s of disk space on '%s'. Empty " ++"your trash and remove temporary packages of former installations using 'sudo " ++"apt-get clean'." ++msgstr "" ++"升级已被中断。此次升级需要有 %s 的可用空间在磁盘 %s 上。请释放至少 %s 的空间" ++"在磁盘 %s 上。您可以清空回收站并使用“sudo apt-get clean”命令以清除之前安装操" ++"作留下的临时文件。" ++ ++#. calc the dist-upgrade and see if the removals are ok/expected ++#. do the dist-upgrade ++#: ../DistUpgrade/DistUpgradeController.py:910 ++#: ../DistUpgrade/DistUpgradeController.py:1692 ++msgid "Calculating the changes" ++msgstr "正在计算变更" ++ ++#. ask the user ++#: ../DistUpgrade/DistUpgradeController.py:942 ++msgid "Do you want to start the upgrade?" ++msgstr "您要开始升级么?" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1008 ++msgid "Upgrade canceled" ++msgstr "升级已取消" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1009 ++msgid "" ++"The upgrade will cancel now and the original system state will be restored. " ++"You can resume the upgrade at a later time." ++msgstr "升级将会取消,系统会恢复到原始状态。您可以在之后继续升级。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1015 ++#: ../DistUpgrade/DistUpgradeController.py:1149 ++msgid "Could not download the upgrades" ++msgstr "无法下载升级包" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1016 ++msgid "" ++"The upgrade has aborted. Please check your Internet connection or " ++"installation media and try again. All files downloaded so far have been kept." ++msgstr "" ++"已中止升级。请检查您的互联网连接或安装媒体并重试。所有已下载的文件都已保存。" ++ ++#. FIXME: strings are not good, but we are in string freeze ++#. currently ++#: ../DistUpgrade/DistUpgradeController.py:1100 ++#: ../DistUpgrade/DistUpgradeController.py:1137 ++#: ../DistUpgrade/DistUpgradeController.py:1242 ++msgid "Error during commit" ++msgstr "确认时出错" ++ ++#. generate a new cache ++#: ../DistUpgrade/DistUpgradeController.py:1102 ++#: ../DistUpgrade/DistUpgradeController.py:1139 ++#: ../DistUpgrade/DistUpgradeController.py:1281 ++msgid "Restoring original system state" ++msgstr "正在恢复原始系统状态" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1103 ++#: ../DistUpgrade/DistUpgradeController.py:1118 ++#: ../DistUpgrade/DistUpgradeController.py:1140 ++msgid "Could not install the upgrades" ++msgstr "无法安装升级" ++ ++#. invoke the frontend now and show a error message ++#: ../DistUpgrade/DistUpgradeController.py:1108 ++msgid "" ++"The upgrade has aborted. Your system could be in an unusable state. A " ++"recovery will run now (dpkg --configure -a)." ++msgstr "" ++"更新已取消。您的系统可能处在不稳定状态。正在恢复 (dpkg --configure -a)。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1113 ++#, python-format ++msgid "" ++"\n" ++"\n" ++"Please report this bug in a browser at http://bugs.launchpad.net/ubuntu/" ++"+source/update-manager/+filebug and attach the files in /var/log/dist-" ++"upgrade/ to the bug report.\n" ++"%s" ++msgstr "" ++"\n" ++"\n" ++"请使用浏览器在 http://bugs.launchpad.net/ubuntu/+source/update-manager/" ++"+filebug 中报告此错误,然后随错误报告附上 /var/log/dist-upgrade/ 中的文件。\n" ++"%s" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1150 ++msgid "" ++"The upgrade has aborted. Please check your Internet connection or " ++"installation media and try again. " ++msgstr "更新已取消。请检查您的因特网连接或安装媒体,然后再试一遍。 " ++ ++#: ../DistUpgrade/DistUpgradeController.py:1230 ++msgid "Remove obsolete packages?" ++msgstr "删除陈旧的软件包?" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1231 ++#: ../DistUpgrade/DistUpgrade.ui.h:8 ++msgid "_Keep" ++msgstr "保持(_K)" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1231 ++msgid "_Remove" ++msgstr "删除(_R)" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1243 ++msgid "" ++"A problem occurred during the clean-up. Please see the below message for " ++"more information. " ++msgstr "清理时出现问题。更多信息请查看以下消息。 " ++ ++#. FIXME: instead of error out, fetch and install it ++#. here ++#: ../DistUpgrade/DistUpgradeController.py:1319 ++msgid "Required depends is not installed" ++msgstr "需要的依赖关系未安装" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1320 ++#, python-format ++msgid "The required dependency '%s' is not installed. " ++msgstr "需要的依赖关系“%s”未安装 。 " ++ ++#. sanity check (check for ubuntu-desktop, brokenCache etc) ++#. then open the cache (again) ++#: ../DistUpgrade/DistUpgradeController.py:1588 ++#: ../DistUpgrade/DistUpgradeController.py:1653 ++msgid "Checking package manager" ++msgstr "正在检查软件包管理器" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1593 ++msgid "Unable to get group configuration package, Please check if the configuration package exists in the software source repository." ++msgstr "无法获取组配置软件包,请检查组配置包是否存在软件源仓库中。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1593 ++msgid "Unable to install group configuration package, Please check the configuration package related dependencies." ++msgstr "无法安装组配置包,请检查组配置包相关依赖。" ++ ++msgid "Unable to perform priority upgrade, please check the dependency related to the priority upgrade package." ++msgstr "无法进行优先升级,请检查优先升级包相关依赖。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1594 ++msgid "" ++"Preparing the system for the upgrade failed so a bug reporting process is " ++"being started." ++msgstr "更新时的系统准备失败,已启动错误报告进程。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1608 ++msgid "Getting upgrade prerequisites failed" ++msgstr "准备升级失败" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1609 ++msgid "" ++"The system was unable to get the prerequisites for the upgrade. The upgrade " ++"will abort now and restore the original system state.\n" ++"\n" ++"Additionally, a bug reporting process is being started." ++msgstr "" ++"系统不能满足升级先决条件。现在会中止升级并恢复原来的系统状态。\n" ++"\n" ++"此外,错误报告进程也将启动。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1637 ++msgid "Updating repository information" ++msgstr "正在更新软件仓库信息" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1644 ++msgid "Failed to add the cdrom" ++msgstr "添加光驱失败" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1645 ++msgid "Sorry, adding the cdrom was not successful." ++msgstr "对不起,没有成功添加光驱。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1673 ++msgid "Invalid package information" ++msgstr "无效的软件包信息" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1674 ++msgid "After updating your package " ++msgstr "" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1698 ++#: ../DistUpgrade/DistUpgradeController.py:1750 ++msgid "Fetching" ++msgstr "正在获取" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1704 ++#: ../DistUpgrade/DistUpgradeController.py:1754 ++msgid "Upgrading" ++msgstr "正在升级" ++ ++#. don't abort here, because it would restore the sources.list ++#: ../DistUpgrade/DistUpgradeController.py:1709 ++#: ../DistUpgrade/DistUpgradeController.py:1756 ++#: ../DistUpgrade/DistUpgradeController.py:1763 ++#: ../DistUpgrade/DistUpgradeController.py:1774 ++msgid "Upgrade complete" ++msgstr "升级完成" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1710 ++#: ../DistUpgrade/DistUpgradeController.py:1757 ++#: ../DistUpgrade/DistUpgradeController.py:1764 ++msgid "" ++"The upgrade has completed but there were errors during the upgrade process." ++msgstr "升级已完成,但其间出现错误。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1717 ++msgid "Searching for obsolete software" ++msgstr "正在搜索废弃的软件" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1726 ++msgid "System upgrade is complete." ++msgstr "系统升级完成。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1775 ++msgid "The partial upgrade was completed." ++msgstr "部分升级完成。" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:204 ++msgid "evms in use" ++msgstr "evms 使用中" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:205 ++msgid "" ++"Your system uses the 'evms' volume manager in /proc/mounts. The 'evms' " ++"software is no longer supported, please switch it off and run the upgrade " ++"again when this is done." ++msgstr "" ++"您的系统在 /proc/mounts 中使用“evms”卷管理器。“evms”软件不再被支持,请关闭它" ++"并重新运行升级。" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:502 ++msgid "Your graphics hardware may not be fully supported in Ubuntu 12.04 LTS." ++msgstr "Ubuntu 12.04 LTS 可能无法完全支持您的显卡。" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:504 ++msgid "" ++"The support in Ubuntu 12.04 LTS for your Intel graphics hardware is limited " ++"and you may encounter problems after the upgrade. For more information see " ++"https://wiki.ubuntu.com/X/Bugs/UpdateManagerWarningForI8xx Do you want to " ++"continue with the upgrade?" ++msgstr "" ++"Ubuntu 12.04 LTS 对 Intel 图形硬件的支持有限制,升级后您可能会遭遇问题。更多" ++"信息,请查看 https://wiki.ubuntu.com/X/Bugs/UpdateManagerWarningForI8xx 。仍" ++"然想要继续升级吗?" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:526 ++#: ../DistUpgrade/DistUpgradeQuirks.py:554 ++#: ../DistUpgrade/DistUpgradeQuirks.py:581 ++msgid "" ++"Upgrading may reduce desktop effects, and performance in games and other " ++"graphically intensive programs." ++msgstr "升级过程可能会降低桌面特效,游戏性能以及对显示要求高的程序。" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:530 ++#: ../DistUpgrade/DistUpgradeQuirks.py:558 ++msgid "" ++"This computer is currently using the NVIDIA 'nvidia' graphics driver. No " ++"version of this driver is available that works with your video card in " ++"Ubuntu 10.04 LTS.\n" ++"\n" ++"Do you want to continue?" ++msgstr "" ++"此计算机正在使用 NVIDIA “nvidia”显卡驱动。该驱动在 Ubuntu 10.04 LTS 中没有与" ++"您的硬件相适应的版本。\n" ++"\n" ++"您想继续吗?" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:585 ++msgid "" ++"This computer is currently using the AMD 'fglrx' graphics driver. No version " ++"of this driver is available that works with your hardware in Ubuntu 10.04 " ++"LTS.\n" ++"\n" ++"Do you want to continue?" ++msgstr "" ++"此计算机正在使用 AMD “fglrx”显卡驱动。该驱动在 Ubuntu 10.04 LTS 中没有与您的" ++"硬件相适应的版本。\n" ++"\n" ++"您想继续吗?" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:615 ++msgid "No i686 CPU" ++msgstr "非 i686 CPU" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:616 ++msgid "" ++"Your system uses an i586 CPU or a CPU that does not have the 'cmov' " ++"extension. All packages were built with optimizations requiring i686 as the " ++"minimal architecture. It is not possible to upgrade your system to a new " ++"Ubuntu release with this hardware." ++msgstr "" ++"您的系统使用的是 i586 或是没有“cmov”扩展的 CPU。所有优化生成的软件包都需要最" ++"低 i686 的架构。这样的硬件无法升级到新的 Ubuntu 发行版。" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:652 ++msgid "No ARMv6 CPU" ++msgstr "没有 ARMv6 CPU" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:653 ++msgid "" ++"Your system uses an ARM CPU that is older than the ARMv6 architecture. All " ++"packages in karmic were built with optimizations requiring ARMv6 as the " ++"minimal architecture. It is not possible to upgrade your system to a new " ++"Ubuntu release with this hardware." ++msgstr "" ++"您的系统使用的是比 ARMv6 架构旧的 ARM CPU。karmic 中所有软件包构建时进行的优" ++"化都需要至少 ARMv6 的 CPU 架构。在这种硬件基础上无法将您的系统升级到一个新的 " ++"Ubuntu 发行版。" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:673 ++msgid "No init available" ++msgstr "无可用的 init" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:674 ++msgid "" ++"Your system appears to be a virtualised environment without an init daemon, " ++"e.g. Linux-VServer. Ubuntu 10.04 LTS cannot function within this type of " ++"environment, requiring an update to your virtual machine configuration " ++"first.\n" ++"\n" ++"Are you sure you want to continue?" ++msgstr "" ++"您的系统似乎运行在一个没有 init daemon 的虚拟化环境中(如 Linux-VServer)," ++"Ubuntu 10.04 LTS 无法在这种环境中运行,您需要先更新您的虚拟机配置。\n" ++"\n" ++"您确定要继续吗?" ++ ++#: ../DistUpgrade/DistUpgradeMain.py:65 ++msgid "Sandbox upgrade using aufs" ++msgstr "使用 aufs 进行沙盒升级" ++ ++#: ../DistUpgrade/DistUpgradeMain.py:67 ++msgid "Use the given path to search for a cdrom with upgradable packages" ++msgstr "使用所给的路径查找带升级包的 CD-ROM" ++ ++#: ../DistUpgrade/DistUpgradeMain.py:73 ++msgid "" ++"Use frontend. Currently available: \n" ++"DistUpgradeViewText, DistUpgradeViewGtk, DistUpgradeViewKDE" ++msgstr "" ++"使用前端。当前可用的有: \n" ++"DistUpgradeViewText, DistUpgradeViewGtk, DistUpgradeViewKDE" ++ ++#: ../DistUpgrade/DistUpgradeMain.py:76 ++msgid "*DEPRECATED* this option will be ignored" ++msgstr "*已废弃* 这个选项将被忽略" ++ ++#: ../DistUpgrade/DistUpgradeMain.py:79 ++msgid "Perform a partial upgrade only (no sources.list rewriting)" ++msgstr "仅执行部分升级(不重写 sources.list)" ++ ++#: ../DistUpgrade/DistUpgradeMain.py:82 ++msgid "Disable GNU screen support" ++msgstr "关闭对 GNU screen 的支持" ++ ++#: ../DistUpgrade/DistUpgradeMain.py:84 ++msgid "Set datadir" ++msgstr "设置数据目录" ++ ++#. print("mediaChange %s %s" % (medium, drive)) ++#: ../DistUpgrade/DistUpgradeViewGtk.py:114 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:117 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:195 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:155 ++#, python-format ++msgid "Please insert '%s' into the drive '%s'" ++msgstr "请将 '%s' 插入光驱 '%s'" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:135 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:138 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:209 ++msgid "Fetching is complete" ++msgstr "下载完成" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:146 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:149 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:222 ++#, python-format ++msgid "Fetching file %li of %li at %sB/s" ++msgstr "正在下载文件 %li/%li 速度 %s/s" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:149 ++#: ../DistUpgrade/DistUpgradeViewGtk.py:296 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:152 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:309 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:223 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:371 ++#, python-format ++msgid "About %s remaining" ++msgstr "大约还要 %s" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:152 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:155 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:225 ++#, python-format ++msgid "Fetching file %li of %li" ++msgstr "下载第 %li 个文件(共 %li 个文件)" ++ ++#. FIXME: add support for the timeout ++#. of the terminal (to display something useful then) ++#. -> longer term, move this code into python-apt ++#: ../DistUpgrade/DistUpgradeViewGtk.py:183 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:186 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:262 ++msgid "Applying changes" ++msgstr "正在应用更改" ++ ++#. we do not report followup errors from earlier failures ++#: ../DistUpgrade/DistUpgradeViewGtk.py:208 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:212 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:275 ++msgid "dependency problems - leaving unconfigured" ++msgstr "依赖关系问题 - 保持未配置状态" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:213 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:217 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:277 ++#, python-format ++msgid "Could not install '%s'" ++msgstr "无法安装“%s”" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:214 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:218 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:278 ++#, python-format ++msgid "" ++"The upgrade will continue but the '%s' package may not be in a working " ++"state. Please consider submitting a bug report about it." ++msgstr "升级将继续进行,但“%s”可能没有工作。请考虑提交关于它的错误报告。" ++ ++#. self.expander.set_expanded(True) ++#: ../DistUpgrade/DistUpgradeViewGtk.py:231 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:235 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:299 ++#, python-format ++msgid "" ++"Replace the customized configuration file\n" ++"'%s'?" ++msgstr "" ++"替换自定义配置文件\n" ++"“%s”吗?" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:232 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:236 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:300 ++msgid "" ++"You will lose any changes you have made to this configuration file if you " ++"choose to replace it with a newer version." ++msgstr "如果选择替换为新版本的配置文件,您将会失去所有已做的修改。" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:251 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:256 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:323 ++msgid "The 'diff' command was not found" ++msgstr "找不到 diff 命令" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:464 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:477 ++#: ../DistUpgrade/DistUpgradeViewText.py:92 ++msgid "A fatal error occurred" ++msgstr "出现致命错误" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:465 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:478 ++msgid "" ++"Please report this as a bug (if you haven't already) and include the files /" ++"var/log/dist-upgrade/main.log and /var/log/dist-upgrade/apt.log in your " ++"report. The upgrade has aborted.\n" ++"Your original sources.list was saved in /etc/apt/sources.list.distUpgrade." ++msgstr "" ++"请报告这个错误(如果还没有的话)并在报告中包括文件 /var/log/dist-upgrade/" ++"main.log 和 /var/log/dist-upgrade/apt.log 。升级已取消。\n" ++"您的原始 sources.list 已保存在 /etc/apt/sources.list.distUpgrade 。" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:482 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:495 ++msgid "Ctrl-c pressed" ++msgstr "Crtl+C 被按下" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:483 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:496 ++msgid "" ++"This will abort the operation and may leave the system in a broken state. " ++"Are you sure you want to do that?" ++msgstr "这将取消本次操作且可能使系统处于被破坏的状态。您确定要这样做?" ++ ++#. append warning ++#: ../DistUpgrade/DistUpgradeViewGtk.py:631 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:629 ++msgid "To prevent data loss close all open applications and documents." ++msgstr "为防止数据丢失,请关闭所有打开的应用程序和文档。" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:645 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:643 ++#, python-format ++msgid "No longer supported by Canonical (%s)" ++msgstr "不再被 Canonical 支持 (%s)" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:646 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:644 ++#, python-format ++msgid "<b>Downgrade (%s)</b>" ++msgstr "<b>降级 (%s)</b>" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:647 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:645 ++#, python-format ++msgid "Remove (%s)" ++msgstr "卸载 (%s)" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:648 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:646 ++#, python-format ++msgid "No longer needed (%s)" ++msgstr "不再需要 (%s)" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:649 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:647 ++#, python-format ++msgid "Install (%s)" ++msgstr "安装 (%s)" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:650 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:648 ++#, python-format ++msgid "Upgrade (%s)" ++msgstr "升级 (%s)" ++ ++#. change = QMessageBox.question(None, _("Media Change"), msg, QMessageBox.Ok, QMessageBox.Cancel) ++#: ../DistUpgrade/DistUpgradeViewKDE.py:196 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:157 ++msgid "Media Change" ++msgstr "改变介质" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:335 ++msgid "Show Difference >>>" ++msgstr "显示差别 >>>" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:338 ++msgid "<<< Hide Difference" ++msgstr "<<< 隐藏差别" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:554 ++msgid "Error" ++msgstr "错误" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:568 ++msgid "&Cancel" ++msgstr "取消(&C)" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:572 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:813 ++msgid "&Close" ++msgstr "关闭(&C)" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:618 ++msgid "Show Terminal >>>" ++msgstr "显示终端 >>>" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:621 ++msgid "<<< Hide Terminal" ++msgstr "<<< 隐藏终端" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:701 ++msgid "Information" ++msgstr "信息" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:751 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:796 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:799 ../DistUpgrade/DistUpgrade.ui.h:7 ++msgid "Details" ++msgstr "细节" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:777 ++#, python-format ++msgid "No longer supported %s" ++msgstr "不再支持 %s" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:779 ++#, python-format ++msgid "Remove %s" ++msgstr "移除 %s" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:781 ++#: ../DistUpgrade/DistUpgradeViewText.py:182 ++#, python-format ++msgid "Remove (was auto installed) %s" ++msgstr "移除(被自动安装的) %s" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:783 ++#, python-format ++msgid "Install %s" ++msgstr "安装 %s" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:785 ++#, python-format ++msgid "Upgrade %s" ++msgstr "升级 %s" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:809 ++#: ../DistUpgrade/DistUpgradeViewText.py:230 ++msgid "Restart required" ++msgstr "需要重启" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:809 ++msgid "<b><big>Restart the system to complete the upgrade</big></b>" ++msgstr "<b><big>重新启动系统以完成升级</big></b>" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:812 ../DistUpgrade/DistUpgrade.ui.h:14 ++#: ../data/gtkbuilder/UpdateManager.ui.h:26 ++msgid "_Restart Now" ++msgstr "现在重启(_R)" ++ ++#. FIXME make this user friendly ++#: ../DistUpgrade/DistUpgradeViewKDE.py:830 ++msgid "" ++"<b><big>Cancel the running upgrade?</big></b>\n" ++"\n" ++"The system could be in an unusable state if you cancel the upgrade. You are " ++"strongly advised to resume the upgrade." ++msgstr "" ++"<b><big>取消正在运行的升级?</big></b>\n" ++"\n" ++"如果取消升级系统可能不稳定,强烈建议您继续升级。" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:834 ++msgid "Cancel Upgrade?" ++msgstr "取消升级?" ++ ++#: ../DistUpgrade/DistUpgradeView.py:61 ++#, python-format ++msgid "%li day" ++msgid_plural "%li days" ++msgstr[0] "%li 天" ++ ++#: ../DistUpgrade/DistUpgradeView.py:63 ++#, python-format ++msgid "%li hour" ++msgid_plural "%li hours" ++msgstr[0] "%li 小时" ++ ++#: ../DistUpgrade/DistUpgradeView.py:65 ++#, python-format ++msgid "%li minute" ++msgid_plural "%li minutes" ++msgstr[0] "%li 分钟" ++ ++#: ../DistUpgrade/DistUpgradeView.py:66 ++#, python-format ++msgid "%li second" ++msgid_plural "%li seconds" ++msgstr[0] "%li 秒" ++ ++#. TRANSLATORS: you can alter the ordering of the remaining time ++#. information here if you shuffle %(str_days)s %(str_hours)s %(str_minutes)s ++#. around. Make sure to keep all '$(str_*)s' in the translated string ++#. and do NOT change anything appart from the ordering. ++#. ++#. %(str_hours)s will be either "1 hour" or "2 hours" depending on the ++#. plural form ++#. ++#. Note: most western languages will not need to change this ++#: ../DistUpgrade/DistUpgradeView.py:82 ++#, python-format ++msgid "%(str_days)s %(str_hours)s" ++msgstr "%(str_days)s %(str_hours)s" ++ ++#. TRANSLATORS: you can alter the ordering of the remaining time ++#. information here if you shuffle %(str_hours)s %(str_minutes)s ++#. around. Make sure to keep all '$(str_*)s' in the translated string ++#. and do NOT change anything appart from the ordering. ++#. ++#. %(str_hours)s will be either "1 hour" or "2 hours" depending on the ++#. plural form ++#. ++#. Note: most western languages will not need to change this ++#: ../DistUpgrade/DistUpgradeView.py:100 ++#, python-format ++msgid "%(str_hours)s %(str_minutes)s" ++msgstr "%(str_hours)s %(str_minutes)s" ++ ++#. 56 kbit ++#. 1Mbit = 1024 kbit ++#: ../DistUpgrade/DistUpgradeView.py:151 ++#, python-format ++msgid "" ++"This download will take about %s with a 1Mbit DSL connection and about %s " ++"with a 56k modem." ++msgstr "" ++"使用 1Mbit 的 DSL 连接下载需要大约 %s 秒的时间, 使用 56K 的调制解调器连接大" ++"约需要 %s 秒时间。" ++ ++#. if we have a estimated speed, use it ++#: ../DistUpgrade/DistUpgradeView.py:155 ++#, python-format ++msgid "This download will take about %s with your connection. " ++msgstr "根据您的连接速度,这次下载将要用大约 %s 的时间 " ++ ++#. Declare these translatable strings from the .ui files here so that ++#. xgettext picks them up. ++#: ../DistUpgrade/DistUpgradeView.py:259 ../DistUpgrade/DistUpgrade.ui.h:21 ++msgid "Preparing to upgrade" ++msgstr "正在准备升级" ++ ++#: ../DistUpgrade/DistUpgradeView.py:260 ++msgid "Getting new software channels" ++msgstr "获得新的软件源" ++ ++#: ../DistUpgrade/DistUpgradeView.py:261 ../DistUpgrade/DistUpgrade.ui.h:23 ++msgid "Getting new packages" ++msgstr "获取新的软件包" ++ ++#: ../DistUpgrade/DistUpgradeView.py:262 ../DistUpgrade/DistUpgrade.ui.h:26 ++msgid "Installing the upgrades" ++msgstr "正在安装升级" ++ ++#: ../DistUpgrade/DistUpgradeView.py:263 ../DistUpgrade/DistUpgrade.ui.h:25 ++msgid "Cleaning up" ++msgstr "正在清理" ++ ++#: ../DistUpgrade/DistUpgradeView.py:348 ++#, python-format ++msgid "" ++"%(amount)d installed package is no longer supported by Canonical. You can " ++"still get support from the community." ++msgid_plural "" ++"%(amount)d installed packages are no longer supported by Canonical. You can " ++"still get support from the community." ++msgstr[0] "" ++"%(amount)d 个已安装的软件包不再被 Canonical 支持。您仍然可以获得社区支持。" ++ ++#. FIXME: make those two separate lines to make it clear ++#. that the "%" applies to the result of ngettext ++#: ../DistUpgrade/DistUpgradeView.py:357 ++#, python-format ++msgid "%d package is going to be removed." ++msgid_plural "%d packages are going to be removed." ++msgstr[0] "将删除 %d 个软件包。" ++ ++#: ../DistUpgrade/DistUpgradeView.py:362 ++#, python-format ++msgid "%d new package is going to be installed." ++msgid_plural "%d new packages are going to be installed." ++msgstr[0] "将安装 %d 个新的软件包。" ++ ++#: ../DistUpgrade/DistUpgradeView.py:368 ++#, python-format ++msgid "%d package is going to be upgraded." ++msgid_plural "%d packages are going to be upgraded." ++msgstr[0] "将升级 %d 个软件包。" ++ ++#: ../DistUpgrade/DistUpgradeView.py:373 ++#, python-format ++msgid "" ++"\n" ++"\n" ++"You have to download a total of %s. " ++msgstr "" ++"\n" ++"\n" ++"您共需下载 %s。 " ++ ++#: ../DistUpgrade/DistUpgradeView.py:378 ++msgid "" ++"Installing the upgrade can take several hours. Once the download has " ++"finished, the process cannot be canceled." ++msgstr "安装升级可能会耗费几小时的时间。一旦下载完毕就不能取消该进程。" ++ ++#: ../DistUpgrade/DistUpgradeView.py:382 ++msgid "" ++"Fetching and installing the upgrade can take several hours. Once the " ++"download has finished, the process cannot be canceled." ++msgstr "" ++"升级文件的获取和安装可能会耗费几小时的时间。一旦下载完毕就不能取消该进程。" ++ ++#: ../DistUpgrade/DistUpgradeView.py:387 ++msgid "Removing the packages can take several hours. " ++msgstr "包的删除可能会耗费几小时的时间。 " ++ ++#. FIXME: this should go into DistUpgradeController ++#: ../DistUpgrade/DistUpgradeView.py:392 ../UpdateManager/UpdateManager.py:676 ++msgid "The software on this computer is up to date." ++msgstr "此计算机软件是最新的" ++ ++#: ../DistUpgrade/DistUpgradeView.py:393 ++msgid "" ++"There are no upgrades available for your system. The upgrade will now be " ++"canceled." ++msgstr "你的系统没有可用升级。升级被取消。" ++ ++#: ../DistUpgrade/DistUpgradeView.py:406 ++msgid "Reboot required" ++msgstr "需要重启" ++ ++#: ../DistUpgrade/DistUpgradeView.py:407 ++msgid "" ++"The upgrade is finished and a reboot is required. Do you want to do this now?" ++msgstr "升级已经完成并需要重启。您要现在重启么?" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:72 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:72 ++#, python-format ++msgid "authenticate '%(file)s' against '%(signature)s' " ++msgstr "使用 '%(signature)s' 对 '%(file)s' 进行验证 " ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:131 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:131 ++#, python-format ++msgid "extracting '%s'" ++msgstr "正在提取 '%s'" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:151 ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:152 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:151 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:152 ++msgid "Could not run the upgrade tool" ++msgstr "不能运行升级工具" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:152 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:152 ++msgid "" ++"This is most likely a bug in the upgrade tool. Please report it as a bug " ++"using the command 'ubuntu-bug update-manager'." ++msgstr "" ++"这可能是升级工具的一个 bug,请使用命令‘ubuntu-bug update-manager’来报告这个 " ++"bug。" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:227 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:227 ++msgid "Upgrade tool signature" ++msgstr "升级工具签名" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:234 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:234 ++msgid "Upgrade tool" ++msgstr "升级工具" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:268 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:268 ++msgid "Failed to fetch" ++msgstr "下载失败" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:269 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:269 ++msgid "Fetching the upgrade failed. There may be a network problem. " ++msgstr "获取升级信息失败。可能网络有问题。 " ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:273 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:273 ++msgid "Authentication failed" ++msgstr "认证失败" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:274 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:274 ++msgid "" ++"Authenticating the upgrade failed. There may be a problem with the network " ++"or with the server. " ++msgstr "认证升级信息失败。可能是网络或服务器的问题。 " ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:279 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:279 ++msgid "Failed to extract" ++msgstr "提取失败" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:280 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:280 ++msgid "" ++"Extracting the upgrade failed. There may be a problem with the network or " ++"with the server. " ++msgstr "提取升级信息失败。可能是网络或服务器的问题。 " ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:285 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:285 ++msgid "Verification failed" ++msgstr "验证失败" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:286 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:286 ++msgid "" ++"Verifying the upgrade failed. There may be a problem with the network or " ++"with the server. " ++msgstr "验证升级程序失败。可能是网络或服务器的问题。 " ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:300 ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:306 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:300 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:306 ++msgid "Can not run the upgrade" ++msgstr "不能运行升级" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:301 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:301 ++msgid "" ++"This usually is caused by a system where /tmp is mounted noexec. Please " ++"remount without noexec and run the upgrade again." ++msgstr "" ++"可能的原因是系统的 /tmp 目录无可执行权限。请以可执行权限重新挂载该目录,重新" ++"升级。" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:307 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:307 ++#, python-format ++msgid "The error message is '%s'." ++msgstr "错误信息是“%s”。" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:93 ++msgid "" ++"Please report this as a bug and include the files /var/log/dist-upgrade/main." ++"log and /var/log/dist-upgrade/apt.log in your report. The upgrade has " ++"aborted.\n" ++"Your original sources.list was saved in /etc/apt/sources.list.distUpgrade." ++msgstr "" ++"请报告这个错误并在报告中包括文件 /var/log/dist-upgrade/main.log 和 /var/log/" ++"dist-upgrade/apt.log 。升级已取消。\n" ++"您的原始 sources.list 已保存在 /etc/apt/sources.list.distUpgrade 。" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:117 ++msgid "Aborting" ++msgstr "中止" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:122 ++msgid "Demoted:\n" ++msgstr "降级:\n" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:129 ++msgid "To continue please press [ENTER]" ++msgstr "按 [ENTER] 键以继续" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:157 ++#: ../DistUpgrade/DistUpgradeViewText.py:196 ++#: ../DistUpgrade/DistUpgradeViewText.py:203 ++msgid "Continue [yN] " ++msgstr "继续 [yN] " ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:157 ++#: ../DistUpgrade/DistUpgradeViewText.py:196 ++msgid "Details [d]" ++msgstr "详细信息[d]" ++ ++#. TRANSLATORS: the "y" is "yes" ++#. TRANSLATORS: first letter of a positive (yes) answer ++#: ../DistUpgrade/DistUpgradeViewText.py:162 ++#: ../DistUpgrade/DistUpgradeViewText.py:206 ++msgid "y" ++msgstr "y" ++ ++#. TRANSLATORS: the "n" is "no" ++#. TRANSLATORS: first letter of a negative (no) answer ++#: ../DistUpgrade/DistUpgradeViewText.py:165 ++#: ../DistUpgrade/DistUpgradeViewText.py:213 ++msgid "n" ++msgstr "n" ++ ++#. TRANSLATORS: the "d" is "details" ++#: ../DistUpgrade/DistUpgradeViewText.py:168 ++msgid "d" ++msgstr "详" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:173 ++#, python-format ++msgid "No longer supported: %s\n" ++msgstr "不再支持:%s\n" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:178 ++#, python-format ++msgid "Remove: %s\n" ++msgstr "移除:%s\n" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:188 ++#, python-format ++msgid "Install: %s\n" ++msgstr "安装:%s\n" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:193 ++#, python-format ++msgid "Upgrade: %s\n" ++msgstr "升级:%s\n" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:210 ++msgid "Continue [Yn] " ++msgstr "继续 [Yn] " ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:231 ++msgid "" ++"To finish the upgrade, a restart is required.\n" ++"If you select 'y' the system will be restarted." ++msgstr "" ++"为了完成升级,必须重新启动。\n" ++"如果你选择“是”,系统将会重新启动。" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:1 ++msgid "_Cancel Upgrade" ++msgstr "取消升级" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:2 ++msgid "_Resume Upgrade" ++msgstr "恢复升级(_R)" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:3 ++msgid "" ++"<b><big>Cancel the running upgrade?</big></b>\n" ++"\n" ++"The system could be in an unusable state if you cancel the upgrade. You are " ++"strongly adviced to resume the upgrade." ++msgstr "" ++"<b><big>取消正在运行的升级?</big></b>\n" ++"如果您取消升级,系统可能不稳定。强烈建议您继续升级。" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:6 ++msgid "_Start Upgrade" ++msgstr "开始升级(_R)" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:9 ++msgid "_Replace" ++msgstr "替换(_R)" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:10 ++msgid "Difference between the files" ++msgstr "文件之间的差别" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:11 ++msgid "_Report Bug" ++msgstr "报告 Bug(_R)" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:12 ++msgid "_Continue" ++msgstr "继续(_C)" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:13 ++msgid "<b><big>Start the upgrade?</big></b>" ++msgstr "<b><big>开始升级?</big></b>" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:15 ++msgid "" ++"<b><big>Restart the system to complete the upgrade</big></b>\n" ++"\n" ++"Please save your work before continuing." ++msgstr "" ++"<b><big>重启系统以完成升级</big></b>\n" ++"\n" ++"请在继续前保存你的工作。" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:18 ++msgid "Distribution Upgrade" ++msgstr "发行版升级" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:19 ++#, fuzzy ++msgid "<b><big>Upgrading Ubuntu to version 12.10</big></b>" ++msgstr "<b><big>把 Ubuntu 升级到 11.10</big></b>" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:20 ++msgid " " ++msgstr " " ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:22 ++msgid "Setting new software channels" ++msgstr "设定新的软件源" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:24 ++msgid "Restarting the computer" ++msgstr "系统正在重新启动" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:27 ++msgid "Terminal" ++msgstr "终端" ++ ++#: ../UpdateManager/backend/InstallBackendSynaptic.py:64 ++msgid "Please wait, this can take some time." ++msgstr "请稍候,这需要花一些时间。" ++ ++#: ../UpdateManager/backend/InstallBackendSynaptic.py:66 ++msgid "Update is complete" ++msgstr "更新完成" ++ ++#: ../UpdateManager/DistUpgradeFetcher.py:114 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:109 ++msgid "Could not find the release notes" ++msgstr "无法找到发行注记" ++ ++#: ../UpdateManager/DistUpgradeFetcher.py:115 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:110 ++msgid "The server may be overloaded. " ++msgstr "服务器可能已过载。 " ++ ++#: ../UpdateManager/DistUpgradeFetcher.py:125 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:114 ++msgid "Could not download the release notes" ++msgstr "无法下载发行说明" ++ ++#: ../UpdateManager/DistUpgradeFetcher.py:126 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:115 ++msgid "Please check your internet connection." ++msgstr "请检查您的互联网连接。" ++ ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:68 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:91 ++msgid "Upgrade" ++msgstr "升级" ++ ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:95 ++#: ../data/gtkbuilder/UpdateManager.ui.h:20 ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:2 ++msgid "Release Notes" ++msgstr "发行注记" ++ ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:134 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:148 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:150 ++msgid "Downloading additional package files..." ++msgstr "正在下载附加软件包..." ++ ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:148 ++#, python-format ++msgid "File %s of %s at %sB/s" ++msgstr "文件 %s/%s 速度: %sB/s" ++ ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:150 ++#, python-format ++msgid "File %s of %s" ++msgstr "文件 %s/%s" ++ ++#: ../UpdateManager/ChangelogViewer.py:75 ++msgid "Open Link in Browser" ++msgstr "在浏览器中打开链接" ++ ++#: ../UpdateManager/ChangelogViewer.py:78 ++msgid "Copy Link to Clipboard" ++msgstr "复制链接到剪贴板" ++ ++#: ../UpdateManager/GtkProgress.py:162 ++#, python-format ++msgid "Downloading file %(current)li of %(total)li with %(speed)s/s" ++msgstr "正在下载文件 %(current)li/%(total)li 速度 %(speed)s/s" ++ ++#: ../UpdateManager/GtkProgress.py:167 ++#, python-format ++msgid "Downloading file %(current)li of %(total)li" ++msgstr "正在下载文件 %(current)li/%(total)li" ++ ++#: ../UpdateManager/UpdateManager.py:106 ../do-release-upgrade:100 ++msgid "Your Ubuntu release is not supported anymore." ++msgstr "不再提供您的 Ubuntu 版本的支持。" ++ ++#: ../UpdateManager/UpdateManager.py:107 ++msgid "" ++"You will not get any further security fixes or critical updates. Please " ++"upgrade to a later version of Ubuntu." ++msgstr "不会得到进一步的安全修补程序或关键更新。请升级到较新的 Ubuntu 版本。" ++ ++#: ../UpdateManager/UpdateManager.py:115 ++msgid "Upgrade information" ++msgstr "升级信息" ++ ++#: ../UpdateManager/UpdateManager.py:233 ++#: ../UpdateManagerText/UpdateManagerText.py:35 ++msgid "Install" ++msgstr "安装" ++ ++#: ../UpdateManager/UpdateManager.py:235 ++msgid "Name" ++msgstr "名称" ++ ++#. upload_archive = version_match.group(2).strip() ++#: ../UpdateManager/UpdateManager.py:395 ++#, python-format ++msgid "Version %s: \n" ++msgstr "版本 %s: \n" ++ ++#: ../UpdateManager/UpdateManager.py:453 ++msgid "" ++"No network connection detected, you can not download changelog information." ++msgstr "未检测到网络连接,您无法下载更新日志。" ++ ++#: ../UpdateManager/UpdateManager.py:463 ++msgid "Downloading list of changes..." ++msgstr "正在下载更新列表..." ++ ++#: ../UpdateManager/UpdateManager.py:507 ++msgid "_Deselect All" ++msgstr "全部不选(_D)" ++ ++#: ../UpdateManager/UpdateManager.py:513 ++msgid "Select _All" ++msgstr "选择全部(_A)" ++ ++#: ../UpdateManager/UpdateManager.py:572 ++#, python-format ++msgid "%s will be downloaded." ++msgstr "将会下载 %s。" ++ ++#: ../UpdateManager/UpdateManager.py:584 ++#, fuzzy ++msgid "The update has already been downloaded." ++msgid_plural "The updates have already been downloaded." ++msgstr[0] "该更新已下载完毕但未安装。" ++ ++#: ../UpdateManager/UpdateManager.py:589 ++msgid "There are no updates to install." ++msgstr "" ++ ++#: ../UpdateManager/UpdateManager.py:598 ++msgid "Unknown download size." ++msgstr "未知下载大小。" ++ ++#: ../UpdateManager/UpdateManager.py:624 ++msgid "" ++"It is unknown when the package information was updated last. Please click " ++"the 'Check' button to update the information." ++msgstr "软件包信息上次更新的时间未知。请点击“检查”按钮更新软件包信息。" ++ ++#: ../UpdateManager/UpdateManager.py:630 ++#, python-format ++msgid "" ++"The package information was last updated %(days_ago)s days ago.\n" ++"Press the 'Check' button below to check for new software updates." ++msgstr "" ++"%(days_ago)s 天前最后一次更新软件包信息。\n" ++"要检查软件更新,点击下面的“检查”按钮。" ++ ++#: ../UpdateManager/UpdateManager.py:635 ++#, python-format ++msgid "The package information was last updated %(days_ago)s day ago." ++msgid_plural "The package information was last updated %(days_ago)s days ago." ++msgstr[0] "软件包信息上次是在 %(days_ago)s 天前更新的。" ++ ++#: ../UpdateManager/UpdateManager.py:639 ++#, python-format ++msgid "The package information was last updated %(hours_ago)s hour ago." ++msgid_plural "" ++"The package information was last updated %(hours_ago)s hours ago." ++msgstr[0] "软件包信息上次是在 %(hours_ago)s 小时前更新的。" ++ ++#. TRANSLATORS: only in plural form, as %s minutes ago is one of 15, 30, 45 minutes ago ++#: ../UpdateManager/UpdateManager.py:644 ../UpdateManager/UpdateManager.py:646 ++#: ../UpdateManager/UpdateManager.py:648 ++#, python-format ++msgid "The package information was last updated about %s minutes ago." ++msgstr "该软件包信息已在 %s 分钟之前更新。" ++ ++#: ../UpdateManager/UpdateManager.py:650 ++msgid "The package information was just updated." ++msgstr "该软件包信息刚刚更新。" ++ ++#: ../UpdateManager/UpdateManager.py:689 ++msgid "Software updates may be available for your computer." ++msgstr "您的计算机有可用的软件更新。" ++ ++#: ../UpdateManager/UpdateManager.py:697 ++#, python-format ++msgid "" ++"Updated software has been issued since %s was released. Do you want to " ++"install it now?" ++msgstr "" ++ ++#: ../UpdateManager/UpdateManager.py:700 ++msgid "" ++"Updated software is available for this computer. Do you want to install it " ++"now?" ++msgstr "" ++ ++#: ../UpdateManager/UpdateManager.py:758 ++#, python-format ++msgid "" ++"The upgrade needs a total of %s free space on disk '%s'. Please free at " ++"least an additional %s of disk space on '%s'. Empty your trash and remove " ++"temporary packages of former installations using 'sudo apt-get clean'." ++msgstr "" ++"这个更新需要花去 %s 磁盘上总计 %s 的空间。请在 %s 磁盘上留出 %s 空间。清空您" ++"的回收站和临时文件,用“sudo apt-get clean”清理以前的安装文件。" ++ ++#: ../UpdateManager/UpdateManager.py:783 ++msgid "" ++"The computer needs to restart to finish installing updates. Please save your " ++"work before continuing." ++msgstr "计算机需要重新启动来完成更新。请在继续操作前保存好您的工作。" ++ ++#: ../UpdateManager/UpdateManager.py:847 ++msgid "Reading package information" ++msgstr "读取软件包信息" ++ ++#: ../UpdateManager/UpdateManager.py:862 ++msgid "Connecting..." ++msgstr "正在连接..." ++ ++#: ../UpdateManager/UpdateManager.py:879 ++msgid "You may not be able to check for updates or download new updates." ++msgstr "您可能无法检查更新或下载新的更新。" ++ ++#: ../UpdateManager/UpdateManager.py:1002 ++msgid "Could not initialize the package information" ++msgstr "无法初始化软件包信息" ++ ++msgid "An unresolvable problem occurred while initializing the package." ++msgstr "初始化包信息时遇到无法解决的问题。" ++ ++#: ../UpdateManager/UpdateManager.py:1003 ++msgid "" ++"An unresolvable problem occurred while initializing the package " ++"information.\n" ++"\n" ++"Please report this bug against the 'kylin-system-updater' package and include the " ++"following error message:\n" ++msgstr "" ++"初始化包信息时遇到无法解决的问题。\n" ++"\n" ++"请汇报这个“kylin-system-updater”软件包的错误,并且将如下信息包含在报告中:\n" ++ ++#: ../UpdateManager/UpdateManager.py:1032 ++msgid "" ++"An unresolvable problem occurred while calculating the upgrade.\n" ++"\n" ++"Please report this bug against the 'kylin-system-updater' package and include the " ++"following error message:" ++msgstr "" ++"在计算升级时遇到一个无法解决的问题。\n" ++"\n" ++"请汇报这个有关“update-manager”软件包的错误,并且将下列错误信息包含在错误报告" ++"中:" ++ ++#: ../UpdateManager/UpdateManager.py:1056 ++msgid " (New install)" ++msgstr " (新安装)" ++ ++#. TRANSLATORS: the b stands for Bytes ++#: ../UpdateManager/UpdateManager.py:1063 ++#, python-format ++msgid "(Size: %s)" ++msgstr "(大小:%s)" ++ ++#: ../UpdateManager/UpdateManager.py:1067 ++#, python-format ++msgid "From version %(old_version)s to %(new_version)s" ++msgstr "从版本 %(old_version)s 到 %(new_version)s" ++ ++#: ../UpdateManager/UpdateManager.py:1071 ++#, python-format ++msgid "Version %s" ++msgstr "版本 %s" ++ ++#: ../UpdateManager/UpdateManager.py:1104 ../do-release-upgrade:112 ++msgid "Release upgrade not possible right now" ++msgstr "版本升级目前不可用" ++ ++#: ../UpdateManager/UpdateManager.py:1105 ../do-release-upgrade:113 ++#, c-format, python-format ++msgid "" ++"The release upgrade can not be performed currently, please try again later. " ++"The server reported: '%s'" ++msgstr "发行版目前无法更新,请再试一次。服务器报告:'%s'" ++ ++#: ../UpdateManager/UpdateManager.py:1107 ../check-new-release-gtk:126 ++msgid "Downloading the release upgrade tool" ++msgstr "正在下载发布升级工具" ++ ++#: ../UpdateManager/UpdateManager.py:1114 ++#, python-format ++msgid "<b>New Ubuntu release '%s' is available</b>" ++msgstr "<b>新的 Ubuntu 发行版 %s 可用</b>" ++ ++#. we assert a clean cache ++#: ../UpdateManager/UpdateManager.py:1153 ++msgid "Software index is broken" ++msgstr "软件索引已经损坏" ++ ++#: ../UpdateManager/UpdateManager.py:1154 ++msgid "" ++"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." ++msgstr "" ++"无法安装或删除任何软件。请在终端运行 \"sudo apt-get " ++"install -f\" 来修正这个问题。" ++ ++#: ../UpdateManager/UnitySupport.py:57 ++msgid "Check for Updates" ++msgstr "检查更新" ++ ++#: ../UpdateManager/UnitySupport.py:66 ++msgid "Install All Available Updates" ++msgstr "安装全部可利用的更新" ++ ++#: ../UpdateManagerText/UpdateManagerText.py:34 ++msgid "Cancel" ++msgstr "取消" ++ ++#: ../UpdateManagerText/UpdateManagerText.py:37 ++msgid "Changelog" ++msgstr "变更日志" ++ ++#: ../UpdateManagerText/UpdateManagerText.py:40 ++msgid "Updates" ++msgstr "更新" ++ ++#: ../UpdateManagerText/UpdateManagerText.py:53 ++msgid "Building Updates List" ++msgstr "正在建立更新列表" ++ ++#: ../UpdateManagerText/UpdateManagerText.py:56 ++msgid "" ++"\n" ++"A normal upgrade can not be calculated, please run: \n" ++" sudo apt-get dist-upgrade\n" ++"\n" ++"\n" ++"This can be caused by:\n" ++" * A previous upgrade which didn't complete\n" ++" * Problems with some of the installed software\n" ++" * Unofficial software packages not provided by Ubuntu\n" ++" * Normal changes of a pre-release version of Ubuntu" ++msgstr "" ++"\n" ++"一个普通升级无法被计算,请运行: \n" ++" sudo apt-get dist-upgrade\n" ++"\n" ++"\n" ++"这可能是因为:\n" ++" * 先前的升级尚未完成\n" ++" * 已安装软件引起的问题\n" ++" * 不是由 Ubuntu 提供的非官方软件包\n" ++" * Ubuntu 预发行版本的普通更改" ++ ++#: ../UpdateManagerText/UpdateManagerText.py:125 ++msgid "Downloading changelog" ++msgstr "正在下载变更日志" ++ ++#: ../UpdateManager/Core/MyCache.py:147 ++#, python-format ++msgid "Other updates (%s)" ++msgstr "其它更新 (%s)" ++ ++#: ../UpdateManager/Core/MyCache.py:325 ++msgid "This update does not come from a source that supports changelogs." ++msgstr "" ++ ++#: ../UpdateManager/Core/MyCache.py:331 ../UpdateManager/Core/MyCache.py:359 ++msgid "" ++"Failed to download the list of changes. \n" ++"Please check your Internet connection." ++msgstr "" ++ ++#: ../UpdateManager/Core/MyCache.py:338 ++#, python-format ++msgid "" ++"Changes for the versions:\n" ++"Installed version: %s\n" ++"Available version: %s\n" ++"\n" ++msgstr "" ++"版本变更:\n" ++"已安装的版本:%s\n" ++"可用版本:%s\n" ++"\n" ++ ++#: ../UpdateManager/Core/MyCache.py:348 ++#, python-format ++msgid "" ++"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." ++msgstr "" ++ ++#: ../UpdateManager/Core/MyCache.py:353 ++#, python-format ++msgid "" ++"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." ++msgstr "" ++ ++#: ../UpdateManager/Core/UpdateList.py:51 ++msgid "Failed to detect distribution" ++msgstr "检测发行版失败" ++ ++#: ../UpdateManager/Core/UpdateList.py:52 ++#, python-format ++msgid "A error '%s' occurred while checking what system you are using." ++msgstr "检查您正在使用哪种系统的时候发生一个错误 “%s”。" ++ ++#: ../UpdateManager/Core/UpdateList.py:63 ++msgid "Important security updates" ++msgstr "重要安全更新" ++ ++#: ../UpdateManager/Core/UpdateList.py:64 ++msgid "Recommended updates" ++msgstr "推荐更新" ++ ++#: ../UpdateManager/Core/UpdateList.py:65 ++msgid "Proposed updates" ++msgstr "提前释放的更新" ++ ++#: ../UpdateManager/Core/UpdateList.py:66 ++msgid "Backports" ++msgstr "Backports" ++ ++#: ../UpdateManager/Core/UpdateList.py:67 ++msgid "Distribution updates" ++msgstr "发行版升级" ++ ++#: ../UpdateManager/Core/UpdateList.py:72 ++msgid "Other updates" ++msgstr "其它更新" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:1 ++#, fuzzy ++msgid "<big><b>Starting Software Updater</b></big>" ++msgstr "<b><big>运行升级管理器</big></b>" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:2 ++msgid "" ++"Software updates correct errors, eliminate security vulnerabilities and " ++"provide new features." ++msgstr "软件更新可以为您修复错误,消除安全漏洞及提供新的特性" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:3 ++msgid "_Partial Upgrade" ++msgstr "部分升级(_P)" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:4 ++msgid "<big><b>Not all updates can be installed</b></big>" ++msgstr "<big><b>没有更新可供安装</b></big>" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:5 ++msgid "" ++"Run a partial upgrade, to install as many updates as possible. \n" ++"\n" ++"This can be caused by:\n" ++" * A previous upgrade which didn't complete\n" ++" * Problems with some of the installed software\n" ++" * Unofficial software packages not provided by Ubuntu\n" ++" * Normal changes of a pre-release version of Ubuntu" ++msgstr "" ++"执行一次部分升级,安装尽可能多的更新。 \n" ++"\n" ++"这可能是下面的情况引起的:\n" ++" * 前一次升级没有完成\n" ++" * 一些已经安装的软件的问题\n" ++" *Ubuntu 不支持的非官方软件包\n" ++" *Ubuntu 预发布版本的正常变动" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:12 ++msgid "Chec_k" ++msgstr "检查(_K)" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:13 ++msgid "" ++"<b><big>You must check for updates manually</big></b>\n" ++"\n" ++"Your system does not check for updates automatically. You can configure this " ++"behavior in <i>Software Sources</i> on the <i>Updates</i> tab." ++msgstr "" ++"<b><big>您必须手动检测升级</big></b>\n" ++"\n" ++"您的系统未自动检测升级,可以通过编辑<i>软件源</i>在<i>网络升级</i>中更改。" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:16 ++msgid "_Hide this information in the future" ++msgstr "不再显示该信息(_H)" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:17 ++msgid "Co_ntinue" ++msgstr "继续(_N)" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:18 ++msgid "<big><b>Running on battery</b></big>" ++msgstr "<big><b>电池供电</b></big>" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:19 ++msgid "Your system is running on battery. Are you sure you want to continue?" ++msgstr "您的系统正在使用电池。 您确定要继续吗?" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:21 ++msgid "_Upgrade" ++msgstr "升级(_U)" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:22 ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:8 ++msgid "Show progress of individual files" ++msgstr "显示单个文件进度" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:23 ++#: ../data/update-manager.desktop.in.h:1 ++#, fuzzy ++msgid "Software Updater" ++msgstr "软件更新" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:24 ++#, fuzzy ++msgid "Starting Software Updater" ++msgstr "软件更新" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:25 ++msgid "U_pgrade" ++msgstr "升级(_P)" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:27 ++msgid "updates" ++msgstr "更新" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:28 ++msgid "Changes" ++msgstr "变更" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:29 ++msgid "Description" ++msgstr "描述" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:30 ++#, fuzzy ++msgid "Details of updates" ++msgstr "升级描述" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:31 ++msgid "" ++"You are connected via roaming and may be charged for the data consumed by " ++"this update." ++msgstr "您正在使用漫游连接,更新可能会耗费很多流量。" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:32 ++msgid "" ++"You may want to wait until you’re not using a mobile broadband connection." ++msgstr "" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:33 ++msgid "It’s safer to connect the computer to AC power before updating." ++msgstr "在更新前最好将计算机连接电源。" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:34 ++msgid "_Settings..." ++msgstr "设置(_S)..." ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:35 ++#, fuzzy ++msgid "_Install Now" ++msgstr "安装" ++ ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:1 ++msgid "<b>A new version of Ubuntu is available. Would you like to upgrade?</b>" ++msgstr "<b>有新版本的 Ubuntu 可用。您想升级吗?</b>" ++ ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:3 ++msgid "Don't Upgrade" ++msgstr "不升级" ++ ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:4 ++msgid "Ask Me Later" ++msgstr "稍后询问" ++ ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:5 ++msgid "Yes, Upgrade Now" ++msgstr "是的,现在就升级" ++ ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:6 ++msgid "You have declined to upgrade to the new Ubuntu" ++msgstr "您已经拒绝升级到新版本的 Ubuntu" ++ ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:7 ++#, fuzzy ++msgid "" ++"You can upgrade at a later time by opening Software Updater and click on " ++"\"Upgrade\"." ++msgstr "如果您希望以后再升级,可以打开更新管理器,然后点击“升级”。" ++ ++#: ../data/update-manager.desktop.in.h:2 ++msgid "Software Updates" ++msgstr "软件更新" ++ ++#: ../data/update-manager.desktop.in.h:3 ++msgid "Show and install available updates" ++msgstr "显示并安装可用更新" ++ ++#: ../update-manager:66 ../update-manager-text:55 ../do-release-upgrade:51 ++msgid "Show version and exit" ++msgstr "显示版本并退出" ++ ++#: ../update-manager:69 ++msgid "Directory that contains the data files" ++msgstr "包含数据文件的文件夹" ++ ++#: ../update-manager:72 ++msgid "Check if a new Ubuntu release is available" ++msgstr "检查是否有新的 Ubuntu 发行版可用" ++ ++#: ../update-manager:75 ../do-release-upgrade:54 ../check-new-release-gtk:186 ++msgid "Check if upgrading to the latest devel release is possible" ++msgstr "验证是否能够升级到最新版本" ++ ++#: ../update-manager:79 ++msgid "Upgrade using the latest proposed version of the release upgrader" ++msgstr "使用最新 proposed 版本的升级器升级" ++ ++#: ../update-manager:86 ++msgid "Do not focus on map when starting" ++msgstr "开始时焦点不要定位在图上" ++ ++#: ../update-manager:89 ++msgid "Try to run a dist-upgrade" ++msgstr "尝试进行版本升级" ++ ++#: ../update-manager:92 ++msgid "Do not check for updates when starting" ++msgstr "启动时不检查更新" ++ ++#: ../update-manager:96 ../do-release-upgrade:70 ++msgid "Test upgrade with a sandbox aufs overlay" ++msgstr "进行带沙盒 aufs 隔离层的测试升级" ++ ++#: ../update-manager:116 ++msgid "Running partial upgrade" ++msgstr "运行部分升级" ++ ++msgid " Will be deleted Due to Upgrade " ++msgstr " 将要被删除,由于升级 " ++ ++msgid " Will be deleted Due to Install or Upgrade " ++msgstr " 将要被删除,由于安装 " ++ ++#: ../update-manager-text:59 ++msgid "Show description of the package instead of the changelog" ++msgstr "显示包的说明而不是变更日志" ++ ++#: ../do-release-upgrade:58 ../check-new-release-gtk:190 ++msgid "" ++"Try upgrading to the latest release using the upgrader from $distro-proposed" ++msgstr "尝试通过 $distro-proposed 更新到最新版本。" ++ ++#: ../do-release-upgrade:62 ++msgid "" ++"Run in a special upgrade mode.\n" ++"Currently 'desktop' for regular upgrades of a desktop system and 'server' " ++"for server systems are supported." ++msgstr "" ++"以特定模式升级。\n" ++"目前支持:用“桌面”为桌面系统,“服务器”为服务器系统升级。" ++ ++#: ../do-release-upgrade:68 ++msgid "Run the specified frontend" ++msgstr "运行指定的前端" ++ ++#: ../do-release-upgrade:73 ++msgid "" ++"Check only if a new distribution release is available and report the result " ++"via the exit code" ++msgstr "仅在有新的发行版发布时检查,并通过退出码(exit code)报告结果" ++ ++#: ../do-release-upgrade:87 ++msgid "Checking for a new Ubuntu release" ++msgstr "正在检查新版 Ubuntu" ++ ++#: ../do-release-upgrade:101 ++msgid "" ++"For upgrade information, please visit:\n" ++"%(url)s\n" ++msgstr "" ++"要获得关于升级的信息,请访问:\n" ++"%(url)s\n" ++ ++#: ../do-release-upgrade:107 ++msgid "No new release found" ++msgstr "未发现新版本" ++ ++#: ../do-release-upgrade:119 ++#, c-format ++msgid "New release '%s' available." ++msgstr "有新版本“%s”可供使用" ++ ++#: ../do-release-upgrade:120 ++msgid "Run 'do-release-upgrade' to upgrade to it." ++msgstr "运行“do-release-upgrade”来升级到新版本。" ++ ++#: ../check-new-release-gtk:101 ++msgid "Ubuntu %(version)s Upgrade Available" ++msgstr "Ubuntu %(version)s 升级可用" ++ ++#: ../check-new-release-gtk:143 ++#, c-format ++msgid "You have declined the upgrade to Ubuntu %s" ++msgstr "您已经拒绝升级到 Ubuntu %s" ++ ++#: ../check-new-release-gtk:196 ++msgid "Add debug output" ++msgstr "添加调试输出" ++ ++#: ../ubuntu-support-status:91 ++msgid "Show unsupported packages on this machine" ++msgstr "显示此计算机中不受支持的包" ++ ++#: ../ubuntu-support-status:94 ++msgid "Show supported packages on this machine" ++msgstr "显示此计算机中受支持的包" ++ ++#: ../ubuntu-support-status:97 ++msgid "Show all packages with their status" ++msgstr "显示所有包及其状态" ++ ++#: ../ubuntu-support-status:100 ++msgid "Show all packages in a list" ++msgstr "列出所有包" ++ ++#: ../ubuntu-support-status:142 ++#, c-format ++msgid "Support status summary of '%s':" ++msgstr "'%s' 的支持状态摘要:" ++ ++#: ../ubuntu-support-status:145 ++msgid "You have %(num)s packages (%(percent).1f%%) supported until %(time)s" ++msgstr "有 %(num)s 个包(%(percent).1f%%)的支持截止时间是 %(time)s" ++ ++#: ../ubuntu-support-status:151 ++msgid "" ++"You have %(num)s packages (%(percent).1f%%) that can not/no-longer be " ++"downloaded" ++msgstr "有 %(num)s 个包(%(percent).1f%%) (已)不能不能下载" ++ ++#: ../ubuntu-support-status:154 ++msgid "You have %(num)s packages (%(percent).1f%%) that are unsupported" ++msgstr "有 %(num)s 个包(%(percent).1f%%)不受支持" ++ ++#: ../ubuntu-support-status:162 ++msgid "" ++"Run with --show-unsupported, --show-supported or --show-all to see more " ++"details" ++msgstr "" ++"通过 --show-unsupported、--show-supported 或 --show-all 运行以查看更多详细信" ++"息" ++ ++#: ../ubuntu-support-status:166 ++msgid "No longer downloadable:" ++msgstr "已不能下载:" ++ ++#: ../ubuntu-support-status:169 ++msgid "Unsupported: " ++msgstr "不受支持: " ++ ++#: ../ubuntu-support-status:174 ++#, c-format ++msgid "Supported until %s:" ++msgstr "支持截止时间 %s:" ++ ++#: ../ubuntu-support-status:183 ++msgid "Unsupported" ++msgstr "不受支持" ++ ++#. Why do we use %s here instead of $strings or {} format placeholders? ++#. It's because we don't want to break existing translations. ++#: ../janitor/plugincore/exceptions.py:42 ++#, python-format ++msgid "Unimplemented method: %s" ++msgstr "尚未实现的方法:%s" ++ ++#: ../janitor/plugincore/core/file_cruft.py:41 ++msgid "A file on disk" ++msgstr "磁盘上的文件" ++ ++#: ../janitor/plugincore/core/missing_package_cruft.py:39 ++msgid "Install missing package." ++msgstr "安装缺失的软件包。" ++ ++#. 2012-06-08 BAW: i18n string; don't use {} or PEP 292. ++#: ../janitor/plugincore/core/missing_package_cruft.py:49 ++#, python-format ++msgid "Package %s should be installed." ++msgstr "包 %s 应当被安装。" ++ ++#: ../janitor/plugincore/core/package_cruft.py:49 ++msgid ".deb package" ++msgstr ".deb 软件包" ++ ++#: ../janitor/plugincore/plugins/langpack_manual_plugin.py:46 ++#, python-format ++msgid "%s needs to be marked as manually installed." ++msgstr "%s 需要标记为手动安装。" ++ ++#: ../janitor/plugincore/plugins/kdelibs4to5_plugin.py:49 ++msgid "" ++"When upgrading, if kdelibs4-dev is installed, kdelibs5-dev needs to be " ++"installed. See bugs.launchpad.net, bug #279621 for details." ++msgstr "" ++"在升级时, 如果已经安装了 kdelibs4-dev, 那么需要安装 kdelibs5-dev。参阅 " ++"bugs.launchpad.net 上的 bug #279621 来了解详情。" ++ ++#: ../janitor/plugincore/plugins/dpkg_status_plugin.py:44 ++#, python-format ++msgid "%i obsolete entries in the status file" ++msgstr "状态文件中包含 %i 项过期条目" ++ ++#: ../janitor/plugincore/plugins/dpkg_status_plugin.py:47 ++msgid "Obsolete entries in dpkg status" ++msgstr "dpkg 状态中的过期条目" ++ ++#. pragma: no cover ++#: ../janitor/plugincore/plugins/dpkg_status_plugin.py:50 ++msgid "Obsolete dpkg status entries" ++msgstr "过期的 dpkg 状态条目" ++ ++#: ../janitor/plugincore/plugins/remove_lilo_plugin.py:42 ++msgid "Remove lilo since grub is also installed.(See bug #314004 for details.)" ++msgstr "由于 grub 已安装,移除 lilo。(详情参见 bug #314004 。)" ++ ++msgid "" ++"After your package information was updated the essential package '%s' can " ++"not be found anymore so a bug reporting process is being started." ++msgstr "" ++"更新包信息后就已找不到基本包(essential) '%s',因此即将启动错误报告进程。" ++ ++msgid "<b><big>Upgrading Ubuntu to version 12.04</big></b>" ++msgstr "<b><big>正在将 Ubuntu 升级到 12.04 版</big></b>" ++ ++msgid "%(count)s update has been selected." ++msgid_plural "%(count)s updates have been selected." ++msgstr[0] "已选择 %(count)s 个更新。" ++ ++msgid "%(count_str)s %(download_str)s" ++msgstr "%(count_str)s %(download_str)s" ++ ++msgid "Welcome to Ubuntu" ++msgstr "欢迎使用 Ubuntu" ++ ++msgid "" ++"These software updates have been issued since this version of Ubuntu was " ++"released." ++msgstr "自正式发布起,Ubuntu 已提供了下列软件更新。" ++ ++msgid "Software updates are available for this computer." ++msgstr "当前计算机有可用软件更新。" ++ ++msgid "Update Manager" ++msgstr "更新管理器" ++ ++msgid "Starting Update Manager" ++msgstr "正在启动更新管理器" ++ ++msgid "You are connected via a wireless modem." ++msgstr "您在使用无线调制解调器连接网络。" ++ ++msgid "_Install Updates" ++msgstr "安装更新(_I)" ++ ++msgid "Checking for a new ubuntu release" ++msgstr "检查新的 Ubuntu 发行版本" ++ ++msgid "Your system is up-to-date" ++msgstr "您的系统已为最新" ++ ++msgid "" ++"Fetching and installing the upgrade can take several hours. Once the " ++"download has finished, the process cannot be cancelled." ++msgstr "获取并安装升级可能会持续几个小时。一旦下载结束,进程将不能被取消。" ++ ++msgid "" ++"If you don't want to install them now, choose \"Update Manager\" from the " ++"Administration menu later." ++msgstr "如果您不想现在安装,稍后可以在“系统管理”菜单选择“更新管理器”进行安装。" ++ ++msgid "" ++"This upgrade is running in sandbox (test) mode. All changes are written to " ++"'%s' and will be lost on the next reboot.\n" ++"\n" ++"*No* changes written to a systemdir from now until the next reboot are " ++"permanent." ++msgstr "" ++"这个升级正运行在沙盒(测试)模式下。所有的变更都被写入“%s”并在下次重启时丢" ++"失。\n" ++"\n" ++"从现在起直到下次重启,被写入 systemdir 的变更*不是*永久的。" ++ ++msgid "Software updates are available for this computer" ++msgstr "有适合当前系统的软件更新" ++ ++msgid "There are no updates to install" ++msgstr "没有可安装的更新" ++ ++msgid "The update has already been downloaded, but not installed" ++msgid_plural "The updates have already been downloaded, but not installed" ++msgstr[0] "更新已经下载,但还没有安装。" ++ ++msgid "" ++"You will not get any further security fixes or critical updates. Please " ++"upgrade to a later version of Ubuntu Linux." ++msgstr "您不会再收到任何安全更新。请升级到更新版本的 Ubuntu。" ++ ++msgid "" ++"If you don't want to install them now, choose \"Update Manager\" from " ++"Applications later." ++msgstr "如果您不想现在安装,可以今后再从应用程序中打开“更新管理器”。" ++ ++msgid "0 kB" ++msgstr "0 kB" ++ ++msgid "1 kB" ++msgstr "1 kB" ++ ++msgid "" ++"The system was unable to get the prerequisites for the upgrade. The upgrade " ++"will abort now and restore the original system state.\n" ++"\n" ++"Please report this as a bug using the command 'ubuntu-bug update-manager' in " ++"a terminal and include the files in /var/log/dist-upgrade/ in the bug report." ++msgstr "" ++"系统无法获取升级所必需的文件。将放弃升级并恢复到原始系统状态。\n" ++"\n" ++"请在终端里输入命令‘ubuntu-bug update-manager’来报告这个 bug。在报告 bug 时附" ++"上位于 /var/log/dist-upgrade/ 的文件。" ++ ++msgid "" ++"After your package information was updated the essential package '%s' can " ++"not be found anymore.\n" ++"This indicates a serious error, please report this bug using the command " ++"'ubuntu-bug update-manager' in a terminal and include the files in /var/log/" ++"dist-upgrade/ in the bug report." ++msgstr "" ++"在更新了您的软件包信息之后,系统找不到‘%s’这个必需的软件包。\n" ++"这预示着一个严重错误。在终端里输入命令‘ubuntu-bug update-manager’来报告这个 " ++"bug。在报告 bug 时附上位于 /var/log/dist-upgrade/ 的文件。" ++ ++msgid "Your graphics hardware may not be fully supported in Ubuntu 11.04." ++msgstr "您的显卡可能无法完整地被 Ubuntu 11.04 支持。" ++ ++msgid "" ++"The support in Ubuntu 11.04 for your intel graphics hardware is limited and " ++"you may encounter problems after the upgrade. Do you want to continue with " ++"the upgrade?" ++msgstr "" ++"Ubuntu 11.04 对您的 intel 显卡支持有限。您可能在升级之后会遇到问题,确定要继" ++"续升级吗?" ++ ++msgid "" ++"\n" ++"\n" ++"Please report this bug using the command 'ubuntu-bug update-manager' in a " ++"terminal and include the files in /var/log/dist-upgrade/ in the bug report.\n" ++"%s" ++msgstr "" ++"\n" ++"\n" ++"请在终端里输入命令‘ubuntu-bug update-manager’来报告这个 bug。在报告 bug 时附" ++"上位于 /var/log/dist-upgrade/ 的文件。\n" ++"%s" ++ ++msgid "" ++"Preparing the system for the upgrade failed. Please report this using the " ++"command 'ubuntu-bug update-manager' in a terminal and include the files in /" ++"var/log/dist-upgrade/ in the bug report." ++msgstr "" ++"无法获取要升级的系统。请在终端里输入命令‘ubuntu-bug update-manager’来报告这" ++"个 bug。在报告 bug 时附上位于 /var/log/dist-upgrade/ 的文件。" ++ ++msgid "" ++"Upgrading the repository information resulted in a invalid file. Please " ++"report this as a bug using the command 'ubuntu-bug update-manager' in a " ++"terminal." ++msgstr "" ++"升级目录信息得到一个非法的文件。请在终端里输入命令‘ubuntu-bug update-" ++"manager’来报告这个 bug。" ++ ++msgid "" ++"These software updates have been issued since this version of Ubuntu was " ++"released. If you don't want to install them now, choose \"Update Manager\" " ++"from Applications later." ++msgstr "" ++"这些软件的更新在该版本的 Ubuntu 发行之后被证明有问题。如果您不想现在安装它" ++"们,可以在稍后从系统应用“更新管理器”中选择安装。" ++ ++msgid "" ++"These software updates have been issued since this version of Ubuntu was " ++"released. If you don't want to install them now, choose \"Update Manager\" " ++"from the Administration Menu later." ++msgstr "" ++"这些软件的更新在该版本的 Ubuntu 发行之后被证明有问题。如果您不想现在安装它" ++"们,可以在稍后从管理员菜单“更新管理器”中选择安装。" ++ ++msgid "%.0f kB" ++msgstr "%.0f kB" ++ ++msgid "Unable to access the source management server, please try again later" ++msgstr "无法访问源管理服务器,请稍后再试" ++ ++msgid "Access to the source management server timed out, please try again later" ++msgstr "访问源管理服务器超时,请稍后再试" ++ ++msgid "Check if your network requires authentication?" ++msgstr "检查您的网络需要认证吗?" ++ ++msgid "Check your source public key signature" ++msgstr "检查您的源数字签名" ++ ++msgid "update important list occur Exception" ++msgstr "获取推送出现异常,请稍后再试" ++ ++msgid "You need to be root to run this application" ++msgstr "你需要root权限运行" ++ ++msgid "There is an exception in the update package." ++msgstr "更新包存在异常!" ++ ++msgid "You request the removal of a system-essential package." ++msgstr "您要求删除一个系统必要的软件包。" ++ ++msgid "This update cannot detect the upgradeable package." ++msgstr "本次更新无法检测到可升级的软件包。" ++ ++msgid "read important list failed" ++msgstr "无法读取推送升级列表,请稍后再试" ++ ++msgid "Priority Upgrade Package being updated" ++msgstr "正在更新分组配置" ++ ++msgid "Exceptions of Priority Upgrade." ++msgstr "优先升级异常" ++ ++msgid "Due to the presence of deleted packages." ++msgstr "由于存在删除的软件包" ++ ++msgid "The system update configuration file is read abnormally, please check if the system update configuration file format is correct." ++msgstr "读取系统更新配置文件异常,请检查系统更新配置文件格式是否正确。" ++ ++msgid "Installation progress: " ++msgstr "安装进度: " ++ ++msgid "Installation successful, about to shut down" ++msgstr "安装成功,即将关机" ++ ++msgid "Installation failed, about to shut down" ++msgstr "安装失败,即将关机" ++ ++msgid "groups JSON ConfigPkgs install failed" ++msgstr "无法安装分组配置文件" ++ ++msgid "Installtion timeout to exit Due to inactivity" ++msgstr "安装超时退出由于" ++ ++msgid "Command execution error" ++msgstr "命令执行报错" ++ ++msgid "Unsupported architecture" ++msgstr "架构不符合" ++ ++msgid "Other Error" ++msgstr "其他错误" ++ ++msgid "dependency is not satisfied" ++msgstr "依赖关系不满足" ++ ++msgid "dependency is not satisfied will download" ++msgstr "依赖关系不满足" ++ ++msgid "Disk space is insufficient, please clean the disk and then upgrade" ++msgstr "磁盘空间不足,请清理磁盘后进行升级更新" ++ ++msgid "Network anomaly, can't check for updates!" ++msgstr "网络异常,无法检查更新!" ++ ++msgid "Check for update exceptions!" ++msgstr "检查更新异常!" ++ ++msgid "Check for update exceptions,fix system APT environment error." ++msgstr "检查更新异常,修复系统APT环境出现错误。" ++ ++msgid "The system APT environment is abnormal, please check the system APT environment." ++msgstr "修复系统APT环境异常,请检查系统APT环境。" ++ ++msgid "Priority upgrade status exception." ++msgstr "优先升级状态异常。" ++ ++msgid "Upgrade configuration acquisition exception." ++msgstr "升级配置获取异常。" ++ ++msgid "Please check your network connection and retry." ++msgstr "请检查您的网络连接后再试。" ++ ++msgid "Please check your source list and retry." ++msgstr "请检查您的源列表后再试。" ++ ++msgid "Checking network connection" ++msgstr "检查网络连接中" ++ ++msgid "Updating Source Template" ++msgstr "更新源模板中" ++ ++msgid "Update Manager upgrade is complete, please restart the setting panel before performing the system update." ++msgstr "更新管理器升级完成,请重启设置-更新后再进行系统更新。" ++ ++msgid "Update Manager exception, please restart the setting panel before performing the system update." ++msgstr "更新管理器异常,请重启设置-更新后再进行系统更新。" ++ ++msgid "Uninstallation completed" ++msgstr "卸载完成。" ++ ++msgid "Package validation failed and installation was rejected." ++msgstr "软件包验证失败,拒绝安装。" ++ ++msgid "Other tasks are being updated and upgraded, please uninstall them later." ++msgstr "其他任务正在更新升级中,请稍后再卸载。" ++ ++#: ../aptdaemon/worker/aptworker.py:1353 ++msgid "The following packages have unmet dependencies:" ++msgstr "下列软件包未满足的依赖关系:" ++ ++#: ../aptdaemon/worker/aptworker.py:1406 ++msgid "but it is a virtual package" ++msgstr "但是它是虚拟软件包" ++ ++#: ../aptdaemon/worker/aptworker.py:1409 ++msgid "but it is not installed" ++msgstr "但是 %s 没有安装" ++ ++#: ../aptdaemon/worker/aptworker.py:1411 ++msgid "but it is not going to be installed" ++msgstr "但是无法安装 %s" ++ ++#. TRANSLATORS: %s is a version number ++#: ../aptdaemon/worker/aptworker.py:1415 ++#, python-format ++msgid "but %s is installed" ++msgstr "但是 %s 已经安装" ++ ++#. TRANSLATORS: %s is a version number ++#: ../aptdaemon/worker/aptworker.py:1419 ++#, python-format ++msgid "but %s is to be installed" ++msgstr "但是将要安装 %s" ++ ++#: ../SystemUpdater/Core/enums.py:763 ++msgid "Kylin System Updater" ++msgstr "麒麟更新器" ++ ++#: ../SystemUpdater/Core/enums.py:609 ++msgid "Kylin Installer" ++msgstr "麒麟安装器" ++ ++#: ../SystemUpdater/Core/enums.py:610 ++msgid "Kylin Uninstaller" ++msgstr "麒麟卸载器" ++ ++#: ../SystemUpdater/Core/enums.py:611 ++msgid "Kylin Background Upgrade" ++msgstr "静默更新" ++ ++#: ../SystemUpdater/Core/enums.py:612 ++msgid "Kylin Software Center" ++msgstr "软件商店" ++ ++#: ../SystemUpdater/UpdateManagerDbus.py:355 ++msgid " requires authentication to uninstall software packages." ++msgstr "卸载软件包需要认证" ++ ++#. 验签失败,提权 ++#: ../SystemUpdater/UpdateManager.py:463 ++msgid " requires authentication to install software packages." ++msgstr "安装软件包需要认证" ++ ++#: ../SystemUpdater/Core/utils.py:750 ++msgid "Authentication success." ++msgstr "认证成功" ++ ++#: ../SystemUpdater/Core/utils.py:753 ++msgid "Authentication failure." ++msgstr "认证失败" ++ ++#: ../SystemUpdater/Core/enums.py:101 ++msgid "Deb format exception, read local deb file error." ++msgstr "软件包格式异常,读取失败。" ++ ++#: ../SystemUpdater/Core/enums.py:102 ++msgid "Install deb error." ++msgstr "安装软件包失败。" ++ ++msgid "Upgrade System" ++msgstr "全盘升级" ++ ++msgid "kylin-unattended-upgrade" ++msgstr "自动更新" ++ ++msgid "Please check the system time and synchronize the system time before updating." ++msgstr "请检查系统时间,同步系统时间后再进行更新。" ++ ++msgid "The package is unsigned, refuses to install." ++msgstr "软件包未签名,拒绝安装。" ++ ++msgid "Program exception, please contact the administrator to solve." ++msgstr "程序执行异常,请联系管理员解决。" ++ ++msgid "Exceptions to running the check script." ++msgstr "运行检查脚本出现异常。" ++ ++msgid "Installing or removing software packages." ++msgstr "正在安装或删除软件包。" ++ ++msgid "The system is installing updates. Please do not turn off your computer. " ++msgstr "系统正在安装更新,请不要关闭计算机。" ++ ++msgid "If the device is less than 50% charged, please charge it before updating the system." ++msgstr "设备电量不足50%,请充电后再进行系统更新。" ++ ++msgid "Application installation control policy not enabled ." ++msgstr "应用安装管控未开启." ++ ++msgid "Installation failed! Application is not in the software whitelist list!" ++msgstr "安装失败!软件包不在白名单列表!" ++ ++msgid "Installation failed! Application is in the software blacklist list!" ++msgstr "安装失败!软件包在黑名单列表!" ++ ++msgid "Application installation in unknown mode ." ++msgstr "应用安装管控策略未知." ++ ++msgid "Other tasks are being updated and upgraded, please try again later." ++msgstr "其他任务正在更新升级中,请稍后再试" ++ ++msgid "Cancel Upgrade" ++msgstr "取消升级" ++ ++msgid "There is no way to rollback to the previous version, there is nothing to rollback." ++msgstr "无法回滚到上一个版本,没有可回滚内容。" ++ ++msgid "Check for update exceptions! Please verify that the system deployment is complete." ++msgstr "检查更新异常!请检查系统部署是否完整。" ++ ++msgid "Update push is abnormal, and the corresponding branch information cannot be found on the server." ++msgstr "更新推送异常,未在服务端找到相应分支信息。" ++ ++msgid "Name of the repository deployed by the current system is empty." ++msgstr "更新检查异常,当前系统部署分支仓库所属信息为空。" ++ ++#: ../SystemUpdater/Core/utils.py:753 ++msgid "Cancel authentication." ++msgstr "取消认证" ++ ++msgid "System upgrade error" ++msgstr "系统更新异常" +diff --git a/backend-immutable/tests/po/zh_HK.po b/backend-immutable/tests/po/zh_HK.po +new file mode 100644 +index 0000000..4b147fd +--- /dev/null ++++ b/backend-immutable/tests/po/zh_HK.po +@@ -0,0 +1,2712 @@ ++msgid "" ++msgstr "" ++"Project-Id-Version: update-manager 0.41.1\n" ++"Report-Msgid-Bugs-To: sebastian.heinlein@web.de\n" ++"POT-Creation-Date: 2012-06-14 00:53+0100\n" ++"PO-Revision-Date: 2012-04-13 00:58+0000\n" ++"Last-Translator: Walter Cheuk <wwycheuk@gmail.com>\n" ++"Language-Team: Chinese (Hong Kong) <zh_HK@li.org>\n" ++"Language: zh_HK\n" ++"MIME-Version: 1.0\n" ++"Content-Type: text/plain; charset=UTF-8\n" ++"Content-Transfer-Encoding: 8bit\n" ++"Plural-Forms: nplurals=1; plural=0;\n" ++"X-Launchpad-Export-Date: 2012-04-17 13:41+0000\n" ++"X-Generator: Launchpad (build 15099)\n" ++ ++#. TRANSLATORS: download size of small updates, e.g. "250 kB" ++#: ../DistUpgrade/utils.py:433 ../UpdateManager/Core/utils.py:433 ++#, python-format ++msgid "%(size).0f kB" ++msgid_plural "%(size).0f kB" ++msgstr[0] "" ++msgstr[1] "" ++ ++#. TRANSLATORS: download size of updates, e.g. "2.3 MB" ++#: ../DistUpgrade/utils.py:436 ../UpdateManager/Core/utils.py:436 ++#, python-format ++msgid "%.1f MB" ++msgstr "%.1f MB" ++ ++#. TRANSLATORS: %s is a country ++#: ../DistUpgrade/distro.py:206 ../DistUpgrade/distro.py:436 ++#, python-format ++msgid "Server for %s" ++msgstr "%s伺服器" ++ ++#. More than one server is used. Since we don't handle this case ++#. in the user interface we set "custom servers" to true and ++#. append a list of all used servers ++#: ../DistUpgrade/distro.py:224 ../DistUpgrade/distro.py:230 ++#: ../DistUpgrade/distro.py:246 ++msgid "Main server" ++msgstr "主要伺服器" ++ ++#: ../DistUpgrade/distro.py:250 ++msgid "Custom servers" ++msgstr "自訂伺服器" ++ ++#: ../DistUpgrade/DistUpgradeAptCdrom.py:142 ++msgid "Could not calculate sources.list entry" ++msgstr "無法推算 sources.list 項目" ++ ++#: ../DistUpgrade/DistUpgradeAptCdrom.py:251 ++msgid "" ++"Unable to locate any package files, perhaps this is not a Ubuntu Disc or the " ++"wrong architecture?" ++msgstr "找不到套件檔案;也許這不是 Ubuntu 光碟,又或者處理器架構不對?" ++ ++#: ../DistUpgrade/DistUpgradeAptCdrom.py:294 ++msgid "Failed to add the CD" ++msgstr "無法加入光碟" ++ ++#: ../DistUpgrade/DistUpgradeAptCdrom.py:295 ++#, python-format ++msgid "" ++"There was a error adding the CD, the upgrade will abort. Please report this " ++"as a bug if this is a valid Ubuntu CD.\n" ++"\n" ++"The error message was:\n" ++"'%s'" ++msgstr "" ++"加入光碟時發生錯誤,升級將終止。若確定光碟沒有問題,請將此匯報為臭蟲。\n" ++"\n" ++"錯誤訊息:\n" ++"「%s」" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:151 ++msgid "Remove package in bad state" ++msgid_plural "Remove packages in bad state" ++msgstr[0] "移除有問題套件" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:154 ++#, python-format ++msgid "" ++"The package '%s' is in an inconsistent state and needs to be reinstalled, " ++"but no archive can be found for it. Do you want to remove this package now " ++"to continue?" ++msgid_plural "" ++"The packages '%s' are in an inconsistent state and need to be reinstalled, " ++"but no archives can be found for them. Do you want to remove these packages " ++"now to continue?" ++msgstr[0] "" ++"這個套件 '%s' 狀態不一致而需要重新安裝,但找不到它的存檔。您想移除這個套件然" ++"後繼續嗎?" ++ ++#. FIXME: not ideal error message, but we just reuse a ++#. existing one here to avoid a new string ++#: ../DistUpgrade/DistUpgradeCache.py:255 ++msgid "The server may be overloaded" ++msgstr "伺服器可能負載過大" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:368 ++msgid "Broken packages" ++msgstr "損毀的套件" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:369 ++msgid "" ++"Your system contains broken packages that couldn't be fixed with this " ++"software. Please fix them first using synaptic or apt-get before proceeding." ++msgstr "" ++"系統有一些本軟件無法修正的損毀套件。請先以「Synaptic 套件管理員」或 apt-get " ++"修正再繼續。" ++ ++#. FIXME: change the text to something more useful ++#: ../DistUpgrade/DistUpgradeCache.py:693 ++#, python-format ++msgid "" ++"An unresolvable problem occurred while calculating the upgrade:\n" ++"%s\n" ++"\n" ++" This can be caused by:\n" ++" * Upgrading to a pre-release version of Ubuntu\n" ++" * Running the current pre-release version of Ubuntu\n" ++" * Unofficial software packages not provided by Ubuntu\n" ++"\n" ++msgstr "" ++"為升級進行推算時有問題無法解決:\n" ++"%s\n" ++"\n" ++" 原因可能是:\n" ++" * 要升級至測試版 Ubuntu\n" ++" * 正使用的是測試版 Ubuntu\n" ++" * 非 Ubuntu 官方軟件套件的問題\n" ++"\n" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:703 ++msgid "This is most likely a transient problem, please try again later." ++msgstr "這通常只是暫時性的問題。請稍候再試。" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:706 ++msgid "" ++"If none of this applies, then please report this bug using the command " ++"'ubuntu-bug update-manager' in a terminal." ++msgstr "" ++"如全都不對,請在終端機內輸入指令「ubuntu-bug update-manager」回報錯誤。" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:711 ++#: ../UpdateManager/UpdateManager.py:1031 ++msgid "Could not calculate the upgrade" ++msgstr "無法為升級進行推算" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:762 ++msgid "Error authenticating some packages" ++msgstr "認證一些套件時發生錯誤" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:763 ++msgid "" ++"It was not possible to authenticate some packages. This may be a transient " ++"network problem. You may want to try again later. See below for a list of " ++"unauthenticated packages." ++msgstr "" ++"系統無法認證一些套件。這可能是由於短暫的網絡問題;您可以稍後再試。以下為沒有" ++"認證的套件。" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:783 ++#, python-format ++msgid "" ++"The package '%s' is marked for removal but it is in the removal blacklist." ++msgstr "套件「%s」標記作移除,但它在移除黑名單中。" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:787 ++#, python-format ++msgid "The essential package '%s' is marked for removal." ++msgstr "必備套件「%s」被標記作移除。" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:796 ++#, python-format ++msgid "Trying to install blacklisted version '%s'" ++msgstr "正在嘗試安裝黑名單版本「%s」" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:914 ++#, python-format ++msgid "Can't install '%s'" ++msgstr "無法安裝「%s」" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:915 ++msgid "" ++"It was impossible to install a required package. Please report this as a bug " ++"using 'ubuntu-bug update-manager' in a terminal." ++msgstr "" ++"無法安裝必要的套件。請在終端機內輸入「ubuntu-bug update-manager」回報錯誤。" ++ ++#. FIXME: provide a list ++#: ../DistUpgrade/DistUpgradeCache.py:926 ++msgid "Can't guess meta-package" ++msgstr "無法估計元套件 (meta-package)" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:927 ++msgid "" ++"Your system does not contain a ubuntu-desktop, kubuntu-desktop, xubuntu-" ++"desktop or edubuntu-desktop package and it was not possible to detect which " ++"version of Ubuntu you are running.\n" ++" Please install one of the packages above first using synaptic or apt-get " ++"before proceeding." ++msgstr "" ++"您的系統沒有安裝 ubuntu-desktop、kubuntu-desktop、xubuntu-desktop 或 " ++"edubuntu-desktop 套件,因此無法偵測正在使用那個版本的 Ubuntu。\n" ++" 請先以「Synaptic 套件管理員」或 apt-get 安裝上述其中一個套件再繼續。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:114 ++msgid "Reading cache" ++msgstr "正在讀取快取" ++ ++#: ../DistUpgrade/DistUpgradeController.py:223 ++msgid "Unable to get exclusive lock" ++msgstr "無法取得(使用)排他鎖定" ++ ++#: ../DistUpgrade/DistUpgradeController.py:224 ++msgid "" ++"This usually means that another package management application (like apt-get " ++"or aptitude) already running. Please close that application first." ++msgstr "" ++"這通常表示有其他的套件管理員程式(如 apt-get 或 aptitude)正在執行。請先關閉" ++"這些應用程式。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:257 ++msgid "Upgrading over remote connection not supported" ++msgstr "不支援透過遠端連線升級" ++ ++#: ../DistUpgrade/DistUpgradeController.py:258 ++msgid "" ++"You are running the upgrade over a remote ssh connection with a frontend " ++"that does not support this. Please try a text mode upgrade with 'do-release-" ++"upgrade'.\n" ++"\n" ++"The upgrade will abort now. Please try without ssh." ++msgstr "" ++"您正在透過遠端 ssh 連線的前端介面執行更新,而此介面不支援。請試試純文字模式下" ++"以 'do-release-upgrade' 進行更新。\n" ++"\n" ++"目前更新將會中止。請透過非 ssh 連線重試。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:272 ++msgid "Continue running under SSH?" ++msgstr "繼續執行於 SSH 中?" ++ ++#: ../DistUpgrade/DistUpgradeController.py:273 ++#, python-format ++msgid "" ++"This session appears to be running under ssh. It is not recommended to " ++"perform a upgrade over ssh currently because in case of failure it is harder " ++"to recover.\n" ++"\n" ++"If you continue, an additional ssh daemon will be started at port '%s'.\n" ++"Do you want to continue?" ++msgstr "" ++"此連線階段似乎是在 ssh 下執行。目前不建議在 ssh 連線下進行升級,因為若發生失" ++"敗將會較難以修復。\n" ++"\n" ++"若您繼續,一個額外的 ssh 背景程序將會啟動在 '%s' 連接埠。\n" ++"您想要繼續嗎?" ++ ++#: ../DistUpgrade/DistUpgradeController.py:287 ++msgid "Starting additional sshd" ++msgstr "啟動後備 sshd 中" ++ ++#: ../DistUpgrade/DistUpgradeController.py:288 ++#, python-format ++msgid "" ++"To make recovery in case of failure easier, an additional sshd will be " ++"started on port '%s'. If anything goes wrong with the running ssh you can " ++"still connect to the additional one.\n" ++msgstr "" ++"為在失敗時更易進行修復,一個後備 sshd 將在‘%s’埠被啟動。如果使用中的 ssh 有任" ++"何問題,您仍可以連接後備的 sshd 。\n" ++ ++#: ../DistUpgrade/DistUpgradeController.py:296 ++#, python-format ++msgid "" ++"If you run a firewall, you may need to temporarily open this port. As this " ++"is potentially dangerous it's not done automatically. You can open the port " ++"with e.g.:\n" ++"'%s'" ++msgstr "" ++"若有執行防火牆,可能需要暫時開啟此連接埠。由於此動作有潛在危險,系統不會自動" ++"執行。可以這樣開啟連接埠:\n" ++"「%s」" ++ ++#: ../DistUpgrade/DistUpgradeController.py:368 ++#: ../DistUpgrade/DistUpgradeController.py:413 ++msgid "Can not upgrade" ++msgstr "無法升級" ++ ++#: ../DistUpgrade/DistUpgradeController.py:369 ++#, python-format ++msgid "An upgrade from '%s' to '%s' is not supported with this tool." ++msgstr "這個工具不支援從‘%s’到‘%s’的升級" ++ ++#: ../DistUpgrade/DistUpgradeController.py:378 ++msgid "Sandbox setup failed" ++msgstr "沙堆(Sandbox)架設失敗" ++ ++#: ../DistUpgrade/DistUpgradeController.py:379 ++msgid "It was not possible to create the sandbox environment." ++msgstr "不可能建立沙堆(sandbox)環境。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:385 ++msgid "Sandbox mode" ++msgstr "沙堆(Sandbox)模式" ++ ++#: ../DistUpgrade/DistUpgradeController.py:386 ++#, python-format ++msgid "" ++"This upgrade is running in sandbox (test) mode. All changes are written to " ++"'%s' and will be lost on the next reboot.\n" ++"\n" ++"*No* changes written to a system directory from now until the next reboot " ++"are permanent." ++msgstr "" ++ ++#: ../DistUpgrade/DistUpgradeController.py:414 ++msgid "" ++"Your python install is corrupted. Please fix the '/usr/bin/python' symlink." ++msgstr "您的 python 安裝已毀損。請修正 ‘/usr/bin/python’ 的符號連結。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:440 ++msgid "Package 'debsig-verify' is installed" ++msgstr "套件 'debsig-verify' 安裝完成。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:441 ++msgid "" ++"The upgrade can not continue with that package installed.\n" ++"Please remove it with synaptic or 'apt-get remove debsig-verify' first and " ++"run the upgrade again." ++msgstr "" ++"該已安裝套件令升級無法繼續,請先以「Synaptic 套件管理員」或「apt-get remove " ++"debsig-verify」指令將其移除再進行升級。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:453 ++#, python-format ++msgid "Can not write to '%s'" ++msgstr "" ++ ++#: ../DistUpgrade/DistUpgradeController.py:454 ++#, python-format ++msgid "" ++"Its not possible to write to the system directory '%s' on your system. The " ++"upgrade can not continue.\n" ++"Please make sure that the system directory is writable." ++msgstr "" ++ ++#: ../DistUpgrade/DistUpgradeController.py:465 ++msgid "Include latest updates from the Internet?" ++msgstr "包括互聯網上的最新更新嗎?" ++ ++#: ../DistUpgrade/DistUpgradeController.py:466 ++msgid "" ++"The upgrade system can use the internet to automatically download the latest " ++"updates and install them during the upgrade. If you have a network " ++"connection this is highly recommended.\n" ++"\n" ++"The upgrade will take longer, but when it is complete, your system will be " ++"fully up to date. You can choose not to do this, but you should install the " ++"latest updates soon after upgrading.\n" ++"If you answer 'no' here, the network is not used at all." ++msgstr "" ++"升級系統可自動由互聯網下載最新更新並在升級時安裝。如有網絡連線,建議使用這方" ++"法。\n" ++"\n" ++"升級的時間會比較久,但完成後,系統就會完全在最新狀態。您可以選擇現在不進行這" ++"工作,但是在升級後,應該要盡快安裝最新的更新。\n" ++"如在此回答「否」,將完全不會使用網絡。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:686 ++#, python-format ++msgid "disabled on upgrade to %s" ++msgstr "因升級至 %s 停用" ++ ++#: ../DistUpgrade/DistUpgradeController.py:713 ++msgid "No valid mirror found" ++msgstr "找不到有效的鏡像站" ++ ++#: ../DistUpgrade/DistUpgradeController.py:714 ++#, python-format ++msgid "" ++"While scanning your repository information no mirror entry for the upgrade " ++"was found. This can happen if you run a internal mirror or if the mirror " ++"information is out of date.\n" ++"\n" ++"Do you want to rewrite your 'sources.list' file anyway? If you choose 'Yes' " ++"here it will update all '%s' to '%s' entries.\n" ++"If you select 'No' the upgrade will cancel." ++msgstr "" ++"掃描套件庫資料時沒有發現可提供升級的鏡像站。如果使用的是內部鏡像站或鏡像站資" ++"料已經過時,就會發生這種情況。\n" ++"\n" ++"是否無論如何都希望覆寫 'sources.list' 檔案?如果在此選擇「是」則會將所有 " ++"'%s' 項目更新成 '%s'。\n" ++"如果選擇「否」,則會取消更新。" ++ ++#. hm, still nothing useful ... ++#: ../DistUpgrade/DistUpgradeController.py:734 ++msgid "Generate default sources?" ++msgstr "產生預設的來源?" ++ ++#: ../DistUpgrade/DistUpgradeController.py:735 ++#, python-format ++msgid "" ++"After scanning your 'sources.list' no valid entry for '%s' was found.\n" ++"\n" ++"Should default entries for '%s' be added? If you select 'No', the upgrade " ++"will cancel." ++msgstr "" ++"在掃描您的 'sources.list' 後,沒找到與 '%s' 有效的項目。\n" ++"\n" ++"要新增 '%s' 的預設項目嗎?如果選擇「否」,則會取消升級。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:770 ++msgid "Repository information invalid" ++msgstr "套件庫資料無效" ++ ++#: ../DistUpgrade/DistUpgradeController.py:771 ++msgid "" ++"Upgrading the repository information resulted in a invalid file so a bug " ++"reporting process is being started." ++msgstr "因更新套件庫資料導致檔案無效,故啟動錯誤報告程序。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:778 ++msgid "Third party sources disabled" ++msgstr "已停用第三方來源" ++ ++#: ../DistUpgrade/DistUpgradeController.py:779 ++msgid "" ++"Some third party entries in your sources.list were disabled. You can re-" ++"enable them after the upgrade with the 'software-properties' tool or your " ++"package manager." ++msgstr "" ++"部份在 sources.list 的第三方項目已經停用。升級完成後可以「軟件來源(software-" ++"properties)」工具或套件管理員重新啟用。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:819 ++msgid "Package in inconsistent state" ++msgid_plural "Packages in inconsistent state" ++msgstr[0] "套件在不一致狀態" ++ ++#: ../DistUpgrade/DistUpgradeController.py:822 ++#, python-format ++msgid "" ++"The package '%s' is in an inconsistent state and needs to be reinstalled, " ++"but no archive can be found for it. Please reinstall the package manually or " ++"remove it from the system." ++msgid_plural "" ++"The packages '%s' are in an inconsistent state and need to be reinstalled, " ++"but no archive can be found for them. Please reinstall the packages manually " ++"or remove them from the system." ++msgstr[0] "" ++"這個套件 '%s' 狀態不一致而需要重新安裝,但找不到其存檔套件。請手動重新安裝或" ++"將其由系統移除。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:870 ++msgid "Error during update" ++msgstr "更新時發生錯誤" ++ ++#: ../DistUpgrade/DistUpgradeController.py:871 ++msgid "" ++"A problem occurred during the update. This is usually some sort of network " ++"problem, please check your network connection and retry." ++msgstr "更新時發生錯誤。這可能是某些網絡問題,請檢查網絡連線後再試。" ++ ++#. print("on_button_install_clicked") ++#: ../DistUpgrade/DistUpgradeController.py:880 ++#: ../UpdateManager/UpdateManager.py:757 ++msgid "Not enough free disk space" ++msgstr "磁碟空間不足" ++ ++#: ../DistUpgrade/DistUpgradeController.py:881 ++#, python-format ++msgid "" ++"The upgrade has aborted. The upgrade needs a total of %s free space on disk " ++"'%s'. Please free at least an additional %s of disk space on '%s'. Empty " ++"your trash and remove temporary packages of former installations using 'sudo " ++"apt-get clean'." ++msgstr "" ++"已放棄升級。升級程序總共需要 %s 可用空間於磁碟「%s」。請釋放至少額外 %s 的磁" ++"碟空間於磁碟「%s」。清理您的回收筒,並使用 'sudo apt-get clean' 來移除之前安" ++"裝時的暫時性套件。" ++ ++#. calc the dist-upgrade and see if the removals are ok/expected ++#. do the dist-upgrade ++#: ../DistUpgrade/DistUpgradeController.py:910 ++#: ../DistUpgrade/DistUpgradeController.py:1692 ++msgid "Calculating the changes" ++msgstr "正在推算改動" ++ ++#. ask the user ++#: ../DistUpgrade/DistUpgradeController.py:942 ++msgid "Do you want to start the upgrade?" ++msgstr "想要開始更新嗎?" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1008 ++msgid "Upgrade canceled" ++msgstr "升級取消了" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1009 ++msgid "" ++"The upgrade will cancel now and the original system state will be restored. " ++"You can resume the upgrade at a later time." ++msgstr "現在將取消升級,並將系統還原至原來狀態。您可以在之後繼續升級。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1015 ++#: ../DistUpgrade/DistUpgradeController.py:1149 ++msgid "Could not download the upgrades" ++msgstr "無法下載升級套件" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1016 ++msgid "" ++"The upgrade has aborted. Please check your Internet connection or " ++"installation media and try again. All files downloaded so far have been kept." ++msgstr "" ++"升級已中止。請檢查您的互聯網連線,或安裝媒體並重試。所有目前已下載的檔案都會" ++"被保留。" ++ ++#. FIXME: strings are not good, but we are in string freeze ++#. currently ++#: ../DistUpgrade/DistUpgradeController.py:1100 ++#: ../DistUpgrade/DistUpgradeController.py:1137 ++#: ../DistUpgrade/DistUpgradeController.py:1242 ++msgid "Error during commit" ++msgstr "提交時發生錯誤" ++ ++#. generate a new cache ++#: ../DistUpgrade/DistUpgradeController.py:1102 ++#: ../DistUpgrade/DistUpgradeController.py:1139 ++#: ../DistUpgrade/DistUpgradeController.py:1281 ++msgid "Restoring original system state" ++msgstr "回復原有系統狀態" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1103 ++#: ../DistUpgrade/DistUpgradeController.py:1118 ++#: ../DistUpgrade/DistUpgradeController.py:1140 ++msgid "Could not install the upgrades" ++msgstr "無法安裝升級套件" ++ ++#. invoke the frontend now and show a error message ++#: ../DistUpgrade/DistUpgradeController.py:1108 ++msgid "" ++"The upgrade has aborted. Your system could be in an unusable state. A " ++"recovery will run now (dpkg --configure -a)." ++msgstr "" ++"已放棄升級。系統可能會處於不穩定狀態。現在會執行復原程序 (dpkg --configure -" ++"a)。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1113 ++#, python-format ++msgid "" ++"\n" ++"\n" ++"Please report this bug in a browser at http://bugs.launchpad.net/ubuntu/" ++"+source/update-manager/+filebug and attach the files in /var/log/dist-" ++"upgrade/ to the bug report.\n" ++"%s" ++msgstr "" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1150 ++msgid "" ++"The upgrade has aborted. Please check your Internet connection or " ++"installation media and try again. " ++msgstr "已放棄升級。請檢查您的互聯網連線或安裝媒體,接著再試一次。 " ++ ++#: ../DistUpgrade/DistUpgradeController.py:1230 ++msgid "Remove obsolete packages?" ++msgstr "移除廢棄的套件?" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1231 ++#: ../DistUpgrade/DistUpgrade.ui.h:8 ++msgid "_Keep" ++msgstr "保留(_K)" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1231 ++msgid "_Remove" ++msgstr "移除(_R)" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1243 ++msgid "" ++"A problem occurred during the clean-up. Please see the below message for " ++"more information. " ++msgstr "進行清理時發生問題。詳情請參閱以下訊息。 " ++ ++#. FIXME: instead of error out, fetch and install it ++#. here ++#: ../DistUpgrade/DistUpgradeController.py:1319 ++msgid "Required depends is not installed" ++msgstr "必需的相依套件未安裝" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1320 ++#, python-format ++msgid "The required dependency '%s' is not installed. " ++msgstr "必需的相依套件「%s」未安裝。 " ++ ++#. sanity check (check for ubuntu-desktop, brokenCache etc) ++#. then open the cache (again) ++#: ../DistUpgrade/DistUpgradeController.py:1588 ++#: ../DistUpgrade/DistUpgradeController.py:1653 ++msgid "Checking package manager" ++msgstr "正在檢查套件管理員" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1593 ++msgid "Preparing the upgrade failed" ++msgstr "準備升級失敗" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1594 ++msgid "" ++"Preparing the system for the upgrade failed so a bug reporting process is " ++"being started." ++msgstr "" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1608 ++msgid "Getting upgrade prerequisites failed" ++msgstr "取得升級先決元件失敗" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1609 ++msgid "" ++"The system was unable to get the prerequisites for the upgrade. The upgrade " ++"will abort now and restore the original system state.\n" ++"\n" ++"Additionally, a bug reporting process is being started." ++msgstr "" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1637 ++msgid "Updating repository information" ++msgstr "更新套件庫資料" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1644 ++msgid "Failed to add the cdrom" ++msgstr "無法加入光碟" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1645 ++msgid "Sorry, adding the cdrom was not successful." ++msgstr "很抱歉,沒有成功加入光碟。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1673 ++msgid "Invalid package information" ++msgstr "套件資訊無效" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1674 ++msgid "After updating your package " ++msgstr "" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1698 ++#: ../DistUpgrade/DistUpgradeController.py:1750 ++msgid "Fetching" ++msgstr "提取中" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1704 ++#: ../DistUpgrade/DistUpgradeController.py:1754 ++msgid "Upgrading" ++msgstr "升級中" ++ ++#. don't abort here, because it would restore the sources.list ++#: ../DistUpgrade/DistUpgradeController.py:1709 ++#: ../DistUpgrade/DistUpgradeController.py:1756 ++#: ../DistUpgrade/DistUpgradeController.py:1763 ++#: ../DistUpgrade/DistUpgradeController.py:1774 ++msgid "Upgrade complete" ++msgstr "升級完成" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1710 ++#: ../DistUpgrade/DistUpgradeController.py:1757 ++#: ../DistUpgrade/DistUpgradeController.py:1764 ++msgid "" ++"The upgrade has completed but there were errors during the upgrade process." ++msgstr "升級已經完成,但在升級過程中有發生錯誤。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1717 ++msgid "Searching for obsolete software" ++msgstr "搜尋廢棄的軟件中" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1726 ++msgid "System upgrade is complete." ++msgstr "系統升級完成。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1775 ++msgid "The partial upgrade was completed." ++msgstr "部份升級完成。" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:204 ++msgid "evms in use" ++msgstr "有軟件正使用 evms" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:205 ++msgid "" ++"Your system uses the 'evms' volume manager in /proc/mounts. The 'evms' " ++"software is no longer supported, please switch it off and run the upgrade " ++"again when this is done." ++msgstr "" ++"您的系統於 /proc/mounts 使用 'evms' volume 管理程式。'evms' 軟件已不受支援," ++"請將其關閉再執行升級工作。" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:502 ++msgid "Your graphics hardware may not be fully supported in Ubuntu 12.04 LTS." ++msgstr "" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:504 ++msgid "" ++"The support in Ubuntu 12.04 LTS for your Intel graphics hardware is limited " ++"and you may encounter problems after the upgrade. For more information see " ++"https://wiki.ubuntu.com/X/Bugs/UpdateManagerWarningForI8xx Do you want to " ++"continue with the upgrade?" ++msgstr "" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:526 ++#: ../DistUpgrade/DistUpgradeQuirks.py:554 ++#: ../DistUpgrade/DistUpgradeQuirks.py:581 ++msgid "" ++"Upgrading may reduce desktop effects, and performance in games and other " ++"graphically intensive programs." ++msgstr "升級可能減低桌面特效和遊戲及其他著重圖形程式的表現。" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:530 ++#: ../DistUpgrade/DistUpgradeQuirks.py:558 ++msgid "" ++"This computer is currently using the NVIDIA 'nvidia' graphics driver. No " ++"version of this driver is available that works with your video card in " ++"Ubuntu 10.04 LTS.\n" ++"\n" ++"Do you want to continue?" ++msgstr "" ++"這個電腦目前正使用 NVIDIA 的「nvidia」圖形驅動程式。這個驅動程式沒有任何版本" ++"可在 Ubuntu 10.04 LTS 中正常驅動您的硬件。\n" ++"\n" ++"是否要繼續?" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:585 ++msgid "" ++"This computer is currently using the AMD 'fglrx' graphics driver. No version " ++"of this driver is available that works with your hardware in Ubuntu 10.04 " ++"LTS.\n" ++"\n" ++"Do you want to continue?" ++msgstr "" ++"這個電腦目前正使用 AMD 的「fglrx」圖形驅動程式。這個驅動程式沒有任何版本可在 " ++"Ubuntu 10.04 LTS 中正常驅動您的硬件。\n" ++"\n" ++"是否要繼續?" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:615 ++msgid "No i686 CPU" ++msgstr "非 i686 處理器" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:616 ++msgid "" ++"Your system uses an i586 CPU or a CPU that does not have the 'cmov' " ++"extension. All packages were built with optimizations requiring i686 as the " ++"minimal architecture. It is not possible to upgrade your system to a new " ++"Ubuntu release with this hardware." ++msgstr "" ++"您的系統使用 i586 處理器,或是不支援「cmov」擴充功能的處理器。所有套件都以 " ++"i686 架構為最佳化目標來建置,所以處理器至少需要有 i686 等級。對於目前的硬件來" ++"說,無法將系統升級到新的 Ubuntu 發行版。" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:652 ++msgid "No ARMv6 CPU" ++msgstr "非 ARMv6 處理器" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:653 ++msgid "" ++"Your system uses an ARM CPU that is older than the ARMv6 architecture. All " ++"packages in karmic were built with optimizations requiring ARMv6 as the " ++"minimal architecture. It is not possible to upgrade your system to a new " ++"Ubuntu release with this hardware." ++msgstr "" ++"您系統使用的 ARM 處理器舊於 ARMv6 架構。所有 karmic 套件都以 ARMv6 架構為最佳" ++"化目標來建置,所以處理器至少需要有 ARMv6 等級。對於目前的硬件來說,無法將系統" ++"升級到新的 Ubuntu 發行版。" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:673 ++msgid "No init available" ++msgstr "無法初始化" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:674 ++msgid "" ++"Your system appears to be a virtualised environment without an init daemon, " ++"e.g. Linux-VServer. Ubuntu 10.04 LTS cannot function within this type of " ++"environment, requiring an update to your virtual machine configuration " ++"first.\n" ++"\n" ++"Are you sure you want to continue?" ++msgstr "" ++"您的系統似乎在虛擬化環境中且無初始化程序,例如 Linux-VServer。Ubuntu 10.04 " ++"LTS 無法在此環境中執行,需要先更新您的虛擬機器設定。\n" ++"\n" ++"您確定想要繼續?" ++ ++#: ../DistUpgrade/DistUpgradeMain.py:65 ++msgid "Sandbox upgrade using aufs" ++msgstr "使用 aufs 作為沙堆升級" ++ ++#: ../DistUpgrade/DistUpgradeMain.py:67 ++msgid "Use the given path to search for a cdrom with upgradable packages" ++msgstr "使用指定路徑搜尋附有升級套件的光碟" ++ ++#: ../DistUpgrade/DistUpgradeMain.py:73 ++msgid "" ++"Use frontend. Currently available: \n" ++"DistUpgradeViewText, DistUpgradeViewGtk, DistUpgradeViewKDE" ++msgstr "" ++"使用前端介面。可供選擇的有: \n" ++"DistUpgradeViewText (純文字), DistUpgradeViewGtk (GTK+), DistUpgradeViewKDE " ++"(KDE)" ++ ++#: ../DistUpgrade/DistUpgradeMain.py:76 ++msgid "*DEPRECATED* this option will be ignored" ++msgstr "*已棄用* 這個選項會被忽略" ++ ++#: ../DistUpgrade/DistUpgradeMain.py:79 ++msgid "Perform a partial upgrade only (no sources.list rewriting)" ++msgstr "只進行部份更新 (無須修改 sources.list)" ++ ++#: ../DistUpgrade/DistUpgradeMain.py:82 ++msgid "Disable GNU screen support" ++msgstr "停用 GNU screen 支援" ++ ++#: ../DistUpgrade/DistUpgradeMain.py:84 ++msgid "Set datadir" ++msgstr "設定資料目錄" ++ ++#. print("mediaChange %s %s" % (medium, drive)) ++#: ../DistUpgrade/DistUpgradeViewGtk.py:114 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:117 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:195 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:155 ++#, python-format ++msgid "Please insert '%s' into the drive '%s'" ++msgstr "請將‘%s’放入光碟機‘%s’" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:135 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:138 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:209 ++msgid "Fetching is complete" ++msgstr "提取完成" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:146 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:149 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:222 ++#, python-format ++msgid "Fetching file %li of %li at %sB/s" ++msgstr "正提取第 %li 個檔案 (共 %li 個),速度為 %sB/秒" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:149 ++#: ../DistUpgrade/DistUpgradeViewGtk.py:296 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:152 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:309 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:223 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:371 ++#, python-format ++msgid "About %s remaining" ++msgstr "大約還有 %s" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:152 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:155 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:225 ++#, python-format ++msgid "Fetching file %li of %li" ++msgstr "正提取第 %li 個檔案 (共 %li 個)" ++ ++#. FIXME: add support for the timeout ++#. of the terminal (to display something useful then) ++#. -> longer term, move this code into python-apt ++#: ../DistUpgrade/DistUpgradeViewGtk.py:183 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:186 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:262 ++msgid "Applying changes" ++msgstr "正在套用改動" ++ ++#. we do not report followup errors from earlier failures ++#: ../DistUpgrade/DistUpgradeViewGtk.py:208 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:212 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:275 ++msgid "dependency problems - leaving unconfigured" ++msgstr "相依關係問題 - 仍未被設定" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:213 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:217 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:277 ++#, python-format ++msgid "Could not install '%s'" ++msgstr "無法安裝‘%s’" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:214 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:218 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:278 ++#, python-format ++msgid "" ++"The upgrade will continue but the '%s' package may not be in a working " ++"state. Please consider submitting a bug report about it." ++msgstr "更新會繼續,但 '%s' 套件可能無法運作。請考慮提交關於該套件的臭蟲報告。" ++ ++#. self.expander.set_expanded(True) ++#: ../DistUpgrade/DistUpgradeViewGtk.py:231 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:235 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:299 ++#, python-format ++msgid "" ++"Replace the customized configuration file\n" ++"'%s'?" ++msgstr "" ++"是否取代已有的設定檔案\n" ++"「%s」?" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:232 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:236 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:300 ++msgid "" ++"You will lose any changes you have made to this configuration file if you " ++"choose to replace it with a newer version." ++msgstr "如選擇以新版取代,會失去您改動過的設定。" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:251 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:256 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:323 ++msgid "The 'diff' command was not found" ++msgstr "找不到‘diff’指令" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:464 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:477 ++#: ../DistUpgrade/DistUpgradeViewText.py:92 ++msgid "A fatal error occurred" ++msgstr "發生嚴重錯誤" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:465 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:478 ++msgid "" ++"Please report this as a bug (if you haven't already) and include the files /" ++"var/log/dist-upgrade/main.log and /var/log/dist-upgrade/apt.log in your " ++"report. The upgrade has aborted.\n" ++"Your original sources.list was saved in /etc/apt/sources.list.distUpgrade." ++msgstr "" ++"請回報此錯誤 (若您尚未回報) 並將檔案 /var/log/dist-upgrade/main.log 與 /var/" ++"log/dist-upgrade/apt.log 附在報告。升級程序已中止。\n" ++"您原先的 sources.list 儲存於 /etc/apt/sources.list.distUpgrade" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:482 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:495 ++msgid "Ctrl-c pressed" ++msgstr "按下 Ctrl+c" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:483 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:496 ++msgid "" ++"This will abort the operation and may leave the system in a broken state. " ++"Are you sure you want to do that?" ++msgstr "這將會中止操作並可能令系統在不完整的狀態。您確定要進行嗎?" ++ ++#. append warning ++#: ../DistUpgrade/DistUpgradeViewGtk.py:631 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:629 ++msgid "To prevent data loss close all open applications and documents." ++msgstr "為避免遺失資料,請關閉所有已開啟的程式及文件。" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:645 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:643 ++#, python-format ++msgid "No longer supported by Canonical (%s)" ++msgstr "不再受 Canonical 支援 (%s 個)" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:646 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:644 ++#, python-format ++msgid "<b>Downgrade (%s)</b>" ++msgstr "<b>降級 (%s)</b>" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:647 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:645 ++#, python-format ++msgid "Remove (%s)" ++msgstr "移除 (%s 個)" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:648 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:646 ++#, python-format ++msgid "No longer needed (%s)" ++msgstr "不再需要 (%s 個)" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:649 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:647 ++#, python-format ++msgid "Install (%s)" ++msgstr "安裝 (%s 個)" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:650 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:648 ++#, python-format ++msgid "Upgrade (%s)" ++msgstr "升級 (%s 個)" ++ ++#. change = QMessageBox.question(None, _("Media Change"), msg, QMessageBox.Ok, QMessageBox.Cancel) ++#: ../DistUpgrade/DistUpgradeViewKDE.py:196 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:157 ++msgid "Media Change" ++msgstr "媒體變更" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:335 ++msgid "Show Difference >>>" ++msgstr "顯示差異 >>>" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:338 ++msgid "<<< Hide Difference" ++msgstr "<<< 隱藏差異" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:554 ++msgid "Error" ++msgstr "錯誤" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:568 ++msgid "&Cancel" ++msgstr "取消(&C)" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:572 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:813 ++msgid "&Close" ++msgstr "關閉(&C)" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:618 ++msgid "Show Terminal >>>" ++msgstr "顯示終端畫面 >>>" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:621 ++msgid "<<< Hide Terminal" ++msgstr "<<< 隱藏終端畫面" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:701 ++msgid "Information" ++msgstr "資訊" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:751 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:796 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:799 ../DistUpgrade/DistUpgrade.ui.h:7 ++msgid "Details" ++msgstr "詳情" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:777 ++#, python-format ++msgid "No longer supported %s" ++msgstr "不再受支援 %s" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:779 ++#, python-format ++msgid "Remove %s" ++msgstr "移除 %s" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:781 ++#: ../DistUpgrade/DistUpgradeViewText.py:182 ++#, python-format ++msgid "Remove (was auto installed) %s" ++msgstr "移除 (自動安裝的) %s" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:783 ++#, python-format ++msgid "Install %s" ++msgstr "安裝 %s" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:785 ++#, python-format ++msgid "Upgrade %s" ++msgstr "升級 %s" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:809 ++#: ../DistUpgrade/DistUpgradeViewText.py:230 ++msgid "Restart required" ++msgstr "需要重新開機" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:809 ++msgid "<b><big>Restart the system to complete the upgrade</big></b>" ++msgstr "<b><big>重新啟動系統以完成更新</big></b>" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:812 ../DistUpgrade/DistUpgrade.ui.h:14 ++#: ../data/gtkbuilder/UpdateManager.ui.h:26 ++msgid "_Restart Now" ++msgstr "現在重新啟動(_R)" ++ ++#. FIXME make this user friendly ++#: ../DistUpgrade/DistUpgradeViewKDE.py:830 ++msgid "" ++"<b><big>Cancel the running upgrade?</big></b>\n" ++"\n" ++"The system could be in an unusable state if you cancel the upgrade. You are " ++"strongly advised to resume the upgrade." ++msgstr "" ++"<b><big>取消進行中的升級嗎?</big></b>\n" ++"\n" ++"如果您取消升級,系統可能會在不穩定的狀態。強烈建議您繼續升級工作。" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:834 ++msgid "Cancel Upgrade?" ++msgstr "要取消升級嗎?" ++ ++#: ../DistUpgrade/DistUpgradeView.py:61 ++#, python-format ++msgid "%li day" ++msgid_plural "%li days" ++msgstr[0] "%li 日" ++ ++#: ../DistUpgrade/DistUpgradeView.py:63 ++#, python-format ++msgid "%li hour" ++msgid_plural "%li hours" ++msgstr[0] "%li 小時" ++ ++#: ../DistUpgrade/DistUpgradeView.py:65 ++#, python-format ++msgid "%li minute" ++msgid_plural "%li minutes" ++msgstr[0] "%li 分鐘" ++ ++#: ../DistUpgrade/DistUpgradeView.py:66 ++#, python-format ++msgid "%li second" ++msgid_plural "%li seconds" ++msgstr[0] "%li 秒" ++ ++#. TRANSLATORS: you can alter the ordering of the remaining time ++#. information here if you shuffle %(str_days)s %(str_hours)s %(str_minutes)s ++#. around. Make sure to keep all '$(str_*)s' in the translated string ++#. and do NOT change anything appart from the ordering. ++#. ++#. %(str_hours)s will be either "1 hour" or "2 hours" depending on the ++#. plural form ++#. ++#. Note: most western languages will not need to change this ++#: ../DistUpgrade/DistUpgradeView.py:82 ++#, python-format ++msgid "%(str_days)s %(str_hours)s" ++msgstr "%(str_days)s零 %(str_hours)s" ++ ++#. TRANSLATORS: you can alter the ordering of the remaining time ++#. information here if you shuffle %(str_hours)s %(str_minutes)s ++#. around. Make sure to keep all '$(str_*)s' in the translated string ++#. and do NOT change anything appart from the ordering. ++#. ++#. %(str_hours)s will be either "1 hour" or "2 hours" depending on the ++#. plural form ++#. ++#. Note: most western languages will not need to change this ++#: ../DistUpgrade/DistUpgradeView.py:100 ++#, python-format ++msgid "%(str_hours)s %(str_minutes)s" ++msgstr "%(str_hours)s又 %(str_minutes)s" ++ ++#. 56 kbit ++#. 1Mbit = 1024 kbit ++#: ../DistUpgrade/DistUpgradeView.py:151 ++#, python-format ++msgid "" ++"This download will take about %s with a 1Mbit DSL connection and about %s " ++"with a 56k modem." ++msgstr "這次下載所需時間在 1M 寬頻連線大約要 %s,用 56k 數據機大約要 %s。" ++ ++#. if we have a estimated speed, use it ++#: ../DistUpgrade/DistUpgradeView.py:155 ++#, python-format ++msgid "This download will take about %s with your connection. " ++msgstr "按照您的連線速度,此下載會花約 %s。 " ++ ++#. Declare these translatable strings from the .ui files here so that ++#. xgettext picks them up. ++#: ../DistUpgrade/DistUpgradeView.py:259 ../DistUpgrade/DistUpgrade.ui.h:21 ++msgid "Preparing to upgrade" ++msgstr "準備升級" ++ ++#: ../DistUpgrade/DistUpgradeView.py:260 ++msgid "Getting new software channels" ++msgstr "取得新軟件頻道中" ++ ++#: ../DistUpgrade/DistUpgradeView.py:261 ../DistUpgrade/DistUpgrade.ui.h:23 ++msgid "Getting new packages" ++msgstr "取得新套件" ++ ++#: ../DistUpgrade/DistUpgradeView.py:262 ../DistUpgrade/DistUpgrade.ui.h:26 ++msgid "Installing the upgrades" ++msgstr "安裝升級" ++ ++#: ../DistUpgrade/DistUpgradeView.py:263 ../DistUpgrade/DistUpgrade.ui.h:25 ++msgid "Cleaning up" ++msgstr "清理" ++ ++#: ../DistUpgrade/DistUpgradeView.py:348 ++#, python-format ++msgid "" ++"%(amount)d installed package is no longer supported by Canonical. You can " ++"still get support from the community." ++msgid_plural "" ++"%(amount)d installed packages are no longer supported by Canonical. You can " ++"still get support from the community." ++msgstr[0] "" ++"已安裝套件中有 %(amount)d 個不再受 Canonical 支援。您仍可以取得來自社羣的支" ++"援。" ++ ++#. FIXME: make those two separate lines to make it clear ++#. that the "%" applies to the result of ngettext ++#: ../DistUpgrade/DistUpgradeView.py:357 ++#, python-format ++msgid "%d package is going to be removed." ++msgid_plural "%d packages are going to be removed." ++msgstr[0] "即將移除 %d 個套件。" ++ ++#: ../DistUpgrade/DistUpgradeView.py:362 ++#, python-format ++msgid "%d new package is going to be installed." ++msgid_plural "%d new packages are going to be installed." ++msgstr[0] "即將安裝 %d 個新套件。" ++ ++#: ../DistUpgrade/DistUpgradeView.py:368 ++#, python-format ++msgid "%d package is going to be upgraded." ++msgid_plural "%d packages are going to be upgraded." ++msgstr[0] "即將升級 %d 個套件。" ++ ++#: ../DistUpgrade/DistUpgradeView.py:373 ++#, python-format ++msgid "" ++"\n" ++"\n" ++"You have to download a total of %s. " ++msgstr "" ++"\n" ++"\n" ++"您必須下載全部的%s。 " ++ ++#: ../DistUpgrade/DistUpgradeView.py:378 ++msgid "" ++"Installing the upgrade can take several hours. Once the download has " ++"finished, the process cannot be canceled." ++msgstr "" ++ ++#: ../DistUpgrade/DistUpgradeView.py:382 ++msgid "" ++"Fetching and installing the upgrade can take several hours. Once the " ++"download has finished, the process cannot be canceled." ++msgstr "" ++ ++#: ../DistUpgrade/DistUpgradeView.py:387 ++msgid "Removing the packages can take several hours. " ++msgstr "" ++ ++#. FIXME: this should go into DistUpgradeController ++#: ../DistUpgrade/DistUpgradeView.py:392 ../UpdateManager/UpdateManager.py:676 ++msgid "The software on this computer is up to date." ++msgstr "" ++ ++#: ../DistUpgrade/DistUpgradeView.py:393 ++msgid "" ++"There are no upgrades available for your system. The upgrade will now be " ++"canceled." ++msgstr "您的系統已經在最新狀態。現在將取消升級的動作。" ++ ++#: ../DistUpgrade/DistUpgradeView.py:406 ++msgid "Reboot required" ++msgstr "需要重新開機" ++ ++#: ../DistUpgrade/DistUpgradeView.py:407 ++msgid "" ++"The upgrade is finished and a reboot is required. Do you want to do this now?" ++msgstr "升級已經完成及需要重新啟動。現在要重新啟動嗎?" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:72 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:72 ++#, python-format ++msgid "authenticate '%(file)s' against '%(signature)s' " ++msgstr "" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:131 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:131 ++#, python-format ++msgid "extracting '%s'" ++msgstr "" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:151 ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:152 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:151 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:152 ++msgid "Could not run the upgrade tool" ++msgstr "無法執行升級工具" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:152 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:152 ++msgid "" ++"This is most likely a bug in the upgrade tool. Please report it as a bug " ++"using the command 'ubuntu-bug update-manager'." ++msgstr "" ++"這可能是升級工具的錯誤,請使用指令「ubuntu-bug update-manager」回報錯誤。" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:227 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:227 ++msgid "Upgrade tool signature" ++msgstr "升級工具簽署" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:234 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:234 ++msgid "Upgrade tool" ++msgstr "升級工具" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:268 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:268 ++msgid "Failed to fetch" ++msgstr "提取失敗" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:269 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:269 ++msgid "Fetching the upgrade failed. There may be a network problem. " ++msgstr "提取升級套件失敗。可能是網絡問題。 " ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:273 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:273 ++msgid "Authentication failed" ++msgstr "認證失敗" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:274 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:274 ++msgid "" ++"Authenticating the upgrade failed. There may be a problem with the network " ++"or with the server. " ++msgstr "認證升級套件失敗。可能是因為跟伺服器的網絡連線出現問題。 " ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:279 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:279 ++msgid "Failed to extract" ++msgstr "解壓失敗" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:280 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:280 ++msgid "" ++"Extracting the upgrade failed. There may be a problem with the network or " ++"with the server. " ++msgstr "升級套件解壓失敗。可能是因為網絡或伺服器出現問題。 " ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:285 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:285 ++msgid "Verification failed" ++msgstr "驗證失敗" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:286 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:286 ++msgid "" ++"Verifying the upgrade failed. There may be a problem with the network or " ++"with the server. " ++msgstr "驗證升級套件失敗。可能是因為網絡或伺服器出現問題。 " ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:300 ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:306 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:300 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:306 ++msgid "Can not run the upgrade" ++msgstr "不能進行升級" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:301 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:301 ++msgid "" ++"This usually is caused by a system where /tmp is mounted noexec. Please " ++"remount without noexec and run the upgrade again." ++msgstr "" ++"這通常是由使用 noexec 掛載 /tmp 的系統引致的。請不要使用 noexec 重新掛載,並" ++"再次進行升級。" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:307 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:307 ++#, python-format ++msgid "The error message is '%s'." ++msgstr "錯誤訊息 '%s'。" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:93 ++msgid "" ++"Please report this as a bug and include the files /var/log/dist-upgrade/main." ++"log and /var/log/dist-upgrade/apt.log in your report. The upgrade has " ++"aborted.\n" ++"Your original sources.list was saved in /etc/apt/sources.list.distUpgrade." ++msgstr "" ++"請回報此錯誤並將檔案 /var/log/dist-upgrade/main.log 與 /var/log/dist-upgrade/" ++"apt.log 附在報告。升級程序已中止。\n" ++"您原先的 sources.list 儲存於 /etc/apt/sources.list.distUpgrade" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:117 ++msgid "Aborting" ++msgstr "正在中止" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:122 ++msgid "Demoted:\n" ++msgstr "降等:\n" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:129 ++msgid "To continue please press [ENTER]" ++msgstr "若要繼續請按 [ENTER]" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:157 ++#: ../DistUpgrade/DistUpgradeViewText.py:196 ++#: ../DistUpgrade/DistUpgradeViewText.py:203 ++msgid "Continue [yN] " ++msgstr "繼續 [yN] " ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:157 ++#: ../DistUpgrade/DistUpgradeViewText.py:196 ++msgid "Details [d]" ++msgstr "詳情 [d]" ++ ++#. TRANSLATORS: the "y" is "yes" ++#. TRANSLATORS: first letter of a positive (yes) answer ++#: ../DistUpgrade/DistUpgradeViewText.py:162 ++#: ../DistUpgrade/DistUpgradeViewText.py:206 ++msgid "y" ++msgstr "y" ++ ++#. TRANSLATORS: the "n" is "no" ++#. TRANSLATORS: first letter of a negative (no) answer ++#: ../DistUpgrade/DistUpgradeViewText.py:165 ++#: ../DistUpgrade/DistUpgradeViewText.py:213 ++msgid "n" ++msgstr "n" ++ ++#. TRANSLATORS: the "d" is "details" ++#: ../DistUpgrade/DistUpgradeViewText.py:168 ++msgid "d" ++msgstr "d" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:173 ++#, python-format ++msgid "No longer supported: %s\n" ++msgstr "不再支援:%s\n" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:178 ++#, python-format ++msgid "Remove: %s\n" ++msgstr "移除: %s\n" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:188 ++#, python-format ++msgid "Install: %s\n" ++msgstr "安裝: %s\n" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:193 ++#, python-format ++msgid "Upgrade: %s\n" ++msgstr "升級: %s\n" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:210 ++msgid "Continue [Yn] " ++msgstr "繼續 [Yn] " ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:231 ++msgid "" ++"To finish the upgrade, a restart is required.\n" ++"If you select 'y' the system will be restarted." ++msgstr "" ++"需要重新開機才能完成更新。\n" ++"如果您選擇 'y' 系統將會重新開機。" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:1 ++msgid "_Cancel Upgrade" ++msgstr "取消升級(_C)" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:2 ++msgid "_Resume Upgrade" ++msgstr "繼續升級(_R)" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:3 ++msgid "" ++"<b><big>Cancel the running upgrade?</big></b>\n" ++"\n" ++"The system could be in an unusable state if you cancel the upgrade. You are " ++"strongly adviced to resume the upgrade." ++msgstr "" ++"<b><big>取消正在執行的升級?</big></b>\n" ++"\n" ++"如果您取消升級可能會導致系統不穩定。強烈建議您繼續升級。" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:6 ++msgid "_Start Upgrade" ++msgstr "開始升級(_S)" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:9 ++msgid "_Replace" ++msgstr "取代(_R)" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:10 ++msgid "Difference between the files" ++msgstr "檔案差異" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:11 ++msgid "_Report Bug" ++msgstr "匯報錯誤(_R)" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:12 ++msgid "_Continue" ++msgstr "繼續(_C)" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:13 ++msgid "<b><big>Start the upgrade?</big></b>" ++msgstr "<b><big>開始升級嗎?</big></b>" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:15 ++msgid "" ++"<b><big>Restart the system to complete the upgrade</big></b>\n" ++"\n" ++"Please save your work before continuing." ++msgstr "" ++"<b><big>重新啟動系統以完成更新</big></b>\n" ++"\n" ++"請在繼續前先儲存您的作業。" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:18 ++msgid "Distribution Upgrade" ++msgstr "發行版升級" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:19 ++#, fuzzy ++msgid "<b><big>Upgrading Ubuntu to version 12.10</big></b>" ++msgstr "<b><big>將 Ubuntu 升級至 11.10 版</big></b>" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:20 ++msgid " " ++msgstr " " ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:22 ++msgid "Setting new software channels" ++msgstr "設定新的軟件來源頻道" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:24 ++msgid "Restarting the computer" ++msgstr "重新啟動系統" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:27 ++msgid "Terminal" ++msgstr "終端" ++ ++#: ../UpdateManager/backend/InstallBackendSynaptic.py:64 ++msgid "Please wait, this can take some time." ++msgstr "請稍候,這可能需要一點時間。" ++ ++#: ../UpdateManager/backend/InstallBackendSynaptic.py:66 ++msgid "Update is complete" ++msgstr "更新完成" ++ ++#: ../UpdateManager/DistUpgradeFetcher.py:114 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:109 ++msgid "Could not find the release notes" ++msgstr "找不到發行公告" ++ ++#: ../UpdateManager/DistUpgradeFetcher.py:115 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:110 ++msgid "The server may be overloaded. " ++msgstr "伺服器可能負荷過重。 " ++ ++#: ../UpdateManager/DistUpgradeFetcher.py:125 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:114 ++msgid "Could not download the release notes" ++msgstr "無法下載發行公告" ++ ++#: ../UpdateManager/DistUpgradeFetcher.py:126 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:115 ++msgid "Please check your internet connection." ++msgstr "請檢查您的互聯網連線。" ++ ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:68 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:91 ++msgid "Upgrade" ++msgstr "升級" ++ ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:95 ++#: ../data/gtkbuilder/UpdateManager.ui.h:20 ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:2 ++msgid "Release Notes" ++msgstr "發行公告" ++ ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:134 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:148 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:150 ++msgid "Downloading additional package files..." ++msgstr "正在下載額外的套件檔案..." ++ ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:148 ++#, python-format ++msgid "File %s of %s at %sB/s" ++msgstr "檔案 %s / %s (速度:%sB/秒)" ++ ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:150 ++#, python-format ++msgid "File %s of %s" ++msgstr "檔案 %s / %s" ++ ++#: ../UpdateManager/ChangelogViewer.py:75 ++msgid "Open Link in Browser" ++msgstr "用瀏覽器開啟連結" ++ ++#: ../UpdateManager/ChangelogViewer.py:78 ++msgid "Copy Link to Clipboard" ++msgstr "複製連結至剪貼簿" ++ ++#: ../UpdateManager/GtkProgress.py:162 ++#, python-format ++msgid "Downloading file %(current)li of %(total)li with %(speed)s/s" ++msgstr "共 %(total)li 個檔案,正下載第 %(current)li 個 (速度:%(speed)s/秒)" ++ ++#: ../UpdateManager/GtkProgress.py:167 ++#, python-format ++msgid "Downloading file %(current)li of %(total)li" ++msgstr "共 %(total)li 個檔案,正下載第 %(current)li 個" ++ ++#: ../UpdateManager/UpdateManager.py:106 ../do-release-upgrade:100 ++msgid "Your Ubuntu release is not supported anymore." ++msgstr "您的 Ubuntu 發行版本已經不再支援。" ++ ++#: ../UpdateManager/UpdateManager.py:107 ++msgid "" ++"You will not get any further security fixes or critical updates. Please " ++"upgrade to a later version of Ubuntu." ++msgstr "將無法再取得保安修正或重要更新。請升級至最新版本 Ubuntu。" ++ ++#: ../UpdateManager/UpdateManager.py:115 ++msgid "Upgrade information" ++msgstr "升級資訊" ++ ++#: ../UpdateManager/UpdateManager.py:233 ++#: ../UpdateManagerText/UpdateManagerText.py:35 ++msgid "Install" ++msgstr "安裝" ++ ++#: ../UpdateManager/UpdateManager.py:235 ++msgid "Name" ++msgstr "" ++ ++#. upload_archive = version_match.group(2).strip() ++#: ../UpdateManager/UpdateManager.py:395 ++#, python-format ++msgid "Version %s: \n" ++msgstr "版本 %s: \n" ++ ++#: ../UpdateManager/UpdateManager.py:453 ++msgid "" ++"No network connection detected, you can not download changelog information." ++msgstr "偵測不到網絡連線,故無法下載改動記錄。" ++ ++#: ../UpdateManager/UpdateManager.py:463 ++msgid "Downloading list of changes..." ++msgstr "正在下載改動清單..." ++ ++#: ../UpdateManager/UpdateManager.py:507 ++msgid "_Deselect All" ++msgstr "取消所有選取(_D)" ++ ++#: ../UpdateManager/UpdateManager.py:513 ++msgid "Select _All" ++msgstr "全部選取(_A)" ++ ++#: ../UpdateManager/UpdateManager.py:572 ++#, python-format ++msgid "%s will be downloaded." ++msgstr "會下載 %s。" ++ ++#: ../UpdateManager/UpdateManager.py:584 ++#, fuzzy ++msgid "The update has already been downloaded." ++msgid_plural "The updates have already been downloaded." ++msgstr[0] "更新已下載,但尚未安裝" ++ ++#: ../UpdateManager/UpdateManager.py:589 ++msgid "There are no updates to install." ++msgstr "" ++ ++#: ../UpdateManager/UpdateManager.py:598 ++msgid "Unknown download size." ++msgstr "下載大小不詳。" ++ ++#: ../UpdateManager/UpdateManager.py:624 ++msgid "" ++"It is unknown when the package information was updated last. Please click " ++"the 'Check' button to update the information." ++msgstr "未知套件資訊的上次更新時間。請點擊「檢查」按鈕來更新資訊。" ++ ++#: ../UpdateManager/UpdateManager.py:630 ++#, python-format ++msgid "" ++"The package information was last updated %(days_ago)s days ago.\n" ++"Press the 'Check' button below to check for new software updates." ++msgstr "" ++"%(days_ago)s 天前更新過套件資訊。\n" ++"請按下方「檢查」鈕看看有否新資訊。" ++ ++#: ../UpdateManager/UpdateManager.py:635 ++#, python-format ++msgid "The package information was last updated %(days_ago)s day ago." ++msgid_plural "The package information was last updated %(days_ago)s days ago." ++msgstr[0] "上次更新套件資訊是 %(days_ago)s 天前。" ++ ++#: ../UpdateManager/UpdateManager.py:639 ++#, python-format ++msgid "The package information was last updated %(hours_ago)s hour ago." ++msgid_plural "" ++"The package information was last updated %(hours_ago)s hours ago." ++msgstr[0] "上次更新套件資訊是 %(hours_ago)s 小時前。" ++ ++#. TRANSLATORS: only in plural form, as %s minutes ago is one of 15, 30, 45 minutes ago ++#: ../UpdateManager/UpdateManager.py:644 ../UpdateManager/UpdateManager.py:646 ++#: ../UpdateManager/UpdateManager.py:648 ++#, python-format ++msgid "The package information was last updated about %s minutes ago." ++msgstr "套件資訊約 %s 分鐘前更新。" ++ ++#: ../UpdateManager/UpdateManager.py:650 ++msgid "The package information was just updated." ++msgstr "套件資訊剛更新。" ++ ++#: ../UpdateManager/UpdateManager.py:689 ++msgid "Software updates may be available for your computer." ++msgstr "可能有軟體更新提供予閣下之電腦。" ++ ++#: ../UpdateManager/UpdateManager.py:697 ++#, python-format ++msgid "" ++"Updated software has been issued since %s was released. Do you want to " ++"install it now?" ++msgstr "" ++ ++#: ../UpdateManager/UpdateManager.py:700 ++msgid "" ++"Updated software is available for this computer. Do you want to install it " ++"now?" ++msgstr "" ++ ++#: ../UpdateManager/UpdateManager.py:758 ++#, python-format ++msgid "" ++"The upgrade needs a total of %s free space on disk '%s'. Please free at " ++"least an additional %s of disk space on '%s'. Empty your trash and remove " ++"temporary packages of former installations using 'sudo apt-get clean'." ++msgstr "" ++"升級工作需要總共 %s 可用空間於硬碟 ‘%s’。請額外空出最少 %s 的空間於 ‘%s’。清" ++"理清理您的回收筒或使用 ‘sudo apt-get clean’ 移除上次安裝的暫存套件。" ++ ++#: ../UpdateManager/UpdateManager.py:783 ++msgid "" ++"The computer needs to restart to finish installing updates. Please save your " ++"work before continuing." ++msgstr "必須重新啟動電腦來完成安裝更新,在繼續之前請先儲存您的工作。" ++ ++#: ../UpdateManager/UpdateManager.py:847 ++msgid "Reading package information" ++msgstr "正在讀取套件資訊" ++ ++#: ../UpdateManager/UpdateManager.py:862 ++msgid "Connecting..." ++msgstr "連線中..." ++ ++#: ../UpdateManager/UpdateManager.py:879 ++msgid "You may not be able to check for updates or download new updates." ++msgstr "可能無法檢查是否有、或下載新的更新。" ++ ++#: ../UpdateManager/UpdateManager.py:1002 ++msgid "Could not initialize the package information" ++msgstr "無法為套件資訊進行初始化" ++ ++#: ../UpdateManager/UpdateManager.py:1003 ++msgid "" ++"An unresolvable problem occurred while initializing the package " ++"information.\n" ++"\n" ++"Please report this bug against the 'update-manager' package and include the " ++"following error message:\n" ++msgstr "" ++"為套件資訊進行初始化時發生不能解決的問題。\n" ++"\n" ++"請將此匯報為「update-manager」套件的問題並附上以下錯誤訊息:\n" ++ ++#: ../UpdateManager/UpdateManager.py:1032 ++msgid "" ++"An unresolvable problem occurred while calculating the upgrade.\n" ++"\n" ++"Please report this bug against the 'update-manager' package and include the " ++"following error message:" ++msgstr "" ++"為升級進行推算時有問題無法解決。\n" ++"\n" ++"請為「update-manager」套件匯報此問題並附上以下錯誤訊息:" ++ ++#: ../UpdateManager/UpdateManager.py:1056 ++msgid " (New install)" ++msgstr " (新安裝)" ++ ++#. TRANSLATORS: the b stands for Bytes ++#: ../UpdateManager/UpdateManager.py:1063 ++#, python-format ++msgid "(Size: %s)" ++msgstr "(大小:%s)" ++ ++#: ../UpdateManager/UpdateManager.py:1067 ++#, python-format ++msgid "From version %(old_version)s to %(new_version)s" ++msgstr "由 %(old_version)s 版更新至 %(new_version)s 版" ++ ++#: ../UpdateManager/UpdateManager.py:1071 ++#, python-format ++msgid "Version %s" ++msgstr "%s 版" ++ ++#: ../UpdateManager/UpdateManager.py:1104 ../do-release-upgrade:112 ++msgid "Release upgrade not possible right now" ++msgstr "目前不能升級發行版" ++ ++#: ../UpdateManager/UpdateManager.py:1105 ../do-release-upgrade:113 ++#, c-format, python-format ++msgid "" ++"The release upgrade can not be performed currently, please try again later. " ++"The server reported: '%s'" ++msgstr "目前無法升級發行版,請稍後重試。該伺服器回報:「%s」。" ++ ++#: ../UpdateManager/UpdateManager.py:1107 ../check-new-release-gtk:126 ++msgid "Downloading the release upgrade tool" ++msgstr "正在下載發行版更新工具" ++ ++#: ../UpdateManager/UpdateManager.py:1114 ++#, python-format ++msgid "<b>New Ubuntu release '%s' is available</b>" ++msgstr "<b>有新的 Ubuntu 發行版 '%s' 可供升級</b>" ++ ++#. we assert a clean cache ++#: ../UpdateManager/UpdateManager.py:1153 ++msgid "Software index is broken" ++msgstr "軟件索引已損壞" ++ ++#: ../UpdateManager/UpdateManager.py:1154 ++msgid "" ++"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." ++msgstr "" ++"無法安裝或移除套件。請先以「Synaptic 套件管理員」或在終端機執行「sudo apt-" ++"get install -f」修正問題。" ++ ++#: ../UpdateManager/UnitySupport.py:57 ++msgid "Check for Updates" ++msgstr "檢查有否更新" ++ ++#: ../UpdateManager/UnitySupport.py:66 ++msgid "Install All Available Updates" ++msgstr "安裝所有可進行的更新" ++ ++#: ../UpdateManagerText/UpdateManagerText.py:34 ++msgid "Cancel" ++msgstr "取消" ++ ++#: ../UpdateManagerText/UpdateManagerText.py:37 ++msgid "Changelog" ++msgstr "" ++ ++#: ../UpdateManagerText/UpdateManagerText.py:40 ++msgid "Updates" ++msgstr "" ++ ++#: ../UpdateManagerText/UpdateManagerText.py:53 ++msgid "Building Updates List" ++msgstr "正在建立更新清單" ++ ++#: ../UpdateManagerText/UpdateManagerText.py:56 ++msgid "" ++"\n" ++"A normal upgrade can not be calculated, please run: \n" ++" sudo apt-get dist-upgrade\n" ++"\n" ++"\n" ++"This can be caused by:\n" ++" * A previous upgrade which didn't complete\n" ++" * Problems with some of the installed software\n" ++" * Unofficial software packages not provided by Ubuntu\n" ++" * Normal changes of a pre-release version of Ubuntu" ++msgstr "" ++"\n" ++"無法為標準升級進行推算,請執行:\n" ++" sudo apt-get dist-upgrade\n" ++"\n" ++"\n" ++" 原因可能是:\n" ++" * 前次升級程序未完成\n" ++" * 某些已安裝的軟件有問題\n" ++" * 非 Ubuntu 官方軟件套件的問題\n" ++" * 測試版 Ubuntu 的正常改動" ++ ++#: ../UpdateManagerText/UpdateManagerText.py:125 ++msgid "Downloading changelog" ++msgstr "正在下載改動記錄" ++ ++#: ../UpdateManager/Core/MyCache.py:147 ++#, python-format ++msgid "Other updates (%s)" ++msgstr "其他更新 (%s)" ++ ++#: ../UpdateManager/Core/MyCache.py:325 ++msgid "This update does not come from a source that supports changelogs." ++msgstr "此更新來源不支援改動記錄。" ++ ++#: ../UpdateManager/Core/MyCache.py:331 ../UpdateManager/Core/MyCache.py:359 ++msgid "" ++"Failed to download the list of changes. \n" ++"Please check your Internet connection." ++msgstr "" ++"未能下載改動清單。\n" ++"請檢查互聯網連線。" ++ ++#: ../UpdateManager/Core/MyCache.py:338 ++#, python-format ++msgid "" ++"Changes for the versions:\n" ++"Installed version: %s\n" ++"Available version: %s\n" ++"\n" ++msgstr "" ++"版本改動:\n" ++"已安裝版本:%s\n" ++"新版本:%s\n" ++"\n" ++ ++#: ../UpdateManager/Core/MyCache.py:348 ++#, python-format ++msgid "" ++"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." ++msgstr "" ++"改動記錄並未包含相關資料。\n" ++"\n" ++"有改動紀錄提供前請看 http://launchpad.net/ubuntu/+source/%s/%s/" ++"+changelog ,\n" ++"或稍候再嘗試。" ++ ++#: ../UpdateManager/Core/MyCache.py:353 ++#, python-format ++msgid "" ++"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." ++msgstr "" ++"仍未有改動清單提供。\n" ++"\n" ++"有改動清單提供前請看 http://launchpad.net/ubuntu/+source/%s/%s/" ++"+changelog ,\n" ++"或稍候再嘗試。" ++ ++#: ../UpdateManager/Core/UpdateList.py:51 ++msgid "Failed to detect distribution" ++msgstr "無法偵測出版本" ++ ++#: ../UpdateManager/Core/UpdateList.py:52 ++#, python-format ++msgid "A error '%s' occurred while checking what system you are using." ++msgstr "當檢查您所使用的系統時有錯誤 \"%s\" 發生。" ++ ++#: ../UpdateManager/Core/UpdateList.py:63 ++msgid "Important security updates" ++msgstr "重要保安更新" ++ ++#: ../UpdateManager/Core/UpdateList.py:64 ++msgid "Recommended updates" ++msgstr "重要更新" ++ ++#: ../UpdateManager/Core/UpdateList.py:65 ++msgid "Proposed updates" ++msgstr "建議更新" ++ ++#: ../UpdateManager/Core/UpdateList.py:66 ++msgid "Backports" ++msgstr "回植套件" ++ ++#: ../UpdateManager/Core/UpdateList.py:67 ++msgid "Distribution updates" ++msgstr "發行版更新" ++ ++#: ../UpdateManager/Core/UpdateList.py:72 ++msgid "Other updates" ++msgstr "其他更新" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:1 ++#, fuzzy ++msgid "<big><b>Starting Software Updater</b></big>" ++msgstr "<big><b>啟動更新管理員</b></big>" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:2 ++msgid "" ++"Software updates correct errors, eliminate security vulnerabilities and " ++"provide new features." ++msgstr "軟件更新會更正錯誤、排除安全隱患並提供新功能。" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:3 ++msgid "_Partial Upgrade" ++msgstr "部份升級(_P)" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:4 ++msgid "<big><b>Not all updates can be installed</b></big>" ++msgstr "<big><b>並非所有更新都可以安裝</b></big>" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:5 ++msgid "" ++"Run a partial upgrade, to install as many updates as possible. \n" ++"\n" ++"This can be caused by:\n" ++" * A previous upgrade which didn't complete\n" ++" * Problems with some of the installed software\n" ++" * Unofficial software packages not provided by Ubuntu\n" ++" * Normal changes of a pre-release version of Ubuntu" ++msgstr "" ++"執行部份升級,會儘可能安裝最多更新。 \n" ++"\n" ++"原因可能是:\n" ++" * 前次升級程序未完成\n" ++" * 某些已安裝的軟件有問題\n" ++" * 非 Ubuntu 官方軟件套件的問題\n" ++" * 測試版 Ubuntu 的正常改動" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:12 ++msgid "Chec_k" ++msgstr "檢查(_K)" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:13 ++msgid "" ++"<b><big>You must check for updates manually</big></b>\n" ++"\n" ++"Your system does not check for updates automatically. You can configure this " ++"behavior in <i>Software Sources</i> on the <i>Updates</i> tab." ++msgstr "" ++"<b><big>必須手動檢查更新</big></b>\n" ++"\n" ++"系統不會自動檢查更新。可在<i>更新</i> 分頁的<i>軟件來源</i> 設定。" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:16 ++msgid "_Hide this information in the future" ++msgstr "以後不要再顯示此訊息(_H)" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:17 ++msgid "Co_ntinue" ++msgstr "繼續(_N)" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:18 ++msgid "<big><b>Running on battery</b></big>" ++msgstr "<big><b>使用電池運行</b></big>" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:19 ++msgid "Your system is running on battery. Are you sure you want to continue?" ++msgstr "您的系統正在使用電池。確定要繼續嗎?" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:21 ++msgid "_Upgrade" ++msgstr "升級(_U)" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:22 ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:8 ++msgid "Show progress of individual files" ++msgstr "顯示個別檔案進度" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:23 ++#: ../data/update-manager.desktop.in.h:1 ++#, fuzzy ++msgid "Software Updater" ++msgstr "軟件更新" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:24 ++#, fuzzy ++msgid "Starting Software Updater" ++msgstr "軟件更新" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:25 ++msgid "U_pgrade" ++msgstr "升級(_P)" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:27 ++msgid "updates" ++msgstr "更新" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:28 ++msgid "Changes" ++msgstr "改動" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:29 ++msgid "Description" ++msgstr "說明" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:30 ++#, fuzzy ++msgid "Details of updates" ++msgstr "更新說明" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:31 ++msgid "" ++"You are connected via roaming and may be charged for the data consumed by " ++"this update." ++msgstr "正以漫遊連上網絡,可能要對本次更新所下載的資料量付費。" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:32 ++msgid "" ++"You may want to wait until you’re not using a mobile broadband connection." ++msgstr "" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:33 ++msgid "It’s safer to connect the computer to AC power before updating." ++msgstr "在更新前先將電腦接上變壓器比較安全。" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:34 ++msgid "_Settings..." ++msgstr "設定(_S)..." ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:35 ++#, fuzzy ++msgid "_Install Now" ++msgstr "安裝" ++ ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:1 ++msgid "<b>A new version of Ubuntu is available. Would you like to upgrade?</b>" ++msgstr "<b>有新版本 Ubuntu。是否升級?</b>" ++ ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:3 ++msgid "Don't Upgrade" ++msgstr "不升級" ++ ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:4 ++msgid "Ask Me Later" ++msgstr "稍後再問我" ++ ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:5 ++msgid "Yes, Upgrade Now" ++msgstr "是,現在升級" ++ ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:6 ++msgid "You have declined to upgrade to the new Ubuntu" ++msgstr "您已拒絕升級至新版 Ubuntu" ++ ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:7 ++#, fuzzy ++msgid "" ++"You can upgrade at a later time by opening Software Updater and click on " ++"\"Upgrade\"." ++msgstr "您稍後仍可以透過開啟更新管理員並按下『升級』進行升級。" ++ ++#: ../data/update-manager.desktop.in.h:2 ++msgid "Software Updates" ++msgstr "軟件更新" ++ ++#: ../data/update-manager.desktop.in.h:3 ++msgid "Show and install available updates" ++msgstr "顯示並安裝現有的軟件更新" ++ ++#: ../update-manager:66 ../update-manager-text:55 ../do-release-upgrade:51 ++msgid "Show version and exit" ++msgstr "顯示版本並結束" ++ ++#: ../update-manager:69 ++msgid "Directory that contains the data files" ++msgstr "含有資料檔案的目錄" ++ ++#: ../update-manager:72 ++msgid "Check if a new Ubuntu release is available" ++msgstr "檢查有否新 Ubuntu 發行版可供升級" ++ ++#: ../update-manager:75 ../do-release-upgrade:54 ../check-new-release-gtk:186 ++msgid "Check if upgrading to the latest devel release is possible" ++msgstr "檢查能否升級至最新測試版" ++ ++#: ../update-manager:79 ++msgid "Upgrade using the latest proposed version of the release upgrader" ++msgstr "以最新建議版本的發行升級工具進行升級" ++ ++#: ../update-manager:86 ++msgid "Do not focus on map when starting" ++msgstr "啟動時不預先選取圖錄" ++ ++#: ../update-manager:89 ++msgid "Try to run a dist-upgrade" ++msgstr "試著執行 dist-upgrade" ++ ++#: ../update-manager:92 ++msgid "Do not check for updates when starting" ++msgstr "啟動時不要檢查更新" ++ ++#: ../update-manager:96 ../do-release-upgrade:70 ++msgid "Test upgrade with a sandbox aufs overlay" ++msgstr "使用沙堆 aufs 層測試升級" ++ ++#: ../update-manager:116 ++msgid "Running partial upgrade" ++msgstr "執行部份升級" ++ ++#: ../update-manager-text:59 ++msgid "Show description of the package instead of the changelog" ++msgstr "" ++ ++#: ../do-release-upgrade:58 ../check-new-release-gtk:190 ++msgid "" ++"Try upgrading to the latest release using the upgrader from $distro-proposed" ++msgstr "嘗試使用 $distro-proposed 的升級程式來升級至最新的發行版本" ++ ++#: ../do-release-upgrade:62 ++msgid "" ++"Run in a special upgrade mode.\n" ++"Currently 'desktop' for regular upgrades of a desktop system and 'server' " ++"for server systems are supported." ++msgstr "" ++"以特殊升級模式進行。\n" ++"目前只支援以 'desktop' 模式升級桌面版本的系統,以及以 'server' 模式升級伺服器" ++"版的系統。" ++ ++#: ../do-release-upgrade:68 ++msgid "Run the specified frontend" ++msgstr "執行指定的前端" ++ ++#: ../do-release-upgrade:73 ++msgid "" ++"Check only if a new distribution release is available and report the result " ++"via the exit code" ++msgstr "檢查是否有新的發行版並以結束碼報告結果" ++ ++#: ../do-release-upgrade:87 ++msgid "Checking for a new Ubuntu release" ++msgstr "" ++ ++#: ../do-release-upgrade:101 ++msgid "" ++"For upgrade information, please visit:\n" ++"%(url)s\n" ++msgstr "" ++"若要取得升級資訊,請參訪:\n" ++"%(url)s\n" ++ ++#: ../do-release-upgrade:107 ++msgid "No new release found" ++msgstr "找不到新發行版" ++ ++#: ../do-release-upgrade:119 ++#, c-format ++msgid "New release '%s' available." ++msgstr "有新發行版 '%s' 提供。" ++ ++#: ../do-release-upgrade:120 ++msgid "Run 'do-release-upgrade' to upgrade to it." ++msgstr "執行 ‘do-release-upgrade’ 進行升級工作。" ++ ++#: ../check-new-release-gtk:101 ++msgid "Ubuntu %(version)s Upgrade Available" ++msgstr "可以升級至 Ubuntu %(version)s" ++ ++#: ../check-new-release-gtk:143 ++#, c-format ++msgid "You have declined the upgrade to Ubuntu %s" ++msgstr "您已拒絕升級至 Ubuntu %s" ++ ++#: ../check-new-release-gtk:196 ++msgid "Add debug output" ++msgstr "" ++ ++#: ../ubuntu-support-status:91 ++msgid "Show unsupported packages on this machine" ++msgstr "" ++ ++#: ../ubuntu-support-status:94 ++msgid "Show supported packages on this machine" ++msgstr "" ++ ++#: ../ubuntu-support-status:97 ++msgid "Show all packages with their status" ++msgstr "" ++ ++#: ../ubuntu-support-status:100 ++msgid "Show all packages in a list" ++msgstr "" ++ ++#: ../ubuntu-support-status:142 ++#, c-format ++msgid "Support status summary of '%s':" ++msgstr "" ++ ++#: ../ubuntu-support-status:145 ++msgid "You have %(num)s packages (%(percent).1f%%) supported until %(time)s" ++msgstr "" ++ ++#: ../ubuntu-support-status:151 ++msgid "" ++"You have %(num)s packages (%(percent).1f%%) that can not/no-longer be " ++"downloaded" ++msgstr "" ++ ++#: ../ubuntu-support-status:154 ++msgid "You have %(num)s packages (%(percent).1f%%) that are unsupported" ++msgstr "" ++ ++#: ../ubuntu-support-status:162 ++msgid "" ++"Run with --show-unsupported, --show-supported or --show-all to see more " ++"details" ++msgstr "" ++ ++#: ../ubuntu-support-status:166 ++msgid "No longer downloadable:" ++msgstr "" ++ ++#: ../ubuntu-support-status:169 ++msgid "Unsupported: " ++msgstr "" ++ ++#: ../ubuntu-support-status:174 ++#, c-format ++msgid "Supported until %s:" ++msgstr "" ++ ++#: ../ubuntu-support-status:183 ++msgid "Unsupported" ++msgstr "" ++ ++#. Why do we use %s here instead of $strings or {} format placeholders? ++#. It's because we don't want to break existing translations. ++#: ../janitor/plugincore/exceptions.py:42 ++#, python-format ++msgid "Unimplemented method: %s" ++msgstr "未實作的方法: %s" ++ ++#: ../janitor/plugincore/core/file_cruft.py:41 ++msgid "A file on disk" ++msgstr "磁碟上之檔案" ++ ++#: ../janitor/plugincore/core/missing_package_cruft.py:39 ++msgid "Install missing package." ++msgstr "安裝缺少的套件。" ++ ++#. 2012-06-08 BAW: i18n string; don't use {} or PEP 292. ++#: ../janitor/plugincore/core/missing_package_cruft.py:49 ++#, python-format ++msgid "Package %s should be installed." ++msgstr "應安裝 %s 套件。" ++ ++#: ../janitor/plugincore/core/package_cruft.py:49 ++msgid ".deb package" ++msgstr ".deb 套件" ++ ++#: ../janitor/plugincore/plugins/langpack_manual_plugin.py:46 ++#, python-format ++msgid "%s needs to be marked as manually installed." ++msgstr "%s 要標記為手動安裝。" ++ ++#: ../janitor/plugincore/plugins/kdelibs4to5_plugin.py:49 ++msgid "" ++"When upgrading, if kdelibs4-dev is installed, kdelibs5-dev needs to be " ++"installed. See bugs.launchpad.net, bug #279621 for details." ++msgstr "" ++"升級時如已安裝 kdelibs4-dev,那就必須安裝 kdelibs5-dev。詳情請見 bugs." ++"launchpad.net,bug #279621。" ++ ++#: ../janitor/plugincore/plugins/dpkg_status_plugin.py:44 ++#, python-format ++msgid "%i obsolete entries in the status file" ++msgstr "狀態檔中有 %i 條廢棄項目" ++ ++#: ../janitor/plugincore/plugins/dpkg_status_plugin.py:47 ++msgid "Obsolete entries in dpkg status" ++msgstr "dpkg 狀態中有廢棄條目" ++ ++#. pragma: no cover ++#: ../janitor/plugincore/plugins/dpkg_status_plugin.py:50 ++msgid "Obsolete dpkg status entries" ++msgstr "廢棄的 dpkg 狀態項目" ++ ++#: ../janitor/plugincore/plugins/remove_lilo_plugin.py:42 ++msgid "Remove lilo since grub is also installed.(See bug #314004 for details.)" ++msgstr "因 grub 已安裝,移除 lilo (詳情見 bug #314004)" ++ ++#~ msgid "<b><big>Upgrading Ubuntu to version 12.04</big></b>" ++#~ msgstr "<b><big>將 Ubuntu 升級至 12.04 版</big></b>" ++ ++#~ msgid "%(count)s update has been selected." ++#~ msgid_plural "%(count)s updates have been selected." ++#~ msgstr[0] "已選取 %(count)s 項更新。" ++ ++#~ msgid "%(count_str)s %(download_str)s" ++#~ msgstr "%(count_str)s %(download_str)s" ++ ++#~ msgid "Welcome to Ubuntu" ++#~ msgstr "歡迎使用 Ubuntu" ++ ++#~ msgid "Update Manager" ++#~ msgstr "更新管理員" ++ ++#~ msgid "Starting Update Manager" ++#~ msgstr "正在啟動更新管理員" ++ ++#~ msgid "You are connected via a wireless modem." ++#~ msgstr "您正以無線數據機連線。" ++ ++#~ msgid "_Install Updates" ++#~ msgstr "安裝更新套件(_I)" ++ ++#~ msgid "Your system is up-to-date" ++#~ msgstr "系統已經在最新狀態" ++ ++#~ msgid "Software updates are available for this computer" ++#~ msgstr "有軟件更新適用於此電腦" ++ ++#~ msgid "There are no updates to install" ++#~ msgstr "沒有要安裝的升級" ++ ++#~ msgid "%.0f kB" ++#~ msgstr "%.0f kB" ++ ++#~ msgid "0 kB" ++#~ msgstr "0 kB" ++ ++#~ msgid "" ++#~ "\n" ++#~ "\n" ++#~ "Please report this bug using the command 'ubuntu-bug update-manager' in a " ++#~ "terminal and include the files in /var/log/dist-upgrade/ in the bug " ++#~ "report.\n" ++#~ "%s" ++#~ msgstr "" ++#~ "\n" ++#~ "\n" ++#~ "請在終端機內輸入指令「ubuntu-bug update-manager」回報錯誤,並在錯誤報告中" ++#~ "附上 /var/log/dist-upgrade/ 內之檔案。\n" ++#~ "%s" ++ ++#~ msgid "" ++#~ "Preparing the system for the upgrade failed. Please report this using the " ++#~ "command 'ubuntu-bug update-manager' in a terminal and include the files " ++#~ "in /var/log/dist-upgrade/ in the bug report." ++#~ msgstr "" ++#~ "準備系統升級失敗。請在終端機內輸入指令「ubuntu-bug update-manager」回報錯" ++#~ "誤,並在錯誤報告中附上 /var/log/dist-upgrade/ 內之檔案。" ++ ++#~ msgid "" ++#~ "Upgrading the repository information resulted in a invalid file. Please " ++#~ "report this as a bug using the command 'ubuntu-bug update-manager' in a " ++#~ "terminal." ++#~ msgstr "" ++#~ "升級套件庫時導致無效的檔案。請在終端機內輸入指令「ubuntu-bug update-" ++#~ "manager」回報錯誤。" ++ ++#~ msgid "1 kB" ++#~ msgstr "1 kB" ++ ++#~ msgid "Your graphics hardware may not be fully supported in Ubuntu 11.04." ++#~ msgstr "您的繪圖硬件可能無法在 Ubuntu 11.04 獲得完整的支援。" ++ ++#~ msgid "" ++#~ "The support in Ubuntu 11.04 for your intel graphics hardware is limited " ++#~ "and you may encounter problems after the upgrade. Do you want to continue " ++#~ "with the upgrade?" ++#~ msgstr "" ++#~ "您的 Intel 繪圖硬件在 Ubuntu 11.04 內的支援有限,且可能會在升級之後碰到一" ++#~ "些問題。您要繼續進行升級嗎?" ++ ++#~ msgid "" ++#~ "The system was unable to get the prerequisites for the upgrade. The " ++#~ "upgrade will abort now and restore the original system state.\n" ++#~ "\n" ++#~ "Please report this as a bug using the command 'ubuntu-bug update-manager' " ++#~ "in a terminal and include the files in /var/log/dist-upgrade/ in the bug " ++#~ "report." ++#~ msgstr "" ++#~ "此系統無法達到升級之要求。現在將中斷升級並將系統回復至原來狀態。\n" ++#~ "\n" ++#~ "請在終端機內輸入指令「ubuntu-bug update-manager」回報錯誤,並在錯誤報告中" ++#~ "附上 /var/log/dist-upgrade/ 內之檔案。" ++ ++#~ msgid "" ++#~ "After your package information was updated the essential package '%s' can " ++#~ "not be found anymore.\n" ++#~ "This indicates a serious error, please report this bug using the command " ++#~ "'ubuntu-bug update-manager' in a terminal and include the files in /var/" ++#~ "log/dist-upgrade/ in the bug report." ++#~ msgstr "" ++#~ "更新套件資訊後再找不到必備套件「%s」。\n" ++#~ "此為嚴重錯誤,請在終端機內輸入指令「ubuntu-bug update-manager」回報錯誤," ++#~ "並在錯誤報告附上 /var/log/dist-upgrade/ 內的檔案。" ++ ++#~ msgid "" ++#~ "You will not get any further security fixes or critical updates. Please " ++#~ "upgrade to a later version of Ubuntu Linux." ++#~ msgstr "無法再取得保安修正與重大更新。請升級至新版 Ubuntu Linux。" ++ ++#~ msgid "" ++#~ "If you don't want to install them now, choose \"Update Manager\" from " ++#~ "Applications later." ++#~ msgstr "若現在不想安裝,可稍後於「應用程式」選單開啟「更新管理員」再安裝。" ++ ++#~ msgid "" ++#~ "These software updates have been issued since this version of Ubuntu was " ++#~ "released. If you don't want to install them now, choose \"Update Manager" ++#~ "\" from Applications later." ++#~ msgstr "" ++#~ "本 Ubuntu 版本自發行已發布此等軟件更新。若現在不想安裝,可稍後於「應用程" ++#~ "式」選單開啟「更新管理員」再安裝。" ++ ++#~ msgid "" ++#~ "These software updates have been issued since this version of Ubuntu was " ++#~ "released. If you don't want to install them now, choose \"Update Manager" ++#~ "\" from the Administration Menu later." ++#~ msgstr "" ++#~ "本 Ubuntu 版本自發行已發布此等軟件更新。若現在不想安裝,可稍後於「管理」選" ++#~ "單開啟「更新管理員」再安裝。" ++ ++#~ msgid "" ++#~ "If you don't want to install them now, choose \"Update Manager\" from the " ++#~ "Administration menu later." ++#~ msgstr "若現在不想安裝,可稍後於「管理」選單開啟「更新管理員」再安裝。" ++ ++#~ msgid "" ++#~ "This upgrade is running in sandbox (test) mode. All changes are written " ++#~ "to '%s' and will be lost on the next reboot.\n" ++#~ "\n" ++#~ "*No* changes written to a systemdir from now until the next reboot are " ++#~ "permanent." ++#~ msgstr "" ++#~ "這次升級是在沙堆 (測試) 模式執行。所有改動都會寫入至「%s」並在下次重新開機" ++#~ "時消失。\n" ++#~ "\n" ++#~ "由現在起至下次重新開機,寫入至系統目錄之改動都*不會*保留。" ++ ++#~ msgid "Checking for a new ubuntu release" ++#~ msgstr "檢查有否新 Ubuntu 發行版" ++ ++#~ msgid "" ++#~ "Fetching and installing the upgrade can take several hours. Once the " ++#~ "download has finished, the process cannot be cancelled." ++#~ msgstr "提取檔案及安裝升級可能要花數小時。一旦下載完成即不能取消升級程序。" ++ ++ ++msgid "Unable to access the source management server, please try again later" ++msgstr "無法存取來源管理伺服器,請稍後再試" ++ ++msgid "Check if your network requires authentication?" ++msgstr "檢查您的網路需要認證嗎?" ++ ++msgid "Check your source public key signature" ++msgstr "檢查您的源數字簽名" ++ ++msgid "update important list occur Exception" ++msgstr "獲取推送出現異常,請稍後再試" ++ ++msgid "You need to be root to run this application" ++msgstr "你需要root許可權運行" ++ ++msgid "There is an exception in the update package." ++msgstr "更新包存在異常!" ++ ++msgid "You request the removal of a system-essential package." ++msgstr "您要求刪除系統必要的套件。" ++ ++msgid "This update cannot detect the upgradeable package." ++msgstr "本次更新無法檢測到可升級的軟體包。" ++ ++msgid "read important list failed" ++msgstr "無法讀取推送升級清單,請稍後再試" ++ ++msgid "Priority Upgrade Package being updated" ++msgstr "正在更新分組配置" ++ ++msgid "Exceptions of Priority Upgrade." ++msgstr "優先升級異常" ++ ++msgid "Due to the presence of deleted packages." ++msgstr "由於存在刪除的套件" ++ ++msgid "The system update configuration file is read abnormally, please check if the system update configuration file format is correct." ++msgstr "讀取系統更新設定檔異常,請檢查系統更新配置檔格式是否正確。" ++ ++msgid "Installation progress: " ++msgstr "安裝進度: " ++ ++msgid "Installation successful, about to shut down" ++msgstr "安裝成功,即將關機" ++ ++msgid "Installation failed, about to shut down" ++msgstr "安裝失敗,即將關機" ++ ++msgid "groups JSON ConfigPkgs install failed" ++msgstr "無法安裝分組配置檔" ++ ++msgid "Installtion timeout to exit Due to inactivity" ++msgstr "安裝超時退出由於" ++ ++msgid "Command execution error" ++msgstr "命令執行報錯" ++ ++msgid "Unsupported architecture" ++msgstr "架構不符合" ++ ++msgid "Other Error" ++msgstr "其他錯誤" ++ ++msgid "dependency is not satisfied" ++msgstr "依賴關係不滿足" ++ ++msgid "dependency is not satisfied will download" ++msgstr "依賴關係不滿足" ++ ++msgid "Disk space is insufficient, please clean the disk and then upgrade" ++msgstr "磁碟空間不足,請清理磁碟後進行升級更新。" ++ ++msgid "Network anomaly, can't check for updates!" ++msgstr "網路異常,無法檢查更新!" ++ ++msgid "Check for update exceptions!" ++msgstr "檢查更新異常!" ++ ++msgid "Check for update exceptions,fix system APT environment error." ++msgstr "檢查更新異常,修復系統APT環境出現錯誤。" ++ ++msgid "The system APT environment is abnormal, please check the system APT environment." ++msgstr "修復系統APT環境異常,請檢查系統APT環境。" ++ ++msgid "Priority upgrade status exception." ++msgstr "優先升級狀態異常。" ++ ++msgid "Upgrade configuration acquisition exception." ++msgstr "升級配置獲取異常。" ++ ++msgid "Please check your network connection and retry." ++msgstr "請檢查您的網路連接后再試。" ++ ++msgid "Please check your source list and retry." ++msgstr "請檢查您的源清單後再試。" ++ ++msgid "Checking network connection" ++msgstr "檢查網路連接中" ++ ++msgid "Updating Source Template" ++msgstr "更新源範本中" ++ ++msgid "Update Manager upgrade is complete, please restart the setting panel before performing the system update." ++msgstr "更新管理器升級完成,請重啟設置-更新后再進行系統更新。" ++ ++msgid "Uninstallation completed" ++msgstr "卸載完成。" ++ ++msgid "Package validation failed and installation was rejected." ++msgstr "套件驗證失敗,拒絕安裝。" ++ ++msgid "Other tasks are being updated and upgraded, please uninstall them later." ++msgstr "其他任務正在更新升級中,請稍後再卸載。" ++ ++#: ../SystemUpdater/Core/enums.py:763 ++msgid "Kylin System Updater" ++msgstr "麒麟更新器" ++ ++#: ../SystemUpdater/Core/enums.py:609 ++msgid "Kylin Installer" ++msgstr "麒麟安裝器" ++ ++#: ../SystemUpdater/Core/enums.py:610 ++msgid "Kylin Uninstaller" ++msgstr "麒麟卸載器" ++ ++#: ../SystemUpdater/Core/enums.py:611 ++msgid "Kylin Background Upgrade" ++msgstr "靜默更新" ++ ++#: ../SystemUpdater/Core/enums.py:612 ++msgid "Kylin Software Center" ++msgstr "軟體商店" ++ ++#: ../SystemUpdater/UpdateManagerDbus.py:355 ++msgid " requires authentication to uninstall software packages." ++msgstr "卸載套件需要認證。" ++ ++#: ../SystemUpdater/UpdateManager.py:463 ++msgid " requires authentication to install software packages." ++msgstr "安裝套件需要認證。" ++ ++#: ../SystemUpdater/Core/utils.py:750 ++msgid "Authentication success." ++msgstr "認證成功。" ++ ++#: ../SystemUpdater/Core/utils.py:753 ++msgid "Authentication failure." ++msgstr "認證失敗。" ++ ++#: ../SystemUpdater/Core/enums.py:101 ++msgid "Deb format exception, read local deb file error." ++msgstr "軟體包格式異常,讀取失敗。" ++ ++#: ../SystemUpdater/Core/enums.py:102 ++msgid "Install deb error." ++msgstr "安裝套件失敗。" ++ ++msgid "Upgrade System" ++msgstr "全盤升級" ++ ++msgid "kylin-unattended-upgrade" ++msgstr "自動更新" ++ ++msgid "Please check the system time and synchronize the system time before updating." ++msgstr "請檢查系統時間,同步系統時間后再進行更新。" ++ ++msgid "The package is unsigned, refuses to install." ++msgstr "軟體包未簽名,拒絕安裝。" ++ ++msgid "Application installation control policy not enabled ." ++msgstr "應用安裝管控未開啓." ++ ++msgid "Installation failed! Application is not in the software whitelist list!" ++msgstr "安裝失敗!軟件包不在白名單列表!" ++ ++msgid "Installation failed! Application is in the software blacklist list!" ++msgstr "安裝失敗!軟件包在黑名單列表!" ++ ++msgid "Application installation in unknown mode ." ++msgstr "應用安裝管控策略未知." ++ ++#: ../SystemUpdater/Core/utils.py:753 ++msgid "Cancel authentication." ++msgstr "取消認證" +diff --git a/backend-immutable/tests/po/zh_TW.po b/backend-immutable/tests/po/zh_TW.po +new file mode 100644 +index 0000000..412d059 +--- /dev/null ++++ b/backend-immutable/tests/po/zh_TW.po +@@ -0,0 +1,2744 @@ ++msgid "" ++msgstr "" ++"Project-Id-Version: update-manager 0.41.1\n" ++"Report-Msgid-Bugs-To: sebastian.heinlein@web.de\n" ++"POT-Creation-Date: 2012-06-14 00:53+0100\n" ++"PO-Revision-Date: 2012-03-23 02:57+0000\n" ++"Last-Translator: Cheng-Chia Tseng <pswo10680@gmail.com>\n" ++"Language-Team: Chinese (Taiwan) <zh-l10n@linux.org.tw>\n" ++"Language: \n" ++"MIME-Version: 1.0\n" ++"Content-Type: text/plain; charset=UTF-8\n" ++"Content-Transfer-Encoding: 8bit\n" ++"Plural-Forms: nplurals=1; plural=0;\n" ++"X-Launchpad-Export-Date: 2012-04-17 13:41+0000\n" ++"X-Generator: Launchpad (build 15099)\n" ++ ++#. TRANSLATORS: download size of small updates, e.g. "250 kB" ++#: ../DistUpgrade/utils.py:433 ../UpdateManager/Core/utils.py:433 ++#, python-format ++msgid "%(size).0f kB" ++msgid_plural "%(size).0f kB" ++msgstr[0] "%(size).0f kB" ++ ++#. TRANSLATORS: download size of updates, e.g. "2.3 MB" ++#: ../DistUpgrade/utils.py:436 ../UpdateManager/Core/utils.py:436 ++#, python-format ++msgid "%.1f MB" ++msgstr "%.1f MB" ++ ++#. TRANSLATORS: %s is a country ++#: ../DistUpgrade/distro.py:206 ../DistUpgrade/distro.py:436 ++#, python-format ++msgid "Server for %s" ++msgstr "%s伺服器" ++ ++#. More than one server is used. Since we don't handle this case ++#. in the user interface we set "custom servers" to true and ++#. append a list of all used servers ++#: ../DistUpgrade/distro.py:224 ../DistUpgrade/distro.py:230 ++#: ../DistUpgrade/distro.py:246 ++msgid "Main server" ++msgstr "主伺服器" ++ ++#: ../DistUpgrade/distro.py:250 ++msgid "Custom servers" ++msgstr "自訂伺服器" ++ ++#: ../DistUpgrade/DistUpgradeAptCdrom.py:142 ++msgid "Could not calculate sources.list entry" ++msgstr "無法計算 sources.list 項目" ++ ++#: ../DistUpgrade/DistUpgradeAptCdrom.py:251 ++msgid "" ++"Unable to locate any package files, perhaps this is not a Ubuntu Disc or the " ++"wrong architecture?" ++msgstr "找不到套件檔,也許這不是 Ubuntu 光碟,或者處理器架構不對。" ++ ++#: ../DistUpgrade/DistUpgradeAptCdrom.py:294 ++msgid "Failed to add the CD" ++msgstr "無法加入光碟" ++ ++#: ../DistUpgrade/DistUpgradeAptCdrom.py:295 ++#, python-format ++msgid "" ++"There was a error adding the CD, the upgrade will abort. Please report this " ++"as a bug if this is a valid Ubuntu CD.\n" ++"\n" ++"The error message was:\n" ++"'%s'" ++msgstr "" ++"加入光碟時發生錯誤,升級將終止。若您確定光碟沒有問題,請將此匯報為臭蟲。\n" ++"\n" ++"錯誤訊息:\n" ++"「%s」" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:151 ++msgid "Remove package in bad state" ++msgid_plural "Remove packages in bad state" ++msgstr[0] "移除有問題套件" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:154 ++#, python-format ++msgid "" ++"The package '%s' is in an inconsistent state and needs to be reinstalled, " ++"but no archive can be found for it. Do you want to remove this package now " ++"to continue?" ++msgid_plural "" ++"The packages '%s' are in an inconsistent state and need to be reinstalled, " ++"but no archives can be found for them. Do you want to remove these packages " ++"now to continue?" ++msgstr[0] "" ++"「%s」套件狀態不一致而需要重新安裝,但找不到其存檔。是否移除此套件並繼續?" ++ ++#. FIXME: not ideal error message, but we just reuse a ++#. existing one here to avoid a new string ++#: ../DistUpgrade/DistUpgradeCache.py:255 ++msgid "The server may be overloaded" ++msgstr "伺服器可能負載過大" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:368 ++msgid "Broken packages" ++msgstr "損毀套件" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:369 ++msgid "" ++"Your system contains broken packages that couldn't be fixed with this " ++"software. Please fix them first using synaptic or apt-get before proceeding." ++msgstr "" ++"本軟體無法修正閣下系統某有損毀套件。請先使用 synaptic 或 apt-get 修正才繼續。" ++ ++#. FIXME: change the text to something more useful ++#: ../DistUpgrade/DistUpgradeCache.py:693 ++#, python-format ++msgid "" ++"An unresolvable problem occurred while calculating the upgrade:\n" ++"%s\n" ++"\n" ++" This can be caused by:\n" ++" * Upgrading to a pre-release version of Ubuntu\n" ++" * Running the current pre-release version of Ubuntu\n" ++" * Unofficial software packages not provided by Ubuntu\n" ++"\n" ++msgstr "" ++"準備升級時發生不能解決的問題:\n" ++"%s\n" ++"\n" ++" 這可能由以下原因造成:\n" ++" * 正升級至非正式發佈版本的 Ubuntu\n" ++" * 正使用非正式發佈版本的 Ubuntu\n" ++" * 安裝了非由 Ubuntu 官方提供的軟體套件\n" ++"\n" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:703 ++msgid "This is most likely a transient problem, please try again later." ++msgstr "很可能只是暫時有問題。請稍候再試。" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:706 ++msgid "" ++"If none of this applies, then please report this bug using the command " ++"'ubuntu-bug update-manager' in a terminal." ++msgstr "" ++"如全都不對,請在終端機內輸入指令「ubuntu-bug update-manager」回報錯誤。" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:711 ++#: ../UpdateManager/UpdateManager.py:1031 ++msgid "Could not calculate the upgrade" ++msgstr "無法計算升級" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:762 ++msgid "Error authenticating some packages" ++msgstr "認證一些套件時發生錯誤" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:763 ++msgid "" ++"It was not possible to authenticate some packages. This may be a transient " ++"network problem. You may want to try again later. See below for a list of " ++"unauthenticated packages." ++msgstr "" ++"無法驗證部份套件。可能是因為短時間的網路問題。您可以稍候再試一次。下面是未被" ++"驗證的套件清單。" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:783 ++#, python-format ++msgid "" ++"The package '%s' is marked for removal but it is in the removal blacklist." ++msgstr "套件「%s」標記作移除,但它在移除黑名單中。" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:787 ++#, python-format ++msgid "The essential package '%s' is marked for removal." ++msgstr "必要套件「%s」被標記作移除。" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:796 ++#, python-format ++msgid "Trying to install blacklisted version '%s'" ++msgstr "正在嘗試安裝黑名單版本「%s」" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:914 ++#, python-format ++msgid "Can't install '%s'" ++msgstr "無法安裝「%s」" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:915 ++msgid "" ++"It was impossible to install a required package. Please report this as a bug " ++"using 'ubuntu-bug update-manager' in a terminal." ++msgstr "" ++"無法安裝必要的套件。請在終端機內輸入「ubuntu-bug update-manager」回報錯誤。" ++ ++#. FIXME: provide a list ++#: ../DistUpgrade/DistUpgradeCache.py:926 ++msgid "Can't guess meta-package" ++msgstr "無法估計元套件 (meta-package)" ++ ++#: ../DistUpgrade/DistUpgradeCache.py:927 ++msgid "" ++"Your system does not contain a ubuntu-desktop, kubuntu-desktop, xubuntu-" ++"desktop or edubuntu-desktop package and it was not possible to detect which " ++"version of Ubuntu you are running.\n" ++" Please install one of the packages above first using synaptic or apt-get " ++"before proceeding." ++msgstr "" ++"您的系統沒有安裝 ubuntu-desktop,kubuntu-desktop 或 edubuntu-desktop 套件,因" ++"此無法偵測正在使用那個版本的 Ubuntu。\n" ++" 請先使用 synaptic 或 apt-get 安裝上述其中一個套件再繼續作業" ++ ++#: ../DistUpgrade/DistUpgradeController.py:114 ++msgid "Reading cache" ++msgstr "正在讀取快取" ++ ++#: ../DistUpgrade/DistUpgradeController.py:223 ++msgid "Unable to get exclusive lock" ++msgstr "無法取得(使用)排他鎖定" ++ ++#: ../DistUpgrade/DistUpgradeController.py:224 ++msgid "" ++"This usually means that another package management application (like apt-get " ++"or aptitude) already running. Please close that application first." ++msgstr "" ++"這通常表示有其他的套件管理員程式(如 apt-get 或 aptitude)正在執行。請先關閉" ++"這些應用程式。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:257 ++msgid "Upgrading over remote connection not supported" ++msgstr "不支援透過遠端連線升級" ++ ++#: ../DistUpgrade/DistUpgradeController.py:258 ++msgid "" ++"You are running the upgrade over a remote ssh connection with a frontend " ++"that does not support this. Please try a text mode upgrade with 'do-release-" ++"upgrade'.\n" ++"\n" ++"The upgrade will abort now. Please try without ssh." ++msgstr "" ++"您正在透過遠端 ssh 連線的前端介面執行更新,而此介面不支援。請試試純文字模式下" ++"以 'do-release-upgrade' 進行更新。\n" ++"\n" ++"目前更新將會中止。請透過非 ssh 連線重試。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:272 ++msgid "Continue running under SSH?" ++msgstr "繼續執行於 SSH 中?" ++ ++#: ../DistUpgrade/DistUpgradeController.py:273 ++#, python-format ++msgid "" ++"This session appears to be running under ssh. It is not recommended to " ++"perform a upgrade over ssh currently because in case of failure it is harder " ++"to recover.\n" ++"\n" ++"If you continue, an additional ssh daemon will be started at port '%s'.\n" ++"Do you want to continue?" ++msgstr "" ++"此連線階段似乎是在 ssh 下執行。目前不建議在 ssh 連線下進行升級,因為若發生失" ++"敗將會較難以修復。\n" ++"\n" ++"若您繼續,一個額外的 ssh 背景程序將會啟動在 '%s' 連接埠。\n" ++"您想要繼續嗎?" ++ ++#: ../DistUpgrade/DistUpgradeController.py:287 ++msgid "Starting additional sshd" ++msgstr "啟動後備 sshd 中" ++ ++#: ../DistUpgrade/DistUpgradeController.py:288 ++#, python-format ++msgid "" ++"To make recovery in case of failure easier, an additional sshd will be " ++"started on port '%s'. If anything goes wrong with the running ssh you can " ++"still connect to the additional one.\n" ++msgstr "" ++"為在失敗時更易進行修復,一個後備 sshd 將在‘%s’埠被啟動。如果使用中的 ssh 有任" ++"何問題,您仍可以連接後備的 sshd 。\n" ++ ++#: ../DistUpgrade/DistUpgradeController.py:296 ++#, python-format ++msgid "" ++"If you run a firewall, you may need to temporarily open this port. As this " ++"is potentially dangerous it's not done automatically. You can open the port " ++"with e.g.:\n" ++"'%s'" ++msgstr "" ++"若您有執行防火牆,您可能需要暫時開啟此連接埠。由於此動作存在潛在危險,系統不" ++"會自動執行。您可以這樣開啟連接埠:\n" ++"「%s」" ++ ++#: ../DistUpgrade/DistUpgradeController.py:368 ++#: ../DistUpgrade/DistUpgradeController.py:413 ++msgid "Can not upgrade" ++msgstr "無法升級" ++ ++#: ../DistUpgrade/DistUpgradeController.py:369 ++#, python-format ++msgid "An upgrade from '%s' to '%s' is not supported with this tool." ++msgstr "這個工具不支援從‘%s’到‘%s’的升級" ++ ++#: ../DistUpgrade/DistUpgradeController.py:378 ++msgid "Sandbox setup failed" ++msgstr "沙堆(Sandbox)架設失敗" ++ ++#: ../DistUpgrade/DistUpgradeController.py:379 ++msgid "It was not possible to create the sandbox environment." ++msgstr "不可能建立沙堆(sandbox)環境。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:385 ++msgid "Sandbox mode" ++msgstr "沙堆(Sandbox)模式" ++ ++#: ../DistUpgrade/DistUpgradeController.py:386 ++#, python-format ++msgid "" ++"This upgrade is running in sandbox (test) mode. All changes are written to " ++"'%s' and will be lost on the next reboot.\n" ++"\n" ++"*No* changes written to a system directory from now until the next reboot " ++"are permanent." ++msgstr "" ++"此升級程序正以沙箱 (測試) 模式執行。所有的變更會寫入「%s」,下次重新開機後會" ++"消失。\n" ++"\n" ++"從現在起直到下次重新開機前,任何寫入系統目錄的變更都「不會」被留存。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:414 ++msgid "" ++"Your python install is corrupted. Please fix the '/usr/bin/python' symlink." ++msgstr "您的 python 安裝已毀損。請修正 ‘/usr/bin/python’ 的符號連結。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:440 ++msgid "Package 'debsig-verify' is installed" ++msgstr "套件 'debsig-verify' 安裝完成。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:441 ++msgid "" ++"The upgrade can not continue with that package installed.\n" ++"Please remove it with synaptic or 'apt-get remove debsig-verify' first and " ++"run the upgrade again." ++msgstr "" ++"此已安裝的套件令升級無法繼續,請使用 synaptic 或命令 'apt-get remove debsig-" ++"verify' 移除它後再次進行升級。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:453 ++#, python-format ++msgid "Can not write to '%s'" ++msgstr "無法寫入「%s」" ++ ++#: ../DistUpgrade/DistUpgradeController.py:454 ++#, python-format ++msgid "" ++"Its not possible to write to the system directory '%s' on your system. The " ++"upgrade can not continue.\n" ++"Please make sure that the system directory is writable." ++msgstr "" ++"無法寫入您的系統目錄「%s」。升級程序無法繼續。\n" ++"請確認該系統目錄可以寫入。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:465 ++msgid "Include latest updates from the Internet?" ++msgstr "要否包括來自網際網路的最新更新?" ++ ++#: ../DistUpgrade/DistUpgradeController.py:466 ++msgid "" ++"The upgrade system can use the internet to automatically download the latest " ++"updates and install them during the upgrade. If you have a network " ++"connection this is highly recommended.\n" ++"\n" ++"The upgrade will take longer, but when it is complete, your system will be " ++"fully up to date. You can choose not to do this, but you should install the " ++"latest updates soon after upgrading.\n" ++"If you answer 'no' here, the network is not used at all." ++msgstr "" ++"升級系統可自動由網際網路下載最新更新並於升級時安裝。如您有網路連線,建議使用" ++"這方法。\n" ++"\n" ++"升級時間會較長,但完成後,您的系統就會完全在最新狀態。您可以選擇現在不進行這" ++"工作,但是在升級後,您應該要盡快安裝最新的更新。\n" ++"如果您在此回答「否」,將完全不會使用網路。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:686 ++#, python-format ++msgid "disabled on upgrade to %s" ++msgstr "因升級至 %s 停用" ++ ++#: ../DistUpgrade/DistUpgradeController.py:713 ++msgid "No valid mirror found" ++msgstr "找不到有效的鏡像站" ++ ++#: ../DistUpgrade/DistUpgradeController.py:714 ++#, python-format ++msgid "" ++"While scanning your repository information no mirror entry for the upgrade " ++"was found. This can happen if you run a internal mirror or if the mirror " ++"information is out of date.\n" ++"\n" ++"Do you want to rewrite your 'sources.list' file anyway? If you choose 'Yes' " ++"here it will update all '%s' to '%s' entries.\n" ++"If you select 'No' the upgrade will cancel." ++msgstr "" ++"掃描閣下套件庫時沒有發現升級映射項目資訊。如果您運行內部映射或者映射資訊已經" ++"過期,就會發生這種情況。\n" ++"\n" ++"是否無論如何都希望覆寫您的「sources.list」檔案?如選「是」則將會將所有「%s」" ++"更新成「%s」項目。\n" ++"如選「否」則會取消更新。" ++ ++#. hm, still nothing useful ... ++#: ../DistUpgrade/DistUpgradeController.py:734 ++msgid "Generate default sources?" ++msgstr "產生預設的來源?" ++ ++#: ../DistUpgrade/DistUpgradeController.py:735 ++#, python-format ++msgid "" ++"After scanning your 'sources.list' no valid entry for '%s' was found.\n" ++"\n" ++"Should default entries for '%s' be added? If you select 'No', the upgrade " ++"will cancel." ++msgstr "" ++"在掃描您的「sources.list」後,沒找到與「%s」有效的項目。\n" ++"\n" ++"要新增「%s」的預設項目嗎?如果您選擇「否」則會取消升級。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:770 ++msgid "Repository information invalid" ++msgstr "套件庫資料無效" ++ ++#: ../DistUpgrade/DistUpgradeController.py:771 ++msgid "" ++"Upgrading the repository information resulted in a invalid file so a bug " ++"reporting process is being started." ++msgstr "升級套件庫資訊導致檔案無效,因此正在啟動臭蟲回報程序" ++ ++#: ../DistUpgrade/DistUpgradeController.py:778 ++msgid "Third party sources disabled" ++msgstr "已停用第三方來源" ++ ++#: ../DistUpgrade/DistUpgradeController.py:779 ++msgid "" ++"Some third party entries in your sources.list were disabled. You can re-" ++"enable them after the upgrade with the 'software-properties' tool or your " ++"package manager." ++msgstr "" ++"sources.list 中的某些第三方項目已停用。以「軟體來源」工具或套件管理員升級後可" ++"將之重新啟用。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:819 ++msgid "Package in inconsistent state" ++msgid_plural "Packages in inconsistent state" ++msgstr[0] "套件在不一致狀態" ++ ++#: ../DistUpgrade/DistUpgradeController.py:822 ++#, python-format ++msgid "" ++"The package '%s' is in an inconsistent state and needs to be reinstalled, " ++"but no archive can be found for it. Please reinstall the package manually or " ++"remove it from the system." ++msgid_plural "" ++"The packages '%s' are in an inconsistent state and need to be reinstalled, " ++"but no archive can be found for them. Please reinstall the packages manually " ++"or remove them from the system." ++msgstr[0] "" ++"這個套件 '%s' 狀態不一致而需要重新安裝,但找不到它的套件。請手動地重新安裝套" ++"件或將它由系統中移除。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:870 ++msgid "Error during update" ++msgstr "更新時發生錯誤" ++ ++#: ../DistUpgrade/DistUpgradeController.py:871 ++msgid "" ++"A problem occurred during the update. This is usually some sort of network " ++"problem, please check your network connection and retry." ++msgstr "更新時發生錯誤。這可能是某些網路問題,試檢查網路連線後再試。" ++ ++#. print("on_button_install_clicked") ++#: ../DistUpgrade/DistUpgradeController.py:880 ++#: ../UpdateManager/UpdateManager.py:757 ++msgid "Not enough free disk space" ++msgstr "磁碟空間不足" ++ ++#: ../DistUpgrade/DistUpgradeController.py:881 ++#, python-format ++msgid "" ++"The upgrade has aborted. The upgrade needs a total of %s free space on disk " ++"'%s'. Please free at least an additional %s of disk space on '%s'. Empty " ++"your trash and remove temporary packages of former installations using 'sudo " ++"apt-get clean'." ++msgstr "" ++"已放棄升級。升級程序總共需要 %s 可用空間於磁碟「%s」。請釋放至少額外 %s 的磁" ++"碟空間於磁碟「%s」。清理您的回收筒,並使用 'sudo apt-get clean' 來移除之前安" ++"裝時的暫時性套件。" ++ ++#. calc the dist-upgrade and see if the removals are ok/expected ++#. do the dist-upgrade ++#: ../DistUpgrade/DistUpgradeController.py:910 ++#: ../DistUpgrade/DistUpgradeController.py:1692 ++msgid "Calculating the changes" ++msgstr "正在計算所有的更動" ++ ++#. ask the user ++#: ../DistUpgrade/DistUpgradeController.py:942 ++msgid "Do you want to start the upgrade?" ++msgstr "要開始升級嗎?" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1008 ++msgid "Upgrade canceled" ++msgstr "升級取消了" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1009 ++msgid "" ++"The upgrade will cancel now and the original system state will be restored. " ++"You can resume the upgrade at a later time." ++msgstr "現在將取消升級,並且還原至原始系統。您可以在之後繼續升級。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1015 ++#: ../DistUpgrade/DistUpgradeController.py:1149 ++msgid "Could not download the upgrades" ++msgstr "無法下載升級套件" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1016 ++msgid "" ++"The upgrade has aborted. Please check your Internet connection or " ++"installation media and try again. All files downloaded so far have been kept." ++msgstr "" ++"升級已中止。請檢查您的網際網路連線,或安裝媒體並重試。所有目前已下載的檔案都" ++"會被保留。" ++ ++#. FIXME: strings are not good, but we are in string freeze ++#. currently ++#: ../DistUpgrade/DistUpgradeController.py:1100 ++#: ../DistUpgrade/DistUpgradeController.py:1137 ++#: ../DistUpgrade/DistUpgradeController.py:1242 ++msgid "Error during commit" ++msgstr "提交時發生錯誤" ++ ++#. generate a new cache ++#: ../DistUpgrade/DistUpgradeController.py:1102 ++#: ../DistUpgrade/DistUpgradeController.py:1139 ++#: ../DistUpgrade/DistUpgradeController.py:1281 ++msgid "Restoring original system state" ++msgstr "回復原有系統狀態" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1103 ++#: ../DistUpgrade/DistUpgradeController.py:1118 ++#: ../DistUpgrade/DistUpgradeController.py:1140 ++msgid "Could not install the upgrades" ++msgstr "無法安裝升級" ++ ++#. invoke the frontend now and show a error message ++#: ../DistUpgrade/DistUpgradeController.py:1108 ++msgid "" ++"The upgrade has aborted. Your system could be in an unusable state. A " ++"recovery will run now (dpkg --configure -a)." ++msgstr "" ++"已放棄升級。您的系統可能處於不穩定狀態。現在將執行復原程序 (dpkg --configure " ++"-a)。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1113 ++#, python-format ++msgid "" ++"\n" ++"\n" ++"Please report this bug in a browser at http://bugs.launchpad.net/ubuntu/" ++"+source/update-manager/+filebug and attach the files in /var/log/dist-" ++"upgrade/ to the bug report.\n" ++"%s" ++msgstr "" ++"\n" ++"\n" ++"請透過瀏覽器回報此臭蟲於 http://bugs.launchpad.net/ubuntu/+source/update-" ++"manager/+filebug ,並請附加 /var/log/dist-upgrade/ 內的檔案至臭蟲回報中。\n" ++"%s" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1150 ++msgid "" ++"The upgrade has aborted. Please check your Internet connection or " ++"installation media and try again. " ++msgstr "已放棄升級。請檢查您的網際網路連線或安裝媒體,接著再試一次。 " ++ ++#: ../DistUpgrade/DistUpgradeController.py:1230 ++msgid "Remove obsolete packages?" ++msgstr "移除廢棄的套件?" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1231 ++#: ../DistUpgrade/DistUpgrade.ui.h:8 ++msgid "_Keep" ++msgstr "保留(_K)" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1231 ++msgid "_Remove" ++msgstr "移除(_R)" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1243 ++msgid "" ++"A problem occurred during the clean-up. Please see the below message for " ++"more information. " ++msgstr "在清理時發生一些問題。詳情請參閱以下訊息。 " ++ ++#. FIXME: instead of error out, fetch and install it ++#. here ++#: ../DistUpgrade/DistUpgradeController.py:1319 ++msgid "Required depends is not installed" ++msgstr "必需的相依套件未被安裝" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1320 ++#, python-format ++msgid "The required dependency '%s' is not installed. " ++msgstr "必需的相依套件‘%s’未被安裝。 " ++ ++#. sanity check (check for ubuntu-desktop, brokenCache etc) ++#. then open the cache (again) ++#: ../DistUpgrade/DistUpgradeController.py:1588 ++#: ../DistUpgrade/DistUpgradeController.py:1653 ++msgid "Checking package manager" ++msgstr "正在檢查套件管理員" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1593 ++msgid "Preparing the upgrade failed" ++msgstr "準備升級失敗" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1594 ++msgid "" ++"Preparing the system for the upgrade failed so a bug reporting process is " ++"being started." ++msgstr "準備系統以供升級的動作失敗,所以正在啟動臭蟲回報程序。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1608 ++msgid "Getting upgrade prerequisites failed" ++msgstr "取得升級先決元件失敗" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1609 ++msgid "" ++"The system was unable to get the prerequisites for the upgrade. The upgrade " ++"will abort now and restore the original system state.\n" ++"\n" ++"Additionally, a bug reporting process is being started." ++msgstr "" ++"本系統無法達到升級的先決條件。升級將立刻中止並且還原至原始系統狀態。\n" ++"\n" ++"此外,臭蟲回報程序正在啟動中。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1637 ++msgid "Updating repository information" ++msgstr "正在更新套件庫資訊" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1644 ++msgid "Failed to add the cdrom" ++msgstr "無法加入光碟" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1645 ++msgid "Sorry, adding the cdrom was not successful." ++msgstr "很抱歉,沒有成功加入光碟。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1673 ++msgid "Invalid package information" ++msgstr "無效的套件資訊" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1674 ++msgid "After updating your package " ++msgstr "" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1698 ++#: ../DistUpgrade/DistUpgradeController.py:1750 ++msgid "Fetching" ++msgstr "接收中" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1704 ++#: ../DistUpgrade/DistUpgradeController.py:1754 ++msgid "Upgrading" ++msgstr "升級中" ++ ++#. don't abort here, because it would restore the sources.list ++#: ../DistUpgrade/DistUpgradeController.py:1709 ++#: ../DistUpgrade/DistUpgradeController.py:1756 ++#: ../DistUpgrade/DistUpgradeController.py:1763 ++#: ../DistUpgrade/DistUpgradeController.py:1774 ++msgid "Upgrade complete" ++msgstr "升級完成" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1710 ++#: ../DistUpgrade/DistUpgradeController.py:1757 ++#: ../DistUpgrade/DistUpgradeController.py:1764 ++msgid "" ++"The upgrade has completed but there were errors during the upgrade process." ++msgstr "升級已經完成,但在升級過程中有發生錯誤。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1717 ++msgid "Searching for obsolete software" ++msgstr "搜尋廢棄的軟體中" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1726 ++msgid "System upgrade is complete." ++msgstr "系統升級完成。" ++ ++#: ../DistUpgrade/DistUpgradeController.py:1775 ++msgid "The partial upgrade was completed." ++msgstr "部份升級完成。" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:204 ++msgid "evms in use" ++msgstr "有軟體正使用 evms" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:205 ++msgid "" ++"Your system uses the 'evms' volume manager in /proc/mounts. The 'evms' " ++"software is no longer supported, please switch it off and run the upgrade " ++"again when this is done." ++msgstr "" ++"您的系統於 /proc/mounts 使用 'evms' volume 管理程式。'evms' 軟體已不受支援," ++"請將其關閉再執行升級工作。" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:502 ++msgid "Your graphics hardware may not be fully supported in Ubuntu 12.04 LTS." ++msgstr "您的繪圖硬體可能無法被 Ubuntu 12.04 LTS 完整支援。" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:504 ++msgid "" ++"The support in Ubuntu 12.04 LTS for your Intel graphics hardware is limited " ++"and you may encounter problems after the upgrade. For more information see " ++"https://wiki.ubuntu.com/X/Bugs/UpdateManagerWarningForI8xx Do you want to " ++"continue with the upgrade?" ++msgstr "" ++"Ubuntu 12.04 LTS 對您的 Intel 繪圖硬體的支援有限,升級之後您可能會碰到一些問" ++"題。更多資訊可以查看 \"https://wiki.ubuntu.com/X/Bugs/" ++"UpdateManagerWarningForI8xx\"。您是否要繼續升級?" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:526 ++#: ../DistUpgrade/DistUpgradeQuirks.py:554 ++#: ../DistUpgrade/DistUpgradeQuirks.py:581 ++msgid "" ++"Upgrading may reduce desktop effects, and performance in games and other " ++"graphically intensive programs." ++msgstr "升級可能減低桌面特效和遊戲及其他著重圖形程式的表現。" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:530 ++#: ../DistUpgrade/DistUpgradeQuirks.py:558 ++msgid "" ++"This computer is currently using the NVIDIA 'nvidia' graphics driver. No " ++"version of this driver is available that works with your video card in " ++"Ubuntu 10.04 LTS.\n" ++"\n" ++"Do you want to continue?" ++msgstr "" ++"這個電腦目前正使用 NVIDIA 的「nvidia」圖形驅動程式。這個驅動程式沒有任何版本" ++"可在 Ubuntu 10.04 LTS 中正常驅動您的硬體。\n" ++"\n" ++"是否要繼續?" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:585 ++msgid "" ++"This computer is currently using the AMD 'fglrx' graphics driver. No version " ++"of this driver is available that works with your hardware in Ubuntu 10.04 " ++"LTS.\n" ++"\n" ++"Do you want to continue?" ++msgstr "" ++"這個電腦目前正使用 AMD 的「fglrx」圖形驅動程式。這個驅動程式沒有任何版本可在 " ++"Ubuntu 10.04 LTS 中正常驅動您的硬體。\n" ++"\n" ++"是否要繼續?" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:615 ++msgid "No i686 CPU" ++msgstr "無 i686 CPU" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:616 ++msgid "" ++"Your system uses an i586 CPU or a CPU that does not have the 'cmov' " ++"extension. All packages were built with optimizations requiring i686 as the " ++"minimal architecture. It is not possible to upgrade your system to a new " ++"Ubuntu release with this hardware." ++msgstr "" ++"您的系統使用 i586 CPU,或是不支援 'cmov' 擴充功能的 CPU。所有套件都以 i686 架" ++"構為最佳化目標來建置,所以 CPU 至少需要有 i686 等級。對於您目前的硬體來說,您" ++"無法將系統升級到最新的 Ubuntu 發行。" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:652 ++msgid "No ARMv6 CPU" ++msgstr "無 ARMv6 處理器" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:653 ++msgid "" ++"Your system uses an ARM CPU that is older than the ARMv6 architecture. All " ++"packages in karmic were built with optimizations requiring ARMv6 as the " ++"minimal architecture. It is not possible to upgrade your system to a new " ++"Ubuntu release with this hardware." ++msgstr "" ++"您系統使用的 ARM 處理器舊於 ARMv6 架構。所有 karmic 套件都是以 ARMv6 為最低架" ++"構優化建造的。所以伴隨此硬體無法升級您的系統到新的 Ubuntu 版本。" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:673 ++msgid "No init available" ++msgstr "無法初始化" ++ ++#: ../DistUpgrade/DistUpgradeQuirks.py:674 ++msgid "" ++"Your system appears to be a virtualised environment without an init daemon, " ++"e.g. Linux-VServer. Ubuntu 10.04 LTS cannot function within this type of " ++"environment, requiring an update to your virtual machine configuration " ++"first.\n" ++"\n" ++"Are you sure you want to continue?" ++msgstr "" ++"您的系統似乎處於虛擬化環境且無初始化程序,例如 Linux-VServer。Ubuntu 10.04 " ++"LTS 無法於此環境執行,需要先更新您的虛擬機器設定。\n" ++"\n" ++"您確定想要繼續?" ++ ++#: ../DistUpgrade/DistUpgradeMain.py:65 ++msgid "Sandbox upgrade using aufs" ++msgstr "使用 aufs 作為沙堆升級" ++ ++#: ../DistUpgrade/DistUpgradeMain.py:67 ++msgid "Use the given path to search for a cdrom with upgradable packages" ++msgstr "使用指定路徑搜尋附有升級套件的光碟" ++ ++#: ../DistUpgrade/DistUpgradeMain.py:73 ++msgid "" ++"Use frontend. Currently available: \n" ++"DistUpgradeViewText, DistUpgradeViewGtk, DistUpgradeViewKDE" ++msgstr "" ++"使用前端介面。可供選擇的有: \n" ++"DistUpgradeViewText (純文字), DistUpgradeViewGtk (GTK+), DistUpgradeViewKDE " ++"(KDE)" ++ ++#: ../DistUpgrade/DistUpgradeMain.py:76 ++msgid "*DEPRECATED* this option will be ignored" ++msgstr "*已棄用* 這個選項會被忽略" ++ ++#: ../DistUpgrade/DistUpgradeMain.py:79 ++msgid "Perform a partial upgrade only (no sources.list rewriting)" ++msgstr "只進行部份升級 (無須修改 sources.list)" ++ ++#: ../DistUpgrade/DistUpgradeMain.py:82 ++msgid "Disable GNU screen support" ++msgstr "停用 GNU 螢幕支援" ++ ++#: ../DistUpgrade/DistUpgradeMain.py:84 ++msgid "Set datadir" ++msgstr "設定資料目錄" ++ ++#. print("mediaChange %s %s" % (medium, drive)) ++#: ../DistUpgrade/DistUpgradeViewGtk.py:114 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:117 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:195 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:155 ++#, python-format ++msgid "Please insert '%s' into the drive '%s'" ++msgstr "請將‘%s’放入光碟機‘%s’" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:135 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:138 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:209 ++msgid "Fetching is complete" ++msgstr "接收完成" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:146 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:149 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:222 ++#, python-format ++msgid "Fetching file %li of %li at %sB/s" ++msgstr "接收第 %li 個檔案 (共 %li 個檔案,速度為 %sB/s)" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:149 ++#: ../DistUpgrade/DistUpgradeViewGtk.py:296 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:152 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:309 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:223 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:371 ++#, python-format ++msgid "About %s remaining" ++msgstr "大約剩下 %s" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:152 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:155 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:225 ++#, python-format ++msgid "Fetching file %li of %li" ++msgstr "接收第 %li 個檔案 (共 %li 個)" ++ ++#. FIXME: add support for the timeout ++#. of the terminal (to display something useful then) ++#. -> longer term, move this code into python-apt ++#: ../DistUpgrade/DistUpgradeViewGtk.py:183 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:186 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:262 ++msgid "Applying changes" ++msgstr "正在套用變更" ++ ++#. we do not report followup errors from earlier failures ++#: ../DistUpgrade/DistUpgradeViewGtk.py:208 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:212 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:275 ++msgid "dependency problems - leaving unconfigured" ++msgstr "相依問題 - 保留為未設定" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:213 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:217 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:277 ++#, python-format ++msgid "Could not install '%s'" ++msgstr "無法安裝‘%s’" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:214 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:218 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:278 ++#, python-format ++msgid "" ++"The upgrade will continue but the '%s' package may not be in a working " ++"state. Please consider submitting a bug report about it." ++msgstr "" ++"此次更新將繼續但是 '%s' 套件可能無法運作。請考慮提交關於該套件的臭蟲報告。" ++ ++#. self.expander.set_expanded(True) ++#: ../DistUpgrade/DistUpgradeViewGtk.py:231 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:235 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:299 ++#, python-format ++msgid "" ++"Replace the customized configuration file\n" ++"'%s'?" ++msgstr "" ++"是否要覆蓋自訂的設定檔案\n" ++"‘%s’?" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:232 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:236 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:300 ++msgid "" ++"You will lose any changes you have made to this configuration file if you " ++"choose to replace it with a newer version." ++msgstr "如果選擇以新版覆蓋,那麼將會失去您改變過的設定。" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:251 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:256 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:323 ++msgid "The 'diff' command was not found" ++msgstr "找不到‘diff’指令" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:464 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:477 ++#: ../DistUpgrade/DistUpgradeViewText.py:92 ++msgid "A fatal error occurred" ++msgstr "發生嚴重錯誤" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:465 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:478 ++msgid "" ++"Please report this as a bug (if you haven't already) and include the files /" ++"var/log/dist-upgrade/main.log and /var/log/dist-upgrade/apt.log in your " ++"report. The upgrade has aborted.\n" ++"Your original sources.list was saved in /etc/apt/sources.list.distUpgrade." ++msgstr "" ++"請回報此錯誤 (若您尚未回報) 並將檔案 /var/log/dist-upgrade/main.log 與 /var/" ++"log/dist-upgrade/apt.log 附在您的報告中。升級程序已取消。\n" ++"您原先的 sources.list 儲存於 /etc/apt/sources.list.distUpgrade。" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:482 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:495 ++msgid "Ctrl-c pressed" ++msgstr "按下 Ctrl+c" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:483 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:496 ++msgid "" ++"This will abort the operation and may leave the system in a broken state. " ++"Are you sure you want to do that?" ++msgstr "操作將被中止。這可能會造成系統的不完整。你確定要進行嗎?" ++ ++#. append warning ++#: ../DistUpgrade/DistUpgradeViewGtk.py:631 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:629 ++msgid "To prevent data loss close all open applications and documents." ++msgstr "為避免遺失資料,請關閉所有開啟的程式及文件。" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:645 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:643 ++#, python-format ++msgid "No longer supported by Canonical (%s)" ++msgstr "不再受 Canonical 支援 (%s)" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:646 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:644 ++#, python-format ++msgid "<b>Downgrade (%s)</b>" ++msgstr "<b>降級 (%s)</b>" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:647 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:645 ++#, python-format ++msgid "Remove (%s)" ++msgstr "移除 (%s)" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:648 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:646 ++#, python-format ++msgid "No longer needed (%s)" ++msgstr "不再需要 (%s)" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:649 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:647 ++#, python-format ++msgid "Install (%s)" ++msgstr "安裝 (%s)" ++ ++#: ../DistUpgrade/DistUpgradeViewGtk.py:650 ++#: ../DistUpgrade/DistUpgradeViewGtk3.py:648 ++#, python-format ++msgid "Upgrade (%s)" ++msgstr "升級 (%s)" ++ ++#. change = QMessageBox.question(None, _("Media Change"), msg, QMessageBox.Ok, QMessageBox.Cancel) ++#: ../DistUpgrade/DistUpgradeViewKDE.py:196 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:157 ++msgid "Media Change" ++msgstr "媒體變更" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:335 ++msgid "Show Difference >>>" ++msgstr "顯示差異 >>>" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:338 ++msgid "<<< Hide Difference" ++msgstr "<<< 隱藏差異" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:554 ++msgid "Error" ++msgstr "錯誤" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:568 ++msgid "&Cancel" ++msgstr "取消(&C)" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:572 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:813 ++msgid "&Close" ++msgstr "關閉(&C)" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:618 ++msgid "Show Terminal >>>" ++msgstr "顯示終端畫面 >>>" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:621 ++msgid "<<< Hide Terminal" ++msgstr "<<< 隱藏終端畫面" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:701 ++msgid "Information" ++msgstr "資訊" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:751 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:796 ++#: ../DistUpgrade/DistUpgradeViewKDE.py:799 ../DistUpgrade/DistUpgrade.ui.h:7 ++msgid "Details" ++msgstr "詳情" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:777 ++#, python-format ++msgid "No longer supported %s" ++msgstr "不再支援 %s" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:779 ++#, python-format ++msgid "Remove %s" ++msgstr "移除 %s" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:781 ++#: ../DistUpgrade/DistUpgradeViewText.py:182 ++#, python-format ++msgid "Remove (was auto installed) %s" ++msgstr "移除 (曾是自動安裝的) %s" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:783 ++#, python-format ++msgid "Install %s" ++msgstr "安裝 %s" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:785 ++#, python-format ++msgid "Upgrade %s" ++msgstr "升級 %s" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:809 ++#: ../DistUpgrade/DistUpgradeViewText.py:230 ++msgid "Restart required" ++msgstr "需要重新開機" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:809 ++msgid "<b><big>Restart the system to complete the upgrade</big></b>" ++msgstr "<b><big>重新啟動系統以完成更新</big></b>" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:812 ../DistUpgrade/DistUpgrade.ui.h:14 ++#: ../data/gtkbuilder/UpdateManager.ui.h:26 ++msgid "_Restart Now" ++msgstr "現在重新啟動(_R)" ++ ++#. FIXME make this user friendly ++#: ../DistUpgrade/DistUpgradeViewKDE.py:830 ++msgid "" ++"<b><big>Cancel the running upgrade?</big></b>\n" ++"\n" ++"The system could be in an unusable state if you cancel the upgrade. You are " ++"strongly advised to resume the upgrade." ++msgstr "" ++"<b><big>是否取消正進行的升級?</big></b>\n" ++"\n" ++"如果您取消升級,系統可能會在不穩定的狀態。強烈建議您繼續升級工作。" ++ ++#: ../DistUpgrade/DistUpgradeViewKDE.py:834 ++msgid "Cancel Upgrade?" ++msgstr "要取消升級嗎?" ++ ++#: ../DistUpgrade/DistUpgradeView.py:61 ++#, python-format ++msgid "%li day" ++msgid_plural "%li days" ++msgstr[0] "%li 日" ++ ++#: ../DistUpgrade/DistUpgradeView.py:63 ++#, python-format ++msgid "%li hour" ++msgid_plural "%li hours" ++msgstr[0] "%li 小時" ++ ++#: ../DistUpgrade/DistUpgradeView.py:65 ++#, python-format ++msgid "%li minute" ++msgid_plural "%li minutes" ++msgstr[0] "%li 分鐘" ++ ++#: ../DistUpgrade/DistUpgradeView.py:66 ++#, python-format ++msgid "%li second" ++msgid_plural "%li seconds" ++msgstr[0] "%li 秒" ++ ++#. TRANSLATORS: you can alter the ordering of the remaining time ++#. information here if you shuffle %(str_days)s %(str_hours)s %(str_minutes)s ++#. around. Make sure to keep all '$(str_*)s' in the translated string ++#. and do NOT change anything appart from the ordering. ++#. ++#. %(str_hours)s will be either "1 hour" or "2 hours" depending on the ++#. plural form ++#. ++#. Note: most western languages will not need to change this ++#: ../DistUpgrade/DistUpgradeView.py:82 ++#, python-format ++msgid "%(str_days)s %(str_hours)s" ++msgstr "%(str_days)s %(str_hours)s" ++ ++#. TRANSLATORS: you can alter the ordering of the remaining time ++#. information here if you shuffle %(str_hours)s %(str_minutes)s ++#. around. Make sure to keep all '$(str_*)s' in the translated string ++#. and do NOT change anything appart from the ordering. ++#. ++#. %(str_hours)s will be either "1 hour" or "2 hours" depending on the ++#. plural form ++#. ++#. Note: most western languages will not need to change this ++#: ../DistUpgrade/DistUpgradeView.py:100 ++#, python-format ++msgid "%(str_hours)s %(str_minutes)s" ++msgstr "%(str_hours)s又 %(str_minutes)s" ++ ++#. 56 kbit ++#. 1Mbit = 1024 kbit ++#: ../DistUpgrade/DistUpgradeView.py:151 ++#, python-format ++msgid "" ++"This download will take about %s with a 1Mbit DSL connection and about %s " ++"with a 56k modem." ++msgstr "這次下載所需時間在 1M DSL 連線大約要 %s,用 56k 數據機大約要 %s。" ++ ++#. if we have a estimated speed, use it ++#: ../DistUpgrade/DistUpgradeView.py:155 ++#, python-format ++msgid "This download will take about %s with your connection. " ++msgstr "依照您的連線速率,此下載將要約 %s 的時間。 " ++ ++#. Declare these translatable strings from the .ui files here so that ++#. xgettext picks them up. ++#: ../DistUpgrade/DistUpgradeView.py:259 ../DistUpgrade/DistUpgrade.ui.h:21 ++msgid "Preparing to upgrade" ++msgstr "準備升級中" ++ ++#: ../DistUpgrade/DistUpgradeView.py:260 ++msgid "Getting new software channels" ++msgstr "正取得新軟體頻道" ++ ++#: ../DistUpgrade/DistUpgradeView.py:261 ../DistUpgrade/DistUpgrade.ui.h:23 ++msgid "Getting new packages" ++msgstr "取得新套件" ++ ++#: ../DistUpgrade/DistUpgradeView.py:262 ../DistUpgrade/DistUpgrade.ui.h:26 ++msgid "Installing the upgrades" ++msgstr "安裝升級中" ++ ++#: ../DistUpgrade/DistUpgradeView.py:263 ../DistUpgrade/DistUpgrade.ui.h:25 ++msgid "Cleaning up" ++msgstr "清理中" ++ ++#: ../DistUpgrade/DistUpgradeView.py:348 ++#, python-format ++msgid "" ++"%(amount)d installed package is no longer supported by Canonical. You can " ++"still get support from the community." ++msgid_plural "" ++"%(amount)d installed packages are no longer supported by Canonical. You can " ++"still get support from the community." ++msgstr[0] "" ++"%(amount)d 個已安裝套件不再受 Canonical 支援。您仍可以取得來自社群的支援。" ++ ++#. FIXME: make those two separate lines to make it clear ++#. that the "%" applies to the result of ngettext ++#: ../DistUpgrade/DistUpgradeView.py:357 ++#, python-format ++msgid "%d package is going to be removed." ++msgid_plural "%d packages are going to be removed." ++msgstr[0] "即將移除 %d 個套件。" ++ ++#: ../DistUpgrade/DistUpgradeView.py:362 ++#, python-format ++msgid "%d new package is going to be installed." ++msgid_plural "%d new packages are going to be installed." ++msgstr[0] "即將安裝 %d 個新套件。" ++ ++#: ../DistUpgrade/DistUpgradeView.py:368 ++#, python-format ++msgid "%d package is going to be upgraded." ++msgid_plural "%d packages are going to be upgraded." ++msgstr[0] "即將升級 %d 個套件。" ++ ++#: ../DistUpgrade/DistUpgradeView.py:373 ++#, python-format ++msgid "" ++"\n" ++"\n" ++"You have to download a total of %s. " ++msgstr "" ++"\n" ++"\n" ++"要下載共%s。 " ++ ++#: ../DistUpgrade/DistUpgradeView.py:378 ++msgid "" ++"Installing the upgrade can take several hours. Once the download has " ++"finished, the process cannot be canceled." ++msgstr "安裝升級可能會花上幾個鐘頭。一旦下載完成,程序便無法取消。" ++ ++#: ../DistUpgrade/DistUpgradeView.py:382 ++msgid "" ++"Fetching and installing the upgrade can take several hours. Once the " ++"download has finished, the process cannot be canceled." ++msgstr "升級的擷取與安裝可能花上數個鐘頭。一旦下載完成,升級程序無法取消。" ++ ++#: ../DistUpgrade/DistUpgradeView.py:387 ++msgid "Removing the packages can take several hours. " ++msgstr "移除套件可能會花上幾個鐘頭。 " ++ ++#. FIXME: this should go into DistUpgradeController ++#: ../DistUpgrade/DistUpgradeView.py:392 ../UpdateManager/UpdateManager.py:676 ++msgid "The software on this computer is up to date." ++msgstr "這臺電腦上的軟體已處最新狀態。" ++ ++#: ../DistUpgrade/DistUpgradeView.py:393 ++msgid "" ++"There are no upgrades available for your system. The upgrade will now be " ++"canceled." ++msgstr "您的系統已在最新狀態。現在將取消升級動作。" ++ ++#: ../DistUpgrade/DistUpgradeView.py:406 ++msgid "Reboot required" ++msgstr "需要重新開機" ++ ++#: ../DistUpgrade/DistUpgradeView.py:407 ++msgid "" ++"The upgrade is finished and a reboot is required. Do you want to do this now?" ++msgstr "升級已經完成並需要重新開機。是否現在重新開機?" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:72 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:72 ++#, python-format ++msgid "authenticate '%(file)s' against '%(signature)s' " ++msgstr "以「%(signature)s」驗證「%(file)s」 " ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:131 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:131 ++#, python-format ++msgid "extracting '%s'" ++msgstr "正在抽出「%s」" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:151 ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:152 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:151 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:152 ++msgid "Could not run the upgrade tool" ++msgstr "無法執行升級工具" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:152 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:152 ++msgid "" ++"This is most likely a bug in the upgrade tool. Please report it as a bug " ++"using the command 'ubuntu-bug update-manager'." ++msgstr "" ++"這可能是升級工具的錯誤,請使用指令「ubuntu-bug update-manager」回報錯誤。" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:227 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:227 ++msgid "Upgrade tool signature" ++msgstr "升級工具簽署" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:234 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:234 ++msgid "Upgrade tool" ++msgstr "升級工具" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:268 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:268 ++msgid "Failed to fetch" ++msgstr "接收失敗" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:269 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:269 ++msgid "Fetching the upgrade failed. There may be a network problem. " ++msgstr "接收升級套件失敗。可能是網路問題。 " ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:273 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:273 ++msgid "Authentication failed" ++msgstr "認證失敗" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:274 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:274 ++msgid "" ++"Authenticating the upgrade failed. There may be a problem with the network " ++"or with the server. " ++msgstr "認證升級套件失敗。可能是因為跟伺服器的網路連線出現問題。 " ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:279 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:279 ++msgid "Failed to extract" ++msgstr "解壓失敗" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:280 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:280 ++msgid "" ++"Extracting the upgrade failed. There may be a problem with the network or " ++"with the server. " ++msgstr "升級套件解壓失敗。可能是因為網路或伺服器出現問題。 " ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:285 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:285 ++msgid "Verification failed" ++msgstr "檢驗失敗" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:286 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:286 ++msgid "" ++"Verifying the upgrade failed. There may be a problem with the network or " ++"with the server. " ++msgstr "檢驗升級套件失敗。可能是因為網路或伺服器出現問題。 " ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:300 ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:306 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:300 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:306 ++msgid "Can not run the upgrade" ++msgstr "不能進行升級" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:301 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:301 ++msgid "" ++"This usually is caused by a system where /tmp is mounted noexec. Please " ++"remount without noexec and run the upgrade again." ++msgstr "" ++"這通常是由使用 noexec 掛載 /tmp 的系統引致的。請不要使用 noexec 重新掛載,並" ++"再次進行升級。" ++ ++#: ../DistUpgrade/DistUpgradeFetcherCore.py:307 ++#: ../UpdateManager/Core/DistUpgradeFetcherCore.py:307 ++#, python-format ++msgid "The error message is '%s'." ++msgstr "錯誤訊息 '%s'。" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:93 ++msgid "" ++"Please report this as a bug and include the files /var/log/dist-upgrade/main." ++"log and /var/log/dist-upgrade/apt.log in your report. The upgrade has " ++"aborted.\n" ++"Your original sources.list was saved in /etc/apt/sources.list.distUpgrade." ++msgstr "" ++"請回報此錯誤並將檔案 /var/log/dist-upgrade/main.log 與 /var/log/dist-upgrade/" ++"apt.log 附在您的報告中。升級程序已取消。\n" ++"您原先的 sources.list 儲存於 /etc/apt/sources.list.distUpgrade。" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:117 ++msgid "Aborting" ++msgstr "正在中止" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:122 ++msgid "Demoted:\n" ++msgstr "降級:\n" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:129 ++msgid "To continue please press [ENTER]" ++msgstr "若要繼續請按 [ENTER]" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:157 ++#: ../DistUpgrade/DistUpgradeViewText.py:196 ++#: ../DistUpgrade/DistUpgradeViewText.py:203 ++msgid "Continue [yN] " ++msgstr "繼續 [yN] " ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:157 ++#: ../DistUpgrade/DistUpgradeViewText.py:196 ++msgid "Details [d]" ++msgstr "詳情 [d]" ++ ++#. TRANSLATORS: the "y" is "yes" ++#. TRANSLATORS: first letter of a positive (yes) answer ++#: ../DistUpgrade/DistUpgradeViewText.py:162 ++#: ../DistUpgrade/DistUpgradeViewText.py:206 ++msgid "y" ++msgstr "y" ++ ++#. TRANSLATORS: the "n" is "no" ++#. TRANSLATORS: first letter of a negative (no) answer ++#: ../DistUpgrade/DistUpgradeViewText.py:165 ++#: ../DistUpgrade/DistUpgradeViewText.py:213 ++msgid "n" ++msgstr "n" ++ ++#. TRANSLATORS: the "d" is "details" ++#: ../DistUpgrade/DistUpgradeViewText.py:168 ++msgid "d" ++msgstr "d" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:173 ++#, python-format ++msgid "No longer supported: %s\n" ++msgstr "不再支援:%s\n" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:178 ++#, python-format ++msgid "Remove: %s\n" ++msgstr "移除: %s\n" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:188 ++#, python-format ++msgid "Install: %s\n" ++msgstr "安裝: %s\n" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:193 ++#, python-format ++msgid "Upgrade: %s\n" ++msgstr "升級: %s\n" ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:210 ++msgid "Continue [Yn] " ++msgstr "繼續 [Yn] " ++ ++#: ../DistUpgrade/DistUpgradeViewText.py:231 ++msgid "" ++"To finish the upgrade, a restart is required.\n" ++"If you select 'y' the system will be restarted." ++msgstr "" ++"需要重新開機才能完成升級。\n" ++"如果您選擇「y」系統將會重新開機。" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:1 ++msgid "_Cancel Upgrade" ++msgstr "取消升級(_C)" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:2 ++msgid "_Resume Upgrade" ++msgstr "繼續升級(_R)" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:3 ++msgid "" ++"<b><big>Cancel the running upgrade?</big></b>\n" ++"\n" ++"The system could be in an unusable state if you cancel the upgrade. You are " ++"strongly adviced to resume the upgrade." ++msgstr "" ++"<b><big>是否取消正執行的升級?</big></b>\n" ++"\n" ++"如果取消升級可能會導致系統不穩定。強烈建議您繼續升級。" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:6 ++msgid "_Start Upgrade" ++msgstr "開始升級(_S)" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:9 ++msgid "_Replace" ++msgstr "取代(_R)" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:10 ++msgid "Difference between the files" ++msgstr "檔案間的差別" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:11 ++msgid "_Report Bug" ++msgstr "回報錯誤(_R)" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:12 ++msgid "_Continue" ++msgstr "繼續(_C)" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:13 ++msgid "<b><big>Start the upgrade?</big></b>" ++msgstr "<b><big>開始升級嗎?</big></b>" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:15 ++msgid "" ++"<b><big>Restart the system to complete the upgrade</big></b>\n" ++"\n" ++"Please save your work before continuing." ++msgstr "" ++"<b><big>重新啟動系統以完成升級</big></b>\n" ++"\n" ++"請在繼續前先儲存您的作業。" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:18 ++msgid "Distribution Upgrade" ++msgstr "發行版升級" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:19 ++#, fuzzy ++msgid "<b><big>Upgrading Ubuntu to version 12.10</big></b>" ++msgstr "<b><big>將 Ubuntu 升級至 11.10 版</big></b>" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:20 ++msgid " " ++msgstr " " ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:22 ++msgid "Setting new software channels" ++msgstr "設定新軟體頻道" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:24 ++msgid "Restarting the computer" ++msgstr "重新啟動系統" ++ ++#: ../DistUpgrade/DistUpgrade.ui.h:27 ++msgid "Terminal" ++msgstr "終端" ++ ++#: ../UpdateManager/backend/InstallBackendSynaptic.py:64 ++msgid "Please wait, this can take some time." ++msgstr "請稍候,這需要一點時間。" ++ ++#: ../UpdateManager/backend/InstallBackendSynaptic.py:66 ++msgid "Update is complete" ++msgstr "更新完成" ++ ++#: ../UpdateManager/DistUpgradeFetcher.py:114 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:109 ++msgid "Could not find the release notes" ++msgstr "找不到發行公告" ++ ++#: ../UpdateManager/DistUpgradeFetcher.py:115 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:110 ++msgid "The server may be overloaded. " ++msgstr "伺服器可能負荷過重。 " ++ ++#: ../UpdateManager/DistUpgradeFetcher.py:125 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:114 ++msgid "Could not download the release notes" ++msgstr "無法下載發行公告" ++ ++#: ../UpdateManager/DistUpgradeFetcher.py:126 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:115 ++msgid "Please check your internet connection." ++msgstr "請檢查您的網際網路連線。" ++ ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:68 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:91 ++msgid "Upgrade" ++msgstr "升級" ++ ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:95 ++#: ../data/gtkbuilder/UpdateManager.ui.h:20 ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:2 ++msgid "Release Notes" ++msgstr "發行公告" ++ ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:134 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:148 ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:150 ++msgid "Downloading additional package files..." ++msgstr "正在下載額外的套件檔案..." ++ ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:148 ++#, python-format ++msgid "File %s of %s at %sB/s" ++msgstr "檔案 %s / %s (速度:%s 位元組/秒)" ++ ++#: ../UpdateManager/DistUpgradeFetcherKDE.py:150 ++#, python-format ++msgid "File %s of %s" ++msgstr "檔案 %s / %s" ++ ++#: ../UpdateManager/ChangelogViewer.py:75 ++msgid "Open Link in Browser" ++msgstr "用瀏覽器開啟連結" ++ ++#: ../UpdateManager/ChangelogViewer.py:78 ++msgid "Copy Link to Clipboard" ++msgstr "複製連結至剪貼簿" ++ ++#: ../UpdateManager/GtkProgress.py:162 ++#, python-format ++msgid "Downloading file %(current)li of %(total)li with %(speed)s/s" ++msgstr "共 %(total)li 個檔案,正下載第 %(current)li 個 (速度: %(speed)s/秒)" ++ ++#: ../UpdateManager/GtkProgress.py:167 ++#, python-format ++msgid "Downloading file %(current)li of %(total)li" ++msgstr "共 %(total)li 個檔案,正下載第 %(current)li 個" ++ ++#: ../UpdateManager/UpdateManager.py:106 ../do-release-upgrade:100 ++msgid "Your Ubuntu release is not supported anymore." ++msgstr "您的 Ubuntu 發行版本已經不再支援。" ++ ++#: ../UpdateManager/UpdateManager.py:107 ++msgid "" ++"You will not get any further security fixes or critical updates. Please " ++"upgrade to a later version of Ubuntu." ++msgstr "您將無法再取得安全性修正或重大更新。請升級至較新版本的 Ubuntu。" ++ ++#: ../UpdateManager/UpdateManager.py:115 ++msgid "Upgrade information" ++msgstr "升級資訊" ++ ++#: ../UpdateManager/UpdateManager.py:233 ++#: ../UpdateManagerText/UpdateManagerText.py:35 ++msgid "Install" ++msgstr "安裝" ++ ++#: ../UpdateManager/UpdateManager.py:235 ++msgid "Name" ++msgstr "名稱" ++ ++#. upload_archive = version_match.group(2).strip() ++#: ../UpdateManager/UpdateManager.py:395 ++#, python-format ++msgid "Version %s: \n" ++msgstr "版本 %s: \n" ++ ++#: ../UpdateManager/UpdateManager.py:453 ++msgid "" ++"No network connection detected, you can not download changelog information." ++msgstr "未偵測到網路連線,您無法下載更動紀錄資訊。" ++ ++#: ../UpdateManager/UpdateManager.py:463 ++msgid "Downloading list of changes..." ++msgstr "正下載更動清單..." ++ ++#: ../UpdateManager/UpdateManager.py:507 ++msgid "_Deselect All" ++msgstr "全部不選(_D)" ++ ++#: ../UpdateManager/UpdateManager.py:513 ++msgid "Select _All" ++msgstr "全部選取(_A)" ++ ++#: ../UpdateManager/UpdateManager.py:572 ++#, python-format ++msgid "%s will be downloaded." ++msgstr "將下載 %s。" ++ ++#: ../UpdateManager/UpdateManager.py:584 ++#, fuzzy ++msgid "The update has already been downloaded." ++msgid_plural "The updates have already been downloaded." ++msgstr[0] "更新已經下載,但尚未安裝。" ++ ++#: ../UpdateManager/UpdateManager.py:589 ++msgid "There are no updates to install." ++msgstr "沒有要安裝的更新。" ++ ++#: ../UpdateManager/UpdateManager.py:598 ++msgid "Unknown download size." ++msgstr "未知下載大小。" ++ ++#: ../UpdateManager/UpdateManager.py:624 ++msgid "" ++"It is unknown when the package information was updated last. Please click " ++"the 'Check' button to update the information." ++msgstr "未知套件資訊的上次更新時間。請點擊「檢查」按鈕來更新資訊。" ++ ++#: ../UpdateManager/UpdateManager.py:630 ++#, python-format ++msgid "" ++"The package information was last updated %(days_ago)s days ago.\n" ++"Press the 'Check' button below to check for new software updates." ++msgstr "" ++"%(days_ago)s 天前更新過套件資訊。\n" ++"請按下方「檢查」鈕看看有否新資訊。" ++ ++#: ../UpdateManager/UpdateManager.py:635 ++#, python-format ++msgid "The package information was last updated %(days_ago)s day ago." ++msgid_plural "The package information was last updated %(days_ago)s days ago." ++msgstr[0] "%(days_ago)s 天前更新過套件資訊。" ++ ++#: ../UpdateManager/UpdateManager.py:639 ++#, python-format ++msgid "The package information was last updated %(hours_ago)s hour ago." ++msgid_plural "" ++"The package information was last updated %(hours_ago)s hours ago." ++msgstr[0] "%(hours_ago)s 小時前更新過套件資訊。" ++ ++#. TRANSLATORS: only in plural form, as %s minutes ago is one of 15, 30, 45 minutes ago ++#: ../UpdateManager/UpdateManager.py:644 ../UpdateManager/UpdateManager.py:646 ++#: ../UpdateManager/UpdateManager.py:648 ++#, python-format ++msgid "The package information was last updated about %s minutes ago." ++msgstr "套件資訊約 %s 分鐘前更新。" ++ ++#: ../UpdateManager/UpdateManager.py:650 ++msgid "The package information was just updated." ++msgstr "套件資訊剛更新。" ++ ++#: ../UpdateManager/UpdateManager.py:689 ++msgid "Software updates may be available for your computer." ++msgstr "可能有軟體更新提供予閣下之電腦。" ++ ++#: ../UpdateManager/UpdateManager.py:697 ++#, python-format ++msgid "" ++"Updated software has been issued since %s was released. Do you want to " ++"install it now?" ++msgstr "" ++ ++#: ../UpdateManager/UpdateManager.py:700 ++msgid "" ++"Updated software is available for this computer. Do you want to install it " ++"now?" ++msgstr "" ++ ++#: ../UpdateManager/UpdateManager.py:758 ++#, python-format ++msgid "" ++"The upgrade needs a total of %s free space on disk '%s'. Please free at " ++"least an additional %s of disk space on '%s'. Empty your trash and remove " ++"temporary packages of former installations using 'sudo apt-get clean'." ++msgstr "" ++"升級工作需要總共 %s 可用空間於硬碟 ‘%s’。請額外空出最少 %s 的空間於 ‘%s’。清" ++"理清理您的回收筒或使用 ‘sudo apt-get clean’ 移除上次安裝的暫存套件。" ++ ++#: ../UpdateManager/UpdateManager.py:783 ++msgid "" ++"The computer needs to restart to finish installing updates. Please save your " ++"work before continuing." ++msgstr "必須重啟電腦以完成安裝更新。繼續前請先儲存工作。" ++ ++#: ../UpdateManager/UpdateManager.py:847 ++msgid "Reading package information" ++msgstr "正在讀取套件資訊" ++ ++#: ../UpdateManager/UpdateManager.py:862 ++msgid "Connecting..." ++msgstr "連線中..." ++ ++#: ../UpdateManager/UpdateManager.py:879 ++msgid "You may not be able to check for updates or download new updates." ++msgstr "您可能無法檢查是否有更新,或是下載新的更新。" ++ ++#: ../UpdateManager/UpdateManager.py:1002 ++msgid "Could not initialize the package information" ++msgstr "無法初始化套件資訊" ++ ++#: ../UpdateManager/UpdateManager.py:1003 ++msgid "" ++"An unresolvable problem occurred while initializing the package " ++"information.\n" ++"\n" ++"Please report this bug against the 'update-manager' package and include the " ++"following error message:\n" ++msgstr "" ++"初始套件資訊時發生不能解決的問題。\n" ++"\n" ++"請匯報此為『update-manager』套件的問題並附上以下的錯誤訊息:\n" ++ ++#: ../UpdateManager/UpdateManager.py:1032 ++msgid "" ++"An unresolvable problem occurred while calculating the upgrade.\n" ++"\n" ++"Please report this bug against the 'update-manager' package and include the " ++"following error message:" ++msgstr "" ++"計算升級時發生不能解決的問題。\n" ++"\n" ++"請匯報此為『update-manager』套件的問題並附上以下的錯誤訊息:" ++ ++#: ../UpdateManager/UpdateManager.py:1056 ++msgid " (New install)" ++msgstr " (新安裝)" ++ ++#. TRANSLATORS: the b stands for Bytes ++#: ../UpdateManager/UpdateManager.py:1063 ++#, python-format ++msgid "(Size: %s)" ++msgstr "(大小:%s)" ++ ++#: ../UpdateManager/UpdateManager.py:1067 ++#, python-format ++msgid "From version %(old_version)s to %(new_version)s" ++msgstr "由版本 %(old_version)s 更新至 %(new_version)s" ++ ++#: ../UpdateManager/UpdateManager.py:1071 ++#, python-format ++msgid "Version %s" ++msgstr "版本 %s" ++ ++#: ../UpdateManager/UpdateManager.py:1104 ../do-release-upgrade:112 ++msgid "Release upgrade not possible right now" ++msgstr "目前不能升級發行版" ++ ++#: ../UpdateManager/UpdateManager.py:1105 ../do-release-upgrade:113 ++#, c-format, python-format ++msgid "" ++"The release upgrade can not be performed currently, please try again later. " ++"The server reported: '%s'" ++msgstr "發行版升級升級目前無法執行,請稍後重試。該伺服器回報:「%s」。" ++ ++#: ../UpdateManager/UpdateManager.py:1107 ../check-new-release-gtk:126 ++msgid "Downloading the release upgrade tool" ++msgstr "正下載發行版更新工具" ++ ++#: ../UpdateManager/UpdateManager.py:1114 ++#, python-format ++msgid "<b>New Ubuntu release '%s' is available</b>" ++msgstr "<b>有新 Ubuntu 發行版 '%s' 可供升級</b>" ++ ++#. we assert a clean cache ++#: ../UpdateManager/UpdateManager.py:1153 ++msgid "Software index is broken" ++msgstr "軟體索引損壞" ++ ++#: ../UpdateManager/UpdateManager.py:1154 ++msgid "" ++"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." ++msgstr "" ++"無法安裝或移除軟體。請先用 Synaptic 套件管理員或在終端機執行「sudo apt-get " ++"install -f」修正。" ++ ++#: ../UpdateManager/UnitySupport.py:57 ++msgid "Check for Updates" ++msgstr "檢查更新" ++ ++#: ../UpdateManager/UnitySupport.py:66 ++msgid "Install All Available Updates" ++msgstr "安裝所有可進行的更新" ++ ++#: ../UpdateManagerText/UpdateManagerText.py:34 ++msgid "Cancel" ++msgstr "取消" ++ ++#: ../UpdateManagerText/UpdateManagerText.py:37 ++msgid "Changelog" ++msgstr "變更記錄" ++ ++#: ../UpdateManagerText/UpdateManagerText.py:40 ++msgid "Updates" ++msgstr "更新" ++ ++#: ../UpdateManagerText/UpdateManagerText.py:53 ++msgid "Building Updates List" ++msgstr "正建立更新清單" ++ ++#: ../UpdateManagerText/UpdateManagerText.py:56 ++msgid "" ++"\n" ++"A normal upgrade can not be calculated, please run: \n" ++" sudo apt-get dist-upgrade\n" ++"\n" ++"\n" ++"This can be caused by:\n" ++" * A previous upgrade which didn't complete\n" ++" * Problems with some of the installed software\n" ++" * Unofficial software packages not provided by Ubuntu\n" ++" * Normal changes of a pre-release version of Ubuntu" ++msgstr "" ++"\n" ++"無法計算標準升級,請執行: \n" ++" sudo apt-get dist-upgrade\n" ++"\n" ++"\n" ++" 這可能由以下原因造成:\n" ++" * 前次升級程序未完成\n" ++" * 某些已安裝的軟體有問題\n" ++" * 安裝了非由 Ubuntu 官方提供的軟體套件\n" ++" * Ubuntu 非正式發佈版本的正常更動" ++ ++#: ../UpdateManagerText/UpdateManagerText.py:125 ++msgid "Downloading changelog" ++msgstr "正下載變更記錄" ++ ++#: ../UpdateManager/Core/MyCache.py:147 ++#, python-format ++msgid "Other updates (%s)" ++msgstr "其他更新 (%s)" ++ ++#: ../UpdateManager/Core/MyCache.py:325 ++msgid "This update does not come from a source that supports changelogs." ++msgstr "這份更新的來源不支援變更記錄 (changelog)。" ++ ++#: ../UpdateManager/Core/MyCache.py:331 ../UpdateManager/Core/MyCache.py:359 ++msgid "" ++"Failed to download the list of changes. \n" ++"Please check your Internet connection." ++msgstr "" ++"下載更動清單失敗。\n" ++"請檢查網際網路連線。" ++ ++#: ../UpdateManager/Core/MyCache.py:338 ++#, python-format ++msgid "" ++"Changes for the versions:\n" ++"Installed version: %s\n" ++"Available version: %s\n" ++"\n" ++msgstr "" ++"版本的更動情況:\n" ++"已安裝版本:%s\n" ++"可用的版本:%s\n" ++"\n" ++ ++#: ../UpdateManager/Core/MyCache.py:348 ++#, python-format ++msgid "" ++"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." ++msgstr "" ++"變更記錄並未包含相關更動。\n" ++"\n" ++"有變更記錄提供前請至 http://launchpad.net/ubuntu/+source/%s/%s/+changelog,\n" ++"或稍候再試。" ++ ++#: ../UpdateManager/Core/MyCache.py:353 ++#, python-format ++msgid "" ++"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." ++msgstr "" ++"仍未有更動清單。\n" ++"\n" ++"有更動清單提供前請至 http://launchpad.net/ubuntu/+source/%s/%s/+changelog,\n" ++"或稍候再試。" ++ ++#: ../UpdateManager/Core/UpdateList.py:51 ++msgid "Failed to detect distribution" ++msgstr "無法偵測出版本" ++ ++#: ../UpdateManager/Core/UpdateList.py:52 ++#, python-format ++msgid "A error '%s' occurred while checking what system you are using." ++msgstr "當檢查您所使用的系統時有錯誤 \"%s\" 發生。" ++ ++#: ../UpdateManager/Core/UpdateList.py:63 ++msgid "Important security updates" ++msgstr "重要的安全性更新" ++ ++#: ../UpdateManager/Core/UpdateList.py:64 ++msgid "Recommended updates" ++msgstr "推薦更新" ++ ++#: ../UpdateManager/Core/UpdateList.py:65 ++msgid "Proposed updates" ++msgstr "建議更新" ++ ++#: ../UpdateManager/Core/UpdateList.py:66 ++msgid "Backports" ++msgstr "回殖套件 (Backports)" ++ ++#: ../UpdateManager/Core/UpdateList.py:67 ++msgid "Distribution updates" ++msgstr "發行版更新" ++ ++#: ../UpdateManager/Core/UpdateList.py:72 ++msgid "Other updates" ++msgstr "其他更新" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:1 ++#, fuzzy ++msgid "<big><b>Starting Software Updater</b></big>" ++msgstr "<big><b>啟動更新管理員</b></big>" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:2 ++msgid "" ++"Software updates correct errors, eliminate security vulnerabilities and " ++"provide new features." ++msgstr "軟體更新會修正錯誤、消除安全隱患並提供新功能。" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:3 ++msgid "_Partial Upgrade" ++msgstr "部份升級(_P)" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:4 ++msgid "<big><b>Not all updates can be installed</b></big>" ++msgstr "<big><b>並非所有更新都可安裝</b></big>" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:5 ++msgid "" ++"Run a partial upgrade, to install as many updates as possible. \n" ++"\n" ++"This can be caused by:\n" ++" * A previous upgrade which didn't complete\n" ++" * Problems with some of the installed software\n" ++" * Unofficial software packages not provided by Ubuntu\n" ++" * Normal changes of a pre-release version of Ubuntu" ++msgstr "" ++"執行部份升級,將儘可能安裝最多的更新。 \n" ++" 這可能由以下原因造成:\n" ++" * 前次升級程序未完成\n" ++" * 某些已安裝的軟體有問題\n" ++" * 安裝了非由 Ubuntu 官方提供的軟體套件\n" ++" * Ubuntu 非正式發佈版本的正常更動" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:12 ++msgid "Chec_k" ++msgstr "檢查(_K)" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:13 ++msgid "" ++"<b><big>You must check for updates manually</big></b>\n" ++"\n" ++"Your system does not check for updates automatically. You can configure this " ++"behavior in <i>Software Sources</i> on the <i>Updates</i> tab." ++msgstr "" ++"<b><big>您必須手動檢查更新</big></b>\n" ++"\n" ++"您的系統不會自動檢查更新。您可以 <i>更新</i> 分頁之 <i>軟體來源</i> 設定。" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:16 ++msgid "_Hide this information in the future" ++msgstr "以後不要再顯示此訊息(_H)" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:17 ++msgid "Co_ntinue" ++msgstr "繼續(_N)" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:18 ++msgid "<big><b>Running on battery</b></big>" ++msgstr "<big><b>使用電池運行</b></big>" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:19 ++msgid "Your system is running on battery. Are you sure you want to continue?" ++msgstr "您的系統正在使用電池。確定要繼續嗎?" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:21 ++msgid "_Upgrade" ++msgstr "升級(_U)" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:22 ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:8 ++msgid "Show progress of individual files" ++msgstr "顯示單一檔案的進度" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:23 ++#: ../data/update-manager.desktop.in.h:1 ++#, fuzzy ++msgid "Software Updater" ++msgstr "軟體更新" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:24 ++#, fuzzy ++msgid "Starting Software Updater" ++msgstr "軟體更新" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:25 ++msgid "U_pgrade" ++msgstr "升級(_P)" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:27 ++msgid "updates" ++msgstr "更新" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:28 ++msgid "Changes" ++msgstr "變更" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:29 ++msgid "Description" ++msgstr "說明" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:30 ++#, fuzzy ++msgid "Details of updates" ++msgstr "更新說明" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:31 ++msgid "" ++"You are connected via roaming and may be charged for the data consumed by " ++"this update." ++msgstr "您已透過漫遊連上網路,可能要對本次更新所下載的資料量付費。" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:32 ++msgid "" ++"You may want to wait until you’re not using a mobile broadband connection." ++msgstr "" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:33 ++msgid "It’s safer to connect the computer to AC power before updating." ++msgstr "在更新前先將電腦接上 AC 電源比較安全。" ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:34 ++msgid "_Settings..." ++msgstr "設定(_S)..." ++ ++#: ../data/gtkbuilder/UpdateManager.ui.h:35 ++#, fuzzy ++msgid "_Install Now" ++msgstr "安裝" ++ ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:1 ++msgid "<b>A new version of Ubuntu is available. Would you like to upgrade?</b>" ++msgstr "<b>有新版本 Ubuntu。要否升級?</b>" ++ ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:3 ++msgid "Don't Upgrade" ++msgstr "不升級" ++ ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:4 ++msgid "Ask Me Later" ++msgstr "稍後再問我" ++ ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:5 ++msgid "Yes, Upgrade Now" ++msgstr "是,現在升級" ++ ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:6 ++msgid "You have declined to upgrade to the new Ubuntu" ++msgstr "您已拒絕升級至新的 Ubuntu" ++ ++#: ../data/gtkbuilder/UpgradePromptDialog.ui.h:7 ++#, fuzzy ++msgid "" ++"You can upgrade at a later time by opening Software Updater and click on " ++"\"Upgrade\"." ++msgstr "您稍後仍可以透過開啟更新管理員並按下『升級』進行升級。" ++ ++#: ../data/update-manager.desktop.in.h:2 ++msgid "Software Updates" ++msgstr "軟體更新" ++ ++#: ../data/update-manager.desktop.in.h:3 ++msgid "Show and install available updates" ++msgstr "顯示並安裝現有軟體更新" ++ ++#: ../update-manager:66 ../update-manager-text:55 ../do-release-upgrade:51 ++msgid "Show version and exit" ++msgstr "顯示版本後結束" ++ ++#: ../update-manager:69 ++msgid "Directory that contains the data files" ++msgstr "含有資料檔案的目錄" ++ ++#: ../update-manager:72 ++msgid "Check if a new Ubuntu release is available" ++msgstr "檢查有否新 Ubuntu 發行版可供升級" ++ ++#: ../update-manager:75 ../do-release-upgrade:54 ../check-new-release-gtk:186 ++msgid "Check if upgrading to the latest devel release is possible" ++msgstr "檢查能否升級至最新開發發行版" ++ ++#: ../update-manager:79 ++msgid "Upgrade using the latest proposed version of the release upgrader" ++msgstr "使用最新建議版本的發行升級工具升級" ++ ++#: ../update-manager:86 ++msgid "Do not focus on map when starting" ++msgstr "啟動時不預先選取圖錄" ++ ++#: ../update-manager:89 ++msgid "Try to run a dist-upgrade" ++msgstr "試著執行 dist-upgrade" ++ ++#: ../update-manager:92 ++msgid "Do not check for updates when starting" ++msgstr "當啟動時不要檢查更新" ++ ++#: ../update-manager:96 ../do-release-upgrade:70 ++msgid "Test upgrade with a sandbox aufs overlay" ++msgstr "使用沙堆 aufs 層測試升級" ++ ++#: ../update-manager:116 ++msgid "Running partial upgrade" ++msgstr "執行部份升級" ++ ++#: ../update-manager-text:59 ++msgid "Show description of the package instead of the changelog" ++msgstr "顯示套件描述而不是變更紀錄" ++ ++#: ../do-release-upgrade:58 ../check-new-release-gtk:190 ++msgid "" ++"Try upgrading to the latest release using the upgrader from $distro-proposed" ++msgstr "試著使用 $distro-proposed 的套件升級程式來升級到最新的發行版本" ++ ++#: ../do-release-upgrade:62 ++msgid "" ++"Run in a special upgrade mode.\n" ++"Currently 'desktop' for regular upgrades of a desktop system and 'server' " ++"for server systems are supported." ++msgstr "" ++"在特殊升級模式執行。\n" ++"目前只支援以 'desktop' 模式升級桌面版本的系統,以及以 'server' 模式升級伺服器" ++"版的系統。" ++ ++#: ../do-release-upgrade:68 ++msgid "Run the specified frontend" ++msgstr "執行指定的前端" ++ ++#: ../do-release-upgrade:73 ++msgid "" ++"Check only if a new distribution release is available and report the result " ++"via the exit code" ++msgstr "檢查有否新發行版並以結束碼報告結果" ++ ++#: ../do-release-upgrade:87 ++msgid "Checking for a new Ubuntu release" ++msgstr "檢查是否有新的 Ubuntu 發行" ++ ++#: ../do-release-upgrade:101 ++msgid "" ++"For upgrade information, please visit:\n" ++"%(url)s\n" ++msgstr "" ++"若要取得升級資訊,請參訪:\n" ++"%(url)s\n" ++ ++#: ../do-release-upgrade:107 ++msgid "No new release found" ++msgstr "沒找到新發行版" ++ ++#: ../do-release-upgrade:119 ++#, c-format ++msgid "New release '%s' available." ++msgstr "有新版「%s」提供。" ++ ++#: ../do-release-upgrade:120 ++msgid "Run 'do-release-upgrade' to upgrade to it." ++msgstr "執行 ‘do-release-upgrade’ 進行升級工作。" ++ ++#: ../check-new-release-gtk:101 ++msgid "Ubuntu %(version)s Upgrade Available" ++msgstr "可以升級至 Ubuntu %(version)s" ++ ++#: ../check-new-release-gtk:143 ++#, c-format ++msgid "You have declined the upgrade to Ubuntu %s" ++msgstr "您已拒絕升級至 Ubuntu %s" ++ ++#: ../check-new-release-gtk:196 ++msgid "Add debug output" ++msgstr "加入除錯輸出" ++ ++#: ../ubuntu-support-status:91 ++msgid "Show unsupported packages on this machine" ++msgstr "顯示此機器上不再支援的套件" ++ ++#: ../ubuntu-support-status:94 ++msgid "Show supported packages on this machine" ++msgstr "顯示此機器上受支援的套件" ++ ++#: ../ubuntu-support-status:97 ++msgid "Show all packages with their status" ++msgstr "顯示所有套件及其狀態" ++ ++#: ../ubuntu-support-status:100 ++msgid "Show all packages in a list" ++msgstr "將所有套件以清單顯示" ++ ++#: ../ubuntu-support-status:142 ++#, c-format ++msgid "Support status summary of '%s':" ++msgstr "「%s」的支援狀態摘要:" ++ ++#: ../ubuntu-support-status:145 ++msgid "You have %(num)s packages (%(percent).1f%%) supported until %(time)s" ++msgstr "您現在有 %(num)s 個套件 (%(percent).1f%%) 將支援直到 %(time)s" ++ ++#: ../ubuntu-support-status:151 ++msgid "" ++"You have %(num)s packages (%(percent).1f%%) that can not/no-longer be " ++"downloaded" ++msgstr "您有 %(num)s 個套件 (%(percent).1f%%) 無法/不再能下載" ++ ++#: ../ubuntu-support-status:154 ++msgid "You have %(num)s packages (%(percent).1f%%) that are unsupported" ++msgstr "您有 %(num)s 個套件 (%(percent).1f%%) 不再支援" ++ ++#: ../ubuntu-support-status:162 ++msgid "" ++"Run with --show-unsupported, --show-supported or --show-all to see more " ++"details" ++msgstr "" ++"加上 --show-unsupported、--show-supported 或是 --show-all 作為參數執行以查看" ++"更多細節" ++ ++#: ../ubuntu-support-status:166 ++msgid "No longer downloadable:" ++msgstr "不再能下載:" ++ ++#: ../ubuntu-support-status:169 ++msgid "Unsupported: " ++msgstr "不再支援: " ++ ++#: ../ubuntu-support-status:174 ++#, c-format ++msgid "Supported until %s:" ++msgstr "支援直至 %s:" ++ ++#: ../ubuntu-support-status:183 ++msgid "Unsupported" ++msgstr "不再支援" ++ ++#. Why do we use %s here instead of $strings or {} format placeholders? ++#. It's because we don't want to break existing translations. ++#: ../janitor/plugincore/exceptions.py:42 ++#, python-format ++msgid "Unimplemented method: %s" ++msgstr "未實作的方法: %s" ++ ++#: ../janitor/plugincore/core/file_cruft.py:41 ++msgid "A file on disk" ++msgstr "磁碟上之檔案" ++ ++#: ../janitor/plugincore/core/missing_package_cruft.py:39 ++msgid "Install missing package." ++msgstr "安裝缺少的套件。" ++ ++#. 2012-06-08 BAW: i18n string; don't use {} or PEP 292. ++#: ../janitor/plugincore/core/missing_package_cruft.py:49 ++#, python-format ++msgid "Package %s should be installed." ++msgstr "應安裝 %s 套件。" ++ ++#: ../janitor/plugincore/core/package_cruft.py:49 ++msgid ".deb package" ++msgstr ".deb 套件" ++ ++#: ../janitor/plugincore/plugins/langpack_manual_plugin.py:46 ++#, python-format ++msgid "%s needs to be marked as manually installed." ++msgstr "%s 要標記為手動安裝。" ++ ++#: ../janitor/plugincore/plugins/kdelibs4to5_plugin.py:49 ++msgid "" ++"When upgrading, if kdelibs4-dev is installed, kdelibs5-dev needs to be " ++"installed. See bugs.launchpad.net, bug #279621 for details." ++msgstr "" ++"在升級時,如果有安裝 kdelibs4-dev,那 kdelibs5-dev 就必須安裝。詳情請見 bugs." ++"launchpad.net, bug #279621。" ++ ++#: ../janitor/plugincore/plugins/dpkg_status_plugin.py:44 ++#, python-format ++msgid "%i obsolete entries in the status file" ++msgstr "狀態檔有 %i 個廢棄條目" ++ ++#: ../janitor/plugincore/plugins/dpkg_status_plugin.py:47 ++msgid "Obsolete entries in dpkg status" ++msgstr "dpkg 狀態中之廢棄條目" ++ ++#. pragma: no cover ++#: ../janitor/plugincore/plugins/dpkg_status_plugin.py:50 ++msgid "Obsolete dpkg status entries" ++msgstr "廢棄的 dpkg 狀態條目" ++ ++#: ../janitor/plugincore/plugins/remove_lilo_plugin.py:42 ++msgid "Remove lilo since grub is also installed.(See bug #314004 for details.)" ++msgstr "因已安裝 grub,移除 lilo。(詳情見 bug #314004。)" ++ ++#~ msgid "" ++#~ "After your package information was updated the essential package '%s' can " ++#~ "not be found anymore so a bug reporting process is being started." ++#~ msgstr "" ++#~ "在您的套件資訊更新之後,我們無法再找到必要的「%s」套件,因此正在啟動臭蟲回" ++#~ "報程序。" ++ ++#~ msgid "<b><big>Upgrading Ubuntu to version 12.04</big></b>" ++#~ msgstr "<b><big>升級 Ubuntu 至 12.04 版</big></b>" ++ ++#~ msgid "%(count)s update has been selected." ++#~ msgid_plural "%(count)s updates have been selected." ++#~ msgstr[0] "已選取 %(count)s 項更新。" ++ ++#~ msgid "%(count_str)s %(download_str)s" ++#~ msgstr "%(count_str)s %(download_str)s" ++ ++#~ msgid "Welcome to Ubuntu" ++#~ msgstr "歡迎使用 Ubuntu" ++ ++#~ msgid "" ++#~ "These software updates have been issued since this version of Ubuntu was " ++#~ "released." ++#~ msgstr "自從這個版本的 Ubuntu 發行後,已經釋出這些軟體更新" ++ ++#~ msgid "Software updates are available for this computer." ++#~ msgstr "有此電腦可用的軟體更新。" ++ ++#~ msgid "Update Manager" ++#~ msgstr "更新管理員" ++ ++#~ msgid "Starting Update Manager" ++#~ msgstr "正在啟動更新管理員" ++ ++#~ msgid "You are connected via a wireless modem." ++#~ msgstr "您正透過無線數據機連線。" ++ ++#~ msgid "_Install Updates" ++#~ msgstr "安裝更新套件(_I)" ++ ++#~ msgid "Checking for a new ubuntu release" ++#~ msgstr "檢查有沒有新的 ubuntu 發行版" ++ ++#~ msgid "Your system is up-to-date" ++#~ msgstr "系統已經在最新狀態" ++ ++#~ msgid "" ++#~ "This upgrade is running in sandbox (test) mode. All changes are written " ++#~ "to '%s' and will be lost on the next reboot.\n" ++#~ "\n" ++#~ "*No* changes written to a systemdir from now until the next reboot are " ++#~ "permanent." ++#~ msgstr "" ++#~ "這次升級是在沙堆(sandbox, 測試)模式中執行。所有更動都會寫入至「%s」並且會" ++#~ "在下次重新開機時消失。\n" ++#~ "\n" ++#~ "現在起到下次重新開機前將*不會*有任何變更寫入至系統目錄。" ++ ++#~ msgid "The update has already been downloaded, but not installed" ++#~ msgid_plural "The updates have already been downloaded, but not installed" ++#~ msgstr[0] "更新已下載,但尚未安裝" ++ ++#~ msgid "" ++#~ "Fetching and installing the upgrade can take several hours. Once the " ++#~ "download has finished, the process cannot be cancelled." ++#~ msgstr "下載和安裝升級會花上數小時。一旦下載完成,程序將不能取消。" ++ ++#~ msgid "There are no updates to install" ++#~ msgstr "沒有要安裝的更新" ++ ++#~ msgid "Software updates are available for this computer" ++#~ msgstr "有軟體更新提供予此電腦。" ++ ++#~ msgid "" ++#~ "If you don't want to install them now, choose \"Update Manager\" from the " ++#~ "Administration menu later." ++#~ msgstr "若目前不想安裝,可稍後於管理選單開啟「更新管理員」。" ++ ++#~ msgid "" ++#~ "If you don't want to install them now, choose \"Update Manager\" from " ++#~ "Applications later." ++#~ msgstr "" ++#~ "若現在您不想要安裝它們,您可以稍後從「應用程式」選擇「更新管理員」再安裝。" ++ ++#~ msgid "" ++#~ "You will not get any further security fixes or critical updates. Please " ++#~ "upgrade to a later version of Ubuntu Linux." ++#~ msgstr "您將無法再取得安全性修正與重大更新。請升級至新版 Ubuntu Linux。" ++ ++#~ msgid "" ++#~ "Upgrading the repository information resulted in a invalid file. Please " ++#~ "report this as a bug using the command 'ubuntu-bug update-manager' in a " ++#~ "terminal." ++#~ msgstr "" ++#~ "升級套件庫時導致無效的檔案。請在終端機內輸入指令「ubuntu-bug update-" ++#~ "manager」回報錯誤。" ++ ++#~ msgid "" ++#~ "\n" ++#~ "\n" ++#~ "Please report this bug using the command 'ubuntu-bug update-manager' in a " ++#~ "terminal and include the files in /var/log/dist-upgrade/ in the bug " ++#~ "report.\n" ++#~ "%s" ++#~ msgstr "" ++#~ "\n" ++#~ "\n" ++#~ "請在終端機內輸入指令「ubuntu-bug update-manager」回報錯誤,並在錯誤報告中" ++#~ "附上 /var/log/dist-upgrade/ 內之檔案。\n" ++#~ "%s" ++ ++#~ msgid "" ++#~ "The system was unable to get the prerequisites for the upgrade. The " ++#~ "upgrade will abort now and restore the original system state.\n" ++#~ "\n" ++#~ "Please report this as a bug using the command 'ubuntu-bug update-manager' " ++#~ "in a terminal and include the files in /var/log/dist-upgrade/ in the bug " ++#~ "report." ++#~ msgstr "" ++#~ "此系統無法達到升級之要求。現在將中斷升級並回復系統至原始狀態。\n" ++#~ "\n" ++#~ "請在終端機內輸入指令「ubuntu-bug update-manager」回報錯誤,並在錯誤報告中" ++#~ "附上 /var/log/dist-upgrade/ 內之檔案。" ++ ++#~ msgid "" ++#~ "Preparing the system for the upgrade failed. Please report this using the " ++#~ "command 'ubuntu-bug update-manager' in a terminal and include the files " ++#~ "in /var/log/dist-upgrade/ in the bug report." ++#~ msgstr "" ++#~ "準備系統升級失敗。請在終端機內輸入指令「ubuntu-bug update-manager」回報錯" ++#~ "誤,並在錯誤報告中附上 /var/log/dist-upgrade/ 內之檔案。" ++ ++#~ msgid "" ++#~ "After your package information was updated the essential package '%s' can " ++#~ "not be found anymore.\n" ++#~ "This indicates a serious error, please report this bug using the command " ++#~ "'ubuntu-bug update-manager' in a terminal and include the files in /var/" ++#~ "log/dist-upgrade/ in the bug report." ++#~ msgstr "" ++#~ "套件資訊更新後無法找到必要套件「%s」。\n" ++#~ "此為嚴重錯誤,請在終端機內輸入指令「ubuntu-bug update-manager」回報錯誤," ++#~ "並在錯誤報告中附上 /var/log/dist-upgrade/ 內的檔案。" ++ ++#~ msgid "Your graphics hardware may not be fully supported in Ubuntu 11.04." ++#~ msgstr "您的繪圖硬體可能無法在 Ubuntu 11.04 獲得完整的支援。" ++ ++#~ msgid "" ++#~ "The support in Ubuntu 11.04 for your intel graphics hardware is limited " ++#~ "and you may encounter problems after the upgrade. Do you want to continue " ++#~ "with the upgrade?" ++#~ msgstr "" ++#~ "您的 Intel 繪圖硬體在 Ubuntu 11.04 內的支援有限,且可能會在升級之後碰到一" ++#~ "些問題。您要繼續進行升級嗎?" ++ ++#~ msgid "%.0f kB" ++#~ msgstr "%.0f kB" ++ ++#~ msgid "0 kB" ++#~ msgstr "0 kB" ++ ++#~ msgid "1 kB" ++#~ msgstr "1 kB" ++ ++#~ msgid "" ++#~ "These software updates have been issued since this version of Ubuntu was " ++#~ "released. If you don't want to install them now, choose \"Update Manager" ++#~ "\" from Applications later." ++#~ msgstr "" ++#~ "自從這個 Ubuntu 版本發行後已經發布這些軟體更新。如果您現在還不想安裝它們," ++#~ "請稍後從「應用程式」選單選擇「更新管理員」。" ++ ++#~ msgid "" ++#~ "These software updates have been issued since this version of Ubuntu was " ++#~ "released. If you don't want to install them now, choose \"Update Manager" ++#~ "\" from the Administration Menu later." ++#~ msgstr "" ++#~ "自從這個 Ubuntu 版本發行後已經發布這些軟體更新。如果您現在還不想安裝它們," ++#~ "請稍後從「管理」選單選擇「更新管理員」。" ++ ++ ++msgid "Unable to access the source management server, please try again later" ++msgstr "無法存取來源管理伺服器,請稍後再試" ++ ++msgid "Check if your network requires authentication?" ++msgstr "檢查您的網路需要認證嗎?" ++ ++msgid "Check your source public key signature" ++msgstr "檢查您的源數字簽名" ++ ++msgid "update important list occur Exception" ++msgstr "獲取推送出現異常,請稍後再試" ++ ++msgid "You need to be root to run this application" ++msgstr "你需要root許可權運行" ++ ++msgid "There is an exception in the update package." ++msgstr "更新包存在異常!" ++ ++msgid "You request the removal of a system-essential package." ++msgstr "您要求刪除系統必要的套件。" ++ ++msgid "This update cannot detect the upgradeable package." ++msgstr "本次更新無法檢測到可升級的軟體包。" ++ ++msgid "read important list failed" ++msgstr "無法讀取推送升級清單,請稍後再試" ++ ++msgid "Priority Upgrade Package being updated" ++msgstr "正在更新分組配置" ++ ++msgid "Exceptions of Priority Upgrade." ++msgstr "優先升級異常" ++ ++msgid "Due to the presence of deleted packages." ++msgstr "由於存在刪除的套件" ++ ++msgid "The system update configuration file is read abnormally, please check if the system update configuration file format is correct." ++msgstr "讀取系統更新設定檔異常,請檢查系統更新配置檔格式是否正確。" ++ ++msgid "Installation progress: " ++msgstr "安裝進度: " ++ ++msgid "Installation successful, about to shut down" ++msgstr "安裝成功,即將關機" ++ ++msgid "Installation failed, about to shut down" ++msgstr "安裝失敗,即將關機" ++ ++msgid "groups JSON ConfigPkgs install failed" ++msgstr "無法安裝分組配置檔" ++ ++msgid "Installtion timeout to exit Due to inactivity" ++msgstr "安裝超時退出由於" ++ ++msgid "Command execution error" ++msgstr "命令執行報錯" ++ ++msgid "Unsupported architecture" ++msgstr "架構不符合" ++ ++msgid "Other Error" ++msgstr "其他錯誤" ++ ++msgid "dependency is not satisfied" ++msgstr "依賴關係不滿足" ++ ++msgid "dependency is not satisfied will download" ++msgstr "依賴關係不滿足" ++ ++msgid "Disk space is insufficient, please clean the disk and then upgrade" ++msgstr "磁碟空間不足,請清理磁碟後進行升級更新。" ++ ++msgid "Network anomaly, can't check for updates!" ++msgstr "網路異常,無法檢查更新!" ++ ++msgid "Check for update exceptions!" ++msgstr "檢查更新異常!" ++ ++msgid "Check for update exceptions,fix system APT environment error." ++msgstr "檢查更新異常,修復系統APT環境出現錯誤。" ++ ++msgid "The system APT environment is abnormal, please check the system APT environment." ++msgstr "修復系統APT環境異常,請檢查系統APT環境。" ++ ++msgid "Priority upgrade status exception." ++msgstr "優先升級狀態異常。" ++ ++msgid "Upgrade configuration acquisition exception." ++msgstr "升級配置獲取異常。" ++ ++msgid "Please check your network connection and retry." ++msgstr "請檢查您的網路連接后再試。" ++ ++msgid "Please check your source list and retry." ++msgstr "請檢查您的源清單後再試。" ++ ++msgid "Checking network connection" ++msgstr "檢查網路連接中" ++ ++msgid "Updating Source Template" ++msgstr "更新源範本中" ++ ++msgid "Update Manager upgrade is complete, please restart the setting panel before performing the system update." ++msgstr "更新管理器升級完成,請重啟設置-更新后再進行系統更新。" ++ ++msgid "Uninstallation completed" ++msgstr "卸載完成。" ++ ++msgid "Package validation failed and installation was rejected." ++msgstr "套件驗證失敗,拒絕安裝。" ++ ++msgid "Other tasks are being updated and upgraded, please uninstall them later." ++msgstr "其他任務正在更新升級中,請稍後再卸載。" ++ ++#: ../SystemUpdater/Core/enums.py:763 ++msgid "Kylin System Updater" ++msgstr "麒麟更新器" ++ ++#: ../SystemUpdater/Core/enums.py:609 ++msgid "Kylin Installer" ++msgstr "麒麟安裝器" ++ ++#: ../SystemUpdater/Core/enums.py:610 ++msgid "Kylin Uninstaller" ++msgstr "麒麟卸載器" ++ ++#: ../SystemUpdater/Core/enums.py:611 ++msgid "Kylin Background Upgrade" ++msgstr "靜默更新" ++ ++#: ../SystemUpdater/Core/enums.py:612 ++msgid "Kylin Software Center" ++msgstr "軟體商店" ++ ++#: ../SystemUpdater/UpdateManagerDbus.py:355 ++msgid " requires authentication to uninstall software packages." ++msgstr "卸載套件需要認證。" ++ ++#: ../SystemUpdater/UpdateManager.py:463 ++msgid " requires authentication to install software packages." ++msgstr "安裝套件需要認證。" ++ ++#: ../SystemUpdater/Core/utils.py:750 ++msgid "Authentication success." ++msgstr "認證成功。" ++ ++#: ../SystemUpdater/Core/utils.py:753 ++msgid "Authentication failure." ++msgstr "認證失敗。" ++ ++#: ../SystemUpdater/Core/enums.py:101 ++msgid "Deb format exception, read local deb file error." ++msgstr "軟體包格式異常,讀取失敗。" ++ ++#: ../SystemUpdater/Core/enums.py:102 ++msgid "Install deb error." ++msgstr "安裝套件失敗。" ++ ++msgid "Upgrade System" ++msgstr "全盤升級" ++ ++msgid "kylin-unattended-upgrade" ++msgstr "自動更新" ++ ++msgid "Please check the system time and synchronize the system time before updating." ++msgstr "請檢查系統時間,同步系統時間后再進行更新。" ++ ++msgid "The package is unsigned, refuses to install." ++msgstr "軟體包未簽名,拒絕安裝。" ++ ++msgid "Application installation control policy not enabled ." ++msgstr "應用安裝管控未開啓." ++ ++msgid "Installation failed! Application is not in the software whitelist list!" ++msgstr "安裝失敗!軟件包不在白名單列表!" ++ ++msgid "Installation failed! Application is in the software blacklist list!" ++msgstr "安裝失敗!軟件包在黑名單列表!" ++ ++msgid "Application installation in unknown mode ." ++msgstr "應用安裝管控策略未知." ++ ++#: ../SystemUpdater/Core/utils.py:753 ++msgid "Cancel authentication." ++msgstr "取消認證" +diff --git a/backend-immutable/tests/report-updater-bug b/backend-immutable/tests/report-updater-bug +new file mode 100755 +index 0000000..ef3bf05 +--- /dev/null ++++ b/backend-immutable/tests/report-updater-bug +@@ -0,0 +1,62 @@ ++#!/bin/sh ++#系统升级收集bug日志使用 ++ ++if [ $(id -u) -eq 0 ]; then ++ echo "当前执行权限是root,请使用普通权限来执行" ++ exit 1 ++fi ++ ++echo "系统升级收集BUG日志使用..." ++ ++#建立收集的log目录 ++mkdir updaterlog ++#记录一些基本信息 ++date >> updaterlog/base-info ++dpkg -l | grep kylin-system-updater >> updaterlog/base-info ++echo $1 >> updaterlog/base-info ++echo "记录BUG产生时间(系统当前时间)以及升级相关的版本信息:" ++cat updaterlog/base-info ++ ++cp /etc/apt/sources.list updaterlog || true ++cp -r /etc/apt/apt.conf.d updaterlog || true ++cp -r /var/log/syslog 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 updaterlog || true ++cp -r /var/log/dpkg.log updaterlog || true ++cp -r /var/log/kylin-unattended-upgrades/ updaterlog || true ++cp -r ~/.config/kylin-background-upgrade/ updaterlog || true ++ ++#激活 ++cp -r ~/.log/kylin-activation/ updaterlog || true ++ ++#收集前端日志 ++cp -r ~/.log/kylin-update-frontend-notifysend.log updaterlog >/dev/null 2>&1 || true ++cp -r ~/.log/ukui-control-center.log updaterlog >/dev/null 2>&1 || true ++cp -r ~/.log/ukui-notification-daemon.log updaterlog >/dev/null 2>&1 || true ++cp -r ~/.config/ukui-session/ updaterlog >/dev/null 2>&1 || true ++cp -r /tmp/kylin-updateresult-notify.log updaterlog >/dev/null 2>&1 || 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日志文件提交到禅道... " +\ No newline at end of file +diff --git a/backend-immutable/tests/setup.cfg b/backend-immutable/tests/setup.cfg +new file mode 100644 +index 0000000..a9eefe5 +--- /dev/null ++++ b/backend-immutable/tests/setup.cfg +@@ -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 +diff --git a/backend-immutable/tests/setup.py b/backend-immutable/tests/setup.py +new file mode 100755 +index 0000000..1824eb0 +--- /dev/null ++++ b/backend-immutable/tests/setup.py +@@ -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 ++ } ++) +diff --git a/backend-immutable/tests/sqlite_tests.py b/backend-immutable/tests/sqlite_tests.py +new file mode 100644 +index 0000000..4a429a2 +--- /dev/null ++++ b/backend-immutable/tests/sqlite_tests.py +@@ -0,0 +1,13 @@ ++from SystemUpdater.Core.Database import Sqlite3Server ++from SystemUpdater.Core.DataAcquisition import get_east_8_time ++ ++class sqliteTests(): ++ def __init__(self): ++ self.sqlite3_server = Sqlite3Server(self) ++ ++ def test_insert_into_display(self): ++ # insert: check_time ++ self.sqlite3_server.insert_into_display("check_time", get_east_8_time()[0:-4]) ++ ++if __name__ == "__main__": ++ sqliteTests().test_insert_into_display() +diff --git a/backend-immutable/tests/test-configs.py b/backend-immutable/tests/test-configs.py +new file mode 100644 +index 0000000..f6ae998 +--- /dev/null ++++ b/backend-immutable/tests/test-configs.py +@@ -0,0 +1,86 @@ ++import json ++from SystemUpdater.configs import JSONConfig ++ ++configs_file = "/tmp/configs1.json" ++configs2_file = "/tmp/configs2.json" ++ ++test_data = { ++ "name": "Alice", ++ "age": 20, ++ "local": { ++ "pre": { ++ "en": "wwwww" ++ } ++ } ++} ++ ++def save(save_file,data): ++ with open(save_file, 'w') as f: ++ json.dump(data, f, indent=4) ++ ++def test_read_config(): ++ save(configs_file, test_data) ++ ++ # 创建副本 ++ test_data["name"] = "Bob" ++ save(configs2_file, test_data) ++ ++ settings = JSONConfig( ++ settings_files=[ ++ configs_file, ++ configs2_file, ++ ] ++ ) ++ # config2 优先级最高 ++ assert settings.get("name") == "Bob" ++ ++ # 测试不存在的配置文件读取 ++ settings = JSONConfig( ++ settings_files=[ ++ configs_file, ++ "/tmp/not-exist.json", ++ ] ++ ) ++ assert settings.get("name") == "Alice" ++ ++ # 不存在的配置项 读取时 走默认 ++ assert settings.get("name1", "default") == "default" ++ ++ # 测试不存在的配置文件读取 ++ settings = JSONConfig( ++ settings_files=[ ++ "/tmp/not-exist1.json", ++ "/tmp/not-exist.json", ++ ] ++ ) ++ assert settings.get("name", "default") == "default" ++ ++def test_parse_value(): ++ save(configs_file, test_data) ++ ++ # 创建副本 ++ test_data["local"]["pre"]["en"] = "xxxxx" ++ save(configs2_file, test_data) ++ ++ settings = JSONConfig( ++ settings_files=[ ++ configs_file, ++ configs2_file, ++ ] ++ ) ++ assert settings.get("local",{}).get("pre",{}).get("en") == "xxxxx" ++ # 不存在的配置为默认 ++ assert settings.get("loca1l",{}).get("pre",{}).get("zh","default") == "default" ++ ++ #多级赋值 ++ localsd = settings.get("local",{}) ++ localsd["pre"]["en"] = "yyyyy" ++ ++ assert settings.get("local",{}).get("pre",{}).get("en") == "yyyyy" ++ ++ settings["me"] = "zzzzz" ++ # assert settings.me == "zzzzz" ++ ++test_read_config() # 通过 ++ ++test_parse_value() # 通过 +\ No newline at end of file +diff --git a/backend-immutable/tests/test-for-frontend.sh b/backend-immutable/tests/test-for-frontend.sh +new file mode 100755 +index 0000000..41a46fd +--- /dev/null ++++ b/backend-immutable/tests/test-for-frontend.sh +@@ -0,0 +1,45 @@ ++#!/bin/bash ++updater_program=$(pwd)/../kylin-system-updater ++ ++. ./libtest.sh ++ ++fn_simulate_ostree_system() { ++ # 模拟ostree系统的环境 进行测试 ++ # 主要做的: ++ # - 初始化一个ostree仓库:testos-repo ++ # - 在osdata目录创建模拟一些系统文件 and 提交两次到testos-repo 仓库 ++ # - 复制osdata数据到osdata-devel,做出修改然后再次提交 ++ # - 用init-fs创建sysroot 和 os-init 和 syslinux ++ # - 提交一个新的commit用空目录,标记初始分支($BUILD_MASTER_BRACH)为EOL 重新定向到新分支(testos/buildmaster/newbranch) ++ ++ # 创建ostree仓库 仓库类型 and boot类型 包括默认开启一个远程 ++ setup_os_repository "archive" "syslinux" ++ ++ rm $PUSH_FILE | true ++ ++ cd ${test_tmpdir} ++ ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo remote add --set=gpg-verify=false testos $(cat httpd-address)/ostree/testos-repo ++ ++ ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo pull testos $BUILD_MASTER_BRACH > /dev/null ++ rev=$(${CMD_PREFIX} ostree --repo=sysroot/ostree/repo rev-parse $BUILD_MASTER_BRACH) ++ echo "拉取分支:$BUILD_MASTER_BRACH 从远程仓库testos-repo and revision=${rev}" ++ ++ echo "部署分支:$BUILD_MASTER_BRACH" ++ ${CMD_PREFIX} ostree admin deploy --karg=root=LABEL=MOO --karg=quiet --os=testos testos:$BUILD_MASTER_BRACH > /dev/null ++ assert_has_dir sysroot/boot/ostree/testos-${bootcsum} ++} ++ ++fn_simulate_ostree_system ++ ++# 启动系统更新服务 ++$updater_program -r -d --sysroot=$test_tmpdir/sysroot --os=testos & ++ ++new_brach="testos/buildmaster/newbranch1" ++os_repository_new_commit 1 1 $new_brach ++ ++echo -n "当前系统分支:$BUILD_MASTER_BRACH,配置切换到新分支$new_brach revision:" ++ ++mkdir empty ++${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo commit --tree=dir=$(pwd)/empty --add-metadata-string "ostree.endoflife-rebase=testos/buildmaster/newbranch1" -b testos/buildmaster/newbranch -s "EOL redirect to new branch" ++ ++sleep 10000000 +\ No newline at end of file +diff --git a/backend-immutable/tests/test-offline-upgrade.sh b/backend-immutable/tests/test-offline-upgrade.sh +new file mode 100755 +index 0000000..722c153 +--- /dev/null ++++ b/backend-immutable/tests/test-offline-upgrade.sh +@@ -0,0 +1,202 @@ ++#!/bin/bash ++ ++. ./libtest.sh ++ ++test_delta_upgrade_progress() { ++ rm -rf files repo temp-repo out.txt tmpsubdir repo2 deltadir repo3 deltadir | true ++ bindatafiles="bash true ostree" ++ morebindatafiles="false ls" ++ ++ # 创建开始的仓库 ++ mkdir repo ++ ostree_repo_init repo --mode=archive ++ ++ # 创建一些测试文件 ++ mkdir files ++ for bin in ${bindatafiles}; do ++ cp $(which ${bin}) files ++ done ++ ++ # ----------------------------------------------服务端仓库 远程仓库------------------------------------------- ++ # 进行第一次提交 commit 到仓库 ++ ${CMD_PREFIX} ostree --repo=repo commit -b test -s test --tree=dir=files ++ origrev=$(${CMD_PREFIX} ostree --repo=repo rev-parse test) ++ ++ # 进行第二次提交 到仓库 ++ cp /usr/bin/apt-get files ++ ${CMD_PREFIX} ostree --repo=repo commit -b test -s "原始版本" --tree=dir=files ++ newrev=$(${CMD_PREFIX} ostree --repo=repo rev-parse test) ++ ++ # 第三次提交 到仓库 ++ cp /usr/bin/bash files ++ ${CMD_PREFIX} ostree --repo=repo commit --add-metadata-string=update-name="dsdsdd" -b test1 -s "最新更新" --tree=dir=files ++ sedrev=$(${CMD_PREFIX} ostree --repo=repo rev-parse test1) ++ ++ ${CMD_PREFIX} ostree --repo=repo static-delta generate --from=${origrev} --to=${newrev} ++ # 产生delta文件 ++ ${CMD_PREFIX} ostree --repo=repo static-delta generate --from=${newrev} --to=${sedrev} ++ ++ ${CMD_PREFIX} ostree --repo=repo summary -u ++ ++ # ---------------------------------------------创建一个空仓库 + 离线数据----------------------------------------- ++ mkdir repo2 && ostree_repo_init repo2 --mode=archive ++ ++ ${CMD_PREFIX} ostree --repo=repo2 remote add --set=gpg-verify=false repo file://$(pwd)/repo ++ ++ # 拉取将要升级的源数据 ++ ${CMD_PREFIX} ostree --repo=repo2 pull --depth=-1 --commit-metadata-only repo test ++ ${CMD_PREFIX} ostree --repo=repo2 pull --depth=-1 --commit-metadata-only repo test1 ++ ++ # 放置delta差分数据 ++ cp -r repo/deltas repo2/ ++ ++ # 复制服务都的summary文件 因为服务端包含完整的commit和分支数据 ++ cp -r repo/summary repo2/ ++ # ${CMD_PREFIX} ostree --repo=repo2 summary -u ++ ++ #-----------------------------------------------本地测试设备------------------------------------------------ ++ # 创建空仓库 ++ mkdir repo3 && ostree_repo_init repo3 --mode=bare ++ ++ # 模拟 从服务端仓库拉取最开始的版本 ++ ${CMD_PREFIX} ostree --repo=repo3 remote add --set=gpg-verify=false repo file://$(pwd)/repo ++ ${CMD_PREFIX} ostree --repo=repo3 pull repo test@${origrev} ++ ${CMD_PREFIX} ostree --repo=repo3 remote delete repo ++ rm -rf repo ++ ++ # 配置本地仓库连接到 u盘源仓库 ++ ${CMD_PREFIX} ostree --repo=repo3 remote add --set=gpg-verify=false repo file://$(pwd)/repo2 ++ ++ # 检查更新阶段 ++ # ${CMD_PREFIX} ostree --repo=repo3 pull --depth=-1 --commit-metadata-only repo test ++ # ${CMD_PREFIX} ostree --repo=repo3 pull --depth=-1 --commit-metadata-only repo test1 ++ ++ # 下载阶段 ++ ${CMD_PREFIX} ostree --repo=repo3 pull --require-static-deltas --depth=-1 repo test@${newrev} ++ ${CMD_PREFIX} ostree --repo=repo3 pull --require-static-deltas --depth=-1 repo test1@${sedrev} ++ ++ echo "-------------" ++ ++ ${CMD_PREFIX} ostree --repo=repo3 log repo:test ++ echo "-------------" ++} ++ ++ ++test_delta_upgrade() { ++ fn_simulate_ostree_system ++ ++ OS_DATA="/tmp/ostree-auto-test/osdata" ++ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo static-delta generate --to=$BUILD_MASTER_BRACH ++ # 服务器 提交新的可升级补丁 ++ cp /usr/bin/bash $OS_DATA ++ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo commit $metadata_string --add-metadata-string new_version=1.0.11 -b $BUILD_MASTER_BRACH -s "Build" --tree=dir=$OS_DATA ++ newrev=$(${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo rev-parse $BUILD_MASTER_BRACH) ++ ++ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo static-delta generate --to=$BUILD_MASTER_BRACH ++ ++ cp /usr/bin/apt-get $OS_DATA ++ echo "1" > $OS_DATA/usr/bin/content-iteration ++ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo commit $metadata_string --add-metadata-string new_version=1.0.11 -b $BUILD_MASTER_BRACH -s "Build" --tree=dir=$OS_DATA ++ sedrev=$(${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo rev-parse $BUILD_MASTER_BRACH) ++ ++ # 产生delta文件 ++ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo static-delta generate --to=$BUILD_MASTER_BRACH ++ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo summary -u ++ ++ # ---------------------------------------------构建离线更新源----------------------------------------- ++ mkdir ${test_tmpdir}/offline-repo && ostree_repo_init ${test_tmpdir}/offline-repo --mode=archive ++ ++ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/offline-repo remote add --set=gpg-verify=false testos file://${test_tmpdir}/testos-repo ++ ++ # 拉取将要升级的源数据 需要拉取所有的元数据 ++ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/offline-repo pull --depth=-1 --commit-metadata-only testos $BUILD_MASTER_BRACH ++ ++ # 防止源数据 ++ cp -r ${test_tmpdir}/testos-repo/deltas ${test_tmpdir}/offline-repo/ ++ ++ cp ${test_tmpdir}/testos-repo/summary ${test_tmpdir}/offline-repo/ ++ # ---------------------------------------------------------------------------------------------------- ++ ++ ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo remote delete testos ++ ++ ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo remote add --set=gpg-verify=false testos file://${test_tmpdir}/offline-repo ++ ++ echo "testos:${BUILD_MASTER_BRACH}@${sedrev}" > $PUSH_FILE ++ ++ echo "" ++ echo "检查更新" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$update_method > /dev/null ++ moniter_signal_success $update_finished_signal ++ ++ echo "部署下载" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$download_method > /dev/null ++ moniter_signal_success $download_finished_signal ++ ++ echo "部署内容" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$deploy_method > /dev/null ++ moniter_signal_success $deploy_finished_signal ++ ++ # 部署完成后的检查 ++ assert_file_has_content sysroot/boot/loader/entries/ostree-2-testos.conf "${bootcsum}" ++ rev=$(${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo rev-parse $BUILD_MASTER_BRACH) ++ assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.0/usr/bin/content-iteration "1" ++ assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.0.origin "newbranch" ++} ++ ++ ++test_all_upgrade() { ++ fn_simulate_ostree_system ++ ++ OS_DATA="/tmp/ostree-auto-test/osdata" ++ ++ cp /usr/bin/bash $OS_DATA ++ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo commit $metadata_string --add-metadata-string new_version=1.0.11 -b $BUILD_MASTER_BRACH -s "Build" --tree=dir=$OS_DATA ++ newrev=$(${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo rev-parse $BUILD_MASTER_BRACH) ++ ++ cp /usr/bin/apt-get $OS_DATA ++ echo "1" > $OS_DATA/usr/bin/content-iteration ++ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo commit $metadata_string --add-metadata-string new_version=1.0.11 -b $BUILD_MASTER_BRACH -s "Build" --tree=dir=$OS_DATA ++ sedrev=$(${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo rev-parse $BUILD_MASTER_BRACH) ++ ++ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo summary -u ++ ++ # ---------------------------------------------构建离线更新源----------------------------------------- ++ mkdir ${test_tmpdir}/offline-repo && ostree_repo_init ${test_tmpdir}/offline-repo --mode=archive ++ ++ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/offline-repo remote add --set=gpg-verify=false testos file://${test_tmpdir}/testos-repo ++ ++ # 拉取将要升级的源数据 需要拉取所有的元数据 ++ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/offline-repo pull --depth=1 testos $BUILD_MASTER_BRACH@${sedrev} ++ ++ # cp ${test_tmpdir}/testos-repo/summary ${test_tmpdir}/offline-repo/ ++ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/offline-repo summary -u ++ # ---------------------------------------------------------------------------------------------------- ++ ++ ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo remote delete testos ++ ++ ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo remote add --set=gpg-verify=false testos file://${test_tmpdir}/offline-repo ++ ++ echo "testos:${BUILD_MASTER_BRACH}@${sedrev}" > $PUSH_FILE ++ ++ echo "" ++ echo "检查更新" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$update_method > /dev/null ++ moniter_signal_success $update_finished_signal ++ ++ echo "部署下载" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$download_method > /dev/null ++ moniter_signal_success $download_finished_signal ++ ++ echo "部署内容" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$deploy_method > /dev/null ++ moniter_signal_success $deploy_finished_signal ++ ++ # 部署完成后的检查 ++ assert_file_has_content sysroot/boot/loader/entries/ostree-2-testos.conf "${bootcsum}" ++ rev=$(${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo rev-parse $BUILD_MASTER_BRACH) ++ assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.0/usr/bin/content-iteration "1" ++ assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.0.origin "newbranch" ++} ++ ++test_delta_upgrade ++test_all_upgrade +\ No newline at end of file +diff --git a/backend-immutable/tests/test-pull.sh b/backend-immutable/tests/test-pull.sh +new file mode 100755 +index 0000000..0710028 +--- /dev/null ++++ b/backend-immutable/tests/test-pull.sh +@@ -0,0 +1,39 @@ ++#!/bin/bash ++ ++. ./libtest.sh ++ ++test_large_os_download() { ++ fn_simulate_ostree_system ++ ++ info "测试下载超大文件 3.0G以上,花费时间较长 主要花费时间在commit内容上" ++ ++ dd if=/dev/urandom of=${test_tmpdir}/osdata/random_data.bin bs=1G count=100 ++ new_brach="testos/buildmaster/newbranch1" ++ os_repository_new_commit 1 1 $new_brach ++ ++ mkdir empty ++ echo -n "当前系统分支:$BUILD_MASTER_BRACH,配置切换到新分支$new_brach revision:" ++ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo commit --tree=dir=$(pwd)/empty --add-metadata-string "ostree.endoflife-rebase=testos/buildmaster/newbranch1" -b testos/buildmaster/newbranch -s "EOL redirect to new branch" ++ ++ echo "" ++ echo "检查更新" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$update_method > /dev/null ++ moniter_signal_success $update_finished_signal ++ ++ echo "部署下载" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$download_method > /dev/null ++ moniter_signal_success $download_finished_signal ++ ++ echo "部署内容" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$deploy_method > /dev/null ++ moniter_signal_success $deploy_finished_signal ++ ++ # 部署完成后的检查 ++ assert_file_has_content sysroot/boot/loader/entries/ostree-2-testos.conf "${bootcsum}" ++ rev=$(${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo rev-parse $new_brach) ++ assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.0/usr/bin/content-iteration "1" ++ ++ assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.0.origin "newbranch" ++} ++ ++test_large_os_download +\ No newline at end of file +diff --git a/backend-immutable/tests/test-push-update.sh b/backend-immutable/tests/test-push-update.sh +new file mode 100755 +index 0000000..feabe2d +--- /dev/null ++++ b/backend-immutable/tests/test-push-update.sh +@@ -0,0 +1,201 @@ ++#!/bin/bash ++ ++. ./libtest.sh ++ ++test_only_branch() { ++ fn_simulate_ostree_system ++ ++ new_brach="testos/buildmaster/newbranch1" ++ os_repository_new_commit 1 1 $new_brach ++ push_content="testos:${new_brach}" ++ ++ info "测试:推送更新内容,仅包含分支信息,推送内容:$push_content" ++ ++ echo "${push_content}" > $PUSH_FILE ++ ++ echo "检查更新" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$update_method > /dev/null ++ moniter_signal_success $update_finished_signal ++ ++ echo "部署下载" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$download_method > /dev/null ++ moniter_signal_success $download_finished_signal ++ ++ ++ echo "部署内容" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$deploy_method > /dev/null ++ moniter_signal_success $deploy_finished_signal ++ ++ # 部署完成后的检查 ++ assert_file_has_content sysroot/boot/loader/entries/ostree-2-testos.conf "${bootcsum}" ++ rev=$(${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo rev-parse $new_brach) ++ assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.0/usr/bin/content-iteration "1" ++ ++ assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.0.origin "newbranch" ++} ++ ++test_new_branch_and_commit() { ++ fn_simulate_ostree_system ++ new_brach="testos/buildmaster/newbranch1" ++ os_repository_new_commit 1 1 $new_brach ++ newrev=$(${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo rev-parse $new_brach) ++ ++ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo commit $metadata_string --add-metadata-string new_version=1.0.11 -b $BUILD_MASTER_BRACH -s "Build" --tree=dir=$OS_DATA ++ ++ push_content="testos:${new_brach}@$newrev" ++ ++ info "测试:推送更新内容:$push_content" ++ ++ echo "$push_content" > $PUSH_FILE ++ ++ echo "检查更新" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$update_method > /dev/null ++ moniter_signal_success $update_finished_signal ++ ++ echo "部署下载" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$download_method > /dev/null ++ moniter_signal_success $download_finished_signal ++ ++ echo "部署内容" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$deploy_method > /dev/null ++ moniter_signal_success $deploy_finished_signal ++ ++ # 部署完成后的检查 ++ assert_file_has_content sysroot/boot/loader/entries/ostree-2-testos.conf "${bootcsum}" ++ # rev=$(${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo rev-parse $new_brach) ++ assert_file_has_content sysroot/ostree/deploy/testos/deploy/${newrev}.0/usr/bin/content-iteration "1" ++ ++ assert_file_has_content sysroot/ostree/deploy/testos/deploy/${newrev}.0.origin "newbranch" ++} ++ ++test_new_branch_and_old_commit() { ++ echo "测试部署新分支 并且提交的commit在" ++ fn_simulate_ostree_system ++ new_brach="testos/buildmaster/newbranch1" ++ ++ # 再次提交保持不是最新的 ++ os_repository_new_commit 1 1 $new_brach ++ newrev=$(${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo rev-parse $new_brach) ++ ++ os_repository_new_commit 1 1 $new_brach ++ push_content="testos:${new_brach}@$newrev" ++ ++ info "测试:推送更新内容:$push_content" ++ ++ echo "$push_content" > $PUSH_FILE ++ ++ ++ echo "检查更新" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$update_method > /dev/null ++ moniter_signal_success $update_finished_signal ++ ++ ++ echo "部署下载" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$download_method > /dev/null ++ moniter_signal_success $download_finished_signal ++ ++ echo "部署内容" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$deploy_method > /dev/null ++ moniter_signal_success $deploy_finished_signal ++ ++ # 部署完成后的检查 ++ assert_file_has_content sysroot/boot/loader/entries/ostree-2-testos.conf "${bootcsum}" ++ # rev=$(${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo rev-parse $new_brach) ++ assert_file_has_content sysroot/ostree/deploy/testos/deploy/${newrev}.0/usr/bin/content-iteration "1" ++ ++ assert_file_has_content sysroot/ostree/deploy/testos/deploy/${newrev}.0.origin "newbranch" ++} ++ ++ ++ ++test_old_branch_and_commit() { ++ fn_simulate_ostree_system ++ ++ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo commit $metadata_string --add-metadata-string new_version=1.0.11 -b $BUILD_MASTER_BRACH -s "Build" --tree=dir=$OS_DATA ++ newrev=$(${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo rev-parse $BUILD_MASTER_BRACH) ++ ++ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo commit $metadata_string --add-metadata-string new_version=1.0.12 -b $BUILD_MASTER_BRACH -s "Build" --tree=dir=$OS_DATA ++ ++ push_content="testos:${BUILD_MASTER_BRACH}@$newrev" ++ info "测试分支@commit号,例如推送更新内容:$push_content" ++ ++ echo $push_content > $PUSH_FILE ++ ++ echo "检查更新" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$update_method > /dev/null ++ moniter_signal_success $update_finished_signal ++ ++ echo "部署下载" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$download_method > /dev/null ++ moniter_signal_success $download_finished_signal ++ ++ echo "部署内容" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$deploy_method > /dev/null ++ moniter_signal_success $deploy_finished_signal ++ ++ # 部署完成后的检查 ++ # rev=$(${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo rev-parse $new_brach) ++ assert_file_has_content sysroot/ostree/deploy/testos/deploy/${newrev}.0/usr/bin/sh "executable" ++ assert_file_has_content sysroot/ostree/deploy/testos/deploy/${newrev}.0.origin "newbranch" ++} ++test_on_push_update() { ++ info "测试 没有推送更新文件的情况并且不切换分支,直接提示最新的。" ++ if [ -f "$PUSH_FILE" ]; then ++ rm $PUSH_FILE | true ++ fi ++ ++ fn_simulate_ostree_system ++ ++ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo commit $metadata_string --add-metadata-string new_version=1.0.11 -b $BUILD_MASTER_BRACH -s "Build" --tree=dir=$OS_DATA ++ newrev=$(${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo rev-parse $BUILD_MASTER_BRACH) ++ ++ echo "" ++ echo "测试:推送更新内容,升级到当前分支 $BUILD_MASTER_BRACH @$newrev" ++ ++ echo "" ++ echo "检查更新" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$update_method > /dev/null ++ ++ moniter_string "$update_finished_signal success = True , upgrade_group = \[\]" ++} ++test_exeption_push() { ++ fn_simulate_ostree_system ++ ++ new_brach="testos/buildmaster/newbranch1" ++ os_repository_new_commit 1 1 $new_brach ++ push_content="${new_brach}" ++ ++ info "测试:推送更新内容异常情况,推送内容:$push_content" ++ ++ echo "${push_content}" > $PUSH_FILE ++ ++ echo "检查更新" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$update_method > /dev/null ++ ++ # 提示为最新 ++ moniter_string "$update_finished_signal success = True , upgrade_group = \[\]" ++ ++ info "测试:推送错误的分支。" ++ push_content="testos:dddd" ++ echo "${push_content}" > $PUSH_FILE ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$update_method > /dev/null ++ moniter_string "$update_finished_signal success = False , upgrade_group = \[''\], error_code = #0003" ++ ++ # info "测试:推送错误的commit号。" ++ # push_content="testos:${new_brach}@dsdsdsdsdsds" ++ # echo "${push_content}" > $PUSH_FILE ++ # gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$update_method > /dev/null ++ # moniter_string "$update_finished_signal success = False , upgrade_group = \[''\]" ++} ++ ++test_on_push_update ++ ++test_only_branch ++ ++test_new_branch_and_commit ++ ++test_old_branch_and_commit ++ ++test_exeption_push ++ ++test_new_branch_and_old_commit +\ No newline at end of file +diff --git a/backend-immutable/tests/test-upgrade-endoflife.sh b/backend-immutable/tests/test-upgrade-endoflife.sh +new file mode 100755 +index 0000000..732b514 +--- /dev/null ++++ b/backend-immutable/tests/test-upgrade-endoflife.sh +@@ -0,0 +1,54 @@ ++#!/bin/bash ++ ++. ./libtest.sh ++ ++test_deploy_limit() { ++ ++ fn_simulate_ostree_system ++ if [ -f "$PUSH_FILE" ]; then ++ rm $PUSH_FILE | true ++ fi ++ ++ for ((i=1; i<=$1; i++)) ++ do ++ y=$((i - 1)) ++ ++ if [ $y -eq 0 ]; then ++ y="" ++ fi ++ echo "" ++ echo "测试:从当前分支切换到新分支部署升级" ++ ++ new_brach="testos/buildmaster/newbranch$i" ++ os_repository_new_commit 1 1 $new_brach ++ ++ mkdir empty$i ++ echo -n "当前系统分支:$BUILD_MASTER_BRACH,配置切换到新分支$new_brach revision:" ++ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo commit --tree=dir=$(pwd)/empty$i --add-metadata-string "ostree.endoflife-rebase=testos/buildmaster/newbranch$i" -b testos/buildmaster/newbranch$y -s "EOL redirect to new branch" ++ ++ echo "" ++ echo "Ostree 测试环境创建成功, 开始系统更新功能测试" ++ echo "" ++ ++ echo "检查更新" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$update_method > /dev/null ++ moniter_signal_success $update_finished_signal ++ ++ echo "部署下载" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$download_method > /dev/null ++ moniter_signal_success $download_finished_signal ++ ++ echo "部署内容" ++ gdbus call --system --dest $bus_name --object-path $object_path --method $interface_name.$deploy_method > /dev/null ++ moniter_signal_success $deploy_finished_signal ++ ++ # 部署完成后的检查 ++ assert_file_has_content sysroot/boot/loader/entries/ostree-2-testos.conf "${bootcsum}" ++ rev=$(${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo rev-parse $new_brach) ++ assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.0/usr/bin/content-iteration "1" ++ ++ assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.0.origin "newbranch" ++ done ++} ++ ++test_deploy_limit 1 diff -Nru kylin-system-updater-3.0.0.0/debian/patches/series kylin-system-updater-3.0.0.0/debian/patches/series --- kylin-system-updater-3.0.0.0/debian/patches/series 2024-11-27 13:56:00.000000000 +0800 +++ kylin-system-updater-3.0.0.0/debian/patches/series 2025-02-25 14:13:19.000000000 +0800 @@ -23,3 +23,4 @@ 0023-changelog-3.0.0.0-ok29.patch 0024-2.patch 0025-3.0.0.0-ok32.patch +0026-changelog-3.0.0.0-ok33.patch