2
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
3
# See LICENSE for details.
6
from twisted.python import log, syslog
7
from twisted.python.util import switchUID
8
from twisted.application import app, service
9
from twisted.scripts import mktap
10
from twisted import copyright
14
class ServerOptions(app.ServerOptions):
15
synopsis = "Usage: twistd [options]"
17
optFlags = [['nodaemon','n', "don't daemonize"],
18
['quiet', 'q', "No-op for backwards compatability."],
19
['originalname', None, "Don't try to change the process name"],
20
['syslog', None, "Log to syslog, not to file"],
22
"Set only effective user-id rather than real user-id. "
23
"(This option has no effect unless the server is running as "
24
"root, in which case it means not to shed all privileges "
25
"after binding ports, retaining the option to regain "
26
"privileges in cases such as spawning processes. "
27
"Use with caution.)"],
31
['prefix', None,'twisted',
32
"use the given prefix when syslogging"],
33
['pidfile','','twistd.pid',
34
"Name of the pidfile"],
35
['chroot', None, None,
36
'Chroot to a supplied directory before running'],
37
['uid', 'u', None, "The uid to run as."],
38
['gid', 'g', None, "The gid to run as."],
40
zsh_altArgDescr = {"prefix":"Use the given prefix when syslogging (default: twisted)",
41
"pidfile":"Name of the pidfile (default: twistd.pid)",}
42
#zsh_multiUse = ["foo", "bar"]
43
#zsh_mutuallyExclusive = [("foo", "bar"), ("bar", "baz")]
44
zsh_actions = {"pidfile":'_files -g "*.pid"', "chroot":'_dirs'}
45
zsh_actionDescr = {"chroot":"chroot directory"}
47
def opt_version(self):
48
"""Print version information and exit.
50
print 'twistd (the Twisted daemon) %s' % copyright.version
51
print copyright.copyright
54
def postOptions(self):
55
app.ServerOptions.postOptions(self)
57
self['pidfile'] = os.path.abspath(self['pidfile'])
60
def checkPID(pidfile):
63
if os.path.exists(pidfile):
65
pid = int(open(pidfile).read())
67
sys.exit('Pidfile %s contains non-numeric value' % pidfile)
71
if why[0] == errno.ESRCH:
72
# The pid doesnt exists.
73
log.msg('Removing stale pidfile %s' % pidfile, isError=True)
76
sys.exit("Can't check status of PID %s from pidfile %s: %s" %
77
(pid, pidfile, why[1]))
80
Another twistd server is running, PID %s\n
81
This could either be a previously started instance of your application or a
82
different application entirely. To start a new one, either run it in some other
83
directory, or use the --pidfile and --logfile parameters to avoid clashes.
86
def removePID(pidfile):
92
if e.errno == errno.EACCES or e.errno == errno.EPERM:
93
log.msg("Warning: No permission to delete pid file")
95
log.msg("Failed to unlink PID file:")
98
log.msg("Failed to unlink PID file:")
101
def startLogging(logfilename, sysLog, prefix, nodaemon):
102
if logfilename == '-':
104
print 'daemons cannot log to stdout'
108
syslog.startLogging(prefix)
109
elif nodaemon and not logfilename:
112
logFile = app.getLogFile(logfilename or 'twistd.log')
118
def rotateLog(signal, frame):
119
from twisted.internet import reactor
120
reactor.callFromThread(logFile.rotate)
121
signal.signal(signal.SIGUSR1, rotateLog)
124
log.startLogging(logFile)
129
# See http://www.erlenstar.demon.co.uk/unix/faq_toc.html#TOC16
130
if os.fork(): # launch child and...
131
os._exit(0) # kill off parent
133
if os.fork(): # launch child and...
134
os._exit(0) # kill off parent again.
136
null=os.open('/dev/null', os.O_RDWR)
141
if e.errno != errno.EBADF:
145
def shedPrivileges(euid, uid, gid):
146
if uid is not None or gid is not None:
147
switchUID(uid, gid, euid)
148
extra = euid and 'e' or ''
149
log.msg('set %suid/%sgid %s/%s' % (extra, extra, uid, gid))
151
def launchWithName(name):
152
if name and name != sys.argv[0]:
153
exe = os.path.realpath(sys.executable)
154
log.msg('Changing process name to ' + name)
155
os.execv(exe, [name, sys.argv[0], '--originalname']+sys.argv[1:])
157
def setupEnvironment(config):
158
if config['chroot'] is not None:
159
os.chroot(config['chroot'])
160
if config['rundir'] == '.':
161
config['rundir'] = '/'
162
os.chdir(config['rundir'])
163
if not config['nodaemon']:
165
if config['pidfile']:
166
open(config['pidfile'],'wb').write(str(os.getpid()))
168
def startApplication(config, application):
169
process = service.IProcess(application, None)
170
if not config['originalname']:
171
launchWithName(process.processName)
172
setupEnvironment(config)
173
service.IService(application).privilegedStartService()
175
uid, gid = mktap.getid(config['uid'], config['gid'])
181
shedPrivileges(config['euid'], uid, gid)
182
app.startApplication(application, not config['no_save'])
2
The Twisted Daemon: platform-independent interface.
4
Stability: Unstable. Please contact the maintainer if you need any
7
@author: U{Christopher Armstrong<mailto:radix@twistedmatrix.com>}
11
from twisted.application import app
13
from twisted.python.runtime import platformType
14
if platformType == "win32":
15
from twisted.scripts._twistw import ServerOptions, \
16
WindowsApplicationRunner as _SomeApplicationRunner
18
from twisted.scripts._twistd_unix import ServerOptions, \
19
UnixApplicationRunner as _SomeApplicationRunner
185
22
def runApp(config):
186
checkPID(config['pidfile'])
187
passphrase = app.getPassphrase(config['encrypted'])
188
app.installReactor(config['reactor'])
189
config['nodaemon'] = config['nodaemon'] or config['debug']
190
oldstdout = sys.stdout
191
oldstderr = sys.stderr
192
startLogging(config['logfile'], config['syslog'], config['prefix'],
195
application = app.getApplication(config, passphrase)
196
startApplication(config, application)
197
app.runReactorWithLogging(config, oldstdout, oldstderr)
198
removePID(config['pidfile'])
199
app.reportProfile(config['report-profile'],
200
service.IProcess(application).processName)
201
log.msg("Server Shut Down.")
23
_SomeApplicationRunner(config).run()
205
27
app.run(runApp, ServerOptions)
30
__all__ = ['run', 'runApp']