-/* Unregisters pidfile from being unlinked when the program terminates via
-* exit() or a fatal signal. */
-void
-remove_pidfile_from_unlink(void)
-{
- if (pidfile) {
- fatal_signal_remove_file_to_unlink(pidfile);
- }
-}
-
-/* Registers pidfile to be unlinked when the program terminates via exit() or a
- * fatal signal. */
-void
-add_pidfile_to_unlink(void)
-{
- if (pidfile) {
- fatal_signal_add_file_to_unlink(pidfile);
- }
-}
-
-/* If a pidfile has been configured, creates it and stores the running
- * process's pid in it. Ensures that the pidfile will be deleted when the
- * process exits. */
-static void
-make_pidfile(void)
-{
- long int pid = getpid();
- struct stat s;
- char *tmpfile;
- FILE *file;
- int error;
-
- /* Create a temporary pidfile. */
- if (overwrite_pidfile) {
- tmpfile = xasprintf("%s.tmp%ld", pidfile, pid);
- fatal_signal_add_file_to_unlink(tmpfile);
- } else {
- /* Everyone shares the same file which will be treated as a lock. To
- * avoid some uncomfortable race conditions, we can't set up the fatal
- * signal unlink until we've acquired it. */
- tmpfile = xasprintf("%s.tmp", pidfile);
- }
-
- file = fopen(tmpfile, "a+");
- if (!file) {
- VLOG_FATAL("%s: create failed (%s)", tmpfile, ovs_strerror(errno));
- }
-
- error = lock_pidfile(file, F_SETLK);
- if (error) {
- /* Looks like we failed to acquire the lock. Note that, if we failed
- * for some other reason (and '!overwrite_pidfile'), we will have
- * left 'tmpfile' as garbage in the file system. */
- VLOG_FATAL("%s: fcntl(F_SETLK) failed (%s)", tmpfile,
- ovs_strerror(error));
- }
-
- if (!overwrite_pidfile) {
- /* We acquired the lock. Make sure to clean up on exit, and verify
- * that we're allowed to create the actual pidfile. */
- fatal_signal_add_file_to_unlink(tmpfile);
- check_already_running();
- }
-
- if (fstat(fileno(file), &s) == -1) {
- VLOG_FATAL("%s: fstat failed (%s)", tmpfile, ovs_strerror(errno));
- }
-
- if (ftruncate(fileno(file), 0) == -1) {
- VLOG_FATAL("%s: truncate failed (%s)", tmpfile, ovs_strerror(errno));
- }
-
- fprintf(file, "%ld\n", pid);
- if (fflush(file) == EOF) {
- VLOG_FATAL("%s: write failed (%s)", tmpfile, ovs_strerror(errno));
- }
-
- error = rename(tmpfile, pidfile);
-
- /* Due to a race, 'tmpfile' may be owned by a different process, so we
- * shouldn't delete it on exit. */
- fatal_signal_remove_file_to_unlink(tmpfile);
-
- if (error < 0) {
- VLOG_FATAL("failed to rename \"%s\" to \"%s\" (%s)",
- tmpfile, pidfile, ovs_strerror(errno));
- }
-
- /* Ensure that the pidfile will get deleted on exit. */
- fatal_signal_add_file_to_unlink(pidfile);
-
- /* Clean up.
- *
- * We don't close 'file' because its file descriptor must remain open to
- * hold the lock. */
- pidfile_dev = s.st_dev;
- pidfile_ino = s.st_ino;
- free(tmpfile);
-}
-
-/* If configured with set_pidfile() or set_detach(), creates the pid file and
- * detaches from the foreground session. */
-void
-daemonize(void)
-{
- daemonize_start();
- daemonize_complete();
-}
-
-/* Calls fork() and on success returns its return value. On failure, logs an
- * error and exits unsuccessfully.
- *
- * Post-fork, but before returning, this function calls a few other functions
- * that are generally useful if the child isn't planning to exec a new
- * process. */
-pid_t
-fork_and_clean_up(void)
-{
- pid_t pid = xfork();
- if (pid > 0) {
- /* Running in parent process. */
- fatal_signal_fork();
- } else if (!pid) {
- /* Running in child process. */
- lockfile_postfork();
- }
- return pid;
-}
-
-/* Forks, then:
- *
- * - In the parent, waits for the child to signal that it has completed its
- * startup sequence. Then stores -1 in '*fdp' and returns the child's pid.
- *
- * - In the child, stores a fd in '*fdp' and returns 0. The caller should
- * pass the fd to fork_notify_startup() after it finishes its startup
- * sequence.
- *
- * If something goes wrong with the fork, logs a critical error and aborts the
- * process. */
-static pid_t
-fork_and_wait_for_startup(int *fdp)
-{
- int fds[2];
- pid_t pid;
-
- xpipe(fds);
-
- pid = fork_and_clean_up();
- if (pid > 0) {
- /* Running in parent process. */
- size_t bytes_read;
- char c;
-
- close(fds[1]);
- if (read_fully(fds[0], &c, 1, &bytes_read) != 0) {
- int retval;
- int status;
-
- do {
- retval = waitpid(pid, &status, 0);
- } while (retval == -1 && errno == EINTR);
-
- if (retval == pid) {
- if (WIFEXITED(status) && WEXITSTATUS(status)) {
- /* Child exited with an error. Convey the same error
- * to our parent process as a courtesy. */
- exit(WEXITSTATUS(status));
- } else {
- char *status_msg = process_status_msg(status);
- VLOG_FATAL("fork child died before signaling startup (%s)",
- status_msg);
- }
- } else if (retval < 0) {
- VLOG_FATAL("waitpid failed (%s)", ovs_strerror(errno));
- } else {
- NOT_REACHED();
- }
- }
- close(fds[0]);
- *fdp = -1;
- } else if (!pid) {
- /* Running in child process. */
- close(fds[0]);
- *fdp = fds[1];
- }
-
- return pid;
-}
-
-static void
-fork_notify_startup(int fd)
-{
- if (fd != -1) {
- size_t bytes_written;
- int error;
-
- error = write_fully(fd, "", 1, &bytes_written);
- if (error) {
- VLOG_FATAL("pipe write failed (%s)", ovs_strerror(error));
- }
-
- close(fd);
- }
-}
-
-static bool
-should_restart(int status)
-{
- if (WIFSIGNALED(status)) {
- static const int error_signals[] = {
- SIGABRT, SIGALRM, SIGBUS, SIGFPE, SIGILL, SIGPIPE, SIGSEGV,
- SIGXCPU, SIGXFSZ
- };
-
- size_t i;
-
- for (i = 0; i < ARRAY_SIZE(error_signals); i++) {
- if (error_signals[i] == WTERMSIG(status)) {
- return true;
- }
- }
- }
- return false;
-}
-
-static void
-monitor_daemon(pid_t daemon_pid)
-{
- /* XXX Should log daemon's stderr output at startup time. */
- time_t last_restart;
- char *status_msg;
- int crashes;
-
- set_subprogram_name("monitor");
- status_msg = xstrdup("healthy");
- last_restart = TIME_MIN;
- crashes = 0;
- for (;;) {
- int retval;
- int status;
-
- proctitle_set("monitoring pid %lu (%s)",
- (unsigned long int) daemon_pid, status_msg);
-
- do {
- retval = waitpid(daemon_pid, &status, 0);
- } while (retval == -1 && errno == EINTR);
-
- if (retval == -1) {
- VLOG_FATAL("waitpid failed (%s)", ovs_strerror(errno));
- } else if (retval == daemon_pid) {
- char *s = process_status_msg(status);
- if (should_restart(status)) {
- free(status_msg);
- status_msg = xasprintf("%d crashes: pid %lu died, %s",
- ++crashes,
- (unsigned long int) daemon_pid, s);
- free(s);
-
- if (WCOREDUMP(status)) {
- /* Disable further core dumps to save disk space. */
- struct rlimit r;
-
- r.rlim_cur = 0;
- r.rlim_max = 0;
- if (setrlimit(RLIMIT_CORE, &r) == -1) {
- VLOG_WARN("failed to disable core dumps: %s",
- ovs_strerror(errno));
- }
- }
-
- /* Throttle restarts to no more than once every 10 seconds. */
- if (time(NULL) < last_restart + 10) {
- VLOG_WARN("%s, waiting until 10 seconds since last "
- "restart", status_msg);
- for (;;) {
- time_t now = time(NULL);
- time_t wakeup = last_restart + 10;
- if (now >= wakeup) {
- break;
- }
- sleep(wakeup - now);
- }
- }
- last_restart = time(NULL);
-
- VLOG_ERR("%s, restarting", status_msg);
- daemon_pid = fork_and_wait_for_startup(&daemonize_fd);
- if (!daemon_pid) {
- break;
- }
- } else {
- VLOG_INFO("pid %lu died, %s, exiting",
- (unsigned long int) daemon_pid, s);
- free(s);
- exit(0);
- }