daemon.py 3.8 KB

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