Commit | Line | Data |
---|---|---|
0b5a4de9 JG |
1 | /* |
2 | * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com> | |
3 | * | |
4 | * SPDX-License-Identifier: LGPL-2.1-only | |
5 | * | |
6 | */ | |
7 | ||
7b0027b3 | 8 | #include <common/compat/fcntl.h> |
0b5a4de9 JG |
9 | #include <common/sessiond-comm/sessiond-comm.h> |
10 | #include <common/payload.h> | |
11 | #include <common/payload-view.h> | |
12 | #include <common/unix.h> | |
13 | #include <common/utils.h> | |
14 | #include <common/defaults.h> | |
15 | #include <tap/tap.h> | |
0b5a4de9 JG |
16 | #include <stdbool.h> |
17 | #include <common/error.h> | |
18 | #include <lttng/constant.h> | |
19 | #include <stdio.h> | |
20 | #include <pthread.h> | |
21 | #include <unistd.h> | |
22 | ||
23 | #define HIGH_FD_COUNT LTTCOMM_MAX_SEND_FDS | |
24 | #define MESSAGE_COUNT 4 | |
25 | #define LARGE_PAYLOAD_SIZE 4 * 1024 | |
26 | #define LARGE_PAYLOAD_RECV_SIZE 100 | |
27 | ||
28 | static const int TEST_COUNT = 33; | |
29 | ||
30 | /* For error.h */ | |
31 | int lttng_opt_quiet; | |
32 | int lttng_opt_verbose; | |
33 | int lttng_opt_mi; | |
34 | ||
35 | /* | |
36 | * Validate that a large number of file descriptors can be received in one shot. | |
37 | */ | |
38 | static void test_high_fd_count(unsigned int fd_count) | |
39 | { | |
40 | int sockets[2] = {-1, -1}; | |
41 | int ret; | |
42 | unsigned int i; | |
43 | const unsigned int payload_content = 42; | |
44 | struct lttng_payload sent_payload; | |
45 | struct lttng_payload received_payload; | |
46 | ||
47 | diag("Send and receive high FD count atomically (%u FDs)", fd_count); | |
48 | lttng_payload_init(&sent_payload); | |
49 | lttng_payload_init(&received_payload); | |
50 | ||
51 | ret = lttcomm_create_anon_unix_socketpair(sockets); | |
52 | ok(ret == 0, "Created anonymous unix socket pair"); | |
53 | if (ret < 0) { | |
54 | PERROR("Failed to create an anonymous pair of unix sockets"); | |
55 | goto error; | |
56 | } | |
57 | ||
58 | /* Add dummy content to payload. */ | |
59 | ret = lttng_dynamic_buffer_append(&sent_payload.buffer, | |
60 | &payload_content, sizeof(payload_content)); | |
61 | if (ret) { | |
62 | PERROR("Failed to initialize test payload"); | |
63 | goto error; | |
64 | } | |
65 | ||
66 | for (i = 0; i < fd_count; i++) { | |
67 | struct fd_handle *handle; | |
7b0027b3 | 68 | int fd = fcntl(STDOUT_FILENO, F_DUPFD, 0); |
0b5a4de9 JG |
69 | |
70 | if (fd < 0) { | |
7b0027b3 | 71 | PERROR("Failed to create fd while creating test payload"); |
0b5a4de9 JG |
72 | goto error; |
73 | } | |
74 | ||
75 | handle = fd_handle_create(fd); | |
76 | if (!handle) { | |
77 | if (close(fd)) { | |
7b0027b3 | 78 | PERROR("Failed to close fd while preparing test payload"); |
0b5a4de9 JG |
79 | goto error; |
80 | } | |
81 | } | |
82 | ||
83 | ret = lttng_payload_push_fd_handle(&sent_payload, handle); | |
84 | fd_handle_put(handle); | |
85 | if (ret) { | |
86 | PERROR("Failed to add fd handle to test payload"); | |
87 | goto error; | |
88 | } | |
89 | } | |
90 | ||
91 | /* Send payload. */ | |
92 | { | |
93 | ssize_t sock_ret; | |
94 | struct lttng_payload_view pv = lttng_payload_view_from_payload( | |
95 | &sent_payload, 0, -1); | |
96 | ||
97 | /* Not expected to block considering the size of the payload. */ | |
98 | sock_ret = lttcomm_send_unix_sock( | |
99 | sockets[0], pv.buffer.data, pv.buffer.size); | |
100 | ok(sock_ret == pv.buffer.size, "Sent complete test payload"); | |
101 | if (sock_ret != pv.buffer.size) { | |
102 | ERR("Failed to send test payload bytes: ret = %zd, expected = %zu", | |
103 | sock_ret, pv.buffer.size); | |
104 | goto error; | |
105 | } | |
106 | ||
107 | sock_ret = lttcomm_send_payload_view_fds_unix_sock( | |
108 | sockets[0], &pv); | |
109 | ok(sock_ret == 1, "Sent test payload file descriptors"); | |
110 | if (sock_ret != 1) { | |
111 | if (sock_ret < 0) { | |
112 | PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d", | |
113 | sock_ret, 1); | |
114 | } else { | |
115 | diag("Failed to send test payload file descriptors: ret = %zd, expected = %d", | |
116 | sock_ret, 1); | |
117 | } | |
118 | ||
119 | goto error; | |
120 | } | |
121 | } | |
122 | ||
123 | /* Receive payload */ | |
124 | { | |
125 | ssize_t sock_ret; | |
126 | ||
127 | ret = lttng_dynamic_buffer_set_size(&received_payload.buffer, | |
128 | sent_payload.buffer.size); | |
129 | if (ret) { | |
130 | PERROR("Failed to pre-allocate reception buffer"); | |
131 | goto error; | |
132 | } | |
133 | ||
134 | sock_ret = lttcomm_recv_unix_sock(sockets[1], | |
135 | received_payload.buffer.data, | |
136 | received_payload.buffer.size); | |
137 | ok(sock_ret == received_payload.buffer.size, | |
138 | "Received payload bytes"); | |
139 | if (sock_ret != received_payload.buffer.size) { | |
140 | ERR("Failed to receive payload bytes: ret = %zd, expected = %zu", | |
141 | sock_ret, received_payload.buffer.size); | |
142 | goto error; | |
143 | } | |
144 | ||
145 | sock_ret = lttcomm_recv_payload_fds_unix_sock( | |
146 | sockets[1], fd_count, &received_payload); | |
147 | ok(sock_ret == (int) (sizeof(int) * fd_count), | |
148 | "FD reception return value is number of fd * sizeof(int)"); | |
149 | if (sock_ret != (int) (sizeof(int) * fd_count)) { | |
150 | ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %d", | |
151 | sock_ret, | |
152 | (int) (fd_count * sizeof(int))); | |
153 | goto error; | |
154 | } | |
155 | ||
156 | { | |
157 | const struct lttng_payload_view pv = | |
158 | lttng_payload_view_from_payload( | |
159 | &received_payload, 0, | |
160 | -1); | |
161 | const int fd_handle_count = | |
162 | lttng_payload_view_get_fd_handle_count( | |
163 | &pv); | |
164 | ||
165 | ok(fd_handle_count == fd_count, | |
166 | "Received all test payload file descriptors in one invocation"); | |
167 | } | |
168 | } | |
169 | ||
170 | error: | |
171 | for (i = 0; i < 2; i++) { | |
172 | if (sockets[i] < 0) { | |
173 | continue; | |
174 | } | |
175 | ||
176 | if (close(sockets[i])) { | |
177 | PERROR("Failed to close unix socket"); | |
178 | } | |
179 | } | |
180 | ||
181 | lttng_payload_reset(&sent_payload); | |
182 | lttng_payload_reset(&received_payload); | |
183 | } | |
184 | ||
185 | /* | |
186 | * Validate that if the sender sent multiple messages, each containing 1 fd, | |
187 | * the receiver can receive one message at a time (the binary payload and its | |
188 | * fd) and is not forced to receive all file descriptors at once. | |
189 | */ | |
190 | static void test_one_fd_per_message(unsigned int message_count) | |
191 | { | |
192 | const unsigned int payload_content = 42; | |
193 | int sockets[2] = {-1, -1}; | |
194 | int ret; | |
195 | unsigned int i; | |
196 | struct lttng_payload sent_payload; | |
197 | struct lttng_payload received_payload; | |
198 | ||
199 | diag("Send and receive small messages with one FD each (%u messages)", | |
200 | message_count); | |
201 | lttng_payload_init(&sent_payload); | |
202 | lttng_payload_init(&received_payload); | |
203 | ||
204 | ret = lttcomm_create_anon_unix_socketpair(sockets); | |
205 | ok(ret == 0, "Created anonymous unix socket pair"); | |
206 | if (ret < 0) { | |
207 | PERROR("Failed to create an anonymous pair of unix sockets"); | |
208 | goto error; | |
209 | } | |
210 | ||
211 | /* Send messages with one fd each. */ | |
212 | for (i = 0; i < message_count; i++) { | |
213 | struct fd_handle *handle; | |
214 | int fd; | |
215 | ||
216 | /* Add dummy content to payload. */ | |
217 | ret = lttng_dynamic_buffer_append(&sent_payload.buffer, | |
218 | &payload_content, sizeof(payload_content)); | |
219 | if (ret) { | |
220 | PERROR("Failed to initialize test payload"); | |
221 | goto error; | |
222 | } | |
223 | ||
7b0027b3 | 224 | fd = fcntl(STDOUT_FILENO, F_DUPFD, 0); |
0b5a4de9 | 225 | if (fd < 0) { |
7b0027b3 | 226 | PERROR("Failed to create fd while creating test payload"); |
0b5a4de9 JG |
227 | goto error; |
228 | } | |
229 | ||
230 | handle = fd_handle_create(fd); | |
231 | if (!handle) { | |
232 | if (close(fd)) { | |
7b0027b3 | 233 | PERROR("Failed to close fd while preparing test payload"); |
0b5a4de9 JG |
234 | goto error; |
235 | } | |
236 | } | |
237 | ||
238 | ret = lttng_payload_push_fd_handle(&sent_payload, handle); | |
239 | fd_handle_put(handle); | |
240 | if (ret) { | |
241 | PERROR("Failed to add fd handle to test payload"); | |
242 | goto error; | |
243 | } | |
244 | ||
245 | /* Send payload. */ | |
246 | { | |
247 | ssize_t sock_ret; | |
248 | struct lttng_payload_view pv = | |
249 | lttng_payload_view_from_payload( | |
250 | &sent_payload, 0, -1); | |
251 | ||
252 | /* Not expected to block considering the size of the | |
253 | * payload. */ | |
254 | sock_ret = lttcomm_send_unix_sock(sockets[0], | |
255 | pv.buffer.data, pv.buffer.size); | |
256 | ok(sock_ret == pv.buffer.size, | |
257 | "Sent binary payload for message %u", | |
258 | i); | |
259 | if (sock_ret != pv.buffer.size) { | |
260 | ERR("Failed to send test payload bytes: ret = %zd, expected = %zu", | |
261 | sock_ret, pv.buffer.size); | |
262 | goto error; | |
263 | } | |
264 | ||
265 | sock_ret = lttcomm_send_payload_view_fds_unix_sock( | |
266 | sockets[0], &pv); | |
267 | ok(sock_ret == 1, | |
268 | "Sent file descriptors payload for message %u", | |
269 | i); | |
270 | if (sock_ret != 1) { | |
271 | if (sock_ret < 0) { | |
272 | PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d", | |
273 | sock_ret, 1); | |
274 | } else { | |
275 | diag("Failed to send test payload file descriptors: ret = %zd, expected = %d", | |
276 | sock_ret, 1); | |
277 | } | |
278 | ||
279 | goto error; | |
280 | } | |
281 | } | |
282 | ||
283 | lttng_payload_clear(&sent_payload); | |
284 | } | |
285 | ||
286 | /* Receive messages one at a time. */ | |
287 | for (i = 0; i < message_count; i++) { | |
288 | ssize_t sock_ret; | |
289 | ||
290 | ret = lttng_dynamic_buffer_set_size(&received_payload.buffer, | |
291 | sizeof(payload_content)); | |
292 | if (ret) { | |
293 | PERROR("Failed to pre-allocate reception buffer"); | |
294 | goto error; | |
295 | } | |
296 | ||
297 | sock_ret = lttcomm_recv_unix_sock(sockets[1], | |
298 | received_payload.buffer.data, | |
299 | received_payload.buffer.size); | |
300 | ok(sock_ret == received_payload.buffer.size, | |
301 | "Received payload bytes for message %u", i); | |
302 | if (sock_ret != received_payload.buffer.size) { | |
303 | ERR("Failed to receive payload bytes: ret = %zd, expected = %zu", | |
304 | sock_ret, received_payload.buffer.size); | |
305 | goto error; | |
306 | } | |
307 | ||
308 | sock_ret = lttcomm_recv_payload_fds_unix_sock( | |
309 | sockets[1], 1, &received_payload); | |
310 | ok(sock_ret == (int) sizeof(int), "Received fd for message %u", | |
311 | i); | |
312 | if (sock_ret != (int) sizeof(int)) { | |
313 | ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %u", | |
314 | sock_ret, (int) sizeof(int)); | |
315 | goto error; | |
316 | } | |
317 | ||
318 | { | |
319 | const struct lttng_payload_view pv = | |
320 | lttng_payload_view_from_payload( | |
321 | &received_payload, 0, | |
322 | -1); | |
323 | const int fd_handle_count = | |
324 | lttng_payload_view_get_fd_handle_count( | |
325 | &pv); | |
326 | ||
327 | ok(fd_handle_count == 1, | |
328 | "Payload contains 1 fd for message %u", | |
329 | i); | |
330 | } | |
331 | ||
332 | lttng_payload_clear(&received_payload); | |
333 | } | |
334 | ||
335 | error: | |
336 | for (i = 0; i < 2; i++) { | |
337 | if (sockets[i] < 0) { | |
338 | continue; | |
339 | } | |
340 | ||
341 | if (close(sockets[i])) { | |
342 | PERROR("Failed to close unix socket"); | |
343 | } | |
344 | } | |
345 | ||
346 | lttng_payload_reset(&sent_payload); | |
347 | lttng_payload_reset(&received_payload); | |
348 | } | |
349 | ||
350 | /* | |
351 | * Validate that a large message can be received in multiple chunks. | |
352 | */ | |
353 | static void test_receive_in_chunks( | |
354 | unsigned int payload_size, unsigned int max_recv_size) | |
355 | { | |
356 | int sockets[2] = {-1, -1}; | |
357 | int ret; | |
358 | unsigned int i; | |
359 | struct lttng_payload sent_payload; | |
360 | struct lttng_payload received_payload; | |
361 | struct fd_handle *handle; | |
362 | int fd; | |
363 | ssize_t sock_ret, received = 0; | |
364 | ||
365 | diag("Receive a message in multiple chunks"); | |
366 | lttng_payload_init(&sent_payload); | |
367 | lttng_payload_init(&received_payload); | |
368 | ||
369 | ret = lttcomm_create_anon_unix_socketpair(sockets); | |
370 | ok(ret == 0, "Created anonymous unix socket pair"); | |
371 | if (ret < 0) { | |
372 | PERROR("Failed to create an anonymous pair of unix sockets"); | |
373 | goto error; | |
374 | } | |
375 | ||
376 | /* Add dummy content to payload. */ | |
377 | ret = lttng_dynamic_buffer_set_size(&sent_payload.buffer, payload_size); | |
378 | if (ret) { | |
379 | PERROR("Failed to initialize test payload"); | |
380 | goto error; | |
381 | } | |
382 | ||
7b0027b3 | 383 | fd = fcntl(STDOUT_FILENO, F_DUPFD, 0); |
0b5a4de9 | 384 | if (fd < 0) { |
7b0027b3 | 385 | PERROR("Failed to create fd while creating test payload"); |
0b5a4de9 JG |
386 | goto error; |
387 | } | |
388 | ||
389 | handle = fd_handle_create(fd); | |
390 | if (!handle) { | |
391 | if (close(fd)) { | |
7b0027b3 | 392 | PERROR("Failed to close fd while preparing test payload"); |
0b5a4de9 JG |
393 | goto error; |
394 | } | |
395 | } | |
396 | ||
397 | ret = lttng_payload_push_fd_handle(&sent_payload, handle); | |
398 | fd_handle_put(handle); | |
399 | if (ret) { | |
400 | PERROR("Failed to add fd handle to test payload"); | |
401 | goto error; | |
402 | } | |
403 | ||
404 | /* Send payload. */ | |
405 | { | |
406 | struct lttng_payload_view pv = lttng_payload_view_from_payload( | |
407 | &sent_payload, 0, -1); | |
408 | ||
409 | /* Not expected to block considering the size of the payload. */ | |
410 | sock_ret = lttcomm_send_unix_sock( | |
411 | sockets[0], pv.buffer.data, pv.buffer.size); | |
412 | ok(sock_ret == pv.buffer.size, "Sent complete test payload"); | |
413 | if (sock_ret != pv.buffer.size) { | |
414 | ERR("Failed to send test payload bytes: ret = %zd, expected = %zu", | |
415 | sock_ret, pv.buffer.size); | |
416 | goto error; | |
417 | } | |
418 | ||
419 | sock_ret = lttcomm_send_payload_view_fds_unix_sock( | |
420 | sockets[0], &pv); | |
421 | ok(sock_ret == 1, "Sent test payload file descriptors"); | |
422 | if (sock_ret != 1) { | |
423 | if (sock_ret < 0) { | |
424 | PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d", | |
425 | sock_ret, 1); | |
426 | } else { | |
427 | diag("Failed to send test payload file descriptors: ret = %zd, expected = %d", | |
428 | sock_ret, 1); | |
429 | } | |
430 | ||
431 | goto error; | |
432 | } | |
433 | } | |
434 | ||
435 | /* Receive payload */ | |
436 | ret = lttng_dynamic_buffer_set_size( | |
437 | &received_payload.buffer, sent_payload.buffer.size); | |
438 | if (ret) { | |
439 | PERROR("Failed to pre-allocate reception buffer"); | |
440 | goto error; | |
441 | } | |
442 | ||
443 | do { | |
444 | const ssize_t to_receive_this_pass = min(max_recv_size, | |
445 | sent_payload.buffer.size - received); | |
446 | ||
447 | sock_ret = lttcomm_recv_unix_sock(sockets[1], | |
448 | received_payload.buffer.data + received, | |
449 | to_receive_this_pass); | |
450 | if (sock_ret != to_receive_this_pass) { | |
451 | ERR("Failed to receive payload bytes: ret = %zd, expected = %zu", | |
452 | sock_ret, to_receive_this_pass); | |
453 | break; | |
454 | } | |
455 | ||
456 | received += sock_ret; | |
457 | } while (received < sent_payload.buffer.size); | |
458 | ||
459 | ok(received == sent_payload.buffer.size, | |
460 | "Received complete payload in chunks of %u bytes", | |
461 | max_recv_size); | |
462 | if (received != sent_payload.buffer.size) { | |
463 | goto error; | |
464 | } | |
465 | ||
466 | sock_ret = lttcomm_recv_payload_fds_unix_sock( | |
467 | sockets[1], 1, &received_payload); | |
468 | ok(sock_ret == (int) sizeof(int), | |
469 | "Received file descriptor after receiving payload in chunks"); | |
470 | if (sock_ret != (int) sizeof(int)) { | |
471 | ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %d", | |
472 | sock_ret, (int) sizeof(int)); | |
473 | goto error; | |
474 | } | |
475 | ||
476 | { | |
477 | const struct lttng_payload_view pv = | |
478 | lttng_payload_view_from_payload( | |
479 | &received_payload, 0, -1); | |
480 | const int fd_handle_count = | |
481 | lttng_payload_view_get_fd_handle_count(&pv); | |
482 | ||
483 | ok(fd_handle_count == 1, | |
484 | "Payload contains 1 fd after receiving payload in chunks"); | |
485 | } | |
486 | ||
487 | error: | |
488 | for (i = 0; i < 2; i++) { | |
489 | if (sockets[i] < 0) { | |
490 | continue; | |
491 | } | |
492 | ||
493 | if (close(sockets[i])) { | |
494 | PERROR("Failed to close unix socket"); | |
495 | } | |
496 | } | |
497 | ||
498 | lttng_payload_reset(&sent_payload); | |
499 | lttng_payload_reset(&received_payload); | |
500 | } | |
501 | ||
502 | int main(void) | |
503 | { | |
504 | plan_tests(TEST_COUNT); | |
505 | ||
506 | test_high_fd_count(HIGH_FD_COUNT); | |
507 | test_one_fd_per_message(MESSAGE_COUNT); | |
508 | test_receive_in_chunks(LARGE_PAYLOAD_SIZE, LARGE_PAYLOAD_RECV_SIZE); | |
509 | ||
510 | return exit_status(); | |
511 | } |