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