1 # Copyright (c) 2010, 2011, 2012 Nicira, Inc.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at:
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
24 import ovs.fatal_signal
27 import ovs.socket_util
32 vlog = ovs.vlog.Vlog("daemon")
34 # --detach: Should we run in the background?
37 # --pidfile: Name of pidfile (null if none).
40 # Our pidfile's inode and device, if we have created one.
44 # --overwrite-pidfile: Create pidfile even if one already exists and is locked?
45 _overwrite_pidfile = False
47 # --no-chdir: Should we chdir to "/"?
50 # --monitor: Should a supervisory process monitor the daemon and restart it if
51 # it dies due to an error signal?
54 # File descriptor used by daemonize_start() and daemonize_complete().
60 def make_pidfile_name(name):
61 """Returns the file name that would be used for a pidfile if 'name' were
62 provided to set_pidfile()."""
63 if name is None or name == "":
64 return "%s/%s.pid" % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME)
66 return ovs.util.abs_file_name(ovs.dirs.RUNDIR, name)
69 def set_pidfile(name):
70 """Sets up a following call to daemonize() to create a pidfile named
71 'name'. If 'name' begins with '/', then it is treated as an absolute path.
72 Otherwise, it is taken relative to ovs.util.RUNDIR, which is
73 $(prefix)/var/run by default.
75 If 'name' is null, then ovs.util.PROGRAM_NAME followed by ".pid" is
78 _pidfile = make_pidfile_name(name)
82 """Returns an absolute path to the configured pidfile, or None if no
83 pidfile is configured."""
88 """Sets that we do not chdir to "/"."""
93 def is_chdir_enabled():
94 """Will we chdir to "/" as part of daemonizing?"""
98 def ignore_existing_pidfile():
99 """Normally, daemonize() or daemonize_start() will terminate the program
100 with a message if a locked pidfile already exists. If this function is
101 called, an existing pidfile will be replaced, with a warning."""
102 global _overwrite_pidfile
103 _overwrite_pidfile = True
107 """Sets up a following call to daemonize() to detach from the foreground
108 session, running this process in the background."""
114 """Will daemonize() really detach?"""
119 """Sets up a following call to daemonize() to fork a supervisory process to
120 monitor the daemon and restart it if it dies due to an error signal."""
127 sys.stderr.write("%s\n" % msg)
132 """If a pidfile has been configured, creates it and stores the running
133 process's pid in it. Ensures that the pidfile will be deleted when the
137 # Create a temporary pidfile.
138 tmpfile = "%s.tmp%d" % (_pidfile, pid)
139 ovs.fatal_signal.add_file_to_unlink(tmpfile)
141 # This is global to keep Python from garbage-collecting and
142 # therefore closing our file after this function exits. That would
143 # unlock the lock for us, and we don't want that.
146 file_handle = open(tmpfile, "w")
148 _fatal("%s: create failed (%s)" % (tmpfile, e.strerror))
151 s = os.fstat(file_handle.fileno())
153 _fatal("%s: fstat failed (%s)" % (tmpfile, e.strerror))
156 file_handle.write("%s\n" % pid)
159 _fatal("%s: write failed: %s" % (tmpfile, e.strerror))
162 fcntl.lockf(file_handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
164 _fatal("%s: fcntl failed: %s" % (tmpfile, e.strerror))
166 # Rename or link it to the correct name.
167 if _overwrite_pidfile:
169 os.rename(tmpfile, _pidfile)
171 _fatal("failed to rename \"%s\" to \"%s\" (%s)"
172 % (tmpfile, _pidfile, e.strerror))
176 os.link(tmpfile, _pidfile)
180 if error == errno.EEXIST:
181 _check_already_running()
182 elif error != errno.EINTR:
185 _fatal("failed to link \"%s\" as \"%s\" (%s)"
186 % (tmpfile, _pidfile, os.strerror(error)))
188 # Ensure that the pidfile will get deleted on exit.
189 ovs.fatal_signal.add_file_to_unlink(_pidfile)
191 # Delete the temporary pidfile if it still exists.
192 if not _overwrite_pidfile:
193 error = ovs.fatal_signal.unlink_file_now(tmpfile)
195 _fatal("%s: unlink failed (%s)" % (tmpfile, os.strerror(error)))
199 _pidfile_dev = s.st_dev
200 _pidfile_ino = s.st_ino
204 """If configured with set_pidfile() or set_detach(), creates the pid file
205 and detaches from the foreground session."""
210 def _waitpid(pid, options):
213 return os.waitpid(pid, options)
215 if e.errno == errno.EINTR:
220 def _fork_and_wait_for_startup():
224 sys.stderr.write("pipe failed: %s\n" % os.strerror(e.errno))
230 sys.stderr.write("could not fork: %s\n" % os.strerror(e.errno))
234 # Running in parent process.
236 ovs.fatal_signal.fork()
244 if error != errno.EINTR:
247 retval, status = _waitpid(pid, 0)
249 if os.WIFEXITED(status) and os.WEXITSTATUS(status):
250 # Child exited with an error. Convey the same error to
251 # our parent process as a courtesy.
252 sys.exit(os.WEXITSTATUS(status))
254 sys.stderr.write("fork child failed to signal "
256 % ovs.process.status_msg(status))
259 sys.stderr.write("waitpid failed (%s)\n"
260 % os.strerror(-retval))
265 # Running in parent process.
267 ovs.timeval.postfork()
268 #ovs.lockfile.postfork()
275 def _fork_notify_startup(fd):
277 error, bytes_written = ovs.socket_util.write_fully(fd, "0")
279 sys.stderr.write("could not write to pipe\n")
284 def _should_restart(status):
285 global RESTART_EXIT_CODE
287 if os.WIFEXITED(status) and os.WEXITSTATUS(status) == RESTART_EXIT_CODE:
290 if os.WIFSIGNALED(status):
291 for signame in ("SIGABRT", "SIGALRM", "SIGBUS", "SIGFPE", "SIGILL",
292 "SIGPIPE", "SIGSEGV", "SIGXCPU", "SIGXFSZ"):
293 if os.WTERMSIG(status) == getattr(signal, signame, None):
298 def _monitor_daemon(daemon_pid):
299 # XXX should log daemon's stderr output at startup time
300 # XXX should use setproctitle module if available
303 retval, status = _waitpid(daemon_pid, 0)
305 sys.stderr.write("waitpid failed\n")
307 elif retval == daemon_pid:
308 status_msg = ("pid %d died, %s"
309 % (daemon_pid, ovs.process.status_msg(status)))
311 if _should_restart(status):
312 if os.WCOREDUMP(status):
313 # Disable further core dumps to save disk space.
315 resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
316 except resource.error:
317 vlog.warn("failed to disable core dumps")
319 # Throttle restarts to no more than once every 10 seconds.
320 if (last_restart is not None and
321 ovs.timeval.msec() < last_restart + 10000):
322 vlog.warn("%s, waiting until 10 seconds since last "
323 "restart" % status_msg)
325 now = ovs.timeval.msec()
326 wakeup = last_restart + 10000
329 print "sleep %f" % ((wakeup - now) / 1000.0)
330 time.sleep((wakeup - now) / 1000.0)
331 last_restart = ovs.timeval.msec()
333 vlog.err("%s, restarting" % status_msg)
334 daemon_pid = _fork_and_wait_for_startup()
338 vlog.info("%s, exiting" % status_msg)
341 # Running in new daemon process.
344 def _close_standard_fds():
345 """Close stdin, stdout, stderr. If we're started from e.g. an SSH session,
346 then this keeps us from holding that session open artificially."""
347 null_fd = ovs.socket_util.get_null_fd()
354 def daemonize_start():
355 """If daemonization is configured, then starts daemonization, by forking
356 and returning in the child process. The parent process hangs around until
357 the child lets it know either that it completed startup successfully (by
358 calling daemon_complete()) or that it failed to start up (by exiting with a
359 nonzero exit code)."""
362 if _fork_and_wait_for_startup() > 0:
363 # Running in parent process.
366 # Running in daemon or monitor process.
370 saved_daemonize_fd = _daemonize_fd
371 daemon_pid = _fork_and_wait_for_startup()
373 # Running in monitor process.
374 _fork_notify_startup(saved_daemonize_fd)
375 _close_standard_fds()
376 _monitor_daemon(daemon_pid)
377 # Running in daemon process
383 def daemonize_complete():
384 """If daemonization is configured, then this function notifies the parent
385 process that the child process has completed startup successfully."""
386 _fork_notify_startup(_daemonize_fd)
391 _close_standard_fds()
397 --detach run in background as daemon
398 --no-chdir do not chdir to '/'
399 --pidfile[=FILE] create pidfile (default: %s/%s.pid)
400 --overwrite-pidfile with --pidfile, start even if already running
401 """ % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME))
404 def __read_pidfile(pidfile, delete_if_stale):
405 if _pidfile_dev is not None:
408 if s.st_ino == _pidfile_ino and s.st_dev == _pidfile_dev:
409 # It's our own pidfile. We can't afford to open it,
410 # because closing *any* fd for a file that a process
411 # has locked also releases all the locks on that file.
413 # Fortunately, we know the associated pid anyhow.
419 file_handle = open(pidfile, "r+")
421 if e.errno == errno.ENOENT and delete_if_stale:
423 vlog.warn("%s: open: %s" % (pidfile, e.strerror))
426 # Python fcntl doesn't directly support F_GETLK so we have to just try
429 fcntl.lockf(file_handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
431 # pidfile exists but wasn't locked by anyone. Now we have the lock.
432 if not delete_if_stale:
434 vlog.warn("%s: pid file is stale" % pidfile)
437 # Is the file we have locked still named 'pidfile'?
441 s2 = os.fstat(file_handle.fileno())
442 if s.st_ino != s2.st_ino or s.st_dev != s2.st_dev:
447 vlog.warn("%s: lost race to delete pidfile" % pidfile)
448 return -errno.EALREADY
450 # We won the right to delete the stale pidfile.
454 vlog.warn("%s: failed to delete stale pidfile (%s)"
455 % (pidfile, e.strerror))
458 vlog.dbg("%s: deleted stale pidfile" % pidfile)
462 if e.errno not in [errno.EACCES, errno.EAGAIN]:
463 vlog.warn("%s: fcntl: %s" % (pidfile, e.strerror))
466 # Someone else has the pidfile locked.
469 error = int(file_handle.readline())
471 vlog.warn("%s: read: %s" % (pidfile, e.strerror))
474 vlog.warn("%s does not contain a pid" % pidfile)
475 error = -errno.EINVAL
485 def read_pidfile(pidfile):
486 """Opens and reads a PID from 'pidfile'. Returns the positive PID if
487 successful, otherwise a negative errno value."""
488 return __read_pidfile(pidfile, False)
491 def _check_already_running():
492 pid = __read_pidfile(_pidfile, True)
494 _fatal("%s: already running as pid %d, aborting" % (_pidfile, pid))
496 _fatal("%s: pidfile check failed (%s), aborting"
497 % (_pidfile, os.strerror(pid)))
500 def add_args(parser):
501 """Populates 'parser', an ArgumentParser allocated using the argparse
502 module, with the command line arguments required by the daemon module."""
504 pidfile = make_pidfile_name(None)
506 group = parser.add_argument_group(title="Daemon Options")
507 group.add_argument("--detach", action="store_true",
508 help="Run in background as a daemon.")
509 group.add_argument("--no-chdir", action="store_true",
510 help="Do not chdir to '/'.")
511 group.add_argument("--monitor", action="store_true",
512 help="Monitor %s process." % ovs.util.PROGRAM_NAME)
513 group.add_argument("--pidfile", nargs="?", const=pidfile,
514 help="Create pidfile (default %s)." % pidfile)
515 group.add_argument("--overwrite-pidfile", action="store_true",
516 help="With --pidfile, start even if already running.")
519 def handle_args(args):
520 """Handles daemon module settings in 'args'. 'args' is an object
521 containing values parsed by the parse_args() method of ArgumentParser. The
522 parent ArgumentParser should have been prepared by add_args() before
523 calling parse_args()."""
532 set_pidfile(args.pidfile)
534 if args.overwrite_pidfile:
535 ignore_existing_pidfile()