#!/usr/local/bin/python import os, sys, time, atexit, re, logging from subprocess import call, Popen, PIPE from signal import SIGTERM ''' config options and logging facility ''' checkIntervalSeconds = 30 shutdownAfterInactivitySeconds = 1800 #logging.getLogger().setLevel(logging.DEBUG) class Debugger: @staticmethod def debug(message): logging.debug(message) class Daemon: def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): Debugger.debug('call: Daemon->__init__') self.stdin = stdin self.stdout = stdout self.stderr = stderr self.pidfile = pidfile self.forkCalls = 0 def daemonize(self): Debugger.debug('call: Daemon->daemonize') self.fork() self.decoupleFromParentEnvironment() self.fork() self.redirectStandardFileDescriptors() self.writePidFile() def fork(self): Debugger.debug('call: Daemon->fork') self.forkCalls += 1 try: pid = os.fork() if pid > 0: sys.exit(0) except OSError, e: sys.stderr.write("fork #" + self.forkCalls + " failed: %d (%s)\n" % (e.errno, e.strerror)) sys.exit(1) def decoupleFromParentEnvironment(self): Debugger.debug('call: Daemon->decoupleFromParentEnvironment') os.chdir("/") os.setsid() os.umask(0) def redirectStandardFileDescriptors(self): Debugger.debug('call: Daemon->redirectStandardFileDescriptors') sys.stdout.flush() sys.stderr.flush() Debugger.debug('flush ok') si = file(self.stdin, 'r') so = file(self.stdout, 'a+') se = file(self.stderr, 'a+', 0) Debugger.debug('s ok') os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) Debugger.debug('os ok') Debugger.debug('end: Daemon->redirectStandardFileDescriptors') def writePidFile(self): Debugger.debug('call: Daemon->writePidFile') atexit.register(self.delpid) pid = str(os.getpid()) file(self.pidfile,'w+').write("%s\n" % pid) def delpid(self): Debugger.debug('call: Daemon->delpid') os.remove(self.pidfile) def start(self): Debugger.debug('call: Daemon->start') if self.isDaemonAlreadyRunning(): message = "pidfile %s already exist. Daemon already running?\n" sys.stderr.write(message % self.pidfile) sys.exit(1) self.daemonize() self.run() def isDaemonAlreadyRunning(self): Debugger.debug('call: Daemon->isDaemonAlreadyRunning') try: pf = file(self.pidfile,'r') self.pid = int(pf.read().strip()) pf.close() except IOError: self.pid = None return self.pid def stop(self): Debugger.debug('call: Daemon->stop') if not self.isDaemonAlreadyRunning(): message = "pidfile %s does not exist. Daemon not running?\n" sys.stderr.write(message % self.pidfile) return # not an error in a restart # Try killing the daemon process try: while 1: os.kill(self.pid, SIGTERM) time.sleep(0.1) except OSError, err: err = str(err) if err.find("No such process") > 0: if os.path.exists(self.pidfile): os.remove(self.pidfile) else: print str(err) sys.exit(1) def restart(self): Debugger.debug('call: Daemon->restart') self.stop() self.start() def run(self): Debugger.debug('call: Daemon->run') """ You should override this method when you subclass Daemon. It will be called after the process has been daemonized by start() or restart(). """ class ShellCommand: def __init__(self, command): Debugger.debug('call: ShellCommand->__init__') self.command = command def execute(self): Debugger.debug('call: ShellCommand->execute') pipe = Popen(self.command, stdout=PIPE, stderr=PIPE, shell=True) pipe.wait() self.output = pipe.communicate()[0]; return self.output def getOutputOfLastExecution(self): Debugger.debug('call: ShellCommand->getOutputOfLastExecution') return self.output class SystemActivityDetector: activityDetectedTime = None # Todo shellCommands = { 'fileWriteActivity' : ShellCommand('/bin/df | /usr/bin/grep /mnt/'), 'consoleUserActivity' : ShellCommand('/usr/bin/w'), 'openFilesAndConnectedShares' : ShellCommand('/usr/bin/fstat | grep /mnt/'), 'smbStatusActivity' : ShellCommand('/usr/local/bin/smbstatus -b') } def __init__(self): Debugger.debug('call: SystemActivityDetector->__init__') self.performInitialExecution(); def performInitialExecution(self): Debugger.debug('call: SystemActivityDetector->performInitialExecution') for shellCommand in self.shellCommands.itervalues(): shellCommand.execute() def detectActivity(self): Debugger.debug('call: SystemActivityDetector->detectActivity') methods = [ 'detectFileWriteActivity', 'detectConsoleUserActivity', 'detectOpenFilesAndConnectedShares' ] for methodName in methods: if getattr(self, methodName)(): Debugger.debug('activity detected: ' + methodName) return True return False def detectFileWriteActivity(self): Debugger.debug('call: SystemActivityDetector->detectFileWriteActivity') activityCommand = self.shellCommands['fileWriteActivity'] lastOutput = activityCommand.getOutputOfLastExecution() if activityCommand.getOutputOfLastExecution() == activityCommand.execute(): return False return True def detectConsoleUserActivity(self): Debugger.debug('call: SystemActivityDetector->detectConsoleUserActivity') activityCommand = self.shellCommands['consoleUserActivity'] lines = activityCommand.execute().split('\n'); del lines[0] for line in lines: line = line.strip() if line == '': continue cols = line.split() idle = cols[4]; if idle == 'IDLE': continue if idle == '-': return True if idle.isdigit(): minutes = int(idle) if minutes < 5: return True return False def detectOpenFilesAndConnectedShares(self): Debugger.debug('call: SystemActivityDetector->detectOpenFilesAndConnectedShares') activityCommand = self.shellCommands['openFilesAndConnectedShares'] lines = activityCommand.execute().split('\n'); for line in lines: line = line.strip(); if line == '': continue return True return False class AutoShutdownDaemon(Daemon): checkIntervalSeconds = 30 shutdownAfterInactivitySeconds = 1800 lastDetectedActivityTime = time.time() systemActivityDetector = None def setShutdownAfterInactivitySeconds(self, seconds): Debugger.debug('call: AutoShutdownDaemon->setShutdownAfterInactivitySeconds') if seconds < 120: seconds = 120 self.shutdownAfterInactivitySeconds = seconds def run(self): Debugger.debug('call: AutoShutdownDaemon->run') self.systemActivityDetector = SystemActivityDetector() while True: Debugger.debug('next cycle for AutoShutdownDaemon') self.shutdownWhenNoActivity() time.sleep(self.checkIntervalSeconds) def shutdownWhenNoActivity(self): Debugger.debug('call: AutoShutdownDaemon->shutdownWhenNoActivity') if self.systemActivityDetector.detectActivity(): self.updateLastDetectedActivityTime() if self.isShutdownTime(): self.shutdown() def updateLastDetectedActivityTime(self): Debugger.debug('call: AutoShutdownDaemon->updateLastDetectedActivityTime') self.lastDetectedActivityTime = time.time() def isShutdownTime(self): Debugger.debug('call: AutoShutdownDaemon->isShutdownTime') if time.time() > self.lastDetectedActivityTime + self.shutdownAfterInactivitySeconds: Debugger.debug('its shutdown time') return True Debugger.debug('keep running') return False def shutdown(self): Debugger.debug('call: AutoShutdownDaemon->shutdown') ShellCommand('shutdown -p now').execute() if __name__ == "__main__": daemon = AutoShutdownDaemon('/tmp/autoshutdown.pid', '/dev/null', '/dev/stdout') daemon.checkIntervalSeconds = checkIntervalSeconds daemon.shutdownAfterInactivitySeconds = shutdownAfterInactivitySeconds if len(sys.argv) == 2: if 'start' == sys.argv[1]: daemon.start() elif 'stop' == sys.argv[1]: daemon.stop() elif 'restart' == sys.argv[1]: daemon.restart() else: print "Unknown command" sys.exit(2) sys.exit(0) else: print "usage: %s start|stop|restart" % sys.argv[0] sys.exit(2)