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