1/***
2  This file is part of libdaemon.
3
4  Copyright 2003-2008 Lennart Poettering
5
6  Permission is hereby granted, free of charge, to any person obtaining a copy
7  of this software and associated documentation files (the "Software"), to deal
8  in the Software without restriction, including without limitation the rights
9  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  copies of the Software, and to permit persons to whom the Software is
11  furnished to do so, subject to the following conditions:
12
13  The above copyright notice and this permission notice shall be included in
14  all copies or substantial portions of the Software.
15
16  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  SOFTWARE.
23
24***/
25
26#ifdef HAVE_CONFIG_H
27#include <config.h>
28#endif
29
30#include <limits.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <signal.h>
34#include <string.h>
35#include <unistd.h>
36#include <errno.h>
37#include <sys/types.h>
38#include <sys/stat.h>
39#include <time.h>
40#include <sys/select.h>
41#include <fcntl.h>
42#include <stddef.h>
43#include <sys/time.h>
44
45#include "dpid.h"
46#include "dlog.h"
47
48#ifndef ETIME
49#define ETIME ETIMEDOUT /* For FreeBSD */
50#endif
51
52#ifndef PATH_MAX
53#define PATH_MAX 512
54#endif
55
56#define VARRUN LOCALSTATEDIR "/run"
57
58const char *daemon_pid_file_ident = NULL;
59daemon_pid_file_proc_t daemon_pid_file_proc = daemon_pid_file_proc_default;
60
61const char *daemon_pid_file_proc_default(void) {
62#ifdef HAVE_ASPRINTF
63    static char *fn = NULL;
64    free(fn);
65    asprintf(&fn,  "%s/%s.pid", VARRUN, daemon_pid_file_ident ? daemon_pid_file_ident : "unknown");
66#else
67    static char fn[PATH_MAX];
68    snprintf(fn, sizeof(fn), "%s/%s.pid", VARRUN, daemon_pid_file_ident ? daemon_pid_file_ident : "unknown");
69#endif
70
71    return fn;
72}
73
74static int lock_file(int fd, int enable) {
75    struct flock f;
76
77    memset(&f, 0, sizeof(f));
78    f.l_type = enable ? F_WRLCK : F_UNLCK;
79    f.l_whence = SEEK_SET;
80    f.l_start = 0;
81    f.l_len = 0;
82
83    if (fcntl(fd, F_SETLKW, &f) < 0) {
84
85        if (enable && errno == EBADF) {
86            f.l_type = F_RDLCK;
87
88            if (fcntl(fd, F_SETLKW, &f) >= 0)
89                return 0;
90        }
91
92        daemon_log(LOG_WARNING, "fcntl(F_SETLKW) failed: %s", strerror(errno));
93        return -1;
94    }
95
96    return 0;
97}
98
99pid_t daemon_pid_file_is_running(void) {
100    const char *fn;
101    static char txt[256];
102    int fd = -1, locked = -1;
103    pid_t ret = (pid_t) -1, pid;
104    ssize_t l;
105    long lpid;
106    char *e = NULL;
107
108    if (!(fn = daemon_pid_file_proc())) {
109        errno = EINVAL;
110        goto finish;
111    }
112
113    if ((fd = open(fn, O_RDWR, 0644)) < 0) {
114        if ((fd = open(fn, O_RDONLY, 0644)) < 0) {
115            if (errno != ENOENT)
116                daemon_log(LOG_WARNING, "Failed to open PID file: %s", strerror(errno));
117
118            goto finish;
119        }
120    }
121
122    if ((locked = lock_file(fd, 1)) < 0)
123        goto finish;
124
125    if ((l = read(fd, txt, sizeof(txt)-1)) < 0) {
126        int saved_errno = errno;
127        daemon_log(LOG_WARNING, "read(): %s", strerror(errno));
128        unlink(fn);
129        errno = saved_errno;
130        goto finish;
131    }
132
133    txt[l] = 0;
134    txt[strcspn(txt, "\r\n")] = 0;
135
136    errno = 0;
137    lpid = strtol(txt, &e, 10);
138    pid = (pid_t) lpid;
139
140    if (errno != 0 || !e || *e || (long) pid != lpid) {
141        daemon_log(LOG_WARNING, "PID file corrupt, removing. (%s)", fn);
142        unlink(fn);
143        errno = EINVAL;
144        goto finish;
145    }
146
147    if (kill(pid, 0) != 0 && errno != EPERM) {
148        int saved_errno = errno;
149        daemon_log(LOG_WARNING, "Process %lu died: %s; trying to remove PID file. (%s)", (unsigned long) pid, strerror(errno), fn);
150        unlink(fn);
151        errno = saved_errno;
152        goto finish;
153    }
154
155    ret = pid;
156
157finish:
158
159    if (fd >= 0) {
160        int saved_errno = errno;
161        if (locked >= 0)
162            lock_file(fd, 0);
163        close(fd);
164        errno = saved_errno;
165    }
166
167    return ret;
168}
169
170int daemon_pid_file_kill(int s) {
171    pid_t pid;
172
173    if ((pid = daemon_pid_file_is_running()) == (pid_t) -1)
174        return -1;
175
176    if (kill(pid, s) < 0)
177        return -1;
178
179    return 0;
180}
181
182int daemon_pid_file_kill_wait(int s, int m) {
183    pid_t pid;
184    time_t t;
185
186    if ((pid = daemon_pid_file_is_running()) < 0)
187        return -1;
188
189    if (kill(pid, s) < 0)
190        return -1;
191
192    t = time(NULL) + m;
193
194    for (;;) {
195        int r;
196        struct timeval tv = { 0, 100000 };
197
198        if (time(NULL) > t) {
199            errno = ETIME;
200            return -1;
201        }
202
203        if ((r = kill(pid, 0)) < 0 && errno != ESRCH)
204            return -1;
205
206        if (r)
207            return 0;
208
209        if (select(0, NULL, NULL, NULL, &tv) < 0)
210            return -1;
211    }
212}
213
214int daemon_pid_file_create(void) {
215    const char *fn;
216    int fd = -1;
217    int ret = -1;
218    int locked = -1;
219    char t[64];
220    ssize_t l;
221    mode_t u;
222
223    u = umask(022);
224
225    if (!(fn = daemon_pid_file_proc())) {
226        errno = EINVAL;
227        goto finish;
228    }
229
230    if ((fd = open(fn, O_CREAT|O_RDWR|O_EXCL, 0644)) < 0) {
231        daemon_log(LOG_ERR, "open(%s): %s", fn, strerror(errno));
232        goto finish;
233    }
234
235    if ((locked = lock_file(fd, 1)) < 0) {
236        int saved_errno = errno;
237        unlink(fn);
238        errno = saved_errno;
239        goto finish;
240    }
241
242    snprintf(t, sizeof(t), "%lu\n", (unsigned long) getpid());
243
244    l = strlen(t);
245    if (write(fd, t, l) != l) {
246        int saved_errno = errno;
247        daemon_log(LOG_WARNING, "write(): %s", strerror(errno));
248        unlink(fn);
249        errno = saved_errno;
250        goto finish;
251    }
252
253    ret = 0;
254
255finish:
256
257    if (fd >= 0) {
258        int saved_errno = errno;
259
260        if (locked >= 0)
261            lock_file(fd, 0);
262
263        close(fd);
264        errno = saved_errno;
265    }
266
267    umask(u);
268
269    return ret;
270}
271
272int daemon_pid_file_remove(void) {
273    const char *fn;
274
275    if (!(fn = daemon_pid_file_proc())) {
276        errno = EINVAL;
277        return -1;
278    }
279
280    return unlink(fn);
281}
282