X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=lib%2Fdaemon.c;h=b8313d4488013c303572f92adc5b4a18f60497e1;hb=ca7e7bee86b4ee821d61b58bf15c89a9d8a3cb30;hp=9895f7251f057189c25654f405b033d612d2366c;hpb=7943cd51e7eba1442846a853b6ed62e89a05d110;p=cascardo%2Fovs.git diff --git a/lib/daemon.c b/lib/daemon.c index 9895f7251..b8313d448 100644 --- a/lib/daemon.c +++ b/lib/daemon.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010 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,102 +13,21 @@ * 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 "socket-util.h" -#include "timeval.h" #include "util.h" +#include "ovs-thread.h" +#include "openvswitch/vlog.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 descriptor used by daemonize_start() and daemonize_complete(). */ -static int daemonize_fd = -1; - -/* 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_; -} - -/* 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; -} +VLOG_DEFINE_THIS_MODULE(daemon); -/* 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 @@ -117,294 +36,87 @@ 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 daemonize(void) { - daemonize_start(); + daemonize_start(false); daemonize_complete(); } -static pid_t -fork_and_wait_for_startup(int *fdp) -{ - int fds[2]; - pid_t pid; - - if (pipe(fds) < 0) { - ovs_fatal(errno, "pipe failed"); - } - - pid = fork(); - if (pid > 0) { - /* Running in parent process. */ - char c; - - close(fds[1]); - fatal_signal_fork(); - if (read(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, "fork child failed to signal startup"); - } - close(fds[0]); - *fdp = -1; - } else if (!pid) { - /* Running in child process. */ - close(fds[0]); - time_postfork(); - lockfile_postfork(); - *fdp = fds[1]; - } else { - ovs_fatal(errno, "could not fork"); - } - - 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) { - ovs_fatal(error, "could not write to pipe"); - } - - close(fd); - } -} - -/* Close stdin, stdout, stderr. If we're started from e.g. an SSH session, - * then this keeps us from holding that session open artificially. */ -static void -close_standard_fds(void) -{ - int null_fd = get_null_fd(); - if (null_fd >= 0) { - dup2(null_fd, STDIN_FILENO); - dup2(null_fd, STDOUT_FILENO); - dup2(null_fd, STDERR_FILENO); - } -} - -/* 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) { - daemonize_fd = -1; - - if (detach) { - if (fork_and_wait_for_startup(&daemonize_fd) > 0) { - /* Running in parent process. */ - exit(0); - } - /* Running in daemon process. */ - } - - 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) -{ - fork_notify_startup(daemonize_fd); - - if (detach) { - 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; } - close_standard_fds(); } -} -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); }