1
0

daemon.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. '''
  2. ***
  3. Modified generic daemon class
  4. ***
  5. Author: http://www.jejik.com/articles/2007/02/
  6. a_simple_unix_linux_daemon_in_python/www.boxedice.com
  7. License: http://creativecommons.org/licenses/by-sa/3.0/
  8. Changes: 23rd Jan 2009 (David Mytton <david@boxedice.com>)
  9. - Replaced hard coded '/dev/null in __init__ with os.devnull
  10. - Added OS check to conditionally remove code that doesn't
  11. work on OS X
  12. - Added output to console on completion
  13. - Tidied up formatting
  14. 11th Mar 2009 (David Mytton <david@boxedice.com>)
  15. - Fixed problem with daemon exiting on Python 2.4
  16. (before SystemExit was part of the Exception base)
  17. 13th Aug 2010 (David Mytton <david@boxedice.com>
  18. - Fixed unhandled exception if PID file is empty
  19. '''
  20. # Core modules
  21. from __future__ import print_function
  22. import atexit
  23. import errno
  24. import os
  25. import sys
  26. import time
  27. import signal
  28. class Daemon(object):
  29. """
  30. A generic daemon class.
  31. Usage: subclass the Daemon class and override the run() method
  32. """
  33. def __init__(self, pidfile, stdin=os.devnull,
  34. stdout=os.devnull, stderr=os.devnull,
  35. home_dir='.', umask=0o22, verbose=1,
  36. use_gevent=False, use_eventlet=False):
  37. self.stdin = stdin
  38. self.stdout = stdout
  39. self.stderr = stderr
  40. self.pidfile = pidfile
  41. self.home_dir = home_dir
  42. self.verbose = verbose
  43. self.umask = umask
  44. self.daemon_alive = True
  45. self.use_gevent = use_gevent
  46. self.use_eventlet = use_eventlet
  47. def log(self, *args):
  48. if self.verbose >= 1:
  49. print(*args)
  50. def daemonize(self):
  51. """
  52. Do the UNIX double-fork magic, see Stevens' "Advanced
  53. Programming in the UNIX Environment" for details (ISBN 0201563177)
  54. http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
  55. """
  56. if self.use_eventlet:
  57. import eventlet.tpool
  58. eventlet.tpool.killall()
  59. try:
  60. pid = os.fork()
  61. if pid > 0:
  62. # Exit first parent
  63. sys.exit(0)
  64. except OSError as e:
  65. sys.stderr.write(
  66. "fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
  67. sys.exit(1)
  68. # Decouple from parent environment
  69. os.chdir(self.home_dir)
  70. os.setsid()
  71. os.umask(self.umask)
  72. # Do second fork
  73. try:
  74. pid = os.fork()
  75. if pid > 0:
  76. # Exit from second parent
  77. sys.exit(0)
  78. except OSError as e:
  79. sys.stderr.write(
  80. "fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
  81. sys.exit(1)
  82. if sys.platform != 'darwin': # This block breaks on OS X
  83. # Redirect standard file descriptors
  84. sys.stdout.flush()
  85. sys.stderr.flush()
  86. si = open(self.stdin, 'r')
  87. so = open(self.stdout, 'a+')
  88. if self.stderr:
  89. try:
  90. se = open(self.stderr, 'a+', 0)
  91. except ValueError:
  92. # Python 3 can't have unbuffered text I/O
  93. se = open(self.stderr, 'a+', 1)
  94. else:
  95. se = so
  96. os.dup2(si.fileno(), sys.stdin.fileno())
  97. os.dup2(so.fileno(), sys.stdout.fileno())
  98. os.dup2(se.fileno(), sys.stderr.fileno())
  99. def sigtermhandler(signum, frame):
  100. self.daemon_alive = False
  101. sys.exit()
  102. if self.use_gevent:
  103. import gevent
  104. gevent.reinit()
  105. gevent.signal(signal.SIGTERM, sigtermhandler, signal.SIGTERM, None)
  106. gevent.signal(signal.SIGINT, sigtermhandler, signal.SIGINT, None)
  107. else:
  108. signal.signal(signal.SIGTERM, sigtermhandler)
  109. signal.signal(signal.SIGINT, sigtermhandler)
  110. self.log("Started")
  111. # Write pidfile
  112. atexit.register(
  113. self.delpid) # Make sure pid file is removed if we quit
  114. pid = str(os.getpid())
  115. open(self.pidfile, 'w+').write("%s\n" % pid)
  116. def delpid(self):
  117. try:
  118. # the process may fork itself again
  119. pid = int(open(self.pidfile, 'r').read().strip())
  120. if pid == os.getpid():
  121. os.remove(self.pidfile)
  122. except OSError as e:
  123. if e.errno == errno.ENOENT:
  124. pass
  125. else:
  126. raise
  127. def start(self, *args, **kwargs):
  128. """
  129. Start the daemon
  130. """
  131. self.log("Starting...")
  132. # Check for a pidfile to see if the daemon already runs
  133. try:
  134. pf = open(self.pidfile, 'r')
  135. pid = int(pf.read().strip())
  136. pf.close()
  137. except IOError:
  138. pid = None
  139. except SystemExit:
  140. pid = None
  141. if pid:
  142. message = "pidfile %s already exists. Is it already running?\n"
  143. sys.stderr.write(message % self.pidfile)
  144. sys.exit(1)
  145. # Start the daemon
  146. self.daemonize()
  147. self.run(*args, **kwargs)
  148. def stop(self):
  149. """
  150. Stop the daemon
  151. """
  152. if self.verbose >= 1:
  153. self.log("Stopping...")
  154. # Get the pid from the pidfile
  155. pid = self.get_pid()
  156. if not pid:
  157. message = "pidfile %s does not exist. Not running?\n"
  158. sys.stderr.write(message % self.pidfile)
  159. # Just to be sure. A ValueError might occur if the PID file is
  160. # empty but does actually exist
  161. if os.path.exists(self.pidfile):
  162. os.remove(self.pidfile)
  163. return # Not an error in a restart
  164. # Try killing the daemon process
  165. try:
  166. i = 0
  167. while 1:
  168. os.kill(pid, signal.SIGTERM)
  169. time.sleep(0.1)
  170. i = i + 1
  171. if i % 10 == 0:
  172. os.kill(pid, signal.SIGHUP)
  173. except OSError as err:
  174. if err.errno == errno.ESRCH:
  175. if os.path.exists(self.pidfile):
  176. os.remove(self.pidfile)
  177. else:
  178. print(str(err))
  179. sys.exit(1)
  180. self.log("Stopped")
  181. def restart(self):
  182. """
  183. Restart the daemon
  184. """
  185. self.stop()
  186. self.start()
  187. def get_pid(self):
  188. try:
  189. pf = open(self.pidfile, 'r')
  190. pid = int(pf.read().strip())
  191. pf.close()
  192. except IOError:
  193. pid = None
  194. except SystemExit:
  195. pid = None
  196. return pid
  197. def is_running(self):
  198. pid = self.get_pid()
  199. if pid is None:
  200. self.log('Process is stopped')
  201. return False
  202. elif os.path.exists('/proc/%d' % pid):
  203. self.log('Process (pid %d) is running...' % pid)
  204. return True
  205. else:
  206. self.log('Process (pid %d) is killed' % pid)
  207. return False
  208. def run(self):
  209. """
  210. You should override this method when you subclass Daemon.
  211. It will be called after the process has been
  212. daemonized by start() or restart().
  213. """
  214. raise NotImplementedError