1 # Copyright (c) 2010 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().
55 def make_pidfile_name(name):
56 """Returns the file name that would be used for a pidfile if 'name' were
57 provided to set_pidfile()."""
58 if name is None or name == "":
59 return "%s/%s.pid" % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME)
61 return ovs.util.abs_file_name(ovs.dirs.RUNDIR, name)
63 def set_pidfile(name):
64 """Sets up a following call to daemonize() to create a pidfile named
65 'name'. If 'name' begins with '/', then it is treated as an absolute path.
66 Otherwise, it is taken relative to ovs.util.RUNDIR, which is
67 $(prefix)/var/run by default.
69 If 'name' is null, then ovs.util.PROGRAM_NAME followed by ".pid" is
72 _pidfile = make_pidfile_name(name)
75 """Returns an absolute path to the configured pidfile, or None if no
76 pidfile is configured. The caller must not modify or free the returned
81 """Sets that we do not chdir to "/"."""
85 def is_chdir_enabled():
86 """Will we chdir to "/" as part of daemonizing?"""
89 def ignore_existing_pidfile():
90 """Normally, die_if_already_running() will terminate the program with a
91 message if a locked pidfile already exists. If this function is called,
92 die_if_already_running() will merely log a warning."""
93 global _overwrite_pidfile
94 _overwrite_pidfile = True
97 """Sets up a following call to daemonize() to detach from the foreground
98 session, running this process in the background."""
103 """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."""
112 def _already_running():
113 """If a pidfile has been configured and that pidfile already exists and is
114 locked by a running process, returns True. Otherwise, returns False."""
115 if _pidfile is not None:
117 file = open(_pidfile, "r+")
120 fcntl.lockf(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
122 if e.errno in [errno.EACCES, errno.EAGAIN]:
124 logging.error("error locking %s (%s)"
125 % (_pidfile, os.strerror(e.errno)))
128 # This releases the lock, which we don't really want.
131 if e.errno == errno.ENOENT:
133 logging.error("error opening %s (%s)"
134 % (_pidfile, os.strerror(e.errno)))
137 def die_if_already_running():
138 """If a locked pidfile exists, issue a warning message and, unless
139 ignore_existing_pidfile() has been called, terminate the program."""
140 if _already_running():
141 if not _overwrite_pidfile:
142 sys.stderr.write("%s: already running\n" % get_pidfile())
145 logging.warn("%s: %s already running"
146 % (get_pidfile(), ovs.util.PROGRAM_NAME))
149 """If a pidfile has been configured, creates it and stores the running
150 process's pid in it. Ensures that the pidfile will be deleted when the
152 if _pidfile is not None:
153 # Create pidfile via temporary file, so that observers never see an
154 # empty pidfile or an unlocked pidfile.
156 tmpfile = "%s.tmp%d" % (_pidfile, pid)
157 ovs.fatal_signal.add_file_to_unlink(tmpfile)
160 # This is global to keep Python from garbage-collecting and
161 # therefore closing our file after this function exits. That would
162 # unlock the lock for us, and we don't want that.
165 file = open(tmpfile, "w")
167 logging.error("%s: create failed: %s"
168 % (tmpfile, os.strerror(e.errno)))
172 fcntl.lockf(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
174 logging.error("%s: fcntl failed: %s"
175 % (tmpfile, os.strerror(e.errno)))
180 file.write("%s\n" % pid)
182 ovs.fatal_signal.add_file_to_unlink(_pidfile)
184 logging.error("%s: write failed: %s"
185 % (tmpfile, os.strerror(e.errno)))
190 os.rename(tmpfile, _pidfile)
192 ovs.fatal_signal.remove_file_to_unlink(_pidfile)
193 logging.error("failed to rename \"%s\" to \"%s\": %s"
194 % (tmpfile, _pidfile, os.strerror(e.errno)))
198 s = os.fstat(file.fileno())
199 _pidfile_dev = s.st_dev
200 _pidfile_ino = s.st_ino
203 """If configured with set_pidfile() or set_detach(), creates the pid file
204 and detaches from the foreground session."""
208 def _waitpid(pid, options):
211 return os.waitpid(pid, options)
213 if e.errno == errno.EINTR:
217 def _fork_and_wait_for_startup():
221 sys.stderr.write("pipe failed: %s\n" % os.strerror(e.errno))
227 sys.stderr.write("could not fork: %s\n" % os.strerror(e.errno))
231 # Running in parent process.
233 ovs.fatal_signal.fork()
239 retval, status = _waitpid(pid, 0)
240 if (retval == pid and
241 os.WIFEXITED(status) and os.WEXITSTATUS(status)):
242 # Child exited with an error. Convey the same error to
243 # our parent process as a courtesy.
244 sys.exit(os.WEXITSTATUS(status))
246 sys.stderr.write("fork child failed to signal startup\n")
251 # Running in parent process.
253 ovs.timeval.postfork()
254 #ovs.lockfile.postfork()
260 def _fork_notify_startup(fd):
262 error, bytes_written = ovs.socket_util.write_fully(fd, "0")
264 sys.stderr.write("could not write to pipe\n")
268 def _should_restart(status):
269 if os.WIFSIGNALED(status):
270 for signame in ("SIGABRT", "SIGALRM", "SIGBUS", "SIGFPE", "SIGILL",
271 "SIGPIPE", "SIGSEGV", "SIGXCPU", "SIGXFSZ"):
272 if (signame in signal.__dict__ and
273 os.WTERMSIG(status) == signal.__dict__[signame]):
277 def _monitor_daemon(daemon_pid):
278 # XXX should log daemon's stderr output at startup time
279 # XXX should use setproctitle module if available
282 retval, status = _waitpid(daemon_pid, 0)
284 sys.stderr.write("waitpid failed\n")
286 elif retval == daemon_pid:
287 status_msg = ("pid %d died, %s"
288 % (daemon_pid, ovs.process.status_msg(status)))
290 if _should_restart(status):
291 if os.WCOREDUMP(status):
292 # Disable further core dumps to save disk space.
294 resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
295 except resource.error:
296 logging.warning("failed to disable core dumps")
298 # Throttle restarts to no more than once every 10 seconds.
299 if (last_restart is not None and
300 ovs.timeval.msec() < last_restart + 10000):
301 logging.warning("%s, waiting until 10 seconds since last "
302 "restart" % status_msg)
304 now = ovs.timeval.msec()
305 wakeup = last_restart + 10000
308 print "sleep %f" % ((wakeup - now) / 1000.0)
309 time.sleep((wakeup - now) / 1000.0)
310 last_restart = ovs.timeval.msec()
312 logging.error("%s, restarting" % status_msg)
313 daemon_pid = _fork_and_wait_for_startup()
317 logging.info("%s, exiting" % status_msg)
320 # Running in new daemon process.
322 def _close_standard_fds():
323 """Close stdin, stdout, stderr. If we're started from e.g. an SSH session,
324 then this keeps us from holding that session open artificially."""
325 null_fd = ovs.socket_util.get_null_fd()
331 def daemonize_start():
332 """If daemonization is configured, then starts daemonization, by forking
333 and returning in the child process. The parent process hangs around until
334 the child lets it know either that it completed startup successfully (by
335 calling daemon_complete()) or that it failed to start up (by exiting with a
336 nonzero exit code)."""
339 if _fork_and_wait_for_startup() > 0:
340 # Running in parent process.
342 # Running in daemon or monitor process.
345 saved_daemonize_fd = _daemonize_fd
346 daemon_pid = _fork_and_wait_for_startup()
348 # Running in monitor process.
349 _fork_notify_startup(saved_daemonize_fd)
350 _close_standard_fds()
351 _monitor_daemon(daemon_pid)
352 # Running in daemon process
356 def daemonize_complete():
357 """If daemonization is configured, then this function notifies the parent
358 process that the child process has completed startup successfully."""
359 _fork_notify_startup(_daemonize_fd)
365 _close_standard_fds()
370 --detach run in background as daemon
371 --no-chdir do not chdir to '/'
372 --pidfile[=FILE] create pidfile (default: %s/%s.pid)
373 --overwrite-pidfile with --pidfile, start even if already running
374 """ % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME))
376 def read_pidfile(pidfile):
377 """Opens and reads a PID from 'pidfile'. Returns the nonnegative PID if
378 successful, otherwise a negative errno value."""
379 if _pidfile_dev is not None:
382 if s.st_ino == _pidfile_ino and s.st_dev == _pidfile_dev:
383 # It's our own pidfile. We can't afford to open it,
384 # because closing *any* fd for a file that a process
385 # has locked also releases all the locks on that file.
387 # Fortunately, we know the associated pid anyhow.
393 file = open(pidfile, "r")
395 logging.warning("%s: open: %s" % (pidfile, os.strerror(e.errno)))
398 # Python fcntl doesn't directly support F_GETLK so we have to just try
399 # to lock it. If we get a conflicting lock that's "success"; otherwise
400 # the file is not locked.
402 fcntl.lockf(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
403 # File isn't locked if we get here, so treat that as an error.
404 logging.warning("%s: pid file is not locked" % pidfile)
406 # As a side effect, this drops the lock.
412 if e.errno not in [errno.EACCES, errno.EAGAIN]:
413 logging.warn("%s: fcntl: %s" % (pidfile, os.strerror(e.errno)))
418 return int(file.readline())
420 logging.warning("%s: read: %s" % (pidfile, e.strerror))
423 logging.warning("%s does not contain a pid" % pidfile)
431 # XXX Python's getopt does not support options with optional arguments, so we
432 # have to separate --pidfile (with no argument) from --pidfile-name (with an
433 # argument). Need to write our own getopt I guess.
434 LONG_OPTIONS = ["detach", "no-chdir", "pidfile", "pidfile-name=",
435 "overwrite-pidfile", "monitor"]
437 def parse_opt(option, arg):
438 if option == '--detach':
440 elif option == '--no-chdir':
442 elif option == '--pidfile':
444 elif option == '--pidfile-name':
446 elif option == '--overwrite-pidfile':
447 ignore_existing_pidfile()
448 elif option == '--monitor':