/* Return the maximun suggested buffer size for both getpwname_r() * and getgrnam_r(). * * This size may still not be big enough. in case getpwname_r() * and friends return ERANGE, a larger buffer should be supplied to * retry. (The man page did not specify the max size to stop at, we * will keep trying with doubling the buffer size for each round until * the size wrapps around size_t. */ static size_t get_sysconf_buffer_size(void) { size_t bufsize, pwd_bs = 0, grp_bs = 0; const size_t default_bufsize = 1024; errno = 0; if ((pwd_bs = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) { if (errno) { VLOG_FATAL("%s: Read initial passwordd struct size " "failed (%s), aborting. ", pidfile, ovs_strerror(errno)); } } if ((grp_bs = sysconf(_SC_GETGR_R_SIZE_MAX)) == -1) { if (errno) { VLOG_FATAL("%s: Read initial group struct size " "failed (%s), aborting. ", pidfile, ovs_strerror(errno)); } } bufsize = MAX(pwd_bs, grp_bs); return bufsize ? bufsize : default_bufsize; }
/* 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) { int error; error = GetFileAttributes(pidfile); if (error != INVALID_FILE_ATTRIBUTES) { /* pidfile exists. Try to unlink() it. */ error = unlink(pidfile); if (error) { VLOG_FATAL("Failed to delete existing pidfile %s (%s)", pidfile, ovs_strerror(errno)); } } filep_pidfile = fopen(pidfile, "w"); if (filep_pidfile == NULL) { VLOG_FATAL("failed to open %s (%s)", pidfile, ovs_strerror(errno)); } fatal_signal_add_hook(unlink_pidfile, NULL, NULL, true); fprintf(filep_pidfile, "%d\n", _getpid()); if (fflush(filep_pidfile) == EOF) { VLOG_FATAL("Failed to write into the pidfile %s", pidfile); } /* Don't close the pidfile till the process exits. */ }
/* 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). */ void daemonize_start(bool access_datapath) { assert_single_threaded(); daemonize_fd = -1; if (switch_user) { daemon_become_new_user__(access_datapath); switch_user = false; } if (detach) { pid_t pid; if (fork_and_wait_for_startup(&daemonize_fd, &pid)) { VLOG_FATAL("could not detach from foreground session"); } if (pid > 0) { /* Running in parent process. */ exit(0); } /* Running in daemon or monitor process. */ setsid(); } if (monitor) { int saved_daemonize_fd = daemonize_fd; pid_t daemon_pid; if (fork_and_wait_for_startup(&daemonize_fd, &daemon_pid)) { VLOG_FATAL("could not initiate process monitoring"); } if (daemon_pid > 0) { /* Running in monitor process. */ fork_notify_startup(saved_daemonize_fd); if (detach) { close_standard_fds(); } monitor_daemon(daemon_pid); } /* Running in daemon process. */ } forbid_forking("running in daemon process"); if (pidfile) { make_pidfile(); } /* Make sure that the unixctl commands for vlog get registered in a * daemon, even before the first log message. */ vlog_init(); }
/* Checks whether a process with the given 'pidfile' is already running and, * if so, aborts. If 'pidfile' is stale, deletes it. */ static void check_already_running(void) { long int pid = read_pidfile__(pidfile, true); if (pid > 0) { VLOG_FATAL("%s: already running as pid %ld, aborting", pidfile, pid); } else if (pid < 0) { VLOG_FATAL("%s: pidfile check failed (%s), aborting", pidfile, ovs_strerror(-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 { OVS_NOT_REACHED(); } } close(fds[0]); *fdp = -1; } else if (!pid) { /* Running in child process. */ close(fds[0]); *fdp = fds[1]; } return pid; }
static pid_t fork_and_wait_for_startup(int *fdp) { int fds[2]; pid_t pid; xpipe(fds); pid = fork(); if (pid > 0) { /* Running in parent process. */ size_t bytes_read; char c; close(fds[1]); fatal_signal_fork(); 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 && WIFEXITED(status) && WEXITSTATUS(status)) { /* Child exited with an error. Convey the same error to * our parent process as a courtesy. */ exit(WEXITSTATUS(status)); } VLOG_FATAL("fork child failed to signal startup (%s)", strerror(errno)); } close(fds[0]); *fdp = -1; } else if (!pid) { /* Running in child process. */ close(fds[0]); time_postfork(); lockfile_postfork(); *fdp = fds[1]; } else { VLOG_FATAL("fork failed (%s)", strerror(errno)); } return pid; }
static void fatal_signal_init(void) { static bool inited = false; if (!inited) { size_t i; inited = true; xpipe(signal_fds); set_nonblocking(signal_fds[0]); set_nonblocking(signal_fds[1]); sigemptyset(&fatal_signal_set); for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) { int sig_nr = fatal_signals[i]; struct sigaction old_sa; sigaddset(&fatal_signal_set, sig_nr); xsigaction(sig_nr, NULL, &old_sa); if (old_sa.sa_handler == SIG_DFL && signal(sig_nr, fatal_signal_handler) == SIG_ERR) { VLOG_FATAL("signal failed (%s)", strerror(errno)); } } atexit(atexit_handler); } }
/* In case of an unexpected termination, configure the action to be * taken. */ static void set_config_failure_actions() { /* In case of a failure, restart the process the first two times * After 'dwResetPeriod', the failure count is reset. */ SC_ACTION fail_action[3] = { {SC_ACTION_RESTART, 0}, {SC_ACTION_RESTART, 0}, {SC_ACTION_NONE, 0} }; SERVICE_FAILURE_ACTIONS service_fail_action; /* Reset failure count after (in seconds). */ service_fail_action.dwResetPeriod = 10; /* Reboot message. */ service_fail_action.lpRebootMsg = NULL; /* The command line of the process. */ service_fail_action.lpCommand = NULL; /* Number of elements in 'fail_actions'. */ service_fail_action.cActions = sizeof(fail_action)/sizeof(fail_action[0]); /* A pointer to an array of SC_ACTION structures. */ service_fail_action.lpsaActions = fail_action; if (!ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS, &service_fail_action)) { char *msg_buf = ovs_lasterror_to_string(); VLOG_FATAL("Failed to configure service fail actions (%s).", msg_buf); } }
/* Initializes the fatal signal handling module. Calling this function is * optional, because calling any other function in the module will also * initialize it. However, in a multithreaded program, the module must be * initialized while the process is still single-threaded. */ void fatal_signal_init(void) { static bool inited = false; if (!inited) { size_t i; assert_single_threaded(); inited = true; ovs_mutex_init_recursive(&mutex); xpipe_nonblocking(signal_fds); for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) { int sig_nr = fatal_signals[i]; struct sigaction old_sa; xsigaction(sig_nr, NULL, &old_sa); if (old_sa.sa_handler == SIG_DFL && signal(sig_nr, fatal_signal_handler) == SIG_ERR) { VLOG_FATAL("signal failed (%s)", ovs_strerror(errno)); } } atexit(atexit_handler); } }
uint32_t ofputil_versions_from_string(const char *s) { size_t i = 0; uint32_t bitmap = 0; while (s[i]) { size_t j; int version; char *key; if (is_delimiter(s[i])) { i++; continue; } j = 0; while (s[i + j] && !is_delimiter(s[i + j])) { j++; } key = xmemdup0(s + i, j); version = ofputil_version_from_string(key); if (!version) { VLOG_FATAL("Unknown OpenFlow version: \"%s\"", key); } free(key); bitmap |= 1u << version; i += j; } return bitmap; }
void xgettimeofday(struct timeval *tv) { if (gettimeofday(tv, NULL) == -1) { VLOG_FATAL("gettimeofday failed (%s)", ovs_strerror(errno)); } }
/* Check whether 'program_name' has been created as a service. */ static void check_service() { /* Establish a connection to the local service control manager. */ manager = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE); if (!manager) { char *msg_buf = ovs_lasterror_to_string(); VLOG_FATAL("Failed to open the service control manager (%s).", msg_buf); } service = OpenService(manager, program_name, SERVICE_ALL_ACCESS); if (!service) { char *msg_buf = ovs_lasterror_to_string(); VLOG_FATAL("Failed to open service (%s).", msg_buf); } }
static void daemon_switch_user(const uid_t uid, const char *user) { if ((setuid(uid) == -1) || !uid_verify(uid)) { VLOG_FATAL("%s: fail to switch user to %s, aborting", pidfile, user); } }
static void daemon_switch_group(gid_t gid) { if ((setgid(gid) == -1) || !gid_verify(gid)) { VLOG_FATAL("%s: fail to switch group to gid as %d, aborting", pidfile, gid); } }
/* Initializes 'buffer' with 'n' bytes of high-quality random numbers. Exits * if an error occurs. */ void get_entropy_or_die(void *buffer, size_t n) { int error = get_entropy(buffer, n); if (error) { VLOG_FATAL("%s: read error (%s)", urandom, ovs_retval_to_string(error)); } }
/* Initializes the fatal signal handling module. Calling this function is * optional, because calling any other function in the module will also * initialize it. However, in a multithreaded program, the module must be * initialized while the process is still single-threaded. */ void fatal_signal_init(void) { static bool inited = false; if (!inited) { size_t i; assert_single_threaded(); inited = true; ovs_mutex_init_recursive(&mutex); #ifndef _WIN32 xpipe_nonblocking(signal_fds); #else wevent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!wevent) { char *msg_buf = ovs_lasterror_to_string(); VLOG_FATAL("Failed to create a event (%s).", msg_buf); } /* Register a function to handle Ctrl+C. */ SetConsoleCtrlHandler(ConsoleHandlerRoutine, true); #endif for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) { int sig_nr = fatal_signals[i]; #ifndef _WIN32 struct sigaction old_sa; xsigaction(sig_nr, NULL, &old_sa); if (old_sa.sa_handler == SIG_DFL && signal(sig_nr, fatal_signal_handler) == SIG_ERR) { VLOG_FATAL("signal failed (%s)", ovs_strerror(errno)); } #else if (signal(sig_nr, fatal_signal_handler) == SIG_ERR) { VLOG_FATAL("signal failed (%s)", ovs_strerror(errno)); } #endif } atexit(fatal_signal_atexit_handler); } }
static void daemon_switch_user(const uid_t real, const uid_t effective, const uid_t saved, const char *user) { if ((setresuid(real, effective, saved) == -1) || !uid_verify(real, effective, saved)) { VLOG_FATAL("%s: fail to switch user to %s, aborting", pidfile, user); } }
static void daemon_switch_group(gid_t real, gid_t effective, gid_t saved) { if ((setresgid(real, effective, saved) == -1) || !gid_verify(real, effective, saved)) { VLOG_FATAL("%s: failed to switch group to gid as %d, aborting", pidfile, gid); } }
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); } }
/* Linux specific implementation of daemon_become_new_user() * using libcap-ng. */ static void daemon_become_new_user_linux(bool access_datapath OVS_UNUSED) { #if defined __linux__ && HAVE_LIBCAPNG int ret; ret = capng_get_caps_process(); if (!ret) { if (capng_have_capabilities(CAPNG_SELECT_CAPS) > CAPNG_NONE) { const capng_type_t cap_sets = CAPNG_EFFECTIVE|CAPNG_PERMITTED; capng_clear(CAPNG_SELECT_BOTH); ret = capng_update(CAPNG_ADD, cap_sets, CAP_IPC_LOCK) || capng_update(CAPNG_ADD, cap_sets, CAP_NET_BIND_SERVICE); if (access_datapath && !ret) { ret = capng_update(CAPNG_ADD, cap_sets, CAP_NET_ADMIN) || capng_update(CAPNG_ADD, cap_sets, CAP_NET_RAW) || capng_update(CAPNG_ADD, cap_sets, CAP_NET_BROADCAST); } } else { ret = -1; } } if (!ret) { /* CAPNG_INIT_SUPP_GRP will be a better choice than * CAPNG_DROP_SUPP_GRP. However this enum value is only defined * with libcap-ng higher than version 0.7.4, which is not wildly * available on many Linux distributions yet. Taking a more * conservative approach to make sure OVS behaves consistently. * * XXX We may change this for future OVS releases. */ ret = capng_change_id(uid, gid, CAPNG_DROP_SUPP_GRP | CAPNG_CLEAR_BOUNDING); } if (ret) { VLOG_FATAL("%s: libcap-ng fail to switch to user and group " "%d:%d, aborting", pidfile, uid, gid); } #endif }
/* 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; pid = fork(); if (pid > 0) { /* Running in parent process. */ fatal_signal_fork(); } else if (!pid) { /* Running in child process. */ time_postfork(); lockfile_postfork(); } else { VLOG_FATAL("fork failed (%s)", strerror(errno)); } return pid; }
void daemonize_complete(void) { /* If running as a child because '--detach' option was specified, * communicate with the parent to inform that the child is ready. */ if (detached) { int error; close_standard_fds(); error = WriteFile(write_handle, "a", 1, NULL, NULL); if (!error) { VLOG_FATAL("Failed to communicate with the parent (%s)", ovs_lasterror_to_string()); } } service_complete(); }
static void daemon_become_new_user__(bool access_datapath) { /* If vlog file has been created, change its owner to the non-root user * as specifed by the --user option. */ vlog_change_owner_unix(uid, gid); if (LINUX) { if (LIBCAPNG) { daemon_become_new_user_linux(access_datapath); } else { VLOG_FATAL("%s: fail to downgrade user using libcap-ng. " "(libcap-ng is not configured at compile time), " "aborting.", pidfile); } } else { daemon_become_new_user_unix(); } }
/* Use portable Unix APIs to switch uid:gid, when datapath * access is not required. On Linux systems, all capabilities * will be dropped. */ static void daemon_become_new_user_unix(void) { /* "Setuid Demystified" by Hao Chen, etc outlines some caveats of * around unix system call setuid() and friends. This implementation * mostly follow the advice given by the paper. The paper is * published in 2002, so things could have changed. */ /* Change both real and effective uid and gid will permanently * drop the process' privilege. "Setuid Demystified" suggested * that calling getuid() after each setuid() call to verify they * are actually set, because checking return code alone is not * sufficient. */ daemon_switch_group(gid, gid, gid); if (user && initgroups(user, gid) == -1) { VLOG_FATAL("%s: fail to add supplementary group gid %d, " "aborting", pidfile, gid); } daemon_switch_user(uid, uid, uid, user); }
static char * parse_options(int argc, char *argv[], char **unixctl_pathp) { enum { OPT_PEER_CA_CERT = UCHAR_MAX + 1, OPT_MLOCKALL, OPT_UNIXCTL, VLOG_OPTION_ENUMS, OPT_BOOTSTRAP_CA_CERT, OPT_ENABLE_DUMMY, OPT_DISABLE_SYSTEM, DAEMON_OPTION_ENUMS, OPT_DPDK, }; static const struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {"mlockall", no_argument, NULL, OPT_MLOCKALL}, {"unixctl", required_argument, NULL, OPT_UNIXCTL}, DAEMON_LONG_OPTIONS, VLOG_LONG_OPTIONS, STREAM_SSL_LONG_OPTIONS, {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT}, {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT}, {"enable-dummy", optional_argument, NULL, OPT_ENABLE_DUMMY}, {"disable-system", no_argument, NULL, OPT_DISABLE_SYSTEM}, {"dpdk", required_argument, NULL, OPT_DPDK}, {NULL, 0, NULL, 0}, }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); for (;;) { int c; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { case 'h': usage(); case 'V': ovs_print_version(OFP10_VERSION, OFP10_VERSION); exit(EXIT_SUCCESS); case OPT_MLOCKALL: want_mlockall = true; break; case OPT_UNIXCTL: *unixctl_pathp = optarg; break; VLOG_OPTION_HANDLERS DAEMON_OPTION_HANDLERS STREAM_SSL_OPTION_HANDLERS case OPT_PEER_CA_CERT: stream_ssl_set_peer_ca_cert_file(optarg); break; case OPT_BOOTSTRAP_CA_CERT: stream_ssl_set_ca_cert_file(optarg, true); break; case OPT_ENABLE_DUMMY: dummy_enable(optarg && !strcmp(optarg, "override")); break; case OPT_DISABLE_SYSTEM: dp_blacklist_provider("system"); break; case '?': exit(EXIT_FAILURE); case OPT_DPDK: ovs_fatal(0, "--dpdk must be given at beginning of command line."); break; default: abort(); } } free(short_options); argc -= optind; argv += optind; switch (argc) { case 0: return xasprintf("unix:%s/db.sock", ovs_rundir()); case 1: return xstrdup(argv[0]); default: VLOG_FATAL("at most one non-option argument accepted; " "use --help for usage"); } }
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; } xsleep(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); } } } free(status_msg); /* Running in new daemon process. */ proctitle_restore(); set_subprogram_name(""); }
/* 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); }
/* Registers the call-back and configures the actions in case of a failure * with the Windows services manager. */ void service_start(int *argcp, char **argvp[]) { int argc = *argcp; char **argv = *argvp; int i; SERVICE_TABLE_ENTRY service_table[] = { {(LPTSTR)program_name, (LPSERVICE_MAIN_FUNCTION)main}, {NULL, NULL} }; /* 'detached' is 'false' when service_start() is called the first time. * It is 'true', when it is called the second time by the Windows services * manager. */ if (detached) { init_service_status(); wevent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!wevent) { char *msg_buf = ovs_lasterror_to_string(); VLOG_FATAL("Failed to create a event (%s).", msg_buf); } poll_fd_wait_event(0, wevent, POLLIN); /* Register the control handler. This function is called by the service * manager to stop the service. */ hstatus = RegisterServiceCtrlHandler(program_name, (LPHANDLER_FUNCTION)control_handler); if (!hstatus) { char *msg_buf = ovs_lasterror_to_string(); VLOG_FATAL("Failed to register the service control handler (%s).", msg_buf); } if (monitor) { set_config_failure_actions(); } /* When the service control manager does the call back, it does not * send the same arguments as sent to the main function during the * service start. So, use the arguments passed over during the first * time. */ *argcp = sargc; *argvp = *sargvp; /* XXX: Windows implementation cannot have a unixctl commands in the * traditional sense of unix domain sockets. If an implementation is * done that involves 'unixctl' vlog commands the following call is * needed to make sure that the unixctl commands for vlog get * registered in a daemon, even before the first log message. */ vlog_init(); return; } assert_single_threaded(); /* A reference to arguments passed to the main function the first time. * We need it after the call-back from service control manager. */ sargc = argc; sargvp = argvp; /* We are only interested in the '--service' and '--service-monitor' * options before the call-back from the service control manager. */ for (i = 0; i < argc; i ++) { if (!strcmp(argv[i], "--service")) { detach = true; } else if (!strcmp(argv[i], "--service-monitor")) { monitor = true; } } /* If '--service' is not a command line option, run in foreground. */ if (!detach) { return; } /* If we have been configured to run as a service, then that service * should already have been created either manually or through a start up * script. */ check_service(); detached = true; /* StartServiceCtrlDispatcher blocks and returns after the service is * stopped. */ if (!StartServiceCtrlDispatcher(service_table)) { char *msg_buf = ovs_lasterror_to_string(); VLOG_FATAL("Failed at StartServiceCtrlDispatcher (%s)", msg_buf); } exit(0); }
static void NO_RETURN nossl_option(const char *detail) { VLOG_FATAL("%s specified but Open vSwitch was built without SSL support", detail); }
int main(int argc, char *argv[]) { struct unixctl_server *unixctl; bool exiting; int retval; ovs_cmdl_proctitle_init(argc, argv); set_program_name(argv[0]); parse_options(argc, argv); fatal_ignore_sigpipe(); daemonize_start(); retval = unixctl_server_create(NULL, &unixctl); if (retval) { exit(EXIT_FAILURE); } unixctl_command_register("exit", "", 0, 0, ovn_controller_exit, &exiting); daemonize_complete(); ovsrec_init(); sbrec_init(); ofctrl_init(); lflow_init(); /* Connect to OVS OVSDB instance. We do not monitor all tables by * default, so modules must register their interest explicitly. */ struct idl_loop ovs_idl_loop = IDL_LOOP_INITIALIZER( ovsdb_idl_create(ovs_remote, &ovsrec_idl_class, false, true)); ovsdb_idl_add_table(ovs_idl_loop.idl, &ovsrec_table_open_vswitch); ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_open_vswitch_col_external_ids); chassis_register_ovs_idl(ovs_idl_loop.idl); encaps_register_ovs_idl(ovs_idl_loop.idl); binding_register_ovs_idl(ovs_idl_loop.idl); physical_register_ovs_idl(ovs_idl_loop.idl); get_initial_snapshot(ovs_idl_loop.idl); /* Connect to OVN SB database. */ char *ovnsb_remote = get_ovnsb_remote(ovs_idl_loop.idl); struct idl_loop ovnsb_idl_loop = IDL_LOOP_INITIALIZER( ovsdb_idl_create(ovnsb_remote, &sbrec_idl_class, true, true)); get_initial_snapshot(ovnsb_idl_loop.idl); /* Main loop. */ exiting = false; while (!exiting) { struct controller_ctx ctx = { .ovs_idl = ovs_idl_loop.idl, .ovs_idl_txn = idl_loop_run(&ovs_idl_loop), .ovnsb_idl = ovnsb_idl_loop.idl, .ovnsb_idl_txn = idl_loop_run(&ovnsb_idl_loop), }; const struct ovsrec_bridge *br_int = get_br_int(ctx.ovs_idl); const char *chassis_id = get_chassis_id(ctx.ovs_idl); if (chassis_id) { chassis_run(&ctx, chassis_id); encaps_run(&ctx, br_int, chassis_id); binding_run(&ctx, br_int, chassis_id); } if (br_int) { struct hmap flow_table = HMAP_INITIALIZER(&flow_table); lflow_run(&ctx, &flow_table); if (chassis_id) { physical_run(&ctx, br_int, chassis_id, &flow_table); } ofctrl_run(br_int, &flow_table); hmap_destroy(&flow_table); } unixctl_server_run(unixctl); unixctl_server_wait(unixctl); if (exiting) { poll_immediate_wake(); } idl_loop_commit_and_wait(&ovnsb_idl_loop); idl_loop_commit_and_wait(&ovs_idl_loop); if (br_int) { ofctrl_wait(); } poll_block(); } /* It's time to exit. Clean up the databases. */ bool done = false; while (!done) { struct controller_ctx ctx = { .ovs_idl = ovs_idl_loop.idl, .ovs_idl_txn = idl_loop_run(&ovs_idl_loop), .ovnsb_idl = ovnsb_idl_loop.idl, .ovnsb_idl_txn = idl_loop_run(&ovnsb_idl_loop), }; const struct ovsrec_bridge *br_int = get_br_int(ctx.ovs_idl); const char *chassis_id = get_chassis_id(ctx.ovs_idl); /* Run all of the cleanup functions, even if one of them returns false. * We're done if all of them return true. */ done = binding_cleanup(&ctx, chassis_id); done = chassis_cleanup(&ctx, chassis_id) && done; done = encaps_cleanup(&ctx, br_int) && done; if (done) { poll_immediate_wake(); } idl_loop_commit_and_wait(&ovnsb_idl_loop); idl_loop_commit_and_wait(&ovs_idl_loop); poll_block(); } unixctl_server_destroy(unixctl); lflow_destroy(); ofctrl_destroy(); idl_loop_destroy(&ovs_idl_loop); idl_loop_destroy(&ovnsb_idl_loop); free(ovnsb_remote); free(ovs_remote); exit(retval); } static void parse_options(int argc, char *argv[]) { enum { OPT_PEER_CA_CERT = UCHAR_MAX + 1, VLOG_OPTION_ENUMS, DAEMON_OPTION_ENUMS }; static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, VLOG_LONG_OPTIONS, DAEMON_LONG_OPTIONS, STREAM_SSL_LONG_OPTIONS, {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT}, {NULL, 0, NULL, 0} }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); for (;;) { int c; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { case 'h': usage(); case 'V': ovs_print_version(OFP13_VERSION, OFP13_VERSION); exit(EXIT_SUCCESS); VLOG_OPTION_HANDLERS DAEMON_OPTION_HANDLERS STREAM_SSL_OPTION_HANDLERS case OPT_PEER_CA_CERT: stream_ssl_set_peer_ca_cert_file(optarg); break; case '?': exit(EXIT_FAILURE); default: abort(); } } free(short_options); argc -= optind; argv += optind; if (argc == 0) { ovs_remote = xasprintf("unix:%s/db.sock", ovs_rundir()); } else if (argc == 1) { ovs_remote = xstrdup(argv[0]); } else { VLOG_FATAL("exactly zero or one non-option argument required; " "use --help for usage"); } } static void usage(void) { printf("%s: OVN controller\n" "usage %s [OPTIONS] [OVS-DATABASE]\n" "where OVS-DATABASE is a socket on which the OVS OVSDB server is listening.\n", program_name, program_name); stream_usage("OVS-DATABASE", true, false, false); daemon_usage(); vlog_usage(); printf("\nOther options:\n" " -h, --help display this help message\n" " -V, --version display version information\n"); exit(EXIT_SUCCESS); } static void ovn_controller_exit(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *exiting_) { bool *exiting = exiting_; *exiting = true; unixctl_command_reply(conn, NULL); }