X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fdaemon.c;h=4c7c8e16ae3f2f9b6760185e86dfa00258c5be56;hb=HEAD;hp=c4effa9b0b91c425326b29e365a94febe0631da7;hpb=95440284bdf8ac9a94c3e119d011d76acab577a7;p=sliver-openvswitch.git diff --git a/lib/daemon.c b/lib/daemon.c index c4effa9b0..4c7c8e16a 100644 --- a/lib/daemon.c +++ b/lib/daemon.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009 Nicira Networks. + * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,101 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include #include "daemon.h" +#include "daemon-private.h" #include #include -#include -#include -#include #include -#include "fatal-signal.h" -#include "dirs.h" -#include "lockfile.h" -#include "timeval.h" -#include "util.h" - -#define THIS_MODULE VLM_daemon #include "vlog.h" -/* Should we run in the background? */ -static bool detach; - -/* Name of pidfile (null if none). */ -static char *pidfile; - -/* Create pidfile even if one already exists and is locked? */ -static bool overwrite_pidfile; - -/* Should we chdir to "/"? */ -static bool chdir_ = true; - -/* File descriptors used by daemonize_start() and daemonize_complete(). */ -static int daemonize_fds[2]; - -/* 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) -{ - return (!name ? xasprintf("%s/%s.pid", ovs_rundir, program_name) - : *name == '/' ? xstrdup(name) - : xasprintf("%s/%s", ovs_rundir, name)); -} - -/* Sets up a following call to daemonize() to create a pidfile named 'name'. - * If 'name' begins with '/', then it is treated as an absolute path. - * Otherwise, it is taken relative to RUNDIR, which is $(prefix)/var/run by - * default. - * - * If 'name' is null, then program_name followed by ".pid" is used. */ -void -set_pidfile(const char *name) -{ - free(pidfile); - pidfile = make_pidfile_name(name); -} - -/* Returns an absolute path to the configured pidfile, or a null pointer if no - * pidfile is configured. The caller must not modify or free the returned - * string. */ -const char * -get_pidfile(void) -{ - return pidfile; -} - -/* Sets that we do not chdir to "/". */ -void -set_no_chdir(void) -{ - chdir_ = false; -} - -/* Will we chdir to "/" as part of daemonizing? */ -bool -is_chdir_enabled(void) -{ - return chdir_; -} +VLOG_DEFINE_THIS_MODULE(daemon); -/* Normally, die_if_already_running() will terminate the program with a message - * if a locked pidfile already exists. If this function is called, - * die_if_already_running() will merely log a warning. */ -void -ignore_existing_pidfile(void) -{ - overwrite_pidfile = true; -} - -/* Sets up a following call to daemonize() to detach from the foreground - * session, running this process in the background. */ -void -set_detach(void) -{ - detach = true; -} +/* For each of the standard file descriptors, whether to replace it by + * /dev/null (if false) or keep it for the daemon to use (if true). */ +static bool save_fds[3]; /* Will daemonize() really detach? */ bool @@ -116,100 +34,6 @@ get_detach(void) return detach; } -/* If a pidfile has been configured and that pidfile already exists and is - * locked by a running process, returns the pid of the running process. - * Otherwise, returns 0. */ -static pid_t -already_running(void) -{ - pid_t pid = 0; - if (pidfile) { - int fd = open(pidfile, O_RDWR); - if (fd >= 0) { - struct flock lck; - lck.l_type = F_WRLCK; - lck.l_whence = SEEK_SET; - lck.l_start = 0; - lck.l_len = 0; - if (fcntl(fd, F_GETLK, &lck) != -1 && lck.l_type != F_UNLCK) { - pid = lck.l_pid; - } - close(fd); - } - } - return pid; -} - -/* If a locked pidfile exists, issue a warning message and, unless - * ignore_existing_pidfile() has been called, terminate the program. */ -void -die_if_already_running(void) -{ - pid_t pid = already_running(); - if (pid) { - if (!overwrite_pidfile) { - ovs_fatal(0, "%s: already running as pid %ld", - get_pidfile(), (long int) pid); - } else { - VLOG_WARN("%s: %s already running as pid %ld", - get_pidfile(), program_name, (long int) pid); - } - } -} - -/* 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. */ -static void -make_pidfile(void) -{ - if (pidfile) { - /* Create pidfile via temporary file, so that observers never see an - * empty pidfile or an unlocked pidfile. */ - long int pid = getpid(); - char *tmpfile; - int fd; - - tmpfile = xasprintf("%s.tmp%ld", pidfile, pid); - fatal_signal_add_file_to_unlink(tmpfile); - fd = open(tmpfile, O_CREAT | O_WRONLY | O_TRUNC, 0666); - if (fd >= 0) { - struct flock lck; - lck.l_type = F_WRLCK; - lck.l_whence = SEEK_SET; - lck.l_start = 0; - lck.l_len = 0; - if (fcntl(fd, F_SETLK, &lck) != -1) { - char *text = xasprintf("%ld\n", pid); - if (write(fd, text, strlen(text)) == strlen(text)) { - fatal_signal_add_file_to_unlink(pidfile); - if (rename(tmpfile, pidfile) < 0) { - VLOG_ERR("failed to rename \"%s\" to \"%s\": %s", - tmpfile, pidfile, strerror(errno)); - fatal_signal_remove_file_to_unlink(pidfile); - close(fd); - } else { - /* Keep 'fd' open to retain the lock. */ - } - free(text); - } else { - VLOG_ERR("%s: write failed: %s", tmpfile, strerror(errno)); - close(fd); - } - } else { - VLOG_ERR("%s: fcntl failed: %s", tmpfile, strerror(errno)); - close(fd); - } - } else { - VLOG_ERR("%s: create failed: %s", tmpfile, strerror(errno)); - } - fatal_signal_remove_file_to_unlink(tmpfile); - free(tmpfile); - } - free(pidfile); - pidfile = NULL; -} - /* If configured with set_pidfile() or set_detach(), creates the pid file and * detaches from the foreground session. */ void @@ -219,147 +43,78 @@ daemonize(void) daemonize_complete(); } -/* If daemonization is configured, then starts daemonization, by forking and - * returning in the child process. The parent process hangs around until the - * child lets it know either that it completed startup successfully (by calling - * daemon_complete()) or that it failed to start up (by exiting with a nonzero - * exit code). */ +/* Sets up a following call to daemonize() to create a pidfile named 'name'. + * If 'name' begins with '/' (or contains ':' in windows), then it is treated + * as an absolute path. Otherwise, it is taken relative to RUNDIR, + * which is $(prefix)/var/run by default. + * + * If 'name' is null, then program_name followed by ".pid" is used. */ void -daemonize_start(void) +set_pidfile(const char *name) { - if (detach) { - pid_t pid; - - if (pipe(daemonize_fds) < 0) { - ovs_fatal(errno, "pipe failed"); - } - - pid = fork(); - if (pid > 0) { - /* Running in parent process. */ - char c; - - close(daemonize_fds[1]); - fatal_signal_fork(); - if (read(daemonize_fds[0], &c, 1) != 1) { - int retval; - int status; - - do { - retval = waitpid(pid, &status, 0); - } while (retval == -1 && errno == EINTR); - - if (retval == pid - && WIFEXITED(status) - && WEXITSTATUS(status)) { - /* Child exited with an error. Convey the same error to - * our parent process as a courtesy. */ - exit(WEXITSTATUS(status)); - } - - ovs_fatal(errno, "daemon child failed to signal startup"); - } - exit(0); - } else if (!pid) { - /* Running in child process. */ - close(daemonize_fds[0]); - make_pidfile(); - time_postfork(); - lockfile_postfork(); - } else { - ovs_fatal(errno, "could not fork"); - } - } else { - make_pidfile(); - } + assert_single_threaded(); + free(pidfile); + pidfile = make_pidfile_name(name); } -/* If daemonization is configured, then this function notifies the parent - * process that the child process has completed startup successfully. */ +/* A daemon doesn't normally have any use for the file descriptors for stdin, + * stdout, and stderr after it detaches. To keep these file descriptors from + * e.g. holding an SSH session open, by default detaching replaces each of + * these file descriptors by /dev/null. But a few daemons expect the user to + * redirect stdout or stderr to a file, in which case it is desirable to keep + * these file descriptors. This function, therefore, disables replacing 'fd' + * by /dev/null when the daemon detaches. */ void -daemonize_complete(void) -{ - if (detach) { - char c = 0; - - ignore(write(daemonize_fds[1], &c, 1)); - close(daemonize_fds[1]); - setsid(); - if (chdir_) { - ignore(chdir("/")); +daemon_save_fd(int fd) +{ + ovs_assert(fd == STDIN_FILENO || + fd == STDOUT_FILENO || + fd == STDERR_FILENO); + save_fds[fd] = true; +} + +/* Returns a readable and writable fd for /dev/null, if successful, otherwise + * a negative errno value. The caller must not close the returned fd (because + * the same fd will be handed out to subsequent callers). */ +static int +get_null_fd(void) +{ + static int null_fd; +#ifndef _WIN32 + char *device = "/dev/null"; +#else + char *device = "nul"; +#endif + + if (!null_fd) { + null_fd = open(device, O_RDWR); + if (null_fd < 0) { + int error = errno; + VLOG_ERR("could not open %s: %s", device, ovs_strerror(error)); + null_fd = -error; } } -} -void -daemon_usage(void) -{ - printf( - "\nDaemon options:\n" - " --detach run in background as daemon\n" - " --no-chdir do not chdir to '/'\n" - " --pidfile[=FILE] create pidfile (default: %s/%s.pid)\n" - " --overwrite-pidfile with --pidfile, start even if already " - "running\n", - ovs_rundir, program_name); + return null_fd; } -/* Opens and reads a PID from 'pidfile'. Returns the nonnegative PID if - * successful, otherwise a negative errno value. */ -pid_t -read_pidfile(const char *pidfile) +/* Close standard file descriptors (except any that the client has requested we + * leave open by calling daemon_save_fd()). If we're started from e.g. an SSH + * session, then this keeps us from holding that session open artificially. */ +void +close_standard_fds(void) { - char line[128]; - struct flock lck; - FILE *file; - int error; - - file = fopen(pidfile, "r"); - if (!file) { - error = errno; - VLOG_WARN("%s: open: %s", pidfile, strerror(error)); - goto error; - } - - lck.l_type = F_WRLCK; - lck.l_whence = SEEK_SET; - lck.l_start = 0; - lck.l_len = 0; - if (fcntl(fileno(file), F_GETLK, &lck)) { - error = errno; - VLOG_WARN("%s: fcntl: %s", pidfile, strerror(error)); - goto error; - } - if (lck.l_type == F_UNLCK) { - error = ESRCH; - VLOG_WARN("%s: pid file is not locked", pidfile); - goto error; - } + int null_fd = get_null_fd(); + if (null_fd >= 0) { + int fd; - if (!fgets(line, sizeof line, file)) { - if (ferror(file)) { - error = errno; - VLOG_WARN("%s: read: %s", pidfile, strerror(error)); - } else { - error = ESRCH; - VLOG_WARN("%s: read: unexpected end of file", pidfile); + for (fd = 0; fd < 3; fd++) { + if (!save_fds[fd]) { + dup2(null_fd, fd); + } } - goto error; - } - - if (lck.l_pid != strtoul(line, NULL, 10)) { - error = ESRCH; - VLOG_WARN("l_pid (%ld) != %s pid (%s)", - (long int) lck.l_pid, pidfile, line); - goto error; } - fclose(file); - return lck.l_pid; - -error: - if (file) { - fclose(file); - } - return -error; + /* Disable logging to stderr to avoid wasting CPU time. */ + vlog_set_levels(NULL, VLF_CONSOLE, VLL_OFF); }