#!/usr/bin/python ''' sagator.py (c) 2003-2009 Jan ONDREJ (SAL) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. ''' __pychecker__ = 'unusednames=_ \ maxreturns=20 maxlines=300 \ no-local no-argsused no-stringiter no-abstract no-isliteral \ no-stdlib no-callinit no-badexcept' # Check unwanted fds. This code need to be before loading configuration, # because configuration opens new fds (sockets, files, ...). import os try: maxfds = os.sysconf(os.sysconf_names['SC_OPEN_MAX']) except (AttributeError, ValueError): maxfds = 256 alien_fds = {} for fdi in range(3, maxfds): try: f_stat = os.fstat(fdi) alien_fds[fdi] = (f_stat.st_dev, f_stat.st_ino) except OSError: pass from aglib import * import signal, time, avlib # load config try: from etc import * except ImportError, import_es: if str(import_es)[-6:]=="etc": print "ERROR! Config file not found! Exiting now." sys.exit(1) else: raise def wait_for_pid_remove(pidfile): for t in range(50): time.sleep(0.1) if not os.path.isfile(pidfile): return 0 print "PID file has been not removed! SAGATOR is still running?" return 1 if __name__ == '__main__': debug.set_level(DEBUG_LEVEL) try: opts, files = getopt.gnu_getopt(sys.argv[1:], 'hnc:l:', ['help', 'config=', 'daemon', 'nodaemon', 'logfile=', 'debug=', 'test', 'wait', 'kill']) except getopt.GetoptError, (msg, opt): print "Error:", msg sys.exit(1) if files: print "Unknown parameter(s):", ' '.join(files) sys.exit(1) for key, value in opts: if key in ('--help', '-h'): print "SAGATOR", SG_VER_REL print "(c) Jan ONDREJ (SAL) " print "Licensed under GNU GPL." print "" print "Params: --help this help" print " --config=f load \"f\" as alternate config "\ "(without .py extension)" print " --nodaemon don't daemonize after startup" print " --logfile=l filename for logging ('-' for stdout)" print " --debug=l set debug level to l" print " --test test configuration and exit" print " --kill kill all processes and "\ "wait for pid file removation" print " --wait wait for pid file removation" sys.exit(0) elif key in ('--config', '-c'): try: exec("from %s import *" % value) except ImportError, import_es: print "ImportError:", import_es elif key=='--daemon': globals.daemon = True elif key in ('--nodaemon', '-n'): globals.daemon = False LOGFILE = '-' elif key in ('--logfile', '-l'): LOGFILE = value elif key=='--debug': debug.set_level(int(value)) elif key=='--test': sys.exit(0) elif key=='--wait': sys.exit(wait_for_pid_remove(PID_FILE)) elif key=='--kill': try: for pid in open(PID_FILE).readlines(): os.kill(int(pid.strip()), signal.SIGTERM) except OSError, eces: print "Process not running [%s]?" % eces sys.exit(1) except IOError, eces: print "Can't open PID file %s [%s]." % (PID_FILE, eces) sys.exit(1) sys.exit(wait_for_pid_remove(PID_FILE)) debug.set_logfile(LOGFILE) safe.ROOT_PATH = CHROOT globals.setuidgid(USER, GROUP) globals.SRV = SRV globals.pid_file = PID_FILE avlib.smtp.SMTP_SERVER = SMTP_SERVER signal.signal(signal.SIGUSR2, sigusr2) try: # start services if globals.daemon: # first fork if os.fork()>0: os._exit(0) # try to create a new SID for the child process try: os.setsid() except OSError, e: debug.echo(1, "Unable to change process SID: %s" % e) # second fork, we never acquire a terminal if os.fork()>0: os._exit(0) # change current directory os.chdir("/") # try to close all unwanted fds for i, inode in alien_fds.items(): try: debug.echo(4, "Closing alien fd: %d [%s]" % (i, inode)) os.close(i) except OSError, e: debug.echo(1, "Error closing alien fd: %d [%s], %s" % (i, inode, e)) os.close(0) # close stdin os.open('/dev/null', os.O_RDONLY) debug.dup() debug.echo(1, "SAGATOR %s starting at %s" \ % (SG_VER_REL, time.strftime("%c", time.localtime()))) for srv in SRV: try: pids = srv.start() except socket.error, e: debug.echo(3, "%s: ERROR: %s" % (srv.name, e)) raise signal.signal(signal.SIGHUP, sighup) signal.signal(signal.SIGTERM, sigterm) signal.signal(signal.SIGCHLD, sigchld) if globals.daemon and globals.pid_file: # open PID file try: pidf = open(globals.pid_file, 'w') pidf.write(str(os.getpid())) pidf.close() except Exception, e: debug.echo(1, "Error writing pid file %s: %s" % (globals.pid_file, e)) service_pids = [srv.childs for srv in SRV] debug.echo(6, "Services finished, pids: %s" % service_pids) fork_time = time.time() while True: time.sleep(60) for srv in SRV: debug.echo(9, "%s: processes %d/%d %s" % (srv.name, len(srv.childs), srv.MIN_CHILDS, srv.childs)) if len(srv.childs)0: debug.echo(1, "%s: WARNING: Respawning too fast!" " Please wait at least %d seconds!" % (srv.name, delta)) else: # start another child debug.echo(1, "%s: A new child was started ... [%s]" % (srv.name, srv.fork())) fork_time = time.time() except KeyboardInterrupt: for srv in SRV: srv.stop() sigterm('KeyboardInterrupt') # kill self