Rename C++ header files to .hpp
[lttng-tools.git] / tests / unit / test_unix_socket.cpp
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 <algorithm>
9 #include <common/compat/fcntl.hpp>
10 #include <common/sessiond-comm/sessiond-comm.hpp>
11 #include <common/payload.hpp>
12 #include <common/payload-view.hpp>
13 #include <common/unix.hpp>
14 #include <common/utils.hpp>
15 #include <common/defaults.hpp>
16 #include <tap/tap.h>
17 #include <stdbool.h>
18 #include <common/error.hpp>
19 #include <lttng/constant.h>
20 #include <stdio.h>
21 #include <pthread.h>
22 #include <unistd.h>
23 #include <sys/wait.h>
24 #include <stdlib.h>
25
26 #define HIGH_FD_COUNT LTTCOMM_MAX_SEND_FDS
27 #define MESSAGE_COUNT 4
28 #define LARGE_PAYLOAD_SIZE 4 * 1024
29 #define LARGE_PAYLOAD_RECV_SIZE 100
30
31 static const int TEST_COUNT = 37;
32
33 /* For error.h */
34 int lttng_opt_quiet;
35 int lttng_opt_verbose;
36 int lttng_opt_mi;
37
38 /*
39 * Validate that a large number of file descriptors can be received in one shot.
40 */
41 static void test_high_fd_count(unsigned int fd_count)
42 {
43 int sockets[2] = {-1, -1};
44 int ret;
45 unsigned int i;
46 const unsigned int payload_content = 42;
47 struct lttng_payload sent_payload;
48 struct lttng_payload received_payload;
49
50 diag("Send and receive high FD count atomically (%u FDs)", fd_count);
51 lttng_payload_init(&sent_payload);
52 lttng_payload_init(&received_payload);
53
54 ret = lttcomm_create_anon_unix_socketpair(sockets);
55 ok(ret == 0, "Created anonymous unix socket pair");
56 if (ret < 0) {
57 PERROR("Failed to create an anonymous pair of unix sockets");
58 goto error;
59 }
60
61 /* Add dummy content to payload. */
62 ret = lttng_dynamic_buffer_append(&sent_payload.buffer,
63 &payload_content, sizeof(payload_content));
64 if (ret) {
65 PERROR("Failed to initialize test payload");
66 goto error;
67 }
68
69 for (i = 0; i < fd_count; i++) {
70 struct fd_handle *handle;
71 int fd = fcntl(STDOUT_FILENO, F_DUPFD, 0);
72
73 if (fd < 0) {
74 PERROR("Failed to create fd while creating test payload");
75 goto error;
76 }
77
78 handle = fd_handle_create(fd);
79 if (!handle) {
80 if (close(fd)) {
81 PERROR("Failed to close fd while preparing test payload");
82 goto error;
83 }
84 }
85
86 ret = lttng_payload_push_fd_handle(&sent_payload, handle);
87 fd_handle_put(handle);
88 if (ret) {
89 PERROR("Failed to add fd handle to test payload");
90 goto error;
91 }
92 }
93
94 /* Send payload. */
95 {
96 ssize_t sock_ret;
97 struct lttng_payload_view pv = lttng_payload_view_from_payload(
98 &sent_payload, 0, -1);
99
100 /* Not expected to block considering the size of the payload. */
101 sock_ret = lttcomm_send_unix_sock(
102 sockets[0], pv.buffer.data, pv.buffer.size);
103 ok(sock_ret == pv.buffer.size, "Sent complete test payload");
104 if (sock_ret != pv.buffer.size) {
105 ERR("Failed to send test payload bytes: ret = %zd, expected = %zu",
106 sock_ret, pv.buffer.size);
107 goto error;
108 }
109
110 sock_ret = lttcomm_send_payload_view_fds_unix_sock(
111 sockets[0], &pv);
112 ok(sock_ret == 1, "Sent test payload file descriptors");
113 if (sock_ret != 1) {
114 if (sock_ret < 0) {
115 PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d",
116 sock_ret, 1);
117 } else {
118 diag("Failed to send test payload file descriptors: ret = %zd, expected = %d",
119 sock_ret, 1);
120 }
121
122 goto error;
123 }
124 }
125
126 /* Receive payload */
127 {
128 ssize_t sock_ret;
129
130 ret = lttng_dynamic_buffer_set_size(&received_payload.buffer,
131 sent_payload.buffer.size);
132 if (ret) {
133 PERROR("Failed to pre-allocate reception buffer");
134 goto error;
135 }
136
137 sock_ret = lttcomm_recv_unix_sock(sockets[1],
138 received_payload.buffer.data,
139 received_payload.buffer.size);
140 ok(sock_ret == received_payload.buffer.size,
141 "Received payload bytes");
142 if (sock_ret != received_payload.buffer.size) {
143 ERR("Failed to receive payload bytes: ret = %zd, expected = %zu",
144 sock_ret, received_payload.buffer.size);
145 goto error;
146 }
147
148 sock_ret = lttcomm_recv_payload_fds_unix_sock(
149 sockets[1], fd_count, &received_payload);
150 ok(sock_ret == (int) (sizeof(int) * fd_count),
151 "FD reception return value is number of fd * sizeof(int)");
152 if (sock_ret != (int) (sizeof(int) * fd_count)) {
153 ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %d",
154 sock_ret,
155 (int) (fd_count * sizeof(int)));
156 goto error;
157 }
158
159 {
160 const struct lttng_payload_view pv =
161 lttng_payload_view_from_payload(
162 &received_payload, 0,
163 -1);
164 const int fd_handle_count =
165 lttng_payload_view_get_fd_handle_count(
166 &pv);
167
168 ok(fd_handle_count == fd_count,
169 "Received all test payload file descriptors in one invocation");
170 }
171 }
172
173 error:
174 for (i = 0; i < 2; i++) {
175 if (sockets[i] < 0) {
176 continue;
177 }
178
179 if (close(sockets[i])) {
180 PERROR("Failed to close unix socket");
181 }
182 }
183
184 lttng_payload_reset(&sent_payload);
185 lttng_payload_reset(&received_payload);
186 }
187
188 /*
189 * Validate that if the sender sent multiple messages, each containing 1 fd,
190 * the receiver can receive one message at a time (the binary payload and its
191 * fd) and is not forced to receive all file descriptors at once.
192 */
193 static void test_one_fd_per_message(unsigned int message_count)
194 {
195 const unsigned int payload_content = 42;
196 int sockets[2] = {-1, -1};
197 int ret;
198 unsigned int i;
199 struct lttng_payload sent_payload;
200 struct lttng_payload received_payload;
201
202 diag("Send and receive small messages with one FD each (%u messages)",
203 message_count);
204 lttng_payload_init(&sent_payload);
205 lttng_payload_init(&received_payload);
206
207 ret = lttcomm_create_anon_unix_socketpair(sockets);
208 ok(ret == 0, "Created anonymous unix socket pair");
209 if (ret < 0) {
210 PERROR("Failed to create an anonymous pair of unix sockets");
211 goto error;
212 }
213
214 /* Send messages with one fd each. */
215 for (i = 0; i < message_count; i++) {
216 struct fd_handle *handle;
217 int fd;
218
219 /* Add dummy content to payload. */
220 ret = lttng_dynamic_buffer_append(&sent_payload.buffer,
221 &payload_content, sizeof(payload_content));
222 if (ret) {
223 PERROR("Failed to initialize test payload");
224 goto error;
225 }
226
227 fd = fcntl(STDOUT_FILENO, F_DUPFD, 0);
228 if (fd < 0) {
229 PERROR("Failed to create fd while creating test payload");
230 goto error;
231 }
232
233 handle = fd_handle_create(fd);
234 if (!handle) {
235 if (close(fd)) {
236 PERROR("Failed to close fd while preparing test payload");
237 goto error;
238 }
239 }
240
241 ret = lttng_payload_push_fd_handle(&sent_payload, handle);
242 fd_handle_put(handle);
243 if (ret) {
244 PERROR("Failed to add fd handle to test payload");
245 goto error;
246 }
247
248 /* Send payload. */
249 {
250 ssize_t sock_ret;
251 struct lttng_payload_view pv =
252 lttng_payload_view_from_payload(
253 &sent_payload, 0, -1);
254
255 /* Not expected to block considering the size of the
256 * payload. */
257 sock_ret = lttcomm_send_unix_sock(sockets[0],
258 pv.buffer.data, pv.buffer.size);
259 ok(sock_ret == pv.buffer.size,
260 "Sent binary payload for message %u",
261 i);
262 if (sock_ret != pv.buffer.size) {
263 ERR("Failed to send test payload bytes: ret = %zd, expected = %zu",
264 sock_ret, pv.buffer.size);
265 goto error;
266 }
267
268 sock_ret = lttcomm_send_payload_view_fds_unix_sock(
269 sockets[0], &pv);
270 ok(sock_ret == 1,
271 "Sent file descriptors payload for message %u",
272 i);
273 if (sock_ret != 1) {
274 if (sock_ret < 0) {
275 PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d",
276 sock_ret, 1);
277 } else {
278 diag("Failed to send test payload file descriptors: ret = %zd, expected = %d",
279 sock_ret, 1);
280 }
281
282 goto error;
283 }
284 }
285
286 lttng_payload_clear(&sent_payload);
287 }
288
289 /* Receive messages one at a time. */
290 for (i = 0; i < message_count; i++) {
291 ssize_t sock_ret;
292
293 ret = lttng_dynamic_buffer_set_size(&received_payload.buffer,
294 sizeof(payload_content));
295 if (ret) {
296 PERROR("Failed to pre-allocate reception buffer");
297 goto error;
298 }
299
300 sock_ret = lttcomm_recv_unix_sock(sockets[1],
301 received_payload.buffer.data,
302 received_payload.buffer.size);
303 ok(sock_ret == received_payload.buffer.size,
304 "Received payload bytes for message %u", i);
305 if (sock_ret != received_payload.buffer.size) {
306 ERR("Failed to receive payload bytes: ret = %zd, expected = %zu",
307 sock_ret, received_payload.buffer.size);
308 goto error;
309 }
310
311 sock_ret = lttcomm_recv_payload_fds_unix_sock(
312 sockets[1], 1, &received_payload);
313 ok(sock_ret == (int) sizeof(int), "Received fd for message %u",
314 i);
315 if (sock_ret != (int) sizeof(int)) {
316 ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %u",
317 sock_ret, (int) sizeof(int));
318 goto error;
319 }
320
321 {
322 const struct lttng_payload_view pv =
323 lttng_payload_view_from_payload(
324 &received_payload, 0,
325 -1);
326 const int fd_handle_count =
327 lttng_payload_view_get_fd_handle_count(
328 &pv);
329
330 ok(fd_handle_count == 1,
331 "Payload contains 1 fd for message %u",
332 i);
333 }
334
335 lttng_payload_clear(&received_payload);
336 }
337
338 error:
339 for (i = 0; i < 2; i++) {
340 if (sockets[i] < 0) {
341 continue;
342 }
343
344 if (close(sockets[i])) {
345 PERROR("Failed to close unix socket");
346 }
347 }
348
349 lttng_payload_reset(&sent_payload);
350 lttng_payload_reset(&received_payload);
351 }
352
353 /*
354 * Validate that a large message can be received in multiple chunks.
355 */
356 static void test_receive_in_chunks(
357 unsigned int payload_size, unsigned int max_recv_size)
358 {
359 int sockets[2] = {-1, -1};
360 int ret;
361 unsigned int i;
362 struct lttng_payload sent_payload;
363 struct lttng_payload received_payload;
364 struct fd_handle *handle;
365 int fd;
366 ssize_t sock_ret, received = 0;
367
368 diag("Receive a message in multiple chunks");
369 lttng_payload_init(&sent_payload);
370 lttng_payload_init(&received_payload);
371
372 ret = lttcomm_create_anon_unix_socketpair(sockets);
373 ok(ret == 0, "Created anonymous unix socket pair");
374 if (ret < 0) {
375 PERROR("Failed to create an anonymous pair of unix sockets");
376 goto error;
377 }
378
379 /* Add dummy content to payload. */
380 ret = lttng_dynamic_buffer_set_size(&sent_payload.buffer, payload_size);
381 if (ret) {
382 PERROR("Failed to initialize test payload");
383 goto error;
384 }
385
386 fd = fcntl(STDOUT_FILENO, F_DUPFD, 0);
387 if (fd < 0) {
388 PERROR("Failed to create fd while creating test payload");
389 goto error;
390 }
391
392 handle = fd_handle_create(fd);
393 if (!handle) {
394 if (close(fd)) {
395 PERROR("Failed to close fd while preparing test payload");
396 goto error;
397 }
398 }
399
400 ret = lttng_payload_push_fd_handle(&sent_payload, handle);
401 fd_handle_put(handle);
402 if (ret) {
403 PERROR("Failed to add fd handle to test payload");
404 goto error;
405 }
406
407 /* Send payload. */
408 {
409 struct lttng_payload_view pv = lttng_payload_view_from_payload(
410 &sent_payload, 0, -1);
411
412 /* Not expected to block considering the size of the payload. */
413 sock_ret = lttcomm_send_unix_sock(
414 sockets[0], pv.buffer.data, pv.buffer.size);
415 ok(sock_ret == pv.buffer.size, "Sent complete test payload");
416 if (sock_ret != pv.buffer.size) {
417 ERR("Failed to send test payload bytes: ret = %zd, expected = %zu",
418 sock_ret, pv.buffer.size);
419 goto error;
420 }
421
422 sock_ret = lttcomm_send_payload_view_fds_unix_sock(
423 sockets[0], &pv);
424 ok(sock_ret == 1, "Sent test payload file descriptors");
425 if (sock_ret != 1) {
426 if (sock_ret < 0) {
427 PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d",
428 sock_ret, 1);
429 } else {
430 diag("Failed to send test payload file descriptors: ret = %zd, expected = %d",
431 sock_ret, 1);
432 }
433
434 goto error;
435 }
436 }
437
438 /* Receive payload */
439 ret = lttng_dynamic_buffer_set_size(
440 &received_payload.buffer, sent_payload.buffer.size);
441 if (ret) {
442 PERROR("Failed to pre-allocate reception buffer");
443 goto error;
444 }
445
446 do {
447 const ssize_t to_receive_this_pass =
448 std::min<ssize_t>(max_recv_size,
449 sent_payload.buffer.size - received);
450
451 sock_ret = lttcomm_recv_unix_sock(sockets[1],
452 received_payload.buffer.data + received,
453 to_receive_this_pass);
454 if (sock_ret != to_receive_this_pass) {
455 ERR("Failed to receive payload bytes: ret = %zd, expected = %zu",
456 sock_ret, to_receive_this_pass);
457 break;
458 }
459
460 received += sock_ret;
461 } while (received < sent_payload.buffer.size);
462
463 ok(received == sent_payload.buffer.size,
464 "Received complete payload in chunks of %u bytes",
465 max_recv_size);
466 if (received != sent_payload.buffer.size) {
467 goto error;
468 }
469
470 sock_ret = lttcomm_recv_payload_fds_unix_sock(
471 sockets[1], 1, &received_payload);
472 ok(sock_ret == (int) sizeof(int),
473 "Received file descriptor after receiving payload in chunks");
474 if (sock_ret != (int) sizeof(int)) {
475 ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %d",
476 sock_ret, (int) sizeof(int));
477 goto error;
478 }
479
480 {
481 const struct lttng_payload_view pv =
482 lttng_payload_view_from_payload(
483 &received_payload, 0, -1);
484 const int fd_handle_count =
485 lttng_payload_view_get_fd_handle_count(&pv);
486
487 ok(fd_handle_count == 1,
488 "Payload contains 1 fd after receiving payload in chunks");
489 }
490
491 error:
492 for (i = 0; i < 2; i++) {
493 if (sockets[i] < 0) {
494 continue;
495 }
496
497 if (close(sockets[i])) {
498 PERROR("Failed to close unix socket");
499 }
500 }
501
502 lttng_payload_reset(&sent_payload);
503 lttng_payload_reset(&received_payload);
504 }
505
506 static
507 void test_creds_passing(void)
508 {
509 pid_t fork_ret = -1;
510 int ret, parent_socket = -1, child_connection_socket = -1;
511 ssize_t sock_ret;
512 char socket_dir_path[] = "/tmp/test.unix.socket.creds.passing.XXXXXX";
513 char socket_path[PATH_MAX] = {};
514 struct expected_creds {
515 uid_t euid;
516 gid_t egid;
517 pid_t pid;
518 } expected_creds;
519
520 diag("Receive peer's effective uid, effective gid, and pid from a unix socket");
521
522 if (!mkdtemp(socket_dir_path)) {
523 PERROR("Failed to generate temporary socket location");
524 goto error;
525 }
526
527 strncat(socket_path, socket_dir_path,
528 sizeof(socket_path) - strlen(socket_path) - 1);
529 strncat(socket_path, "/test_unix_socket",
530 sizeof(socket_path) - strlen(socket_path) - 1);
531
532 parent_socket = lttcomm_create_unix_sock(socket_path);
533 ok(parent_socket >= 0, "Created unix socket at path `%s`", socket_path);
534 if (parent_socket < 0) {
535 PERROR("Failed to create unix socket at path `%s`", socket_path);
536 goto error;
537 }
538
539 ret = lttcomm_listen_unix_sock(parent_socket);
540 if (ret < 0) {
541 PERROR("Failed to mark parent socket as a passive socket");
542 goto error;
543 }
544
545 ret = lttcomm_setsockopt_creds_unix_sock(parent_socket);
546 if (ret) {
547 PERROR("Failed to set SO_PASSCRED on parent socket");
548 goto error;
549 }
550
551 fork_ret = fork();
552 if (fork_ret < 0) {
553 PERROR("Failed to fork");
554 goto error;
555 }
556
557 if (fork_ret == 0) {
558 /* Child. */
559 int child_socket;
560
561 expected_creds = (struct expected_creds){
562 .euid = geteuid(),
563 .egid = getegid(),
564 .pid = getpid(),
565 };
566
567 child_socket = lttcomm_connect_unix_sock(socket_path);
568 if (child_socket < 0) {
569 PERROR("Failed to connect to parent socket");
570 goto error;
571 }
572
573 ret = lttcomm_setsockopt_creds_unix_sock(child_socket);
574 if (ret) {
575 PERROR("Failed to set SO_PASSCRED on child socket");
576 }
577
578 sock_ret = lttcomm_send_creds_unix_sock(child_socket, &expected_creds,
579 sizeof(expected_creds));
580 if (sock_ret < 0) {
581 PERROR("Failed to send expected credentials");
582 }
583
584 ret = close(child_socket);
585 if (ret) {
586 PERROR("Failed to close child socket");
587 }
588 } else {
589 /* Parent. */
590 int child_status;
591 pid_t wait_pid_ret;
592 lttng_sock_cred received_creds = {};
593
594 child_connection_socket =
595 lttcomm_accept_unix_sock(parent_socket);
596 if (child_connection_socket < 0) {
597 PERROR();
598 goto error;
599 }
600
601 ret = lttcomm_setsockopt_creds_unix_sock(
602 child_connection_socket);
603 if (ret) {
604 PERROR("Failed to set SO_PASSCRED on child connection socket");
605 goto error;
606 }
607
608 sock_ret = lttcomm_recv_creds_unix_sock(child_connection_socket,
609 &expected_creds, sizeof(expected_creds),
610 &received_creds);
611 if (sock_ret < 0) {
612 PERROR("Failed to receive credentials");
613 goto error;
614 }
615
616 wait_pid_ret = waitpid(fork_ret, &child_status, 0);
617 if (wait_pid_ret == -1) {
618 PERROR("Failed to wait for termination of child process");
619 goto error;
620 }
621 if (!WIFEXITED(child_status) || WEXITSTATUS(child_status)) {
622 diag("Child process reported an error, test failed");
623 goto error;
624 }
625
626 ok(expected_creds.euid == received_creds.uid,
627 "Received the expected effective uid (%d == %d)",
628 expected_creds.euid, received_creds.uid);
629 ok(expected_creds.egid == received_creds.gid,
630 "Received the expected effective gid (%d == %d)",
631 expected_creds.egid, received_creds.gid);
632 ok(expected_creds.pid == received_creds.pid,
633 "Received the expected pid (%d == %d)",
634 expected_creds.pid, received_creds.pid);
635 }
636
637 error:
638 if (parent_socket >= 0) {
639 ret = close(parent_socket);
640 if (ret) {
641 PERROR("Failed to close parent socket");
642 }
643 }
644
645 if (fork_ret == 0) {
646 /* Prevent libtap from printing a result for the child. */
647 fclose(stdout);
648 fclose(stderr);
649
650 /* Child exits at the end of this test. */
651 exit(0);
652 } else if (parent_socket >= 0) {
653 if (child_connection_socket >= 0) {
654 ret = close(child_connection_socket);
655 if (ret) {
656 PERROR("Failed to close child connection socket");
657 }
658 }
659
660 ret = unlink(socket_path);
661 if (ret) {
662 PERROR("Failed to unlink socket at path `%s`",
663 socket_path);
664 }
665
666 ret = rmdir(socket_dir_path);
667 if (ret) {
668 PERROR("Failed to remove test directory at `%s`",
669 socket_dir_path);
670 }
671 }
672 }
673
674 int main(void)
675 {
676 plan_tests(TEST_COUNT);
677
678 test_high_fd_count(HIGH_FD_COUNT);
679 test_one_fd_per_message(MESSAGE_COUNT);
680 test_receive_in_chunks(LARGE_PAYLOAD_SIZE, LARGE_PAYLOAD_RECV_SIZE);
681 test_creds_passing();
682
683 return exit_status();
684 }
This page took 0.044787 seconds and 5 git commands to generate.