/usr/lib/python2.7/dist-packages/daemonize.py is in python-daemonize 2.3.1-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | # #!/usr/bin/python
import fcntl
import os
import pwd
import grp
import sys
import signal
import resource
import logging
import atexit
from logging import handlers
class Daemonize(object):
""" Daemonize object
Object constructor expects three arguments:
- app: contains the application name which will be sent to syslog.
- pid: path to the pidfile.
- action: your custom function which will be executed after daemonization.
- keep_fds: optional list of fds which should not be closed.
- auto_close_fds: optional parameter to not close opened fds.
- privileged_action: action that will be executed before drop privileges if user or
group parameter is provided.
If you want to transfer anything from privileged_action to action, such as
opened privileged file descriptor, you should return it from
privileged_action function and catch it inside action function.
- user: drop privileges to this user if provided.
- group: drop privileges to this group if provided.
- verbose: send debug messages to logger if provided.
- logger: use this logger object instead of creating new one, if provided.
"""
def __init__(self, app, pid, action, keep_fds=None, auto_close_fds=True, privileged_action=None, user=None, group=None, verbose=False, logger=None):
self.app = app
self.pid = pid
self.action = action
self.keep_fds = keep_fds or []
self.privileged_action = privileged_action or (lambda: ())
self.user = user
self.group = group
self.logger = logger
self.verbose = verbose
self.auto_close_fds = auto_close_fds
def sigterm(self, signum, frame):
""" sigterm method
These actions will be done after SIGTERM.
"""
self.logger.warn("Caught signal %s. Stopping daemon." % signum)
os.remove(self.pid)
sys.exit(0)
def exit(self):
""" exit method
Cleanup pid file at exit.
"""
self.logger.warn("Stopping daemon.")
os.remove(self.pid)
sys.exit(0)
def start(self):
""" start method
Main daemonization process.
"""
# If pidfile already exists, we should read pid from there; to overwrite it, if locking
# will fail, because locking attempt somehow purges the file contents.
if os.path.isfile(self.pid):
with open(self.pid, "r") as old_pidfile:
old_pid = old_pidfile.read()
# Create a lockfile so that only one instance of this daemon is running at any time.
try:
lockfile = open(self.pid, "w")
except IOError:
print("Unable to create the pidfile.")
sys.exit(1)
try:
# Try to get an exclusive lock on the file. This will fail if another process has the file
# locked.
fcntl.flock(lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
print("Unable to lock on the pidfile.")
# We need to overwrite the pidfile if we got here.
with open(self.pid, "w") as pidfile:
pidfile.write(old_pid)
sys.exit(1)
# Fork, creating a new process for the child.
process_id = os.fork()
if process_id < 0:
# Fork error. Exit badly.
sys.exit(1)
elif process_id != 0:
# This is the parent process. Exit.
sys.exit(0)
# This is the child process. Continue.
# Stop listening for signals that the parent process receives.
# This is done by getting a new process id.
# setpgrp() is an alternative to setsid().
# setsid puts the process in a new parent group and detaches its controlling terminal.
process_id = os.setsid()
if process_id == -1:
# Uh oh, there was a problem.
sys.exit(1)
# Add lockfile to self.keep_fds.
self.keep_fds.append(lockfile.fileno())
# Close all file descriptors, except the ones mentioned in self.keep_fds.
devnull = "/dev/null"
if hasattr(os, "devnull"):
# Python has set os.devnull on this system, use it instead as it might be different
# than /dev/null.
devnull = os.devnull
if self.auto_close_fds:
for fd in range(3, resource.getrlimit(resource.RLIMIT_NOFILE)[0]):
if fd not in self.keep_fds:
try:
os.close(fd)
except OSError:
pass
devnull_fd = os.open(devnull, os.O_RDWR)
os.dup2(devnull_fd, 0)
os.dup2(devnull_fd, 1)
os.dup2(devnull_fd, 2)
if self.logger is None:
# Initialize logging.
self.logger = logging.getLogger(self.app)
self.logger.setLevel(logging.DEBUG)
# Display log messages only on defined handlers.
self.logger.propagate = False
# Initialize syslog.
# It will correctly work on OS X, Linux and FreeBSD.
if sys.platform == "darwin":
syslog_address = "/var/run/syslog"
else:
syslog_address = "/dev/log"
# We will continue with syslog initialization only if actually have such capabilities
# on the machine we are running this.
if os.path.isfile(syslog_address):
syslog = handlers.SysLogHandler(syslog_address)
if self.verbose:
syslog.setLevel(logging.DEBUG)
else:
syslog.setLevel(logging.INFO)
# Try to mimic to normal syslog messages.
formatter = logging.Formatter("%(asctime)s %(name)s: %(message)s",
"%b %e %H:%M:%S")
syslog.setFormatter(formatter)
self.logger.addHandler(syslog)
# Set umask to default to safe file permissions when running as a root daemon. 027 is an
# octal number which we are typing as 0o27 for Python3 compatibility.
os.umask(0o27)
# Change to a known directory. If this isn't done, starting a daemon in a subdirectory that
# needs to be deleted results in "directory busy" errors.
os.chdir("/")
# Execute privileged action
privileged_action_result = self.privileged_action()
if not privileged_action_result:
privileged_action_result = []
# Change gid
if self.group:
try:
gid = grp.getgrnam(self.group).gr_gid
except KeyError:
self.logger.error("Group {0} not found".format(self.group))
sys.exit(1)
try:
os.setgid(gid)
except OSError:
self.logger.error("Unable to change gid.")
sys.exit(1)
# Change uid
if self.user:
try:
uid = pwd.getpwnam(self.user).pw_uid
except KeyError:
self.logger.error("User {0} not found.".format(self.user))
sys.exit(1)
try:
os.setuid(uid)
except OSError:
self.logger.error("Unable to change uid.")
sys.exit(1)
try:
lockfile.write("%s" % (os.getpid()))
lockfile.flush()
except IOError:
self.logger.error("Unable to write pid to the pidfile.")
print("Unable to write pid to the pidfile.")
sys.exit(1)
# Set custom action on SIGTERM.
signal.signal(signal.SIGTERM, self.sigterm)
atexit.register(self.exit)
self.logger.warn("Starting daemon.")
self.action(*privileged_action_result)
|