1 /* Test non-blocking use of the UDP client.
2 Copyright (C) 2017-2023 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
19 #include <netinet/in.h>
24 #include <support/check.h>
25 #include <support/namespace.h>
26 #include <support/test-driver.h>
27 #include <support/xsocket.h>
28 #include <support/xunistd.h>
29 #include <sys/socket.h>
33 /* Test data serialization and deserialization. */
43 xdr_test_query (XDR
*xdrs
, void *data
, ...)
45 struct test_query
*p
= data
;
46 return xdr_uint32_t (xdrs
, &p
->a
)
47 && xdr_uint32_t (xdrs
, &p
->b
)
48 && xdr_uint32_t (xdrs
, &p
->timeout_ms
);
59 xdr_test_response (XDR
*xdrs
, void *data
, ...)
61 struct test_response
*p
= data
;
62 return xdr_uint32_t (xdrs
, &p
->server_id
)
63 && xdr_uint32_t (xdrs
, &p
->seq
)
64 && xdr_uint32_t (xdrs
, &p
->sum
);
67 /* Implementation of the test server. */
71 /* Number of test servers to run. */
74 /* RPC parameters, chosen at random. */
78 /* Main RPC operation. */
81 /* Request process termination. */
84 /* Special exit status to mark successful processing. */
88 /* Set by the parent process to tell test servers apart. */
91 /* Implementation of the test server. */
93 server_dispatch (struct svc_req
*request
, SVCXPRT
*transport
)
95 /* Query sequence number. */
96 static uint32_t seq
= 0;
98 static bool proc_add_seen
;
101 printf ("info: server_dispatch server_id=%d seq=%u rq_proc=%lu\n",
102 server_id
, seq
, request
->rq_proc
);
104 switch (request
->rq_proc
)
108 struct test_query query
;
109 memset (&query
, 0xc0, sizeof (query
));
111 (svc_getargs (transport
, xdr_test_query
,
115 printf (" a=%u b=%u timeout_ms=%u\n",
116 query
.a
, query
.b
, query
.timeout_ms
);
118 usleep (query
.timeout_ms
* 1000);
120 struct test_response response
=
122 .server_id
= server_id
,
124 .sum
= query
.a
+ query
.b
,
126 TEST_VERIFY (svc_sendreply (transport
, xdr_test_response
,
127 (void *) &response
));
129 printf (" server id %d response seq=%u sent\n", server_id
, seq
);
130 proc_add_seen
= true;
135 TEST_VERIFY (proc_add_seen
);
136 TEST_VERIFY (svc_sendreply (transport
, (xdrproc_t
) xdr_void
, NULL
));
141 FAIL_EXIT1 ("invalid rq_proc value: %lu", request
->rq_proc
);
146 /* Return the number seconds since an arbitrary point in time. */
152 if (clock_gettime (CLOCK_MONOTONIC
, &ts
) == 0)
153 return ts
.tv_sec
+ ts
.tv_nsec
* 1e-9;
157 TEST_VERIFY_EXIT (gettimeofday (&tv
, NULL
) == 0);
158 return tv
.tv_sec
+ tv
.tv_usec
* 1e-6;
165 support_become_root ();
166 support_enter_network_namespace ();
168 /* Information about the test servers. */
172 struct sockaddr_in address
;
175 } servers
[SERVER_COUNT
];
177 /* Spawn the test servers. */
178 for (int i
= 0; i
< SERVER_COUNT
; ++i
)
180 servers
[i
].transport
= svcudp_create (RPC_ANYSOCK
);
181 TEST_VERIFY_EXIT (servers
[i
].transport
!= NULL
);
182 servers
[i
].address
= (struct sockaddr_in
)
184 .sin_family
= AF_INET
,
185 .sin_addr
.s_addr
= htonl (INADDR_LOOPBACK
),
186 .sin_port
= htons (servers
[i
].transport
->xp_port
),
188 servers
[i
].xid
= 0xabcd0101 + i
;
190 printf ("info: setting up server %d xid=%x on port %d\n",
191 i
, servers
[i
].xid
, servers
[i
].transport
->xp_port
);
194 servers
[i
].pid
= xfork ();
195 if (servers
[i
].pid
== 0)
197 TEST_VERIFY (svc_register (servers
[i
].transport
,
198 PROGNUM
, VERSNUM
, server_dispatch
, 0));
200 FAIL_EXIT1 ("supposed to be unreachable");
202 /* We need to close the socket so that we do not accidentally
203 consume the request. */
204 TEST_VERIFY (close (servers
[i
].transport
->xp_sock
) == 0);
208 /* The following code mirrors what ypbind does. */
210 /* Copied from clnt_udp.c (like ypbind). */
215 struct sockaddr_in cu_raddr
;
217 struct timeval cu_wait
;
218 struct timeval cu_total
;
219 struct rpc_err cu_error
;
228 int client_socket
= xsocket (AF_INET
, SOCK_DGRAM
| SOCK_NONBLOCK
, 0);
229 CLIENT
*clnt
= clntudp_create (&servers
[0].address
, PROGNUM
, VERSNUM
,
230 /* 5 seconds per-response timeout. */
231 ((struct timeval
) { 5, 0 }),
233 TEST_VERIFY (clnt
!= NULL
);
234 clnt
->cl_auth
= authunix_create_default ();
236 struct timeval zero
= { 0, 0 };
237 TEST_VERIFY (clnt_control (clnt
, CLSET_TIMEOUT
, (void *) &zero
));
240 /* Poke at internal data structures (like ypbind). */
241 struct cu_data
*cu
= (struct cu_data
*) clnt
->cl_private
;
243 /* Send a ping to each server. */
244 double before_pings
= get_ticks ();
245 for (int i
= 0; i
< SERVER_COUNT
; ++i
)
248 printf ("info: sending server %d ping\n", i
);
249 /* Reset the xid because it is changed by each invocation of
250 clnt_call. Subtract one to compensate for the xid update
252 *((uint32_t *) (cu
->cu_outbuf
)) = servers
[i
].xid
- 1;
253 cu
->cu_raddr
= servers
[i
].address
;
255 struct test_query query
= { .a
= 100, .b
= i
+ 1 };
257 /* Shorter timeout to prefer this server. These timeouts must
258 be much shorter than the 5-second per-response timeout
259 configured with clntudp_create. */
260 query
.timeout_ms
= 750;
262 query
.timeout_ms
= 1500;
263 struct test_response response
= { 0 };
264 /* NB: Do not check the return value. The server reply will
265 prove that the call worked. */
266 double before_one_ping
= get_ticks ();
267 clnt_call (clnt
, PROC_ADD
,
268 xdr_test_query
, (void *) &query
,
269 xdr_test_response
, (void *) &response
,
270 ((struct timeval
) { 0, 0 }));
271 double after_one_ping
= get_ticks ();
273 printf ("info: non-blocking send took %f seconds\n",
274 after_one_ping
- before_one_ping
);
275 /* clnt_call should return immediately. Accept some delay in
276 case the process is descheduled. */
277 TEST_VERIFY (after_one_ping
- before_one_ping
< 0.3);
280 /* Collect the non-blocking response. */
282 printf ("info: collecting response\n");
283 struct test_response response
= { 0 };
285 (clnt_call (clnt
, PROC_ADD
, NULL
, NULL
,
286 xdr_test_response
, (void *) &response
,
287 ((struct timeval
) { 0, 0 })) == RPC_SUCCESS
);
288 double after_pings
= get_ticks ();
290 printf ("info: send/receive took %f seconds\n",
291 after_pings
- before_pings
);
292 /* Expected timeout is 0.75 seconds. */
293 TEST_VERIFY (0.70 <= after_pings
- before_pings
);
294 TEST_VERIFY (after_pings
- before_pings
< 1.2);
297 memcpy (&xid
, &cu
->cu_inbuf
, sizeof (xid
));
299 printf ("info: non-blocking response: xid=%x server_id=%u seq=%u sum=%u\n",
300 xid
, response
.server_id
, response
.seq
, response
.sum
);
301 /* Check that the reply from the preferred server was used. */
302 TEST_VERIFY (servers
[1].xid
== xid
);
303 TEST_VERIFY (response
.server_id
== 1);
304 TEST_VERIFY (response
.seq
== 1);
305 TEST_VERIFY (response
.sum
== 102);
307 auth_destroy (clnt
->cl_auth
);
310 for (int i
= 0; i
< SERVER_COUNT
; ++i
)
313 printf ("info: requesting server %d termination\n", i
);
314 client_socket
= RPC_ANYSOCK
;
315 clnt
= clntudp_create (&servers
[i
].address
, PROGNUM
, VERSNUM
,
316 ((struct timeval
) { 5, 0 }),
318 TEST_VERIFY_EXIT (clnt
!= NULL
);
319 TEST_VERIFY (clnt_call (clnt
, PROC_EXIT
,
320 (xdrproc_t
) xdr_void
, NULL
,
321 (xdrproc_t
) xdr_void
, NULL
,
322 ((struct timeval
) { 3, 0 })) == RPC_SUCCESS
);
326 xwaitpid (servers
[i
].pid
, &status
, 0);
327 TEST_VERIFY (WIFEXITED (status
) && WEXITSTATUS (status
) == EXIT_MARKER
);
333 #include <support/test-driver.c>