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."""
82 """Sets that we do not chdir to "/"."""
86 def is_chdir_enabled():
87 """Will we chdir to "/" as part of daemonizing?"""
90 def ignore_existing_pidfile():
91 """Normally, daemonize() or daemonize_start() will terminate the program
92 with a message if a locked pidfile already exists. If this function is
93 called, an existing pidfile will be replaced, with a warning."""
94 global _overwrite_pidfile
95 _overwrite_pidfile = True
98 """Sets up a following call to daemonize() to detach from the foreground
99 session, running this process in the background."""
104 """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."""
115 sys.stderr.write("%s\n" % msg)
119 """If a pidfile has been configured, creates it and stores the running
120 process's pid in it. Ensures that the pidfile will be deleted when the
124 # Create a temporary pidfile.
125 tmpfile = "%s.tmp%d" % (_pidfile, pid)
126 ovs.fatal_signal.add_file_to_unlink(tmpfile)
128 # This is global to keep Python from garbage-collecting and
129 # therefore closing our file after this function exits. That would
130 # unlock the lock for us, and we don't want that.
133 file = open(tmpfile, "w")
135 _fatal("%s: create failed (%s)" % (tmpfile, e.strerror))
138 s = os.fstat(file.fileno())
140 _fatal("%s: fstat failed (%s)" % (tmpfile, e.strerror))
143 file.write("%s\n" % pid)
146 _fatal("%s: write failed: %s" % (tmpfile, e.strerror))
149 fcntl.lockf(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
151 _fatal("%s: fcntl failed: %s" % (tmpfile, e.strerror))
153 # Rename or link it to the correct name.
154 if _overwrite_pidfile:
156 os.rename(tmpfile, _pidfile)
158 _fatal("failed to rename \"%s\" to \"%s\" (%s)"
159 % (tmpfile, _pidfile, e.strerror))
163 os.link(tmpfile, _pidfile)
167 if error == errno.EEXIST:
168 _check_already_running()
169 elif error != errno.EINTR:
172 _fatal("failed to link \"%s\" as \"%s\" (%s)"
173 % (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)))
185 _pidfile_dev = s.st_dev
186 _pidfile_ino = s.st_ino
189 """If configured with set_pidfile() or set_detach(), creates the pid file
190 and detaches from the foreground session."""
194 def _waitpid(pid, options):
197 return os.waitpid(pid, options)
199 if e.errno == errno.EINTR:
203 def _fork_and_wait_for_startup():
207 sys.stderr.write("pipe failed: %s\n" % os.strerror(e.errno))
213 sys.stderr.write("could not fork: %s\n" % os.strerror(e.errno))
217 # Running in parent process.
219 ovs.fatal_signal.fork()
227 if error != errno.EINTR:
230 retval, status = _waitpid(pid, 0)
231 if (retval == pid and
232 os.WIFEXITED(status) and os.WEXITSTATUS(status)):
233 # Child exited with an error. Convey the same error to
234 # our parent process as a courtesy.
235 sys.exit(os.WEXITSTATUS(status))
237 sys.stderr.write("fork child failed to signal startup\n")
242 # Running in parent process.
244 ovs.timeval.postfork()
245 #ovs.lockfile.postfork()
251 def _fork_notify_startup(fd):
253 error, bytes_written = ovs.socket_util.write_fully(fd, "0")
255 sys.stderr.write("could not write to pipe\n")
259 def _should_restart(status):
260 global RESTART_EXIT_CODE
262 if os.WIFEXITED(status) and os.WEXITSTATUS(status) == RESTART_EXIT_CODE:
265 if os.WIFSIGNALED(status):
266 for signame in ("SIGABRT", "SIGALRM", "SIGBUS", "SIGFPE", "SIGILL",
267 "SIGPIPE", "SIGSEGV", "SIGXCPU", "SIGXFSZ"):
268 if os.WTERMSIG(status) == getattr(signal, signame, None):
272 def _monitor_daemon(daemon_pid):
273 # XXX should log daemon's stderr output at startup time
274 # XXX should use setproctitle module if available
277 retval, status = _waitpid(daemon_pid, 0)
279 sys.stderr.write("waitpid failed\n")
281 elif retval == daemon_pid:
282 status_msg = ("pid %d died, %s"
283 % (daemon_pid, ovs.process.status_msg(status)))
285 if _should_restart(status):
286 if os.WCOREDUMP(status):
287 # Disable further core dumps to save disk space.
289 resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
290 except resource.error:
291 logging.warning("failed to disable core dumps")
293 # Throttle restarts to no more than once every 10 seconds.
294 if (last_restart is not None and
295 ovs.timeval.msec() < last_restart + 10000):
296 logging.warning("%s, waiting until 10 seconds since last "
297 "restart" % status_msg)
299 now = ovs.timeval.msec()
300 wakeup = last_restart + 10000
303 print "sleep %f" % ((wakeup - now) / 1000.0)
304 time.sleep((wakeup - now) / 1000.0)
305 last_restart = ovs.timeval.msec()
307 logging.error("%s, restarting" % status_msg)
308 daemon_pid = _fork_and_wait_for_startup()
312 logging.info("%s, exiting" % status_msg)
315 # Running in new daemon process.
317 def _close_standard_fds():
318 """Close stdin, stdout, stderr. If we're started from e.g. an SSH session,
319 then this keeps us from holding that session open artificially."""
320 null_fd = ovs.socket_util.get_null_fd()
326 def daemonize_start():
327 """If daemonization is configured, then starts daemonization, by forking
328 and returning in the child process. The parent process hangs around until
329 the child lets it know either that it completed startup successfully (by
330 calling daemon_complete()) or that it failed to start up (by exiting with a
331 nonzero exit code)."""
334 if _fork_and_wait_for_startup() > 0:
335 # Running in parent process.
337 # Running in daemon or monitor process.
340 saved_daemonize_fd = _daemonize_fd
341 daemon_pid = _fork_and_wait_for_startup()
343 # Running in monitor process.
344 _fork_notify_startup(saved_daemonize_fd)
345 _close_standard_fds()
346 _monitor_daemon(daemon_pid)
347 # Running in daemon process
352 def daemonize_complete():
353 """If daemonization is configured, then this function notifies the parent
354 process that the child process has completed startup successfully."""
355 _fork_notify_startup(_daemonize_fd)
361 _close_standard_fds()
366 --detach run in background as daemon
367 --no-chdir do not chdir to '/'
368 --pidfile[=FILE] create pidfile (default: %s/%s.pid)
369 --overwrite-pidfile with --pidfile, start even if already running
370 """ % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME))
372 def __read_pidfile(pidfile, delete_if_stale):
373 if _pidfile_dev is not None:
376 if s.st_ino == _pidfile_ino and s.st_dev == _pidfile_dev:
377 # It's our own pidfile. We can't afford to open it,
378 # because closing *any* fd for a file that a process
379 # has locked also releases all the locks on that file.
381 # Fortunately, we know the associated pid anyhow.
387 file = open(pidfile, "r+")
389 if e.errno == errno.ENOENT and delete_if_stale:
391 logging.warning("%s: open: %s" % (pidfile, e.strerror))
394 # Python fcntl doesn't directly support F_GETLK so we have to just try
397 fcntl.lockf(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
399 # pidfile exists but wasn't locked by anyone. Now we have the lock.
400 if not delete_if_stale:
402 logging.warning("%s: pid file is stale" % pidfile)
405 # Is the file we have locked still named 'pidfile'?
409 s2 = os.fstat(file.fileno())
410 if s.st_ino != s2.st_ino or s.st_dev != s2.st_dev:
415 logging.warning("%s: lost race to delete pidfile" % pidfile)
416 return -errno.ALREADY
418 # We won the right to delete the stale pidfile.
422 logging.warning("%s: failed to delete stale pidfile"
423 % (pidfile, e.strerror))
426 logging.debug("%s: deleted stale pidfile" % pidfile)
430 if e.errno not in [errno.EACCES, errno.EAGAIN]:
431 logging.warn("%s: fcntl: %s" % (pidfile, e.strerror))
434 # Someone else has the pidfile locked.
437 return int(file.readline())
439 logging.warning("%s: read: %s" % (pidfile, e.strerror))
442 logging.warning("%s does not contain a pid" % pidfile)
450 def read_pidfile(pidfile):
451 """Opens and reads a PID from 'pidfile'. Returns the positive PID if
452 successful, otherwise a negative errno value."""
453 return __read_pidfile(pidfile, False)
455 def _check_already_running():
456 pid = __read_pidfile(_pidfile, True)
458 _fatal("%s: already running as pid %d, aborting" % (_pidfile, pid))
460 _fatal("%s: pidfile check failed (%s), aborting"
461 % (_pidfile, os.strerror(pid)))
463 # XXX Python's getopt does not support options with optional arguments, so we
464 # have to separate --pidfile (with no argument) from --pidfile-name (with an
465 # argument). Need to write our own getopt I guess.
466 LONG_OPTIONS = ["detach", "no-chdir", "pidfile", "pidfile-name=",
467 "overwrite-pidfile", "monitor"]
469 def parse_opt(option, arg):
470 if option == '--detach':
472 elif option == '--no-chdir':
474 elif option == '--pidfile':
476 elif option == '--pidfile-name':
478 elif option == '--overwrite-pidfile':
479 ignore_existing_pidfile()
480 elif option == '--monitor':