Procházet zdrojové kódy

Daemonization+pidfile no longer depends on python-daemon/lockfile

Ask Solem před 14 roky
rodič
revize
bc327d6f43
1 změnil soubory, kde provedl 85 přidání a 30 odebrání
  1. 85 30
      celery/platforms.py

+ 85 - 30
celery/platforms.py

@@ -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,