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