1/*-
2 * Copyright (c) 2007-2009 Dag-Erling Co��dan 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/cdefs.h>
29__FBSDID("$FreeBSD$");
30
31#include <sys/param.h>
32#include <sys/wait.h>
33#include <sys/event.h>
34
35#include <fcntl.h>
36#include <errno.h>
37#include <signal.h>
38#include <stdint.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <unistd.h>
43
44#include <libutil.h>
45
46/*
47 * We need a signal handler so kill(2) will interrupt the child
48 * instead of killing it.
49 */
50static void
51signal_handler(int sig)
52{
53	(void)sig;
54}
55
56/*
57 * Test that pidfile_open() can create a pidfile and that pidfile_write()
58 * can write to it.
59 */
60static const char *
61test_pidfile_uncontested(void)
62{
63	const char *fn = "test_pidfile_uncontested";
64	struct pidfh *pf;
65	pid_t other = 0;
66
67	unlink(fn);
68	pf = pidfile_open(fn, 0600, &other);
69	if (pf == NULL && other != 0)
70		return ("pidfile exists and is locked");
71	if (pf == NULL)
72		return (strerror(errno));
73	if (pidfile_write(pf) != 0) {
74		pidfile_close(pf);
75		unlink(fn);
76		return ("failed to write PID");
77	}
78	pidfile_close(pf);
79	unlink(fn);
80	return (NULL);
81}
82
83/*
84 * Test that pidfile_open() locks against self.
85 */
86static const char *
87test_pidfile_self(void)
88{
89	const char *fn = "test_pidfile_self";
90	struct pidfh *pf1, *pf2;
91	pid_t other = 0;
92	int serrno;
93
94	unlink(fn);
95	pf1 = pidfile_open(fn, 0600, &other);
96	if (pf1 == NULL && other != 0)
97		return ("pidfile exists and is locked");
98	if (pf1 == NULL)
99		return (strerror(errno));
100	if (pidfile_write(pf1) != 0) {
101		serrno = errno;
102		pidfile_close(pf1);
103		unlink(fn);
104		return (strerror(serrno));
105	}
106	// second open should fail
107	pf2 = pidfile_open(fn, 0600, &other);
108	if (pf2 != NULL) {
109		pidfile_close(pf1);
110		pidfile_close(pf2);
111		unlink(fn);
112		return ("managed to opened pidfile twice");
113	}
114	if (other != getpid()) {
115		pidfile_close(pf1);
116		unlink(fn);
117		return ("pidfile contained wrong PID");
118	}
119	pidfile_close(pf1);
120	unlink(fn);
121	return (NULL);
122}
123
124/*
125 * Common code for test_pidfile_{contested,inherited}.
126 */
127static const char *
128common_test_pidfile_child(const char *fn, int parent_open)
129{
130	struct pidfh *pf = NULL;
131	pid_t other = 0, pid = 0;
132	int fd[2], serrno, status;
133	struct kevent event, ke;
134	char ch;
135	int kq;
136
137	unlink(fn);
138	if (pipe(fd) != 0)
139		return (strerror(errno));
140
141	if (parent_open) {
142		pf = pidfile_open(fn, 0600, &other);
143		if (pf == NULL && other != 0)
144			return ("pidfile exists and is locked");
145		if (pf == NULL)
146			return (strerror(errno));
147	}
148
149	pid = fork();
150	if (pid == -1)
151		return (strerror(errno));
152	if (pid == 0) {
153		// child
154		close(fd[0]);
155		signal(SIGINT, signal_handler);
156		if (!parent_open) {
157			pf = pidfile_open(fn, 0600, &other);
158			if (pf == NULL && other != 0)
159				return ("pidfile exists and is locked");
160			if (pf == NULL)
161				return (strerror(errno));
162		}
163		if (pidfile_write(pf) != 0) {
164			serrno = errno;
165			pidfile_close(pf);
166			unlink(fn);
167			return (strerror(serrno));
168		}
169		if (pf == NULL)
170			_exit(1);
171		if (pidfile_write(pf) != 0)
172			_exit(2);
173		kq = kqueue();
174		if (kq == -1)
175			_exit(3);
176		EV_SET(&ke, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
177		/* Attach event to the kqueue. */
178		if (kevent(kq, &ke, 1, NULL, 0, NULL) != 0)
179			_exit(4);
180		/* Inform the parent we are ready to receive SIGINT */
181		if (write(fd[1], "*", 1) != 1)
182			_exit(5);
183		/* Wait for SIGINT received */
184		if (kevent(kq, NULL, 0, &event, 1, NULL) != 1)
185			_exit(6);
186		_exit(0);
187	}
188	// parent
189	close(fd[1]);
190	if (pf)
191		pidfile_close(pf);
192
193	// wait for the child to signal us
194	if (read(fd[0], &ch, 1) != 1) {
195		serrno = errno;
196		unlink(fn);
197		kill(pid, SIGTERM);
198		errno = serrno;
199		return (strerror(errno));
200	}
201
202	// We shouldn't be able to lock the same pidfile as our child
203	pf = pidfile_open(fn, 0600, &other);
204	if (pf != NULL) {
205		pidfile_close(pf);
206		unlink(fn);
207		return ("managed to lock contested pidfile");
208	}
209
210	// Failed to lock, but not because it was contested
211	if (other == 0) {
212		unlink(fn);
213		return (strerror(errno));
214	}
215
216	// Locked by the wrong process
217	if (other != pid) {
218		unlink(fn);
219		return ("pidfile contained wrong PID");
220	}
221
222	// check our child's fate
223	if (pf)
224		pidfile_close(pf);
225	unlink(fn);
226	if (kill(pid, SIGINT) != 0)
227		return (strerror(errno));
228	if (waitpid(pid, &status, 0) == -1)
229		return (strerror(errno));
230	if (WIFSIGNALED(status))
231		return ("child caught signal");
232	if (WEXITSTATUS(status) != 0)
233		return ("child returned non-zero status");
234
235	// success
236	return (NULL);
237}
238
239/*
240 * Test that pidfile_open() fails when attempting to open a pidfile that
241 * is already locked, and that it returns the correct PID.
242 */
243static const char *
244test_pidfile_contested(void)
245{
246	const char *fn = "test_pidfile_contested";
247	const char *result;
248
249	result = common_test_pidfile_child(fn, 0);
250	return (result);
251}
252
253/*
254 * Test that the pidfile lock is inherited.
255 */
256static const char *
257test_pidfile_inherited(void)
258{
259	const char *fn = "test_pidfile_inherited";
260	const char *result;
261
262	result = common_test_pidfile_child(fn, 1);
263	return (result);
264}
265
266/*
267 * Make sure we handle relative pidfile paths correctly.
268 */
269static const char *
270test_pidfile_relative(void)
271{
272	char path[PATH_MAX], pid[32], tmpdir[PATH_MAX];
273	struct pidfh *pfh;
274	int fd;
275
276	(void)snprintf(tmpdir, sizeof(tmpdir), "%s.XXXXXX", __func__);
277	if (mkdtemp(tmpdir) == NULL)
278		return (strerror(errno));
279	(void)snprintf(path, sizeof(path), "%s/pidfile", tmpdir);
280
281	pfh = pidfile_open(path, 0600, NULL);
282	if (pfh == NULL)
283		return (strerror(errno));
284	if (pidfile_write(pfh) != 0)
285		return (strerror(errno));
286	fd = open(path, O_RDONLY);
287	if (fd < 0)
288		return (strerror(errno));
289	if (read(fd, pid, sizeof(pid)) < 0)
290		return (strerror(errno));
291	if (atoi(pid) != getpid())
292		return ("pid mismatch");
293	if (close(fd) != 0)
294		return (strerror(errno));
295	if (pidfile_close(pfh) != 0)
296		return (strerror(errno));
297	return (NULL);
298}
299
300static struct test {
301	const char *name;
302	const char *(*func)(void);
303} t[] = {
304	{ "pidfile_uncontested", test_pidfile_uncontested },
305	{ "pidfile_self", test_pidfile_self },
306	{ "pidfile_contested", test_pidfile_contested },
307	{ "pidfile_inherited", test_pidfile_inherited },
308	{ "pidfile_relative", test_pidfile_relative },
309};
310
311int
312main(void)
313{
314	const char *result;
315	int i, nt;
316
317	nt = sizeof(t) / sizeof(*t);
318	printf("1..%d\n", nt);
319	for (i = 0; i < nt; ++i) {
320		if ((result = t[i].func()) != NULL)
321			printf("not ok %d - %s # %s\n", i + 1,
322			    t[i].name, result);
323		else
324			printf("ok %d - %s\n", i + 1,
325			    t[i].name);
326	}
327	exit(0);
328}
329