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, die_if_already_running() will terminate the program with a
93 message if a locked pidfile already exists. If this function is called,
94 die_if_already_running() will merely log 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 _already_running():
115 """If a pidfile has been configured and that pidfile already exists and is
116 locked by a running process, returns True. Otherwise, returns False."""
117 if _pidfile is not None:
119 file = open(_pidfile, "r+")
122 fcntl.lockf(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
124 if e.errno in [errno.EACCES, errno.EAGAIN]:
126 logging.error("error locking %s (%s)"
127 % (_pidfile, os.strerror(e.errno)))
130 # This releases the lock, which we don't really want.
133 if e.errno == errno.ENOENT:
135 logging.error("error opening %s (%s)"
136 % (_pidfile, os.strerror(e.errno)))
139 def die_if_already_running():
140 """If a locked pidfile exists, issue a warning message and, unless
141 ignore_existing_pidfile() has been called, terminate the program."""
142 if _already_running():
143 if not _overwrite_pidfile:
144 msg = "%s: already running" % _pidfile
145 logging.error("%s, aborting" % msg)
146 sys.stderr.write("%s\n" % msg)
149 logging.warn("%s: %s already running"
150 % (get_pidfile(), ovs.util.PROGRAM_NAME))
153 """If a pidfile has been configured, creates it and stores the running
154 process's pid in it. Ensures that the pidfile will be deleted when the
156 if _pidfile is not None:
157 # Create pidfile via temporary file, so that observers never see an
158 # empty pidfile or an unlocked pidfile.
160 tmpfile = "%s.tmp%d" % (_pidfile, pid)
161 ovs.fatal_signal.add_file_to_unlink(tmpfile)
164 # This is global to keep Python from garbage-collecting and
165 # therefore closing our file after this function exits. That would
166 # unlock the lock for us, and we don't want that.
169 file = open(tmpfile, "w")
171 logging.error("%s: create failed: %s"
172 % (tmpfile, os.strerror(e.errno)))
176 fcntl.lockf(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
178 logging.error("%s: fcntl failed: %s"
179 % (tmpfile, os.strerror(e.errno)))
184 file.write("%s\n" % pid)
186 ovs.fatal_signal.add_file_to_unlink(_pidfile)
188 logging.error("%s: write failed: %s"
189 % (tmpfile, os.strerror(e.errno)))
194 os.rename(tmpfile, _pidfile)
196 ovs.fatal_signal.remove_file_to_unlink(_pidfile)
197 logging.error("failed to rename \"%s\" to \"%s\": %s"
198 % (tmpfile, _pidfile, os.strerror(e.errno)))
202 s = os.fstat(file.fileno())
203 _pidfile_dev = s.st_dev
204 _pidfile_ino = s.st_ino
207 """If configured with set_pidfile() or set_detach(), creates the pid file
208 and detaches from the foreground session."""
212 def _waitpid(pid, options):
215 return os.waitpid(pid, options)
217 if e.errno == errno.EINTR:
221 def _fork_and_wait_for_startup():
225 sys.stderr.write("pipe failed: %s\n" % os.strerror(e.errno))
231 sys.stderr.write("could not fork: %s\n" % os.strerror(e.errno))
235 # Running in parent process.
237 ovs.fatal_signal.fork()
243 retval, status = _waitpid(pid, 0)
244 if (retval == pid and
245 os.WIFEXITED(status) and os.WEXITSTATUS(status)):
246 # Child exited with an error. Convey the same error to
247 # our parent process as a courtesy.
248 sys.exit(os.WEXITSTATUS(status))
250 sys.stderr.write("fork child failed to signal startup\n")
255 # Running in parent process.
257 ovs.timeval.postfork()
258 #ovs.lockfile.postfork()
264 def _fork_notify_startup(fd):
266 error, bytes_written = ovs.socket_util.write_fully(fd, "0")
268 sys.stderr.write("could not write to pipe\n")
272 def _should_restart(status):
273 global RESTART_EXIT_CODE
275 if os.WIFEXITED(status) and os.WEXITSTATUS(status) == RESTART_EXIT_CODE:
278 if os.WIFSIGNALED(status):
279 for signame in ("SIGABRT", "SIGALRM", "SIGBUS", "SIGFPE", "SIGILL",
280 "SIGPIPE", "SIGSEGV", "SIGXCPU", "SIGXFSZ"):
281 if (signame in signal.__dict__ and
282 os.WTERMSIG(status) == signal.__dict__[signame]):
286 def _monitor_daemon(daemon_pid):
287 # XXX should log daemon's stderr output at startup time
288 # XXX should use setproctitle module if available
291 retval, status = _waitpid(daemon_pid, 0)
293 sys.stderr.write("waitpid failed\n")
295 elif retval == daemon_pid:
296 status_msg = ("pid %d died, %s"
297 % (daemon_pid, ovs.process.status_msg(status)))
299 if _should_restart(status):
300 if os.WCOREDUMP(status):
301 # Disable further core dumps to save disk space.
303 resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
304 except resource.error:
305 logging.warning("failed to disable core dumps")
307 # Throttle restarts to no more than once every 10 seconds.
308 if (last_restart is not None and
309 ovs.timeval.msec() < last_restart + 10000):
310 logging.warning("%s, waiting until 10 seconds since last "
311 "restart" % status_msg)
313 now = ovs.timeval.msec()
314 wakeup = last_restart + 10000
317 print "sleep %f" % ((wakeup - now) / 1000.0)
318 time.sleep((wakeup - now) / 1000.0)
319 last_restart = ovs.timeval.msec()
321 logging.error("%s, restarting" % status_msg)
322 daemon_pid = _fork_and_wait_for_startup()
326 logging.info("%s, exiting" % status_msg)
329 # Running in new daemon process.
331 def _close_standard_fds():
332 """Close stdin, stdout, stderr. If we're started from e.g. an SSH session,
333 then this keeps us from holding that session open artificially."""
334 null_fd = ovs.socket_util.get_null_fd()
340 def daemonize_start():
341 """If daemonization is configured, then starts daemonization, by forking
342 and returning in the child process. The parent process hangs around until
343 the child lets it know either that it completed startup successfully (by
344 calling daemon_complete()) or that it failed to start up (by exiting with a
345 nonzero exit code)."""
348 if _fork_and_wait_for_startup() > 0:
349 # Running in parent process.
351 # Running in daemon or monitor process.
354 saved_daemonize_fd = _daemonize_fd
355 daemon_pid = _fork_and_wait_for_startup()
357 # Running in monitor process.
358 _fork_notify_startup(saved_daemonize_fd)
359 _close_standard_fds()
360 _monitor_daemon(daemon_pid)
361 # Running in daemon process
365 def daemonize_complete():
366 """If daemonization is configured, then this function notifies the parent
367 process that the child process has completed startup successfully."""
368 _fork_notify_startup(_daemonize_fd)
374 _close_standard_fds()
379 --detach run in background as daemon
380 --no-chdir do not chdir to '/'
381 --pidfile[=FILE] create pidfile (default: %s/%s.pid)
382 --overwrite-pidfile with --pidfile, start even if already running
383 """ % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME))
385 def read_pidfile(pidfile):
386 """Opens and reads a PID from 'pidfile'. Returns the nonnegative PID if
387 successful, otherwise a negative errno value."""
388 if _pidfile_dev is not None:
391 if s.st_ino == _pidfile_ino and s.st_dev == _pidfile_dev:
392 # It's our own pidfile. We can't afford to open it,
393 # because closing *any* fd for a file that a process
394 # has locked also releases all the locks on that file.
396 # Fortunately, we know the associated pid anyhow.
402 file = open(pidfile, "r")
404 logging.warning("%s: open: %s" % (pidfile, os.strerror(e.errno)))
407 # Python fcntl doesn't directly support F_GETLK so we have to just try
408 # to lock it. If we get a conflicting lock that's "success"; otherwise
409 # the file is not locked.
411 fcntl.lockf(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
412 # File isn't locked if we get here, so treat that as an error.
413 logging.warning("%s: pid file is not locked" % pidfile)
415 # As a side effect, this drops the lock.
421 if e.errno not in [errno.EACCES, errno.EAGAIN]:
422 logging.warn("%s: fcntl: %s" % (pidfile, os.strerror(e.errno)))
427 return int(file.readline())
429 logging.warning("%s: read: %s" % (pidfile, e.strerror))
432 logging.warning("%s does not contain a pid" % pidfile)
440 # XXX Python's getopt does not support options with optional arguments, so we
441 # have to separate --pidfile (with no argument) from --pidfile-name (with an
442 # argument). Need to write our own getopt I guess.
443 LONG_OPTIONS = ["detach", "no-chdir", "pidfile", "pidfile-name=",
444 "overwrite-pidfile", "monitor"]
446 def parse_opt(option, arg):
447 if option == '--detach':
449 elif option == '--no-chdir':
451 elif option == '--pidfile':
453 elif option == '--pidfile-name':
455 elif option == '--overwrite-pidfile':
456 ignore_existing_pidfile()
457 elif option == '--monitor':