1
# Twisted, the Framework of Your Internet
2
# Copyright (C) 2001-2003 Matthew W. Lefkowitz
4
# This library is free software; you can redistribute it and/or
5
# modify it under the terms of version 2.1 of the GNU Lesser General Public
6
# License as published by the Free Software Foundation.
8
# This library is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
# Lesser General Public License for more details.
13
# You should have received a copy of the GNU Lesser General Public
14
# License along with this library; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""Service architecture for Twisted
19
Services are arranged in a hierarchy. At the leafs of the hierarchy,
20
the services which actually interact with the outside world are started.
21
Services can be named or anonymous -- usually, they will be named if
22
there is need to access them through the hierarchy (from a parent or
25
API Stability: unstable
27
Maintainer: U{Moshe Zadka<mailto:moshez@twistedmatrix.com>}
29
from twisted.python import components
30
from twisted.internet import defer
31
from twisted.persisted import sob
33
class IService(components.Interface):
38
Run start-up and shut-down code at the appropriate times.
41
@ivar name: The name of the service (or None)
42
@type running: C{boolean}
43
@ivar running: Whether the service is running.
46
def setName(self, name):
47
"""Set the name of the service.
50
@raise L{RuntimeError}: Raised if the service already has a parent.
53
def setServiceParent(self, parent):
54
"""Set the parent of the service.
56
@type name: C{IServiceCollection}
57
@raise L{RuntimeError}: Raised if the service already has a parent
58
or if the service has a name and the parent already has a child
62
def disownServiceParent(self):
63
"""Remove the parent of the service."""
65
def startService(self):
66
"""Start the the service."""
68
def stopService(self):
69
"""Stop the the service.
72
@return: a deferred which is triggered when the service has
73
finished shutting down. If shutting down is immediate,
74
a value can be returned (usually, None).
77
def privilegedStartService(self):
78
"""Do preperation work for starting the service.
80
Here things which should be done before changing directory,
81
root or shedding privileges are done."""
87
Base class for services
89
Most services should inherit from this class. It handles the
90
book-keeping reponsibilities of starting and stopping, as well
91
as not serializing this book-keeping information.
94
__implements__ = IService,
100
def __getstate__(self):
101
dict = self.__dict__.copy()
102
if dict.has_key("running"):
106
def setName(self, name):
107
if self.parent is not None:
108
raise RuntimeError("cannot change name when parent exists")
111
def setServiceParent(self, parent):
112
if self.parent is not None:
113
self.disownServiceParent()
115
self.parent.addService(self)
117
def disownServiceParent(self):
118
self.parent.removeService(self)
121
def privilegedStartService(self):
124
def startService(self):
127
def stopService(self):
131
class IServiceCollection(components.Interface):
133
"""Collection of services.
135
Contain several services, and manage their start-up/shut-down.
136
Services can be accessed by name if they have a name, and it
137
is always possible to iterate over them.
140
def getServiceNamed(self, name):
141
"""Get the child service with a given name.
145
@raise L{KeyError}: Raised if the service has no child with the
150
"""Get an iterator over all child services"""
152
def addService(self, service):
153
"""Add a child service.
155
@type service: C{IService}
156
@raise L{RuntimeError}: Raised if the service has a child with
160
def removeService(self, service):
161
"""Remove a child service.
163
@type service: C{IService}
164
@raise L{ValueError}: Raised if the given service is not a child.
168
class MultiService(Service):
170
"""Straightforward Service Container
172
Hold a collection of services, and manage them in a simplistic
173
way. No service will wait for another, but this object itself
174
will not finish shutting down until all of its child services
178
__implements__ = Service.__implements__, IServiceCollection
182
self.namedServices = {}
185
def privilegedStartService(self):
186
Service.privilegedStartService(self)
188
service.privilegedStartService()
190
def startService(self):
191
Service.startService(self)
193
service.startService()
195
def stopService(self):
196
Service.stopService(self)
199
l.append(defer.maybeDeferred(service.stopService))
200
return defer.DeferredList(l)
202
def getServiceNamed(self, name):
203
return self.namedServices[name]
206
return iter(self.services)
208
def addService(self, service):
209
if service.name is not None:
210
if self.namedServices.has_key(service.name):
211
raise RuntimeError("cannot have two services with same name")
212
self.namedServices[service.name] = service
213
self.services.append(service)
215
service.startService()
217
def removeService(self, service):
219
del self.namedServices[service.name]
220
self.services.remove(service)
222
service.stopService()
225
class IProcess(components.Interface):
227
"""Process running parameters
229
Represents parameters for how processes should be run.
231
@ivar processName: the name the process should have in ps (or None)
232
@type processName: C{str}
233
@ivar uid: the user-id the process should run under.
235
@ivar gid: the group-id the process should run under.
242
"""Process running parameters
244
Sets up uid/gid in the constructor, and has a default
245
of C{None} as C{processName}.
247
__implements__ = IProcess,
250
def __init__(self, uid=None, gid=None):
253
By default, uid or gid will be 0 (superuser)
259
def Application(name, uid=None, gid=None):
260
"""Return a compound class.
262
Return an object supporting the C{IService}, C{IServiceCollection},
263
C{IProcess} and C{sob.IPersistable} interfaces, with the given
264
parameters. Always access the return value by explicit casting to
265
one of the interfaces.
267
ret = components.Componentized()
268
for comp in (MultiService(), sob.Persistant(ret, name), Process(uid, gid)):
269
ret.addComponent(comp, ignoreClass=1)
270
IService(ret).setName(name)
273
def loadApplication(filename, kind, passphrase=None):
274
"""Load Application from file
276
@type filename: C{str}
278
@type passphrase: C{str}
280
Load application from a given file. The serialization format it
281
was saved in should be given as C{kind}, and is one of 'pickle', 'source',
282
'xml' or 'python'. If C{passphrase} is given, the application was encrypted
283
with the given passphrase.
286
application = sob.loadValueFromFile(filename, 'application', passphrase)
288
application = sob.load(filename, kind, passphrase)
289
if IService(application, None) is None:
290
from twisted.application import compat
291
application = compat.convert(application)
294
__all__ = ['IService', 'Service', 'IServiceCollection', 'MultiService',
295
'IProcess', 'Process', 'Application', 'loadApplication']