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