|
@@ -26,7 +26,7 @@
|
|
|
daemon sleeps until it wakes up to check if there's any
|
|
|
new messages on the queue.
|
|
|
|
|
|
-.. cmdoption:: -d, --daemon
|
|
|
+.. cmdoption:: -d, --detach, --daemon
|
|
|
|
|
|
Run in the background as a daemon.
|
|
|
|
|
@@ -35,6 +35,26 @@
|
|
|
Discard all waiting tasks before the daemon is started.
|
|
|
**WARNING**: This is unrecoverable, and the tasks will be
|
|
|
deleted from the messaging server.
|
|
|
+
|
|
|
+.. cmdoption:: -u, --uid
|
|
|
+
|
|
|
+ User-id to run ``celeryd`` as when in daemon mode.
|
|
|
+
|
|
|
+.. cmdoption:: -g, --gid
|
|
|
+
|
|
|
+ Group-id to run ``celeryd`` as when in daemon mode.
|
|
|
+
|
|
|
+.. cmdoption:: --umask
|
|
|
+
|
|
|
+ umask of the process when in daemon mode.
|
|
|
+
|
|
|
+.. cmdoption:: --workdir
|
|
|
+
|
|
|
+ Directory to change to when in daemon mode.
|
|
|
+
|
|
|
+.. cmdoption:: --chroot
|
|
|
+
|
|
|
+ Change root directory to this path when in daemon mode.
|
|
|
|
|
|
"""
|
|
|
import os
|
|
@@ -45,7 +65,6 @@ if django_project_dir:
|
|
|
sys.path.append(django_project_dir)
|
|
|
|
|
|
from django.conf import settings
|
|
|
-from celery.platform import PIDFile, daemonize, remove_pidfile
|
|
|
from celery.log import emergency_error
|
|
|
from celery.conf import LOG_LEVELS, DAEMON_LOG_FILE, DAEMON_LOG_LEVEL
|
|
|
from celery.conf import DAEMON_CONCURRENCY, DAEMON_PID_FILE
|
|
@@ -56,11 +75,45 @@ from celery.worker import WorkController
|
|
|
import traceback
|
|
|
import optparse
|
|
|
import atexit
|
|
|
+from daemon import DaemonContext
|
|
|
+from daemon.pidlockfile import PIDLockFile
|
|
|
|
|
|
|
|
|
-def main(concurrency=DAEMON_CONCURRENCY, daemon=False,
|
|
|
+def acquire_pidlock(pidfile):
|
|
|
+ """Get the :class:`daemon.pidlockfile.PIDLockFile` handler for
|
|
|
+ ``pidfile``.
|
|
|
+
|
|
|
+ If the ``pidfile`` already exists, but the process is not running the
|
|
|
+ ``pidfile`` will be removed, a ``"stale pidfile"`` message is emitted
|
|
|
+ and execution continues as normally. However, if the process is still
|
|
|
+ running the program will exit complaning that the program is already
|
|
|
+ running in the background somewhere.
|
|
|
+
|
|
|
+ """
|
|
|
+ pidlock = PIDLockFile(pidfile)
|
|
|
+ if not pidlock.is_locked():
|
|
|
+ return pidlock
|
|
|
+ pid = pidlock.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")
|
|
|
+ pidlock.release()
|
|
|
+ return
|
|
|
+ else:
|
|
|
+ raise SystemExit(
|
|
|
+ "ERROR: Pidfile (%s) already exists.\n"
|
|
|
+ "Seems celeryd is already running? (PID: %d)" % (
|
|
|
+ pidfile, pid))
|
|
|
+ return pidlock
|
|
|
+
|
|
|
+
|
|
|
+def run_worker(concurrency=DAEMON_CONCURRENCY, daemon=False,
|
|
|
loglevel=DAEMON_LOG_LEVEL, logfile=DAEMON_LOG_FILE, discard=False,
|
|
|
- pidfile=DAEMON_PID_FILE, queue_wakeup_after=QUEUE_WAKEUP_AFTER):
|
|
|
+ pidfile=DAEMON_PID_FILE, queue_wakeup_after=QUEUE_WAKEUP_AFTER,
|
|
|
+ umask=0, uid=None, gid=None, working_directory=None, chroot=None,
|
|
|
+ **kwargs):
|
|
|
"""Run the celery daemon."""
|
|
|
if settings.DATABASE_ENGINE == "sqlite3" and concurrency > 1:
|
|
|
import warnings
|
|
@@ -68,6 +121,9 @@ def main(concurrency=DAEMON_CONCURRENCY, daemon=False,
|
|
|
"concurrency. We'll be using a single process only.",
|
|
|
UserWarning)
|
|
|
concurrency = 1
|
|
|
+
|
|
|
+ if not isinstance(loglevel, int):
|
|
|
+ loglevel = LOG_LEVELS[loglevel.upper()]
|
|
|
|
|
|
if discard:
|
|
|
discarded_count = discard_all()
|
|
@@ -77,11 +133,24 @@ def main(concurrency=DAEMON_CONCURRENCY, daemon=False,
|
|
|
sys.stderr.write("Discard: Erased %d %s from the queue.\n" % (
|
|
|
discarded_count, what))
|
|
|
if daemon:
|
|
|
+ # Since without stderr any errors will be silently suppressed,
|
|
|
+ # we need to know that we have access to the logfile
|
|
|
+ pidlock = acquire_pidlock(pidfile)
|
|
|
+ if not umask:
|
|
|
+ umask = 0
|
|
|
+ if logfile:
|
|
|
+ open(logfile, "a").close()
|
|
|
+ uid = uid and int(uid) or os.geteuid()
|
|
|
+ gid = gid and int(gid) or os.getegid()
|
|
|
+ working_directory = working_directory or os.getcwd()
|
|
|
sys.stderr.write("Launching celeryd in the background...\n")
|
|
|
- pidfile_handler = PIDFile(pidfile)
|
|
|
- pidfile_handler.check()
|
|
|
- daemonize(pidfile=pidfile_handler)
|
|
|
- atexit.register(remove_pidfile, pidfile)
|
|
|
+ context = DaemonContext(chroot_directory=chroot,
|
|
|
+ working_directory=working_directory,
|
|
|
+ umask=umask,
|
|
|
+ pidfile=pidlock,
|
|
|
+ uid=uid,
|
|
|
+ gid=gid)
|
|
|
+ context.open()
|
|
|
else:
|
|
|
logfile = None # log to stderr when not running as daemon.
|
|
|
|
|
@@ -121,26 +190,34 @@ OPTION_LIST = (
|
|
|
help="If the queue is empty, this is the time *in seconds* the "
|
|
|
"daemon sleeps until it wakes up to check if there's any "
|
|
|
"new messages on the queue."),
|
|
|
- optparse.make_option('-d', '--daemon', default=False,
|
|
|
+ optparse.make_option('-d', '--detach', '--daemon', default=False,
|
|
|
action="store_true", dest="daemon",
|
|
|
help="Run in the background as a daemon."),
|
|
|
-)
|
|
|
+ optparse.make_option('-u', '--uid', default=None,
|
|
|
+ action="store", dest="uid",
|
|
|
+ help="User-id to run celeryd as when in daemon mode."),
|
|
|
+ optparse.make_option('-g', '--gid', default=None,
|
|
|
+ action="store", dest="gid",
|
|
|
+ help="Group-id to run celeryd as when in daemon mode."),
|
|
|
+ optparse.make_option('--umask', default=0,
|
|
|
+ action="store", type="int", dest="umask",
|
|
|
+ help="umask of the process when in daemon mode."),
|
|
|
+ optparse.make_option('--workdir', default=None,
|
|
|
+ action="store", dest="working_directory",
|
|
|
+ help="Directory to change to when in daemon mode."),
|
|
|
+ optparse.make_option('--chroot', default=None,
|
|
|
+ action="store", dest="chroot",
|
|
|
+ help="Change root directory to this path when in daemon mode."),
|
|
|
+ )
|
|
|
|
|
|
|
|
|
def parse_options(arguments):
|
|
|
- """Option parsers for the available options to ``celeryd``."""
|
|
|
+ """Parse the available options to ``celeryd``."""
|
|
|
parser = optparse.OptionParser(option_list=OPTION_LIST)
|
|
|
options, values = parser.parse_args(arguments)
|
|
|
- if not isinstance(options.loglevel, int):
|
|
|
- options.loglevel = LOG_LEVELS[options.loglevel.upper()]
|
|
|
return options
|
|
|
|
|
|
+
|
|
|
if __name__ == "__main__":
|
|
|
options = parse_options(sys.argv[1:])
|
|
|
- main(concurrency=options.concurrency,
|
|
|
- daemon=options.daemon,
|
|
|
- logfile=options.logfile,
|
|
|
- loglevel=options.loglevel,
|
|
|
- pidfile=options.pidfile,
|
|
|
- discard=options.discard,
|
|
|
- queue_wakeup_after=options.queue_wakeup_after)
|
|
|
+ run_worker(**options)
|