-# Copyright (c) 2010, 2011 Nicira Networks
+# Copyright (c) 2010, 2011 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
import errno
import fcntl
-import logging
import os
import resource
import signal
import ovs.socket_util
import ovs.timeval
import ovs.util
+import ovs.vlog
+
+vlog = ovs.vlog.Vlog("daemon")
# --detach: Should we run in the background?
_detach = False
def _fatal(msg):
- logging.error(msg)
+ vlog.err(msg)
sys.stderr.write("%s\n" % msg)
sys.exit(1)
# This is global to keep Python from garbage-collecting and
# therefore closing our file after this function exits. That would
# unlock the lock for us, and we don't want that.
- global file
+ global file_handle
- file = open(tmpfile, "w")
+ file_handle = open(tmpfile, "w")
except IOError, e:
_fatal("%s: create failed (%s)" % (tmpfile, e.strerror))
try:
- s = os.fstat(file.fileno())
+ s = os.fstat(file_handle.fileno())
except IOError, e:
_fatal("%s: fstat failed (%s)" % (tmpfile, e.strerror))
try:
- file.write("%s\n" % pid)
- file.flush()
+ file_handle.write("%s\n" % pid)
+ file_handle.flush()
except OSError, e:
_fatal("%s: write failed: %s" % (tmpfile, e.strerror))
try:
- fcntl.lockf(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
+ fcntl.lockf(file_handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError, e:
_fatal("%s: fcntl failed: %s" % (tmpfile, e.strerror))
break
if len(s) != 1:
retval, status = _waitpid(pid, 0)
- if (retval == pid and
- os.WIFEXITED(status) and os.WEXITSTATUS(status)):
- # Child exited with an error. Convey the same error to
- # our parent process as a courtesy.
- sys.exit(os.WEXITSTATUS(status))
+ if retval == pid:
+ if os.WIFEXITED(status) and os.WEXITSTATUS(status):
+ # Child exited with an error. Convey the same error to
+ # our parent process as a courtesy.
+ sys.exit(os.WEXITSTATUS(status))
+ else:
+ sys.stderr.write("fork child failed to signal "
+ "startup (%s)\n"
+ % ovs.process.status_msg(status))
else:
- sys.stderr.write("fork child failed to signal startup\n")
+ assert retval < 0
+ sys.stderr.write("waitpid failed (%s)\n"
+ % os.strerror(-retval))
sys.exit(1)
os.close(rfd)
try:
resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
except resource.error:
- logging.warning("failed to disable core dumps")
+ vlog.warn("failed to disable core dumps")
# Throttle restarts to no more than once every 10 seconds.
if (last_restart is not None and
ovs.timeval.msec() < last_restart + 10000):
- logging.warning("%s, waiting until 10 seconds since last "
- "restart" % status_msg)
+ vlog.warn("%s, waiting until 10 seconds since last "
+ "restart" % status_msg)
while True:
now = ovs.timeval.msec()
wakeup = last_restart + 10000
time.sleep((wakeup - now) / 1000.0)
last_restart = ovs.timeval.msec()
- logging.error("%s, restarting" % status_msg)
+ vlog.err("%s, restarting" % status_msg)
daemon_pid = _fork_and_wait_for_startup()
if not daemon_pid:
break
else:
- logging.info("%s, exiting" % status_msg)
+ vlog.info("%s, exiting" % status_msg)
sys.exit(0)
# Running in new daemon process.
pass
try:
- file = open(pidfile, "r+")
+ file_handle = open(pidfile, "r+")
except IOError, e:
if e.errno == errno.ENOENT and delete_if_stale:
return 0
- logging.warning("%s: open: %s" % (pidfile, e.strerror))
+ vlog.warn("%s: open: %s" % (pidfile, e.strerror))
return -e.errno
# Python fcntl doesn't directly support F_GETLK so we have to just try
# to lock it.
try:
- fcntl.lockf(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
+ fcntl.lockf(file_handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
# pidfile exists but wasn't locked by anyone. Now we have the lock.
if not delete_if_stale:
- file.close()
- logging.warning("%s: pid file is stale" % pidfile)
+ file_handle.close()
+ vlog.warn("%s: pid file is stale" % pidfile)
return -errno.ESRCH
# Is the file we have locked still named 'pidfile'?
try:
raced = False
s = os.stat(pidfile)
- s2 = os.fstat(file.fileno())
+ s2 = os.fstat(file_handle.fileno())
if s.st_ino != s2.st_ino or s.st_dev != s2.st_dev:
raced = True
except IOError:
raced = True
if raced:
- logging.warning("%s: lost race to delete pidfile" % pidfile)
+ vlog.warn("%s: lost race to delete pidfile" % pidfile)
return -errno.EALREADY
# We won the right to delete the stale pidfile.
try:
os.unlink(pidfile)
except IOError, e:
- logging.warning("%s: failed to delete stale pidfile (%s)"
+ vlog.warn("%s: failed to delete stale pidfile (%s)"
% (pidfile, e.strerror))
return -e.errno
else:
- logging.debug("%s: deleted stale pidfile" % pidfile)
- file.close()
+ vlog.dbg("%s: deleted stale pidfile" % pidfile)
+ file_handle.close()
return 0
except IOError, e:
if e.errno not in [errno.EACCES, errno.EAGAIN]:
- logging.warn("%s: fcntl: %s" % (pidfile, e.strerror))
+ vlog.warn("%s: fcntl: %s" % (pidfile, e.strerror))
return -e.errno
# Someone else has the pidfile locked.
try:
try:
- return int(file.readline())
+ error = int(file_handle.readline())
except IOError, e:
- logging.warning("%s: read: %s" % (pidfile, e.strerror))
- return -e.errno
+ vlog.warn("%s: read: %s" % (pidfile, e.strerror))
+ error = -e.errno
except ValueError:
- logging.warning("%s does not contain a pid" % pidfile)
- return -errno.EINVAL
+ vlog.warn("%s does not contain a pid" % pidfile)
+ error = -errno.EINVAL
+
+ return error
finally:
try:
- file.close()
+ file_handle.close()
except IOError:
pass
_fatal("%s: pidfile check failed (%s), aborting"
% (_pidfile, os.strerror(pid)))
-# XXX Python's getopt does not support options with optional arguments, so we
-# have to separate --pidfile (with no argument) from --pidfile-name (with an
-# argument). Need to write our own getopt I guess.
-LONG_OPTIONS = ["detach", "no-chdir", "pidfile", "pidfile-name=",
- "overwrite-pidfile", "monitor"]
+
+def add_args(parser):
+ """Populates 'parser', an ArgumentParser allocated using the argparse
+ module, with the command line arguments required by the daemon module."""
+
+ pidfile = make_pidfile_name(None)
+
+ group = parser.add_argument_group(title="Daemon Options")
+ group.add_argument("--detach", action="store_true",
+ help="Run in background as a daemon.")
+ group.add_argument("--no-chdir", action="store_true",
+ help="Do not chdir to '/'.")
+ group.add_argument("--monitor", action="store_true",
+ help="Monitor %s process." % ovs.util.PROGRAM_NAME)
+ group.add_argument("--pidfile", nargs="?", const=pidfile,
+ help="Create pidfile (default %s)." % pidfile)
+ group.add_argument("--overwrite-pidfile", action="store_true",
+ help="With --pidfile, start even if already running.")
-def parse_opt(option, arg):
- if option == '--detach':
+def handle_args(args):
+ """Handles daemon module settings in 'args'. 'args' is an object
+ containing values parsed by the parse_args() method of ArgumentParser. The
+ parent ArgumentParser should have been prepared by add_args() before
+ calling parse_args()."""
+
+ if args.detach:
set_detach()
- elif option == '--no-chdir':
+
+ if args.no_chdir:
set_no_chdir()
- elif option == '--pidfile':
- set_pidfile(None)
- elif option == '--pidfile-name':
- set_pidfile(arg)
- elif option == '--overwrite-pidfile':
+
+ if args.pidfile:
+ set_pidfile(args.pidfile)
+
+ if args.overwrite_pidfile:
ignore_existing_pidfile()
- elif option == '--monitor':
+
+ if args.monitor:
set_monitor()
- else:
- return False
- return True