#!/usr/bin/python3

import dbus
from dbus.mainloop.glib import DBusGMainLoop
from gi.repository import GLib
import logging
import os
import signal
from datetime import datetime, timedelta
import time
import random
import configparser
from apscheduler.schedulers.background import BackgroundScheduler
from threading import Event 
from gettext import gettext as _
import gettext
import getpass
import subprocess
import re
import json
from typing import List, Dict
from dbus.types import Int32, Int64, String, Boolean, Double, Array,Dictionary
from dbus import service

UNATTENDED_UPGRADE_TIMESTAMP = "/var/lib/unattended-upgrades/unattended-upgrades-timestamp"
UNATTENDED_UPGRADE_POLICY_FILE_PATH="/var/lib/unattended-upgrades/unattended-upgrades-policy.conf"

ACTION_DOWNLOADONLY = 1
        
def signal_term_handler(signal,frame):
    # type: (int, object) -> None
    logging.warning("SIGTERM received, will stop")
    os._exit(1)

def ReadValueFromFile(file,section,option):
    config=configparser.ConfigParser(allow_no_value=True)
    config.optionxform = str
    try:
        config.read(file)
        value = config[section][option]
        return value
    except Exception as e:
        logging.error(_("read config file error:%s")%e)
        return None

def WriteValueToFile(file,section,option,value):
    config=configparser.ConfigParser(allow_no_value=True)
    config.optionxform = str
    config.add_section(section)
    config.set(section,option,value)
    config.write(open(file,"w"))   

def UpdateTimeStamp(task_id,last_run_time):
    logging.debug(_("update timestamp:%s %s")%(task_id,last_run_time))
    if os.path.exists(UNATTENDED_UPGRADE_TIMESTAMP):
        config=configparser.ConfigParser(allow_no_value=True)
        config.optionxform = str
        config.read(UNATTENDED_UPGRADE_TIMESTAMP)
        if 'TimeStamp' in config.sections():
            config.set('TimeStamp',task_id,last_run_time)  
            with open(UNATTENDED_UPGRADE_TIMESTAMP,'w') as f:
                config.write(f)


def ini_to_json(ini_path: str) -> dict:
    """
    将INI配置文件转换为JSON格式的字典对象
    
    Args:
        ini_path: INI文件路径
        
    Returns:
        dict: 转换后的JSON格式字典
        
    Raises:
        FileNotFoundError: 当INI文件不存在时抛出
        ValueError: 当文件格式错误时抛出
    """
    # 初始化配置解析器
    config = configparser.ConfigParser()
    
    # 读取INI文件
    if not config.read(ini_path):
        raise FileNotFoundError(f"文件 {ini_path} 未找到")
    
    # 构建嵌套字典
    result = {}
    for section in config.sections():
        result[section] = dict(config.items(section))  # 转换section为字典结构
        
    return result

def json_to_asv(data):
    """
    将 JSON 字典转换为 D-Bus a{sv} 类型
    参数：
        data: dict - 输入字典（需确保键为字符串）
    返回：
        dbus.Dictionary - 符合 a{sv} 签名的 D-Bus 字典
    异常：
        ValueError - 输入类型错误或数据格式不合法
        TypeError - 存在不支持的类型
    """
    def _convert_value(value):
        # 递归处理嵌套结构
        if isinstance(value, dict):
            return Dictionary(
                {k: _convert_value(v) for k, v in value.items()},
                signature=dbus.Signature('sv'),
                variant_level=1  # 隐式创建变体层
            )
        elif isinstance(value, list):
            return Array(
                [_convert_value(v) for v in value],
                signature=dbus.Signature('v'),
                variant_level=1
            )
        elif isinstance(value, bool):
            return Boolean(value)
        elif isinstance(value, int):
            return Int64(value) if abs(value) > 0x7FFFFFFF else Int32(value)
        elif isinstance(value, float):
            return Double(value)
        elif isinstance(value, str):
            return String(value)
        else:
            raise TypeError(f"不支持的类型: {type(value)}")

    try:
        # 输入类型验证
        if not isinstance(data, dict):
            raise ValueError("输入必须是字典类型")
        
        # 键类型验证（需全部为字符串）
        if not all(isinstance(k, str) for k in data.keys()):
            raise ValueError("字典键必须是字符串")
        
        return Dictionary(
            {k: _convert_value(v) for k, v in data.items()},
            signature=dbus.Signature('sv'),
            variant_level=1
        )
    except (TypeError, ValueError) as e:
        raise e
    except Exception as e:
        raise RuntimeError(f"未知错误: {str(e)}") from e
    
def process_time_intervals(data: List[Dict], max_random_minutes: int) -> List[Dict]:
    """
    合并重叠时间区间并生成随机调整后的时间段
    :param data: 输入时间段列表 [{"start":"HH:MM","end":"HH:MM"}]
    :param max_random_minutes: 最大随机偏移分钟数
    :return: 合并调整后的时间段列表
    """
    # 时间转换函数（支持跨天时间）
    def time_to_minutes(t: str) -> int:
        h, m = map(int, t.split(':'))
        return h * 60 + m

    # 分钟数转时间（支持跨天显示）
    def minutes_to_time(m: int) -> str:
        total_m = m % 1440
        return f"{total_m//60:02d}:{total_m%60:02d}"

    # 区间合并算法
    intervals = []
    for period in data:
        start = time_to_minutes(period["start"])
        end = time_to_minutes(period["end"])
        if end < start:  # 处理跨天情况
            intervals.append((start, end + 1440))
        else:
            intervals.append((start, end))

    # 排序并合并
    intervals.sort(key=lambda x: x[0])
    merged = []
    for current in intervals:
        if not merged:
            merged.append(list(current))
        else:
            last = merged[-1]
            if current[0] <= last[1]:
                last[1] = max(last[1], current[1])
            else:
                merged.append(list(current))

    # 生成随机调整后的时间段
    result = []
    for start, end in merged:
        # 计算有效时间跨度（考虑跨天）
        total_minutes = end - start
        random_offset = random.randint(0, min(max_random_minutes, total_minutes))

        # 生成新开始时间
        new_start = (start + random_offset) % 1440

        # 处理跨天显示逻辑
        original_end = end % 1440 if end > 1440 else end
        result.append({
            "start": minutes_to_time(new_start),
            "end": minutes_to_time(original_end)
        })

    return result

def process_timelist(timelist: List[Dict]) -> List[Dict]:
    """处理跨天时间段并生成标准化输出

    Args:
        timelist: 时间区间列表，示例 [{"start":"23:00","end":"11:00"},...]

    Returns:
        包含小时、分钟和超时时间的字典列表，示例 [{"hour":23,"minute":0,"timeout":720}]
    """
    def time_to_minutes(t: str) -> int:
        """将HH:MM转换为分钟数"""
        hours, minutes = map(int, t.split(':'))
        return hours * 60 + minutes

    result = []

    for period in timelist:
        # 解析起止时间
        start = time_to_minutes(period["start"])
        end = time_to_minutes(period["end"])

        # 处理跨天情况
        if end < start:
            end += 1440  # 24小时分钟数

        # 计算超时分钟数
        timeout = end - start

        # 提取小时和分钟
        start_h, start_m = divmod(start, 60)

        result.append({
            "hour": start_h,
            "minute": start_m,
            "timeout": timeout
        })

    return result

def convert_a_sv_to_json(a_sv_data):
    """
    将DBus的a{sv}类型数据转换为JSON对象
    
    Args:
        a_sv_data (dbus.Dictionary): D-Bus类型的a{sv}数据（字典格式）
    
    Returns:
        str: JSON格式字符串
    """
    def _parse_variant(value):
        # 递归解析变体类型中的嵌套数据
        if isinstance(value, dbus.Dictionary):
            return {k: _parse_variant(v) for k, v in value.items()}
        elif isinstance(value, dbus.Array):
            return [_parse_variant(v) for v in value]
        elif isinstance(value, dbus.Struct):
            return tuple(_parse_variant(v) for v in value)
        elif isinstance(value, (dbus.String, dbus.ObjectPath)):
            return str(value)
        elif isinstance(value, dbus.Boolean):
            return bool(value)
        elif isinstance(value, (dbus.Int16, dbus.Int32, dbus.Int64)):
            return int(value)
        elif isinstance(value, (dbus.Double, dbus.UInt16, dbus.UInt32, dbus.UInt64)):
            return float(value) if '.' in str(value) else int(value)
        else:
            return value

    # 转换主逻辑
    json_dict = {}
    for key, variant in a_sv_data.items():
        json_dict[key] = _parse_variant(variant)
    
    return json.dumps(json_dict, indent=2)

def parse_time(time_str: str) -> dict:
    """将HH:MM格式的时间字符串解析为字典
    
    Args:
        time_str: 时间字符串，支持中文/英文冒号，如"20:00"或"20：00"
        
    Returns:
        包含小时和分钟的字典，示例: {'hour':20, 'minute':0}
        
    Raises:
        ValueError: 当格式无效或数值越界时抛出
    """
    # 预处理字符串
    normalized_str = time_str.replace('：', ':')  # 中文冒号转英文
    parts = normalized_str.split(':')
    
    # 格式验证
    if len(parts) != 2:
        raise ValueError(f"无效时间格式: {time_str}，应为HH:MM")
    
    try:
        hour = int(parts[0])
        minute = int(parts[1])
    except ValueError:
        raise ValueError(f"非数字时间值: {time_str}")
    
    # 数值范围验证
    if not (0 <= hour <= 23):
        raise ValueError(f"小时值越界: {hour}，应为0-23")
    if not (0 <= minute <= 59):
        raise ValueError(f"分钟值越界: {minute}，应为0-59")
    
    return {'hour': hour, 'minute': minute}

def parse_time_range(time_str: str) -> dict:
    """
    解析时间字符串并生成时间范围字典
    
    参数：
    time_str - 输入时间字符串，支持三种格式：
               "HH:MM"、"HH:MM:SS"、"HH:MM-HH:MM"
    
    返回：
    {'start': 'HH:MM', 'end': 'HH:MM'}
    
    异常：
    ValueError - 当输入格式无效或时间值越界时抛出
    """
    # 格式验证正则表达式
    time_pattern = r'^([0-2][0-9]:[0-5][0-9])(?::[0-5][0-9])?(?:-([0-2][0-9]:[0-5][0-9]))?$'
    
    if match := re.match(time_pattern, time_str):
        start_time, end_time = match.groups()
        
        # 处理第三种格式（时间区间）
        if end_time:
            return validate_and_format(start_time, end_time)
        
        # 处理前两种格式（单个时间）
        base_time = time_str[:5]  # 截取HH:MM部分
        calculated_end = calculate_end_time(base_time)
        return {'start': base_time, 'end': calculated_end}
    
    raise ValueError(f"无效时间格式：{time_str}")

def validate_and_format(start: str, end: str) -> dict:
    """验证并格式化时间区间"""
    try:
        # 时间有效性验证
        datetime.strptime(start, '%H:%M')
        datetime.strptime(end, '%H:%M')
        return {'start': start, 'end': end}
    except ValueError as e:
        raise ValueError(f"无效时间值：{str(e)}")

def calculate_end_time(base: str) -> str:
    """计算3小时后时间（处理跨天）"""
    try:
        # 时间解析与计算
        base_dt = datetime.strptime(base, '%H:%M')
        end_dt = base_dt + timedelta(hours=12)
        return end_dt.strftime('%H:%M')
    except ValueError as e:
        raise ValueError(f"无效基准时间：{str(e)}")

class NotifyService(service.Object):
    def __init__(self):
        # 连接到会话总线并注册对象路径
        bus = dbus.SystemBus()
        service.Object.__init__(self,bus, '/com/kylin/notifysend')

    @dbus.service.signal(dbus_interface='com.kylin.notifysend', 
                        signature='is')  # 参数类型定义为int
    def RebootNotify(self, status_code,status_description):
        """自定义信号声明：参数为整数状态码"""
        pass  # 函数体为空，通过调用该方法触发信号

    @dbus.service.signal(dbus_interface='com.kylin.notifysend', 
                        signature='is')  # 参数类型定义为int
    def InstallFinishNotify(self, status_code,status_description):
        """自定义信号声明：参数为整数状态码"""
        pass  # 函数体为空，通过调用该方法触发信号

    @dbus.service.signal(dbus_interface='com.kylin.notifysend', 
                        signature='b')  # 参数类型定义为bool
    def ConnectDistUpgrade(self, status):
        """自定义信号声明：参数为是否连接备份和下载完成信号状态码"""
        pass  # 函数体为空，通过调用该方法触发信号

class UnattendedUpgradesShutdown():
    def __init__(self) -> None:
        DBusGMainLoop(set_as_default=True)
        self.loop = GLib.MainLoop()
        self.system_bus = dbus.SystemBus()
        self.update_utils_proxy = self.system_bus.get_object('com.kylin.systemupgrade','/com/kylin/systemupgrade/utils')
        self.update_utils_interface = dbus.Interface(self.update_utils_proxy,dbus_interface='com.kylin.systemupgrade.interface')
        self.update_proxy = self.system_bus.get_object('com.kylin.systemupgrade','/com/kylin/systemupgrade',follow_name_owner_changes=True)
        self.update_interface = dbus.Interface(self.update_proxy,dbus_interface='com.kylin.systemupgrade.interface')
        self.upgrade_strategy_proxy = self.system_bus.get_object('com.kylin.UpgradeStrategies','/com/kylin/UpgradeStrategies',follow_name_owner_changes=True)
        self.upgrade_strategy_interface = dbus.Interface(self.upgrade_strategy_proxy,dbus_interface='com.kylin.UpgradeStrategies.interface')
        self.update_proxy.connect_to_signal('UpdateDetectFinished',self.update_detect_finished)      
        self.update_proxy.connect_to_signal('UpdateDownloadInfo',self.update_download_info)
        self.update_proxy.connect_to_signal('UpdateDownloadFinished',self.update_download_finished)
        self.update_proxy.connect_to_signal('DeployUpdatFinished',self.deploy_update_finished)
        self.upgrade_strategy_proxy.connect_to_signal("UpgradeStrategyChanged",self.change_upgrade_strategy_handler)
        self.update_detect_status = False
        self.update_detect_event = Event()
        self.update_list = []
        self.deploy_finish_status = False
        self.deploy_finish_status_event = Event()
        self.deploy_finish_group = []
        self.download_finish_status = False
        self.download_finish_status_event = Event()
        self.download_finish_group = []
    
    def GetUpgradeStrategy(self):
        return self.upgrade_strategy_interface.GetUpgradeStrategy()
    
    def SetUpgradeStrategy(self,input):
        return self.upgrade_strategy_interface.SetUpgradeStrategy(input)

    def UpdateDownloadAll(self):
        return self.update_interface.UpdateDownloadAll()

    def DeployLatestUpdate(self,mode):
        return self.update_interface.DeployLatestUpdate(mode)

    def CancelDownload(self):
        return self.update_interface.CancelDownload()
    
    def GetBackendStatus(self):
        return self.update_interface.GetBackendStatus("")

    def init_dbus_connections(self):
        pass

    def init_config(self):
        pass

    def init_event_flags(self):
        pass

    def init_scheduler(self):
        pass

    def run(self):
        pass

    def prepare_upgrade(self):
        logging.info(_("prepare for upgrade"))
        self.update_detect_event.clear()
        self.update_interface.UpdateDetect()
        self.update_detect_event.wait()
        logging.debug(_("update detect finish:%s,%s")%(self.update_detect_status,",".join(self.update_list)))
        if self.update_detect_status and len(self.update_list)>0:
            pass
        elif not self.update_detect_status and 'kylin-system-updater' in self.update_list:
            logging.info(_("self update finished"))
        else:
            return False
        return True

    def download(self,timeout):
        logging.debug(_("start download"))
        logging.info("download timeout:%d"%timeout)
        self.download_finish_status=False
        self.download_finish_group=[]
        self.download_finish_status_event.clear()
        errcode,errstr = self.UpdateDownloadAll()
        logging.info("ret code:%d,ret string:%s"%(errcode,errstr))
        if(errcode!=0):
            return False
        self.download_finish_status_event.wait(timeout=timeout)
        backend_status=self.GetBackendStatus()
        logging.info("backend status:%d"%backend_status)
        if(backend_status==ACTION_DOWNLOADONLY):
            logging.info("cancel download")
            self.CancelDownload()
            time.sleep(1)
        logging.debug(_("download finish status:%s,%s")%(self.download_finish_status,",".join(self.download_finish_group)))
        if self.download_finish_status and len(self.download_finish_group)>0:
            pass
        else:
            return False
        return True 
    
    def install_basic(self):
        logging.debug(_("start install"))   
        self.install_finish_status_event.clear()
        self.update_interface.DeployLatestUpdate("reboot")
        self.install_finish_status_event.wait()        
        logging.debug(_("deploy finish status:%s,%s")%(self.install_finish_status,",".join(self.install_finish_group)))
        if self.install_finish_status and len(self.install_finish_group)>0:
            pass
        else:
            return False
        return True      

    def install(self):
        pass
    
    def download_with_timeout(self,timeout):
        pass
         
    def predownload_with_timeout(self,timeout):
        pass

    def cancel(self):
        pass
    
    def reboot(self):
        subprocess.run(["systemctl", "reboot"], check=True)

    def change_upgrade_strategy_handler(self,diff):
        pass
        
    def upgrade_all_now_handler(self):
        pass

    def update_detect_finished(self,success,updatelist,error_status,error_cause):
        logging.info(_("update detect finished:sucess:%s,updatelist:%s,error_status:%s,error_cause:%s")\
            %(success,",".join(updatelist),error_status,error_cause))
        self.update_detect_status = success
        self.update_list = updatelist
        self.update_detect_event.set()
                
    def update_download_finished(self,success,group,error_string,error_desc):
        logging.info(_("update download finish success:%s,group:%s,error string:%s,error desc:%s")\
            %(success,",".join(group),error_string,error_desc))
        self.download_finish_status = success    
        self.download_finish_group = group
        self.download_finish_status_event.set()

    def update_download_info(self,current_item,total_items,downloaded_size,total_size,speed):
        logging.info(_("update download info:current items:%d,total items:%d,downloaded size:%d,total size:%d,download speed:%d")\
            %(current_item,total_items,downloaded_size,total_size,speed))

    def deploy_update_finished(self,success,group,error_string,error_desc):
        logging.info(_("update deploy finish success:%s,group:%s,error string:%s,error desc:%s")\
            %(success,",".join(group),error_string,error_desc))
        self.deploy_finish_status = success
        self.deploy_finish_group = group
        self.deploy_finish_status_event.set()

class UnattendedUpgradeBackend(UnattendedUpgradesShutdown):
    def __init__(self):
        super().__init__()

    def init_config(self):
        updagradestrategy=self.GetUpgradeStrategy()
        configjsonstr=convert_a_sv_to_json(updagradestrategy)
        logging.info(configjsonstr)
        self.config={}
        self.config=json.loads(configjsonstr)
    
    def init_scheduler(self):
        self.background_scheduler=BackgroundScheduler()
        self.background_scheduler.start()
        self.schedule_task()
        with open("/var/log/kylin-unattended-upgrades/unattended-upgrades-shutdown.log",'a+') as f:
            self.background_scheduler.print_jobs(out=f)  
        
    def schedule_task(self):
        try:
            updatemode=self.config.get('updatemode','off')
            logging.info("upgrade mode:%s"%updatemode)
            if(updatemode=="off"):
                pass
            elif(updatemode=="autoupdate"):
                autoupdateconfig=self.config.get("autoupdate",{})
                downloadtime=autoupdateconfig.get("downloadtime",[])
                downloadrandom=autoupdateconfig.get("downloadtimerandom",120)
                period=autoupdateconfig.get("period",1)
                mode=autoupdateconfig.get("mode",1)
                timelist=process_timelist(process_time_intervals(downloadtime,downloadrandom))
                logging.info(timelist)
                downloadtimestamp=ReadValueFromFile(UNATTENDED_UPGRADE_TIMESTAMP,'TimeStamp','download')
                if not downloadtimestamp:
                    now = datetime.now() 
                    downloadtimestamp = now.strftime("%Y-%m-%d %H:%M:%S")
                dtime=datetime.strptime(downloadtimestamp, "%Y-%m-%d %H:%M:%S")
                for t in timelist:
                    taskdate=(dtime+timedelta(days=float(period))).replace(hour=t.get("hour",0),minute=t.get("minute",0))
                    task_id="download_task_%s"%(taskdate.strftime("%Y-%m-%d %H:%M:%S"))
                    self.background_scheduler.add_job(self.download_with_timeout,args=[t.get("timeout",30)*60],trigger='interval',days=period,start_date=taskdate,id=task_id,replace_existing=True)
                if(mode=="timing"):
                    installtimestamp=ReadValueFromFile(UNATTENDED_UPGRADE_TIMESTAMP,'TimeStamp','install')
                    if not installtimestamp:
                        now = datetime.now() 
                        installtimestamp = now.strftime("%Y-%m-%d %H:%M:%S")
                    itime=datetime.strptime(installtimestamp, "%Y-%m-%d %H:%M:%S")
                    installtime=autoupdateconfig.get("installtime","20:00")
                    installtimedict=parse_time(installtime)
                    logging.info(installtimedict)
                    taskdate=(itime+timedelta(days=float(period))).replace(hour=installtimedict.get("hour",0),minute=installtimedict.get("minute",0))
                    task_id="install_task_%s"%(taskdate.strftime("%Y-%m-%d %H:%M:%S"))
                    self.background_scheduler.add_job(self.install,trigger='interval',days=period,start_date=taskdate,id=task_id,replace_existing=True)
            elif(updatemode=="predownload"):
                autoupdateconfig=self.config.get("predownload",{})
                downloadtime=autoupdateconfig.get("downloadtime",[])
                downloadrandom=autoupdateconfig.get("downloadtimerandom",120)
                period=autoupdateconfig.get("period",1)
                timelist=process_timelist(process_time_intervals(downloadtime,downloadrandom))
                logging.info(timelist)
                downloadtimestamp=ReadValueFromFile(UNATTENDED_UPGRADE_TIMESTAMP,'TimeStamp','predownload')
                if not downloadtimestamp:
                    now = datetime.now() 
                    downloadtimestamp = now.strftime("%Y-%m-%d %H:%M:%S")
                dtime=datetime.strptime(downloadtimestamp, "%Y-%m-%d %H:%M:%S")
                for t in timelist:
                    taskdate=(dtime+timedelta(days=float(period))).replace(hour=t.get("hour",0),minute=t.get("minute",0))
                    task_id="predownload_task_%s"%(taskdate.strftime("%Y-%m-%d %H:%M:%S"))
                    self.background_scheduler.add_job(self.predownload_with_timeout,args=[t.get("timeout",30)*60],trigger='interval',days=period,start_date=taskdate,id=task_id,replace_existing=True)
            elif(updatemode=="updatenow"):            
                autoupdateconfig=self.config.get("updatenow",{})
                downloadrandom=autoupdateconfig.get("downloadtimerandom",120)
                delay = random.uniform(0, downloadrandom)
                delta = timedelta(minutes=delay)
                taskdate=datetime.now() + delta
                self.background_scheduler.add_job(self.install,'date',run_date=taskdate)
            else:
                pass 
        except Exception as e:
            logging.error(e) 
                             
    def download_with_timeout(self,timeout):
        wait_timeout=timeout
        start_time=time.time()
        prepare_upgrade_result=self.prepare_upgrade()
        download_result=False
        if(prepare_upgrade_result):
            end_time=time.time()
            time_diff=end_time-start_time
            if(wait_timeout>time_diff):
                wait_timeout=wait_timeout-time_diff
                logging.info("timeout:%d,time diff:%d"%(wait_timeout,time_diff))
                download_result=self.download(wait_timeout)
                if(download_result):
                    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                    UpdateTimeStamp('download',now)
                                     
    def predownload_with_timeout(self,timeout):
        self.need_check_backup=False
        wait_timeout=timeout
        start_time=time.time()
        if(self.prepare_upgrade()):
            end_time=time.time()
            time_diff=end_time-start_time
            logging.info("timeout:%d,time diff:%d"%(wait_timeout,time_diff))
            if(wait_timeout>time_diff):
                wait_timeout=wait_timeout-time_diff
                if(self.download(wait_timeout)):
                    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                    UpdateTimeStamp('predownload',now)

    def change_upgrade_strategy_handler(self, diff):  
        logging.info("upgrade strategy changed:%s"%diff)
        json_diff=json.loads(diff)
        updagradestrategy=self.GetUpgradeStrategy()
        configjsonstr=convert_a_sv_to_json(updagradestrategy)
        logging.info(configjsonstr)
        self.config=json.loads(configjsonstr)
        updatemode=self.config.get("updatemode","off")
        logging.info("upgrade mode:%s"%updatemode)
        added=json_diff.get("added",{})
        modified=json_diff.get("modified",{})
        if(not added and not modified):      
            logging.info("no changes of strategy")
        else:
            logging.info("strategy changed reschedule tasks")
            self.background_scheduler.remove_all_jobs()
            self.schedule_task()
        with open("/var/log/kylin-unattended-upgrades/unattended-upgrades-shutdown.log",'a+') as f:
            self.background_scheduler.print_jobs(out=f)  
        
    def run(self):
        logging.debug(_("Waiting for signal to start operation"))
        self.loop.run() 

if __name__ == "__main__":
    gettext.bindtextdomain("unattended-upgrades","/usr/share/locale")
    gettext.textdomain("unattended-upgrades")
    logdir = "/var/log/kylin-unattended-upgrades/"
    if not os.path.exists(logdir):
        os.makedirs(logdir)
    logfile = os.path.join(logdir, "unattended-upgrades-shutdown.log")
    logging.basicConfig(format='%(asctime)s-%(name)s-%(levelname)s-%(message)s',datefmt='%Y-%m-%d,%H:%M:%S',level=logging.DEBUG,filename=logfile)
    signal.signal(signal.SIGTERM, signal_term_handler)
    unattendedupgrade=UnattendedUpgradeBackend()
    unattendedupgrade.init_dbus_connections()
    unattendedupgrade.init_config()
    unattendedupgrade.init_event_flags()
    unattendedupgrade.init_scheduler()
    unattendedupgrade.run()
   