~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to twisted/scripts/twistd.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2007-01-17 14:52:35 UTC
  • mfrom: (1.1.5 upstream) (2.1.2 etch)
  • Revision ID: james.westby@ubuntu.com-20070117145235-btmig6qfmqfen0om
Tags: 2.5.0-0ubuntu1
New upstream version, compatible with python2.5.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
 
2
 
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
3
 
# See LICENSE for details.
4
 
 
5
 
 
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
11
 
 
12
 
import os, errno, sys
13
 
 
14
 
class ServerOptions(app.ServerOptions):
15
 
    synopsis = "Usage: twistd [options]"
16
 
 
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"],
21
 
                ['euid', '',
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.)"],
28
 
               ]
29
 
 
30
 
    optParameters = [
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."],
39
 
                    ]
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"}
46
 
 
47
 
    def opt_version(self):
48
 
        """Print version information and exit.
49
 
        """
50
 
        print 'twistd (the Twisted daemon) %s' % copyright.version
51
 
        print copyright.copyright
52
 
        sys.exit()
53
 
 
54
 
    def postOptions(self):
55
 
        app.ServerOptions.postOptions(self)
56
 
        if self['pidfile']:
57
 
            self['pidfile'] = os.path.abspath(self['pidfile'])
58
 
 
59
 
 
60
 
def checkPID(pidfile):
61
 
    if not pidfile:
62
 
        return
63
 
    if os.path.exists(pidfile):
64
 
        try:
65
 
            pid = int(open(pidfile).read())
66
 
        except ValueError:
67
 
            sys.exit('Pidfile %s contains non-numeric value' % pidfile)
68
 
        try:
69
 
            os.kill(pid, 0)
70
 
        except OSError, why:
71
 
            if why[0] == errno.ESRCH:
72
 
                # The pid doesnt exists.
73
 
                log.msg('Removing stale pidfile %s' % pidfile, isError=True)
74
 
                os.remove(pidfile)
75
 
            else:
76
 
                sys.exit("Can't check status of PID %s from pidfile %s: %s" %
77
 
                         (pid, pidfile, why[1]))
78
 
        else:
79
 
            sys.exit("""\
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.
84
 
""" %  pid)
85
 
 
86
 
def removePID(pidfile):
87
 
    if not pidfile:
88
 
        return
89
 
    try:
90
 
        os.unlink(pidfile)
91
 
    except OSError, e:
92
 
        if e.errno == errno.EACCES or e.errno == errno.EPERM:
93
 
            log.msg("Warning: No permission to delete pid file")
94
 
        else:
95
 
            log.msg("Failed to unlink PID file:")
96
 
            log.deferr()
97
 
    except:
98
 
        log.msg("Failed to unlink PID file:")
99
 
        log.deferr()
100
 
 
101
 
def startLogging(logfilename, sysLog, prefix, nodaemon):
102
 
    if logfilename == '-':
103
 
        if not nodaemon:
104
 
            print 'daemons cannot log to stdout'
105
 
            os._exit(1)
106
 
        logFile = sys.stdout
107
 
    elif sysLog:
108
 
        syslog.startLogging(prefix)
109
 
    elif nodaemon and not logfilename:
110
 
        logFile = sys.stdout
111
 
    else:
112
 
        logFile = app.getLogFile(logfilename or 'twistd.log')
113
 
        try:
114
 
            import signal
115
 
        except ImportError:
116
 
            pass
117
 
        else:
118
 
            def rotateLog(signal, frame):
119
 
                from twisted.internet import reactor
120
 
                reactor.callFromThread(logFile.rotate)
121
 
            signal.signal(signal.SIGUSR1, rotateLog)
122
 
        
123
 
    if not sysLog:
124
 
        log.startLogging(logFile)
125
 
    sys.stdout.flush()
126
 
 
127
 
 
128
 
def daemonize():
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
132
 
    os.setsid()
133
 
    if os.fork():   # launch child and...
134
 
        os._exit(0) # kill off parent again.
135
 
    os.umask(077)
136
 
    null=os.open('/dev/null', os.O_RDWR)
137
 
    for i in range(3):
138
 
        try:
139
 
            os.dup2(null, i)
140
 
        except OSError, e:
141
 
            if e.errno != errno.EBADF:
142
 
                raise
143
 
    os.close(null)
144
 
 
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))
150
 
 
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:])
156
 
 
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']:
164
 
        daemonize()
165
 
    if config['pidfile']:
166
 
        open(config['pidfile'],'wb').write(str(os.getpid()))
167
 
 
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()
174
 
 
175
 
    uid, gid = mktap.getid(config['uid'], config['gid'])
176
 
    if uid is None:
177
 
        uid = process.uid
178
 
    if gid is None:
179
 
        gid = process.gid
180
 
 
181
 
    shedPrivileges(config['euid'], uid, gid)
182
 
    app.startApplication(application, not config['no_save'])
 
1
"""
 
2
The Twisted Daemon: platform-independent interface.
 
3
 
 
4
Stability: Unstable. Please contact the maintainer if you need any
 
5
improvements.
 
6
 
 
7
@author: U{Christopher Armstrong<mailto:radix@twistedmatrix.com>}
 
8
"""
 
9
 
 
10
 
 
11
from twisted.application import app
 
12
 
 
13
from twisted.python.runtime import platformType
 
14
if platformType == "win32":
 
15
    from twisted.scripts._twistw import ServerOptions, \
 
16
        WindowsApplicationRunner as _SomeApplicationRunner
 
17
else:
 
18
    from twisted.scripts._twistd_unix import ServerOptions, \
 
19
        UnixApplicationRunner as _SomeApplicationRunner
183
20
 
184
21
 
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'],
193
 
                 config['nodaemon'])
194
 
    app.initialLog()
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()
202
24
 
203
25
 
204
26
def run():
205
27
    app.run(runApp, ServerOptions)
206
28
 
207
29
 
 
30
__all__ = ['run', 'runApp']