yjmpd.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import sys
  2. import os
  3. import time
  4. import atexit
  5. import signal
  6. class YJMPD:
  7. """A generic yjdaemon class.
  8. Usage: subclass the yjdaemon class and override the run() method."""
  9. def __init__(self, pidfile, root):
  10. self.pidfile = pidfile
  11. self.root = root
  12. def daemonize(self):
  13. """Deamonize class. UNIX double fork mechanism."""
  14. try:
  15. pid = os.fork()
  16. if pid > 0:
  17. # exit first parent
  18. sys.exit(0)
  19. except OSError as err:
  20. sys.stderr.write('fork #1 failed: {0}\n'.format(err))
  21. sys.exit(1)
  22. # decouple from parent environment
  23. os.chdir(self.root)
  24. os.setsid()
  25. os.umask(0)
  26. # do second fork
  27. try:
  28. pid = os.fork()
  29. if pid > 0:
  30. # exit from second parent
  31. sys.exit(0)
  32. except OSError as err:
  33. sys.stderr.write('fork #2 failed: {0}\n'.format(err))
  34. sys.exit(1)
  35. # redirect standard file descriptors
  36. sys.stdout.flush()
  37. sys.stderr.flush()
  38. si = open(os.devnull, 'r')
  39. so = open(os.devnull, 'a+')
  40. se = open(os.devnull, 'a+')
  41. os.dup2(si.fileno(), sys.stdin.fileno())
  42. os.dup2(so.fileno(), sys.stdout.fileno())
  43. os.dup2(se.fileno(), sys.stderr.fileno())
  44. # write pidfile
  45. atexit.register(self.delpid)
  46. pid = str(os.getpid())
  47. with open(self.pidfile, 'w+') as f:
  48. f.write(pid + '\n')
  49. def delpid(self):
  50. os.remove(self.pidfile)
  51. def start(self):
  52. """Start the yjdaemon."""
  53. # Check for a pidfile to see if the yjdaemon already runs
  54. try:
  55. with open(self.pidfile, 'r') as pf:
  56. pid = int(pf.read().strip())
  57. except IOError:
  58. pid = None
  59. if pid:
  60. message = "pidfile {0} already exist. " + \
  61. "Daemon already running?\n"
  62. sys.stderr.write(message.format(self.pidfile))
  63. sys.exit(1)
  64. # Start the yjdaemon
  65. self.daemonize()
  66. self.run()
  67. def stop(self):
  68. """Stop the yjdaemon."""
  69. # Get the pid from the pidfile
  70. try:
  71. with open(self.pidfile, 'r') as pf:
  72. pid = int(pf.read().strip())
  73. except IOError:
  74. pid = None
  75. if not pid:
  76. message = "pidfile {0} does not exist. " + \
  77. "Daemon not running?\n"
  78. sys.stderr.write(message.format(self.pidfile))
  79. return # not an error in a restart
  80. # Try killing the yjdaemon process
  81. try:
  82. while 1:
  83. os.kill(pid, signal.SIGTERM)
  84. time.sleep(0.1)
  85. except OSError as err:
  86. e = str(err.args)
  87. if e.find("No such process") > 0:
  88. if os.path.exists(self.pidfile):
  89. os.remove(self.pidfile)
  90. else:
  91. print(str(err.args))
  92. sys.exit(1)
  93. def restart(self):
  94. """Restart the yjdaemon."""
  95. self.stop()
  96. self.start()
  97. def status(self):
  98. """Print out status."""
  99. if os.path.exists(self.pidfile):
  100. message = "Daemon running.\n"
  101. else:
  102. message = "Daemon not running.\n"
  103. sys.stdout.write(message)
  104. def run(self):
  105. """You should override this method when you subclass Daemon.
  106. It will be called after the process has been daemonized by
  107. start() or restart()."""