xref: /f-stack/tools/libutil/tests/pidfile_test.c (revision 22ce4aff)
11eaf0ac3Slogwang /*-
21eaf0ac3Slogwang  * Copyright (c) 2007-2009 Dag-Erling Coïdan Smørgrav
31eaf0ac3Slogwang  * All rights reserved.
41eaf0ac3Slogwang  *
51eaf0ac3Slogwang  * Redistribution and use in source and binary forms, with or without
61eaf0ac3Slogwang  * modification, are permitted provided that the following conditions
71eaf0ac3Slogwang  * are met:
81eaf0ac3Slogwang  * 1. Redistributions of source code must retain the above copyright
91eaf0ac3Slogwang  *    notice, this list of conditions and the following disclaimer
101eaf0ac3Slogwang  *    in this position and unchanged.
111eaf0ac3Slogwang  * 2. Redistributions in binary form must reproduce the above copyright
121eaf0ac3Slogwang  *    notice, this list of conditions and the following disclaimer in the
131eaf0ac3Slogwang  *    documentation and/or other materials provided with the distribution.
141eaf0ac3Slogwang  *
151eaf0ac3Slogwang  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
161eaf0ac3Slogwang  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
171eaf0ac3Slogwang  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
181eaf0ac3Slogwang  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
191eaf0ac3Slogwang  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
201eaf0ac3Slogwang  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
211eaf0ac3Slogwang  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
221eaf0ac3Slogwang  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
231eaf0ac3Slogwang  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
241eaf0ac3Slogwang  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
251eaf0ac3Slogwang  * SUCH DAMAGE.
261eaf0ac3Slogwang  */
271eaf0ac3Slogwang 
281eaf0ac3Slogwang #include <sys/cdefs.h>
291eaf0ac3Slogwang __FBSDID("$FreeBSD$");
301eaf0ac3Slogwang 
311eaf0ac3Slogwang #include <sys/param.h>
321eaf0ac3Slogwang #include <sys/wait.h>
33*22ce4affSfengbojiang #include <sys/event.h>
341eaf0ac3Slogwang 
351eaf0ac3Slogwang #include <fcntl.h>
361eaf0ac3Slogwang #include <errno.h>
371eaf0ac3Slogwang #include <signal.h>
381eaf0ac3Slogwang #include <stdint.h>
391eaf0ac3Slogwang #include <stdio.h>
401eaf0ac3Slogwang #include <stdlib.h>
411eaf0ac3Slogwang #include <string.h>
421eaf0ac3Slogwang #include <unistd.h>
431eaf0ac3Slogwang 
441eaf0ac3Slogwang #include <libutil.h>
451eaf0ac3Slogwang 
461eaf0ac3Slogwang /*
47*22ce4affSfengbojiang  * We need a signal handler so kill(2) will interrupt the child
48*22ce4affSfengbojiang  * instead of killing it.
491eaf0ac3Slogwang  */
501eaf0ac3Slogwang static void
signal_handler(int sig)511eaf0ac3Slogwang signal_handler(int sig)
521eaf0ac3Slogwang {
531eaf0ac3Slogwang 	(void)sig;
541eaf0ac3Slogwang }
551eaf0ac3Slogwang 
561eaf0ac3Slogwang /*
571eaf0ac3Slogwang  * Test that pidfile_open() can create a pidfile and that pidfile_write()
581eaf0ac3Slogwang  * can write to it.
591eaf0ac3Slogwang  */
601eaf0ac3Slogwang static const char *
test_pidfile_uncontested(void)611eaf0ac3Slogwang test_pidfile_uncontested(void)
621eaf0ac3Slogwang {
631eaf0ac3Slogwang 	const char *fn = "test_pidfile_uncontested";
641eaf0ac3Slogwang 	struct pidfh *pf;
651eaf0ac3Slogwang 	pid_t other = 0;
661eaf0ac3Slogwang 
671eaf0ac3Slogwang 	unlink(fn);
681eaf0ac3Slogwang 	pf = pidfile_open(fn, 0600, &other);
691eaf0ac3Slogwang 	if (pf == NULL && other != 0)
701eaf0ac3Slogwang 		return ("pidfile exists and is locked");
711eaf0ac3Slogwang 	if (pf == NULL)
721eaf0ac3Slogwang 		return (strerror(errno));
731eaf0ac3Slogwang 	if (pidfile_write(pf) != 0) {
741eaf0ac3Slogwang 		pidfile_close(pf);
751eaf0ac3Slogwang 		unlink(fn);
761eaf0ac3Slogwang 		return ("failed to write PID");
771eaf0ac3Slogwang 	}
781eaf0ac3Slogwang 	pidfile_close(pf);
791eaf0ac3Slogwang 	unlink(fn);
801eaf0ac3Slogwang 	return (NULL);
811eaf0ac3Slogwang }
821eaf0ac3Slogwang 
831eaf0ac3Slogwang /*
841eaf0ac3Slogwang  * Test that pidfile_open() locks against self.
851eaf0ac3Slogwang  */
861eaf0ac3Slogwang static const char *
test_pidfile_self(void)871eaf0ac3Slogwang test_pidfile_self(void)
881eaf0ac3Slogwang {
891eaf0ac3Slogwang 	const char *fn = "test_pidfile_self";
901eaf0ac3Slogwang 	struct pidfh *pf1, *pf2;
911eaf0ac3Slogwang 	pid_t other = 0;
921eaf0ac3Slogwang 	int serrno;
931eaf0ac3Slogwang 
941eaf0ac3Slogwang 	unlink(fn);
951eaf0ac3Slogwang 	pf1 = pidfile_open(fn, 0600, &other);
961eaf0ac3Slogwang 	if (pf1 == NULL && other != 0)
971eaf0ac3Slogwang 		return ("pidfile exists and is locked");
981eaf0ac3Slogwang 	if (pf1 == NULL)
991eaf0ac3Slogwang 		return (strerror(errno));
1001eaf0ac3Slogwang 	if (pidfile_write(pf1) != 0) {
1011eaf0ac3Slogwang 		serrno = errno;
1021eaf0ac3Slogwang 		pidfile_close(pf1);
1031eaf0ac3Slogwang 		unlink(fn);
1041eaf0ac3Slogwang 		return (strerror(serrno));
1051eaf0ac3Slogwang 	}
1061eaf0ac3Slogwang 	// second open should fail
1071eaf0ac3Slogwang 	pf2 = pidfile_open(fn, 0600, &other);
1081eaf0ac3Slogwang 	if (pf2 != NULL) {
1091eaf0ac3Slogwang 		pidfile_close(pf1);
1101eaf0ac3Slogwang 		pidfile_close(pf2);
1111eaf0ac3Slogwang 		unlink(fn);
1121eaf0ac3Slogwang 		return ("managed to opened pidfile twice");
1131eaf0ac3Slogwang 	}
1141eaf0ac3Slogwang 	if (other != getpid()) {
1151eaf0ac3Slogwang 		pidfile_close(pf1);
1161eaf0ac3Slogwang 		unlink(fn);
1171eaf0ac3Slogwang 		return ("pidfile contained wrong PID");
1181eaf0ac3Slogwang 	}
1191eaf0ac3Slogwang 	pidfile_close(pf1);
1201eaf0ac3Slogwang 	unlink(fn);
1211eaf0ac3Slogwang 	return (NULL);
1221eaf0ac3Slogwang }
1231eaf0ac3Slogwang 
1241eaf0ac3Slogwang /*
1251eaf0ac3Slogwang  * Common code for test_pidfile_{contested,inherited}.
1261eaf0ac3Slogwang  */
1271eaf0ac3Slogwang static const char *
common_test_pidfile_child(const char * fn,int parent_open)1281eaf0ac3Slogwang common_test_pidfile_child(const char *fn, int parent_open)
1291eaf0ac3Slogwang {
1301eaf0ac3Slogwang 	struct pidfh *pf = NULL;
1311eaf0ac3Slogwang 	pid_t other = 0, pid = 0;
1321eaf0ac3Slogwang 	int fd[2], serrno, status;
133*22ce4affSfengbojiang 	struct kevent event, ke;
1341eaf0ac3Slogwang 	char ch;
135*22ce4affSfengbojiang 	int kq;
1361eaf0ac3Slogwang 
1371eaf0ac3Slogwang 	unlink(fn);
1381eaf0ac3Slogwang 	if (pipe(fd) != 0)
1391eaf0ac3Slogwang 		return (strerror(errno));
1401eaf0ac3Slogwang 
1411eaf0ac3Slogwang 	if (parent_open) {
1421eaf0ac3Slogwang 		pf = pidfile_open(fn, 0600, &other);
1431eaf0ac3Slogwang 		if (pf == NULL && other != 0)
1441eaf0ac3Slogwang 			return ("pidfile exists and is locked");
1451eaf0ac3Slogwang 		if (pf == NULL)
1461eaf0ac3Slogwang 			return (strerror(errno));
1471eaf0ac3Slogwang 	}
1481eaf0ac3Slogwang 
1491eaf0ac3Slogwang 	pid = fork();
1501eaf0ac3Slogwang 	if (pid == -1)
1511eaf0ac3Slogwang 		return (strerror(errno));
1521eaf0ac3Slogwang 	if (pid == 0) {
1531eaf0ac3Slogwang 		// child
1541eaf0ac3Slogwang 		close(fd[0]);
1551eaf0ac3Slogwang 		signal(SIGINT, signal_handler);
1561eaf0ac3Slogwang 		if (!parent_open) {
1571eaf0ac3Slogwang 			pf = pidfile_open(fn, 0600, &other);
1581eaf0ac3Slogwang 			if (pf == NULL && other != 0)
1591eaf0ac3Slogwang 				return ("pidfile exists and is locked");
1601eaf0ac3Slogwang 			if (pf == NULL)
1611eaf0ac3Slogwang 				return (strerror(errno));
1621eaf0ac3Slogwang 		}
1631eaf0ac3Slogwang 		if (pidfile_write(pf) != 0) {
1641eaf0ac3Slogwang 			serrno = errno;
1651eaf0ac3Slogwang 			pidfile_close(pf);
1661eaf0ac3Slogwang 			unlink(fn);
1671eaf0ac3Slogwang 			return (strerror(serrno));
1681eaf0ac3Slogwang 		}
1691eaf0ac3Slogwang 		if (pf == NULL)
1701eaf0ac3Slogwang 			_exit(1);
1711eaf0ac3Slogwang 		if (pidfile_write(pf) != 0)
172*22ce4affSfengbojiang 			_exit(2);
173*22ce4affSfengbojiang 		kq = kqueue();
174*22ce4affSfengbojiang 		if (kq == -1)
175*22ce4affSfengbojiang 			_exit(3);
176*22ce4affSfengbojiang 		EV_SET(&ke, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
177*22ce4affSfengbojiang 		/* Attach event to the kqueue. */
178*22ce4affSfengbojiang 		if (kevent(kq, &ke, 1, NULL, 0, NULL) != 0)
179*22ce4affSfengbojiang 			_exit(4);
180*22ce4affSfengbojiang 		/* Inform the parent we are ready to receive SIGINT */
1811eaf0ac3Slogwang 		if (write(fd[1], "*", 1) != 1)
182*22ce4affSfengbojiang 			_exit(5);
183*22ce4affSfengbojiang 		/* Wait for SIGINT received */
184*22ce4affSfengbojiang 		if (kevent(kq, NULL, 0, &event, 1, NULL) != 1)
185*22ce4affSfengbojiang 			_exit(6);
1861eaf0ac3Slogwang 		_exit(0);
1871eaf0ac3Slogwang 	}
1881eaf0ac3Slogwang 	// parent
1891eaf0ac3Slogwang 	close(fd[1]);
1901eaf0ac3Slogwang 	if (pf)
1911eaf0ac3Slogwang 		pidfile_close(pf);
1921eaf0ac3Slogwang 
1931eaf0ac3Slogwang 	// wait for the child to signal us
1941eaf0ac3Slogwang 	if (read(fd[0], &ch, 1) != 1) {
1951eaf0ac3Slogwang 		serrno = errno;
1961eaf0ac3Slogwang 		unlink(fn);
1971eaf0ac3Slogwang 		kill(pid, SIGTERM);
1981eaf0ac3Slogwang 		errno = serrno;
1991eaf0ac3Slogwang 		return (strerror(errno));
2001eaf0ac3Slogwang 	}
2011eaf0ac3Slogwang 
2021eaf0ac3Slogwang 	// We shouldn't be able to lock the same pidfile as our child
2031eaf0ac3Slogwang 	pf = pidfile_open(fn, 0600, &other);
2041eaf0ac3Slogwang 	if (pf != NULL) {
2051eaf0ac3Slogwang 		pidfile_close(pf);
2061eaf0ac3Slogwang 		unlink(fn);
2071eaf0ac3Slogwang 		return ("managed to lock contested pidfile");
2081eaf0ac3Slogwang 	}
2091eaf0ac3Slogwang 
2101eaf0ac3Slogwang 	// Failed to lock, but not because it was contested
2111eaf0ac3Slogwang 	if (other == 0) {
2121eaf0ac3Slogwang 		unlink(fn);
2131eaf0ac3Slogwang 		return (strerror(errno));
2141eaf0ac3Slogwang 	}
2151eaf0ac3Slogwang 
2161eaf0ac3Slogwang 	// Locked by the wrong process
2171eaf0ac3Slogwang 	if (other != pid) {
2181eaf0ac3Slogwang 		unlink(fn);
2191eaf0ac3Slogwang 		return ("pidfile contained wrong PID");
2201eaf0ac3Slogwang 	}
2211eaf0ac3Slogwang 
2221eaf0ac3Slogwang 	// check our child's fate
2231eaf0ac3Slogwang 	if (pf)
2241eaf0ac3Slogwang 		pidfile_close(pf);
2251eaf0ac3Slogwang 	unlink(fn);
2261eaf0ac3Slogwang 	if (kill(pid, SIGINT) != 0)
2271eaf0ac3Slogwang 		return (strerror(errno));
2281eaf0ac3Slogwang 	if (waitpid(pid, &status, 0) == -1)
2291eaf0ac3Slogwang 		return (strerror(errno));
2301eaf0ac3Slogwang 	if (WIFSIGNALED(status))
2311eaf0ac3Slogwang 		return ("child caught signal");
2321eaf0ac3Slogwang 	if (WEXITSTATUS(status) != 0)
2331eaf0ac3Slogwang 		return ("child returned non-zero status");
2341eaf0ac3Slogwang 
2351eaf0ac3Slogwang 	// success
2361eaf0ac3Slogwang 	return (NULL);
2371eaf0ac3Slogwang }
2381eaf0ac3Slogwang 
2391eaf0ac3Slogwang /*
2401eaf0ac3Slogwang  * Test that pidfile_open() fails when attempting to open a pidfile that
2411eaf0ac3Slogwang  * is already locked, and that it returns the correct PID.
2421eaf0ac3Slogwang  */
2431eaf0ac3Slogwang static const char *
test_pidfile_contested(void)2441eaf0ac3Slogwang test_pidfile_contested(void)
2451eaf0ac3Slogwang {
2461eaf0ac3Slogwang 	const char *fn = "test_pidfile_contested";
2471eaf0ac3Slogwang 	const char *result;
2481eaf0ac3Slogwang 
2491eaf0ac3Slogwang 	result = common_test_pidfile_child(fn, 0);
2501eaf0ac3Slogwang 	return (result);
2511eaf0ac3Slogwang }
2521eaf0ac3Slogwang 
2531eaf0ac3Slogwang /*
2541eaf0ac3Slogwang  * Test that the pidfile lock is inherited.
2551eaf0ac3Slogwang  */
2561eaf0ac3Slogwang static const char *
test_pidfile_inherited(void)2571eaf0ac3Slogwang test_pidfile_inherited(void)
2581eaf0ac3Slogwang {
2591eaf0ac3Slogwang 	const char *fn = "test_pidfile_inherited";
2601eaf0ac3Slogwang 	const char *result;
2611eaf0ac3Slogwang 
2621eaf0ac3Slogwang 	result = common_test_pidfile_child(fn, 1);
2631eaf0ac3Slogwang 	return (result);
2641eaf0ac3Slogwang }
2651eaf0ac3Slogwang 
266*22ce4affSfengbojiang /*
267*22ce4affSfengbojiang  * Make sure we handle relative pidfile paths correctly.
268*22ce4affSfengbojiang  */
269*22ce4affSfengbojiang static const char *
test_pidfile_relative(void)270*22ce4affSfengbojiang test_pidfile_relative(void)
271*22ce4affSfengbojiang {
272*22ce4affSfengbojiang 	char path[PATH_MAX], pid[32], tmpdir[PATH_MAX];
273*22ce4affSfengbojiang 	struct pidfh *pfh;
274*22ce4affSfengbojiang 	int fd;
275*22ce4affSfengbojiang 
276*22ce4affSfengbojiang 	(void)snprintf(tmpdir, sizeof(tmpdir), "%s.XXXXXX", __func__);
277*22ce4affSfengbojiang 	if (mkdtemp(tmpdir) == NULL)
278*22ce4affSfengbojiang 		return (strerror(errno));
279*22ce4affSfengbojiang 	(void)snprintf(path, sizeof(path), "%s/pidfile", tmpdir);
280*22ce4affSfengbojiang 
281*22ce4affSfengbojiang 	pfh = pidfile_open(path, 0600, NULL);
282*22ce4affSfengbojiang 	if (pfh == NULL)
283*22ce4affSfengbojiang 		return (strerror(errno));
284*22ce4affSfengbojiang 	if (pidfile_write(pfh) != 0)
285*22ce4affSfengbojiang 		return (strerror(errno));
286*22ce4affSfengbojiang 	fd = open(path, O_RDONLY);
287*22ce4affSfengbojiang 	if (fd < 0)
288*22ce4affSfengbojiang 		return (strerror(errno));
289*22ce4affSfengbojiang 	if (read(fd, pid, sizeof(pid)) < 0)
290*22ce4affSfengbojiang 		return (strerror(errno));
291*22ce4affSfengbojiang 	if (atoi(pid) != getpid())
292*22ce4affSfengbojiang 		return ("pid mismatch");
293*22ce4affSfengbojiang 	if (close(fd) != 0)
294*22ce4affSfengbojiang 		return (strerror(errno));
295*22ce4affSfengbojiang 	if (pidfile_close(pfh) != 0)
296*22ce4affSfengbojiang 		return (strerror(errno));
297*22ce4affSfengbojiang 	return (NULL);
298*22ce4affSfengbojiang }
299*22ce4affSfengbojiang 
3001eaf0ac3Slogwang static struct test {
3011eaf0ac3Slogwang 	const char *name;
3021eaf0ac3Slogwang 	const char *(*func)(void);
3031eaf0ac3Slogwang } t[] = {
3041eaf0ac3Slogwang 	{ "pidfile_uncontested", test_pidfile_uncontested },
3051eaf0ac3Slogwang 	{ "pidfile_self", test_pidfile_self },
3061eaf0ac3Slogwang 	{ "pidfile_contested", test_pidfile_contested },
3071eaf0ac3Slogwang 	{ "pidfile_inherited", test_pidfile_inherited },
308*22ce4affSfengbojiang 	{ "pidfile_relative", test_pidfile_relative },
3091eaf0ac3Slogwang };
3101eaf0ac3Slogwang 
3111eaf0ac3Slogwang int
main(void)3121eaf0ac3Slogwang main(void)
3131eaf0ac3Slogwang {
3141eaf0ac3Slogwang 	const char *result;
3151eaf0ac3Slogwang 	int i, nt;
3161eaf0ac3Slogwang 
3171eaf0ac3Slogwang 	nt = sizeof(t) / sizeof(*t);
3181eaf0ac3Slogwang 	printf("1..%d\n", nt);
3191eaf0ac3Slogwang 	for (i = 0; i < nt; ++i) {
3201eaf0ac3Slogwang 		if ((result = t[i].func()) != NULL)
3211eaf0ac3Slogwang 			printf("not ok %d - %s # %s\n", i + 1,
3221eaf0ac3Slogwang 			    t[i].name, result);
3231eaf0ac3Slogwang 		else
3241eaf0ac3Slogwang 			printf("ok %d - %s\n", i + 1,
3251eaf0ac3Slogwang 			    t[i].name);
3261eaf0ac3Slogwang 	}
3271eaf0ac3Slogwang 	exit(0);
3281eaf0ac3Slogwang }
329