医院科室的终端电脑没有关机的习惯,除了浪费能源,还容易诱发安全问题。很多黑客事件都是发生在夜里,长时间不关机的终端电脑容易成为肉鸡或跳板。夜间因值班人员疲惫,当出现异常时也许不会第一时间发觉,无人看管的主机很容易成为被攻击的对象。
所以打算写一个自动关机的程序,通过参数文件控制关机时刻,以及控制是否需要关机或重启。毕竟像收费窗口、手术室电脑是不允许突然关机的。异常的无准备关机可能会造成财务账页不平或手术过程被干扰。
为了观察自动关机程序的行为,在程序中加了日志开关。通过日志开关来控制程序心跳输出,通过日志输出可以得知程序是否还活着或按预期执行。
程序日志信息如下:
程序代码如下:
#!python3 import sys import time import os from configparser import ConfigParser class MyLog: def __init__(self, log_dir): self.is_off = True self.log_dir = log_dir self.log_prefix = 'x-' if not os.path.exists(log_dir): os.makedirs(log_dir) self.logfile = os.path.join(log_dir, self.log_prefix + str(time.strftime('%Y-%m-%d', time.localtime())) + '.log') self.file = open(file=self.logfile, mode='a', encoding='utf-8') def on(self): self.is_off = False def off(self): self.is_off = True def write(self, log_content): if self.is_off: pass else: self.file.write(log_content) self.file.flush() def reload_log(self): self.file.close() self.logfile = os.path.join(self.log_dir, str(time.strftime('%Y-%m-%d', time.localtime()))+'.log') self.file = open(file=self.logfile, mode='a', encoding='utf-8') def __del__(self): self.file.close() def shutdown(second): cmd = 'shutdown -s -t ' + second os.system(cmd) def reboot(second): cmd = 'shutdown -r -t ' + second os.system(cmd) def cancel(): os.system('shutdown -a') def shutdown_conf(f): conf = ConfigParser() conf.read(f, encoding='utf-8') shutdown_time = conf['shutdown']['shutdown_time'] is_shutdown = conf['shutdown'].getboolean('is_shutdown') is_reboot = conf['shutdown'].getboolean('is_reboot') wait_second = conf['shutdown']['wait_second'] return [shutdown_time, is_shutdown, is_reboot, wait_second] def logging_conf(f): conf = ConfigParser() conf.read(f, encoding='utf-8') is_off = conf['logging'].getboolean('is_off') log_dir = conf['logging']['log_dir'] frequency = conf['logging']['frequency'] prefix = conf['logging']['prefix'] return [is_off, log_dir, frequency, prefix] def base_conf(f): conf = ConfigParser() conf.read(f, encoding='utf-8') conf_file = conf['conf']['conf_file'] return conf_file def default_conf(conf_file): '''如果配置文件不存在,就写入默认配置''' content = r''' [shutdown] ; 注释:自动关机配置文件 ; 定义每天关机的触发时刻, 24小时制 ; 格式:03:00 shutdown_time = 01:30 ; 是否自动关机 True 或 False is_shutdown = True ; 是否自动重启 True 或 False ; 优先判断是否关机,当不关机时,判断是否重启 is_reboot = False ; 关机或重启前的等待时长,单位为秒 ; 在倒计时结束前可以取消 wait_second = 180 [logging] ; 注释:默认日志信息 ; 是否写日志 is_off = False ; 日志位置 log_dir = c:\autox\logs ; 更新日志文件的频率,每 sleep 120 次重新加载一下日志文件 ; 目的是切换日志文件名称,特别是跨天时 frequency = 120 ; 日志前缀 prefix = x [conf] ; 注释:默认配置文件信息 ; 配置文件 conf_file = c:\autox\autox.ini ''' if os.path.exists(conf_file): return False conf_folder = os.path.dirname(os.path.abspath(conf_file)) if not os.path.exists(conf_folder): os.makedirs(conf_folder) with open(file=conf_file, mode='w', encoding='utf-8') as f: f.write(content) def wait(shutdown_time): '''等待关机时刻到来''' global log global conf log.write('\n\n\nLogging: 程序开始执行\n') log.write('Logging: 关机时刻 %s\n' % shutdown_time) logging_param = logging_conf(conf) n = 1 # 每循环m次,重新加载一下log文件 # logging_conf => frequency m = int(logging_param[2]) while True: now = time.strftime('%H:%M') if now != shutdown_time: time.sleep(2) n += 1 if n >= m: n = 1 log.reload_log() log.write('Logging: 关机时刻 %s\n' % shutdown_time) # 检查配置文件是否有更新 # 使用配置文件中的 logging 参数控制是否输出日志 logging_param = logging_conf(conf) # logging_conf => is_off log.is_off = logging_param[0] # logging_conf => frequency m = int(logging_param[2]) now = time.strftime('%H:%M') current = time.strftime('%H:%M:%S') log.write('Logging: 等待 %s\n' % current) else: break log.write('Logging: 准备关机!\n') log = MyLog(r'c:\autox\logs') conf = r'c:\autox\autox.ini' # log 开关 # 配置文件中的开关会覆盖程序代码中的 # 程序执行以后需要通过配置文件来控制程序行为 log.is_off = False def main(): while True: global log global conf try: # 当配置文件不存在时,写入默认配置文件 default_conf(conf) # 加载自动关机的参数 # 当关机被手动取消时进入下一轮循环 shutdown_time,is_shutdown,is_reboot,wait_second = shutdown_conf(conf) # 等到关机时刻到来 wait(shutdown_time) if is_shutdown: shutdown(wait_second) if is_reboot: reboot(wait_second) # 等待系统重启倒计时 # 在此期间如果关机动作被手工取消则进入下一轮循环 time.sleep(int(wait_second)) except Exception as e: log.write(f'Error: {e}\n') if __name__ == '__main__': main()
后期准备将程序打包成 exe 格式,并让程序在后台执行。设计成死循环主要是为了保障关机程序可以一起正常执行。并且程序大部分时间处于sleep状态,并不消耗资源。
通过配置文件控制,后期可以增加扩展性功能。比如自动更新、程序自启动。自动关机只是v0.1版的一个功能点,既然准备让这个程序在医院的终端电脑上一直执行,就准备将其作为一个agent,通过更新配置文件和程序版本来提供更多功能。
这里是一个医院环境的自动化运维的初级设想,在医院的终端上埋一个可自动更新的程序。程序可以在后续版本更新中完成指定任务,比如自动安装其它程序,或一些自定义任务。
程序的第一次下发使用医院的HIS程序或天擎程序分发模块。后续将完善程序的自更新功能,让其脱离其它分发程序可以自动更新。
全文完。
评论