summaryrefslogtreecommitdiffstats
path: root/libdaemon/dpid.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdaemon/dpid.c')
-rw-r--r--libdaemon/dpid.c281
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);
+}