fastcgi: Add init script for fastcgi processes.
authorroot <root@template.drsnuggles.stderr.nl>
Fri, 7 Nov 2008 21:56:56 +0000 (22:56 +0100)
committerroot <root@template.drsnuggles.stderr.nl>
Fri, 7 Nov 2008 21:57:28 +0000 (22:57 +0100)
This script will for now ignore its command line arguments and always
restart all scripts. This should be improved in the future.

etc/init.d/fastcgi [new file with mode: 0755]

diff --git a/etc/init.d/fastcgi b/etc/init.d/fastcgi
new file mode 100755 (executable)
index 0000000..6882ff9
--- /dev/null
@@ -0,0 +1,134 @@
+#!/usr/bin/python
+import os
+import re
+import pwd
+import grp
+import shutil
+import stat
+
+ROOT_DIR="/data/www"
+
+# SITES = [(sitename, application_list)]
+# application_list = [application_name, (application_name, command, ...)]
+# Here, sitename is the name of the site. This folder name should exist below ROOT_DIR and
+# is also used below SOCKET_DIR. The site name is also translated to a user and
+# group name by replacing dots by dashes and prepending USER_PREFIX and
+# GROUP_PREFIX.
+#
+# application_list specifies the applications to start for this site. These can
+# be generic (when only application_name is given), in which case the command
+# is looked up in APPLICATIONS using the application_name. For a site-specific
+# application, command is the command that should be run. It will be prefixed
+# with the site's root dir, if is not an absolute path.
+
+SITES=[
+#      ('stderr.nl',             ['php']),
+#      ('stdin.nl',              ['php']),
+       ('stdout.nl',             ['php']),
+#      ('evolution-events.nl',   ['php']), #, ('xerxes', 'applications/xerxes/manage.py runfcgi'), ('wipi', 'applications/wipi/wipi.fcgi')]),
+#      ('stdio.flexvps.nl',      ['php']),
+#      ('foresightsecurity.nl',  ['php']),
+]
+
+# Generic applications that can be run for any site
+# Maps application_name to application_command. application_command will be
+# prefixed with the site's root dir, if it is not an absolute path.
+APPLICATIONS={"php": "/usr/bin/php-cgi"}
+
+# Kill these procs before starting new ones. Only processes of these names that
+# are run by the sites in SITES are killed. This is a bit hackish, we should
+# really be using pidfiles...
+KILL_PROCS=['php-cgi', 'manage.py']
+
+## ABSOLUTE path to the spawn-fcgi binary
+SPAWNFCGI="/usr/bin/spawn-fcgi"
+
+## Dir in which to create the UNIX sockets to listen on
+SOCKET_DIR="%s/var/fcgi" % (ROOT_DIR)
+
+## number of PHP children to spawn
+PHP_FCGI_CHILDREN=2
+
+## maximum number of requests a single PHP process can serve before it is restarted
+PHP_FCGI_MAX_REQUESTS=1000
+
+# The user to run as, will be prefixed to the sitename
+USER_PREFIX="httpd-"
+# The group to run as.
+SCRIPT_GROUP="httpd-users"
+# The group that should be able to use the sockets created
+HTTPD_GROUP="www-data"
+
+# Will be postfixed to the site's root and exported in the PHPRC variable.
+PHPRC_DIR="conf"
+
+#### END OF CONFIG ####
+
+for (site, apps) in SITES:
+       site_name = re.sub('\.', '-', site)
+
+       ## switch to the following user / group
+       user_id    = "%s%s" % (USER_PREFIX, site_name)
+       
+       # Find the site dir
+       site_dir   = os.path.join(ROOT_DIR, site)
+       socket_dir = os.path.join(SOCKET_DIR, site_name)
+
+       if not site_dir:
+               raise Exception("Site dir does not exist: %s" % (site_dir))
+       
+       
+       # Kill existing processes first
+       for procname in KILL_PROCS:
+               os.system('killall --user %s %s' % (user_id, procname))
+
+       # Remove old sockets
+       if os.path.exists(socket_dir): 
+               shutil.rmtree(socket_dir)
+
+       # Create dir for sockets. Make owning group root and set group write
+       # permissions, so the mask field in the acl will not block out anything.
+       os.makedirs(socket_dir)
+       os.chown(socket_dir, pwd.getpwnam(user_id)[2], grp.getgrnam(HTTPD_GROUP)[2])
+       #os.chmod(socket_dir, stat.S_IRWXU)
+       
+       for app in apps:
+               # Unpack app tuple or lookup app command in APPLICATIONS
+               if isinstance(app, tuple):
+                       if len(app) == 2:
+                               (app_name, app_command) = app
+                       else:
+                               raise Exception("Wrong number of elements in site tuple: %s", app)
+               else:
+                       app_name = app
+                       app_command = APPLICATIONS[app_name]
+       
+               # Prefix with site dir if not an absolute path
+               if not os.path.isabs(app_command):
+                       app_command = os.path.join(site_dir, app_command)
+
+               # Create socket filename
+               socket = os.path.join(socket_dir, app_name)
+
+               # Build the command
+               # TODO: Wrap this in env to clear up the environment
+               spawnfcgi = '%s -s "%s" -u "%s" -g "%s"' % (SPAWNFCGI, socket, user_id, SCRIPT_GROUP)
+               fcgiapp = ' -- %s' % (app_command)
+
+               if app_name == 'php':
+                       os.environ['PHP_FCGI_MAX_REQUESTS'] = str(PHP_FCGI_MAX_REQUESTS)
+                       phprc = os.path.join(site_dir, PHPRC_DIR, 'php.ini')
+                       if os.path.exists(phprc):
+                               #os.environ['PHPRC'] = phprc
+                               fcgiapp   += ' -c %s' % (phprc)
+                       spawnfcgi += ' -C %s' % (PHP_FCGI_CHILDREN)
+
+
+               print spawnfcgi + fcgiapp
+               os.system(spawnfcgi + fcgiapp)
+
+               # Ensure www-data can write to the socket :-S
+               # Spawn-fcgi explicitely chmods the socket after creation, very
+               # annoying
+               os.chmod(socket, stat.S_IRWXU | stat.S_IRWXG)