* Skipped automatically if QEMU is not installed. */
#include <signal.h>
+#include <sys/eventfd.h>
#include <sys/socket.h>
#include "sd-event.h"
pidref_done(&pidref);
}
+TEST(qmp_client_qemu_add_fd) {
+ _cleanup_free_ char *qemu = NULL;
+ _cleanup_(qmp_client_unrefp) QmpClient *client = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ _cleanup_(pidref_done_sigkill_wait) PidRef pidref = PIDREF_NULL;
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *args = NULL;
+ _cleanup_close_ int fd_to_pass = -EBADF;
+ QmpTestResult t = {};
+ _cleanup_close_pair_ int qmp_fds[2] = EBADF_PAIR;
+ int r;
+
+ if (find_qemu_binary(&qemu) < 0) {
+ log_tests_skipped("QEMU not found");
+ return;
+ }
+
+ ASSERT_OK(sd_event_new(&event));
+ ASSERT_OK_ERRNO(socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, qmp_fds));
+
+ ASSERT_OK(start_qemu(qemu, qmp_fds[1], &pidref));
+ qmp_fds[1] = safe_close(qmp_fds[1]);
+
+ r = qmp_client_connect_fd(&client, qmp_fds[0]);
+ if (r < 0) {
+ log_tests_skipped_errno(r, "QMP connect failed");
+ return;
+ }
+ TAKE_FD(qmp_fds[0]);
+
+ ASSERT_OK(qmp_client_attach_event(client, event, SD_EVENT_PRIORITY_NORMAL));
+
+ fd_to_pass = eventfd(0, EFD_CLOEXEC);
+ ASSERT_OK_ERRNO(fd_to_pass);
+
+ ASSERT_OK(sd_json_buildo(&args, SD_JSON_BUILD_PAIR_UNSIGNED("fdset-id", 0)));
+
+ /* Pass an fd via SCM_RIGHTS on the very first invoke against a fresh client:
+ * add-fd lands right after the eagerly-enqueued qmp_capabilities. QEMU processes cap
+ * first (no fd needed), then add-fd, popping the fd from its FIFO receive queue. */
+ r = qmp_client_invoke(client, /* ret_slot= */ NULL, "add-fd",
+ QMP_CLIENT_ARGS_FD(args, TAKE_FD(fd_to_pass)),
+ on_test_result, &t);
+ if (r < 0) {
+ log_tests_skipped_errno(r, "QMP add-fd invoke failed");
+ return;
+ }
+ qmp_test_wait(event, &t);
+ ASSERT_EQ(t.error, 0);
+ ASSERT_NOT_NULL(t.result);
+
+ sd_json_variant *fdset_id = ASSERT_NOT_NULL(sd_json_variant_by_key(t.result, "fdset-id"));
+ sd_json_variant *fd_v = ASSERT_NOT_NULL(sd_json_variant_by_key(t.result, "fd"));
+ ASSERT_EQ(sd_json_variant_unsigned(fdset_id), (uint64_t) 0);
+ log_info("add-fd returned fdset-id=%" PRIu64 ", fd=%" PRIu64,
+ sd_json_variant_unsigned(fdset_id),
+ sd_json_variant_unsigned(fd_v));
+
+ qmp_test_result_done(&t);
+
+ /* Clean shutdown */
+ ASSERT_OK(qmp_client_invoke(client, /* ret_slot= */ NULL, "quit", NULL, on_test_result, &t));
+ qmp_test_wait(event, &t);
+ ASSERT_EQ(t.error, 0);
+ qmp_test_result_done(&t);
+
+ siginfo_t si = {};
+ ASSERT_OK(pidref_wait_for_terminate(&pidref, &si));
+ ASSERT_EQ(si.si_code, CLD_EXITED);
+ ASSERT_EQ(si.si_status, EXIT_SUCCESS);
+ pidref_done(&pidref);
+}
+
static int intro(void) {
/* QEMU dies between our last write and read on the QMP socket — without this we'd
* get killed by the SIGPIPE the kernel raises on write-after-EOF. */