#!/usr/bin/env python """$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/server/scgi_server.py $ $Id: scgi_server.py 25579 2004-11-11 20:56:32Z nascheme $ A SCGI server that uses Quixote to publish dynamic content. """ import os,signal,sys from scgi import scgi_server from quixote.http_request import HTTPRequest pidfilename = None class QuixoteHandler(scgi_server.SCGIHandler): def __init__(self, parent_fd, create_publisher, script_name=None): scgi_server.SCGIHandler.__init__(self, parent_fd) self.publisher = create_publisher() self.script_name = script_name def handle_connection(self, conn): input = conn.makefile("r") output = conn.makefile("w") env = self.read_env(input) if self.script_name is not None: # mod_scgi doesn't know SCRIPT_NAME :-( prefix = self.script_name path = env['SCRIPT_NAME'] assert path[:len(prefix)] == prefix, ( "path %r doesn't start with script_name %r" % (path, prefix)) env['SCRIPT_NAME'] = prefix env['PATH_INFO'] = path[len(prefix):] + env.get('PATH_INFO', '') request = HTTPRequest(input, env) response = self.publisher.process_request(request) try: response.write(output) input.close() output.close() conn.close() except IOError, err: self.publisher.log("IOError while sending response " "ignored: %s" % err) def change_uid_gid(uid, gid=None): "Try to change UID and GID to the provided values" # This will only work if this script is run by root. # Try to convert uid and gid to integers, in case they're numeric import pwd, grp try: uid = int(uid) default_grp = pwd.getpwuid(uid)[3] except ValueError: uid, default_grp = pwd.getpwnam(uid)[2:4] if gid is None: gid = default_grp else: try: gid = int(gid) except ValueError: gid = grp.getgrnam(gid)[2] os.setgid(gid) os.setuid(uid) def term_signal(signum, frame): global pidfilename print "Shoutdown wpos-scgi instance" try: os.unlink(pidfilename) except OSError: pass sys.exit() def run(create_publisher, host='', port=3000, script_name=None, max_children=5, daemon=None, logfilename=None, pidfile_name=None, uid=None): global pidfilename pidfilename = pidfile_name if os.getuid() == 0 and uid: change_uid_gid(uid) def create_handler(parent_fd): return QuixoteHandler(parent_fd, create_publisher, script_name) if not daemon: print "Start wpos-scgi, logs on console" scgi_server.SCGIServer(create_handler, host=host, port=port,max_children=max_children).serve() else: log = open(logfilename, "a", 1) os.dup2(log.fileno(), 1) os.dup2(log.fileno(), 2) os.close(0) print "Start wpos-scgi instance" pid = os.fork() if pid == 0: pid = os.getpid() if pidfilename: pidfile = open(pidfilename, 'w') pidfile.write(str(pid)) pidfile.close() signal.signal(signal.SIGTERM, term_signal) try: scgi_server.SCGIServer(create_handler, host=host, port=port,max_children=max_children).serve() finally: # grandchildren get here too, don't let them unlink the pid if pid == os.getpid(): try: os.unlink(pidfilename) except OSError: pass def main(): from optparse import OptionParser from quixote.util import import_object parser = OptionParser() parser.set_description(run.__doc__) daemon = None parser.add_option( '--daemon', dest="daemon", default=daemon, type="string", help="Run as daemon or not. (default=%s)" % daemon) default_log = '/var/tmp/wpos-scgi.log' parser.add_option( '--log', dest="log", default=default_log, type="string", help="Log file (default=%s)" % default_log) default_pid = '/var/tmp/wpos-scgi.pid' parser.add_option( '--pid', dest="pid", default=default_pid, type="string", help="Pid file (default=%s)" % default_pid) default_uid = None parser.add_option( '--uid', dest="uid", default=default_uid, type="string", help="UID to switch to (default=%s)" % default_uid) default_host = 'localhost' parser.add_option( '--host', dest="host", default=default_host, type="string", help="Log file (default=%s)" % default_host) default_port = 3000 parser.add_option( '--port', dest="port", default=default_port, type="int", help="Port to listen on. (default=%s)" % default_port) default_maxchild = 5 parser.add_option( '--max-children', dest="maxchild", default=default_maxchild, type="string", help="Maximum number of children to spawn. (default=%s)" % default_maxchild) parser.add_option( '--script-name', dest="script_name", default=None, type="string", help="Value of SCRIPT_NAME (only needed if using mod_scgi)") default_factory = 'quixote.demo.create_publisher' parser.add_option( '--factory', dest="factory", default=default_factory, help="Path to factory function to create the site Publisher. " "(default=%s)" % default_factory) (options, args) = parser.parse_args() run(import_object(options.factory), host=options.host, port=options.port, script_name=options.script_name, max_children=options.maxchild, daemon=options.daemon,logfilename=options.log,pidfile_name=options.pid,uid=options.uid) if __name__ == '__main__': main()