1 # Copyright (c) 2010, 2011 Nicira Networks
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.
25 import ovs.fatal_signal
28 import ovs.socket_util
32 # --detach: Should we run in the background?
35 # --pidfile: Name of pidfile (null if none).
38 # Our pidfile's inode and device, if we have created one.
42 # --overwrite-pidfile: Create pidfile even if one already exists and is locked?
43 _overwrite_pidfile = False
45 # --no-chdir: Should we chdir to "/"?
48 # --monitor: Should a supervisory process monitor the daemon and restart it if
49 # it dies due to an error signal?
52 # File descriptor used by daemonize_start() and daemonize_complete().
57 def make_pidfile_name(name):
58 """Returns the file name that would be used for a pidfile if 'name' were
59 provided to set_pidfile()."""
60 if name is None or name == "":
61 return "%s/%s.pid" % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME)
63 return ovs.util.abs_file_name(ovs.dirs.RUNDIR, name)
65 def set_pidfile(name):
66 """Sets up a following call to daemonize() to create a pidfile named
67 'name'. If 'name' begins with '/', then it is treated as an absolute path.
68 Otherwise, it is taken relative to ovs.util.RUNDIR, which is
69 $(prefix)/var/run by default.
71 If 'name' is null, then ovs.util.PROGRAM_NAME followed by ".pid" is
74 _pidfile = make_pidfile_name(name)
77 """Returns an absolute path to the configured pidfile, or None if no
78 pidfile is configured. The caller must not modify or free the returned
83 """Sets that we do not chdir to "/"."""
87 def is_chdir_enabled():
88 """Will we chdir to "/" as part of daemonizing?"""
91 def ignore_existing_pidfile():
92 """Normally, daemonize() or daemonize_start() will terminate the program
93 with a message if a locked pidfile already exists. If this function is
94 called, an existing pidfile will be replaced, with a warning."""
95 global _overwrite_pidfile
96 _overwrite_pidfile = True
99 """Sets up a following call to daemonize() to detach from the foreground
100 session, running this process in the background."""
105 """Will daemonize() really detach?"""
109 """Sets up a following call to daemonize() to fork a supervisory process to
110 monitor the daemon and restart it if it dies due to an error signal."""
116 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 = open(tmpfile, "w")
136 _fatal("%s: create failed (%s)" % (tmpfile, e.strerror))
139 s = os.fstat(file.fileno())
141 _fatal("%s: fstat failed (%s)" % (tmpfile, e.strerror))
144 file.write("%s\n" % pid)
147 _fatal("%s: write failed: %s" % (tmpfile, e.strerror))
150 fcntl.lockf(file, 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)))
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)))
186 _pidfile_dev = s.st_dev
187 _pidfile_ino = s.st_ino
190 """If configured with set_pidfile() or set_detach(), creates the pid file
191 and detaches from the foreground session."""
195 def _waitpid(pid, options):
198 return os.waitpid(pid, options)
200 if e.errno == errno.EINTR:
204 def _fork_and_wait_for_startup():
208 sys.stderr.write("pipe failed: %s\n" % os.strerror(e.errno))
214 sys.stderr.write("could not fork: %s\n" % os.strerror(e.errno))
218 # Running in parent process.
220 ovs.fatal_signal.fork()
228 if error != errno.EINTR:
231 retval, status = _waitpid(pid, 0)
232 if (retval == pid and
233 os.WIFEXITED(status) and os.WEXITSTATUS(status)):
234 # Child exited with an error. Convey the same error to
235 # our parent process as a courtesy.
236 sys.exit(os.WEXITSTATUS(status))
238 sys.stderr.write("fork child failed to signal startup\n")
243 # Running in parent process.
245 ovs.timeval.postfork()
246 #ovs.lockfile.postfork()
252 def _fork_notify_startup(fd):
254 error, bytes_written = ovs.socket_util.write_fully(fd, "0")
256 sys.stderr.write("could not write to pipe\n")
260 def _should_restart(status):
261 global RESTART_EXIT_CODE
263 if os.WIFEXITED(status) and os.WEXITSTATUS(status) == RESTART_EXIT_CODE:
266 if os.WIFSIGNALED(status):
267 for signame in ("SIGABRT", "SIGALRM", "SIGBUS", "SIGFPE", "SIGILL",
268 "SIGPIPE", "SIGSEGV", "SIGXCPU", "SIGXFSZ"):
269 if os.WTERMSIG(status) == getattr(signal, signame, None):
273 def _monitor_daemon(daemon_pid):
274 # XXX should log daemon's stderr output at startup time
275 # XXX should use setproctitle module if available
278 retval, status = _waitpid(daemon_pid, 0)
280 sys.stderr.write("waitpid failed\n")
282 elif retval == daemon_pid:
283 status_msg = ("pid %d died, %s"
284 % (daemon_pid, ovs.process.status_msg(status)))
286 if _should_restart(status):
287 if os.WCOREDUMP(status):
288 # Disable further core dumps to save disk space.
290 resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
291 except resource.error:
292 logging.warning("failed to disable core dumps")
294 # Throttle restarts to no more than once every 10 seconds.
295 if (last_restart is not None and
296 ovs.timeval.msec() < last_restart + 10000):
297 logging.warning("%s, waiting until 10 seconds since last "
298 "restart" % status_msg)
300 now = ovs.timeval.msec()
301 wakeup = last_restart + 10000
304 print "sleep %f" % ((wakeup - now) / 1000.0)
305 time.sleep((wakeup - now) / 1000.0)
306 last_restart = ovs.timeval.msec()
308 logging.error("%s, restarting" % status_msg)
309 daemon_pid = _fork_and_wait_for_startup()
313 logging.info("%s, exiting" % status_msg)
316 # Running in new daemon process.
318 def _close_standard_fds():
319 """Close stdin, stdout, stderr. If we're started from e.g. an SSH session,
320 then this keeps us from holding that session open artificially."""
321 null_fd = ovs.socket_util.get_null_fd()
327 def daemonize_start():
328 """If daemonization is configured, then starts daemonization, by forking
329 and returning in the child process. The parent process hangs around until
330 the child lets it know either that it completed startup successfully (by
331 calling daemon_complete()) or that it failed to start up (by exiting with a
332 nonzero exit code)."""
335 if _fork_and_wait_for_startup() > 0:
336 # Running in parent process.
338 # Running in daemon or monitor process.
341 saved_daemonize_fd = _daemonize_fd
342 daemon_pid = _fork_and_wait_for_startup()
344 # Running in monitor process.
345 _fork_notify_startup(saved_daemonize_fd)
346 _close_standard_fds()
347 _monitor_daemon(daemon_pid)
348 # Running in daemon process
353 def daemonize_complete():
354 """If daemonization is configured, then this function notifies the parent
355 process that the child process has completed startup successfully."""
356 _fork_notify_startup(_daemonize_fd)
362 _close_standard_fds()
367 --detach run in background as daemon
368 --no-chdir do not chdir to '/'
369 --pidfile[=FILE] create pidfile (default: %s/%s.pid)
370 --overwrite-pidfile with --pidfile, start even if already running
371 """ % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME))
373 def __read_pidfile(pidfile, delete_if_stale):
374 if _pidfile_dev is not None:
377 if s.st_ino == _pidfile_ino and s.st_dev == _pidfile_dev:
378 # It's our own pidfile. We can't afford to open it,
379 # because closing *any* fd for a file that a process
380 # has locked also releases all the locks on that file.
382 # Fortunately, we know the associated pid anyhow.
388 file = open(pidfile, "r+")
390 if e.errno == errno.ENOENT and delete_if_stale:
392 logging.warning("%s: open: %s" % (pidfile, e.strerror))
395 # Python fcntl doesn't directly support F_GETLK so we have to just try
398 fcntl.lockf(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
400 # pidfile exists but wasn't locked by anyone. Now we have the lock.
401 if not delete_if_stale:
403 logging.warning("%s: pid file is stale" % pidfile)
406 # Is the file we have locked still named 'pidfile'?
410 s2 = os.fstat(file.fileno())
411 if s.st_ino != s2.st_ino or s.st_dev != s2.st_dev:
416 logging.warning("%s: lost race to delete pidfile" % pidfile)
417 return -errno.ALREADY
419 # We won the right to delete the stale pidfile.
423 logging.warning("%s: failed to delete stale pidfile"
424 % (pidfile, e.strerror))
427 logging.debug("%s: deleted stale pidfile" % pidfile)
431 if e.errno not in [errno.EACCES, errno.EAGAIN]:
432 logging.warn("%s: fcntl: %s" % (pidfile, e.strerror))
435 # Someone else has the pidfile locked.
438 return int(file.readline())
440 logging.warning("%s: read: %s" % (pidfile, e.strerror))
443 logging.warning("%s does not contain a pid" % pidfile)
451 def read_pidfile(pidfile):
452 """Opens and reads a PID from 'pidfile'. Returns the positive PID if
453 successful, otherwise a negative errno value."""
454 return __read_pidfile(pidfile, False)
456 def _check_already_running():
457 pid = __read_pidfile(_pidfile, True)
459 _fatal("%s: already running as pid %d, aborting" % (_pidfile, pid))
461 _fatal("%s: pidfile check failed (%s), aborting"
462 % (_pidfile, os.strerror(pid)))
464 # XXX Python's getopt does not support options with optional arguments, so we
465 # have to separate --pidfile (with no argument) from --pidfile-name (with an
466 # argument). Need to write our own getopt I guess.
467 LONG_OPTIONS = ["detach", "no-chdir", "pidfile", "pidfile-name=",
468 "overwrite-pidfile", "monitor"]
470 def parse_opt(option, arg):
471 if option == '--detach':
473 elif option == '--no-chdir':
475 elif option == '--pidfile':
477 elif option == '--pidfile-name':
479 elif option == '--overwrite-pidfile':
480 ignore_existing_pidfile()
481 elif option == '--monitor':