0fd961e0a6df7d909df6582f8b146def14e7398e
[vpp.git] / src / plugins / hs_apps / vcl / sock_test_server.c
1 /*
2  * Copyright (c) 2017-2019 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <unistd.h>
17 #include <errno.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <time.h>
23 #include <ctype.h>
24 #include <hs_apps/vcl/sock_test.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <sys/ioctl.h>
28 #include <sys/epoll.h>
29 #include <sys/un.h>
30
31 #define SOCK_SERVER_MAX_TEST_CONN  10
32 #define SOCK_SERVER_MAX_EPOLL_EVENTS 10
33
34 typedef struct
35 {
36   uint8_t is_alloc;
37   int fd;
38   uint8_t *buf;
39   uint32_t buf_size;
40   vcl_test_cfg_t cfg;
41   vcl_test_stats_t stats;
42 } sock_server_conn_t;
43
44 typedef struct
45 {
46   uint32_t port;
47   uint32_t address_ip6;
48   uint32_t transport_udp;
49 } sock_server_cfg_t;
50
51 typedef struct
52 {
53   int listen_fd;
54   sock_server_cfg_t cfg;
55   int epfd;
56   struct epoll_event listen_ev;
57   struct epoll_event wait_events[SOCK_SERVER_MAX_EPOLL_EVENTS];
58   int af_unix_listen_fd;
59   int af_unix_fd;
60   struct epoll_event af_unix_listen_ev;
61   struct sockaddr_un serveraddr;
62   uint32_t af_unix_xacts;
63   size_t num_conn;
64   size_t conn_pool_size;
65   sock_server_conn_t *conn_pool;
66   int nfds;
67   fd_set rd_fdset;
68   fd_set wr_fdset;
69   struct timeval timeout;
70 } sock_server_main_t;
71
72 sock_server_main_t sock_server_main;
73
74 static inline void
75 conn_pool_expand (size_t expand_size)
76 {
77   sock_server_main_t *ssm = &sock_server_main;
78   sock_server_conn_t *conn_pool;
79   size_t new_size = ssm->conn_pool_size + expand_size;
80   int i;
81
82   conn_pool = realloc (ssm->conn_pool, new_size * sizeof (*ssm->conn_pool));
83   if (conn_pool)
84     {
85       for (i = ssm->conn_pool_size; i < new_size; i++)
86         {
87           sock_server_conn_t *conn = &conn_pool[i];
88           memset (conn, 0, sizeof (*conn));
89           vcl_test_cfg_init (&conn->cfg);
90           vcl_test_buf_alloc (&conn->cfg, 1 /* is_rxbuf */ ,
91                               &conn->buf, &conn->buf_size);
92           conn->cfg.txbuf_size = conn->cfg.rxbuf_size;
93         }
94
95       ssm->conn_pool = conn_pool;
96       ssm->conn_pool_size = new_size;
97     }
98   else
99     {
100       int errno_val = errno;
101       perror ("ERROR in conn_pool_expand()");
102       fprintf (stderr, "SERVER: ERROR: Memory allocation "
103                "failed (errno = %d)!\n", errno_val);
104     }
105 }
106
107 static inline sock_server_conn_t *
108 conn_pool_alloc (void)
109 {
110   sock_server_main_t *ssm = &sock_server_main;
111   int i;
112
113   for (i = 0; i < ssm->conn_pool_size; i++)
114     {
115       if (!ssm->conn_pool[i].is_alloc)
116         {
117           ssm->conn_pool[i].is_alloc = 1;
118           return (&ssm->conn_pool[i]);
119         }
120     }
121
122   return 0;
123 }
124
125 static inline void
126 conn_pool_free (sock_server_conn_t * conn)
127 {
128   conn->fd = 0;
129   conn->is_alloc = 0;
130 }
131
132 static inline void
133 sync_config_and_reply (sock_server_conn_t * conn, vcl_test_cfg_t * rx_cfg)
134 {
135   conn->cfg = *rx_cfg;
136   vcl_test_buf_alloc (&conn->cfg, 1 /* is_rxbuf */ ,
137                       &conn->buf, &conn->buf_size);
138   conn->cfg.txbuf_size = conn->cfg.rxbuf_size;
139
140   if (conn->cfg.verbose)
141     {
142       printf ("\nSERVER (fd %d): Replying to cfg message!\n", conn->fd);
143       vcl_test_cfg_dump (&conn->cfg, 0 /* is_client */ );
144     }
145   (void) sock_test_write (conn->fd, (uint8_t *) & conn->cfg,
146                           sizeof (conn->cfg), NULL, conn->cfg.verbose);
147 }
148
149 static void
150 stream_test_server_start_stop (sock_server_conn_t * conn,
151                                vcl_test_cfg_t * rx_cfg)
152 {
153   sock_server_main_t *ssm = &sock_server_main;
154   int client_fd = conn->fd;
155   vcl_test_t test = rx_cfg->test;
156
157   if (rx_cfg->ctrl_handle == conn->fd)
158     {
159       int i;
160       clock_gettime (CLOCK_REALTIME, &conn->stats.stop);
161
162       for (i = 0; i < ssm->conn_pool_size; i++)
163         {
164           sock_server_conn_t *tc = &ssm->conn_pool[i];
165
166           if (tc->cfg.ctrl_handle == conn->fd)
167             {
168               vcl_test_stats_accumulate (&conn->stats, &tc->stats);
169
170               if (conn->cfg.verbose)
171                 {
172                   static char buf[64];
173
174                   snprintf (buf, sizeof (buf), "SERVER (fd %d) RESULTS",
175                             tc->fd);
176                   vcl_test_stats_dump (buf, &tc->stats, 1 /* show_rx */ ,
177                                        test == VCL_TEST_TYPE_BI
178                                        /* show tx */ ,
179                                        conn->cfg.verbose);
180                 }
181             }
182         }
183
184       vcl_test_stats_dump ("SERVER RESULTS", &conn->stats, 1 /* show_rx */ ,
185                            (test == VCL_TEST_TYPE_BI) /* show_tx */ ,
186                            conn->cfg.verbose);
187       vcl_test_cfg_dump (&conn->cfg, 0 /* is_client */ );
188       if (conn->cfg.verbose)
189         {
190           printf ("  sock server main\n"
191                   VCL_TEST_SEPARATOR_STRING
192                   "       buf:  %p\n"
193                   "  buf size:  %u (0x%08x)\n"
194                   VCL_TEST_SEPARATOR_STRING,
195                   conn->buf, conn->buf_size, conn->buf_size);
196         }
197
198       sync_config_and_reply (conn, rx_cfg);
199       printf ("\nSERVER (fd %d): %s-directional Stream Test Complete!\n"
200               SOCK_TEST_BANNER_STRING "\n", conn->fd,
201               test == VCL_TEST_TYPE_BI ? "Bi" : "Uni");
202     }
203   else
204     {
205       printf ("\n" SOCK_TEST_BANNER_STRING
206               "SERVER (fd %d): %s-directional Stream Test!\n"
207               "  Sending client the test cfg to start streaming data...\n",
208               client_fd, test == VCL_TEST_TYPE_BI ? "Bi" : "Uni");
209
210       rx_cfg->ctrl_handle = (rx_cfg->ctrl_handle == ~0) ? conn->fd :
211         rx_cfg->ctrl_handle;
212
213       sync_config_and_reply (conn, rx_cfg);
214
215       /* read the 1st chunk, record start time */
216       memset (&conn->stats, 0, sizeof (conn->stats));
217       clock_gettime (CLOCK_REALTIME, &conn->stats.start);
218     }
219 }
220
221
222 static inline void
223 stream_test_server (sock_server_conn_t * conn, int rx_bytes)
224 {
225   int client_fd = conn->fd;
226   vcl_test_t test = conn->cfg.test;
227
228   if (test == VCL_TEST_TYPE_BI)
229     (void) sock_test_write (client_fd, conn->buf, rx_bytes, &conn->stats,
230                             conn->cfg.verbose);
231
232   if (conn->stats.rx_bytes >= conn->cfg.total_bytes)
233     {
234       clock_gettime (CLOCK_REALTIME, &conn->stats.stop);
235     }
236 }
237
238 static inline void
239 af_unix_echo (void)
240 {
241   sock_server_main_t *ssm = &sock_server_main;
242   int af_unix_client_fd;
243   int rv;
244   int errno_val;
245   uint8_t buffer[256];
246   size_t nbytes = strlen (SOCK_TEST_MIXED_EPOLL_DATA) + 1;
247
248 #if HAVE_ACCEPT4
249   af_unix_client_fd = accept4 (ssm->af_unix_listen_fd,
250                                (struct sockaddr *) NULL, NULL, NULL);
251 #else
252   af_unix_client_fd = accept (ssm->af_unix_listen_fd,
253                               (struct sockaddr *) NULL, NULL);
254 #endif
255   if (af_unix_client_fd < 0)
256     {
257       errno_val = errno;
258       perror ("ERROR in af_unix_accept()");
259       fprintf (stderr, "SERVER: ERROR: accept failed "
260                "(errno = %d)!\n", errno_val);
261       return;
262     }
263
264   printf ("SERVER: Got an AF_UNIX connection -- fd = %d (0x%08x)!\n",
265           af_unix_client_fd, af_unix_client_fd);
266
267   memset (buffer, 0, sizeof (buffer));
268
269   rv = read (af_unix_client_fd, buffer, nbytes);
270   if (rv < 0)
271     {
272       errno_val = errno;
273       perror ("ERROR in af_unix_echo(): read() failed");
274       fprintf (stderr, "SERVER: ERROR: read(af_unix_client_fd %d (0x%x), "
275                "nbytes %lu) failed (errno = %d)!\n", af_unix_client_fd,
276                af_unix_client_fd, nbytes, errno_val);
277       goto done;
278     }
279   /* Make the buffer is NULL-terminated. */
280   buffer[sizeof (buffer) - 1] = 0;
281   printf ("SERVER (AF_UNIX): RX (%d bytes) - '%s'\n", rv, buffer);
282
283   if (!strncmp (SOCK_TEST_MIXED_EPOLL_DATA, (const char *) buffer, nbytes))
284     {
285       rv = write (af_unix_client_fd, buffer, nbytes);
286       if (rv < 0)
287         {
288           errno_val = errno;
289           perror ("ERROR in af_unix_echo(): write() failed");
290           fprintf (stderr,
291                    "SERVER: ERROR: write(af_unix_client_fd %d (0x%x), "
292                    "\"%s\", nbytes %ld) failed (errno = %d)!\n",
293                    af_unix_client_fd, af_unix_client_fd, buffer, nbytes,
294                    errno_val);
295           goto done;
296         }
297       printf ("SERVER (AF_UNIX): TX (%d bytes) - '%s'\n", rv, buffer);
298       ssm->af_unix_xacts++;
299     }
300 done:
301   close (af_unix_client_fd);
302 }
303
304 static inline void
305 new_client (void)
306 {
307   sock_server_main_t *ssm = &sock_server_main;
308   int client_fd;
309   sock_server_conn_t *conn;
310
311   if (ssm->conn_pool_size < (ssm->num_conn + SOCK_SERVER_MAX_TEST_CONN + 1))
312     conn_pool_expand (SOCK_SERVER_MAX_TEST_CONN + 1);
313
314   conn = conn_pool_alloc ();
315   if (!conn)
316     {
317       fprintf (stderr, "\nSERVER: ERROR: No free connections!\n");
318       return;
319     }
320
321 #if HAVE_ACCEPT4
322   client_fd = accept4 (ssm->listen_fd, (struct sockaddr *) NULL, NULL, NULL);
323 #else
324   client_fd = accept (ssm->listen_fd, (struct sockaddr *) NULL, NULL);
325 #endif
326   if (client_fd < 0)
327     {
328       int errno_val;
329       errno_val = errno;
330       perror ("ERROR in new_client()");
331       fprintf (stderr, "SERVER: ERROR: accept failed "
332                "(errno = %d)!\n", errno_val);
333       return;
334     }
335
336   printf ("SERVER: Got a connection -- fd = %d (0x%08x)!\n",
337           client_fd, client_fd);
338
339   conn->fd = client_fd;
340
341   struct epoll_event ev;
342   int rv;
343
344   ev.events = EPOLLIN;
345   ev.data.u64 = conn - ssm->conn_pool;
346   rv = epoll_ctl (ssm->epfd, EPOLL_CTL_ADD, client_fd, &ev);
347
348   if (rv < 0)
349     {
350       int errno_val;
351       errno_val = errno;
352       perror ("ERROR in new_client()");
353       fprintf (stderr, "SERVER: ERROR: epoll_ctl failed (errno = %d)!\n",
354                errno_val);
355     }
356   else
357     ssm->nfds++;
358 }
359
360 static int
361 socket_server_echo_af_unix_init (sock_server_main_t * ssm)
362 {
363   int rv, errno_val;
364
365   if (ssm->af_unix_listen_fd > 0)
366     return 0;
367
368   unlink ((const char *) SOCK_TEST_AF_UNIX_FILENAME);
369   ssm->af_unix_listen_fd = socket (AF_UNIX, SOCK_STREAM, 0);
370   if (ssm->af_unix_listen_fd < 0)
371     {
372       errno_val = errno;
373       perror ("ERROR in main(): socket(AF_UNIX) failed");
374       fprintf (stderr,
375                "SERVER: ERROR: socket(AF_UNIX, SOCK_STREAM, 0) failed "
376                "(errno = %d)!\n", errno_val);
377       return ssm->af_unix_listen_fd;
378     }
379
380   memset (&ssm->serveraddr, 0, sizeof (ssm->serveraddr));
381   ssm->serveraddr.sun_family = AF_UNIX;
382   strncpy (ssm->serveraddr.sun_path, SOCK_TEST_AF_UNIX_FILENAME,
383            sizeof (ssm->serveraddr.sun_path));
384
385   rv = bind (ssm->af_unix_listen_fd, (struct sockaddr *) &ssm->serveraddr,
386              SUN_LEN (&ssm->serveraddr));
387   if (rv < 0)
388     {
389       errno_val = errno;
390       perror ("ERROR in main(): bind(SOCK_TEST_AF_UNIX_FILENAME) failed");
391       fprintf (stderr, "SERVER: ERROR: bind() fd %d, \"%s\": "
392                "failed (errno = %d)!\n", ssm->af_unix_listen_fd,
393                SOCK_TEST_AF_UNIX_FILENAME, errno_val);
394       close (ssm->af_unix_listen_fd);
395       unlink ((const char *) SOCK_TEST_AF_UNIX_FILENAME);
396       return rv;
397     }
398
399   rv = listen (ssm->af_unix_listen_fd, 10);
400   if (rv < 0)
401     {
402       errno_val = errno;
403       perror ("ERROR in main(): listen(AF_UNIX) failed");
404       fprintf (stderr, "SERVER: ERROR: listen() fd %d, \"%s\": "
405                "failed (errno = %d)!\n", ssm->af_unix_listen_fd,
406                SOCK_TEST_AF_UNIX_FILENAME, errno_val);
407       close (ssm->af_unix_listen_fd);
408       unlink ((const char *) SOCK_TEST_AF_UNIX_FILENAME);
409       return rv;
410     }
411
412   ssm->af_unix_listen_ev.events = EPOLLIN;
413   ssm->af_unix_listen_ev.data.u32 = SOCK_TEST_AF_UNIX_ACCEPT_DATA;
414   rv = epoll_ctl (ssm->epfd, EPOLL_CTL_ADD, ssm->af_unix_listen_fd,
415                   &ssm->af_unix_listen_ev);
416   if (rv < 0)
417     {
418       errno_val = errno;
419       perror ("ERROR in main(): mixed epoll_ctl(EPOLL_CTL_ADD)");
420       fprintf (stderr, "SERVER: ERROR: mixed epoll_ctl(epfd %d (0x%x), "
421                "EPOLL_CTL_ADD, af_unix_listen_fd %d (0x%x), EPOLLIN) failed "
422                "(errno = %d)!\n", ssm->epfd, ssm->epfd,
423                ssm->af_unix_listen_fd, ssm->af_unix_listen_fd, errno_val);
424       close (ssm->af_unix_listen_fd);
425       unlink ((const char *) SOCK_TEST_AF_UNIX_FILENAME);
426       return rv;
427     }
428
429   return 0;
430 }
431
432 void
433 print_usage_and_exit (void)
434 {
435   fprintf (stderr,
436            "sock_test_server [OPTIONS] <port>\n"
437            "  OPTIONS\n"
438            "  -h               Print this message and exit.\n"
439            "  -6               Use IPv6\n"
440            "  -u               Use UDP transport layer\n");
441   exit (1);
442 }
443
444 int
445 main (int argc, char **argv)
446 {
447   sock_server_main_t *ssm = &sock_server_main;
448   int client_fd, rv, main_rv = 0;
449   int tx_bytes, rx_bytes, nbytes;
450   sock_server_conn_t *conn;
451   vcl_test_cfg_t *rx_cfg;
452   uint32_t xtra = 0;
453   uint64_t xtra_bytes = 0;
454   struct sockaddr_storage servaddr;
455   int errno_val;
456   int c, v, i;
457   uint16_t port = VCL_TEST_SERVER_PORT;
458   uint32_t servaddr_size;
459
460   opterr = 0;
461   while ((c = getopt (argc, argv, "6D")) != -1)
462     switch (c)
463       {
464       case '6':
465         ssm->cfg.address_ip6 = 1;
466         break;
467
468       case 'D':
469         ssm->cfg.transport_udp = 1;
470         break;
471
472       case '?':
473         switch (optopt)
474           {
475           default:
476             if (isprint (optopt))
477               fprintf (stderr, "SERVER: ERROR: Unknown "
478                        "option `-%c'.\n", optopt);
479             else
480               fprintf (stderr, "SERVER: ERROR: Unknown "
481                        "option character `\\x%x'.\n", optopt);
482           }
483         /* fall thru */
484       case 'h':
485       default:
486         print_usage_and_exit ();
487       }
488
489   if (argc < (optind + 1))
490     {
491       fprintf (stderr, "SERVER: ERROR: Insufficient number of arguments!\n");
492       print_usage_and_exit ();
493     }
494
495   if (sscanf (argv[optind], "%d", &v) == 1)
496     port = (uint16_t) v;
497   else
498     {
499       fprintf (stderr, "SERVER: ERROR: Invalid port (%s)!\n", argv[optind]);
500       print_usage_and_exit ();
501     }
502
503   conn_pool_expand (SOCK_SERVER_MAX_TEST_CONN + 1);
504
505   ssm->listen_fd = socket (ssm->cfg.address_ip6 ? AF_INET6 : AF_INET,
506                            ssm->cfg.transport_udp ? SOCK_DGRAM : SOCK_STREAM,
507                            0);
508
509   if (ssm->listen_fd < 0)
510     {
511       errno_val = errno;
512       perror ("ERROR in main()");
513       fprintf (stderr, "SERVER: ERROR: socket() failed "
514                "(errno = %d)!\n", errno_val);
515       return ssm->listen_fd;
516     }
517
518   memset (&servaddr, 0, sizeof (servaddr));
519
520   if (ssm->cfg.address_ip6)
521     {
522       struct sockaddr_in6 *server_addr = (struct sockaddr_in6 *) &servaddr;
523       servaddr_size = sizeof (*server_addr);
524       server_addr->sin6_family = AF_INET6;
525       server_addr->sin6_addr = in6addr_any;
526       server_addr->sin6_port = htons (port);
527     }
528   else
529     {
530       struct sockaddr_in *server_addr = (struct sockaddr_in *) &servaddr;
531       servaddr_size = sizeof (*server_addr);
532       server_addr->sin_family = AF_INET;
533       server_addr->sin_addr.s_addr = htonl (INADDR_ANY);
534       server_addr->sin_port = htons (port);
535     }
536
537   rv = bind (ssm->listen_fd, (struct sockaddr *) &servaddr, servaddr_size);
538   if (rv < 0)
539     {
540       errno_val = errno;
541       perror ("ERROR bind returned");
542       fprintf (stderr, "SERVER: ERROR: bind failed (errno = %d)!\n",
543                errno_val);
544       return rv;
545     }
546   if (fcntl (ssm->listen_fd, F_SETFL, O_NONBLOCK) < 0)
547     {
548       errno_val = errno;
549       perror ("ERROR fcntl returned");
550       fprintf (stderr, "SERVER: ERROR: fcntl failed (errno = %d)!\n",
551                errno_val);
552       return rv;
553     }
554   rv = listen (ssm->listen_fd, 10);
555   if (rv < 0)
556     {
557       errno_val = errno;
558       perror ("ERROR in main()");
559       fprintf (stderr, "SERVER: ERROR: listen failed "
560                "(errno = %d)!\n", errno_val);
561       return rv;
562     }
563
564   ssm->epfd = epoll_create (1);
565   if (ssm->epfd < 0)
566     {
567       errno_val = errno;
568       perror ("ERROR in main()");
569       fprintf (stderr, "SERVER: ERROR: epoll_create failed (errno = %d)!\n",
570                errno_val);
571       return ssm->epfd;
572     }
573
574   ssm->listen_ev.events = EPOLLIN;
575   ssm->listen_ev.data.u32 = ~0;
576
577   rv = epoll_ctl (ssm->epfd, EPOLL_CTL_ADD, ssm->listen_fd, &ssm->listen_ev);
578   if (rv < 0)
579     {
580       errno_val = errno;
581       perror ("ERROR in main()");
582       fprintf (stderr, "SERVER: ERROR: epoll_ctl failed "
583                "(errno = %d)!\n", errno_val);
584       return rv;
585     }
586
587   printf ("\nSERVER: Waiting for a client to connect on port %d...\n", port);
588
589   while (1)
590     {
591       int num_ev;
592       num_ev = epoll_wait (ssm->epfd, ssm->wait_events,
593                            SOCK_SERVER_MAX_EPOLL_EVENTS, 60000);
594       if (num_ev < 0)
595         {
596           perror ("epoll_wait()");
597           fprintf (stderr, "\nSERVER: ERROR: epoll_wait() "
598                    "failed -- aborting!\n");
599           main_rv = -1;
600           goto done;
601         }
602       if (num_ev == 0)
603         {
604           fprintf (stderr, "\nSERVER: epoll_wait() timeout!\n");
605           continue;
606         }
607       for (i = 0; i < num_ev; i++)
608         {
609           conn = &ssm->conn_pool[ssm->wait_events[i].data.u32];
610           if (ssm->wait_events[i].events & (EPOLLHUP | EPOLLRDHUP))
611             {
612               close (conn->fd);
613               continue;
614             }
615           if (ssm->wait_events[i].data.u32 == ~0)
616             {
617               new_client ();
618               continue;
619             }
620           else if (ssm->wait_events[i].data.u32 ==
621                    SOCK_TEST_AF_UNIX_ACCEPT_DATA)
622             {
623               af_unix_echo ();
624               continue;
625             }
626           client_fd = conn->fd;
627
628           if (EPOLLIN & ssm->wait_events[i].events)
629             {
630             read_again:
631               rx_bytes = sock_test_read (client_fd, conn->buf,
632                                          conn->buf_size, &conn->stats);
633               if (rx_bytes > 0)
634                 {
635                   rx_cfg = (vcl_test_cfg_t *) conn->buf;
636                   if (rx_cfg->magic == VCL_TEST_CFG_CTRL_MAGIC)
637                     {
638                       if (rx_cfg->verbose)
639                         {
640                           printf ("SERVER (fd %d): Received a cfg message!\n",
641                                   client_fd);
642                           vcl_test_cfg_dump (rx_cfg, 0 /* is_client */ );
643                         }
644
645                       if (rx_bytes != sizeof (*rx_cfg))
646                         {
647                           printf ("SERVER (fd %d): Invalid cfg message "
648                                   "size (%d)!\n  Should be %lu bytes.\n",
649                                   client_fd, rx_bytes, sizeof (*rx_cfg));
650                           conn->cfg.rxbuf_size = 0;
651                           conn->cfg.num_writes = 0;
652                           if (conn->cfg.verbose)
653                             {
654                               printf ("SERVER (fd %d): Replying to "
655                                       "cfg message!\n", client_fd);
656                               vcl_test_cfg_dump (rx_cfg, 0 /* is_client */ );
657                             }
658                           sock_test_write (client_fd, (uint8_t *) & conn->cfg,
659                                            sizeof (conn->cfg), NULL,
660                                            conn->cfg.verbose);
661                           continue;
662                         }
663
664                       switch (rx_cfg->test)
665                         {
666                         case VCL_TEST_TYPE_NONE:
667                           sync_config_and_reply (conn, rx_cfg);
668                           break;
669
670                         case VCL_TEST_TYPE_ECHO:
671                           if (socket_server_echo_af_unix_init (ssm))
672                             goto done;
673                           sync_config_and_reply (conn, rx_cfg);
674                           break;
675
676                         case VCL_TEST_TYPE_BI:
677                         case VCL_TEST_TYPE_UNI:
678                           stream_test_server_start_stop (conn, rx_cfg);
679                           break;
680
681                         case VCL_TEST_TYPE_EXIT:
682                           printf ("SERVER: Have a great day, "
683                                   "connection %d!\n", client_fd);
684                           close (client_fd);
685                           conn_pool_free (conn);
686                           printf ("SERVER: Closed client fd %d\n", client_fd);
687                           ssm->nfds--;
688                           if (!ssm->nfds)
689                             {
690                               printf ("SERVER: All client connections "
691                                       "closed.\n\nSERVER: "
692                                       "May the force be with you!\n\n");
693                               goto done;
694                             }
695                           break;
696
697                         default:
698                           fprintf (stderr,
699                                    "SERVER: ERROR: Unknown test type!\n");
700                           vcl_test_cfg_dump (rx_cfg, 0 /* is_client */ );
701                           break;
702                         }
703                       continue;
704                     }
705
706                   else if ((conn->cfg.test == VCL_TEST_TYPE_UNI) ||
707                            (conn->cfg.test == VCL_TEST_TYPE_BI))
708                     {
709                       stream_test_server (conn, rx_bytes);
710                       if (ioctl (conn->fd, FIONREAD))
711                         goto read_again;
712                       continue;
713                     }
714
715                   else if (isascii (conn->buf[0]))
716                     {
717                       // If it looks vaguely like a string, make sure it's terminated
718                       ((char *) conn->buf)[rx_bytes <
719                                            conn->buf_size ? rx_bytes :
720                                            conn->buf_size - 1] = 0;
721                       printf ("SERVER (fd %d): RX (%d bytes) - '%s'\n",
722                               conn->fd, rx_bytes, conn->buf);
723                     }
724                 }
725               else              // rx_bytes < 0
726                 {
727                   if (errno == ECONNRESET)
728                     {
729                       printf ("\nSERVER: Connection reset by remote peer.\n"
730                               "  Y'all have a great day now!\n\n");
731                       break;
732                     }
733                   else
734                     continue;
735                 }
736
737               if (isascii (conn->buf[0]))
738                 {
739                   /* If it looks vaguely like a string,
740                    * make sure it's terminated
741                    */
742                   ((char *) conn->buf)[rx_bytes <
743                                        conn->buf_size ? rx_bytes :
744                                        conn->buf_size - 1] = 0;
745                   if (xtra)
746                     fprintf (stderr, "SERVER: ERROR: "
747                              "FIFO not drained in previous test!\n"
748                              "       extra chunks %u (0x%x)\n"
749                              "        extra bytes %lu (0x%lx)\n",
750                              xtra, xtra, xtra_bytes, xtra_bytes);
751
752                   xtra = 0;
753                   xtra_bytes = 0;
754
755                   if (conn->cfg.verbose)
756                     printf ("SERVER (fd %d): Echoing back\n", client_fd);
757
758                   nbytes = strlen ((const char *) conn->buf) + 1;
759
760                   tx_bytes = sock_test_write (client_fd, conn->buf,
761                                               nbytes, &conn->stats,
762                                               conn->cfg.verbose);
763                   if (tx_bytes >= 0)
764                     printf ("SERVER (fd %d): TX (%d bytes) - '%s'\n",
765                             conn->fd, tx_bytes, conn->buf);
766                 }
767
768               else              // Extraneous read data from non-echo tests???
769                 {
770                   xtra++;
771                   xtra_bytes += rx_bytes;
772                 }
773             }
774         }
775     }
776
777 done:
778   close (ssm->listen_fd);
779   close (ssm->af_unix_listen_fd);
780   unlink ((const char *) SOCK_TEST_AF_UNIX_FILENAME);
781
782   if (ssm->conn_pool)
783     free (ssm->conn_pool);
784
785   return main_rv;
786 }
787
788 /*
789  * fd.io coding-style-patch-verification: ON
790  *
791  * Local Variables:
792  * eval: (c-set-style "gnu")
793  * End:
794  */