Index: django/core/servers/fastcgi.py
===================================================================
--- django/core/servers/fastcgi.py	(revision 0)
+++ django/core/servers/fastcgi.py	(revision 0)
@@ -0,0 +1,148 @@
+"""
+FastCGI server that implements the WSGI protocol.
+
+Uses the flup python package: http://www.saddi.com/software/flup/
+
+This is a adaptation of the flup package to add FastCGI server support
+to run Django apps from web-servers which support the FastCGI protocol;
+this module can be run stand-alone or from the django-admin / manage.py
+scripts using the `runfcgi` directive.
+
+Run with the extra option "help" for a list of additional options you can
+pass to this server.
+"""
+
+import sys, os
+
+__version__ = "0.1"
+__all__ = ["runfastcgi"]
+
+FASTCGI_HELP = r"""runfcgi:
+  Run this project as a fastcgi application. To do this, the
+  flup package from http://www.saddi.com/software/flup/ is
+  required.
+
+Usage:
+   django-admin.py runfcgi --settings=yourproject.settings [fcgi settings]
+   manage.py runfcgi [fcgi settings]
+     
+Optional Fcgi settings: (setting=value)
+  host=HOSTNAME        hostname to listen on..
+  port=PORTNUM         port to listen on.
+  socket=FILE          UNIX socket to listen on.
+  method=IMPL          prefork or threaded (default prefork)
+  maxspare=NUMBER      max number of spare processes to keep running.
+  minspare=NUMBER      min number of spare processes to prefork.
+  maxchildren=NUMBER   hard limit number of processes in prefork mode.
+  daemonize=BOOL       whether to detach from terminal.
+  pidfile=FILE         write the spawned process-id to this file.
+  workdir=DIRECTORY    change to this directory when daemonizing
+
+Examples:
+  Run a "standard" fastcgi process on a file-descriptor 
+  (for webservers which spawn your processes for you)
+    $ manage.py runfcgi method=threaded
+
+  Run a fastcgi server on a TCP host/port
+    $ manage.py runfcgi method=prefork host=127.0.0.1 port=8025
+
+  Run a fastcgi server on a UNIX domain socket (posix platforms only)
+    $ manage.py runfcgi method=prefork socket=/tmp/fcgi.sock
+
+  Run a fastCGI as a daemon and write the spawned PID in a file
+    $ manage.py runfcgi socket=/tmp/fcgi.sock method=prefork \
+        daemonize=true pidfile=/var/run/django-fcgi.pid
+  
+"""
+
+FASTCGI_OPTIONS = {
+    'host': None,
+    'port': None,
+    'socket': None,
+    'method': 'fork',
+    'daemonize': None,
+    'workdir': '/',
+    'pidfile': None,
+    'maxspare': 5,
+    'minspare': 2,
+    'maxchildren': 50,
+}
+
+def fastcgi_help(message = None):
+    print FASTCGI_HELP
+    if message:
+        print message
+    return False
+
+
+def runfastcgi(argset):
+    options = FASTCGI_OPTIONS.copy()
+    for x in argset:
+        if "=" in x:
+            k, v = x.split('=', 1)
+        else:
+            k,v = x, True
+        options[k.lower()] = v
+
+    if "help" in options:
+        return fastcgi_help()
+
+    try:
+        import flup
+    except ImportError, e:
+        print >> sys.stderr, "ERROR: %s" % e
+        print >> sys.stderr, "  Unable to load the flup package.  In order to run django"
+        print >> sys.stderr, "  as a FastCGI application, you will need to get flup from"
+        print >> sys.stderr, "  http://www.saddi.com/software/flup/   If you have flup already"
+        print >> sys.stderr, "  installed, then make sure you have it in your PYTHONPATH."
+        return False
+        
+    if options['method'] in ('prefork', 'fork'):
+        from flup.server.fcgi_fork import WSGIServer
+        wsgi_opts = dict(
+            maxSpare = int(options["maxspare"]),
+            minSpare = int(options["minspare"]),
+            maxChildren = int(options["maxchildren"]),
+        )
+    elif options['method'] in ('thread', 'threaded'):
+        from flup.server.fcgi import WSGIServer 
+        wsgi_opts = {}
+    else:
+        return fastcgi_help("ERROR: implementation must be one of prefork or thread")
+
+    # prep up and go
+    from django.core.handlers.wsgi import WSGIHandler
+
+    if options["host"] and options["port"] and not options["socket"]:
+        wsgi_opts['bindAddress'] = (options["host"], int(options["port"]))
+    elif options["socket"] and not options["host"] and not options["port"]:
+        wsgi_opts['bindAddress'] = options["socket"]
+    elif not options["socket"] and not options["host"] and not options["port"]:
+        wsgi_opts['bindAddress'] = None
+    else:
+        return fastcgi_help("invalid combination of host,port,socket.")
+
+    if options["daemonize"] is None:
+        # default to daemonizing if we are running on a socket/named pipe
+        daemonize = (wsgi_opts['bindAddress'] is not None) 
+    else:
+        if options["daemonize"].lower() in ('true', 'yes', 't'):
+            daemonize = True
+        elif options["daemonize"].lower() in ('false', 'no', 'f'):
+            daemonize = False
+        else:
+            return fastcgi_help("ERROR: invalid option for daemonize parameter")
+    
+    if daemonize:
+        from django.utils.daemonize import become_daemon
+        become_daemon(ourHomeDir=options["workdir"])
+
+    if options["pidfile"]:
+        fp = open(options["pidfile"], "w")
+        fp.write("%d\n" % os.getpid())
+        fp.close()
+
+    WSGIServer(WSGIHandler(), **wsgi_opts).run()
+
+if __name__ == '__main__':
+    runfastcgi(sys.argv[1:])
Index: django/core/management.py
===================================================================
--- django/core/management.py	(revision 3147)
+++ django/core/management.py	(working copy)
@@ -1081,6 +1081,12 @@
     runshell()
 dbshell.args = ""
 
+def runfcgi(args):
+    """Run this project as a FastCGI application. requires flup."""
+    from django.core.servers.fastcgi import runfastcgi
+    runfastcgi(args)
+runfcgi.args = '[various KEY=val options, use `runfcgi help` for help]'
+
 # Utilities for command-line script
 
 DEFAULT_ACTION_MAPPING = {
@@ -1091,6 +1097,7 @@
     'inspectdb': inspectdb,
     'install': install,
     'reset': reset,
+    'runfcgi': runfcgi,
     'runserver': runserver,
     'shell': run_shell,
     'sql': get_sql_create,
@@ -1210,6 +1217,8 @@
             except ValueError:
                 addr, port = '', args[1]
         action_mapping[action](addr, port)
+    elif action == 'runfcgi':
+        action_mapping[action](args[1:])
     else:
         from django.db import models
         try:
Index: django/utils/daemonize.py
===================================================================
--- django/utils/daemonize.py	(revision 0)
+++ django/utils/daemonize.py	(revision 0)
@@ -0,0 +1,71 @@
+import os
+import sys
+
+if os.name == 'posix':
+
+	def become_daemon(ourHomeDir='.',outLog='/dev/null',errLog='/dev/null'):
+		"""
+		Robustly turn us into a UNIX daemon, running in ourHomeDir.
+		Modelled after the original code of this module and some
+		sample code from the net.
+		"""
+
+		# first fork
+		try:
+			if os.fork() > 0:
+				sys.exit(0)     # kill off parent
+		except OSError, e:
+			sys.stderr.write("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror))
+			sys.exit(1)
+		os.setsid()
+		os.chdir(ourHomeDir)
+		os.umask(0)
+		# second fork
+		try:
+			if os.fork() > 0:
+				sys.exit(0)
+		except OSError, e:
+			sys.stderr.write("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror))
+			sys.exit(1)
+
+		si = open('/dev/null', 'r')
+		so = open(outLog, 'a+', 0)
+		se = open(errLog, 'a+', 0)
+		os.dup2(si.fileno(), sys.stdin.fileno())
+		os.dup2(so.fileno(), sys.stdout.fileno())
+		os.dup2(se.fileno(), sys.stderr.fileno())
+
+else:
+
+	def become_daemon(ourHomeDir='.',outLog=None,errLog=None):
+		"""
+		If we are not running under a POSIX system, just simulate
+		the daemon mode by doing redirections and directory changeing
+		"""
+
+		os.chdir(ourHomeDir)
+		os.umask(0)
+		sys.stdin.close()
+		sys.stdout.close()
+		sys.stderr.close()
+        	if errLog and outLog:
+            		sys.stderr=open (errLog, 'a', 0)
+            		sys.stdout=open (outLog, 'a', 0)
+		elif errLog:
+	    		sys.stderr=open (errLog, 'a', 0)
+	    		sys.stdout=NullDevice ()
+		elif outLog:
+	    		sys.stdout=open (outLog, 'a', 0)
+	    		sys.stderr=NullDevice ()
+        	else:
+	    		sys.stdout = NullDevice()
+	    		sys.stderr = NullDevice()
+
+	class NullDevice:
+		"""
+		A substitute for stdout and stderr that writes to nowhere.
+		This is a substitute for /dev/null
+		"""
+	
+		def write(self, s):
+			pass
