|  | @@ -21,8 +21,61 @@ DAEMON_WORKDIR = "/"
 | 
	
		
			
				|  |  |  DAEMON_REDIRECT_TO = getattr(os, "devnull", "/dev/nulll")
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +def get_fdmax(default=None):
 | 
	
		
			
				|  |  | +    fdmax = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
 | 
	
		
			
				|  |  | +    if fdmax == resource.RLIM_INFINITY:
 | 
	
		
			
				|  |  | +        return default
 | 
	
		
			
				|  |  | +    return fdmax
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def remove_pidfile(path):
 | 
	
		
			
				|  |  | +    try:
 | 
	
		
			
				|  |  | +        os.unlink(path)
 | 
	
		
			
				|  |  | +    except OSError, exc:
 | 
	
		
			
				|  |  | +        if exc.errno in (errno.ENOENT, errno.EACCES):
 | 
	
		
			
				|  |  | +            return
 | 
	
		
			
				|  |  | +        raise
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def read_pid_from_pidfile(path):
 | 
	
		
			
				|  |  | +    try:
 | 
	
		
			
				|  |  | +        fh = open(path, "r")
 | 
	
		
			
				|  |  | +    except IOError, exc:
 | 
	
		
			
				|  |  | +        if exc.errno == errno.ENOENT:
 | 
	
		
			
				|  |  | +            return
 | 
	
		
			
				|  |  | +        raise
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    line = fh.readline().strip()
 | 
	
		
			
				|  |  | +    fh.close()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    try:
 | 
	
		
			
				|  |  | +        return int(line)
 | 
	
		
			
				|  |  | +    except ValueError:
 | 
	
		
			
				|  |  | +        raise ValueError("PID file %r contents invalid." % path)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def remove_pidfile_if_stale(path):
 | 
	
		
			
				|  |  | +    try:
 | 
	
		
			
				|  |  | +        pid = read_pid_from_pidfile(path)
 | 
	
		
			
				|  |  | +    except ValueError, exc:
 | 
	
		
			
				|  |  | +        sys.stderr.write("Broken pidfile found. Removing it.\n")
 | 
	
		
			
				|  |  | +        remove_pidfile(path)
 | 
	
		
			
				|  |  | +        return True
 | 
	
		
			
				|  |  | +    if not pid:
 | 
	
		
			
				|  |  | +        remove_pidfile(path)
 | 
	
		
			
				|  |  | +        return True
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    try:
 | 
	
		
			
				|  |  | +        os.kill(pid, 0)
 | 
	
		
			
				|  |  | +    except os.error, exc:
 | 
	
		
			
				|  |  | +        if exc.errno == errno.ESRCH:
 | 
	
		
			
				|  |  | +            sys.stderr.write("Stale pidfile exists. Removing it.\n")
 | 
	
		
			
				|  |  | +            remove_pidfile(path)
 | 
	
		
			
				|  |  | +            return True
 | 
	
		
			
				|  |  | +    return False
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  def create_pidlock(pidfile):
 | 
	
		
			
				|  |  | -    """Create a pidfile to be used with python-daemon.
 | 
	
		
			
				|  |  | +    """Create or verify pidfile.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      If the pidfile already exists the program exits with an error message,
 | 
	
		
			
				|  |  |      however if the process it refers to is not running anymore, the pidfile
 | 
	
	
		
			
				|  | @@ -30,16 +83,29 @@ def create_pidlock(pidfile):
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      """
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    from lockfile import pidlockfile, LockFailed
 | 
	
		
			
				|  |  | +    class LockFailed(Exception):
 | 
	
		
			
				|  |  | +        pass
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      class PIDFile(object):
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          def __init__(self, path):
 | 
	
		
			
				|  |  |              self.path = os.path.abspath(path)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        def write_pid(self):
 | 
	
		
			
				|  |  | +            open_flags = (os.O_CREAT | os.O_EXCL | os.O_WRONLY)
 | 
	
		
			
				|  |  | +            open_mode = (((os.R_OK | os.W_OK) << 6) |
 | 
	
		
			
				|  |  | +                         ((os.R_OK) << 3) |
 | 
	
		
			
				|  |  | +                         ((os.R_OK)))
 | 
	
		
			
				|  |  | +            pidfile_fd = os.open(self.path, open_flags, open_mode)
 | 
	
		
			
				|  |  | +            pidfile = os.fdopen(pidfile_fd, "w")
 | 
	
		
			
				|  |  | +            pid = os.getpid()
 | 
	
		
			
				|  |  | +            pidfile.write("%d\n" % (pid, ))
 | 
	
		
			
				|  |  | +            pidfile.close()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          def __enter__(self):
 | 
	
		
			
				|  |  |              try:
 | 
	
		
			
				|  |  | -                pidlockfile.write_pid_to_pidfile(self.path)
 | 
	
		
			
				|  |  | +                self.write_pid()
 | 
	
		
			
				|  |  |              except OSError, exc:
 | 
	
		
			
				|  |  |                  raise LockFailed(str(exc))
 | 
	
		
			
				|  |  |              return self
 | 
	
	
		
			
				|  | @@ -51,30 +117,14 @@ def create_pidlock(pidfile):
 | 
	
		
			
				|  |  |              return os.path.exists(self.path)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          def release(self):
 | 
	
		
			
				|  |  | -            try:
 | 
	
		
			
				|  |  | -                os.unlink(self.path)
 | 
	
		
			
				|  |  | -            except OSError, exc:
 | 
	
		
			
				|  |  | -                if exc.errno in (errno.ENOENT, errno.EACCES):
 | 
	
		
			
				|  |  | -                    return
 | 
	
		
			
				|  |  | -                raise
 | 
	
		
			
				|  |  | +            remove_pidfile(self.path)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          def read_pid(self):
 | 
	
		
			
				|  |  | -            return pidlockfile.read_pid_from_pidfile(self.path)
 | 
	
		
			
				|  |  | +            return read_pid_from_pidfile(self.path)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          def is_stale(self):
 | 
	
		
			
				|  |  | -            pid = self.read_pid()
 | 
	
		
			
				|  |  | -            try:
 | 
	
		
			
				|  |  | -                os.kill(pid, 0)
 | 
	
		
			
				|  |  | -            except os.error, exc:
 | 
	
		
			
				|  |  | -                if exc.errno == errno.ESRCH:
 | 
	
		
			
				|  |  | -                    sys.stderr.write("Stale pidfile exists. Removing it.\n")
 | 
	
		
			
				|  |  | -                    self.release()
 | 
	
		
			
				|  |  | -                    return True
 | 
	
		
			
				|  |  | -            except TypeError, exc:
 | 
	
		
			
				|  |  | -                sys.stderr.write("Broken pidfile found. Removing it.\n")
 | 
	
		
			
				|  |  | -                self.release()
 | 
	
		
			
				|  |  | -                return True
 | 
	
		
			
				|  |  | -            return False
 | 
	
		
			
				|  |  | +            return remove_pidfile_if_stale(self.path)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      pidlock = PIDFile(pidfile)
 | 
	
		
			
				|  |  |      if pidlock.is_locked() and not pidlock.is_stale():
 | 
	
	
		
			
				|  | @@ -103,18 +153,23 @@ class DaemonContext(object):
 | 
	
		
			
				|  |  |              os._exit(0)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def open(self):
 | 
	
		
			
				|  |  | -        from daemon import daemon
 | 
	
		
			
				|  |  |          if self._is_open:
 | 
	
		
			
				|  |  |              return
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          self.detach()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          if self.chroot_directory is not None:
 | 
	
		
			
				|  |  | -            daemon.change_root_directory(self.chroot_directory)
 | 
	
		
			
				|  |  | +            os.chdir(self.chroot_directory)
 | 
	
		
			
				|  |  | +            os.chroot(self.chroot_directory)
 | 
	
		
			
				|  |  |          os.chdir(self.working_directory)
 | 
	
		
			
				|  |  |          os.umask(self.umask)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        daemon.close_all_open_files()
 | 
	
		
			
				|  |  | +        for fd in reversed(range(get_fdmax(default=2048))):
 | 
	
		
			
				|  |  | +            try:
 | 
	
		
			
				|  |  | +                os.close(fd)
 | 
	
		
			
				|  |  | +            except OSError, exc:
 | 
	
		
			
				|  |  | +                if exc.errno != errno.EBADF:
 | 
	
		
			
				|  |  | +                    raise
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          os.open(DAEMON_REDIRECT_TO, os.O_RDWR)
 | 
	
		
			
				|  |  |          os.dup2(0, 1)
 | 
	
	
		
			
				|  | @@ -131,9 +186,7 @@ def create_daemon_context(logfile=None, pidfile=None, **options):
 | 
	
		
			
				|  |  |          raise RuntimeError(
 | 
	
		
			
				|  |  |                  "This platform does not support detach.")
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    # set SIGCLD back to the default SIG_DFL (before python-daemon overrode
 | 
	
		
			
				|  |  | -    # it) lets the parent wait() for the terminated child process and stops
 | 
	
		
			
				|  |  | -    # the 'OSError: [Errno 10] No child processes' problem.
 | 
	
		
			
				|  |  | +    # Make sure SIGCLD is using the default handler.
 | 
	
		
			
				|  |  |      reset_signal("SIGCLD")
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      # Since without stderr any errors will be silently suppressed,
 | 
	
	
		
			
				|  | @@ -141,7 +194,9 @@ def create_daemon_context(logfile=None, pidfile=None, **options):
 | 
	
		
			
				|  |  |      if logfile:
 | 
	
		
			
				|  |  |          open(logfile, "a").close()
 | 
	
		
			
				|  |  |      if pidfile:
 | 
	
		
			
				|  |  | -        open(pidfile, "a").close()
 | 
	
		
			
				|  |  | +        # Doesn't actually create the pidfile, but makes sure it's
 | 
	
		
			
				|  |  | +        # not stale.
 | 
	
		
			
				|  |  | +        create_pidlock(pidfile)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      defaults = {"umask": lambda: 0,
 | 
	
		
			
				|  |  |                  "chroot_directory": lambda: None,
 |