Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Added django/core/servers/fastcgi.py and manage.py 'runfcgi' option. …

…Thanks, jcrasta@gmail.com

git-svn-id: http://code.djangoproject.com/svn/django/trunk@3174 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 32228d2031ac661305f219641cc1fc336cee1c2e 1 parent a4b1182
Adrian Holovaty authored June 20, 2006
1  AUTHORS
@@ -65,6 +65,7 @@ answer newbie questions, and generally made Django that much better:
65 65
     Kieran Holland <http://www.kieranholland.com>
66 66
     Robert Rock Howard <http://djangomojo.com/>
67 67
     Jason Huggins <http://www.jrandolph.com/blog/>
  68
+    jcrasta@gmail.com
68 69
     Michael Josephson <http://www.sdjournal.com/>
69 70
     jpellerin@gmail.com
70 71
     junzhang.jn@gmail.com
9  django/core/management.py
@@ -1081,6 +1081,12 @@ def dbshell():
1081 1081
     runshell()
1082 1082
 dbshell.args = ""
1083 1083
 
  1084
+def runfcgi(args):
  1085
+    """Run this project as a FastCGI application. requires flup."""
  1086
+    from django.core.servers.fastcgi import runfastcgi
  1087
+    runfastcgi(args)
  1088
+runfcgi.args = '[various KEY=val options, use `runfcgi help` for help]'
  1089
+
1084 1090
 # Utilities for command-line script
1085 1091
 
1086 1092
 DEFAULT_ACTION_MAPPING = {
@@ -1091,6 +1097,7 @@ def dbshell():
1091 1097
     'inspectdb': inspectdb,
1092 1098
     'install': install,
1093 1099
     'reset': reset,
  1100
+    'runfcgi': runfcgi,
1094 1101
     'runserver': runserver,
1095 1102
     'shell': run_shell,
1096 1103
     'sql': get_sql_create,
@@ -1210,6 +1217,8 @@ def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING, argv=None):
1210 1217
             except ValueError:
1211 1218
                 addr, port = '', args[1]
1212 1219
         action_mapping[action](addr, port)
  1220
+    elif action == 'runfcgi':
  1221
+        action_mapping[action](args[1:])
1213 1222
     else:
1214 1223
         from django.db import models
1215 1224
         try:
147  django/core/servers/fastcgi.py
... ...
@@ -0,0 +1,147 @@
  1
+"""
  2
+FastCGI server that implements the WSGI protocol.
  3
+
  4
+Uses the flup python package: http://www.saddi.com/software/flup/
  5
+
  6
+This is a adaptation of the flup package to add FastCGI server support
  7
+to run Django apps from Web servers that support the FastCGI protocol.
  8
+This module can be run standalone or from the django-admin / manage.py
  9
+scripts using the "runfcgi" directive.
  10
+
  11
+Run with the extra option "help" for a list of additional options you can
  12
+pass to this server.
  13
+"""
  14
+
  15
+import sys, os
  16
+
  17
+__version__ = "0.1"
  18
+__all__ = ["runfastcgi"]
  19
+
  20
+FASTCGI_HELP = r"""runfcgi:
  21
+  Run this project as a fastcgi application. To do this, the
  22
+  flup package from http://www.saddi.com/software/flup/ is
  23
+  required.
  24
+
  25
+Usage:
  26
+   django-admin.py runfcgi --settings=yourproject.settings [fcgi settings]
  27
+   manage.py runfcgi [fcgi settings]
  28
+
  29
+Optional Fcgi settings: (setting=value)
  30
+  host=HOSTNAME        hostname to listen on..
  31
+  port=PORTNUM         port to listen on.
  32
+  socket=FILE          UNIX socket to listen on.
  33
+  method=IMPL          prefork or threaded (default prefork)
  34
+  maxspare=NUMBER      max number of spare processes to keep running.
  35
+  minspare=NUMBER      min number of spare processes to prefork.
  36
+  maxchildren=NUMBER   hard limit number of processes in prefork mode.
  37
+  daemonize=BOOL       whether to detach from terminal.
  38
+  pidfile=FILE         write the spawned process-id to this file.
  39
+  workdir=DIRECTORY    change to this directory when daemonizing
  40
+
  41
+Examples:
  42
+  Run a "standard" fastcgi process on a file-descriptor
  43
+  (for webservers which spawn your processes for you)
  44
+    $ manage.py runfcgi method=threaded
  45
+
  46
+  Run a fastcgi server on a TCP host/port
  47
+    $ manage.py runfcgi method=prefork host=127.0.0.1 port=8025
  48
+
  49
+  Run a fastcgi server on a UNIX domain socket (posix platforms only)
  50
+    $ manage.py runfcgi method=prefork socket=/tmp/fcgi.sock
  51
+
  52
+  Run a fastCGI as a daemon and write the spawned PID in a file
  53
+    $ manage.py runfcgi socket=/tmp/fcgi.sock method=prefork \
  54
+        daemonize=true pidfile=/var/run/django-fcgi.pid
  55
+
  56
+"""
  57
+
  58
+FASTCGI_OPTIONS = {
  59
+    'host': None,
  60
+    'port': None,
  61
+    'socket': None,
  62
+    'method': 'fork',
  63
+    'daemonize': None,
  64
+    'workdir': '/',
  65
+    'pidfile': None,
  66
+    'maxspare': 5,
  67
+    'minspare': 2,
  68
+    'maxchildren': 50,
  69
+}
  70
+
  71
+def fastcgi_help(message=None):
  72
+    print FASTCGI_HELP
  73
+    if message:
  74
+        print message
  75
+    return False
  76
+
  77
+def runfastcgi(argset):
  78
+    options = FASTCGI_OPTIONS.copy()
  79
+    for x in argset:
  80
+        if "=" in x:
  81
+            k, v = x.split('=', 1)
  82
+        else:
  83
+            k, v = x, True
  84
+        options[k.lower()] = v
  85
+
  86
+    if "help" in options:
  87
+        return fastcgi_help()
  88
+
  89
+    try:
  90
+        import flup
  91
+    except ImportError, e:
  92
+        print >> sys.stderr, "ERROR: %s" % e
  93
+        print >> sys.stderr, "  Unable to load the flup package.  In order to run django"
  94
+        print >> sys.stderr, "  as a FastCGI application, you will need to get flup from"
  95
+        print >> sys.stderr, "  http://www.saddi.com/software/flup/   If you've already"
  96
+        print >> sys.stderr, "  installed flup, then make sure you have it in your PYTHONPATH."
  97
+        return False
  98
+
  99
+    if options['method'] in ('prefork', 'fork'):
  100
+        from flup.server.fcgi_fork import WSGIServer
  101
+        wsgi_opts = {
  102
+            'maxSpare': int(options["maxspare"]),
  103
+            'minSpare': int(options["minspare"]),
  104
+            'maxChildren': int(options["maxchildren"]),
  105
+        }
  106
+    elif options['method'] in ('thread', 'threaded'):
  107
+        from flup.server.fcgi import WSGIServer
  108
+        wsgi_opts = {}
  109
+    else:
  110
+        return fastcgi_help("ERROR: Implementation must be one of prefork or thread.")
  111
+
  112
+    # Prep up and go
  113
+    from django.core.handlers.wsgi import WSGIHandler
  114
+
  115
+    if options["host"] and options["port"] and not options["socket"]:
  116
+        wsgi_opts['bindAddress'] = (options["host"], int(options["port"]))
  117
+    elif options["socket"] and not options["host"] and not options["port"]:
  118
+        wsgi_opts['bindAddress'] = options["socket"]
  119
+    elif not options["socket"] and not options["host"] and not options["port"]:
  120
+        wsgi_opts['bindAddress'] = None
  121
+    else:
  122
+        return fastcgi_help("Invalid combination of host, port, socket.")
  123
+
  124
+    if options["daemonize"] is None:
  125
+        # Default to daemonizing if we're running on a socket/named pipe.
  126
+        daemonize = (wsgi_opts['bindAddress'] is not None)
  127
+    else:
  128
+        if options["daemonize"].lower() in ('true', 'yes', 't'):
  129
+            daemonize = True
  130
+        elif options["daemonize"].lower() in ('false', 'no', 'f'):
  131
+            daemonize = False
  132
+        else:
  133
+            return fastcgi_help("ERROR: Invalid option for daemonize parameter.")
  134
+
  135
+    if daemonize:
  136
+        from django.utils.daemonize import become_daemon
  137
+        become_daemon(our_home_dir=options["workdir"])
  138
+
  139
+    if options["pidfile"]:
  140
+        fp = open(options["pidfile"], "w")
  141
+        fp.write("%d\n" % os.getpid())
  142
+        fp.close()
  143
+
  144
+    WSGIServer(WSGIHandler(), **wsgi_opts).run()
  145
+
  146
+if __name__ == '__main__':
  147
+    runfastcgi(sys.argv[1:])
55  django/utils/daemonize.py
... ...
@@ -0,0 +1,55 @@
  1
+import os
  2
+import sys
  3
+
  4
+if os.name == 'posix':
  5
+    def become_daemon(our_home_dir='.', out_log='/dev/null', err_log='/dev/null'):
  6
+        "Robustly turn into a UNIX daemon, running in our_home_dir."
  7
+        # First fork
  8
+        try:
  9
+            if os.fork() > 0:
  10
+                sys.exit(0)     # kill off parent
  11
+        except OSError, e:
  12
+            sys.stderr.write("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror))
  13
+            sys.exit(1)
  14
+        os.setsid()
  15
+        os.chdir(our_home_dir)
  16
+        os.umask(0)
  17
+
  18
+        # Second fork
  19
+        try:
  20
+            if os.fork() > 0:
  21
+                sys.exit(0)
  22
+        except OSError, e:
  23
+            sys.stderr.write("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror))
  24
+            sys.exit(1)
  25
+
  26
+        si = open('/dev/null', 'r')
  27
+        so = open(out_log, 'a+', 0)
  28
+        se = open(err_log, 'a+', 0)
  29
+        os.dup2(si.fileno(), sys.stdin.fileno())
  30
+        os.dup2(so.fileno(), sys.stdout.fileno())
  31
+        os.dup2(se.fileno(), sys.stderr.fileno())
  32
+else:
  33
+    def become_daemon(our_home_dir='.', out_log=None, err_log=None):
  34
+        """
  35
+        If we're not running under a POSIX system, just simulate the daemon
  36
+        mode by doing redirections and directory changing.
  37
+        """
  38
+        os.chdir(our_home_dir)
  39
+        os.umask(0)
  40
+        sys.stdin.close()
  41
+        sys.stdout.close()
  42
+        sys.stderr.close()
  43
+        if err_log:
  44
+            sys.stderr = open(err_log, 'a', 0)
  45
+        else:
  46
+            sys.stderr = NullDevice()
  47
+        if out_log:
  48
+            sys.stdout = open(out_log, 'a', 0)
  49
+        else:
  50
+            sys.stdout = NullDevice()
  51
+
  52
+    class NullDevice:
  53
+        "A writeable object that writes to nowhere -- like /dev/null."
  54
+        def write(self, s):
  55
+            pass

0 notes on commit 32228d2

Please sign in to comment.
Something went wrong with that request. Please try again.