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