Ejecutar el servidor web de Python como servicio de Windows

Tengo scripts de servidor y consola que siguen escuchando en el puerto las solicitudes de consola y servidor.

En el entorno UNIX hice tanto el script del servidor como el de la consola como demonios que se ejecutan continuamente, lo que los mantendrá escuchando en el puerto.

¿Hay alguna manera en Windows para mantenerlos funcionando como demonio en UNIX? También quiero que se levanten al reiniciarse (debería iniciarse automáticamente al reiniciar)

Leí sobre servicios de Windows y seguí el código escrito aquí , pero obtengo un error 404 en mi página web

__version__ = "0.4" __all__ = ["RequestHandler"] import atexit import BaseHTTPServer import CGIHTTPServer import copy import os import select import SimpleHTTPServer import sys import time import threading import urllib from signal import SIGTERM from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler from SocketServer import ThreadingMixIn class ThreadedHTTPServer(ThreadingMixIn, HTTPServer): pass class RequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): have_fork = hasattr(os, 'fork') have_popen2 = hasattr(os, 'popen2') have_popen3 = hasattr(os, 'popen3') rbufsize = 0 def do_POST(self): if self.is_cgi(): self.run_cgi() else: self.send_error(501, "Can only POST to CGI scripts") def send_head(self): if self.is_cgi(): return self.run_cgi() else: return SimpleHTTPServer.SimpleHTTPRequestHandler.send_head(self) def is_cgi(self): splitpath = _url_collapse_path_split(self.path) if splitpath[0] in self.cgi_directories: self.cgi_info = splitpath return True return False cgi_directories = ['/cgi-bin', '/htbin'] def is_executable(self, path): return executable(path) def is_python(self, path): head, tail = os.path.splitext(path) return tail.lower() in (".py", ".pyw") def run_cgi(self): path = self.path dir, rest = self.cgi_info i = path.find('/', len(dir) + 1) while i >= 0: nextdir = path[:i] nextrest = path[i+1:] scriptdir = self.translate_path(nextdir) if os.path.isdir(scriptdir): dir, rest = nextdir, nextrest i = path.find('/', len(dir) + 1) else: break i = rest.rfind('?') if i >= 0: rest, query = rest[:i], rest[i+1:] else: query = '' i = rest.find('/') if i >= 0: script, rest = rest[:i], rest[i:] else: script, rest = rest, '' scriptname = dir + '/' + script scriptfile = self.translate_path(scriptname) if not os.path.exists(scriptfile): self.send_error(404, "No such CGI script (%r)" % scriptname) return if not os.path.isfile(scriptfile): self.send_error(403, "CGI script is not a plain file (%r)" % scriptname) return ispy = self.is_python(scriptname) if not ispy: if not (self.have_fork or self.have_popen2 or self.have_popen3): self.send_error(403, "CGI script is not a Python script (%r)" % scriptname) return if not self.is_executable(scriptfile): self.send_error(403, "CGI script is not executable (%r)" % scriptname) return # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html # XXX Much of the following could be prepared ahead of time! env = {} env['SERVER_SOFTWARE'] = self.version_string() env['SERVER_NAME'] = self.server.server_name env['GATEWAY_INTERFACE'] = 'CGI/1.1' env['SERVER_PROTOCOL'] = self.protocol_version env['SERVER_PORT'] = str(self.server.server_port) env['REQUEST_METHOD'] = self.command uqrest = urllib.unquote(rest) env['PATH_INFO'] = uqrest env['PATH_TRANSLATED'] = self.translate_path(uqrest) env['SCRIPT_NAME'] = scriptname if query: env['QUERY_STRING'] = query host = self.address_string() if host != self.client_address[0]: env['REMOTE_HOST'] = host env['REMOTE_ADDR'] = self.client_address[0] authorization = self.headers.getheader("authorization") if authorization: authorization = authorization.split() if len(authorization) == 2: import base64, binascii env['AUTH_TYPE'] = authorization[0] if authorization[0].lower() == "basic": try: authorization = base64.decodestring(authorization[1]) except binascii.Error: pass else: authorization = authorization.split(':') if len(authorization) == 2: env['REMOTE_USER'] = authorization[0] # XXX REMOTE_IDENT if self.headers.typeheader is None: env['CONTENT_TYPE'] = self.headers.type else: env['CONTENT_TYPE'] = self.headers.typeheader length = self.headers.getheader('content-length') if length: env['CONTENT_LENGTH'] = length referer = self.headers.getheader('referer') if referer: env['HTTP_REFERER'] = referer accept = [] for line in self.headers.getallmatchingheaders('accept'): if line[:1] in "\t\n\r ": accept.append(line.strip()) else: accept = accept + line[7:].split(',') env['HTTP_ACCEPT'] = ','.join(accept) ua = self.headers.getheader('user-agent') if ua: env['HTTP_USER_AGENT'] = ua co = filter(None, self.headers.getheaders('cookie')) if co: env['HTTP_COOKIE'] = ', '.join(co) # XXX Other HTTP_* headers # Since we're setting the env in the parent, provide empty # values to override previously set values for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH', 'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'): env.setdefault(k, "") os.environ.update(env) self.send_response(200, "Script output follows") decoded_query = query.replace('+', ' ') if self.have_fork: # Unix -- fork as we should args = [script] if '=' not in decoded_query: args.append(decoded_query) nobody = nobody_uid() self.wfile.flush() # Always flush before forking pid = os.fork() if pid != 0: # Parent pid, sts = os.waitpid(pid, 0) # throw away additional data [see bug #427345] while select.select([self.rfile], [], [], 0)[0]: if not self.rfile.read(1): break if sts: self.log_error("CGI script exit status %#x", sts) return # Child try: try: os.setuid(nobody) except os.error: pass os.dup2(self.rfile.fileno(), 0) os.dup2(self.wfile.fileno(), 1) os.execve(scriptfile, args, os.environ) except: self.server.handle_error(self.request, self.client_address) os._exit(127) else: # Non Unix - use subprocess import subprocess cmdline = [scriptfile] if self.is_python(scriptfile): interp = sys.executable if interp.lower().endswith("w.exe"): # On Windows, use python.exe, not pythonw.exe interp = interp[:-5] + interp[-4:] cmdline = [interp, '-u'] + cmdline if '=' not in query: cmdline.append(query) self.log_message("command: %s", subprocess.list2cmdline(cmdline)) try: nbytes = int(length) except (TypeError, ValueError): nbytes = 0 p = subprocess.Popen(cmdline, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE ) if self.command.lower() == "post" and nbytes > 0: data = self.rfile.read(nbytes) else: data = None # throw away additional data [see bug #427345] while select.select([self.rfile._sock], [], [], 0)[0]: if not self.rfile._sock.recv(1): break stdout, stderr = p.communicate(data) self.wfile.write(stdout) if stderr: self.log_error('%s', stderr) status = p.returncode if status: self.log_error("CGI script exit status %#x", status) else: self.log_message("CGI script exited OK") def _url_collapse_path_split(path): path_parts = [] for part in path.split('/'): if part == '.': path_parts.append('') else: path_parts.append(part) # Filter out blank non trailing parts before consuming the '..'. path_parts = [part for part in path_parts[:-1] if part] + path_parts[-1:] if path_parts: tail_part = path_parts.pop() else: tail_part = '' head_parts = [] for part in path_parts: if part == '..': head_parts.pop() else: head_parts.append(part) if tail_part and tail_part == '..': head_parts.pop() tail_part = '' return ('/' + '/'.join(head_parts), tail_part) nobody = None def nobody_uid(): """Internal routine to get nobody's uid""" global nobody if nobody: return nobody try: import pwd except ImportError: return -1 try: nobody = pwd.getpwnam('nobody')[2] except KeyError: nobody = 1 + max(map(lambda x: x[2], pwd.getpwall())) return nobody def executable(path): """Test for executable file.""" try: st = os.stat(path) except os.error: return False return st.st_mode & 0111 != 0 Handler = RequestHandler PORT = 7998 ADDRESS = "0.0.0.0" httpd = ThreadedHTTPServer((ADDRESS, PORT), Handler) print "serving at %s:%s" % (ADDRESS, PORT) import os import SocketServer import BaseHTTPServer import SimpleHTTPServer import xmlrpclib import SimpleXMLRPCServer import socket import httplib import inspect import win32service import win32serviceutil import win32api import win32con import win32event import win32evtlogutil class XMLRPCServerService(win32serviceutil.ServiceFramework): _svc_name_ = "XMLRPCServerService" _svc_display_name_ = "XMLRPCServerService" _svc_description_ = "Tests Python service framework by receiving and echoing messages over a named pipe" def __init__(self, args): win32serviceutil.ServiceFramework.__init__(self, args) self.hWaitStop = win32event.CreateEvent(None, 0, 0, None) def SvcStop(self): self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) self.ReportServiceStatus(win32service.SERVICE_STOPPED) win32event.SetEvent(self.hWaitStop) def SvcDoRun(self): import servicemanager servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,servicemanager.PYS_SERVICE_STARTED,(self._svc_name_, '')) self.timeout = 100 while 1: rc = win32event.WaitForSingleObject(self.hWaitStop, self.timeout) if rc == win32event.WAIT_OBJECT_0: servicemanager.LogInfoMsg("XMLRPCServerService - STOPPED") break else: httpd.serve_forever() servicemanager.LogInfoMsg("XMLRPCServerService - is alive and well") def ctrlHandler(ctrlType): return True if __name__ == '__main__': win32api.SetConsoleCtrlHandler(ctrlHandler, True) win32serviceutil.HandleCommandLine(XMLRPCServerService) 

¿Alguna pista de dónde me voy mal? O una buena manera de implementarlo (puede ser sin usar el servicio).

Nota estricta:

La solución debe estar en Python 2.6 (requisitos del proyecto).


Actualizaciones:

Vi algo extraño en el registro: python service.py debug

 127.0.0.1 - - [04/Apr/2014 09:41:04] command: C:\Python27\Lib\site-packages\win3 2\**pythonservice.exe** -u C:\CONSOLE-CGI\cgi-bin\login.py "" 

¿Por qué está ejecutando el script CGI usando pythonservice.exe ?

¿Que me estoy perdiendo aqui?

Más actualizaciones:

Fragmento de código del script de python del proceso del daemon

 #Non Unix - use subprocess import subprocess cmdline = [scriptfile] if self.is_python(scriptfile): #interp = sys.executable // here it return pythonservice.exe interp = "python.exe" // if I hardcode it to python.exe all goes fine if interp.lower().endswith("w.exe"): #On Windows,use python.exe,not pythonw.exe interp = interp[: -5] + interp[-4: ] cmdline = [interp, '-u'] + cmdline 

¿Alguna pista de por qué es así?