daemon.py 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. # -*- coding: utf-8 -*-
  2. import os
  3. import sys
  4. import time
  5. import atexit
  6. import signal
  7. from demeter.core import *
  8. class Daemon(object):
  9. name = 'demeter'
  10. def __init__(self, key='daemon', path='/tmp/', stdin='/dev/null', stdout=True, stderr=True):
  11. self.key = key
  12. self.path = path + self.name + '_' + key + '/'
  13. File.mkdir(self.path)
  14. if stdout == False:
  15. stdout = '/dev/null'
  16. else:
  17. stdout = self.path + 'out.log'
  18. if stderr == False:
  19. stderr = '/dev/null'
  20. else:
  21. stdout = self.path + 'err.log'
  22. self.stdin = stdin
  23. self.stdout = stdout
  24. self.stderr = stderr
  25. self.pid = self.path + self.key + '.pid'
  26. def daemonize(self):
  27. if os.path.exists(self.pid):
  28. raise RuntimeError('Already running.')
  29. # First fork (detaches from parent)
  30. try:
  31. if os.fork() > 0:
  32. raise SystemExit(0)
  33. except OSError as e:
  34. raise RuntimeError('fork #1 faild: {0} ({1})\n'.format(e.errno, e.strerror))
  35. os.chdir('/')
  36. os.setsid()
  37. os.umask(0o22)
  38. # Second fork (relinquish session leadership)
  39. try:
  40. if os.fork() > 0:
  41. raise SystemExit(0)
  42. except OSError as e:
  43. raise RuntimeError('fork #2 faild: {0} ({1})\n'.format(e.errno, e.strerror))
  44. # Flush I/O buffers
  45. sys.stdout.flush()
  46. sys.stderr.flush()
  47. # Replace file descriptors for stdin, stdout, and stderr
  48. with open(self.stdin, 'rb', 0) as f:
  49. os.dup2(f.fileno(), sys.stdin.fileno())
  50. with open(self.stdout, 'ab', 0) as f:
  51. os.dup2(f.fileno(), sys.stdout.fileno())
  52. with open(self.stderr, 'ab', 0) as f:
  53. os.dup2(f.fileno(), sys.stderr.fileno())
  54. # Write the PID file
  55. with open(self.pid, 'w') as f:
  56. print(os.getpid(), file=f)
  57. # Arrange to have the PID file removed on exit/signal
  58. atexit.register(lambda: File.remove(self.pid))
  59. signal.signal(signal.SIGTERM, self.__sigterm_handler)
  60. # Signal handler for termination (required)
  61. @staticmethod
  62. def __sigterm_handler(signo, frame):
  63. raise SystemExit(1)
  64. def start(self):
  65. try:
  66. self.daemonize()
  67. except RuntimeError as e:
  68. print(e, file=sys.stderr)
  69. raise SystemExit(1)
  70. self.run()
  71. def stop(self):
  72. try:
  73. if File.exists(self.pid):
  74. with open(self.pid) as f:
  75. os.kill(int(f.read()), signal.SIGTERM)
  76. else:
  77. print('Not running.', file=sys.stderr)
  78. raise SystemExit(1)
  79. except OSError as e:
  80. if 'No such process' in str(e) and File.exists(self.pid):
  81. File.remove(self.pid)
  82. def restart(self):
  83. self.stop()
  84. self.start()
  85. def run(self):
  86. pass