1169311Srwatson/*-
2169311Srwatson * Copyright (c) 2007 Robert N. M. Watson
3169311Srwatson * All rights reserved.
4169311Srwatson *
5169311Srwatson * Redistribution and use in source and binary forms, with or without
6169311Srwatson * modification, are permitted provided that the following conditions
7169311Srwatson * are met:
8169311Srwatson * 1. Redistributions of source code must retain the above copyright
9169311Srwatson *    notice, this list of conditions and the following disclaimer.
10169311Srwatson * 2. Redistributions in binary form must reproduce the above copyright
11169311Srwatson *    notice, this list of conditions and the following disclaimer in the
12169311Srwatson *    documentation and/or other materials provided with the distribution.
13169311Srwatson *
14169311Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15169311Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16169311Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17169311Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18169311Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19169311Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20169311Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21169311Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22169311Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23169311Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24169311Srwatson * SUCH DAMAGE.
25169311Srwatson *
26169311Srwatson * $FreeBSD$
27169311Srwatson */
28169311Srwatson
29169311Srwatson/*
30169311Srwatson * Sockets serialize I/O in each direction in order to avoid interlacing of
31169311Srwatson * I/O by multiple processes or threcvs recving or sending the socket.  This
32169311Srwatson * is done using some form of kernel lock (varies by kernel version), called
33169311Srwatson * "sblock" in FreeBSD.  However, to avoid unkillable processes waiting on
34169311Srwatson * I/O that may be entirely controlled by a remote network endpoint, that
35169311Srwatson * lock acquisition must be interruptible.
36169311Srwatson *
37169311Srwatson * To test this, set up a local domain stream socket pair and a set of three
38169311Srwatson * processes.  Two processes block in recv(), the first on sbwait (wait for
39169311Srwatson * I/O), and the second on the sblock waiting for the first to finish.  A
40169311Srwatson * third process is responsible for signalling the second process, then
41169311Srwatson * writing to the socket.  Depending on the error returned in the second
42169311Srwatson * process, we can tell whether the sblock wait was interrupted, or if
43169311Srwatson * instead the process only woke up when the write was performed.
44169311Srwatson */
45169311Srwatson
46169311Srwatson#include <sys/socket.h>
47169311Srwatson
48169311Srwatson#include <err.h>
49169311Srwatson#include <errno.h>
50169311Srwatson#include <signal.h>
51169311Srwatson#include <stdio.h>
52169311Srwatson#include <stdlib.h>
53169311Srwatson#include <unistd.h>
54169311Srwatson
55169311Srwatsonstatic int interrupted;
56169311Srwatsonstatic void
57169311Srwatsonsignal_handler(int signum)
58169311Srwatson{
59169311Srwatson
60169311Srwatson	interrupted++;
61169311Srwatson}
62169311Srwatson
63169311Srwatson/*
64169311Srwatson * Process that will perform a blocking recv on a UNIX domain socket.  This
65169311Srwatson * should return one byte of data.
66169311Srwatson */
67169311Srwatsonstatic void
68169311Srwatsonblocking_recver(int fd)
69169311Srwatson{
70169311Srwatson	ssize_t len;
71169311Srwatson	char ch;
72169311Srwatson
73169311Srwatson	len = recv(fd, &ch, sizeof(ch), 0);
74169311Srwatson	if (len < 0)
75169311Srwatson		err(-1, "FAIL: blocking_recver: recv");
76169311Srwatson	if (len == 0)
77169311Srwatson		errx(-1, "FAIL: blocking_recver: recv: eof");
78169311Srwatson	if (len != 1)
79169311Srwatson		errx(-1, "FAIL: blocking_recver: recv: %d bytes", len);
80169311Srwatson	if (interrupted)
81169311Srwatson		errx(-1, "FAIL: blocking_recver: interrupted wrong pid");
82169311Srwatson}
83169311Srwatson
84169311Srwatson/*
85169311Srwatson * Process that will perform a locking recv on a UNIX domain socket.
86169311Srwatson *
87169311Srwatson * This is where we figure out if the test worked or not.  If it has failed,
88169311Srwatson * then recv() will return EOF, as the close() arrives before the signal,
89169311Srwatson * meaning that the wait for the sblock was not interrupted; if it has
90169311Srwatson * succeeded, we get EINTR as the signal interrupts the lock request.
91169311Srwatson */
92169311Srwatsonstatic void
93169311Srwatsonlocking_recver(int fd)
94169311Srwatson{
95169311Srwatson	ssize_t len;
96169311Srwatson	char ch;
97169311Srwatson
98169311Srwatson	if (sleep(1) < 0)
99169311Srwatson		err(-1, "FAIL: locking_recver: sleep");
100169311Srwatson	len = recv(fd, &ch, sizeof(ch), 0);
101169311Srwatson	if (len < 0 && errno != EINTR)
102169311Srwatson		err(-1, "FAIL: locking_recver: recv");
103169311Srwatson	if (len < 0 && errno == EINTR) {
104169311Srwatson		fprintf(stderr, "PASS\n");
105169311Srwatson		exit(0);
106169311Srwatson	}
107169311Srwatson	if (len == 0)
108169311Srwatson		errx(-1, "FAIL: locking_recver: recv: eof");
109169311Srwatson	if (!interrupted)
110169311Srwatson		errx(-1, "FAIL: locking_recver: not interrupted");
111169311Srwatson}
112169311Srwatson
113169311Srwatsonstatic void
114169311Srwatsonsignaller(pid_t locking_recver_pid, int fd)
115169311Srwatson{
116169311Srwatson	ssize_t len;
117169311Srwatson	char ch;
118169311Srwatson
119169311Srwatson	if (sleep(2) < 0) {
120169311Srwatson		warn("signaller sleep(2)");
121169311Srwatson		return;
122169311Srwatson	}
123169311Srwatson	if (kill(locking_recver_pid, SIGHUP) < 0) {
124169311Srwatson		warn("signaller kill(%d)", locking_recver_pid);
125169311Srwatson		return;
126169311Srwatson	}
127169311Srwatson	if (sleep(1) < 0) {
128169311Srwatson		warn("signaller sleep(1)");
129169311Srwatson		return;
130169311Srwatson	}
131169311Srwatson	len = send(fd, &ch, sizeof(ch), 0);
132169311Srwatson	if (len < 0) {
133169311Srwatson		warn("signaller send");
134169311Srwatson		return;
135169311Srwatson	}
136169311Srwatson	if (len != sizeof(ch)) {
137169311Srwatson		warnx("signaller send ret %d", len);
138169311Srwatson		return;
139169311Srwatson	}
140169311Srwatson	if (close(fd) < 0) {
141169311Srwatson		warn("signaller close");
142169311Srwatson		return;
143169311Srwatson	}
144169311Srwatson	if (sleep(1) < 0) {
145169311Srwatson		warn("signaller sleep(1)");
146169311Srwatson		return;
147169311Srwatson	}
148169311Srwatson}
149169311Srwatson
150169311Srwatsonint
151169311Srwatsonmain(int argc, char *argv[])
152169311Srwatson{
153169311Srwatson	int error, fds[2], recver_fd, sender_fd;
154169311Srwatson	pid_t blocking_recver_pid;
155169311Srwatson	pid_t locking_recver_pid;
156169311Srwatson	struct sigaction sa;
157169311Srwatson
158169311Srwatson	if (sigaction(SIGHUP, NULL, &sa) < 0)
159169311Srwatson		err(-1, "FAIL: sigaction(SIGHUP, NULL, &sa)");
160169311Srwatson
161169311Srwatson	sa.sa_handler = signal_handler;
162169311Srwatson	if (sa.sa_flags & SA_RESTART)
163169311Srwatson		printf("SIGHUP restartable by default (cleared)\n");
164169311Srwatson	sa.sa_flags &= ~SA_RESTART;
165169311Srwatson
166169311Srwatson	if (sigaction(SIGHUP, &sa, NULL) < 0)
167169311Srwatson		err(-1, "FAIL: sigaction(SIGHUP, &sa, NULL)");
168169311Srwatson
169169311Srwatson#if 0
170169311Srwatson	if (signal(SIGHUP, signal_handler) == SIG_ERR)
171169311Srwatson		err(-1, "FAIL: signal(SIGHUP)");
172169311Srwatson#endif
173169311Srwatson
174169311Srwatson	if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0)
175169311Srwatson		err(-1, "FAIL: socketpair(PF_LOCAL, SOGK_STREAM, 0)");
176169311Srwatson
177169311Srwatson	sender_fd = fds[0];
178169311Srwatson	recver_fd = fds[1];
179169311Srwatson
180169311Srwatson	blocking_recver_pid = fork();
181169311Srwatson	if (blocking_recver_pid < 0)
182169311Srwatson		err(-1, "FAIL: fork");
183169311Srwatson	if (blocking_recver_pid == 0) {
184169311Srwatson		close(sender_fd);
185169311Srwatson		blocking_recver(recver_fd);
186169311Srwatson		exit(0);
187169311Srwatson	}
188169311Srwatson
189169311Srwatson	locking_recver_pid = fork();
190169311Srwatson	if (locking_recver_pid < 0) {
191169311Srwatson		error = errno;
192169311Srwatson		kill(blocking_recver_pid, SIGKILL);
193169311Srwatson		errno = error;
194169311Srwatson		err(-1, "FAIL: fork");
195169311Srwatson	}
196169311Srwatson	if (locking_recver_pid == 0) {
197169311Srwatson		close(sender_fd);
198169311Srwatson		locking_recver(recver_fd);
199169311Srwatson		exit(0);
200169311Srwatson	}
201169311Srwatson
202169311Srwatson	signaller(locking_recver_pid, sender_fd);
203169311Srwatson
204169311Srwatson	kill(blocking_recver_pid, SIGKILL);
205169311Srwatson	kill(locking_recver_pid, SIGKILL);
206169311Srwatson	exit(0);
207169311Srwatson}
208