-
-/* 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(void)
-{
- daemonize_fd = -1;
-
- if (detach) {
- if (fork_and_wait_for_startup(&daemonize_fd) > 0) {
- /* Running in parent process. */
- exit(0);
- }
- /* Running in daemon or monitor process. */
- }
-
- if (monitor) {
- int saved_daemonize_fd = daemonize_fd;
- pid_t daemon_pid;
-
- daemon_pid = fork_and_wait_for_startup(&daemonize_fd);
- if (daemon_pid > 0) {
- /* Running in monitor process. */
- fork_notify_startup(saved_daemonize_fd);
- close_standard_fds();
- monitor_daemon(daemon_pid);
- }
- /* 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();
-}
-
-/* If daemonization is configured, then this function notifies the parent
- * 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();
- if (chdir_) {
- ignore(chdir("/"));
- }
- close_standard_fds();
- detach = false;
- }
-}
-
-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);
-}
-
-static int
-lock_pidfile__(FILE *file, int command, struct flock *lck)
-{
- int error;
-
- lck->l_type = F_WRLCK;
- lck->l_whence = SEEK_SET;
- lck->l_start = 0;
- lck->l_len = 0;
- lck->l_pid = 0;
-
- do {
- error = fcntl(fileno(file), command, lck) == -1 ? errno : 0;
- } while (error == EINTR);
- return error;
-}
-
-static int
-lock_pidfile(FILE *file, int command)
-{
- struct flock lck;
-
- return lock_pidfile__(file, command, &lck);
-}
-
-static pid_t
-read_pidfile__(const char *pidfile, bool delete_if_stale)
-{
- struct stat s, s2;
- struct flock lck;
- char line[128];
- 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) {
- if (errno == ENOENT && delete_if_stale) {
- return 0;
- }
- error = errno;
- VLOG_WARN("%s: open: %s", pidfile, strerror(error));
- goto error;
- }
-
- error = lock_pidfile__(file, F_GETLK, &lck);
- if (error) {
- VLOG_WARN("%s: fcntl: %s", pidfile, strerror(error));
- goto error;
- }
- if (lck.l_type == F_UNLCK) {
- /* pidfile exists but it isn't locked by anyone. We need to delete it
- * so that a new pidfile can go in its place. But just calling
- * unlink(pidfile) makes a nasty race: what if someone else unlinks it
- * before we do and then replaces it by a valid pidfile? We'd unlink
- * their valid pidfile. We do a little dance to avoid the race, by
- * locking the invalid pidfile. Only one process can have the invalid
- * pidfile locked, and only that process has the right to unlink it. */
- if (!delete_if_stale) {
- error = ESRCH;
- VLOG_DBG("%s: pid file is stale", pidfile);
- goto error;
- }
-
- /* Get the lock. */
- error = lock_pidfile(file, F_SETLK);
- if (error) {
- /* We lost a race with someone else doing the same thing. */
- VLOG_WARN("%s: lost race to lock pidfile", pidfile);
- goto error;
- }
-
- /* Is the file we have locked still named 'pidfile'? */
- if (stat(pidfile, &s) || fstat(fileno(file), &s2)
- || s.st_ino != s2.st_ino || s.st_dev != s2.st_dev) {
- /* No. We lost a race with someone else who got the lock before
- * us, deleted the pidfile, and closed it (releasing the lock). */
- error = EALREADY;
- VLOG_WARN("%s: lost race to delete pidfile", pidfile);
- goto error;
- }
-
- /* We won the right to delete the stale pidfile. */
- if (unlink(pidfile)) {
- error = errno;
- VLOG_WARN("%s: failed to delete stale pidfile (%s)",
- pidfile, strerror(error));
- goto error;
- }
- VLOG_DBG("%s: deleted stale pidfile", pidfile);
- fclose(file);
- return 0;
- }
-
- 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);
- }
- goto error;
- }
-
- if (lck.l_pid != strtoul(line, NULL, 10)) {
- /* The process that has the pidfile locked is not the process that
- * created it. It must be stale, with the process that has it locked
- * preparing to delete it. */
- error = ESRCH;
- VLOG_WARN("%s: stale pidfile for pid %s being deleted by pid %ld",
- pidfile, line, (long int) lck.l_pid);
- goto error;
- }
-
- fclose(file);
- return lck.l_pid;
-
-error:
- if (file) {
- fclose(file);
- }
- return -error;
-}
-
-/* Opens and reads a PID from 'pidfile'. Returns the positive PID if
- * successful, otherwise a negative errno value. */
-pid_t
-read_pidfile(const char *pidfile)
-{
- return read_pidfile__(pidfile, false);
-}
-
-/* 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, strerror(-pid));
- }
-}