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