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 */
47static void
48signal_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 */
57static const char *
58test_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 */
83static const char *
84test_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 */
124static const char *
125common_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 */
240static const char *
241test_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 */
253static const char *
254test_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 */
266static const char *
267test_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
298static 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
309int
310main(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