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