]> kaliko git repositories - mpd-sima.git/blob - sima/lib/daemon.py
Bump version
[mpd-sima.git] / sima / lib / daemon.py
1 # -*- coding: utf-8 -*-
2 # Public Domain
3 #
4 # Copyright 2007, 2009 Sander Marechal <s.marechal@jejik.com>
5 # http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/
6 #
7 # Copyright 2010, 2011 kaliko <kaliko@azylum.org>
8 # https://gitorious.org/python-daemon
9 #
10 #  This file is part of MPD_sima
11 #
12 #  MPD_sima is free software: you can redistribute it and/or modify
13 #  it under the terms of the GNU General Public License as published by
14 #  the Free Software Foundation, either version 3 of the License, or
15 #  (at your option) any later version.
16 #
17 #  MPD_sima is distributed in the hope that it will be useful,
18 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
19 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 #  GNU General Public License for more details.
21 #
22 #  You should have received a copy of the GNU General Public License
23 #  along with MPD_sima.  If not, see <http://www.gnu.org/licenses/>.
24
25 import atexit
26 import os
27 import sys
28 import time
29 from signal import signal, SIGTERM, SIGHUP, SIGUSR1
30
31
32 class Daemon(object):
33     """
34     A generic daemon class.
35
36     Usage: subclass the Daemon class and override the run() method
37
38         Daemon([pidfile[, stdin[, stdout[, stderr]]]])
39
40             pidfile : file to write pid to (default no pid file writen)
41             stdin   : standard input file descriptor (default to /dev/null)
42             stdout  : standard output file descriptor (default to /dev/null)
43             stderr  : standard error file descriptorr (default to /dev/null)
44     """
45     version = '0.6'
46
47     def __init__(self, pidfile,
48             stdin = os.devnull,
49             stdout = os.devnull,
50             stderr = os.devnull):
51         self.stdin = stdin
52         self.stdout = stdout
53         self.stderr = stderr
54         self.pidfile = pidfile
55         self.umask = 0
56
57     def daemonize(self):
58         """
59         Do the UNIX double-fork magic.
60         see W. Richard Stevens, "Advanced Programming in the Unix Environment"
61         for details (ISBN 0201563177)
62
63         Short explanation:
64             Unix processes belong to "process group" which in turn lies within a
65             "session".  A session can have a controlling tty.
66             Forking twice allows to detach the session from a possible tty.
67             The process lives then within the init process.
68         """
69         try:
70             pid = os.fork()
71             if pid > 0:
72                 # exit first parent
73                 sys.exit(0)
74         except OSError as e:
75             sys.stderr.write('fork #1 failed: {0.errno:d} ({0.strerror})\n'.format(e))
76             sys.exit(1)
77
78         # Decouple from parent environment
79         os.chdir('/')
80         os.setsid()
81         self.umask = os.umask(0)
82
83         # Do second fork
84         try:
85             pid = os.fork()
86             if pid > 0:
87                 # exit from second parent
88                 sys.exit(0)
89         except OSError as e:
90             sys.stderr.write('fork #2 failed: {0.errno:d} ({0.strerror})\n'.format(e))
91             sys.exit(1)
92
93         self.write_pid()
94         # redirect standard file descriptors
95         sys.stdout.flush()
96         sys.stderr.flush()
97         # TODO: binary or txt mode?
98         si = open(self.stdin,  mode='rb')
99         so = open(self.stdout, mode='ab+')
100         se = open(self.stderr, mode='ab+', buffering=0)
101         os.dup2(si.fileno(), sys.stdin.fileno())
102         os.dup2(so.fileno(), sys.stdout.fileno())
103         os.dup2(se.fileno(), sys.stderr.fileno())
104
105         atexit.register(self.shutdown)
106         self.signal_management()
107
108     def write_pid(self):
109         # write pidfile
110         if not self.pidfile:
111             return
112         pid = str(os.getpid())
113         try:
114             os.umask(self.umask)
115             open(self.pidfile, 'w').write('%s\n' % pid)
116         except Exception as wpid_err:
117             sys.stderr.write('Error trying to write pid file: {}\n'.format(wpid_err))
118             sys.exit(1)
119         os.umask(0)
120         atexit.register(self.delpid)
121
122     def signal_management(self):
123         """Declare signal handlers
124         """
125         signal(SIGTERM, self.exit_handler)
126         signal(SIGHUP, self.hup_handler)
127         signal(SIGUSR1, self.hup_handler)
128
129     def exit_handler(self, signum, frame):
130         sys.exit(1)
131
132     def hup_handler(self, signum, frame):
133         """SIGHUP handler"""
134         pass
135
136     def delpid(self):
137         """Remove PID file"""
138         try:
139             os.unlink(self.pidfile)
140         except OSError as err:
141             message = 'Error trying to remove PID file: {}\n'
142             sys.stderr.write(message.format(err))
143
144     def start(self):
145         """
146         Start the daemon
147         """
148         # Check for a pidfile to see if the daemon already runs
149         try:
150             pf = open(self.pidfile, 'r')
151             pid = int(pf.read().strip())
152             pf.close()
153         except IOError:
154             pid = None
155
156         if pid:
157             message = 'pidfile {0.pidfile} already exist. Daemon already running?\n'
158             sys.stderr.write(message.format(self))
159             sys.exit(1)
160
161         # Start the daemon
162         self.daemonize()
163         self.run()
164
165     def foreground(self):
166         """
167         Foreground/debug mode
168         """
169         self.write_pid()
170         atexit.register(self.shutdown)
171         self.run()
172
173     def stop(self):
174         """
175         Stop the daemon
176         """
177         # Get the pid from the pidfile
178         try:
179             pf = open(self.pidfile, 'r')
180             pid = int(pf.read().strip())
181             pf.close()
182         except IOError:
183             pid = None
184
185         if not pid:
186             message = 'pidfile {0.pidfile} does not exist. Is the Daemon running?\n'
187             sys.stderr.write(message.format(self))
188             return  # not an error in a restart
189
190         # Try killing the daemon process
191         try:
192             os.kill(pid, SIGTERM)
193             time.sleep(0.1)
194         except OSError as err:
195             if err.errno == 3:
196                 if os.path.exists(self.pidfile):
197                     message = "Daemon's not running? removing pid file {0.pidfile}.\n"
198                     sys.stderr.write(message.format(self))
199                     os.remove(self.pidfile)
200             else:
201                 sys.stderr.write(err.strerror)
202                 sys.exit(1)
203
204     def restart(self):
205         """
206         Restart the daemon
207         """
208         self.stop()
209         self.start()
210
211     def shutdown(self):
212         """
213         You should override this method when you subclass Daemon. It will be
214         called when the process is being stopped.
215         Pay attention:
216         Daemon() uses atexit to call Daemon().shutdown(), as a consequence
217         shutdown and any other functions registered via this module are not
218         called when the program is killed by an un-handled/unknown signal.
219         This is the reason of Daemon().signal_management() existence.
220         """
221
222     def run(self):
223         """
224         You should override this method when you subclass Daemon. It will be
225         called after the process has been daemonized by start() or restart().
226         """
227
228 # VIM MODLINE
229 # vim: ai ts=4 sw=4 sts=4 expandtab