Przeglądaj źródła

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

Ask Solem 14 lat temu
rodzic
commit
bc327d6f43
1 zmienionych plików z 85 dodań i 30 usunięć
  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")
 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):
 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,
     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
     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):
     class PIDFile(object):
 
 
         def __init__(self, path):
         def __init__(self, path):
             self.path = os.path.abspath(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):
         def __enter__(self):
             try:
             try:
-                pidlockfile.write_pid_to_pidfile(self.path)
+                self.write_pid()
             except OSError, exc:
             except OSError, exc:
                 raise LockFailed(str(exc))
                 raise LockFailed(str(exc))
             return self
             return self
@@ -51,30 +117,14 @@ def create_pidlock(pidfile):
             return os.path.exists(self.path)
             return os.path.exists(self.path)
 
 
         def release(self):
         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):
         def read_pid(self):
-            return pidlockfile.read_pid_from_pidfile(self.path)
+            return read_pid_from_pidfile(self.path)
 
 
         def is_stale(self):
         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)
     pidlock = PIDFile(pidfile)
     if pidlock.is_locked() and not pidlock.is_stale():
     if pidlock.is_locked() and not pidlock.is_stale():
@@ -103,18 +153,23 @@ class DaemonContext(object):
             os._exit(0)
             os._exit(0)
 
 
     def open(self):
     def open(self):
-        from daemon import daemon
         if self._is_open:
         if self._is_open:
             return
             return
 
 
         self.detach()
         self.detach()
 
 
         if self.chroot_directory is not None:
         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.chdir(self.working_directory)
         os.umask(self.umask)
         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.open(DAEMON_REDIRECT_TO, os.O_RDWR)
         os.dup2(0, 1)
         os.dup2(0, 1)
@@ -131,9 +186,7 @@ def create_daemon_context(logfile=None, pidfile=None, **options):
         raise RuntimeError(
         raise RuntimeError(
                 "This platform does not support detach.")
                 "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")
     reset_signal("SIGCLD")
 
 
     # Since without stderr any errors will be silently suppressed,
     # Since without stderr any errors will be silently suppressed,
@@ -141,7 +194,9 @@ def create_daemon_context(logfile=None, pidfile=None, **options):
     if logfile:
     if logfile:
         open(logfile, "a").close()
         open(logfile, "a").close()
     if pidfile:
     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,
     defaults = {"umask": lambda: 0,
                 "chroot_directory": lambda: None,
                 "chroot_directory": lambda: None,