Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tools/regression/sockets/sblock/sblock.c
48255 views
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
27
/*
28
* Sockets serialize I/O in each direction in order to avoid interlacing of
29
* I/O by multiple processes or threcvs recving or sending the socket. This
30
* is done using some form of kernel lock (varies by kernel version), called
31
* "sblock" in FreeBSD. However, to avoid unkillable processes waiting on
32
* I/O that may be entirely controlled by a remote network endpoint, that
33
* lock acquisition must be interruptible.
34
*
35
* To test this, set up a local domain stream socket pair and a set of three
36
* processes. Two processes block in recv(), the first on sbwait (wait for
37
* I/O), and the second on the sblock waiting for the first to finish. A
38
* third process is responsible for signalling the second process, then
39
* writing to the socket. Depending on the error returned in the second
40
* process, we can tell whether the sblock wait was interrupted, or if
41
* instead the process only woke up when the write was performed.
42
*/
43
44
#include <sys/socket.h>
45
46
#include <err.h>
47
#include <errno.h>
48
#include <signal.h>
49
#include <stdio.h>
50
#include <stdlib.h>
51
#include <unistd.h>
52
53
static int interrupted;
54
static void
55
signal_handler(int signum __unused)
56
{
57
58
interrupted++;
59
}
60
61
/*
62
* Process that will perform a blocking recv on a UNIX domain socket. This
63
* should return one byte of data.
64
*/
65
static void
66
blocking_recver(int fd)
67
{
68
ssize_t len;
69
char ch;
70
71
len = recv(fd, &ch, sizeof(ch), 0);
72
if (len < 0)
73
err(-1, "FAIL: blocking_recver: recv");
74
if (len == 0)
75
errx(-1, "FAIL: blocking_recver: recv: eof");
76
if (len != 1)
77
errx(-1, "FAIL: blocking_recver: recv: %zd bytes", len);
78
if (interrupted)
79
errx(-1, "FAIL: blocking_recver: interrupted wrong pid");
80
}
81
82
/*
83
* Process that will perform a locking recv on a UNIX domain socket.
84
*
85
* This is where we figure out if the test worked or not. If it has failed,
86
* then recv() will return EOF, as the close() arrives before the signal,
87
* meaning that the wait for the sblock was not interrupted; if it has
88
* succeeded, we get EINTR as the signal interrupts the lock request.
89
*/
90
static void
91
locking_recver(int fd)
92
{
93
ssize_t len;
94
char ch;
95
96
if (sleep(1) != 0)
97
err(-1, "FAIL: locking_recver: sleep");
98
len = recv(fd, &ch, sizeof(ch), 0);
99
if (len < 0 && errno != EINTR)
100
err(-1, "FAIL: locking_recver: recv");
101
if (len < 0 && errno == EINTR) {
102
fprintf(stderr, "PASS\n");
103
exit(0);
104
}
105
if (len == 0)
106
errx(-1, "FAIL: locking_recver: recv: eof");
107
if (!interrupted)
108
errx(-1, "FAIL: locking_recver: not interrupted");
109
}
110
111
static void
112
signaller(pid_t locking_recver_pid, int fd)
113
{
114
ssize_t len;
115
char ch;
116
117
if (sleep(2) != 0) {
118
warn("signaller sleep(2)");
119
return;
120
}
121
if (kill(locking_recver_pid, SIGHUP) < 0) {
122
warn("signaller kill(%d)", locking_recver_pid);
123
return;
124
}
125
if (sleep(1) != 0) {
126
warn("signaller sleep(1)");
127
return;
128
}
129
len = send(fd, &ch, sizeof(ch), 0);
130
if (len < 0) {
131
warn("signaller send");
132
return;
133
}
134
if (len != sizeof(ch)) {
135
warnx("signaller send ret %zd", len);
136
return;
137
}
138
if (close(fd) < 0) {
139
warn("signaller close");
140
return;
141
}
142
if (sleep(1) != 0) {
143
warn("signaller sleep(1)");
144
return;
145
}
146
}
147
148
int
149
main(void)
150
{
151
int error, fds[2], recver_fd, sender_fd;
152
pid_t blocking_recver_pid;
153
pid_t locking_recver_pid;
154
struct sigaction sa;
155
156
if (sigaction(SIGHUP, NULL, &sa) < 0)
157
err(-1, "FAIL: sigaction(SIGHUP, NULL, &sa)");
158
159
sa.sa_handler = signal_handler;
160
if (sa.sa_flags & SA_RESTART)
161
printf("SIGHUP restartable by default (cleared)\n");
162
sa.sa_flags &= ~SA_RESTART;
163
164
if (sigaction(SIGHUP, &sa, NULL) < 0)
165
err(-1, "FAIL: sigaction(SIGHUP, &sa, NULL)");
166
167
#if 0
168
if (signal(SIGHUP, signal_handler) == SIG_ERR)
169
err(-1, "FAIL: signal(SIGHUP)");
170
#endif
171
172
if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0)
173
err(-1, "FAIL: socketpair(PF_LOCAL, SOGK_STREAM, 0)");
174
175
sender_fd = fds[0];
176
recver_fd = fds[1];
177
178
blocking_recver_pid = fork();
179
if (blocking_recver_pid < 0)
180
err(-1, "FAIL: fork");
181
if (blocking_recver_pid == 0) {
182
close(sender_fd);
183
blocking_recver(recver_fd);
184
exit(0);
185
}
186
187
locking_recver_pid = fork();
188
if (locking_recver_pid < 0) {
189
error = errno;
190
kill(blocking_recver_pid, SIGKILL);
191
errno = error;
192
err(-1, "FAIL: fork");
193
}
194
if (locking_recver_pid == 0) {
195
close(sender_fd);
196
locking_recver(recver_fd);
197
exit(0);
198
}
199
200
signaller(locking_recver_pid, sender_fd);
201
202
kill(blocking_recver_pid, SIGKILL);
203
kill(locking_recver_pid, SIGKILL);
204
exit(0);
205
}
206
207