class Xwared(object): def __init__(self, log_novomit): super().__init__() self._log_novomit = log_novomit self.etmPid = 0 self.fdLock = None self.toRunETM = None self.etmStartedAt = None self.etmLongevities = None # Cfg watchers self.etmCfg = dict() self.watchManager = None self.cfgWatcher = None # requirements checking self.ensureNonRoot() profileBootstrap(constants.PROFILE_DIR) setupLogging() self.ensureOneInstance() tryRemove(constants.XWARED_SOCKET) # initialize variables signal.signal(signal.SIGTERM, self.unload) signal.signal(signal.SIGINT, self.unload) self.settings = SettingsAccessorBase(constants.XWARED_CONFIG_FILE, XWARED_DEFAULTS_SETTINGS) self.toRunETM = self.settings.getbool("xwared", "startetm") self.etmLogs = collections.deque(maxlen = 250) self._resetEtmLongevities() # ipc listener self.listener = ServerThread(self) self.listener.start() # using pyinotify to monitor etm.cfg changes self.setupCfgWatcher() def setupCfgWatcher(self): # etm.cfg watcher self.watchManager = pyinotify.WatchManager() self.cfgWatcher = pyinotify.ThreadedNotifier(self.watchManager, self.pyinotifyDispatcher) self.cfgWatcher.name = "cfgWatcher inotifier" self.cfgWatcher.daemon = True self.cfgWatcher.start() self.watchManager.add_watch(constants.ETM_CFG_DIR, pyinotify.ALL_EVENTS) @debounce(0.5, instant_first=True) def onEtmCfgChanged(self): try: with open(constants.ETM_CFG_FILE, 'r') as file: lines = file.readlines() pairs = {} for line in lines: eq = line.index("=") k = line[:eq] v = line[(eq + 1):].strip() pairs[k] = v self.etmCfg = pairs except FileNotFoundError: print("Xware Desktop: etm.cfg not present at the moment.") def pyinotifyDispatcher(self, event): if event.maskname != "IN_CLOSE_WRITE": return if event.pathname == constants.ETM_CFG_FILE: self.onEtmCfgChanged() @staticmethod def ensureNonRoot(): if os.getuid() == 0 or os.geteuid() == 0: print("拒绝以root运行", file = sys.stderr) sys.exit(-1) def ensureOneInstance(self): # If one instance is already running, shout so and then exit the program # otherwise, a) hold the lock to xwared, b) prepare etm lock self.fdLock = os.open(constants.XWARED_LOCK, os.O_CREAT | os.O_RDWR) try: fcntl.flock(self.fdLock, fcntl.LOCK_EX | fcntl.LOCK_NB) except BlockingIOError: print("xwared已经运行", file = sys.stderr) sys.exit(-1) print("xwared: unlocked") def runETM(self): while not self.toRunETM: time.sleep(1) if self.settings.getint("xwared", "startetmwhen") == 2: self.settings.setbool("xwared", "startetm", True) self.settings.save() self.toRunETM = True env = os.environ.copy() env.update(CHMNS_LD_PRELOAD = constants.ETM_PATCH_FILE) if self._log_novomit: additionals = dict( stdout = subprocess.PIPE, stderr = subprocess.STDOUT, ) else: additionals = dict() proc = subprocess.Popen(constants.ETM_COMMANDLINE, env = env, **additionals) self.etmPid = proc.pid if self.etmPid: self.etmStartedAt = time.monotonic() self._watchETM(proc) else: print("Cannot start etm", file = sys.stderr) sys.exit(1) def _resetEtmLongevities(self): sampleNumber = self.settings.getint("etm", "samplenumberoflongevity") if not isinstance(self.etmLongevities, collections.deque): self.etmLongevities = collections.deque(maxlen = sampleNumber) for i in range(sampleNumber): self.etmLongevities.append(float("inf")) def _watchETM(self, proc): if self._log_novomit: for line in iter(proc.stdout.readline, b""): line = line.rstrip().decode("utf-8") self.etmLogs.append(line) ret = proc.wait() self.etmPid = 0 if self._log_novomit: if ret != 0: print("\n".join(self.etmLogs), file = sys.stderr) self.etmLogs.clear() longevity = time.monotonic() - self.etmStartedAt self.etmLongevities.append(longevity) threshold = self.settings.getint("etm", "shortlivedthreshold") if all(map(lambda l: l <= threshold, self.etmLongevities)): print("xwared: ETM持续时间连续{number}次不超过{threshold}秒,终止执行ETM" .format(number = self.etmLongevities.maxlen, threshold = threshold), file = sys.stderr) print("这极有可能是xware本身的bug引起的,更多信息请看 " "https://github.com/Xinkai/XwareDesktop/wiki/故障排查和意见反馈" "#etm持续时间连续3次不超过30秒终止执行etm的调试方法", file = sys.stderr) self.toRunETM = False def stopETM(self, restart): if self.etmPid: self.toRunETM = restart os.kill(self.etmPid, signal.SIGTERM) else: print("ETM not running, ignore stopETM") if self.settings.getint("xwared", "startetmwhen") == 2: self.settings.setbool("xwared", "startetm", restart) self.settings.save() # frontend end interfaces @staticmethod def interface_versions(): return { "xware": XWARE_VERSION, "api": DATE, } def interface_startETM(self): self._resetEtmLongevities() self.toRunETM = True def interface_stopETM(self): self._resetEtmLongevities() self.stopETM(False) def interface_restartETM(self): self._resetEtmLongevities() self.stopETM(True) def interface_start(self): if self.settings.getint("xwared", "startetmwhen") == 3: self.interface_startETM() self.settings.setbool("xwared", "startetm", True) self.settings.save() def interface_quit(self): if self.settings.getint("xwared", "startetmwhen") == 3: self.stopETM(False) self.settings.setbool("xwared", "startetm", True) self.settings.save() def interface_setStartEtmWhen(self, startetmwhen): self.settings.setint("xwared", "startetmwhen", startetmwhen) if startetmwhen == 1: self.settings.setbool("xwared", "startetm", True) self.settings.save() def interface_setMounts(self, mounts): raise NotImplementedError() def interface_getMounts(self): raise NotImplementedError() def interface_infoPoll(self): return { "etmPid": self.etmPid, "lcPort": int(self.etmCfg.get("local_control.listen_port", 0)), "peerId": self.etmCfg.get("rc.peerid", ""), "startEtmWhen": int(self.settings.getint("xwared", "startetmwhen")), } def unload(self, sig, stackframe): print("unloading...") self.stopETM(False) tryClose(self.fdLock) tryRemove(constants.XWARED_LOCK) self.settings.save() sys.exit(0)
class Xwared(object): etmPid = 0 fdLock = None toRunETM = None # Cfg watchers etmCfg = dict() watchManager = None cfgWatcher = None def __init__(self): super().__init__() # requirements checking self.checkUserGroup() self.ensureOneInstance() os.umask(0o006) # initialize variables signal.signal(signal.SIGTERM, self.unload) signal.signal(signal.SIGINT, self.unload) self.settings = SettingsAccessorBase(constants.XWARED_CONFIG_FILE, XWARED_DEFAULTS_SETTINGS) self.toRunETM = self.settings.getbool("xwared", "startetm") # ipc listener self.listener = XwaredCommunicationListener(self) self.listener.start() # using pyinotify to monitor etm.cfg changes self.setupCfgWatcher() def setupCfgWatcher(self): # etm.cfg watcher self.watchManager = pyinotify.WatchManager() self.cfgWatcher = pyinotify.ThreadedNotifier(self.watchManager, self.pyinotifyDispatcher) self.cfgWatcher.name = "cfgWatcher inotifier" self.cfgWatcher.daemon = True self.cfgWatcher.start() self.watchManager.add_watch(constants.ETM_CFG_DIR, pyinotify.ALL_EVENTS) @debounce(0.5, instant_first=True) def onEtmCfgChanged(self): try: with open(constants.ETM_CFG_FILE, 'r') as file: lines = file.readlines() pairs = {} for line in lines: eq = line.index("=") k = line[:eq] v = line[(eq + 1):].strip() pairs[k] = v self.etmCfg = pairs except FileNotFoundError: print("Xware Desktop: etm.cfg not present at the moment.") def pyinotifyDispatcher(self, event): if event.maskname != "IN_CLOSE_WRITE": return if event.pathname == constants.ETM_CFG_FILE: self.onEtmCfgChanged() @staticmethod def checkUserGroup(): from pwd import getpwnam try: usrInfo = getpwnam("xware") except KeyError: print("未找到xware用户。请重新安装。", file = sys.stderr) sys.exit(-1) xware_uid = usrInfo.pw_uid xware_gid = usrInfo.pw_gid if not os.getuid() == os.geteuid() == xware_uid: print("必须以xware用户运行。", file = sys.stderr) sys.exit(-1) if not os.getgid() == os.getegid() == xware_gid: print("必须以xware组运行。", file = sys.stderr) sys.exit(-1) def ensureOneInstance(self): # If one instance is already running, shout so and then exit the program # otherwise, a) hold the lock to xwared, b) prepare etm lock self.fdLock = os.open(constants.XWARED_LOCK, os.O_CREAT | os.O_RDWR, 0o666) try: fcntl.flock(self.fdLock, fcntl.LOCK_EX | fcntl.LOCK_NB) except BlockingIOError: print("xwared已经运行", file = sys.stderr) sys.exit(-1) print("xwared: unlocked") def runETM(self): while not self.toRunETM: time.sleep(1) if self.settings.getint("xwared", "startetmwhen") == 2: self.settings.setbool("xwared", "startetm", True) self.settings.save() self.toRunETM = True try: self.etmPid = os.fork() except OSError: print("Fork failed", file = sys.stderr) sys.exit(-1) if self.etmPid == 0: # child os.putenv("LD_PRELOAD", constants.ETM_PATCH_FILE) print("child: pid({pid}) ppid({ppid})".format(pid = os.getpid(), ppid = self.etmPid)) cmd = constants.ETM_COMMANDLINE etmPath = cmd[0] os.chdir(os.path.dirname(etmPath)) os.execv(etmPath, cmd) sys.exit(-1) else: # parent print("parent: pid({pid}) cpid({cpid})".format(pid = os.getpid(), cpid = self.etmPid)) self._watchETM() def _watchETM(self): os.waitpid(self.etmPid, 0) self.etmPid = 0 def stopETM(self, restart): if self.etmPid: self.toRunETM = restart os.kill(self.etmPid, signal.SIGTERM) else: print("ETM not running, ignore stopETM") if self.settings.getint("xwared", "startetmwhen") == 2: self.settings.setbool("xwared", "startetm", restart) self.settings.save() # frontend end interfaces def interface_startETM(self): self.toRunETM = True def interface_stopETM(self): self.stopETM(False) def interface_restartETM(self): self.stopETM(True) def interface_start(self): if self.settings.getint("xwared", "startetmwhen") == 3: self.interface_startETM() self.settings.setbool("xwared", "startetm", True) self.settings.save() def interface_quit(self): if self.settings.getint("xwared", "startetmwhen") == 3: self.stopETM(False) self.settings.setbool("xwared", "startetm", True) self.settings.save() def interface_getStartEtmWhen(self): return self.settings.getint("xwared", "startetmwhen") def interface_setStartEtmWhen(self, startetmwhen): self.settings.setint("xwared", "startetmwhen", startetmwhen) if startetmwhen == 1: self.settings.setbool("xwared", "startetm", True) self.settings.save() def interface_setMounts(self, mounts): raise NotImplementedError() def interface_getMounts(self): raise NotImplementedError() def interface_permissionCheck(self): raise NotImplementedError() def interface_infoPoll(self): return BackendInfo(etmPid = self.etmPid, lcPort = int(self.etmCfg.get("local_control.listen_port", 0)), userId = int(self.etmCfg.get("userid", 0)), peerId = self.etmCfg.get("rc.peerid", "")) @staticmethod def tryClose(fd): try: os.close(fd) except OSError: pass @staticmethod def tryRemove(path): try: os.remove(path) except OSError: pass def unload(self, sig, stackframe): print("unloading...") self.stopETM(False) self.tryClose(self.fdLock) self.tryRemove(constants.XWARED_LOCK) self.settings.save() sys.exit(0)
class Xwared(object): etmPid = 0 fdLock = None toRunETM = None etmStartedAt = None etmLongevities = None # Cfg watchers etmCfg = dict() watchManager = None cfgWatcher = None def __init__(self): super().__init__() # requirements checking self.ensureNonRoot() self.ensureOneInstance() profileBootstrap(constants.PROFILE_DIR) tryRemove(constants.XWARED_SOCKET[0]) # initialize variables signal.signal(signal.SIGTERM, self.unload) signal.signal(signal.SIGINT, self.unload) self.settings = SettingsAccessorBase(constants.XWARED_CONFIG_FILE, XWARED_DEFAULTS_SETTINGS) self.toRunETM = self.settings.getbool("xwared", "startetm") self._resetEtmLongevities() # ipc listener self.listener = XwaredCommunicationListener(self) self.listener.start() # using pyinotify to monitor etm.cfg changes self.setupCfgWatcher() def setupCfgWatcher(self): # etm.cfg watcher self.watchManager = pyinotify.WatchManager() self.cfgWatcher = pyinotify.ThreadedNotifier(self.watchManager, self.pyinotifyDispatcher) self.cfgWatcher.name = "cfgWatcher inotifier" self.cfgWatcher.daemon = True self.cfgWatcher.start() self.watchManager.add_watch(constants.ETM_CFG_DIR, pyinotify.ALL_EVENTS) @debounce(0.5, instant_first=True) def onEtmCfgChanged(self): try: with open(constants.ETM_CFG_FILE, 'r') as file: lines = file.readlines() pairs = {} for line in lines: eq = line.index("=") k = line[:eq] v = line[(eq + 1):].strip() pairs[k] = v self.etmCfg = pairs except FileNotFoundError: print("Xware Desktop: etm.cfg not present at the moment.") def pyinotifyDispatcher(self, event): if event.maskname != "IN_CLOSE_WRITE": return if event.pathname == constants.ETM_CFG_FILE: self.onEtmCfgChanged() @staticmethod def ensureNonRoot(): if os.getuid() == 0 or os.geteuid() == 0: print("拒绝以root运行", file=sys.stderr) sys.exit(-1) def ensureOneInstance(self): # If one instance is already running, shout so and then exit the program # otherwise, a) hold the lock to xwared, b) prepare etm lock self.fdLock = os.open(constants.XWARED_LOCK, os.O_CREAT | os.O_RDWR) try: fcntl.flock(self.fdLock, fcntl.LOCK_EX | fcntl.LOCK_NB) except BlockingIOError: print("xwared已经运行", file=sys.stderr) sys.exit(-1) print("xwared: unlocked") def runETM(self): while not self.toRunETM: time.sleep(1) if self.settings.getint("xwared", "startetmwhen") == 2: self.settings.setbool("xwared", "startetm", True) self.settings.save() self.toRunETM = True try: self.etmPid = os.fork() except OSError: print("Fork failed", file=sys.stderr) sys.exit(-1) if self.etmPid == 0: # child os.putenv("CHMNS_LD_PRELOAD", constants.ETM_PATCH_FILE) print("child: pid({pid}) ppid({ppid})".format(pid=os.getpid(), ppid=self.etmPid)) cmd = constants.ETM_COMMANDLINE os.execv(cmd[0], cmd) sys.exit(-1) else: # parent self.etmStartedAt = time.monotonic() print("parent: pid({pid}) cpid({cpid})".format(pid=os.getpid(), cpid=self.etmPid)) self._watchETM() def _resetEtmLongevities(self): sampleNumber = self.settings.getint("etm", "samplenumberoflongevity") if not isinstance(self.etmLongevities, collections.deque): self.etmLongevities = collections.deque(maxlen=sampleNumber) for i in range(sampleNumber): self.etmLongevities.append(float("inf")) def _watchETM(self): os.waitpid(self.etmPid, 0) self.etmPid = 0 longevity = time.monotonic() - self.etmStartedAt self.etmLongevities.append(longevity) threshold = self.settings.getint("etm", "shortlivedthreshold") if all(map(lambda l: l <= threshold, self.etmLongevities)): print("xwared: ETM持续时间连续{number}次不超过{threshold}秒,终止执行ETM".format( number=self.etmLongevities.maxlen, threshold=threshold), file=sys.stderr) print( "这极有可能是xware本身的bug引起的,更多信息请看 " "https://github.com/Xinkai/XwareDesktop/wiki/故障排查和意见反馈" "#etm持续时间连续3次不超过30秒终止执行etm的调试方法", file=sys.stderr) self.toRunETM = False def stopETM(self, restart): if self.etmPid: self.toRunETM = restart os.kill(self.etmPid, signal.SIGTERM) else: print("ETM not running, ignore stopETM") if self.settings.getint("xwared", "startetmwhen") == 2: self.settings.setbool("xwared", "startetm", restart) self.settings.save() # frontend end interfaces def interface_startETM(self): self._resetEtmLongevities() self.toRunETM = True def interface_stopETM(self): self._resetEtmLongevities() self.stopETM(False) def interface_restartETM(self): self._resetEtmLongevities() self.stopETM(True) def interface_start(self): if self.settings.getint("xwared", "startetmwhen") == 3: self.interface_startETM() self.settings.setbool("xwared", "startetm", True) self.settings.save() def interface_quit(self): if self.settings.getint("xwared", "startetmwhen") == 3: self.stopETM(False) self.settings.setbool("xwared", "startetm", True) self.settings.save() def interface_getStartEtmWhen(self): return self.settings.getint("xwared", "startetmwhen") def interface_setStartEtmWhen(self, startetmwhen): self.settings.setint("xwared", "startetmwhen", startetmwhen) if startetmwhen == 1: self.settings.setbool("xwared", "startetm", True) self.settings.save() def interface_setMounts(self, mounts): raise NotImplementedError() def interface_getMounts(self): raise NotImplementedError() def interface_infoPoll(self): return BackendInfo(etmPid=self.etmPid, lcPort=int( self.etmCfg.get("local_control.listen_port", 0)), userId=int(self.etmCfg.get("userid", 0)), peerId=self.etmCfg.get("rc.peerid", "")) def unload(self, sig, stackframe): print("unloading...") self.stopETM(False) tryClose(self.fdLock) tryRemove(constants.XWARED_LOCK) self.settings.save() sys.exit(0)
class Xwared(object): def __init__(self, log_novomit): super().__init__() self._log_novomit = log_novomit self.etmPid = 0 self.fdLock = None self.toRunETM = None self.etmStartedAt = None self.etmLongevities = None # Cfg watchers self.etmCfg = dict() self.watchManager = None self.cfgWatcher = None # requirements checking self.ensureNonRoot() profileBootstrap(constants.PROFILE_DIR) setupLogging() self.ensureOneInstance() tryRemove(constants.XWARED_SOCKET) # initialize variables signal.signal(signal.SIGTERM, self.unload) signal.signal(signal.SIGINT, self.unload) self.settings = SettingsAccessorBase(constants.XWARED_CONFIG_FILE, XWARED_DEFAULTS_SETTINGS) self.toRunETM = self.settings.getbool("xwared", "startetm") self.etmLogs = collections.deque(maxlen=250) self._resetEtmLongevities() # ipc listener self.listener = ServerThread(self) self.listener.start() # using pyinotify to monitor etm.cfg changes self.setupCfgWatcher() def setupCfgWatcher(self): # etm.cfg watcher self.watchManager = pyinotify.WatchManager() self.cfgWatcher = pyinotify.ThreadedNotifier(self.watchManager, self.pyinotifyDispatcher) self.cfgWatcher.name = "cfgWatcher inotifier" self.cfgWatcher.daemon = True self.cfgWatcher.start() self.watchManager.add_watch(constants.ETM_CFG_DIR, pyinotify.ALL_EVENTS) @debounce(0.5, instant_first=True) def onEtmCfgChanged(self): try: with open(constants.ETM_CFG_FILE, 'r') as file: lines = file.readlines() pairs = {} for line in lines: eq = line.index("=") k = line[:eq] v = line[(eq + 1):].strip() pairs[k] = v self.etmCfg = pairs except FileNotFoundError: print("Xware Desktop: etm.cfg not present at the moment.") def pyinotifyDispatcher(self, event): if event.maskname != "IN_CLOSE_WRITE": return if event.pathname == constants.ETM_CFG_FILE: self.onEtmCfgChanged() @staticmethod def ensureNonRoot(): if os.getuid() == 0 or os.geteuid() == 0: print("拒绝以root运行", file=sys.stderr) sys.exit(-1) def ensureOneInstance(self): # If one instance is already running, shout so and then exit the program # otherwise, a) hold the lock to xwared, b) prepare etm lock self.fdLock = os.open(constants.XWARED_LOCK, os.O_CREAT | os.O_RDWR) try: fcntl.flock(self.fdLock, fcntl.LOCK_EX | fcntl.LOCK_NB) except BlockingIOError: print("xwared已经运行", file=sys.stderr) sys.exit(-1) print("xwared: unlocked") def runETM(self): while not self.toRunETM: time.sleep(1) if self.settings.getint("xwared", "startetmwhen") == 2: self.settings.setbool("xwared", "startetm", True) self.settings.save() self.toRunETM = True env = os.environ.copy() env.update(CHMNS_LD_PRELOAD=constants.ETM_PATCH_FILE) if self._log_novomit: additionals = dict( stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) else: additionals = dict() if __debug__: proc = subprocess.Popen( ('/opt/xware-desktop/chmns', '/opt/xware-desktop/xware/lib/EmbedThunderManager', '--verbose'), env=env, **additionals) else: proc = subprocess.Popen(constants.ETM_COMMANDLINE, env=env, **additionals) self.etmPid = proc.pid if self.etmPid: self.etmStartedAt = time.monotonic() self._watchETM(proc) else: print("Cannot start etm", file=sys.stderr) sys.exit(1) def _resetEtmLongevities(self): sampleNumber = self.settings.getint("etm", "samplenumberoflongevity") if not isinstance(self.etmLongevities, collections.deque): self.etmLongevities = collections.deque(maxlen=sampleNumber) for i in range(sampleNumber): self.etmLongevities.append(float("inf")) def _watchETM(self, proc): if self._log_novomit: for line in iter(proc.stdout.readline, b""): line = line.rstrip().decode("utf-8") self.etmLogs.append(line) ret = proc.wait() self.etmPid = 0 if self._log_novomit: if ret != 0: print("\n".join(self.etmLogs), file=sys.stderr) self.etmLogs.clear() longevity = time.monotonic() - self.etmStartedAt self.etmLongevities.append(longevity) threshold = self.settings.getint("etm", "shortlivedthreshold") if all(map(lambda l: l <= threshold, self.etmLongevities)): print("xwared: ETM持续时间连续{number}次不超过{threshold}秒,终止执行ETM".format( number=self.etmLongevities.maxlen, threshold=threshold), file=sys.stderr) print( "这极有可能是xware本身的bug引起的,更多信息请看 " "https://github.com/Xinkai/XwareDesktop/wiki/故障排查和意见反馈" "#etm持续时间连续3次不超过30秒终止执行etm的调试方法", file=sys.stderr) self.toRunETM = False def stopETM(self, restart): if self.etmPid: self.toRunETM = restart os.kill(self.etmPid, signal.SIGTERM) else: print("ETM not running, ignore stopETM") if self.settings.getint("xwared", "startetmwhen") == 2: self.settings.setbool("xwared", "startetm", restart) self.settings.save() # frontend end interfaces @staticmethod def interface_versions(): return { "xware": XWARE_VERSION, "api": DATE, } def interface_startETM(self): self._resetEtmLongevities() self.toRunETM = True def interface_stopETM(self): self._resetEtmLongevities() self.stopETM(False) def interface_restartETM(self): self._resetEtmLongevities() self.stopETM(True) def interface_start(self): if self.settings.getint("xwared", "startetmwhen") == 3: self.interface_startETM() self.settings.setbool("xwared", "startetm", True) self.settings.save() def interface_quit(self): if self.settings.getint("xwared", "startetmwhen") == 3: self.stopETM(False) self.settings.setbool("xwared", "startetm", True) self.settings.save() def interface_setStartEtmWhen(self, startetmwhen): self.settings.setint("xwared", "startetmwhen", startetmwhen) if startetmwhen == 1: self.settings.setbool("xwared", "startetm", True) self.settings.save() def interface_setMounts(self, mounts): raise NotImplementedError() def interface_getMounts(self): raise NotImplementedError() def interface_infoPoll(self): return { "etmPid": self.etmPid, "lcPort": int(self.etmCfg.get("local_control.listen_port", 0)), "peerId": self.etmCfg.get("rc.peerid", ""), "startEtmWhen": int(self.settings.getint("xwared", "startetmwhen")), } def unload(self, sig, stackframe): print("unloading...") self.stopETM(False) tryClose(self.fdLock) tryRemove(constants.XWARED_LOCK) self.settings.save() sys.exit(0)
class Xwared(object): etmPid = 0 fdLock = None toRunETM = None etmStartedAt = None etmLongevities = None # Cfg watchers etmCfg = dict() watchManager = None cfgWatcher = None def __init__(self): super().__init__() # requirements checking self.ensureOneInstance() tryRemove(constants.XWARED_SOCKET[0]) # initialize variables signal.signal(signal.SIGTERM, self.unload) signal.signal(signal.SIGINT, self.unload) self.settings = SettingsAccessorBase(constants.XWARED_CONFIG_FILE, XWARED_DEFAULTS_SETTINGS) self.toRunETM = self.settings.getbool("xwared", "startetm") self._resetEtmLongevities() # ipc listener self.listener = XwaredCommunicationListener(self) self.listener.start() # using pyinotify to monitor etm.cfg changes self.setupCfgWatcher() def setupCfgWatcher(self): # etm.cfg watcher self.watchManager = pyinotify.WatchManager() self.cfgWatcher = pyinotify.ThreadedNotifier(self.watchManager, self.pyinotifyDispatcher) self.cfgWatcher.name = "cfgWatcher inotifier" self.cfgWatcher.daemon = True self.cfgWatcher.start() self.watchManager.add_watch(constants.ETM_CFG_DIR, pyinotify.ALL_EVENTS) @debounce(0.5, instant_first=True) def onEtmCfgChanged(self): try: with open(constants.ETM_CFG_FILE, 'r') as file: lines = file.readlines() pairs = {} for line in lines: eq = line.index("=") k = line[:eq] v = line[(eq + 1):].strip() pairs[k] = v self.etmCfg = pairs except FileNotFoundError: print("Xware Desktop: etm.cfg not present at the moment.") def pyinotifyDispatcher(self, event): if event.maskname != "IN_CLOSE_WRITE": return if event.pathname == constants.ETM_CFG_FILE: self.onEtmCfgChanged() def ensureOneInstance(self): # If one instance is already running, shout so and then exit the program # otherwise, a) hold the lock to xwared, b) prepare etm lock self.fdLock = os.open(constants.XWARED_LOCK, os.O_CREAT | os.O_RDWR) try: fcntl.flock(self.fdLock, fcntl.LOCK_EX | fcntl.LOCK_NB) except BlockingIOError: print("xwared已经运行", file = sys.stderr) sys.exit(-1) print("xwared: unlocked") def runETM(self): while not self.toRunETM: time.sleep(1) if self.settings.getint("xwared", "startetmwhen") == 2: self.settings.setbool("xwared", "startetm", True) self.settings.save() self.toRunETM = True try: self.etmPid = os.fork() except OSError: print("Fork failed", file = sys.stderr) sys.exit(-1) if self.etmPid == 0: # child os.putenv("CHMNS_LD_PRELOAD", constants.ETM_PATCH_FILE) print("child: pid({pid}) ppid({ppid})".format(pid = os.getpid(), ppid = self.etmPid)) cmd = constants.ETM_COMMANDLINE os.execv(cmd[0], cmd) sys.exit(-1) else: # parent self.etmStartedAt = time.monotonic() print("parent: pid({pid}) cpid({cpid})".format(pid = os.getpid(), cpid = self.etmPid)) self._watchETM() def _resetEtmLongevities(self): sampleNumber = self.settings.getint("etm", "samplenumberoflongevity") if not isinstance(self.etmLongevities, collections.deque): self.etmLongevities = collections.deque(maxlen = sampleNumber) for i in range(sampleNumber): self.etmLongevities.append(float("inf")) def _watchETM(self): os.waitpid(self.etmPid, 0) self.etmPid = 0 longevity = time.monotonic() - self.etmStartedAt self.etmLongevities.append(longevity) threshold = self.settings.getint("etm", "shortlivedthreshold") if all(map(lambda l: l <= threshold, self.etmLongevities)): print("xwared: ETM持续时间连续{number}次不超过{threshold}秒,终止执行ETM" .format(number = self.etmLongevities.maxlen, threshold = threshold), file = sys.stderr) self.toRunETM = False def stopETM(self, restart): if self.etmPid: self.toRunETM = restart os.kill(self.etmPid, signal.SIGTERM) else: print("ETM not running, ignore stopETM") if self.settings.getint("xwared", "startetmwhen") == 2: self.settings.setbool("xwared", "startetm", restart) self.settings.save() # frontend end interfaces def interface_startETM(self): self._resetEtmLongevities() self.toRunETM = True def interface_stopETM(self): self._resetEtmLongevities() self.stopETM(False) def interface_restartETM(self): self._resetEtmLongevities() self.stopETM(True) def interface_start(self): if self.settings.getint("xwared", "startetmwhen") == 3: self.interface_startETM() self.settings.setbool("xwared", "startetm", True) self.settings.save() def interface_quit(self): if self.settings.getint("xwared", "startetmwhen") == 3: self.stopETM(False) self.settings.setbool("xwared", "startetm", True) self.settings.save() def interface_getStartEtmWhen(self): return self.settings.getint("xwared", "startetmwhen") def interface_setStartEtmWhen(self, startetmwhen): self.settings.setint("xwared", "startetmwhen", startetmwhen) if startetmwhen == 1: self.settings.setbool("xwared", "startetm", True) self.settings.save() def interface_setMounts(self, mounts): raise NotImplementedError() def interface_getMounts(self): raise NotImplementedError() def interface_infoPoll(self): return BackendInfo(etmPid = self.etmPid, lcPort = int(self.etmCfg.get("local_control.listen_port", 0)), userId = int(self.etmCfg.get("userid", 0)), peerId = self.etmCfg.get("rc.peerid", "")) def unload(self, sig, stackframe): print("unloading...") self.stopETM(False) tryClose(self.fdLock) tryRemove(constants.XWARED_LOCK) self.settings.save() sys.exit(0)