diff --git a/py-i2phosts-master b/py-i2phosts-master new file mode 100755 index 0000000..c0e21ba --- /dev/null +++ b/py-i2phosts-master @@ -0,0 +1,153 @@ +#!/usr/bin/python + +import os +import sys +import pwd +import errno +import argparse +import time +import configobj +import validate +import subprocess +import threading +import daemon +import daemon.pidlockfile + +class Thread(threading.Thread): + + def __init__(self): + threading.Thread.__init__(self) + self.setDaemon(True) + + +class FetcherThread(Thread): + """ Run py-i2phosts-fetcher periodically """ + + def run(self): + while True: + run_prog('py-i2phosts-fetcher') + time.sleep(float(config['fetch_interval'])) + + +class CheckerThread(Thread): + """ Run py-i2phosts-checker, py-i2phosts-maint, py-i2phosts-builder periodically """ + + def run(self): + while True: + run_prog('py-i2phosts-checker') + run_prog('py-i2phosts-maint') + run_prog('py-i2phosts-builder') + time.sleep(float(config['check_interval'])) + + +def run_prog(prog): + try: + log.info('launching %s', prog) + sp_args = [prog] + if args.debug or args.verbose: + sp_args.append('-d') + p = subprocess.Popen(sp_args, shell=False) + except OSError, e: + log.error('failed to exec %s: %s', prog, e) + if e.errno == errno.ENOENT: + log.error(' maybe it isn\'t in PATH') + else: + p.wait() + log.info('%s finished', prog) + + +def main(): + def run_fetcher(): + fetcher = FetcherThread() + fetcher.start() + return fetcher + + def run_checker(): + checker = CheckerThread() + checker.start() + return checker + + # if we're just started, wait while fetcher get some new hosts + fetcher = run_fetcher() + fetcher.join(300) # wait for 5 mins + # start checker and other + checker = run_checker() + + while True: + if fetcher.isAlive() == False: + fetcher = run_fetcher() + if checker.isAlive() == False: + checker = run_checker() + # do check every 30 mins + time.sleep(1800) + +# parse command line options +parser = argparse.ArgumentParser( + description='Master daemon for py-i2phosts.', + epilog='Report bugs to http://zzz.i2p/topics/733') +parser.add_argument('-d', '--debug', action='store_true', + help='run in debug mode without detaching from terminal'), +parser.add_argument('-v', '--verbose', action='store_true', + help='run in verbose mode without detaching from terminal'), +parser.add_argument('-c', '--config', default='/etc/py-i2phosts/master.conf', dest='config_file', + help='config file to use') +args = parser.parse_args() + +# read and validate config +spec = ''' + log_file = string(default='/var/log/py-i2phosts/master.log') + log_level = option('debug', 'info', 'warning', 'error', 'critical', default='info') + pid_file = string(default='/var/run/py-i2phosts/master.pid') + runas = string(default='_pyi2phosts') + run_fetcher = boolean(default=True) + run_checker = boolean(default=True) + run_maint = boolean(default=True) + run_builder = boolean(default=True) + check_interval = integer(default=43200) + fetch_interval = integer(default=1800) + ''' +spec = spec.split('\n') +config = configobj.ConfigObj(args.config_file, configspec=spec) +if 'include' in config: + config_included = configobj.ConfigObj(config['include']) + config.merge(config_included) + +# django setup +DJANGO_SETTINGS_MODULE = 'settings' +if 'DJANGO_PROJECT_PATH' in config: + DJANGO_PROJECT_PATH = config['DJANGO_PROJECT_PATH'] +else: + DJANGO_PROJECT_PATH = os.path.dirname(sys.argv[0]) + '/web' +sys.path.insert(1, DJANGO_PROJECT_PATH) +os.environ['DJANGO_SETTINGS_MODULE'] = DJANGO_SETTINGS_MODULE +from web.lib.utils import get_logger + +# configure logger +if args.debug == True: + log_level = 'debug' + log_file = None +elif args.verbose == True: + log_level = 'info' + log_file = None +else: + log_level = config['log_level'] + log_file = config['log_file'] + +if not args.debug and not args.verbose: + # get pid object for daemon + pid = daemon.pidlockfile.TimeoutPIDLockFile(config['pid_file'], 10) + # create daemon context + d = daemon.DaemonContext(pidfile=pid, umask=077) + # write stderr to logfile # FIXME: and how we will deal with log rotation? + logfile = open(config['log_file'], 'a') + d.stderr = logfile + d.stdout = logfile + # drop privileges when started as root + if os.getuid() == 0: + runas = config['runas'] + pw_entry = pwd.getpwnam(runas) + d.uid = pw_entry[2] + d.gid = pw_entry[3] + d.open() # become daemon +log = get_logger(filename=log_file, log_level=log_level) +main()