1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17  """ 
 18  OS Process Integration 
 19  ====================== 
 20   
 21  The modules in this package implement the different ways of integration 
 22  within other frameworks. Currently there are: 
 23   
 24  daemon 
 25    The application is running as a standalone daemon, optionally forking itself 
 26    into the background (Not forking is a both a debugging and production 
 27    feature -- imagine integration into djb's daemontools and stuff. 
 28   
 29   
 30  The typical way to load the proper implementation is:: 
 31   
 32      from wtf import opi 
 33      opi.factory(config, opts, args).work() 
 34   
 35  This evaluates the [wtf] section of the config, where the following options 
 36  are recognized: 
 37   
 38  protocol = ``scgi|http`` 
 39    Required option, because there's no sensible default. ``fastcgi`` also 
 40    handles regular CGI if: 
 41   
 42    - detach = no 
 43    - listen = stdin 
 44    - STDIN is a pipe 
 45   
 46    XXX: implement it! 
 47   
 48  mode = ``Integration mode`` 
 49    ``daemon`` is default. 
 50   
 51  basedir = ``path`` 
 52    The directory to change into immediatly after startup. Default is: ``/`` 
 53   
 54  umask = ``umask`` 
 55    The umask to apply. The default umask is 0. The value is interpreted as 
 56    octal number. You can specify ``inherit`` in order to inherit the umask 
 57    from the caller (e.g. the shell). 
 58   
 59  In daemon mode the following options are used to determine the behaviour 
 60  in detail: 
 61   
 62  detach = ``yes|no`` 
 63    Required option, because there's no sensible default. This option determines 
 64    whether the daemon should fork itself into the background or not. If this 
 65    option is set to ``yes``, command line parameters become interesting. 
 66    The last parameter is evaluated and has to be one of the following: 
 67   
 68    start 
 69      Start a new daemon. If there's already one running, this is a failure. 
 70   
 71    stop 
 72      Stop the daemon. If there's none running, this is not a failure. If 
 73      there's one running, this option is identical to sending a 
 74      SIGTERM + SIGCONT to the process in question. 
 75   
 76    logrotate|logreopen 
 77      Reopen the error log file. 
 78   
 79    The presence of another running daemon is determined by the pidfile (which 
 80    is advisory locked for this purpose). Furthermore a forked away daemon 
 81    does the usual detaching magic like closing all descriptors and stuff. This 
 82    especially means, that STDIN, STDOUT and STDERR all point to /dev/null. 
 83    If you specify an ``errorlog`` it will be attached to STDERR. 
 84   
 85  listen = ``[tcp:]host:port | [unix:]path[(perm)] ...`` 
 86    Required option, because there's no sensible default. This option determines 
 87    where the daemon should listen for requests. This is a list of socket 
 88    specifications which can be either TCP/IP or a unix domain sockets (you can 
 89    mix them, if you want.) The optional ``perm`` parameter for unix sockets is 
 90    an octal value and controls the permissions of the socket path. Note that 
 91    socket paths are limited in length by the OS. See ``unix(7)`` for details. 
 92   
 93  workermodel = ``model`` 
 94    Required option, because there's no sensible default. This option determines 
 95    the worker pool implementation. 
 96   
 97  errorlog = ``path`` 
 98    The file which STDERR should be attached to. By default STDERR goes to 
 99    ``/dev/null``. 
100   
101  pidfile = ``path`` 
102    The option is required. It contains the name of the file where the PID of 
103    the main process is written into. The file is also used to determine 
104    a concurrently running daemon by locking it (The lock is automatically 
105    cleared if the daemon dies). 
106   
107  user = ``id|name`` 
108    The user the working process should change to. If the application is 
109    started as root, it's strongly recommended to define such a user. See also 
110    ``group``. If the application is not started as root, the option is ignored. 
111   
112  group = ``id|name`` 
113    The group the working process should change to. If the application is 
114    started as root, it's strongly recommended to define such a group. See also 
115    ``user``. If the application is not started as root, the option is ignored. 
116  """ 
117  __author__ = u"Andr\xe9 Malo" 
118  __docformat__ = "restructuredtext en" 
119   
120  import os as _os 
121   
122  from wtf import Error 
123  from wtf.config import ConfigurationError 
124   
125   
129   
133   
134   
136      """ 
137      Interface for OPI implementations 
138   
139      :Groups: 
140       - `Running Modes`: `MODE_THREADED`, `MODE_FORKED`, `MODE_SINGLE`, 
141         `MODE_ONCE` 
142   
143      :CVariables: 
144       - `MODE_THREADED`: multithreaded mode 
145       - `MODE_FORKED`: forked mode 
146       - `MODE_SINGLE`: single process mode 
147       - `MODE_ONCE`: run-once mode (like CGI) 
148   
149      :IVariables: 
150       - `mode`: The running mode (one of the ``Running Modes``) 
151       - `config`: The application config 
152   
153      :Types: 
154       - `MODE_THREADED`: ``int`` 
155       - `MODE_FORKED`: ``int`` 
156       - `MODE_SINGLE`: ``int`` 
157       - `MODE_ONCE`: ``int`` 
158   
159       - `mode`: ``int`` 
160       - `config`: `wtf.config.Config` 
161      """ 
162      MODE_THREADED, MODE_FORKED, MODE_SINGLE, MODE_ONCE = xrange(4) 
163   
164 -    def __init__(self, config, opts, args): 
 165          """ 
166          Initialization 
167   
168          :Parameters: 
169           - `config`: The application config 
170           - `opts`: Command line option container 
171           - `args`: Fixed commandline arguments 
172   
173          :Types: 
174           - `config`: `wtf.config.Config` 
175           - `opts`: ``optparse.OptionContainer`` 
176           - `args`: ``list`` 
177          """ 
 178   
180          """ 
181          Invoke the worker mechanism (if any) 
182   
183          This starts handling the incoming request(s) 
184          """ 
  185   
186   
188      """ 
189      Create the OPI instance selected by configuration 
190   
191      :Parameters: 
192       - `config`: configuration 
193       - `opts`: Option container 
194       - `args`: Fixed arguments 
195   
196      :Types: 
197       - `config`: `config.Config` 
198       - `opts`: ``optparse.OptionContainer`` 
199       - `args`: ``list`` 
200   
201      :return: OPI instance 
202      :rtype: `OPIInterface` 
203      """ 
204      self = factory 
205   
206      basemode = config.wtf('mode', 'daemon') 
207      if basemode not in self.basemodes:  
208          raise ConfigurationError("Unknown mode %s" % basemode) 
209   
210      basedir = _os.path.normpath( 
211          _os.path.join(config.ROOT, config.wtf('basedir', '/'))) 
212      _os.chdir(basedir) 
213   
214      if 'umask' in config.wtf: 
215          umask = unicode(config.wtf.umask) 
216          if umask.lower() == 'inherit': 
217              uval = _os.umask(0) 
218          else: 
219              try: 
220                  uval = int(umask, 8) 
221              except (ValueError, TypeError), e: 
222                  raise ConfigurationError("Invalid umask: %s" % str(e)) 
223      else: 
224          uval = 0 
225      _os.umask(uval) 
226   
227       
228      return self.basemodes[basemode](config, opts, args) 
 229  factory.basemodes = {}  
230   
231   
233      """ 
234      Register an OPI implementation 
235   
236      :Parameters: 
237       - `name`: The name (in the config) 
238       - `klass`: The implementation class 
239   
240      :Types: 
241       - `name`: ``str`` 
242       - `klass`: `OPIInterface` 
243      """ 
244      factory.basemodes[name] = klass  
 245