daemon.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. # -*- coding: utf-8-*-
  2. #
  3. # This file is part of stdd, the simple time display daemon,
  4. # written by Helmut Pozimski <helmut@pozimski.eu>,
  5. # licensed under the 3-clause BSD license
  6. """ Generic module to implement a daemon in python, takes care of the creation
  7. of the PID files and the usual daemonizing stuff.
  8. """
  9. from __future__ import unicode_literals
  10. import os
  11. import sys
  12. class Daemon(object):
  13. """ Tries to implement a well behaving unix daemon in a generic way,
  14. so the code could be used in different projects.
  15. """
  16. def __init__(self, pfile_path, pfile_name):
  17. """ Initializes the object. """
  18. self._pfile_path = pfile_path
  19. self._pfile_name = pfile_name
  20. self._daemon = False
  21. def daemonize(self):
  22. """ Turns the calling prozess into a daemon running on it's own """
  23. try:
  24. # Fork for the first time
  25. pid = os.fork()
  26. except OSError:
  27. sys.exit(os.EX_OSERR)
  28. else:
  29. if pid > 0:
  30. sys.exit(os.EX_OK)
  31. # Become session and group leader
  32. os.setsid()
  33. try:
  34. # Fork for the second time
  35. pid = os.fork()
  36. except OSError:
  37. sys.exit(os.EX_OSERR)
  38. else:
  39. if pid > 0:
  40. sys.exit(os.EX_OK)
  41. # Change cwd to / to avoid interfering with other mounted file systems
  42. os.chdir("/")
  43. # Reset the umask
  44. os.umask(0o22)
  45. # Close possibly open file descriptors
  46. os.close(0)
  47. os.close(1)
  48. os.close(2)
  49. # And redirect them to /dev/null
  50. os.open("/dev/null", 0)
  51. os.open("/dev/null", 1)
  52. os.open("/dev/null", 2)
  53. self._daemon = True
  54. def drop_privileges(self, user, group):
  55. """ If the daemon is running as root user, drop privileges and continue
  56. running as the defined unprivileged user. """
  57. pid_file_path = os.path.join(self._pfile_path, self._pfile_name)
  58. passwd_file = open("/etc/passwd", "r")
  59. group_file = open("/etc/group", "r")
  60. uid = ""
  61. gid = ""
  62. for line in passwd_file:
  63. if user in line:
  64. uid = line.split(":")[2]
  65. break
  66. for line in group_file:
  67. if group in line.split(":")[0]:
  68. gid = line.split(":")[2]
  69. break
  70. passwd_file.close()
  71. group_file.close()
  72. if os.getuid() == 0:
  73. os.chown(pid_file_path, int(uid), int(gid))
  74. os.setgid(int(gid))
  75. os.setuid(int(uid))
  76. @staticmethod
  77. def set_name(name, cmdline):
  78. """ Sets the name of the process shown visible in ps and top,
  79. this allows to make your daemon look more like a standalone
  80. program instead of a python script.
  81. """
  82. try:
  83. # First check if prctl is available, otherwise this function
  84. # does nothing
  85. import prctl
  86. except ImportError:
  87. return False
  88. else:
  89. prctl.set_name(name)
  90. prctl.set_proctitle(cmdline)
  91. return True
  92. def start(self):
  93. """ Performs the operations needed to "start" the daemon """
  94. if self._daemon is True:
  95. if os.access(self._pfile_path, os.F_OK & os.W_OK):
  96. pidfile = open(os.path.join(self._pfile_path,
  97. self._pfile_name), "w")
  98. pidfile.write(str(os.getpid()) + "\n")
  99. pidfile.close()
  100. return True
  101. else:
  102. return False
  103. else:
  104. return False
  105. def stop(self):
  106. """ Performs the operations needed to stop the daemon """
  107. if self._daemon is True:
  108. try:
  109. os.remove(os.path.join(self._pfile_path, self._pfile_name))
  110. except OSError:
  111. return False
  112. else:
  113. return True
  114. else:
  115. return True