-# Copyright (c) 2010 Nicira Networks
+# Copyright (c) 2010, 2011 Nicira Networks
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# File descriptor used by daemonize_start() and daemonize_complete().
_daemonize_fd = None
+RESTART_EXIT_CODE = 5
+
def make_pidfile_name(name):
"""Returns the file name that would be used for a pidfile if 'name' were
provided to set_pidfile()."""
return _chdir
def ignore_existing_pidfile():
- """Normally, die_if_already_running() will terminate the program with a
- message if a locked pidfile already exists. If this function is called,
- die_if_already_running() will merely log a warning."""
+ """Normally, daemonize() or daemonize_start() will terminate the program
+ with a message if a locked pidfile already exists. If this function is
+ called, an existing pidfile will be replaced, with a warning."""
global _overwrite_pidfile
_overwrite_pidfile = True
global _monitor
_monitor = True
-def _already_running():
- """If a pidfile has been configured and that pidfile already exists and is
- locked by a running process, returns True. Otherwise, returns False."""
- if _pidfile is not None:
- try:
- file = open(_pidfile, "r+")
- try:
- try:
- fcntl.lockf(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
- except IOError, e:
- if e.errno in [errno.EACCES, errno.EAGAIN]:
- return True
- logging.error("error locking %s (%s)"
- % (_pidfile, os.strerror(e.errno)))
- return False
- finally:
- # This releases the lock, which we don't really want.
- file.close()
- except IOError, e:
- if e.errno == errno.ENOENT:
- return False
- logging.error("error opening %s (%s)"
- % (_pidfile, os.strerror(e.errno)))
- return False
-
-def die_if_already_running():
+def _die_if_already_running():
"""If a locked pidfile exists, issue a warning message and, unless
ignore_existing_pidfile() has been called, terminate the program."""
- if _already_running():
+ if _pidfile is None:
+ return
+ pid = read_pidfile_if_exists(_pidfile)
+ if pid > 0:
if not _overwrite_pidfile:
- sys.stderr.write("%s: already running\n" % get_pidfile())
+ msg = "%s: already running as pid %d" % (_pidfile, pid)
+ logging.error("%s, aborting" % msg)
+ sys.stderr.write("%s\n" % msg)
sys.exit(1)
else:
logging.warn("%s: %s already running"
# Running in parent process.
os.close(wfd)
ovs.fatal_signal.fork()
- try:
- s = os.read(rfd, 1)
- except OSError, e:
- s = ""
+ while True:
+ try:
+ s = os.read(rfd, 1)
+ error = 0
+ except OSError, e:
+ s = ""
+ error = e.errno
+ if error != errno.EINTR:
+ break
if len(s) != 1:
retval, status = _waitpid(pid, 0)
if (retval == pid and
os.close(fd)
def _should_restart(status):
+ global RESTART_EXIT_CODE
+
+ if os.WIFEXITED(status) and os.WEXITSTATUS(status) == RESTART_EXIT_CODE:
+ return True
+
if os.WIFSIGNALED(status):
for signame in ("SIGABRT", "SIGALRM", "SIGBUS", "SIGFPE", "SIGILL",
"SIGPIPE", "SIGSEGV", "SIGXCPU", "SIGXFSZ"):
_monitor_daemon(daemon_pid)
# Running in daemon process
+ _die_if_already_running()
_make_pidfile()
def daemonize_complete():
--overwrite-pidfile with --pidfile, start even if already running
""" % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME))
-def read_pidfile(pidfile):
- """Opens and reads a PID from 'pidfile'. Returns the nonnegative PID if
- successful, otherwise a negative errno value."""
+def __read_pidfile(pidfile, must_exist):
if _pidfile_dev is not None:
try:
s = os.stat(pidfile)
try:
file = open(pidfile, "r")
except IOError, e:
+ if e.errno == errno.ENOENT and not must_exist:
+ return 0
logging.warning("%s: open: %s" % (pidfile, os.strerror(e.errno)))
return -e.errno
except IOError:
pass
+def read_pidfile(pidfile):
+ """Opens and reads a PID from 'pidfile'. Returns the positive PID if
+ successful, otherwise a negative errno value."""
+ return __read_pidfile(pidfile, True)
+
+def read_pidfile_if_exists(pidfile):
+ """Opens and reads a PID from 'pidfile'. Returns 0 if 'pidfile' does not
+ exist, the positive PID if successful, otherwise a negative errno value."""
+ return __read_pidfile(pidfile, False)
+
# 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.