From: Ben Pfaff Date: Wed, 23 Jul 2008 20:07:02 +0000 (-0700) Subject: Lock pidfiles with fcntl and create them atomically. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dae8a4a83cd5f7449dfc153d02cbb93dd7ce7b98;p=openvswitch Lock pidfiles with fcntl and create them atomically. This makes it possible to verify that the program that created the pidfile is still running. --- diff --git a/lib/daemon.c b/lib/daemon.c index e14b776d..b153dc0c 100644 --- a/lib/daemon.c +++ b/lib/daemon.c @@ -34,6 +34,7 @@ #include #include "daemon.h" #include +#include #include #include #include @@ -72,25 +73,56 @@ set_detach(void) detach = true; } -/* If a pidfile has been configured, creates it and stores 'pid' in it. It is - * the caller's responsibility to make sure that the pidfile will eventually - * be deleted. */ +/* 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(pid_t pid) +make_pidfile(void) { if (pidfile) { - FILE *file; + /* 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; - file = fopen(pidfile, "w"); - if (file) { - fprintf(file, "%ld\n", (long int) pid); - fclose(file); + 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) >= 0) { + 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. */ + } + } 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("failed to create \"%s\": %s", pidfile, strerror(errno)); + VLOG_ERR("%s: create failed: %s", tmpfile, strerror(errno)); } - free(pidfile); - pidfile = NULL; + 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 @@ -99,28 +131,37 @@ void daemonize(void) { if (detach) { - pid_t pid; + char c = 0; + int fds[2]; + if (pipe(fds) < 0) { + fatal(errno, "pipe failed"); + } - /* Fork and exit from the parent. */ - pid = fork(); - if (pid < 0) { - fatal(errno, "could not fork"); - } else if (pid) { + switch (fork()) { + default: + /* Parent process: wait for child to create pidfile, then exit. */ + close(fds[1]); fatal_signal_fork(); - make_pidfile(pid); + read(fds[0], &c, 1); exit(0); - } - if (pidfile) { - fatal_signal_add_file_to_unlink(pidfile); + case 0: + /* Child process. */ + close(fds[0]); + make_pidfile(); + write(fds[1], &c, 1); + close(fds[1]); + setsid(); + chdir("/"); + break; + + case -1: + /* Error. */ + fatal(errno, "could not fork"); + break; } - setsid(); - chdir("/"); } else { - if (pidfile) { - fatal_signal_add_file_to_unlink(pidfile); - } - make_pidfile(getpid()); + make_pidfile(); } }