ovsdb-idlc: Make no-op writes to write-only columns cheaper.
[sliver-openvswitch.git] / python / ovs / daemon.py
index 5b9b06a..f74d7f0 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2010, 2011 Nicira Networks
+# Copyright (c) 2010, 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -14,7 +14,6 @@
 
 import errno
 import fcntl
-import logging
 import os
 import resource
 import signal
@@ -28,6 +27,9 @@ import ovs.process
 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
@@ -121,7 +123,7 @@ def set_monitor():
 
 
 def _fatal(msg):
-    logging.error(msg)
+    vlog.err(msg)
     sys.stderr.write("%s\n" % msg)
     sys.exit(1)
 
@@ -139,7 +141,7 @@ def _make_pidfile():
         # 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_handle = open(tmpfile, "w")
     except IOError, e:
@@ -243,13 +245,19 @@ def _fork_and_wait_for_startup():
                 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)
@@ -306,13 +314,13 @@ def _monitor_daemon(daemon_pid):
                     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
@@ -322,12 +330,12 @@ def _monitor_daemon(daemon_pid):
                         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.
@@ -354,7 +362,9 @@ def daemonize_start():
         if _fork_and_wait_for_startup() > 0:
             # Running in parent process.
             sys.exit(0)
+
         # Running in daemon or monitor process.
+        os.setsid()
 
     if _monitor:
         saved_daemonize_fd = _daemonize_fd
@@ -376,7 +386,6 @@ def daemonize_complete():
     _fork_notify_startup(_daemonize_fd)
 
     if _detach:
-        os.setsid()
         if _chdir:
             os.chdir("/")
         _close_standard_fds()
@@ -411,7 +420,7 @@ def __read_pidfile(pidfile, delete_if_stale):
     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
@@ -422,7 +431,7 @@ def __read_pidfile(pidfile, delete_if_stale):
         # pidfile exists but wasn't locked by anyone.  Now we have the lock.
         if not delete_if_stale:
             file_handle.close()
-            logging.warning("%s: pid file is stale" % pidfile)
+            vlog.warn("%s: pid file is stale" % pidfile)
             return -errno.ESRCH
 
         # Is the file we have locked still named 'pidfile'?
@@ -435,35 +444,37 @@ def __read_pidfile(pidfile, delete_if_stale):
         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)
+            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_handle.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_handle.close()
@@ -485,26 +496,43 @@ def _check_already_running():
         _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