xref: /freebsd/lib/libutil/tests/pidfile_test.c (revision 61e21613)
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
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 *
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 *
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 *
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 *
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 *
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 *
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
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