port: no eventfd support on FreeBSD
[lttng-tools.git] / tests / unit / test_unix_socket.c
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
8 #include <common/compat/fcntl.h>
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>
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;
68 int fd = fcntl(STDOUT_FILENO, F_DUPFD, 0);
69
70 if (fd < 0) {
71 PERROR("Failed to create fd while creating test payload");
72 goto error;
73 }
74
75 handle = fd_handle_create(fd);
76 if (!handle) {
77 if (close(fd)) {
78 PERROR("Failed to close fd while preparing test payload");
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
224 fd = fcntl(STDOUT_FILENO, F_DUPFD, 0);
225 if (fd < 0) {
226 PERROR("Failed to create fd while creating test payload");
227 goto error;
228 }
229
230 handle = fd_handle_create(fd);
231 if (!handle) {
232 if (close(fd)) {
233 PERROR("Failed to close fd while preparing test payload");
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
383 fd = fcntl(STDOUT_FILENO, F_DUPFD, 0);
384 if (fd < 0) {
385 PERROR("Failed to create fd while creating test payload");
386 goto error;
387 }
388
389 handle = fd_handle_create(fd);
390 if (!handle) {
391 if (close(fd)) {
392 PERROR("Failed to close fd while preparing test payload");
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 }
This page took 0.058947 seconds and 4 git commands to generate.