Bug help: CGI forking

Jonathan Hayward jonathan.hayward at pobox.com
Fri Aug 1 01:24:33 CEST 2003


I am trying to have a CGI script which forks a daemon the first time,
and on subsequent times asks the daemon to do all the work. The daemon
and the CGI script are run by the same code.

I'm trying to have a class that will handle daemon multithreading, the
"hollow shell" CGI script starting up the daemon, and the CGI script
talking with the daemon. I tried to use recipes from O'Reilly's
_Python Cookbook_, but I must have messed up. The script usually
hangs, and when it doesn't hang, the daemon sometimes isn't started. I
think I have a fork bug, at least.

Any bugfixes and/or clarifications welcome:

class multitasking_manager(ancestor):
    """Class to handle multithreading and multiprocessing material."""
    def __init__(self):
        ancestor.__init__(self)
        thread_specific_storage = {}
        thread_specific_storage_lock = thread.allocate_lock()
    def check_and_start_daemon(self):
        if not self.is_daemon_running():
            self.start_daemon()
    def get_page_from_daemon(self):
        self.check_and_start_daemon()
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sockIn = sock.makefile("r")
        sockOut = sock.makefile("wb")
        try:
            sock.connect((configuration.get_search_server_ip(), \
              configuration.get_search_server_port()))
            for current_environment_key in os.environ.keys():
                sockOut.write("environmental_variable " + \
                  current_environment_key + "\r\n")
                cPickle.dump(os.environ[current_environment_key],
sockOut)
            for cgi_key in cgi.FieldStorage().keys():
                sockOut.write("cgi_value " + cgi_key + "\r\n")
                cPickle.dump(cgi.FieldStorage[cgi_key])
            sockOut.write("\r\n")
            result = cPickle.load(sockIn)
            sockOut.close()
            sockIn.close()
            sock.close()
        except socket.error, e:
            return "Content-type: text/html\n\n<h1>There was an error
loading this page.</h1>" + str(e)
    def get_thread_specific_storage():
        thread_id = thread.get_ident()
        result = thread_specific_storage.get(thread_id)
        if thread_specific_storage is None:
            try:
                thread_specific_storage_lock.acquire()
                thread_specific_storaget[thread_id] = result = {}
            finally:
                thread_specific_storage_lock.release()
        return result
    def is_daemon_running(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            sock.connect((configuration.get_search_server_ip(), \
              configuration.get_search_server_port()))
            sock.close()
            return 1
        except socket.error:
            return 0
    def run_daemon(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.bind(("", configuration.get_search_server_port()))
        sock.listen(5)
        while 1:
            try:
                newsocket, address = sock.accept()
                start_new_thread(self.run_daemon_thread, (newsocket,
address))
            except socket.error:
                sock.close()
                sock = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
                sock.setsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR, 1)
                sock.bind(("",
configuration.get_search_server_port()))
                sock.listen(5)
    def run_daemon_thread(sock, address):
        """Reads a CGI or other header variable alone on a line,
format like
            cgi_value <HTML form element name>
            environmental_variable REMOTE_ADDR
        and then a pickled value. There is exactly one space between
the two
        elements, and neither element may contain a space"""
        sockIn = sock.makefile("wb")
        sockOut = sock.makefile("r")
        line = sockIn.readline()
        while line:
            if get_thread_specific_storage()["cgi"] == None:
                get_thread_specific_storage()["cgi"] = {}
            if get_thread_specific_storage()["environmental_variables"]
== \
              None:
                get_thread_specific_storage()["environmental_variables"]
= {}
            cgi = get_thread_specific_storage["cgi"]
            environmental_variables = \
              get_thread_specific_storage["environmental_variables"]
            line = re.sub("[\r\n]+", "", line)
            if line != "":
                query_line = re.split("\s+", line)
                input_type = query_line[0]
                input_name = query_line[1]
                if input_type == "cgi_value":
                    cgi[input_name] = cPickle.load(sockIn)
                elif input_type == "environmental_variables":
                    environmental_variables[input_name] =
cPickle.load(sockIn)
            else:
                generate_output()
                print_output(sockOut)
            line = sockIn.readline()
        sockIn.close()
        sockOut.close()
        sock.close()
    def start_daemon(self):
        try:
            first_pid = os.fork()
        except OSError, e:
            log_error("Failed to make first fork for daemon. Error: "
+ \
              e.strerror)
            return
        if first_pid == 0:
            os.chdir("/")
            os.setsid()
            os.umask(066)
            try:
                second_pid = os.fork()
            except OSError, e:
                log_error("Failed to make second fork for daemon.
Error: " + \
                  e.strerror)
                return
            if second_pid == 0:
                self.run_daemon()


++ Jonathan Hayward, jonathan.hayward at pobox.com
** To see an award-winning website with stories, essays, artwork,
** games, and a four-dimensional maze, why not visit my home page?
** All of this is waiting for you at http://JonathansCorner.com




More information about the Python-list mailing list