| /*** |
| 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); |
| } |