]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - tools/regression/sockets/sblock/sblock.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / tools / regression / sockets / sblock / sblock.c
1 /*-
2  * Copyright (c) 2007 Robert N. M. Watson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 /*
30  * Sockets serialize I/O in each direction in order to avoid interlacing of
31  * I/O by multiple processes or threcvs recving or sending the socket.  This
32  * is done using some form of kernel lock (varies by kernel version), called
33  * "sblock" in FreeBSD.  However, to avoid unkillable processes waiting on
34  * I/O that may be entirely controlled by a remote network endpoint, that
35  * lock acquisition must be interruptible.
36  *
37  * To test this, set up a local domain stream socket pair and a set of three
38  * processes.  Two processes block in recv(), the first on sbwait (wait for
39  * I/O), and the second on the sblock waiting for the first to finish.  A
40  * third process is responsible for signalling the second process, then
41  * writing to the socket.  Depending on the error returned in the second
42  * process, we can tell whether the sblock wait was interrupted, or if
43  * instead the process only woke up when the write was performed.
44  */
45
46 #include <sys/socket.h>
47
48 #include <err.h>
49 #include <errno.h>
50 #include <signal.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <unistd.h>
54
55 static int interrupted;
56 static void
57 signal_handler(int signum)
58 {
59
60         interrupted++;
61 }
62
63 /*
64  * Process that will perform a blocking recv on a UNIX domain socket.  This
65  * should return one byte of data.
66  */
67 static void
68 blocking_recver(int fd)
69 {
70         ssize_t len;
71         char ch;
72
73         len = recv(fd, &ch, sizeof(ch), 0);
74         if (len < 0)
75                 err(-1, "FAIL: blocking_recver: recv");
76         if (len == 0)
77                 errx(-1, "FAIL: blocking_recver: recv: eof");
78         if (len != 1)
79                 errx(-1, "FAIL: blocking_recver: recv: %zd bytes", len);
80         if (interrupted)
81                 errx(-1, "FAIL: blocking_recver: interrupted wrong pid");
82 }
83
84 /*
85  * Process that will perform a locking recv on a UNIX domain socket.
86  *
87  * This is where we figure out if the test worked or not.  If it has failed,
88  * then recv() will return EOF, as the close() arrives before the signal,
89  * meaning that the wait for the sblock was not interrupted; if it has
90  * succeeded, we get EINTR as the signal interrupts the lock request.
91  */
92 static void
93 locking_recver(int fd)
94 {
95         ssize_t len;
96         char ch;
97
98         if (sleep(1) < 0)
99                 err(-1, "FAIL: locking_recver: sleep");
100         len = recv(fd, &ch, sizeof(ch), 0);
101         if (len < 0 && errno != EINTR)
102                 err(-1, "FAIL: locking_recver: recv");
103         if (len < 0 && errno == EINTR) {
104                 fprintf(stderr, "PASS\n");
105                 exit(0);
106         }
107         if (len == 0)
108                 errx(-1, "FAIL: locking_recver: recv: eof");
109         if (!interrupted)
110                 errx(-1, "FAIL: locking_recver: not interrupted");
111 }
112
113 static void
114 signaller(pid_t locking_recver_pid, int fd)
115 {
116         ssize_t len;
117         char ch;
118
119         if (sleep(2) < 0) {
120                 warn("signaller sleep(2)");
121                 return;
122         }
123         if (kill(locking_recver_pid, SIGHUP) < 0) {
124                 warn("signaller kill(%d)", locking_recver_pid);
125                 return;
126         }
127         if (sleep(1) < 0) {
128                 warn("signaller sleep(1)");
129                 return;
130         }
131         len = send(fd, &ch, sizeof(ch), 0);
132         if (len < 0) {
133                 warn("signaller send");
134                 return;
135         }
136         if (len != sizeof(ch)) {
137                 warnx("signaller send ret %zd", len);
138                 return;
139         }
140         if (close(fd) < 0) {
141                 warn("signaller close");
142                 return;
143         }
144         if (sleep(1) < 0) {
145                 warn("signaller sleep(1)");
146                 return;
147         }
148 }
149
150 int
151 main(int argc, char *argv[])
152 {
153         int error, fds[2], recver_fd, sender_fd;
154         pid_t blocking_recver_pid;
155         pid_t locking_recver_pid;
156         struct sigaction sa;
157
158         if (sigaction(SIGHUP, NULL, &sa) < 0)
159                 err(-1, "FAIL: sigaction(SIGHUP, NULL, &sa)");
160
161         sa.sa_handler = signal_handler;
162         if (sa.sa_flags & SA_RESTART)
163                 printf("SIGHUP restartable by default (cleared)\n");
164         sa.sa_flags &= ~SA_RESTART;
165
166         if (sigaction(SIGHUP, &sa, NULL) < 0)
167                 err(-1, "FAIL: sigaction(SIGHUP, &sa, NULL)");
168
169 #if 0
170         if (signal(SIGHUP, signal_handler) == SIG_ERR)
171                 err(-1, "FAIL: signal(SIGHUP)");
172 #endif
173
174         if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0)
175                 err(-1, "FAIL: socketpair(PF_LOCAL, SOGK_STREAM, 0)");
176
177         sender_fd = fds[0];
178         recver_fd = fds[1];
179
180         blocking_recver_pid = fork();
181         if (blocking_recver_pid < 0)
182                 err(-1, "FAIL: fork");
183         if (blocking_recver_pid == 0) {
184                 close(sender_fd);
185                 blocking_recver(recver_fd);
186                 exit(0);
187         }
188
189         locking_recver_pid = fork();
190         if (locking_recver_pid < 0) {
191                 error = errno;
192                 kill(blocking_recver_pid, SIGKILL);
193                 errno = error;
194                 err(-1, "FAIL: fork");
195         }
196         if (locking_recver_pid == 0) {
197                 close(sender_fd);
198                 locking_recver(recver_fd);
199                 exit(0);
200         }
201
202         signaller(locking_recver_pid, sender_fd);
203
204         kill(blocking_recver_pid, SIGKILL);
205         kill(locking_recver_pid, SIGKILL);
206         exit(0);
207 }