-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdaemon.py
More file actions
170 lines (134 loc) · 4.94 KB
/
Copy pathdaemon.py
File metadata and controls
170 lines (134 loc) · 4.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# -*- coding: utf-8 -*-
'''Response Project - Daemonizing'''
# Copyright (C) 2009-2010 John Feuerstein <john@feurix.com>
#
# This file is part of the response project.
#
# Response is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Response is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ---
#
# Python implementation of the magic UNIX double-fork :-)
# Based on Richard Stevens' suggestions for the "proper" way of daemonizing a
# process, as laid out in his book "UNIX Network Programming: The Sockets
# Networking API" (O'Reilly).
#
# The module will ensure that all additional, TTY-bound file descriptors
# get closed. STDIN, STDOUT and STDERR are redirected to /dev/null per
# default. If STDERR's destination is given, output to it is unbuffered.
# For more details, have a look at daemonize().
from globals import __author__, __copyright__, __license__, __version__
import os
import sys
import exception
from logger import getModuleLog
log = getModuleLog(__name__)
# Initial configuration
CWD = '/'
UMASK = 0
try:
MAXFD = os.sysconf('SC_OPEN_MAX')
except:
MAXFD = 256
if hasattr(os, 'devnull'):
NULL_DEVICE = os.devnull
else:
NULL_DEVICE = '/dev/null'
# The daemonizer }:-)
def daemonize(umask=UMASK, cwd=CWD, close_fds=True, redirect=True,
stdin=NULL_DEVICE, stdout=NULL_DEVICE, stderr=NULL_DEVICE):
'''Convert the calling process into a daemon.
Argument description:
umask: The global umask of the daemon process. (Default: %d)
cwd: Change working directory prior daemonizing. (Default: %s)
close_fds: Boolean. Close additional file descriptors? (Default: %s)
redirect: Boolean. Redirect file descriptors? (Default: %s)
stdin: Target of file descriptor 0. (Default: %s)
stdout: Target of file descriptor 1. (Default: %s)
stderr: Target of file descriptor 2. (Default: %s)
''' % (UMASK, CWD, True, True, NULL_DEVICE, NULL_DEVICE, NULL_DEVICE)
global log
try:
# Fork #1:
# - Create a new session
# - Become the process group and session leader
log.debug('Forking first child')
pid = _fork()
if pid != 0:
os._exit(0)
log.debug('Creating new session')
os.setsid()
# Fork #2:
# - Drop the session leader status, thus:
# - Ensure that the daemon never reacquires a control terminal
log.debug('Forking second child')
pid = _fork()
if pid != 0:
os._exit(0)
log.debug('Setting umask to %d' % umask)
os.umask(umask)
log.debug('Changing working directory to %s' % cwd)
os.chdir(cwd)
if close_fds:
log.debug('Closing additional file descriptors')
_closeFileDescriptors()
if redirect:
log.debug('Redirecting standard file descriptors')
_redirectFileDescriptors(stdin, stdout, stderr)
except exception.DaemonError:
raise
except OSError, e:
raise exception.DaemonError('Error during daemonizing: %s [%d]'
% (e.strerror, e.errno))
# Some helpers
def _fork():
try:
return os.fork()
except OSError, e:
raise exception.DaemonError('Unable to fork(): %s [%d]'
% (e.strerror, e.errno))
def _closeFileDescriptors():
global log
# Load POSIX resource information
import resource
maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
if maxfd == resource.RLIM_INFINITY:
maxfd = MAXFD
# Close all additional file descriptors bound to a TTY
for fd in range(3, maxfd):
try:
os.ttyname(fd)
except:
continue
try:
os.close(fd)
log.debug('Closed file descriptor %d' % fd)
except OSError:
pass
def _redirectFileDescriptors(stdin, stdout, stderr):
global log
# A final flush to the old location :-)
sys.stdout.flush()
sys.stderr.flush()
# Redirect stdin, stdout and stderr (unbuffered)
si = open(stdin, 'rb')
so = open(stdout, 'a+b')
se = open(stderr, 'a+b', 0)
log.debug('Redirecting stdin from "%s"' % stdin)
os.dup2(si.fileno(), sys.stdin.fileno())
log.debug('Redirecting stdout to "%s"' % stdout)
os.dup2(so.fileno(), sys.stdout.fileno())
log.debug('Redirecting stderr to "%s"' % stderr)
os.dup2(se.fileno(), sys.stderr.fileno())