]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - tools/regression/sockets/unix_seqpacket_exercise/unix_seqpacket_exercise.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / tools / regression / sockets / unix_seqpacket_exercise / unix_seqpacket_exercise.c
1 /*-
2  * Copyright (c) 2009 Robert N. M. Watson
3  * All rights reserved.
4  *
5  * This software was developed at the University of Cambridge Computer
6  * Laboratory with support from a grant from Google, Inc.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/socket.h>
34 #include <sys/wait.h>
35 #include <sys/un.h>
36
37 #include <err.h>
38 #include <errno.h>
39 #include <limits.h>
40 #include <stdio.h>
41 #include <signal.h>
42 #include <string.h>
43 #include <unistd.h>
44
45 #define min(x, y)       (x < y ? x : y)
46
47 #define BUFLEN  32768
48
49 #define SEQPACKET_RCVBUF        (131072-16)
50 #define SEQPACKET_SNDBUF        (131072-16)
51
52 #define FAILERR(str)            err(-1, "%s: %s", __func__, str)
53 #define FAILNERR(str, n)        err(-1, "%s %d: %s", __func__, n, str)
54 #define FAILNMERR(str, n, m)    err(-1, "%s %d %d: %s", __func__, n, m, str)
55 #define FAILERRX(str)           errx(-1, "%s: %s", __func__, str)
56 #define FAILNERRX(str, n)       errx(-1, "%s %d: %s", __func__, n, str)
57 #define FAILNMERRX(str, n, m)   errx(-1, "%s %d %d: %s", __func__, n, m, str)
58
59 static int ann = 0;
60
61 #define ANN()           (ann ? warnx("%s: start", __func__) : 0)
62 #define ANNN(n)         (ann ? warnx("%s %d: start", __func__, (n)) : 0)
63 #define ANNNM(n, m)     (ann ? warnx("%s %d %d: start", __func__, (n), (m)) : 0)
64
65 #define OK()            warnx("%s: ok", __func__)
66 #define OKN(n)          warnx("%s %d: ok", __func__, (n))
67 #define OKNM(n, m)      warnx("%s %d %d: ok", __func__, (n), (m))
68
69 #ifdef SO_NOSIGPIPE
70 #define NEW_SOCKET(s) do {                                              \
71         int i;                                                          \
72                                                                         \
73         (s) = socket(PF_LOCAL, SOCK_SEQPACKET, 0);                      \
74         if ((s) < 0)                                                    \
75                 FAILERR("socket");                                      \
76                                                                         \
77         i = 1;                                                          \
78         if (setsockopt((s), SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i)) < 0) \
79                 FAILERR("setsockopt SO_NOSIGPIPE");                     \
80                                                                         \
81         i = SEQPACKET_RCVBUF;                                           \
82         if (setsockopt((s), SOL_SOCKET, SO_RCVBUF, &i, sizeof(i)) < 0)  \
83                 FAILERR("setsockopt SO_RCVBUF");                        \
84                                                                         \
85         i = SEQPACKET_SNDBUF;                                           \
86         if (setsockopt((s), SOL_SOCKET, SO_SNDBUF, &i, sizeof(i)) < 0)  \
87                 FAILERR("setsockopt SO_SNDBUF");                        \
88 } while (0)
89 #else
90 #define NEW_SOCKET(s) do {                                              \
91         int i;                                                          \
92                                                                         \
93         (s) = socket(PF_LOCAL, SOCK_SEQPACKET, 0);                      \
94         if ((s) < 0)                                                    \
95                 FAILERR("socket");                                      \
96                                                                         \
97         i = SEQPACKET_RCVBUF;                                           \
98         if (setsockopt((s), SOL_SOCKET, SO_RCVBUF, &i, sizeof(i)) < 0)  \
99                 FAILERR("setsockopt SO_RCVBUF");                        \
100                                                                         \
101         i = SEQPACKET_SNDBUF;                                           \
102         if (setsockopt((s), SOL_SOCKET, SO_SNDBUF, &i, sizeof(i)) < 0)  \
103                 FAILERR("setsockopt SO_SNDBUF");                        \
104 } while (0)
105 #endif
106
107 static void
108 server(int s_listen)
109 {
110         char buffer[BUFLEN];
111         ssize_t ssize_recv, ssize_send;
112         socklen_t socklen;
113         int i, s_accept;
114
115         while (1) {
116                 s_accept = accept(s_listen, NULL, 0);
117                 if (s_accept >= 0) {
118                         i = SEQPACKET_RCVBUF;
119                         if (setsockopt(s_accept, SOL_SOCKET, SO_RCVBUF, &i,
120                             sizeof(i)) < 0) {
121                                 warn("server: setsockopt SO_RCVBUF");
122                                 close(s_accept);
123                                 continue;
124                         }
125
126                         if (getsockopt(s_accept, SOL_SOCKET, SO_RCVBUF, &i,
127                             &socklen) < 0) {
128                                 warn("server: getsockopt SO_RCVBUF");
129                                 close(s_accept);
130                                 continue;
131                         }
132                         if (i != SEQPACKET_RCVBUF) {
133                                 warnx("server: getsockopt SO_RCVBUF wrong %d",
134                                     i);
135                                 close(s_accept);
136                                 continue;
137                         }
138
139                         socklen = sizeof(i);
140                         if (getsockopt(s_accept, SOL_SOCKET, SO_SNDBUF, &i,
141                             &socklen) < 0) {
142                                 warn("server: getsockopt SO_SNDBUF");
143                                 close(s_accept);
144                                 continue;
145                         }
146                         if (i != SEQPACKET_SNDBUF) {
147                                 warnx("server: getsockopt SO_SNDBUF wrong %d",
148                                     i);
149                                 close(s_accept);
150                                 continue;
151                         }
152
153                         do {
154                                 ssize_recv = recv(s_accept, buffer,
155                                     sizeof(buffer), 0);
156                                 if (ssize_recv == 0)
157                                         break;
158                                 if (ssize_recv < 0) {
159                                         warn("server: recv");
160                                         break;
161                                 }
162                                 ssize_send = send(s_accept, buffer,
163                                     ssize_recv, 0);
164                                 if (ssize_send == 0)
165                                         break;
166                                 if (ssize_send < 0) {
167                                         warn("server: send");
168                                         break;
169                                 }
170                                 if (ssize_send != ssize_recv)
171                                         warnx("server: recv %d sent %d",
172                                             ssize_recv, ssize_send);
173                         } while (1);
174                         close(s_accept);
175                 } else
176                         warn("server: accept");
177         }
178 }
179
180 static void
181 test_connect(struct sockaddr_un *sun)
182 {
183         int s;
184
185         ANN();
186         NEW_SOCKET(s);
187         if (connect(s, (struct sockaddr *)sun, sizeof(*sun)) < 0)
188                 FAILERR("connect");
189         (void)close(s);
190         OK();
191 }
192
193 static void
194 test_connect_send(struct sockaddr_un *sun)
195 {
196         ssize_t ssize;
197         char ch;
198         int s;
199
200         ANN();
201         NEW_SOCKET(s);
202         if (connect(s, (struct sockaddr *)sun, sizeof(*sun)) < 0)
203                 FAILERR("connect");
204         ssize = send(s, &ch, sizeof(ch), 0);
205         if (ssize < 0)
206                 FAILERR("send");
207         if (ssize != sizeof(ch))
208                 FAILERRX("send wrong size");
209         (void)close(s);
210         OK();
211 }
212
213 static void
214 test_connect_shutdown_send(struct sockaddr_un *sun)
215 {
216         ssize_t ssize;
217         char ch;
218         int s;
219
220         ANN();
221         NEW_SOCKET(s);
222         if (connect(s, (struct sockaddr *)sun, sizeof(*sun)) < 0)
223                 FAILERR("connect");
224         if (shutdown(s, SHUT_RDWR) < 0)
225                 FAILERR("shutdown SHUT_RDWR");
226         ssize = send(s, &ch, sizeof(ch), 0);
227         if (ssize >= 0)
228                 FAILERRX("send");
229         if (errno != EPIPE)
230                 FAILERR("send unexpected error");
231         (void)close(s);
232         OK();
233 }
234
235 static void
236 test_connect_send_recv(struct sockaddr_un *sun, size_t size)
237 {
238         char buf[size + 4];     /* Detect extra bytes. */
239         size_t truncsize;
240         ssize_t ssize;
241         int s;
242
243         ANNN(size);
244         NEW_SOCKET(s);
245         if (connect(s, (struct sockaddr *)sun, sizeof(*sun)) < 0)
246                 FAILNERR("connect", size);
247         ssize = send(s, buf, size, 0);
248         if (ssize < 0 && size >= SEQPACKET_RCVBUF)
249                 goto out;
250         if (ssize < 0)
251                 FAILNERR("send", size);
252         if (ssize == 0)
253                 FAILNERR("send eof", size);
254         if (ssize != size)
255                 FAILNERRX("send size", size);
256
257         truncsize = min(size, BUFLEN);
258         ssize = recv(s, buf, sizeof(buf), 0);
259         if (ssize < 0)
260                 FAILNERR("recv", size);
261         if (ssize == 0)
262                 FAILNERRX("recv eof", size);
263         if (ssize < truncsize)
264                 FAILNERRX("recv too few bytes", size);
265         if (ssize > truncsize)
266                 FAILNERRX("recv too many bytes", size);
267 out:
268         (void)close(s);
269         OKN(size);
270 }
271
272 static void
273 test_connect_send_recv_count(struct sockaddr_un *sun, int count, size_t size)
274 {
275         char buf[size + 4];     /* Detect extra bytes and coalescing. */
276         size_t truncsize;
277         ssize_t ssize;
278         int i, s;
279
280         ANNNM(size, count);
281         NEW_SOCKET(s);
282         if (connect(s, (struct sockaddr *)sun, sizeof(*sun)) < 0)
283                 FAILNMERR("connect", size, count);
284         for (i = 0; i < count; i++) {
285                 usleep(5000);
286                 ssize = send(s, buf, size, 0);
287                 if (ssize < 0 && size >= SEQPACKET_RCVBUF)
288                         goto out;
289                 if (ssize < 0)
290                         FAILNMERR("send", size, count);
291                 if (ssize == 0)
292                         FAILNMERRX("send eof", size, count);
293                 if (ssize != size)
294                         FAILNMERRX("send size", size, count);
295         }
296
297         truncsize = min(size, BUFLEN);
298         for (i = 0; i < count; i++) {
299                 ssize = recv(s, buf, sizeof(buf), 0);
300                 if (ssize < 0)
301                         FAILNMERR("recv", size, count);
302                 if (ssize == 0)
303                         FAILNMERRX("recv eof", size, count);
304                 if (ssize < truncsize)
305                         FAILNMERRX("recv too few bytes", size, count);
306                 if (ssize > truncsize)
307                         FAILNMERRX("recv too many bytes", size, count);
308         }
309 out:
310         (void)close(s);
311         OKNM(size, count);
312 }
313
314 static void
315 test_sendto(struct sockaddr_un *sun)
316 {
317         ssize_t ssize;
318         char ch;
319         int s;
320
321         ANN();
322         NEW_SOCKET(s);
323         ssize = sendto(s, &ch, sizeof(ch), 0, (struct sockaddr *)sun,
324             sizeof(*sun));
325         if (ssize < 0)
326                 FAILERR("sendto");
327         (void)close(s);
328         OK();
329 }
330
331 static void
332 client(struct sockaddr_un *sun)
333 {
334         size_t sizes[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048,
335             4096, 8192, 16384, 32768, 65536 /*, 131072 */};
336         int c, i;
337
338         test_connect(sun);
339         test_connect_send(sun);
340         test_connect_shutdown_send(sun);
341
342         /*
343          * Try a range of sizes and packet counts.
344          */
345         for (i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++)
346                 test_connect_send_recv(sun, sizes[i]);
347         for (c = 1; c <= 8; c++) {
348                 for (i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++)
349                         test_connect_send_recv_count(sun, c, sizes[i]);
350         }
351         test_sendto(sun);
352         printf("client done\n");
353 }
354
355 int
356 main(int argc, char *argv[])
357 {
358         struct sockaddr_un sun;
359         char path[PATH_MAX];
360         pid_t pid_client, pid_server;
361         int i, s_listen;
362
363         snprintf(path, sizeof(path), "/tmp/lds_exercise.XXXXXXXXX");
364         if (mktemp(path) == NULL)
365                 FAILERR("mktemp");
366
367         s_listen = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
368         if (s_listen < 0) {
369                 (void)unlink(path);
370                 FAILERR("socket");
371         }
372
373         i = SEQPACKET_RCVBUF;
374         if (setsockopt(s_listen, SOL_SOCKET, SO_RCVBUF, &i, sizeof(i)) < 0) {
375                 (void)unlink(path);
376                 FAILERR("setsockopt SO_RCVBUF");
377         }
378
379         i = SEQPACKET_SNDBUF;
380         if (setsockopt(s_listen, SOL_SOCKET, SO_SNDBUF, &i, sizeof(i)) < 0) {
381                 (void)unlink(path);
382                 FAILERR("setsockopt SO_SNDBUF");
383         }
384
385         i = 1;
386         if (setsockopt(s_listen, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i))
387             < 0) {
388                 (void)unlink(path);
389                 FAILERR("setsockopt SO_NOSIGPIPE");
390         }
391
392         bzero(&sun, sizeof(sun));
393         sun.sun_len = sizeof(sun);
394         sun.sun_family = AF_LOCAL;
395         strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
396
397         if (bind(s_listen, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
398                 (void)unlink(path);
399                 FAILERR("bind");
400         }
401
402         if (listen(s_listen, -1) < 0) {
403                 (void)unlink(path);
404                 FAILERR("listen");
405         }
406
407         pid_server = fork();
408         if (pid_server < 0) {
409                 (void)unlink(path);
410                 FAILERR("fork");
411         }
412         if (pid_server == 0) {
413                 server(s_listen);
414                 return (0);
415         }
416
417         pid_client = fork();
418         if (pid_client < 0) {
419                 (void)kill(pid_server, SIGKILL);
420                 (void)unlink(path);
421                 FAILERR("fork");
422         }
423         if (pid_client == 0) {
424                 client(&sun);
425                 return (0);
426         }
427
428         /*
429          * When the client is done, kill the server and clean up.
430          */
431         (void)waitpid(pid_client, NULL, 0);
432         (void)kill(pid_server, SIGKILL);
433         (void)unlink(path);
434         return (0);
435 }