GROT 239: Re: [Tutor] Bug with fork / socket from CGI
Jonathan Hayward http://JonathansCorner.com
jonathan.hayward@pobox.com
Fri Aug 1 16:08:02 2003
Lloyd Kvam wrote:
> I did not attempt to figure out your problem, the cgitb module with
> its traceback facility usually helps a great deal with debugging an
> errant cgi script.
When I used cgitb, it gave errno 9, 'Bad file descriptor' to this line:
sock.connect((configuration.get_search_server_ip(), \
configuration.get_search_server_port()))
Evaluated, that comes to:
sock.connect(("127.0.0.1", 1374))
Cgitb has helped me know what I need to be using right, but it won't
explain the socket concept I'm missing--that's why I e-mailed the list.
Do you or anyone else on the list know what needs to be changed about
the socket so the above line will be corrected?
TIA
>
> However, wouldn't it be simpler to use fastCGI or an equivalent
> connection
> between the web server and the persistent process?
I looked in to it. That's great if I want to have my private server
running things, but I want to have something that people can install PnP
without repeating the extras I set up.
>
> Jonathan Hayward http://JonathansCorner.com wrote:
>
>> I'm trying to make a CGI script that, on first invocation, starts a
>> daemon. Every invocation from then on queries the daemon via a socket.
>>
>> As is, the first invocation hangs, and subsequent invocations give an
>> error ('Bad file descriptor' refers to a socket.makefile() variable):
>>
>> There was an error loading this page.
>> (9, 'Bad file descriptor')
>>
>> Here's the multitasking object code. Any bugfixes or corrections
>> welcome.
>>
>> class multitasking_manager(ancestor):
>> """Class to handle multithreading and multiprocessing material."""
>> def __init__(self):
>> ancestor.__init__(self)
>> self.thread_specific_storage = {}
>> self.thread_specific_storage_lock = thread.allocate_lock()
>> self.is_in_check_and_appropriately_update_stored_information = 0
>> def check_and_appropriately_update_stored_information(self):
>> self.is_in_check_and_appropriately_update_stored_information = 1
>> # Check if databases etc. should be updated, and if so perform
>> # appropriate updates.
>> self.is_in_check_and_appropriately_update_stored_information = 0
>> def check_and_start_oracle(self):
>> if not self.is_oracle_running():
>> self.start_oracle()
>> def get_page_from_oracle(self):
>> self.check_and_start_oracle()
>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>> sockIn = sock.makefile("r")
>> sockOut = sock.makefile("wb")
>> sock.close()
>> 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()
>> 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_oracle_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_oracle(self):
>> thread.start_new_thread(\
>> self.check_and_appropriately_update_stored_information, ())
>> 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()
>> thread.start_new_thread(self.run_oracle_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_oracle_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")
>> sock.close()
>> 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)
>> line = sockIn.readline()
>> else:
>> generate_output()
>> print_output(sockOut)
>> sockIn.close()
>> sockOut.close()
>> def start_oracle(self):
>> try:
>> first_pid = os.fork()
>> except OSError, e:
>> log_error("Failed to make first fork for oracle. 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 oracle.
>> Error: " + \
>> e.strerror)
>> return
>> if second_pid == 0:
>> self.run_oracle()
>> else:
>> sys.exit(0)
>>
>>
>
--
++ Jonathan Hayward, jonathan.hayward@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