xref: /freebsd/tools/regression/sockets/sblock/sblock.c (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
19d3f3e97SRobert Watson /*-
29d3f3e97SRobert Watson  * Copyright (c) 2007 Robert N. M. Watson
39d3f3e97SRobert Watson  * All rights reserved.
49d3f3e97SRobert Watson  *
59d3f3e97SRobert Watson  * Redistribution and use in source and binary forms, with or without
69d3f3e97SRobert Watson  * modification, are permitted provided that the following conditions
79d3f3e97SRobert Watson  * are met:
89d3f3e97SRobert Watson  * 1. Redistributions of source code must retain the above copyright
99d3f3e97SRobert Watson  *    notice, this list of conditions and the following disclaimer.
109d3f3e97SRobert Watson  * 2. Redistributions in binary form must reproduce the above copyright
119d3f3e97SRobert Watson  *    notice, this list of conditions and the following disclaimer in the
129d3f3e97SRobert Watson  *    documentation and/or other materials provided with the distribution.
139d3f3e97SRobert Watson  *
149d3f3e97SRobert Watson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
159d3f3e97SRobert Watson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
169d3f3e97SRobert Watson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
179d3f3e97SRobert Watson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
189d3f3e97SRobert Watson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
199d3f3e97SRobert Watson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
209d3f3e97SRobert Watson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
219d3f3e97SRobert Watson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
229d3f3e97SRobert Watson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
239d3f3e97SRobert Watson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
249d3f3e97SRobert Watson  * SUCH DAMAGE.
259d3f3e97SRobert Watson  */
269d3f3e97SRobert Watson 
279d3f3e97SRobert Watson /*
289d3f3e97SRobert Watson  * Sockets serialize I/O in each direction in order to avoid interlacing of
299d3f3e97SRobert Watson  * I/O by multiple processes or threcvs recving or sending the socket.  This
309d3f3e97SRobert Watson  * is done using some form of kernel lock (varies by kernel version), called
319d3f3e97SRobert Watson  * "sblock" in FreeBSD.  However, to avoid unkillable processes waiting on
329d3f3e97SRobert Watson  * I/O that may be entirely controlled by a remote network endpoint, that
339d3f3e97SRobert Watson  * lock acquisition must be interruptible.
349d3f3e97SRobert Watson  *
359d3f3e97SRobert Watson  * To test this, set up a local domain stream socket pair and a set of three
369d3f3e97SRobert Watson  * processes.  Two processes block in recv(), the first on sbwait (wait for
379d3f3e97SRobert Watson  * I/O), and the second on the sblock waiting for the first to finish.  A
389d3f3e97SRobert Watson  * third process is responsible for signalling the second process, then
399d3f3e97SRobert Watson  * writing to the socket.  Depending on the error returned in the second
409d3f3e97SRobert Watson  * process, we can tell whether the sblock wait was interrupted, or if
419d3f3e97SRobert Watson  * instead the process only woke up when the write was performed.
429d3f3e97SRobert Watson  */
439d3f3e97SRobert Watson 
449d3f3e97SRobert Watson #include <sys/socket.h>
459d3f3e97SRobert Watson 
469d3f3e97SRobert Watson #include <err.h>
479d3f3e97SRobert Watson #include <errno.h>
489d3f3e97SRobert Watson #include <signal.h>
499d3f3e97SRobert Watson #include <stdio.h>
509d3f3e97SRobert Watson #include <stdlib.h>
519d3f3e97SRobert Watson #include <unistd.h>
529d3f3e97SRobert Watson 
539d3f3e97SRobert Watson static int interrupted;
549d3f3e97SRobert Watson static void
signal_handler(int signum __unused)55*c7ded8baSEnji Cooper signal_handler(int signum __unused)
569d3f3e97SRobert Watson {
579d3f3e97SRobert Watson 
589d3f3e97SRobert Watson 	interrupted++;
599d3f3e97SRobert Watson }
609d3f3e97SRobert Watson 
619d3f3e97SRobert Watson /*
629d3f3e97SRobert Watson  * Process that will perform a blocking recv on a UNIX domain socket.  This
639d3f3e97SRobert Watson  * should return one byte of data.
649d3f3e97SRobert Watson  */
659d3f3e97SRobert Watson static void
blocking_recver(int fd)669d3f3e97SRobert Watson blocking_recver(int fd)
679d3f3e97SRobert Watson {
689d3f3e97SRobert Watson 	ssize_t len;
699d3f3e97SRobert Watson 	char ch;
709d3f3e97SRobert Watson 
719d3f3e97SRobert Watson 	len = recv(fd, &ch, sizeof(ch), 0);
729d3f3e97SRobert Watson 	if (len < 0)
739d3f3e97SRobert Watson 		err(-1, "FAIL: blocking_recver: recv");
749d3f3e97SRobert Watson 	if (len == 0)
759d3f3e97SRobert Watson 		errx(-1, "FAIL: blocking_recver: recv: eof");
769d3f3e97SRobert Watson 	if (len != 1)
77d701ebcbSEd Maste 		errx(-1, "FAIL: blocking_recver: recv: %zd bytes", len);
789d3f3e97SRobert Watson 	if (interrupted)
799d3f3e97SRobert Watson 		errx(-1, "FAIL: blocking_recver: interrupted wrong pid");
809d3f3e97SRobert Watson }
819d3f3e97SRobert Watson 
829d3f3e97SRobert Watson /*
839d3f3e97SRobert Watson  * Process that will perform a locking recv on a UNIX domain socket.
849d3f3e97SRobert Watson  *
859d3f3e97SRobert Watson  * This is where we figure out if the test worked or not.  If it has failed,
869d3f3e97SRobert Watson  * then recv() will return EOF, as the close() arrives before the signal,
879d3f3e97SRobert Watson  * meaning that the wait for the sblock was not interrupted; if it has
889d3f3e97SRobert Watson  * succeeded, we get EINTR as the signal interrupts the lock request.
899d3f3e97SRobert Watson  */
909d3f3e97SRobert Watson static void
locking_recver(int fd)919d3f3e97SRobert Watson locking_recver(int fd)
929d3f3e97SRobert Watson {
939d3f3e97SRobert Watson 	ssize_t len;
949d3f3e97SRobert Watson 	char ch;
959d3f3e97SRobert Watson 
96*c7ded8baSEnji Cooper 	if (sleep(1) != 0)
979d3f3e97SRobert Watson 		err(-1, "FAIL: locking_recver: sleep");
989d3f3e97SRobert Watson 	len = recv(fd, &ch, sizeof(ch), 0);
999d3f3e97SRobert Watson 	if (len < 0 && errno != EINTR)
1009d3f3e97SRobert Watson 		err(-1, "FAIL: locking_recver: recv");
1019d3f3e97SRobert Watson 	if (len < 0 && errno == EINTR) {
1029d3f3e97SRobert Watson 		fprintf(stderr, "PASS\n");
1039d3f3e97SRobert Watson 		exit(0);
1049d3f3e97SRobert Watson 	}
1059d3f3e97SRobert Watson 	if (len == 0)
1069d3f3e97SRobert Watson 		errx(-1, "FAIL: locking_recver: recv: eof");
1079d3f3e97SRobert Watson 	if (!interrupted)
1089d3f3e97SRobert Watson 		errx(-1, "FAIL: locking_recver: not interrupted");
1099d3f3e97SRobert Watson }
1109d3f3e97SRobert Watson 
1119d3f3e97SRobert Watson static void
signaller(pid_t locking_recver_pid,int fd)1129d3f3e97SRobert Watson signaller(pid_t locking_recver_pid, int fd)
1139d3f3e97SRobert Watson {
1149d3f3e97SRobert Watson 	ssize_t len;
1159d3f3e97SRobert Watson 	char ch;
1169d3f3e97SRobert Watson 
117*c7ded8baSEnji Cooper 	if (sleep(2) != 0) {
1189d3f3e97SRobert Watson 		warn("signaller sleep(2)");
1199d3f3e97SRobert Watson 		return;
1209d3f3e97SRobert Watson 	}
1219d3f3e97SRobert Watson 	if (kill(locking_recver_pid, SIGHUP) < 0) {
1229d3f3e97SRobert Watson 		warn("signaller kill(%d)", locking_recver_pid);
1239d3f3e97SRobert Watson 		return;
1249d3f3e97SRobert Watson 	}
125*c7ded8baSEnji Cooper 	if (sleep(1) != 0) {
1269d3f3e97SRobert Watson 		warn("signaller sleep(1)");
1279d3f3e97SRobert Watson 		return;
1289d3f3e97SRobert Watson 	}
1299d3f3e97SRobert Watson 	len = send(fd, &ch, sizeof(ch), 0);
1309d3f3e97SRobert Watson 	if (len < 0) {
1319d3f3e97SRobert Watson 		warn("signaller send");
1329d3f3e97SRobert Watson 		return;
1339d3f3e97SRobert Watson 	}
1349d3f3e97SRobert Watson 	if (len != sizeof(ch)) {
135d701ebcbSEd Maste 		warnx("signaller send ret %zd", len);
1369d3f3e97SRobert Watson 		return;
1379d3f3e97SRobert Watson 	}
1389d3f3e97SRobert Watson 	if (close(fd) < 0) {
1399d3f3e97SRobert Watson 		warn("signaller close");
1409d3f3e97SRobert Watson 		return;
1419d3f3e97SRobert Watson 	}
142*c7ded8baSEnji Cooper 	if (sleep(1) != 0) {
1439d3f3e97SRobert Watson 		warn("signaller sleep(1)");
1449d3f3e97SRobert Watson 		return;
1459d3f3e97SRobert Watson 	}
1469d3f3e97SRobert Watson }
1479d3f3e97SRobert Watson 
1489d3f3e97SRobert Watson int
main(void)149*c7ded8baSEnji Cooper main(void)
1509d3f3e97SRobert Watson {
1519d3f3e97SRobert Watson 	int error, fds[2], recver_fd, sender_fd;
1529d3f3e97SRobert Watson 	pid_t blocking_recver_pid;
1539d3f3e97SRobert Watson 	pid_t locking_recver_pid;
1549d3f3e97SRobert Watson 	struct sigaction sa;
1559d3f3e97SRobert Watson 
1569d3f3e97SRobert Watson 	if (sigaction(SIGHUP, NULL, &sa) < 0)
1579d3f3e97SRobert Watson 		err(-1, "FAIL: sigaction(SIGHUP, NULL, &sa)");
1589d3f3e97SRobert Watson 
1599d3f3e97SRobert Watson 	sa.sa_handler = signal_handler;
1609d3f3e97SRobert Watson 	if (sa.sa_flags & SA_RESTART)
1619d3f3e97SRobert Watson 		printf("SIGHUP restartable by default (cleared)\n");
1629d3f3e97SRobert Watson 	sa.sa_flags &= ~SA_RESTART;
1639d3f3e97SRobert Watson 
1649d3f3e97SRobert Watson 	if (sigaction(SIGHUP, &sa, NULL) < 0)
1659d3f3e97SRobert Watson 		err(-1, "FAIL: sigaction(SIGHUP, &sa, NULL)");
1669d3f3e97SRobert Watson 
1679d3f3e97SRobert Watson #if 0
1689d3f3e97SRobert Watson 	if (signal(SIGHUP, signal_handler) == SIG_ERR)
1699d3f3e97SRobert Watson 		err(-1, "FAIL: signal(SIGHUP)");
1709d3f3e97SRobert Watson #endif
1719d3f3e97SRobert Watson 
1729d3f3e97SRobert Watson 	if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0)
1739d3f3e97SRobert Watson 		err(-1, "FAIL: socketpair(PF_LOCAL, SOGK_STREAM, 0)");
1749d3f3e97SRobert Watson 
1759d3f3e97SRobert Watson 	sender_fd = fds[0];
1769d3f3e97SRobert Watson 	recver_fd = fds[1];
1779d3f3e97SRobert Watson 
1789d3f3e97SRobert Watson 	blocking_recver_pid = fork();
1799d3f3e97SRobert Watson 	if (blocking_recver_pid < 0)
1809d3f3e97SRobert Watson 		err(-1, "FAIL: fork");
1819d3f3e97SRobert Watson 	if (blocking_recver_pid == 0) {
1829d3f3e97SRobert Watson 		close(sender_fd);
1839d3f3e97SRobert Watson 		blocking_recver(recver_fd);
1849d3f3e97SRobert Watson 		exit(0);
1859d3f3e97SRobert Watson 	}
1869d3f3e97SRobert Watson 
1879d3f3e97SRobert Watson 	locking_recver_pid = fork();
1889d3f3e97SRobert Watson 	if (locking_recver_pid < 0) {
1899d3f3e97SRobert Watson 		error = errno;
1909d3f3e97SRobert Watson 		kill(blocking_recver_pid, SIGKILL);
1919d3f3e97SRobert Watson 		errno = error;
1929d3f3e97SRobert Watson 		err(-1, "FAIL: fork");
1939d3f3e97SRobert Watson 	}
1949d3f3e97SRobert Watson 	if (locking_recver_pid == 0) {
1959d3f3e97SRobert Watson 		close(sender_fd);
1969d3f3e97SRobert Watson 		locking_recver(recver_fd);
1979d3f3e97SRobert Watson 		exit(0);
1989d3f3e97SRobert Watson 	}
1999d3f3e97SRobert Watson 
2009d3f3e97SRobert Watson 	signaller(locking_recver_pid, sender_fd);
2019d3f3e97SRobert Watson 
2029d3f3e97SRobert Watson 	kill(blocking_recver_pid, SIGKILL);
2039d3f3e97SRobert Watson 	kill(locking_recver_pid, SIGKILL);
2049d3f3e97SRobert Watson 	exit(0);
2059d3f3e97SRobert Watson }
206