X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fdaemon.c;h=2f6f08192016c03827047a14f4cc1c0e0b917e4e;hb=23e795f4dd9af5191773a5ee840642737a37e91d;hp=8629114637f0df62cb2631d82145ca6b8615fbf3;hpb=ff8decf1a318b4a611cb08bb3f12833044e8a872;p=openvswitch diff --git a/lib/daemon.c b/lib/daemon.c index 86291146..2f6f0819 100644 --- a/lib/daemon.c +++ b/lib/daemon.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010 Nicira Networks. + * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,10 +18,14 @@ #include "daemon.h" #include #include +#include #include #include +#include #include +#include #include +#include "command-line.h" #include "fatal-signal.h" #include "dirs.h" #include "lockfile.h" @@ -29,20 +33,25 @@ #include "socket-util.h" #include "timeval.h" #include "util.h" - -#define THIS_MODULE VLM_daemon #include "vlog.h" -/* Should we run in the background? */ +VLOG_DEFINE_THIS_MODULE(daemon); + +/* --detach: Should we run in the background? */ static bool detach; -/* Name of pidfile (null if none). */ +/* --pidfile: Name of pidfile (null if none). */ static char *pidfile; -/* Create pidfile even if one already exists and is locked? */ +/* Device and inode of pidfile, so we can avoid reopening it. */ +static dev_t pidfile_dev; +static ino_t pidfile_ino; + +/* --overwrite-pidfile: Create pidfile even if one already exists and is + locked? */ static bool overwrite_pidfile; -/* Should we chdir to "/"? */ +/* --no-chdir: Should we chdir to "/"? */ static bool chdir_ = true; /* File descriptor used by daemonize_start() and daemonize_complete(). */ @@ -55,11 +64,11 @@ static bool monitor; /* Returns the file name that would be used for a pidfile if 'name' were * provided to set_pidfile(). The caller must free the returned string. */ char * -make_pidfile_name(const char *name) +make_pidfile_name(const char *name) { - return (!name ? xasprintf("%s/%s.pid", ovs_rundir, program_name) - : *name == '/' ? xstrdup(name) - : xasprintf("%s/%s", ovs_rundir, name)); + return (!name + ? xasprintf("%s/%s.pid", ovs_rundir(), program_name) + : abs_file_name(ovs_rundir(), name)); } /* Sets up a following call to daemonize() to create a pidfile named 'name'. @@ -171,9 +180,9 @@ die_if_already_running(void) } } -/* If a pidfile has been configured, creates it and stores the running process' - * pid init. Ensures that the pidfile will be deleted when the process - * exits. */ +/* 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) { @@ -204,12 +213,21 @@ make_pidfile(void) close(fd); } else { /* Keep 'fd' open to retain the lock. */ + struct stat s; + + if (!fstat(fd, &s)) { + pidfile_dev = s.st_dev; + pidfile_ino = s.st_ino; + } else { + VLOG_ERR("%s: fstat failed: %s", + pidfile, strerror(errno)); + } } - free(text); } else { VLOG_ERR("%s: write failed: %s", tmpfile, strerror(errno)); close(fd); } + free(text); } else { VLOG_ERR("%s: fcntl failed: %s", tmpfile, strerror(errno)); close(fd); @@ -322,16 +340,25 @@ should_restart(int status) static void monitor_daemon(pid_t daemon_pid) { - /* XXX Should limit the rate at which we restart the daemon. */ /* XXX Should log daemon's stderr output at startup time. */ const char *saved_program_name; + time_t last_restart; + char *status_msg; + int crashes; saved_program_name = program_name; program_name = xasprintf("monitor(%s)", program_name); + status_msg = xstrdup("healthy"); + last_restart = TIME_MIN; + crashes = 0; for (;;) { int retval; int status; + proctitle_set("%s: monitoring pid %lu (%s)", + saved_program_name, (unsigned long int) daemon_pid, + status_msg); + do { retval = waitpid(daemon_pid, &status, 0); } while (retval == -1 && errno == EINTR); @@ -339,25 +366,58 @@ monitor_daemon(pid_t daemon_pid) if (retval == -1) { ovs_fatal(errno, "waitpid failed"); } else if (retval == daemon_pid) { - char *status_msg = process_status_msg(status); + char *s = process_status_msg(status); if (should_restart(status)) { - VLOG_ERR("%s daemon died unexpectedly (%s), restarting", - saved_program_name, status_msg); 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", + 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("%s daemon exited normally (%s), exiting", - saved_program_name, status_msg); + VLOG_INFO("pid %lu died, %s, exiting", + (unsigned long int) daemon_pid, s); + free(s); exit(0); } } } + free(status_msg); /* Running in new daemon process. */ + proctitle_restore(); free((char *) program_name); program_name = saved_program_name; } @@ -408,14 +468,21 @@ daemonize_start(void) } make_pidfile(); + + /* Make sure that the unixctl commands for vlog get registered in a + * daemon, even before the first log message. */ + vlog_init(); } /* If daemonization is configured, then this function notifies the parent - * process that the child process has completed startup successfully. */ + * process that the child process has completed startup successfully. + * + * Calling this function more than once has no additional effect. */ void daemonize_complete(void) { fork_notify_startup(daemonize_fd); + daemonize_fd = -1; if (detach) { setsid(); @@ -423,6 +490,7 @@ daemonize_complete(void) ignore(chdir("/")); } close_standard_fds(); + detach = false; } } @@ -436,7 +504,7 @@ daemon_usage(void) " --pidfile[=FILE] create pidfile (default: %s/%s.pid)\n" " --overwrite-pidfile with --pidfile, start even if already " "running\n", - ovs_rundir, program_name); + ovs_rundir(), program_name); } /* Opens and reads a PID from 'pidfile'. Returns the nonnegative PID if @@ -446,9 +514,21 @@ read_pidfile(const char *pidfile) { char line[128]; struct flock lck; + struct stat s; FILE *file; int error; + if ((pidfile_ino || pidfile_dev) + && !stat(pidfile, &s) + && s.st_ino == pidfile_ino && s.st_dev == pidfile_dev) { + /* It's our own pidfile. We can't afford to open it, because closing + * *any* fd for a file that a process has locked also releases all the + * locks on that file. + * + * Fortunately, we know the associated pid anyhow: */ + return getpid(); + } + file = fopen(pidfile, "r"); if (!file) { error = errno; @@ -460,6 +540,7 @@ read_pidfile(const char *pidfile) lck.l_whence = SEEK_SET; lck.l_start = 0; lck.l_len = 0; + lck.l_pid = 0; if (fcntl(fileno(file), F_GETLK, &lck)) { error = errno; VLOG_WARN("%s: fcntl: %s", pidfile, strerror(error));