xref: /freebsd-src/lib/libutil/tests/pidfile_test.c (revision a2f733abcff64628b7771a47089628b7327a88bd)
15aa45fcbSEnji Cooper /*-
2*e738085bSDag-Erling Smørgrav  * Copyright (c) 2007-2009 Dag-Erling Smørgrav
35aa45fcbSEnji Cooper  * All rights reserved.
45aa45fcbSEnji Cooper  *
55aa45fcbSEnji Cooper  * Redistribution and use in source and binary forms, with or without
65aa45fcbSEnji Cooper  * modification, are permitted provided that the following conditions
75aa45fcbSEnji Cooper  * are met:
85aa45fcbSEnji Cooper  * 1. Redistributions of source code must retain the above copyright
95aa45fcbSEnji Cooper  *    notice, this list of conditions and the following disclaimer
105aa45fcbSEnji Cooper  *    in this position and unchanged.
115aa45fcbSEnji Cooper  * 2. Redistributions in binary form must reproduce the above copyright
125aa45fcbSEnji Cooper  *    notice, this list of conditions and the following disclaimer in the
135aa45fcbSEnji Cooper  *    documentation and/or other materials provided with the distribution.
145aa45fcbSEnji Cooper  *
155aa45fcbSEnji Cooper  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
165aa45fcbSEnji Cooper  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
175aa45fcbSEnji Cooper  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
185aa45fcbSEnji Cooper  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
195aa45fcbSEnji Cooper  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
205aa45fcbSEnji Cooper  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
215aa45fcbSEnji Cooper  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
225aa45fcbSEnji Cooper  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
235aa45fcbSEnji Cooper  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
245aa45fcbSEnji Cooper  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
255aa45fcbSEnji Cooper  * SUCH DAMAGE.
265aa45fcbSEnji Cooper  */
275aa45fcbSEnji Cooper 
285aa45fcbSEnji Cooper #include <sys/param.h>
295aa45fcbSEnji Cooper #include <sys/wait.h>
30dab6d6fbSRuslan Bukin #include <sys/event.h>
315aa45fcbSEnji Cooper 
325aa45fcbSEnji Cooper #include <fcntl.h>
335aa45fcbSEnji Cooper #include <errno.h>
345aa45fcbSEnji Cooper #include <signal.h>
355aa45fcbSEnji Cooper #include <stdint.h>
365aa45fcbSEnji Cooper #include <stdio.h>
375aa45fcbSEnji Cooper #include <stdlib.h>
385aa45fcbSEnji Cooper #include <string.h>
395aa45fcbSEnji Cooper #include <unistd.h>
405aa45fcbSEnji Cooper 
415aa45fcbSEnji Cooper #include <libutil.h>
425aa45fcbSEnji Cooper 
435aa45fcbSEnji Cooper /*
44dab6d6fbSRuslan Bukin  * We need a signal handler so kill(2) will interrupt the child
45dab6d6fbSRuslan Bukin  * instead of killing it.
465aa45fcbSEnji Cooper  */
475aa45fcbSEnji Cooper static void
signal_handler(int sig)485aa45fcbSEnji Cooper signal_handler(int sig)
495aa45fcbSEnji Cooper {
505aa45fcbSEnji Cooper 	(void)sig;
515aa45fcbSEnji Cooper }
525aa45fcbSEnji Cooper 
535aa45fcbSEnji Cooper /*
545aa45fcbSEnji Cooper  * Test that pidfile_open() can create a pidfile and that pidfile_write()
555aa45fcbSEnji Cooper  * can write to it.
565aa45fcbSEnji Cooper  */
575aa45fcbSEnji Cooper static const char *
test_pidfile_uncontested(void)585aa45fcbSEnji Cooper test_pidfile_uncontested(void)
595aa45fcbSEnji Cooper {
605aa45fcbSEnji Cooper 	const char *fn = "test_pidfile_uncontested";
615aa45fcbSEnji Cooper 	struct pidfh *pf;
625aa45fcbSEnji Cooper 	pid_t other = 0;
635aa45fcbSEnji Cooper 
645aa45fcbSEnji Cooper 	unlink(fn);
655aa45fcbSEnji Cooper 	pf = pidfile_open(fn, 0600, &other);
665aa45fcbSEnji Cooper 	if (pf == NULL && other != 0)
675aa45fcbSEnji Cooper 		return ("pidfile exists and is locked");
685aa45fcbSEnji Cooper 	if (pf == NULL)
695aa45fcbSEnji Cooper 		return (strerror(errno));
705aa45fcbSEnji Cooper 	if (pidfile_write(pf) != 0) {
715aa45fcbSEnji Cooper 		pidfile_close(pf);
725aa45fcbSEnji Cooper 		unlink(fn);
735aa45fcbSEnji Cooper 		return ("failed to write PID");
745aa45fcbSEnji Cooper 	}
755aa45fcbSEnji Cooper 	pidfile_close(pf);
765aa45fcbSEnji Cooper 	unlink(fn);
775aa45fcbSEnji Cooper 	return (NULL);
785aa45fcbSEnji Cooper }
795aa45fcbSEnji Cooper 
805aa45fcbSEnji Cooper /*
815aa45fcbSEnji Cooper  * Test that pidfile_open() locks against self.
825aa45fcbSEnji Cooper  */
835aa45fcbSEnji Cooper static const char *
test_pidfile_self(void)845aa45fcbSEnji Cooper test_pidfile_self(void)
855aa45fcbSEnji Cooper {
865aa45fcbSEnji Cooper 	const char *fn = "test_pidfile_self";
875aa45fcbSEnji Cooper 	struct pidfh *pf1, *pf2;
885aa45fcbSEnji Cooper 	pid_t other = 0;
895aa45fcbSEnji Cooper 	int serrno;
905aa45fcbSEnji Cooper 
915aa45fcbSEnji Cooper 	unlink(fn);
925aa45fcbSEnji Cooper 	pf1 = pidfile_open(fn, 0600, &other);
935aa45fcbSEnji Cooper 	if (pf1 == NULL && other != 0)
945aa45fcbSEnji Cooper 		return ("pidfile exists and is locked");
955aa45fcbSEnji Cooper 	if (pf1 == NULL)
965aa45fcbSEnji Cooper 		return (strerror(errno));
975aa45fcbSEnji Cooper 	if (pidfile_write(pf1) != 0) {
985aa45fcbSEnji Cooper 		serrno = errno;
995aa45fcbSEnji Cooper 		pidfile_close(pf1);
1005aa45fcbSEnji Cooper 		unlink(fn);
1015aa45fcbSEnji Cooper 		return (strerror(serrno));
1025aa45fcbSEnji Cooper 	}
1035aa45fcbSEnji Cooper 	// second open should fail
1045aa45fcbSEnji Cooper 	pf2 = pidfile_open(fn, 0600, &other);
1055aa45fcbSEnji Cooper 	if (pf2 != NULL) {
1065aa45fcbSEnji Cooper 		pidfile_close(pf1);
1075aa45fcbSEnji Cooper 		pidfile_close(pf2);
1085aa45fcbSEnji Cooper 		unlink(fn);
1095aa45fcbSEnji Cooper 		return ("managed to opened pidfile twice");
1105aa45fcbSEnji Cooper 	}
1115aa45fcbSEnji Cooper 	if (other != getpid()) {
1125aa45fcbSEnji Cooper 		pidfile_close(pf1);
1135aa45fcbSEnji Cooper 		unlink(fn);
1145aa45fcbSEnji Cooper 		return ("pidfile contained wrong PID");
1155aa45fcbSEnji Cooper 	}
1165aa45fcbSEnji Cooper 	pidfile_close(pf1);
1175aa45fcbSEnji Cooper 	unlink(fn);
1185aa45fcbSEnji Cooper 	return (NULL);
1195aa45fcbSEnji Cooper }
1205aa45fcbSEnji Cooper 
1215aa45fcbSEnji Cooper /*
1225aa45fcbSEnji Cooper  * Common code for test_pidfile_{contested,inherited}.
1235aa45fcbSEnji Cooper  */
1245aa45fcbSEnji Cooper static const char *
common_test_pidfile_child(const char * fn,int parent_open)1255aa45fcbSEnji Cooper common_test_pidfile_child(const char *fn, int parent_open)
1265aa45fcbSEnji Cooper {
1275aa45fcbSEnji Cooper 	struct pidfh *pf = NULL;
1285aa45fcbSEnji Cooper 	pid_t other = 0, pid = 0;
1295aa45fcbSEnji Cooper 	int fd[2], serrno, status;
130dab6d6fbSRuslan Bukin 	struct kevent event, ke;
1315aa45fcbSEnji Cooper 	char ch;
132dab6d6fbSRuslan Bukin 	int kq;
1335aa45fcbSEnji Cooper 
1345aa45fcbSEnji Cooper 	unlink(fn);
1355aa45fcbSEnji Cooper 	if (pipe(fd) != 0)
1365aa45fcbSEnji Cooper 		return (strerror(errno));
1375aa45fcbSEnji Cooper 
1385aa45fcbSEnji Cooper 	if (parent_open) {
1395aa45fcbSEnji Cooper 		pf = pidfile_open(fn, 0600, &other);
1405aa45fcbSEnji Cooper 		if (pf == NULL && other != 0)
1415aa45fcbSEnji Cooper 			return ("pidfile exists and is locked");
1425aa45fcbSEnji Cooper 		if (pf == NULL)
1435aa45fcbSEnji Cooper 			return (strerror(errno));
1445aa45fcbSEnji Cooper 	}
1455aa45fcbSEnji Cooper 
1465aa45fcbSEnji Cooper 	pid = fork();
1475aa45fcbSEnji Cooper 	if (pid == -1)
1485aa45fcbSEnji Cooper 		return (strerror(errno));
1495aa45fcbSEnji Cooper 	if (pid == 0) {
1505aa45fcbSEnji Cooper 		// child
1515aa45fcbSEnji Cooper 		close(fd[0]);
1525aa45fcbSEnji Cooper 		signal(SIGINT, signal_handler);
1535aa45fcbSEnji Cooper 		if (!parent_open) {
1545aa45fcbSEnji Cooper 			pf = pidfile_open(fn, 0600, &other);
1555aa45fcbSEnji Cooper 			if (pf == NULL && other != 0)
1565aa45fcbSEnji Cooper 				return ("pidfile exists and is locked");
1575aa45fcbSEnji Cooper 			if (pf == NULL)
1585aa45fcbSEnji Cooper 				return (strerror(errno));
1595aa45fcbSEnji Cooper 		}
1605aa45fcbSEnji Cooper 		if (pidfile_write(pf) != 0) {
1615aa45fcbSEnji Cooper 			serrno = errno;
1625aa45fcbSEnji Cooper 			pidfile_close(pf);
1635aa45fcbSEnji Cooper 			unlink(fn);
1645aa45fcbSEnji Cooper 			return (strerror(serrno));
1655aa45fcbSEnji Cooper 		}
1665aa45fcbSEnji Cooper 		if (pf == NULL)
1675aa45fcbSEnji Cooper 			_exit(1);
1685aa45fcbSEnji Cooper 		if (pidfile_write(pf) != 0)
169dab6d6fbSRuslan Bukin 			_exit(2);
170dab6d6fbSRuslan Bukin 		kq = kqueue();
171dab6d6fbSRuslan Bukin 		if (kq == -1)
172dab6d6fbSRuslan Bukin 			_exit(3);
173dab6d6fbSRuslan Bukin 		EV_SET(&ke, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
174dab6d6fbSRuslan Bukin 		/* Attach event to the kqueue. */
175dab6d6fbSRuslan Bukin 		if (kevent(kq, &ke, 1, NULL, 0, NULL) != 0)
176dab6d6fbSRuslan Bukin 			_exit(4);
177dab6d6fbSRuslan Bukin 		/* Inform the parent we are ready to receive SIGINT */
1785aa45fcbSEnji Cooper 		if (write(fd[1], "*", 1) != 1)
179dab6d6fbSRuslan Bukin 			_exit(5);
180dab6d6fbSRuslan Bukin 		/* Wait for SIGINT received */
181dab6d6fbSRuslan Bukin 		if (kevent(kq, NULL, 0, &event, 1, NULL) != 1)
182dab6d6fbSRuslan Bukin 			_exit(6);
1835aa45fcbSEnji Cooper 		_exit(0);
1845aa45fcbSEnji Cooper 	}
1855aa45fcbSEnji Cooper 	// parent
1865aa45fcbSEnji Cooper 	close(fd[1]);
1875aa45fcbSEnji Cooper 	if (pf)
1885aa45fcbSEnji Cooper 		pidfile_close(pf);
1895aa45fcbSEnji Cooper 
1905aa45fcbSEnji Cooper 	// wait for the child to signal us
1915aa45fcbSEnji Cooper 	if (read(fd[0], &ch, 1) != 1) {
1925aa45fcbSEnji Cooper 		serrno = errno;
1935aa45fcbSEnji Cooper 		unlink(fn);
1945aa45fcbSEnji Cooper 		kill(pid, SIGTERM);
1955aa45fcbSEnji Cooper 		errno = serrno;
1965aa45fcbSEnji Cooper 		return (strerror(errno));
1975aa45fcbSEnji Cooper 	}
1985aa45fcbSEnji Cooper 
1995aa45fcbSEnji Cooper 	// We shouldn't be able to lock the same pidfile as our child
2005aa45fcbSEnji Cooper 	pf = pidfile_open(fn, 0600, &other);
2015aa45fcbSEnji Cooper 	if (pf != NULL) {
2025aa45fcbSEnji Cooper 		pidfile_close(pf);
2035aa45fcbSEnji Cooper 		unlink(fn);
2045aa45fcbSEnji Cooper 		return ("managed to lock contested pidfile");
2055aa45fcbSEnji Cooper 	}
2065aa45fcbSEnji Cooper 
2075aa45fcbSEnji Cooper 	// Failed to lock, but not because it was contested
2085aa45fcbSEnji Cooper 	if (other == 0) {
2095aa45fcbSEnji Cooper 		unlink(fn);
2105aa45fcbSEnji Cooper 		return (strerror(errno));
2115aa45fcbSEnji Cooper 	}
2125aa45fcbSEnji Cooper 
2135aa45fcbSEnji Cooper 	// Locked by the wrong process
2145aa45fcbSEnji Cooper 	if (other != pid) {
2155aa45fcbSEnji Cooper 		unlink(fn);
2165aa45fcbSEnji Cooper 		return ("pidfile contained wrong PID");
2175aa45fcbSEnji Cooper 	}
2185aa45fcbSEnji Cooper 
2195aa45fcbSEnji Cooper 	// check our child's fate
2205aa45fcbSEnji Cooper 	if (pf)
2215aa45fcbSEnji Cooper 		pidfile_close(pf);
2225aa45fcbSEnji Cooper 	unlink(fn);
2235aa45fcbSEnji Cooper 	if (kill(pid, SIGINT) != 0)
2245aa45fcbSEnji Cooper 		return (strerror(errno));
2255aa45fcbSEnji Cooper 	if (waitpid(pid, &status, 0) == -1)
2265aa45fcbSEnji Cooper 		return (strerror(errno));
2275aa45fcbSEnji Cooper 	if (WIFSIGNALED(status))
2285aa45fcbSEnji Cooper 		return ("child caught signal");
2295aa45fcbSEnji Cooper 	if (WEXITSTATUS(status) != 0)
2305aa45fcbSEnji Cooper 		return ("child returned non-zero status");
2315aa45fcbSEnji Cooper 
2325aa45fcbSEnji Cooper 	// success
2335aa45fcbSEnji Cooper 	return (NULL);
2345aa45fcbSEnji Cooper }
2355aa45fcbSEnji Cooper 
2365aa45fcbSEnji Cooper /*
2375aa45fcbSEnji Cooper  * Test that pidfile_open() fails when attempting to open a pidfile that
2385aa45fcbSEnji Cooper  * is already locked, and that it returns the correct PID.
2395aa45fcbSEnji Cooper  */
2405aa45fcbSEnji Cooper static const char *
test_pidfile_contested(void)2415aa45fcbSEnji Cooper test_pidfile_contested(void)
2425aa45fcbSEnji Cooper {
2435aa45fcbSEnji Cooper 	const char *fn = "test_pidfile_contested";
2445aa45fcbSEnji Cooper 	const char *result;
2455aa45fcbSEnji Cooper 
2465aa45fcbSEnji Cooper 	result = common_test_pidfile_child(fn, 0);
2475aa45fcbSEnji Cooper 	return (result);
2485aa45fcbSEnji Cooper }
2495aa45fcbSEnji Cooper 
2505aa45fcbSEnji Cooper /*
2515aa45fcbSEnji Cooper  * Test that the pidfile lock is inherited.
2525aa45fcbSEnji Cooper  */
2535aa45fcbSEnji Cooper static const char *
test_pidfile_inherited(void)2545aa45fcbSEnji Cooper test_pidfile_inherited(void)
2555aa45fcbSEnji Cooper {
2565aa45fcbSEnji Cooper 	const char *fn = "test_pidfile_inherited";
2575aa45fcbSEnji Cooper 	const char *result;
2585aa45fcbSEnji Cooper 
2595aa45fcbSEnji Cooper 	result = common_test_pidfile_child(fn, 1);
2605aa45fcbSEnji Cooper 	return (result);
2615aa45fcbSEnji Cooper }
2625aa45fcbSEnji Cooper 
263a273e09cSMark Johnston /*
264a273e09cSMark Johnston  * Make sure we handle relative pidfile paths correctly.
265a273e09cSMark Johnston  */
266a273e09cSMark Johnston static const char *
test_pidfile_relative(void)267a273e09cSMark Johnston test_pidfile_relative(void)
268a273e09cSMark Johnston {
269a273e09cSMark Johnston 	char path[PATH_MAX], pid[32], tmpdir[PATH_MAX];
270a273e09cSMark Johnston 	struct pidfh *pfh;
271a273e09cSMark Johnston 	int fd;
272a273e09cSMark Johnston 
273a273e09cSMark Johnston 	(void)snprintf(tmpdir, sizeof(tmpdir), "%s.XXXXXX", __func__);
274a273e09cSMark Johnston 	if (mkdtemp(tmpdir) == NULL)
275a273e09cSMark Johnston 		return (strerror(errno));
276a273e09cSMark Johnston 	(void)snprintf(path, sizeof(path), "%s/pidfile", tmpdir);
277a273e09cSMark Johnston 
278a273e09cSMark Johnston 	pfh = pidfile_open(path, 0600, NULL);
279a273e09cSMark Johnston 	if (pfh == NULL)
280a273e09cSMark Johnston 		return (strerror(errno));
281a273e09cSMark Johnston 	if (pidfile_write(pfh) != 0)
282a273e09cSMark Johnston 		return (strerror(errno));
283a273e09cSMark Johnston 	fd = open(path, O_RDONLY);
284a273e09cSMark Johnston 	if (fd < 0)
285a273e09cSMark Johnston 		return (strerror(errno));
286364790beSKonstantin Belousov 	memset(pid, 0, sizeof(pid));
287364790beSKonstantin Belousov 	if (read(fd, pid, sizeof(pid) - 1) < 0)
288a273e09cSMark Johnston 		return (strerror(errno));
289a273e09cSMark Johnston 	if (atoi(pid) != getpid())
290a273e09cSMark Johnston 		return ("pid mismatch");
291a273e09cSMark Johnston 	if (close(fd) != 0)
292a273e09cSMark Johnston 		return (strerror(errno));
293a273e09cSMark Johnston 	if (pidfile_close(pfh) != 0)
294a273e09cSMark Johnston 		return (strerror(errno));
295a273e09cSMark Johnston 	return (NULL);
296a273e09cSMark Johnston }
297a273e09cSMark Johnston 
2985aa45fcbSEnji Cooper static struct test {
2995aa45fcbSEnji Cooper 	const char *name;
3005aa45fcbSEnji Cooper 	const char *(*func)(void);
3015aa45fcbSEnji Cooper } t[] = {
3025aa45fcbSEnji Cooper 	{ "pidfile_uncontested", test_pidfile_uncontested },
3035aa45fcbSEnji Cooper 	{ "pidfile_self", test_pidfile_self },
3045aa45fcbSEnji Cooper 	{ "pidfile_contested", test_pidfile_contested },
3055aa45fcbSEnji Cooper 	{ "pidfile_inherited", test_pidfile_inherited },
306a273e09cSMark Johnston 	{ "pidfile_relative", test_pidfile_relative },
3075aa45fcbSEnji Cooper };
3085aa45fcbSEnji Cooper 
3095aa45fcbSEnji Cooper int
main(void)3105aa45fcbSEnji Cooper main(void)
3115aa45fcbSEnji Cooper {
3125aa45fcbSEnji Cooper 	const char *result;
3135aa45fcbSEnji Cooper 	int i, nt;
3145aa45fcbSEnji Cooper 
3155aa45fcbSEnji Cooper 	nt = sizeof(t) / sizeof(*t);
3165aa45fcbSEnji Cooper 	printf("1..%d\n", nt);
3175aa45fcbSEnji Cooper 	for (i = 0; i < nt; ++i) {
3185aa45fcbSEnji Cooper 		if ((result = t[i].func()) != NULL)
3195aa45fcbSEnji Cooper 			printf("not ok %d - %s # %s\n", i + 1,
3205aa45fcbSEnji Cooper 			    t[i].name, result);
3215aa45fcbSEnji Cooper 		else
3225aa45fcbSEnji Cooper 			printf("ok %d - %s\n", i + 1,
3235aa45fcbSEnji Cooper 			    t[i].name);
3245aa45fcbSEnji Cooper 	}
3255aa45fcbSEnji Cooper 	exit(0);
3265aa45fcbSEnji Cooper }
327