1 /*-
2 * Copyright (c) 2007-2009 Dag-Erling Smørgrav
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer
10 * in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/param.h>
29 #include <sys/wait.h>
30 #include <sys/event.h>
31
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <signal.h>
35 #include <stdint.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40
41 #include <libutil.h>
42
43 /*
44 * We need a signal handler so kill(2) will interrupt the child
45 * instead of killing it.
46 */
47 static void
signal_handler(int sig)48 signal_handler(int sig)
49 {
50 (void)sig;
51 }
52
53 /*
54 * Test that pidfile_open() can create a pidfile and that pidfile_write()
55 * can write to it.
56 */
57 static const char *
test_pidfile_uncontested(void)58 test_pidfile_uncontested(void)
59 {
60 const char *fn = "test_pidfile_uncontested";
61 struct pidfh *pf;
62 pid_t other = 0;
63
64 unlink(fn);
65 pf = pidfile_open(fn, 0600, &other);
66 if (pf == NULL && other != 0)
67 return ("pidfile exists and is locked");
68 if (pf == NULL)
69 return (strerror(errno));
70 if (pidfile_write(pf) != 0) {
71 pidfile_close(pf);
72 unlink(fn);
73 return ("failed to write PID");
74 }
75 pidfile_close(pf);
76 unlink(fn);
77 return (NULL);
78 }
79
80 /*
81 * Test that pidfile_open() locks against self.
82 */
83 static const char *
test_pidfile_self(void)84 test_pidfile_self(void)
85 {
86 const char *fn = "test_pidfile_self";
87 struct pidfh *pf1, *pf2;
88 pid_t other = 0;
89 int serrno;
90
91 unlink(fn);
92 pf1 = pidfile_open(fn, 0600, &other);
93 if (pf1 == NULL && other != 0)
94 return ("pidfile exists and is locked");
95 if (pf1 == NULL)
96 return (strerror(errno));
97 if (pidfile_write(pf1) != 0) {
98 serrno = errno;
99 pidfile_close(pf1);
100 unlink(fn);
101 return (strerror(serrno));
102 }
103 // second open should fail
104 pf2 = pidfile_open(fn, 0600, &other);
105 if (pf2 != NULL) {
106 pidfile_close(pf1);
107 pidfile_close(pf2);
108 unlink(fn);
109 return ("managed to opened pidfile twice");
110 }
111 if (other != getpid()) {
112 pidfile_close(pf1);
113 unlink(fn);
114 return ("pidfile contained wrong PID");
115 }
116 pidfile_close(pf1);
117 unlink(fn);
118 return (NULL);
119 }
120
121 /*
122 * Common code for test_pidfile_{contested,inherited}.
123 */
124 static const char *
common_test_pidfile_child(const char * fn,int parent_open)125 common_test_pidfile_child(const char *fn, int parent_open)
126 {
127 struct pidfh *pf = NULL;
128 pid_t other = 0, pid = 0;
129 int fd[2], serrno, status;
130 struct kevent event, ke;
131 char ch;
132 int kq;
133
134 unlink(fn);
135 if (pipe(fd) != 0)
136 return (strerror(errno));
137
138 if (parent_open) {
139 pf = pidfile_open(fn, 0600, &other);
140 if (pf == NULL && other != 0)
141 return ("pidfile exists and is locked");
142 if (pf == NULL)
143 return (strerror(errno));
144 }
145
146 pid = fork();
147 if (pid == -1)
148 return (strerror(errno));
149 if (pid == 0) {
150 // child
151 close(fd[0]);
152 signal(SIGINT, signal_handler);
153 if (!parent_open) {
154 pf = pidfile_open(fn, 0600, &other);
155 if (pf == NULL && other != 0)
156 return ("pidfile exists and is locked");
157 if (pf == NULL)
158 return (strerror(errno));
159 }
160 if (pidfile_write(pf) != 0) {
161 serrno = errno;
162 pidfile_close(pf);
163 unlink(fn);
164 return (strerror(serrno));
165 }
166 if (pf == NULL)
167 _exit(1);
168 if (pidfile_write(pf) != 0)
169 _exit(2);
170 kq = kqueue();
171 if (kq == -1)
172 _exit(3);
173 EV_SET(&ke, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
174 /* Attach event to the kqueue. */
175 if (kevent(kq, &ke, 1, NULL, 0, NULL) != 0)
176 _exit(4);
177 /* Inform the parent we are ready to receive SIGINT */
178 if (write(fd[1], "*", 1) != 1)
179 _exit(5);
180 /* Wait for SIGINT received */
181 if (kevent(kq, NULL, 0, &event, 1, NULL) != 1)
182 _exit(6);
183 _exit(0);
184 }
185 // parent
186 close(fd[1]);
187 if (pf)
188 pidfile_close(pf);
189
190 // wait for the child to signal us
191 if (read(fd[0], &ch, 1) != 1) {
192 serrno = errno;
193 unlink(fn);
194 kill(pid, SIGTERM);
195 errno = serrno;
196 return (strerror(errno));
197 }
198
199 // We shouldn't be able to lock the same pidfile as our child
200 pf = pidfile_open(fn, 0600, &other);
201 if (pf != NULL) {
202 pidfile_close(pf);
203 unlink(fn);
204 return ("managed to lock contested pidfile");
205 }
206
207 // Failed to lock, but not because it was contested
208 if (other == 0) {
209 unlink(fn);
210 return (strerror(errno));
211 }
212
213 // Locked by the wrong process
214 if (other != pid) {
215 unlink(fn);
216 return ("pidfile contained wrong PID");
217 }
218
219 // check our child's fate
220 if (pf)
221 pidfile_close(pf);
222 unlink(fn);
223 if (kill(pid, SIGINT) != 0)
224 return (strerror(errno));
225 if (waitpid(pid, &status, 0) == -1)
226 return (strerror(errno));
227 if (WIFSIGNALED(status))
228 return ("child caught signal");
229 if (WEXITSTATUS(status) != 0)
230 return ("child returned non-zero status");
231
232 // success
233 return (NULL);
234 }
235
236 /*
237 * Test that pidfile_open() fails when attempting to open a pidfile that
238 * is already locked, and that it returns the correct PID.
239 */
240 static const char *
test_pidfile_contested(void)241 test_pidfile_contested(void)
242 {
243 const char *fn = "test_pidfile_contested";
244 const char *result;
245
246 result = common_test_pidfile_child(fn, 0);
247 return (result);
248 }
249
250 /*
251 * Test that the pidfile lock is inherited.
252 */
253 static const char *
test_pidfile_inherited(void)254 test_pidfile_inherited(void)
255 {
256 const char *fn = "test_pidfile_inherited";
257 const char *result;
258
259 result = common_test_pidfile_child(fn, 1);
260 return (result);
261 }
262
263 /*
264 * Make sure we handle relative pidfile paths correctly.
265 */
266 static const char *
test_pidfile_relative(void)267 test_pidfile_relative(void)
268 {
269 char path[PATH_MAX], pid[32], tmpdir[PATH_MAX];
270 struct pidfh *pfh;
271 int fd;
272
273 (void)snprintf(tmpdir, sizeof(tmpdir), "%s.XXXXXX", __func__);
274 if (mkdtemp(tmpdir) == NULL)
275 return (strerror(errno));
276 (void)snprintf(path, sizeof(path), "%s/pidfile", tmpdir);
277
278 pfh = pidfile_open(path, 0600, NULL);
279 if (pfh == NULL)
280 return (strerror(errno));
281 if (pidfile_write(pfh) != 0)
282 return (strerror(errno));
283 fd = open(path, O_RDONLY);
284 if (fd < 0)
285 return (strerror(errno));
286 memset(pid, 0, sizeof(pid));
287 if (read(fd, pid, sizeof(pid) - 1) < 0)
288 return (strerror(errno));
289 if (atoi(pid) != getpid())
290 return ("pid mismatch");
291 if (close(fd) != 0)
292 return (strerror(errno));
293 if (pidfile_close(pfh) != 0)
294 return (strerror(errno));
295 return (NULL);
296 }
297
298 static struct test {
299 const char *name;
300 const char *(*func)(void);
301 } t[] = {
302 { "pidfile_uncontested", test_pidfile_uncontested },
303 { "pidfile_self", test_pidfile_self },
304 { "pidfile_contested", test_pidfile_contested },
305 { "pidfile_inherited", test_pidfile_inherited },
306 { "pidfile_relative", test_pidfile_relative },
307 };
308
309 int
main(void)310 main(void)
311 {
312 const char *result;
313 int i, nt;
314
315 nt = sizeof(t) / sizeof(*t);
316 printf("1..%d\n", nt);
317 for (i = 0; i < nt; ++i) {
318 if ((result = t[i].func()) != NULL)
319 printf("not ok %d - %s # %s\n", i + 1,
320 t[i].name, result);
321 else
322 printf("ok %d - %s\n", i + 1,
323 t[i].name);
324 }
325 exit(0);
326 }
327