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."""
114 def _die_if_already_running():
115 """If a locked pidfile exists, issue a warning message and, unless
116 ignore_existing_pidfile() has been called, terminate the program."""
119 pid = read_pidfile_if_exists(_pidfile)
121 if not _overwrite_pidfile:
122 msg = "%s: already running as pid %d" % (_pidfile, pid)
123 logging.error("%s, aborting" % msg)
124 sys.stderr.write("%s\n" % msg)
127 logging.warn("%s: %s already running"
128 % (get_pidfile(), ovs.util.PROGRAM_NAME))
131 """If a pidfile has been configured, creates it and stores the running
132 process's pid in it. Ensures that the pidfile will be deleted when the
134 if _pidfile is not None:
135 # Create pidfile via temporary file, so that observers never see an
136 # empty pidfile or an unlocked pidfile.
138 tmpfile = "%s.tmp%d" % (_pidfile, pid)
139 ovs.fatal_signal.add_file_to_unlink(tmpfile)
142 # This is global to keep Python from garbage-collecting and
143 # therefore closing our file after this function exits. That would
144 # unlock the lock for us, and we don't want that.
147 file = open(tmpfile, "w")
149 logging.error("%s: create failed: %s"
150 % (tmpfile, os.strerror(e.errno)))
154 fcntl.lockf(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
156 logging.error("%s: fcntl failed: %s"
157 % (tmpfile, os.strerror(e.errno)))
162 file.write("%s\n" % pid)
164 ovs.fatal_signal.add_file_to_unlink(_pidfile)
166 logging.error("%s: write failed: %s"
167 % (tmpfile, os.strerror(e.errno)))
172 os.rename(tmpfile, _pidfile)
174 ovs.fatal_signal.remove_file_to_unlink(_pidfile)
175 logging.error("failed to rename \"%s\" to \"%s\": %s"
176 % (tmpfile, _pidfile, os.strerror(e.errno)))
180 s = os.fstat(file.fileno())
181 _pidfile_dev = s.st_dev
182 _pidfile_ino = s.st_ino
185 """If configured with set_pidfile() or set_detach(), creates the pid file
186 and detaches from the foreground session."""
190 def _waitpid(pid, options):
193 return os.waitpid(pid, options)
195 if e.errno == errno.EINTR:
199 def _fork_and_wait_for_startup():
203 sys.stderr.write("pipe failed: %s\n" % os.strerror(e.errno))
209 sys.stderr.write("could not fork: %s\n" % os.strerror(e.errno))
213 # Running in parent process.
215 ovs.fatal_signal.fork()
223 if error != errno.EINTR:
226 retval, status = _waitpid(pid, 0)
227 if (retval == pid and
228 os.WIFEXITED(status) and os.WEXITSTATUS(status)):
229 # Child exited with an error. Convey the same error to
230 # our parent process as a courtesy.
231 sys.exit(os.WEXITSTATUS(status))
233 sys.stderr.write("fork child failed to signal startup\n")
238 # Running in parent process.
240 ovs.timeval.postfork()
241 #ovs.lockfile.postfork()
247 def _fork_notify_startup(fd):
249 error, bytes_written = ovs.socket_util.write_fully(fd, "0")
251 sys.stderr.write("could not write to pipe\n")
255 def _should_restart(status):
256 global RESTART_EXIT_CODE
258 if os.WIFEXITED(status) and os.WEXITSTATUS(status) == RESTART_EXIT_CODE:
261 if os.WIFSIGNALED(status):
262 for signame in ("SIGABRT", "SIGALRM", "SIGBUS", "SIGFPE", "SIGILL",
263 "SIGPIPE", "SIGSEGV", "SIGXCPU", "SIGXFSZ"):
264 if (signame in signal.__dict__ and
265 os.WTERMSIG(status) == signal.__dict__[signame]):
269 def _monitor_daemon(daemon_pid):
270 # XXX should log daemon's stderr output at startup time
271 # XXX should use setproctitle module if available
274 retval, status = _waitpid(daemon_pid, 0)
276 sys.stderr.write("waitpid failed\n")
278 elif retval == daemon_pid:
279 status_msg = ("pid %d died, %s"
280 % (daemon_pid, ovs.process.status_msg(status)))
282 if _should_restart(status):
283 if os.WCOREDUMP(status):
284 # Disable further core dumps to save disk space.
286 resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
287 except resource.error:
288 logging.warning("failed to disable core dumps")
290 # Throttle restarts to no more than once every 10 seconds.
291 if (last_restart is not None and
292 ovs.timeval.msec() < last_restart + 10000):
293 logging.warning("%s, waiting until 10 seconds since last "
294 "restart" % status_msg)
296 now = ovs.timeval.msec()
297 wakeup = last_restart + 10000
300 print "sleep %f" % ((wakeup - now) / 1000.0)
301 time.sleep((wakeup - now) / 1000.0)
302 last_restart = ovs.timeval.msec()
304 logging.error("%s, restarting" % status_msg)
305 daemon_pid = _fork_and_wait_for_startup()
309 logging.info("%s, exiting" % status_msg)
312 # Running in new daemon process.
314 def _close_standard_fds():
315 """Close stdin, stdout, stderr. If we're started from e.g. an SSH session,
316 then this keeps us from holding that session open artificially."""
317 null_fd = ovs.socket_util.get_null_fd()
323 def daemonize_start():
324 """If daemonization is configured, then starts daemonization, by forking
325 and returning in the child process. The parent process hangs around until
326 the child lets it know either that it completed startup successfully (by
327 calling daemon_complete()) or that it failed to start up (by exiting with a
328 nonzero exit code)."""
331 if _fork_and_wait_for_startup() > 0:
332 # Running in parent process.
334 # Running in daemon or monitor process.
337 saved_daemonize_fd = _daemonize_fd
338 daemon_pid = _fork_and_wait_for_startup()
340 # Running in monitor process.
341 _fork_notify_startup(saved_daemonize_fd)
342 _close_standard_fds()
343 _monitor_daemon(daemon_pid)
344 # Running in daemon process
346 _die_if_already_running()
349 def daemonize_complete():
350 """If daemonization is configured, then this function notifies the parent
351 process that the child process has completed startup successfully."""
352 _fork_notify_startup(_daemonize_fd)
358 _close_standard_fds()
363 --detach run in background as daemon
364 --no-chdir do not chdir to '/'
365 --pidfile[=FILE] create pidfile (default: %s/%s.pid)
366 --overwrite-pidfile with --pidfile, start even if already running
367 """ % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME))
369 def __read_pidfile(pidfile, must_exist):
370 if _pidfile_dev is not None:
373 if s.st_ino == _pidfile_ino and s.st_dev == _pidfile_dev:
374 # It's our own pidfile. We can't afford to open it,
375 # because closing *any* fd for a file that a process
376 # has locked also releases all the locks on that file.
378 # Fortunately, we know the associated pid anyhow.
384 file = open(pidfile, "r")
386 if e.errno == errno.ENOENT and not must_exist:
388 logging.warning("%s: open: %s" % (pidfile, os.strerror(e.errno)))
391 # Python fcntl doesn't directly support F_GETLK so we have to just try
392 # to lock it. If we get a conflicting lock that's "success"; otherwise
393 # the file is not locked.
395 fcntl.lockf(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
396 # File isn't locked if we get here, so treat that as an error.
397 logging.warning("%s: pid file is not locked" % pidfile)
399 # As a side effect, this drops the lock.
405 if e.errno not in [errno.EACCES, errno.EAGAIN]:
406 logging.warn("%s: fcntl: %s" % (pidfile, os.strerror(e.errno)))
411 return int(file.readline())
413 logging.warning("%s: read: %s" % (pidfile, e.strerror))
416 logging.warning("%s does not contain a pid" % pidfile)
424 def read_pidfile(pidfile):
425 """Opens and reads a PID from 'pidfile'. Returns the positive PID if
426 successful, otherwise a negative errno value."""
427 return __read_pidfile(pidfile, True)
429 def read_pidfile_if_exists(pidfile):
430 """Opens and reads a PID from 'pidfile'. Returns 0 if 'pidfile' does not
431 exist, the positive PID if successful, otherwise a negative errno value."""
432 return __read_pidfile(pidfile, False)
434 # XXX Python's getopt does not support options with optional arguments, so we
435 # have to separate --pidfile (with no argument) from --pidfile-name (with an
436 # argument). Need to write our own getopt I guess.
437 LONG_OPTIONS = ["detach", "no-chdir", "pidfile", "pidfile-name=",
438 "overwrite-pidfile", "monitor"]
440 def parse_opt(option, arg):
441 if option == '--detach':
443 elif option == '--no-chdir':
445 elif option == '--pidfile':
447 elif option == '--pidfile-name':
449 elif option == '--overwrite-pidfile':
450 ignore_existing_pidfile()
451 elif option == '--monitor':