Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / application / service.py
1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
3
4 """
5 Service architecture for Twisted.
6
7 Services are arranged in a hierarchy. At the leafs of the hierarchy,
8 the services which actually interact with the outside world are started.
9 Services can be named or anonymous -- usually, they will be named if
10 there is need to access them through the hierarchy (from a parent or
11 a sibling).
12
13 Maintainer: Moshe Zadka
14 """
15
16 from zope.interface import implements, Interface, Attribute
17
18 from twisted.python.reflect import namedAny
19 from twisted.python import components
20 from twisted.internet import defer
21 from twisted.persisted import sob
22 from twisted.plugin import IPlugin
23
24
25 class IServiceMaker(Interface):
26     """
27     An object which can be used to construct services in a flexible
28     way.
29
30     This interface should most often be implemented along with
31     L{twisted.plugin.IPlugin}, and will most often be used by the
32     'twistd' command.
33     """
34     tapname = Attribute(
35         "A short string naming this Twisted plugin, for example 'web' or "
36         "'pencil'. This name will be used as the subcommand of 'twistd'.")
37
38     description = Attribute(
39         "A brief summary of the features provided by this "
40         "Twisted application plugin.")
41
42     options = Attribute(
43         "A C{twisted.python.usage.Options} subclass defining the "
44         "configuration options for this application.")
45
46
47     def makeService(options):
48         """
49         Create and return an object providing
50         L{twisted.application.service.IService}.
51
52         @param options: A mapping (typically a C{dict} or
53         L{twisted.python.usage.Options} instance) of configuration
54         options to desired configuration values.
55         """
56
57
58
59 class ServiceMaker(object):
60     """
61     Utility class to simplify the definition of L{IServiceMaker} plugins.
62     """
63     implements(IPlugin, IServiceMaker)
64
65     def __init__(self, name, module, description, tapname):
66         self.name = name
67         self.module = module
68         self.description = description
69         self.tapname = tapname
70
71
72     def options():
73         def get(self):
74             return namedAny(self.module).Options
75         return get,
76     options = property(*options())
77
78
79     def makeService():
80         def get(self):
81             return namedAny(self.module).makeService
82         return get,
83     makeService = property(*makeService())
84
85
86
87 class IService(Interface):
88     """
89     A service.
90
91     Run start-up and shut-down code at the appropriate times.
92
93     @type name:            C{string}
94     @ivar name:            The name of the service (or None)
95     @type running:         C{boolean}
96     @ivar running:         Whether the service is running.
97     """
98
99     def setName(name):
100         """
101         Set the name of the service.
102
103         @type name: C{str}
104         @raise RuntimeError: Raised if the service already has a parent.
105         """
106
107     def setServiceParent(parent):
108         """
109         Set the parent of the service.  This method is responsible for setting
110         the C{parent} attribute on this service (the child service).
111
112         @type parent: L{IServiceCollection}
113         @raise RuntimeError: Raised if the service already has a parent
114             or if the service has a name and the parent already has a child
115             by that name.
116         """
117
118     def disownServiceParent():
119         """
120         Use this API to remove an L{IService} from an L{IServiceCollection}.
121
122         This method is used symmetrically with L{setServiceParent} in that it
123         sets the C{parent} attribute on the child.
124
125         @rtype: L{Deferred<defer.Deferred>}
126         @return: a L{Deferred<defer.Deferred>} which is triggered when the
127             service has finished shutting down. If shutting down is immediate,
128             a value can be returned (usually, C{None}).
129         """
130
131     def startService():
132         """
133         Start the service.
134         """
135
136     def stopService():
137         """
138         Stop the service.
139
140         @rtype: L{Deferred<defer.Deferred>}
141         @return: a L{Deferred<defer.Deferred>} which is triggered when the
142             service has finished shutting down. If shutting down is immediate,
143             a value can be returned (usually, C{None}).
144         """
145
146     def privilegedStartService():
147         """
148         Do preparation work for starting the service.
149
150         Here things which should be done before changing directory,
151         root or shedding privileges are done.
152         """
153
154
155 class Service:
156     """
157     Base class for services.
158
159     Most services should inherit from this class. It handles the
160     book-keeping reponsibilities of starting and stopping, as well
161     as not serializing this book-keeping information.
162     """
163
164     implements(IService)
165
166     running = 0
167     name = None
168     parent = None
169
170     def __getstate__(self):
171         dict = self.__dict__.copy()
172         if dict.has_key("running"):
173             del dict['running']
174         return dict
175
176     def setName(self, name):
177         if self.parent is not None:
178             raise RuntimeError("cannot change name when parent exists")
179         self.name = name
180
181     def setServiceParent(self, parent):
182         if self.parent is not None:
183             self.disownServiceParent()
184         parent = IServiceCollection(parent, parent)
185         self.parent = parent
186         self.parent.addService(self)
187
188     def disownServiceParent(self):
189         d = self.parent.removeService(self)
190         self.parent = None
191         return d
192
193     def privilegedStartService(self):
194         pass
195
196     def startService(self):
197         self.running = 1
198
199     def stopService(self):
200         self.running = 0
201
202
203
204 class IServiceCollection(Interface):
205     """
206     Collection of services.
207
208     Contain several services, and manage their start-up/shut-down.
209     Services can be accessed by name if they have a name, and it
210     is always possible to iterate over them.
211     """
212
213     def getServiceNamed(name):
214         """
215         Get the child service with a given name.
216
217         @type name: C{str}
218         @rtype: L{IService}
219         @raise KeyError: Raised if the service has no child with the
220             given name.
221         """
222
223     def __iter__():
224         """
225         Get an iterator over all child services.
226         """
227
228     def addService(service):
229         """
230         Add a child service.
231
232         Only implementations of L{IService.setServiceParent} should use this
233         method.
234
235         @type service: L{IService}
236         @raise RuntimeError: Raised if the service has a child with
237             the given name.
238         """
239
240     def removeService(service):
241         """
242         Remove a child service.
243
244         Only implementations of L{IService.disownServiceParent} should
245         use this method.
246
247         @type service: L{IService}
248         @raise ValueError: Raised if the given service is not a child.
249         @rtype: L{Deferred<defer.Deferred>}
250         @return: a L{Deferred<defer.Deferred>} which is triggered when the
251             service has finished shutting down. If shutting down is immediate,
252             a value can be returned (usually, C{None}).
253         """
254
255
256
257 class MultiService(Service):
258     """
259     Straightforward Service Container.
260
261     Hold a collection of services, and manage them in a simplistic
262     way. No service will wait for another, but this object itself
263     will not finish shutting down until all of its child services
264     will finish.
265     """
266
267     implements(IServiceCollection)
268
269     def __init__(self):
270         self.services = []
271         self.namedServices = {}
272         self.parent = None
273
274     def privilegedStartService(self):
275         Service.privilegedStartService(self)
276         for service in self:
277             service.privilegedStartService()
278
279     def startService(self):
280         Service.startService(self)
281         for service in self:
282             service.startService()
283
284     def stopService(self):
285         Service.stopService(self)
286         l = []
287         services = list(self)
288         services.reverse()
289         for service in services:
290             l.append(defer.maybeDeferred(service.stopService))
291         return defer.DeferredList(l)
292
293     def getServiceNamed(self, name):
294         return self.namedServices[name]
295
296     def __iter__(self):
297         return iter(self.services)
298
299     def addService(self, service):
300         if service.name is not None:
301             if self.namedServices.has_key(service.name):
302                 raise RuntimeError("cannot have two services with same name"
303                                    " '%s'" % service.name)
304             self.namedServices[service.name] = service
305         self.services.append(service)
306         if self.running:
307             # It may be too late for that, but we will do our best
308             service.privilegedStartService()
309             service.startService()
310
311     def removeService(self, service):
312         if service.name:
313             del self.namedServices[service.name]
314         self.services.remove(service)
315         if self.running:
316             # Returning this so as not to lose information from the
317             # MultiService.stopService deferred.
318             return service.stopService()
319         else:
320             return None
321
322
323
324 class IProcess(Interface):
325     """
326     Process running parameters.
327
328     Represents parameters for how processes should be run.
329     """
330     processName = Attribute(
331         """
332         A C{str} giving the name the process should have in ps (or C{None}
333         to leave the name alone).
334         """)
335
336     uid = Attribute(
337         """
338         An C{int} giving the user id as which the process should run (or
339         C{None} to leave the UID alone).
340         """)
341
342     gid = Attribute(
343         """
344         An C{int} giving the group id as which the process should run (or
345         C{None} to leave the GID alone).
346         """)
347
348
349
350 class Process:
351     """
352     Process running parameters.
353
354     Sets up uid/gid in the constructor, and has a default
355     of C{None} as C{processName}.
356     """
357     implements(IProcess)
358     processName = None
359
360     def __init__(self, uid=None, gid=None):
361         """
362         Set uid and gid.
363
364         @param uid: The user ID as whom to execute the process.  If
365             this is C{None}, no attempt will be made to change the UID.
366
367         @param gid: The group ID as whom to execute the process.  If
368             this is C{None}, no attempt will be made to change the GID.
369         """
370         self.uid = uid
371         self.gid = gid
372
373
374 def Application(name, uid=None, gid=None):
375     """
376     Return a compound class.
377
378     Return an object supporting the L{IService}, L{IServiceCollection},
379     L{IProcess} and L{sob.IPersistable} interfaces, with the given
380     parameters. Always access the return value by explicit casting to
381     one of the interfaces.
382     """
383     ret = components.Componentized()
384     for comp in (MultiService(), sob.Persistent(ret, name), Process(uid, gid)):
385         ret.addComponent(comp, ignoreClass=1)
386     IService(ret).setName(name)
387     return ret
388
389
390
391 def loadApplication(filename, kind, passphrase=None):
392     """
393     Load Application from a given file.
394
395     The serialization format it was saved in should be given as
396     C{kind}, and is one of C{pickle}, C{source}, C{xml} or C{python}. If
397     C{passphrase} is given, the application was encrypted with the
398     given passphrase.
399
400     @type filename: C{str}
401     @type kind: C{str}
402     @type passphrase: C{str}
403     """
404     if kind == 'python':
405         application = sob.loadValueFromFile(filename, 'application', passphrase)
406     else:
407         application = sob.load(filename, kind, passphrase)
408     return application
409
410
411 __all__ = ['IServiceMaker', 'IService', 'Service',
412            'IServiceCollection', 'MultiService',
413            'IProcess', 'Process', 'Application', 'loadApplication']