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 """Sets that we do not chdir to "/"."""
87 def ignore_existing_pidfile():
88 """Normally, daemonize() or daemonize_start() will terminate the program
89 with a message if a locked pidfile already exists. If this function is
90 called, an existing pidfile will be replaced, with a warning."""
91 global _overwrite_pidfile
92 _overwrite_pidfile = True
96 """Sets up a following call to daemonize() to detach from the foreground
97 session, running this process in the background."""
103 """Will daemonize() really detach?"""
108 """Sets up a following call to daemonize() to fork a supervisory process to
109 monitor the daemon and restart it if it dies due to an error signal."""
116 sys.stderr.write("%s\n" % msg)
121 """If a pidfile has been configured, creates it and stores the running
122 process's pid in it. Ensures that the pidfile will be deleted when the
126 # Create a temporary pidfile.
127 tmpfile = "%s.tmp%d" % (_pidfile, pid)
128 ovs.fatal_signal.add_file_to_unlink(tmpfile)
130 # This is global to keep Python from garbage-collecting and
131 # therefore closing our file after this function exits. That would
132 # unlock the lock for us, and we don't want that.
135 file_handle = open(tmpfile, "w")
137 _fatal("%s: create failed (%s)" % (tmpfile, e.strerror))
140 s = os.fstat(file_handle.fileno())
142 _fatal("%s: fstat failed (%s)" % (tmpfile, e.strerror))
145 file_handle.write("%s\n" % pid)
148 _fatal("%s: write failed: %s" % (tmpfile, e.strerror))
151 fcntl.lockf(file_handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
153 _fatal("%s: fcntl failed: %s" % (tmpfile, e.strerror))
155 # Rename or link it to the correct name.
156 if _overwrite_pidfile:
158 os.rename(tmpfile, _pidfile)
160 _fatal("failed to rename \"%s\" to \"%s\" (%s)"
161 % (tmpfile, _pidfile, e.strerror))
165 os.link(tmpfile, _pidfile)
169 if error == errno.EEXIST:
170 _check_already_running()
171 elif error != errno.EINTR:
174 _fatal("failed to link \"%s\" as \"%s\" (%s)"
175 % (tmpfile, _pidfile, os.strerror(error)))
177 # Ensure that the pidfile will get deleted on exit.
178 ovs.fatal_signal.add_file_to_unlink(_pidfile)
180 # Delete the temporary pidfile if it still exists.
181 if not _overwrite_pidfile:
182 error = ovs.fatal_signal.unlink_file_now(tmpfile)
184 _fatal("%s: unlink failed (%s)" % (tmpfile, os.strerror(error)))
188 _pidfile_dev = s.st_dev
189 _pidfile_ino = s.st_ino
193 """If configured with set_pidfile() or set_detach(), creates the pid file
194 and detaches from the foreground session."""
199 def _waitpid(pid, options):
202 return os.waitpid(pid, options)
204 if e.errno == errno.EINTR:
209 def _fork_and_wait_for_startup():
213 sys.stderr.write("pipe failed: %s\n" % os.strerror(e.errno))
219 sys.stderr.write("could not fork: %s\n" % os.strerror(e.errno))
223 # Running in parent process.
225 ovs.fatal_signal.fork()
233 if error != errno.EINTR:
236 retval, status = _waitpid(pid, 0)
238 if os.WIFEXITED(status) and os.WEXITSTATUS(status):
239 # Child exited with an error. Convey the same error to
240 # our parent process as a courtesy.
241 sys.exit(os.WEXITSTATUS(status))
243 sys.stderr.write("fork child failed to signal "
245 % ovs.process.status_msg(status))
248 sys.stderr.write("waitpid failed (%s)\n"
249 % os.strerror(-retval))
254 # Running in parent process.
256 ovs.timeval.postfork()
257 #ovs.lockfile.postfork()
264 def _fork_notify_startup(fd):
266 error, bytes_written = ovs.socket_util.write_fully(fd, "0")
268 sys.stderr.write("could not write to pipe\n")
273 def _should_restart(status):
274 global RESTART_EXIT_CODE
276 if os.WIFEXITED(status) and os.WEXITSTATUS(status) == RESTART_EXIT_CODE:
279 if os.WIFSIGNALED(status):
280 for signame in ("SIGABRT", "SIGALRM", "SIGBUS", "SIGFPE", "SIGILL",
281 "SIGPIPE", "SIGSEGV", "SIGXCPU", "SIGXFSZ"):
282 if os.WTERMSIG(status) == getattr(signal, signame, None):
287 def _monitor_daemon(daemon_pid):
288 # XXX should log daemon's stderr output at startup time
289 # XXX should use setproctitle module if available
292 retval, status = _waitpid(daemon_pid, 0)
294 sys.stderr.write("waitpid failed\n")
296 elif retval == daemon_pid:
297 status_msg = ("pid %d died, %s"
298 % (daemon_pid, ovs.process.status_msg(status)))
300 if _should_restart(status):
301 if os.WCOREDUMP(status):
302 # Disable further core dumps to save disk space.
304 resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
305 except resource.error:
306 vlog.warn("failed to disable core dumps")
308 # Throttle restarts to no more than once every 10 seconds.
309 if (last_restart is not None and
310 ovs.timeval.msec() < last_restart + 10000):
311 vlog.warn("%s, waiting until 10 seconds since last "
312 "restart" % status_msg)
314 now = ovs.timeval.msec()
315 wakeup = last_restart + 10000
318 print "sleep %f" % ((wakeup - now) / 1000.0)
319 time.sleep((wakeup - now) / 1000.0)
320 last_restart = ovs.timeval.msec()
322 vlog.err("%s, restarting" % status_msg)
323 daemon_pid = _fork_and_wait_for_startup()
327 vlog.info("%s, exiting" % status_msg)
330 # Running in new daemon process.
333 def _close_standard_fds():
334 """Close stdin, stdout, stderr. If we're started from e.g. an SSH session,
335 then this keeps us from holding that session open artificially."""
336 null_fd = ovs.socket_util.get_null_fd()
343 def daemonize_start():
344 """If daemonization is configured, then starts daemonization, by forking
345 and returning in the child process. The parent process hangs around until
346 the child lets it know either that it completed startup successfully (by
347 calling daemon_complete()) or that it failed to start up (by exiting with a
348 nonzero exit code)."""
351 if _fork_and_wait_for_startup() > 0:
352 # Running in parent process.
355 # Running in daemon or monitor process.
359 saved_daemonize_fd = _daemonize_fd
360 daemon_pid = _fork_and_wait_for_startup()
362 # Running in monitor process.
363 _fork_notify_startup(saved_daemonize_fd)
364 _close_standard_fds()
365 _monitor_daemon(daemon_pid)
366 # Running in daemon process
372 def daemonize_complete():
373 """If daemonization is configured, then this function notifies the parent
374 process that the child process has completed startup successfully."""
375 _fork_notify_startup(_daemonize_fd)
380 _close_standard_fds()
386 --detach run in background as daemon
387 --no-chdir do not chdir to '/'
388 --pidfile[=FILE] create pidfile (default: %s/%s.pid)
389 --overwrite-pidfile with --pidfile, start even if already running
390 """ % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME))
393 def __read_pidfile(pidfile, delete_if_stale):
394 if _pidfile_dev is not None:
397 if s.st_ino == _pidfile_ino and s.st_dev == _pidfile_dev:
398 # It's our own pidfile. We can't afford to open it,
399 # because closing *any* fd for a file that a process
400 # has locked also releases all the locks on that file.
402 # Fortunately, we know the associated pid anyhow.
408 file_handle = open(pidfile, "r+")
410 if e.errno == errno.ENOENT and delete_if_stale:
412 vlog.warn("%s: open: %s" % (pidfile, e.strerror))
415 # Python fcntl doesn't directly support F_GETLK so we have to just try
418 fcntl.lockf(file_handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
420 # pidfile exists but wasn't locked by anyone. Now we have the lock.
421 if not delete_if_stale:
423 vlog.warn("%s: pid file is stale" % pidfile)
426 # Is the file we have locked still named 'pidfile'?
430 s2 = os.fstat(file_handle.fileno())
431 if s.st_ino != s2.st_ino or s.st_dev != s2.st_dev:
436 vlog.warn("%s: lost race to delete pidfile" % pidfile)
437 return -errno.EALREADY
439 # We won the right to delete the stale pidfile.
443 vlog.warn("%s: failed to delete stale pidfile (%s)"
444 % (pidfile, e.strerror))
447 vlog.dbg("%s: deleted stale pidfile" % pidfile)
451 if e.errno not in [errno.EACCES, errno.EAGAIN]:
452 vlog.warn("%s: fcntl: %s" % (pidfile, e.strerror))
455 # Someone else has the pidfile locked.
458 error = int(file_handle.readline())
460 vlog.warn("%s: read: %s" % (pidfile, e.strerror))
463 vlog.warn("%s does not contain a pid" % pidfile)
464 error = -errno.EINVAL
474 def read_pidfile(pidfile):
475 """Opens and reads a PID from 'pidfile'. Returns the positive PID if
476 successful, otherwise a negative errno value."""
477 return __read_pidfile(pidfile, False)
480 def _check_already_running():
481 pid = __read_pidfile(_pidfile, True)
483 _fatal("%s: already running as pid %d, aborting" % (_pidfile, pid))
485 _fatal("%s: pidfile check failed (%s), aborting"
486 % (_pidfile, os.strerror(pid)))
489 def add_args(parser):
490 """Populates 'parser', an ArgumentParser allocated using the argparse
491 module, with the command line arguments required by the daemon module."""
493 pidfile = make_pidfile_name(None)
495 group = parser.add_argument_group(title="Daemon Options")
496 group.add_argument("--detach", action="store_true",
497 help="Run in background as a daemon.")
498 group.add_argument("--no-chdir", action="store_true",
499 help="Do not chdir to '/'.")
500 group.add_argument("--monitor", action="store_true",
501 help="Monitor %s process." % ovs.util.PROGRAM_NAME)
502 group.add_argument("--pidfile", nargs="?", const=pidfile,
503 help="Create pidfile (default %s)." % pidfile)
504 group.add_argument("--overwrite-pidfile", action="store_true",
505 help="With --pidfile, start even if already running.")
508 def handle_args(args):
509 """Handles daemon module settings in 'args'. 'args' is an object
510 containing values parsed by the parse_args() method of ArgumentParser. The
511 parent ArgumentParser should have been prepared by add_args() before
512 calling parse_args()."""
521 set_pidfile(args.pidfile)
523 if args.overwrite_pidfile:
524 ignore_existing_pidfile()