import os import signal import sys import errno from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer # BOOK-KEEPING _PIDS = [] def _kronos(signum, frame): '''As Kronos did before us, we too may have to devour our children''' for pid in _PIDS: try: os.kill(pid, signum) except OSError, e: if e.errno == errno.ESRCH: _PIDS.remove(pid) # wait on all child processes while len(_PIDS): pid, rc = os.waitpid(-1, 0) _PIDS.remove(pid) # exit non-zero to signal abnormal termination sys.exit(1) def _gogentle(signum, frame): '''Do not throw a KeyboardInterrupt Error''' os._exit(1) # API def add(x, y): '''Add x and y''' return x + y def subtract(x, y): '''Subtract y from x''' return x - y # server def main(name, *argv): global _PIDS # JSON-RPC over HTTP on INET socket localhost:8888 # under the hood, this calls `socket.bind` then `socket.listen` s = SimpleJSONRPCServer(( argv[0], int(argv[1]), )) # register our logging math functions for fn in ( add, subtract, ): s.register_function(fn) # simple pre-fork server, fork before accept for i in range(int(argv[2])): # fork our current process pid = os.fork() # if we are the child fork ... if 0 == pid: # die without unhandled exception for signum in ( signal.SIGINT, signal.SIGTERM, ): signal.signal(signum, _gogentle) # under the hood, this calls `socket.accept` s.serve_forever() os._exit(0) # if we are the papa fork else: _PIDS.append(pid) # setup signal relaying for INT and TERM for signum in ( signal.SIGINT, signal.SIGTERM, ): signal.signal(signum, _kronos) # wait on the kids while len(_PIDS): pid, rc = os.waitpid(-1, 0) _PIDS.remove(pid) return 0 if __name__ == '__main__': sys.exit(main(*sys.argv))