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 
58 const char *daemon_pid_file_ident = NULL;
59 daemon_pid_file_proc_t daemon_pid_file_proc = daemon_pid_file_proc_default;
60 
daemon_pid_file_proc_default(void)61 const 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 
lock_file(int fd,int enable)74 static 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 
daemon_pid_file_is_running(void)99 pid_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 
157 finish:
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 
daemon_pid_file_kill(int s)170 int 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 
daemon_pid_file_kill_wait(int s,int m)182 int 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 
daemon_pid_file_create(void)214 int 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 
255 finish:
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 
daemon_pid_file_remove(void)272 int 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