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
26 import ovs.socket_util
31 vlog = ovs.vlog.Vlog("daemon")
33 # --detach: Should we run in the background?
36 # --pidfile: Name of pidfile (null if none).
39 # Our pidfile's inode and device, if we have created one.
43 # --overwrite-pidfile: Create pidfile even if one already exists and is locked?
44 _overwrite_pidfile = False
46 # --no-chdir: Should we chdir to "/"?
49 # --monitor: Should a supervisory process monitor the daemon and restart it if
50 # it dies due to an error signal?
53 # File descriptor used by daemonize_start() and daemonize_complete().
59 def make_pidfile_name(name):
60 """Returns the file name that would be used for a pidfile if 'name' were
61 provided to set_pidfile()."""
62 if name is None or name == "":
63 return "%s/%s.pid" % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME)
65 return ovs.util.abs_file_name(ovs.dirs.RUNDIR, name)
68 def set_pidfile(name):
69 """Sets up a following call to daemonize() to create a pidfile named
70 'name'. If 'name' begins with '/', then it is treated as an absolute path.
71 Otherwise, it is taken relative to ovs.util.RUNDIR, which is
72 $(prefix)/var/run by default.
74 If 'name' is null, then ovs.util.PROGRAM_NAME followed by ".pid" is
77 _pidfile = make_pidfile_name(name)
81 """Sets that we do not chdir to "/"."""
86 def ignore_existing_pidfile():
87 """Normally, daemonize() or daemonize_start() will terminate the program
88 with a message if a locked pidfile already exists. If this function is
89 called, an existing pidfile will be replaced, with a warning."""
90 global _overwrite_pidfile
91 _overwrite_pidfile = True
95 """Sets up a following call to daemonize() to detach from the foreground
96 session, running this process in the background."""
102 """Will daemonize() really detach?"""
107 """Sets up a following call to daemonize() to fork a supervisory process to
108 monitor the daemon and restart it if it dies due to an error signal."""
115 sys.stderr.write("%s\n" % msg)
120 """If a pidfile has been configured, creates it and stores the running
121 process's pid in it. Ensures that the pidfile will be deleted when the
125 # Create a temporary pidfile.
126 tmpfile = "%s.tmp%d" % (_pidfile, pid)
127 ovs.fatal_signal.add_file_to_unlink(tmpfile)
129 # This is global to keep Python from garbage-collecting and
130 # therefore closing our file after this function exits. That would
131 # unlock the lock for us, and we don't want that.
134 file_handle = open(tmpfile, "w")
136 _fatal("%s: create failed (%s)" % (tmpfile, e.strerror))
139 s = os.fstat(file_handle.fileno())
141 _fatal("%s: fstat failed (%s)" % (tmpfile, e.strerror))
144 file_handle.write("%s\n" % pid)
147 _fatal("%s: write failed: %s" % (tmpfile, e.strerror))
150 fcntl.lockf(file_handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
152 _fatal("%s: fcntl failed: %s" % (tmpfile, e.strerror))
154 # Rename or link it to the correct name.
155 if _overwrite_pidfile:
157 os.rename(tmpfile, _pidfile)
159 _fatal("failed to rename \"%s\" to \"%s\" (%s)"
160 % (tmpfile, _pidfile, e.strerror))
164 os.link(tmpfile, _pidfile)
168 if error == errno.EEXIST:
169 _check_already_running()
170 elif error != errno.EINTR:
173 _fatal("failed to link \"%s\" as \"%s\" (%s)"
174 % (tmpfile, _pidfile, os.strerror(error)))
176 # Ensure that the pidfile will get deleted on exit.
177 ovs.fatal_signal.add_file_to_unlink(_pidfile)
179 # Delete the temporary pidfile if it still exists.
180 if not _overwrite_pidfile:
181 error = ovs.fatal_signal.unlink_file_now(tmpfile)
183 _fatal("%s: unlink failed (%s)" % (tmpfile, os.strerror(error)))
187 _pidfile_dev = s.st_dev
188 _pidfile_ino = s.st_ino
192 """If configured with set_pidfile() or set_detach(), creates the pid file
193 and detaches from the foreground session."""
198 def _waitpid(pid, options):
201 return os.waitpid(pid, options)
203 if e.errno == errno.EINTR:
208 def _fork_and_wait_for_startup():
212 sys.stderr.write("pipe failed: %s\n" % os.strerror(e.errno))
218 sys.stderr.write("could not fork: %s\n" % os.strerror(e.errno))
222 # Running in parent process.
224 ovs.fatal_signal.fork()
232 if error != errno.EINTR:
235 retval, status = _waitpid(pid, 0)
237 if os.WIFEXITED(status) and os.WEXITSTATUS(status):
238 # Child exited with an error. Convey the same error to
239 # our parent process as a courtesy.
240 sys.exit(os.WEXITSTATUS(status))
242 sys.stderr.write("fork child failed to signal "
244 % ovs.process.status_msg(status))
247 sys.stderr.write("waitpid failed (%s)\n"
248 % os.strerror(-retval))
253 # Running in parent process.
255 ovs.timeval.postfork()
262 def _fork_notify_startup(fd):
264 error, bytes_written = ovs.socket_util.write_fully(fd, "0")
266 sys.stderr.write("could not write to pipe\n")
271 def _should_restart(status):
272 global RESTART_EXIT_CODE
274 if os.WIFEXITED(status) and os.WEXITSTATUS(status) == RESTART_EXIT_CODE:
277 if os.WIFSIGNALED(status):
278 for signame in ("SIGABRT", "SIGALRM", "SIGBUS", "SIGFPE", "SIGILL",
279 "SIGPIPE", "SIGSEGV", "SIGXCPU", "SIGXFSZ"):
280 if os.WTERMSIG(status) == getattr(signal, signame, None):
285 def _monitor_daemon(daemon_pid):
286 # XXX should log daemon's stderr output at startup time
287 # XXX should use setproctitle module if available
290 retval, status = _waitpid(daemon_pid, 0)
292 sys.stderr.write("waitpid failed\n")
294 elif retval == daemon_pid:
295 status_msg = ("pid %d died, %s"
296 % (daemon_pid, ovs.process.status_msg(status)))
298 if _should_restart(status):
299 if os.WCOREDUMP(status):
300 # Disable further core dumps to save disk space.
302 resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
303 except resource.error:
304 vlog.warn("failed to disable core dumps")
306 # Throttle restarts to no more than once every 10 seconds.
307 if (last_restart is not None and
308 ovs.timeval.msec() < last_restart + 10000):
309 vlog.warn("%s, waiting until 10 seconds since last "
310 "restart" % status_msg)
312 now = ovs.timeval.msec()
313 wakeup = last_restart + 10000
316 sys.stdout.write("sleep %f\n" % (
317 (wakeup - now) / 1000.0))
318 time.sleep((wakeup - now) / 1000.0)
319 last_restart = ovs.timeval.msec()
321 vlog.err("%s, restarting" % status_msg)
322 daemon_pid = _fork_and_wait_for_startup()
326 vlog.info("%s, exiting" % status_msg)
329 # Running in new daemon process.
332 def _close_standard_fds():
333 """Close stdin, stdout, stderr. If we're started from e.g. an SSH session,
334 then this keeps us from holding that session open artificially."""
335 null_fd = ovs.socket_util.get_null_fd()
342 def daemonize_start():
343 """If daemonization is configured, then starts daemonization, by forking
344 and returning in the child process. The parent process hangs around until
345 the child lets it know either that it completed startup successfully (by
346 calling daemon_complete()) or that it failed to start up (by exiting with a
347 nonzero exit code)."""
350 if _fork_and_wait_for_startup() > 0:
351 # Running in parent process.
354 # Running in daemon or monitor process.
358 saved_daemonize_fd = _daemonize_fd
359 daemon_pid = _fork_and_wait_for_startup()
361 # Running in monitor process.
362 _fork_notify_startup(saved_daemonize_fd)
363 _close_standard_fds()
364 _monitor_daemon(daemon_pid)
365 # Running in daemon process
371 def daemonize_complete():
372 """If daemonization is configured, then this function notifies the parent
373 process that the child process has completed startup successfully."""
374 _fork_notify_startup(_daemonize_fd)
379 _close_standard_fds()
385 --detach run in background as daemon
386 --no-chdir do not chdir to '/'
387 --pidfile[=FILE] create pidfile (default: %s/%s.pid)
388 --overwrite-pidfile with --pidfile, start even if already running
389 """ % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME))
392 def __read_pidfile(pidfile, delete_if_stale):
393 if _pidfile_dev is not None:
396 if s.st_ino == _pidfile_ino and s.st_dev == _pidfile_dev:
397 # It's our own pidfile. We can't afford to open it,
398 # because closing *any* fd for a file that a process
399 # has locked also releases all the locks on that file.
401 # Fortunately, we know the associated pid anyhow.
407 file_handle = open(pidfile, "r+")
409 if e.errno == errno.ENOENT and delete_if_stale:
411 vlog.warn("%s: open: %s" % (pidfile, e.strerror))
414 # Python fcntl doesn't directly support F_GETLK so we have to just try
417 fcntl.lockf(file_handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
419 # pidfile exists but wasn't locked by anyone. Now we have the lock.
420 if not delete_if_stale:
422 vlog.warn("%s: pid file is stale" % pidfile)
425 # Is the file we have locked still named 'pidfile'?
429 s2 = os.fstat(file_handle.fileno())
430 if s.st_ino != s2.st_ino or s.st_dev != s2.st_dev:
435 vlog.warn("%s: lost race to delete pidfile" % pidfile)
436 return -errno.EALREADY
438 # We won the right to delete the stale pidfile.
442 vlog.warn("%s: failed to delete stale pidfile (%s)"
443 % (pidfile, e.strerror))
446 vlog.dbg("%s: deleted stale pidfile" % pidfile)
450 if e.errno not in [errno.EACCES, errno.EAGAIN]:
451 vlog.warn("%s: fcntl: %s" % (pidfile, e.strerror))
454 # Someone else has the pidfile locked.
457 error = int(file_handle.readline())
459 vlog.warn("%s: read: %s" % (pidfile, e.strerror))
462 vlog.warn("%s does not contain a pid" % pidfile)
463 error = -errno.EINVAL
473 def read_pidfile(pidfile):
474 """Opens and reads a PID from 'pidfile'. Returns the positive PID if
475 successful, otherwise a negative errno value."""
476 return __read_pidfile(pidfile, False)
479 def _check_already_running():
480 pid = __read_pidfile(_pidfile, True)
482 _fatal("%s: already running as pid %d, aborting" % (_pidfile, pid))
484 _fatal("%s: pidfile check failed (%s), aborting"
485 % (_pidfile, os.strerror(pid)))
488 def add_args(parser):
489 """Populates 'parser', an ArgumentParser allocated using the argparse
490 module, with the command line arguments required by the daemon module."""
492 pidfile = make_pidfile_name(None)
494 group = parser.add_argument_group(title="Daemon Options")
495 group.add_argument("--detach", action="store_true",
496 help="Run in background as a daemon.")
497 group.add_argument("--no-chdir", action="store_true",
498 help="Do not chdir to '/'.")
499 group.add_argument("--monitor", action="store_true",
500 help="Monitor %s process." % ovs.util.PROGRAM_NAME)
501 group.add_argument("--pidfile", nargs="?", const=pidfile,
502 help="Create pidfile (default %s)." % pidfile)
503 group.add_argument("--overwrite-pidfile", action="store_true",
504 help="With --pidfile, start even if already running.")
507 def handle_args(args):
508 """Handles daemon module settings in 'args'. 'args' is an object
509 containing values parsed by the parse_args() method of ArgumentParser. The
510 parent ArgumentParser should have been prepared by add_args() before
511 calling parse_args()."""
520 set_pidfile(args.pidfile)
522 if args.overwrite_pidfile:
523 ignore_existing_pidfile()