# This file is part of jwmud, written by Helmut Pozimski in 2014. # # jwmud is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, version 2 of the License. # # jwmud is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with jwmud. If not, see . # -*- coding: utf8 -*- import os import sys from jwmudlib import jwmu_exceptions class Daemon(object): """ Tries to implement a well behaving unix daemon in a generic way, so the code could be used in different projects. """ def __init__(self, pfile_path, pfile_name): """ Initializes the object. """ self.__pfile_path = pfile_path self.__pfile_name = pfile_name self.__daemon = False self.__pid = 0 def Daemonize(self): """ Turns the calling process into a daemon running on it's own """ try: # Fork for the first time self.__pid = os.fork() except OSError: sys.exit(os.EX_OSERR) else: if self.__pid > 0: sys.exit(os.EX_OK) # Become session and group leader # noinspection PyArgumentList os.setsid() try: #Fork for the second time self.__pid = os.fork() except OSError: sys.exit(os.EX_OSERR) else: if self.__pid > 0: sys.exit(os.EX_OK) # Change cwd to / to avoid interfering with other mounted file systems os.chdir("/") # Reset the umask os.umask(0) # Close possibly open file descriptors os.close(0) os.close(1) os.close(2) # And redirect them to /dev/null os.open("/dev/null", 0) os.open("/dev/null", 1) os.open("/dev/null", 2) self.__daemon = True def DropPrivileges(self, user, group): """ If the daemon is running as root user, drop privileges and continue running as the defined unprivileged user. """ pid_file_path = os.path.join(self.__pfile_path, self.__pfile_name) try: passwd_file = open("/etc/passwd", "r") group_file = open("/etc/group", "r") except PermissionError: raise jwmu_exceptions.PasswdOrGroupAccessFailed() else: uid = "" gid = "" for line in passwd_file: if user in line: uid = line.split(":")[2] break for line in group_file: if group in line.split(":")[0]: gid = line.split(":")[2] break passwd_file.close() group_file.close() if os.getuid() == 0: os.chown(pid_file_path, int(uid), int(gid)) os.setgid(int(gid)) os.setuid(int(uid)) def Start(self): """ Performs the operations needed to "start" the daemon """ pid_file_path = os.path.join(self.__pfile_path, self.__pfile_name) if os.access(pid_file_path, os.F_OK): old_pid_file = open(pid_file_path, "r") old_pid = old_pid_file.read().strip() old_pid_file.close() if os.access(os.path.join("/proc/", old_pid), os.F_OK): raise jwmu_exceptions.DaemonAlreadyRunning() if self.__daemon is True: try: pid_file = open(pid_file_path, "w") except IOError: raise jwmu_exceptions.WritingPIDFileFailed() else: pid_file.write(str(os.getpid()) + "\n") pid_file.close() def Stop(self): """ Performs the operations needed to stop the daemon """ if self.__daemon is True: try: os.remove(os.path.join(self.__pfile_path, self.__pfile_name)) except OSError: return False else: return True else: return True