2 * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
4 * SPDX-License-Identifier: LGPL-2.1-only
8 #include <common/compat/fcntl.hpp>
9 #include <common/defaults.hpp>
10 #include <common/error.hpp>
11 #include <common/payload-view.hpp>
12 #include <common/payload.hpp>
13 #include <common/sessiond-comm/sessiond-comm.hpp>
14 #include <common/unix.hpp>
15 #include <common/utils.hpp>
17 #include <lttng/constant.h>
28 #define HIGH_FD_COUNT LTTCOMM_MAX_SEND_FDS
29 #define MESSAGE_COUNT 4
30 #define LARGE_PAYLOAD_SIZE 4 * 1024
31 #define LARGE_PAYLOAD_RECV_SIZE 100
33 static const int TEST_COUNT
= 37;
37 int lttng_opt_verbose
;
41 * Validate that a large number of file descriptors can be received in one shot.
43 static void test_high_fd_count(unsigned int fd_count
)
45 int sockets
[2] = { -1, -1 };
48 const unsigned int payload_content
= 42;
49 struct lttng_payload sent_payload
;
50 struct lttng_payload received_payload
;
52 diag("Send and receive high FD count atomically (%u FDs)", fd_count
);
53 lttng_payload_init(&sent_payload
);
54 lttng_payload_init(&received_payload
);
56 ret
= lttcomm_create_anon_unix_socketpair(sockets
);
57 ok(ret
== 0, "Created anonymous unix socket pair");
59 PERROR("Failed to create an anonymous pair of unix sockets");
63 /* Add dummy content to payload. */
64 ret
= lttng_dynamic_buffer_append(
65 &sent_payload
.buffer
, &payload_content
, sizeof(payload_content
));
67 PERROR("Failed to initialize test payload");
71 for (i
= 0; i
< fd_count
; i
++) {
72 struct fd_handle
*handle
;
73 int fd
= fcntl(STDOUT_FILENO
, F_DUPFD
, 0);
76 PERROR("Failed to create fd while creating test payload");
80 handle
= fd_handle_create(fd
);
83 PERROR("Failed to close fd while preparing test payload");
88 ret
= lttng_payload_push_fd_handle(&sent_payload
, handle
);
89 fd_handle_put(handle
);
91 PERROR("Failed to add fd handle to test payload");
99 struct lttng_payload_view pv
=
100 lttng_payload_view_from_payload(&sent_payload
, 0, -1);
102 /* Not expected to block considering the size of the payload. */
103 sock_ret
= lttcomm_send_unix_sock(sockets
[0], pv
.buffer
.data
, pv
.buffer
.size
);
104 ok(sock_ret
== pv
.buffer
.size
, "Sent complete test payload");
105 if (sock_ret
!= pv
.buffer
.size
) {
106 ERR("Failed to send test payload bytes: ret = %zd, expected = %zu",
112 sock_ret
= lttcomm_send_payload_view_fds_unix_sock(sockets
[0], &pv
);
113 ok(sock_ret
== 1, "Sent test payload file descriptors");
116 PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d",
120 diag("Failed to send test payload file descriptors: ret = %zd, expected = %d",
129 /* Receive payload */
133 ret
= lttng_dynamic_buffer_set_size(&received_payload
.buffer
,
134 sent_payload
.buffer
.size
);
136 PERROR("Failed to pre-allocate reception buffer");
140 sock_ret
= lttcomm_recv_unix_sock(
141 sockets
[1], received_payload
.buffer
.data
, received_payload
.buffer
.size
);
142 ok(sock_ret
== received_payload
.buffer
.size
, "Received payload bytes");
143 if (sock_ret
!= received_payload
.buffer
.size
) {
144 ERR("Failed to receive payload bytes: ret = %zd, expected = %zu",
146 received_payload
.buffer
.size
);
151 lttcomm_recv_payload_fds_unix_sock(sockets
[1], fd_count
, &received_payload
);
152 ok(sock_ret
== (int) (sizeof(int) * fd_count
),
153 "FD reception return value is number of fd * sizeof(int)");
154 if (sock_ret
!= (int) (sizeof(int) * fd_count
)) {
155 ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %d",
157 (int) (fd_count
* sizeof(int)));
162 const struct lttng_payload_view pv
=
163 lttng_payload_view_from_payload(&received_payload
, 0, -1);
164 const int fd_handle_count
= lttng_payload_view_get_fd_handle_count(&pv
);
166 ok(fd_handle_count
== fd_count
,
167 "Received all test payload file descriptors in one invocation");
172 for (i
= 0; i
< 2; i
++) {
173 if (sockets
[i
] < 0) {
177 if (close(sockets
[i
])) {
178 PERROR("Failed to close unix socket");
182 lttng_payload_reset(&sent_payload
);
183 lttng_payload_reset(&received_payload
);
187 * Validate that if the sender sent multiple messages, each containing 1 fd,
188 * the receiver can receive one message at a time (the binary payload and its
189 * fd) and is not forced to receive all file descriptors at once.
191 static void test_one_fd_per_message(unsigned int message_count
)
193 const unsigned int payload_content
= 42;
194 int sockets
[2] = { -1, -1 };
197 struct lttng_payload sent_payload
;
198 struct lttng_payload received_payload
;
200 diag("Send and receive small messages with one FD each (%u messages)", message_count
);
201 lttng_payload_init(&sent_payload
);
202 lttng_payload_init(&received_payload
);
204 ret
= lttcomm_create_anon_unix_socketpair(sockets
);
205 ok(ret
== 0, "Created anonymous unix socket pair");
207 PERROR("Failed to create an anonymous pair of unix sockets");
211 /* Send messages with one fd each. */
212 for (i
= 0; i
< message_count
; i
++) {
213 struct fd_handle
*handle
;
216 /* Add dummy content to payload. */
217 ret
= lttng_dynamic_buffer_append(
218 &sent_payload
.buffer
, &payload_content
, sizeof(payload_content
));
220 PERROR("Failed to initialize test payload");
224 fd
= fcntl(STDOUT_FILENO
, F_DUPFD
, 0);
226 PERROR("Failed to create fd while creating test payload");
230 handle
= fd_handle_create(fd
);
233 PERROR("Failed to close fd while preparing test payload");
238 ret
= lttng_payload_push_fd_handle(&sent_payload
, handle
);
239 fd_handle_put(handle
);
241 PERROR("Failed to add fd handle to test payload");
248 struct lttng_payload_view pv
=
249 lttng_payload_view_from_payload(&sent_payload
, 0, -1);
251 /* Not expected to block considering the size of the
254 lttcomm_send_unix_sock(sockets
[0], pv
.buffer
.data
, pv
.buffer
.size
);
255 ok(sock_ret
== pv
.buffer
.size
, "Sent binary payload for message %u", i
);
256 if (sock_ret
!= pv
.buffer
.size
) {
257 ERR("Failed to send test payload bytes: ret = %zd, expected = %zu",
263 sock_ret
= lttcomm_send_payload_view_fds_unix_sock(sockets
[0], &pv
);
264 ok(sock_ret
== 1, "Sent file descriptors payload for message %u", i
);
267 PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d",
271 diag("Failed to send test payload file descriptors: ret = %zd, expected = %d",
280 lttng_payload_clear(&sent_payload
);
283 /* Receive messages one at a time. */
284 for (i
= 0; i
< message_count
; i
++) {
287 ret
= lttng_dynamic_buffer_set_size(&received_payload
.buffer
,
288 sizeof(payload_content
));
290 PERROR("Failed to pre-allocate reception buffer");
294 sock_ret
= lttcomm_recv_unix_sock(
295 sockets
[1], received_payload
.buffer
.data
, received_payload
.buffer
.size
);
296 ok(sock_ret
== received_payload
.buffer
.size
,
297 "Received payload bytes for message %u",
299 if (sock_ret
!= received_payload
.buffer
.size
) {
300 ERR("Failed to receive payload bytes: ret = %zd, expected = %zu",
302 received_payload
.buffer
.size
);
306 sock_ret
= lttcomm_recv_payload_fds_unix_sock(sockets
[1], 1, &received_payload
);
307 ok(sock_ret
== (int) sizeof(int), "Received fd for message %u", i
);
308 if (sock_ret
!= (int) sizeof(int)) {
309 ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %u",
316 const struct lttng_payload_view pv
=
317 lttng_payload_view_from_payload(&received_payload
, 0, -1);
318 const int fd_handle_count
= lttng_payload_view_get_fd_handle_count(&pv
);
320 ok(fd_handle_count
== 1, "Payload contains 1 fd for message %u", i
);
323 lttng_payload_clear(&received_payload
);
327 for (i
= 0; i
< 2; i
++) {
328 if (sockets
[i
] < 0) {
332 if (close(sockets
[i
])) {
333 PERROR("Failed to close unix socket");
337 lttng_payload_reset(&sent_payload
);
338 lttng_payload_reset(&received_payload
);
342 * Validate that a large message can be received in multiple chunks.
344 static void test_receive_in_chunks(unsigned int payload_size
, unsigned int max_recv_size
)
346 int sockets
[2] = { -1, -1 };
349 struct lttng_payload sent_payload
;
350 struct lttng_payload received_payload
;
351 struct fd_handle
*handle
;
353 ssize_t sock_ret
, received
= 0;
355 diag("Receive a message in multiple chunks");
356 lttng_payload_init(&sent_payload
);
357 lttng_payload_init(&received_payload
);
359 ret
= lttcomm_create_anon_unix_socketpair(sockets
);
360 ok(ret
== 0, "Created anonymous unix socket pair");
362 PERROR("Failed to create an anonymous pair of unix sockets");
366 /* Add dummy content to payload. */
367 ret
= lttng_dynamic_buffer_set_size(&sent_payload
.buffer
, payload_size
);
369 PERROR("Failed to initialize test payload");
373 fd
= fcntl(STDOUT_FILENO
, F_DUPFD
, 0);
375 PERROR("Failed to create fd while creating test payload");
379 handle
= fd_handle_create(fd
);
382 PERROR("Failed to close fd while preparing test payload");
387 ret
= lttng_payload_push_fd_handle(&sent_payload
, handle
);
388 fd_handle_put(handle
);
390 PERROR("Failed to add fd handle to test payload");
396 struct lttng_payload_view pv
=
397 lttng_payload_view_from_payload(&sent_payload
, 0, -1);
399 /* Not expected to block considering the size of the payload. */
400 sock_ret
= lttcomm_send_unix_sock(sockets
[0], pv
.buffer
.data
, pv
.buffer
.size
);
401 ok(sock_ret
== pv
.buffer
.size
, "Sent complete test payload");
402 if (sock_ret
!= pv
.buffer
.size
) {
403 ERR("Failed to send test payload bytes: ret = %zd, expected = %zu",
409 sock_ret
= lttcomm_send_payload_view_fds_unix_sock(sockets
[0], &pv
);
410 ok(sock_ret
== 1, "Sent test payload file descriptors");
413 PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d",
417 diag("Failed to send test payload file descriptors: ret = %zd, expected = %d",
426 /* Receive payload */
427 ret
= lttng_dynamic_buffer_set_size(&received_payload
.buffer
, sent_payload
.buffer
.size
);
429 PERROR("Failed to pre-allocate reception buffer");
434 const ssize_t to_receive_this_pass
=
435 std::min
<ssize_t
>(max_recv_size
, sent_payload
.buffer
.size
- received
);
437 sock_ret
= lttcomm_recv_unix_sock(
438 sockets
[1], received_payload
.buffer
.data
+ received
, to_receive_this_pass
);
439 if (sock_ret
!= to_receive_this_pass
) {
440 ERR("Failed to receive payload bytes: ret = %zd, expected = %zu",
442 to_receive_this_pass
);
446 received
+= sock_ret
;
447 } while (received
< sent_payload
.buffer
.size
);
449 ok(received
== sent_payload
.buffer
.size
,
450 "Received complete payload in chunks of %u bytes",
452 if (received
!= sent_payload
.buffer
.size
) {
456 sock_ret
= lttcomm_recv_payload_fds_unix_sock(sockets
[1], 1, &received_payload
);
457 ok(sock_ret
== (int) sizeof(int),
458 "Received file descriptor after receiving payload in chunks");
459 if (sock_ret
!= (int) sizeof(int)) {
460 ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %d",
467 const struct lttng_payload_view pv
=
468 lttng_payload_view_from_payload(&received_payload
, 0, -1);
469 const int fd_handle_count
= lttng_payload_view_get_fd_handle_count(&pv
);
471 ok(fd_handle_count
== 1, "Payload contains 1 fd after receiving payload in chunks");
475 for (i
= 0; i
< 2; i
++) {
476 if (sockets
[i
] < 0) {
480 if (close(sockets
[i
])) {
481 PERROR("Failed to close unix socket");
485 lttng_payload_reset(&sent_payload
);
486 lttng_payload_reset(&received_payload
);
489 static void test_creds_passing(void)
492 int ret
, parent_socket
= -1, child_connection_socket
= -1;
494 char socket_dir_path
[] = "/tmp/test.unix.socket.creds.passing.XXXXXX";
495 char socket_path
[PATH_MAX
] = {};
496 struct expected_creds
{
502 diag("Receive peer's effective uid, effective gid, and pid from a unix socket");
504 if (!mkdtemp(socket_dir_path
)) {
505 PERROR("Failed to generate temporary socket location");
509 strncat(socket_path
, socket_dir_path
, sizeof(socket_path
) - strlen(socket_path
) - 1);
510 strncat(socket_path
, "/test_unix_socket", sizeof(socket_path
) - strlen(socket_path
) - 1);
512 parent_socket
= lttcomm_create_unix_sock(socket_path
);
513 ok(parent_socket
>= 0, "Created unix socket at path `%s`", socket_path
);
514 if (parent_socket
< 0) {
515 PERROR("Failed to create unix socket at path `%s`", socket_path
);
519 ret
= lttcomm_listen_unix_sock(parent_socket
);
521 PERROR("Failed to mark parent socket as a passive socket");
525 ret
= lttcomm_setsockopt_creds_unix_sock(parent_socket
);
527 PERROR("Failed to set SO_PASSCRED on parent socket");
533 PERROR("Failed to fork");
541 expected_creds
= (struct expected_creds
){
547 child_socket
= lttcomm_connect_unix_sock(socket_path
);
548 if (child_socket
< 0) {
549 PERROR("Failed to connect to parent socket");
553 ret
= lttcomm_setsockopt_creds_unix_sock(child_socket
);
555 PERROR("Failed to set SO_PASSCRED on child socket");
558 sock_ret
= lttcomm_send_creds_unix_sock(
559 child_socket
, &expected_creds
, sizeof(expected_creds
));
561 PERROR("Failed to send expected credentials");
564 ret
= close(child_socket
);
566 PERROR("Failed to close child socket");
572 lttng_sock_cred received_creds
= {};
574 child_connection_socket
= lttcomm_accept_unix_sock(parent_socket
);
575 if (child_connection_socket
< 0) {
580 ret
= lttcomm_setsockopt_creds_unix_sock(child_connection_socket
);
582 PERROR("Failed to set SO_PASSCRED on child connection socket");
586 sock_ret
= lttcomm_recv_creds_unix_sock(child_connection_socket
,
588 sizeof(expected_creds
),
591 PERROR("Failed to receive credentials");
595 wait_pid_ret
= waitpid(fork_ret
, &child_status
, 0);
596 if (wait_pid_ret
== -1) {
597 PERROR("Failed to wait for termination of child process");
600 if (!WIFEXITED(child_status
) || WEXITSTATUS(child_status
)) {
601 diag("Child process reported an error, test failed");
605 ok(expected_creds
.euid
== received_creds
.uid
,
606 "Received the expected effective uid (%d == %d)",
609 ok(expected_creds
.egid
== received_creds
.gid
,
610 "Received the expected effective gid (%d == %d)",
613 ok(expected_creds
.pid
== received_creds
.pid
,
614 "Received the expected pid (%d == %d)",
620 if (parent_socket
>= 0) {
621 ret
= close(parent_socket
);
623 PERROR("Failed to close parent socket");
628 /* Prevent libtap from printing a result for the child. */
632 /* Child exits at the end of this test. */
634 } else if (parent_socket
>= 0) {
635 if (child_connection_socket
>= 0) {
636 ret
= close(child_connection_socket
);
638 PERROR("Failed to close child connection socket");
642 ret
= unlink(socket_path
);
644 PERROR("Failed to unlink socket at path `%s`", socket_path
);
647 ret
= rmdir(socket_dir_path
);
649 PERROR("Failed to remove test directory at `%s`", socket_dir_path
);
656 plan_tests(TEST_COUNT
);
658 test_high_fd_count(HIGH_FD_COUNT
);
659 test_one_fd_per_message(MESSAGE_COUNT
);
660 test_receive_in_chunks(LARGE_PAYLOAD_SIZE
, LARGE_PAYLOAD_RECV_SIZE
);
661 test_creds_passing();
663 return exit_status();