diff options
Diffstat (limited to 'libdaemon/dpid.c')
-rw-r--r-- | libdaemon/dpid.c | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/libdaemon/dpid.c b/libdaemon/dpid.c new file mode 100644 index 0000000..7f66d0b --- /dev/null +++ b/libdaemon/dpid.c @@ -0,0 +1,281 @@ +/*** + This file is part of libdaemon. + + Copyright 2003-2008 Lennart Poettering + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> +#include <sys/select.h> +#include <fcntl.h> +#include <stddef.h> +#include <sys/time.h> + +#include "dpid.h" +#include "dlog.h" + +#ifndef ETIME +#define ETIME ETIMEDOUT /* For FreeBSD */ +#endif + +#ifndef PATH_MAX +#define PATH_MAX 512 +#endif + +#define VARRUN LOCALSTATEDIR "/run" + +const char *daemon_pid_file_ident = NULL; +daemon_pid_file_proc_t daemon_pid_file_proc = daemon_pid_file_proc_default; + +const char *daemon_pid_file_proc_default(void) { +#ifdef HAVE_ASPRINTF + static char *fn = NULL; + free(fn); + asprintf(&fn, "%s/%s.pid", VARRUN, daemon_pid_file_ident ? daemon_pid_file_ident : "unknown"); +#else + static char fn[PATH_MAX]; + snprintf(fn, sizeof(fn), "%s/%s.pid", VARRUN, daemon_pid_file_ident ? daemon_pid_file_ident : "unknown"); +#endif + + return fn; +} + +static int lock_file(int fd, int enable) { + struct flock f; + + memset(&f, 0, sizeof(f)); + f.l_type = enable ? F_WRLCK : F_UNLCK; + f.l_whence = SEEK_SET; + f.l_start = 0; + f.l_len = 0; + + if (fcntl(fd, F_SETLKW, &f) < 0) { + + if (enable && errno == EBADF) { + f.l_type = F_RDLCK; + + if (fcntl(fd, F_SETLKW, &f) >= 0) + return 0; + } + + daemon_log(LOG_WARNING, "fcntl(F_SETLKW) failed: %s", strerror(errno)); + return -1; + } + + return 0; +} + +pid_t daemon_pid_file_is_running(void) { + const char *fn; + static char txt[256]; + int fd = -1, locked = -1; + pid_t ret = (pid_t) -1, pid; + ssize_t l; + long lpid; + char *e = NULL; + + if (!(fn = daemon_pid_file_proc())) { + errno = EINVAL; + goto finish; + } + + if ((fd = open(fn, O_RDWR, 0644)) < 0) { + if ((fd = open(fn, O_RDONLY, 0644)) < 0) { + if (errno != ENOENT) + daemon_log(LOG_WARNING, "Failed to open PID file: %s", strerror(errno)); + + goto finish; + } + } + + if ((locked = lock_file(fd, 1)) < 0) + goto finish; + + if ((l = read(fd, txt, sizeof(txt)-1)) < 0) { + int saved_errno = errno; + daemon_log(LOG_WARNING, "read(): %s", strerror(errno)); + unlink(fn); + errno = saved_errno; + goto finish; + } + + txt[l] = 0; + txt[strcspn(txt, "\r\n")] = 0; + + errno = 0; + lpid = strtol(txt, &e, 10); + pid = (pid_t) lpid; + + if (errno != 0 || !e || *e || (long) pid != lpid) { + daemon_log(LOG_WARNING, "PID file corrupt, removing. (%s)", fn); + unlink(fn); + errno = EINVAL; + goto finish; + } + + if (kill(pid, 0) != 0 && errno != EPERM) { + int saved_errno = errno; + daemon_log(LOG_WARNING, "Process %lu died: %s; trying to remove PID file. (%s)", (unsigned long) pid, strerror(errno), fn); + unlink(fn); + errno = saved_errno; + goto finish; + } + + ret = pid; + +finish: + + if (fd >= 0) { + int saved_errno = errno; + if (locked >= 0) + lock_file(fd, 0); + close(fd); + errno = saved_errno; + } + + return ret; +} + +int daemon_pid_file_kill(int s) { + pid_t pid; + + if ((pid = daemon_pid_file_is_running()) == (pid_t) -1) + return -1; + + if (kill(pid, s) < 0) + return -1; + + return 0; +} + +int daemon_pid_file_kill_wait(int s, int m) { + pid_t pid; + time_t t; + + if ((pid = daemon_pid_file_is_running()) < 0) + return -1; + + if (kill(pid, s) < 0) + return -1; + + t = time(NULL) + m; + + for (;;) { + int r; + struct timeval tv = { 0, 100000 }; + + if (time(NULL) > t) { + errno = ETIME; + return -1; + } + + if ((r = kill(pid, 0)) < 0 && errno != ESRCH) + return -1; + + if (r) + return 0; + + if (select(0, NULL, NULL, NULL, &tv) < 0) + return -1; + } +} + +int daemon_pid_file_create(void) { + const char *fn; + int fd = -1; + int ret = -1; + int locked = -1; + char t[64]; + ssize_t l; + mode_t u; + + u = umask(022); + + if (!(fn = daemon_pid_file_proc())) { + errno = EINVAL; + goto finish; + } + + if ((fd = open(fn, O_CREAT|O_RDWR|O_EXCL, 0644)) < 0) { + daemon_log(LOG_ERR, "open(%s): %s", fn, strerror(errno)); + goto finish; + } + + if ((locked = lock_file(fd, 1)) < 0) { + int saved_errno = errno; + unlink(fn); + errno = saved_errno; + goto finish; + } + + snprintf(t, sizeof(t), "%lu\n", (unsigned long) getpid()); + + l = strlen(t); + if (write(fd, t, l) != l) { + int saved_errno = errno; + daemon_log(LOG_WARNING, "write(): %s", strerror(errno)); + unlink(fn); + errno = saved_errno; + goto finish; + } + + ret = 0; + +finish: + + if (fd >= 0) { + int saved_errno = errno; + + if (locked >= 0) + lock_file(fd, 0); + + close(fd); + errno = saved_errno; + } + + umask(u); + + return ret; +} + +int daemon_pid_file_remove(void) { + const char *fn; + + if (!(fn = daemon_pid_file_proc())) { + errno = EINVAL; + return -1; + } + + return unlink(fn); +} |