#!/usr/bin/python3 # _*_ coding:utf-8 _*_ ''' sagator.py (c) 2003-2020 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. ''' from __future__ import absolute_import from __future__ import print_function # 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 as 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 as err: (msg, opt) = err.args 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 as 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 as eces: print("Process not running [%s]?" % eces) sys.exit(1) except IOError as 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: if globals.pid_file: # open PID file try: pidf = open(globals.pid_file, 'w') pidf.write(str(os.getpid())) pidf.close() except Exception as e: debug.echo(1, "Error writing pid file %s: %s" % (globals.pid_file, e)) # first fork if os.fork()>0: time.sleep(0.1) os._exit(0) # try to create a new SID for the child process try: os.setsid() except OSError as 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) # save PID file if globals.pid_file: try: pidf = open(globals.pid_file, 'w') pidf.write(str(os.getpid())) pidf.close() except Exception as e: debug.echo(1, "Error writing pid file %s: %s" % (globals.pid_file, e)) # change current directory os.chdir("/") # try to close all unwanted fds for i, inode in list(alien_fds.items()): try: debug.echo(4, "Closing alien fd: %d [%s]" % (i, inode)) os.close(i) except OSError as 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 as 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) 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