int kdbus_name_list(struct kdbus_conn *conn, uint64_t flags) { struct kdbus_cmd_name_list cmd_list; struct kdbus_name_list *list; struct kdbus_cmd_name *name; int ret; cmd_list.flags = flags; ret = ioctl(conn->fd, KDBUS_CMD_NAME_LIST, &cmd_list); if (ret < 0) { kdbus_printf("error listing names: %d (%m)\n", ret); return EXIT_FAILURE; } kdbus_printf("REGISTRY:\n"); list = (struct kdbus_name_list *)(conn->buf + cmd_list.offset); KDBUS_ITEM_FOREACH(name, list, names) kdbus_printf("%8llu flags=0x%08llx conn=0x%08llx '%s'\n", name->owner_id, name->flags, name->conn_flags, name->size > sizeof(struct kdbus_cmd_name) ? name->name : ""); kdbus_printf("\n"); ret = kdbus_free(conn, cmd_list.offset); return ret; }
int timeout_msg_recv(struct kdbus_conn *conn) { struct kdbus_cmd_recv recv = {}; struct kdbus_msg *msg; int ret; ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &recv); if (ret < 0) { kdbus_printf("error receiving message: %d (%m)\n", ret); return -errno; } msg = (struct kdbus_msg *)(conn->buf + recv.offset); ASSERT_RETURN_VAL(msg->payload_type == KDBUS_PAYLOAD_KERNEL, -EINVAL); ASSERT_RETURN_VAL(msg->src_id == KDBUS_SRC_ID_KERNEL, -EINVAL); ASSERT_RETURN_VAL(msg->dst_id == conn->id, -EINVAL); expected &= ~(1ULL << msg->cookie_reply); kdbus_printf("Got message timeout for cookie %llu\n", msg->cookie_reply); ret = kdbus_free(conn, recv.offset); if (ret < 0) return ret; return 0; }
static int do_userns_map_id(pid_t pid, const char *map_file, const char *map_id) { int ret; int fd; fd = open(map_file, O_RDWR); if (fd < 0) { ret = -errno; kdbus_printf("error open %s: %d (%m)\n", map_file, ret); return ret; } ret = write(fd, map_id, strlen(map_id)); if (ret < 0) { ret = -errno; kdbus_printf("error write to %s: %d (%m)\n", map_file, ret); goto out; } ret = 0; out: close(fd); return ret; }
int kdbus_name_acquire(struct kdbus_conn *conn, const char *name, uint64_t flags) { struct kdbus_cmd_name *cmd_name; int ret; uint64_t size = sizeof(*cmd_name) + strlen(name) + 1; cmd_name = alloca(size); memset(cmd_name, 0, size); strcpy(cmd_name->name, name); cmd_name->size = size; cmd_name->flags = flags; ret = ioctl(conn->fd, KDBUS_CMD_NAME_ACQUIRE, cmd_name); if (ret < 0) { ret = -errno; kdbus_printf("error aquiring name: %s\n", strerror(-ret)); return ret; } kdbus_printf("%s(): flags after call: 0x%llx\n", __func__, cmd_name->conn_flags); return 0; }
int drop_privileges(uid_t uid, gid_t gid) { int ret; ret = setgroups(0, NULL); if (ret < 0) { ret = -errno; kdbus_printf("error setgroups: %d (%m)\n", ret); return ret; } ret = setresgid(gid, gid, gid); if (ret < 0) { ret = -errno; kdbus_printf("error setresgid: %d (%m)\n", ret); return ret; } ret = setresuid(uid, uid, uid); if (ret < 0) { ret = -errno; kdbus_printf("error setresuid: %d (%m)\n", ret); return ret; } return ret; }
int kdbus_conn_update_policy(struct kdbus_conn *conn, const char *name, const struct kdbus_policy_access *access, size_t num_access) { struct kdbus_cmd_update *update; struct kdbus_item *item; size_t i, size; int ret; size = sizeof(struct kdbus_cmd_update); size += KDBUS_ITEM_SIZE(strlen(name) + 1); size += num_access * KDBUS_ITEM_SIZE(sizeof(struct kdbus_policy_access)); update = malloc(size); if (!update) { ret = -errno; kdbus_printf("error malloc: %d (%m)\n", ret); return ret; } memset(update, 0, size); update->size = size; item = update->items; item->type = KDBUS_ITEM_NAME; item->size = KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1; strcpy(item->str, name); item = KDBUS_ITEM_NEXT(item); for (i = 0; i < num_access; i++) { item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_policy_access); item->type = KDBUS_ITEM_POLICY_ACCESS; item->policy_access.type = access[i].type; item->policy_access.access = access[i].access; item->policy_access.id = access[i].id; item = KDBUS_ITEM_NEXT(item); } ret = ioctl(conn->fd, KDBUS_CMD_CONN_UPDATE, update); if (ret < 0) { ret = -errno; kdbus_printf("error conn update: %d (%m)\n", ret); } free(update); return ret; }
int kdbus_test_sync_reply(struct kdbus_test_env *env) { pthread_t thread; int ret; conn_a = kdbus_hello(env->buspath, 0, NULL, 0); conn_b = kdbus_hello(env->buspath, 0, NULL, 0); ASSERT_RETURN(conn_a && conn_b); pthread_create(&thread, NULL, run_thread, NULL); ret = kdbus_msg_send(conn_b, NULL, cookie, KDBUS_MSG_FLAGS_EXPECT_REPLY | KDBUS_MSG_FLAGS_SYNC_REPLY, 5000000000ULL, 0, conn_a->id); pthread_join(thread, NULL); ASSERT_RETURN(ret == 0); kdbus_printf("-- closing bus connections\n"); kdbus_conn_free(conn_a); kdbus_conn_free(conn_b); return TEST_OK; }
static void *run_thread_reply(void *data) { int ret; unsigned long status = TEST_OK; ret = kdbus_msg_recv_poll(conn_a, 3000, NULL, NULL); if (ret < 0) goto exit_thread; kdbus_printf("Thread received message, sending reply ...\n"); /* using an unknown cookie must fail */ ret = kdbus_msg_send_reply(conn_a, ~cookie, conn_b->id); if (ret != -EPERM) { status = TEST_ERR; goto exit_thread; } ret = kdbus_msg_send_reply(conn_a, cookie, conn_b->id); if (ret != 0) { status = TEST_ERR; goto exit_thread; } exit_thread: pthread_exit(NULL); return (void *) status; }
static int send_fd(struct kdbus_conn *conn, uint64_t dst_id, int fd) { struct kdbus_item *item; struct kdbus_msg *msg; uint64_t size; int ret; size = sizeof(struct kdbus_msg); size += KDBUS_ITEM_SIZE(sizeof(int[2])); msg = alloca(size); memset(msg, 0, size); msg->size = size; msg->src_id = conn->id; msg->dst_id = dst_id; msg->payload_type = KDBUS_PAYLOAD_DBUS; item = msg->items; item->type = KDBUS_ITEM_FDS; item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(int); item->fds[0] = fd; item = KDBUS_ITEM_NEXT(item); ret = ioctl(conn->fd, KDBUS_CMD_MSG_SEND, msg); if (ret) { kdbus_printf("error sending message: %d err %d (%m)\n", ret, errno); return -errno; } return 0; }
int kdbus_test_timeout(struct kdbus_test_env *env) { struct kdbus_conn *conn_a, *conn_b; struct pollfd fd; int ret, i, n_msgs = 4; conn_a = kdbus_hello(env->buspath, 0, NULL, 0); conn_b = kdbus_hello(env->buspath, 0, NULL, 0); ASSERT_RETURN(conn_a && conn_b); fd.fd = conn_b->fd; /* * send messages that expect a reply (within 100 msec), * but never answer it. */ for (i = 0; i < n_msgs; i++) { kdbus_printf("Sending message with cookie %u ...\n", i); kdbus_msg_send(conn_b, NULL, i, KDBUS_MSG_FLAGS_EXPECT_REPLY, (i + 1) * 100ULL * 1000000ULL, 0, conn_a->id); expected |= 1ULL << i; } for (;;) { fd.events = POLLIN | POLLPRI | POLLHUP; fd.revents = 0; ret = poll(&fd, 1, (n_msgs + 1) * 100); if (ret == 0) kdbus_printf("--- timeout\n"); if (ret <= 0) break; if (fd.revents & POLLIN) timeout_msg_recv(conn_b); if (expected == 0) break; } ASSERT_RETURN(expected == 0); kdbus_conn_free(conn_a); kdbus_conn_free(conn_b); return TEST_OK; }
/** * Create new threads for receiving from multiple senders, * The 'conn_db' will be populated by newly created connections. * Caller should free all allocated connections. * * return 0 on success, negative errno on failure. */ static int kdbus_recv_in_threads(const char *bus, const char *name, struct kdbus_conn **conn_db) { int ret; unsigned int i, tid; unsigned long dst_id; unsigned long cookie = 1; unsigned int thread_nr = MAX_CONN - 1; pthread_t thread_id[MAX_CONN - 1] = {'\0'}; dst_id = name ? KDBUS_DST_ID_NAME : conn_db[0]->id; for (tid = 0, i = 1; tid < thread_nr; tid++, i++) { ret = pthread_create(&thread_id[tid], NULL, kdbus_recv_echo, (void *)conn_db[0]); if (ret < 0) { ret = -errno; kdbus_printf("error pthread_create: %d err %d (%m)\n", ret, errno); break; } /* just free before re-using */ kdbus_conn_free(conn_db[i]); conn_db[i] = NULL; /* We need to create connections here */ conn_db[i] = kdbus_hello(bus, 0, NULL, 0); if (!conn_db[i]) { ret = -errno; break; } ret = kdbus_add_match_empty(conn_db[i]); if (ret < 0) break; ret = kdbus_msg_send(conn_db[i], name, cookie++, 0, 0, 0, dst_id); if (ret < 0) break; } for (tid = 0; tid < thread_nr; tid++) { int thread_ret = 0; if (thread_id[tid]) { pthread_join(thread_id[tid], (void *)&thread_ret); if (thread_ret < 0 && ret == 0) ret = thread_ret; } } return ret; }
int kdbus_free(const struct kdbus_conn *conn, uint64_t offset) { int ret; ret = ioctl(conn->fd, KDBUS_CMD_FREE, &offset); if (ret < 0) { kdbus_printf("KDBUS_CMD_FREE failed: %d (%m)\n", ret); return -errno; } return 0; }
off_t sys_memfd_get_size(int fd, off_t *size) { struct stat stat; int ret; ret = fstat(fd, &stat); if (ret < 0) { kdbus_printf("stat() failed: %m\n"); return ret; } *size = stat.st_size; return 0; }
int kdbus_conn_update_attach_flags(struct kdbus_conn *conn, uint64_t flags) { int ret; size_t size; struct kdbus_cmd_update *update; struct kdbus_item *item; size = sizeof(struct kdbus_cmd_update); size += KDBUS_ITEM_SIZE(sizeof(uint64_t)); update = malloc(size); if (!update) { ret = -errno; kdbus_printf("error malloc: %d (%m)\n", ret); return ret; } memset(update, 0, size); update->size = size; item = update->items; item->type = KDBUS_ITEM_ATTACH_FLAGS; item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(uint64_t); item->data64[0] = flags; item = KDBUS_ITEM_NEXT(item); ret = ioctl(conn->fd, KDBUS_CMD_CONN_UPDATE, update); if (ret < 0) { ret = -errno; kdbus_printf("error conn update: %d (%m)\n", ret); } free(update); return ret; }
int kdbus_name_release(struct kdbus_conn *conn, const char *name) { struct kdbus_cmd_name *cmd_name; int ret; uint64_t size = sizeof(*cmd_name) + strlen(name) + 1; cmd_name = alloca(size); memset(cmd_name, 0, size); strcpy(cmd_name->name, name); cmd_name->size = size; kdbus_printf("conn %lld giving up name '%s'\n", (unsigned long long) conn->id, name); ret = ioctl(conn->fd, KDBUS_CMD_NAME_RELEASE, cmd_name); if (ret < 0) { ret = -errno; kdbus_printf("error releasing name: %s\n", strerror(-ret)); return ret; } return 0; }
int kdbus_create_bus(int control_fd, const char *name, char **path) { struct { struct kdbus_cmd_make head; /* bloom size item */ struct { uint64_t size; uint64_t type; struct kdbus_bloom_parameter bloom; } bs; /* name item */ uint64_t n_size; uint64_t n_type; char name[64]; } bus_make; int ret; memset(&bus_make, 0, sizeof(bus_make)); bus_make.bs.size = sizeof(bus_make.bs); bus_make.bs.type = KDBUS_ITEM_BLOOM_PARAMETER; bus_make.bs.bloom.size = 64; bus_make.bs.bloom.n_hash = 1; snprintf(bus_make.name, sizeof(bus_make.name), "%u-%s", getuid(), name); bus_make.n_type = KDBUS_ITEM_MAKE_NAME; bus_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(bus_make.name) + 1; bus_make.head.flags = KDBUS_MAKE_ACCESS_WORLD; bus_make.head.size = sizeof(struct kdbus_cmd_make) + sizeof(bus_make.bs) + bus_make.n_size; kdbus_printf("Creating bus with name >%s< on control fd %d ...\n", name, control_fd); ret = ioctl(control_fd, KDBUS_CMD_BUS_MAKE, &bus_make); if (ret == 0 && path) asprintf(path, "/dev/" KBUILD_MODNAME "/%s/bus", bus_make.name); return ret; }
int kdbus_test_daemon(struct kdbus_test_env *env) { struct pollfd fds[2]; int count; int ret; /* This test doesn't make any sense in non-interactive mode */ if (!kdbus_util_verbose) return TEST_OK; printf("Created connection %llu on bus '%s'\n", (unsigned long long) env->conn->id, env->buspath); kdbus_name_acquire(env->conn, "com.example.kdbus-test", 0); kdbus_printf(" Aquired name: com.example.kdbus-test\n"); fds[0].fd = env->conn->fd; fds[1].fd = STDIN_FILENO; printf("Monitoring connections:\n"); for (count = 0;; count++) { int i, nfds = sizeof(fds) / sizeof(fds[0]); for (i = 0; i < nfds; i++) { fds[i].events = POLLIN | POLLPRI | POLLHUP; fds[i].revents = 0; } ret = poll(fds, nfds, -1); if (ret <= 0) break; if (fds[0].revents & POLLIN) kdbus_msg_recv(env->conn, NULL); /* stdin */ if (fds[1].revents & POLLIN) break; } printf("Closing bus connection\n"); return TEST_OK; }
static int create_endpoint(const char *buspath, uid_t uid, const char *name, uint64_t flags) { struct { struct kdbus_cmd cmd; /* name item */ struct { uint64_t size; uint64_t type; /* max should be KDBUS_SYSNAME_MAX_LEN */ char str[128]; } name; } ep_make; int fd, ret; fd = open(buspath, O_RDWR); if (fd < 0) return fd; memset(&ep_make, 0, sizeof(ep_make)); snprintf(ep_make.name.str, /* Use the KDBUS_SYSNAME_MAX_LEN or sizeof(str) */ KDBUS_SYSNAME_MAX_LEN > strlen(name) ? KDBUS_SYSNAME_MAX_LEN : sizeof(ep_make.name.str), "%u-%s", uid, name); ep_make.name.type = KDBUS_ITEM_MAKE_NAME; ep_make.name.size = KDBUS_ITEM_HEADER_SIZE + strlen(ep_make.name.str) + 1; ep_make.cmd.flags = flags; ep_make.cmd.size = sizeof(ep_make.cmd) + ep_make.name.size; ret = kdbus_cmd_endpoint_make(fd, &ep_make.cmd); if (ret < 0) { kdbus_printf("error creating endpoint: %d (%m)\n", ret); return ret; } return fd; }
int kdbus_add_match_empty(struct kdbus_conn *conn) { struct { struct kdbus_cmd_match cmd; struct kdbus_item item; } buf; int ret; memset(&buf, 0, sizeof(buf)); buf.item.size = sizeof(uint64_t) * 3; buf.item.type = KDBUS_ITEM_ID; buf.item.id = KDBUS_MATCH_ID_ANY; buf.cmd.size = sizeof(buf.cmd) + buf.item.size; ret = ioctl(conn->fd, KDBUS_CMD_MATCH_ADD, &buf); if (ret < 0) kdbus_printf("--- error adding conn match: %d (%m)\n", ret); return ret; }
int kdbus_test_sync_reply(struct kdbus_test_env *env) { unsigned long status; pthread_t thread; int ret; conn_a = kdbus_hello(env->buspath, 0, NULL, 0); conn_b = kdbus_hello(env->buspath, 0, NULL, 0); ASSERT_RETURN(conn_a && conn_b); pthread_create(&thread, NULL, run_thread_reply, NULL); ret = kdbus_msg_send_sync(conn_b, NULL, cookie, KDBUS_MSG_EXPECT_REPLY, 5000000000ULL, 0, conn_a->id, -1); pthread_join(thread, (void *) &status); ASSERT_RETURN(status == 0); ASSERT_RETURN(ret == 0); ret = interrupt_sync(conn_a, conn_b); ASSERT_RETURN(ret == 0); ret = close_epipe_sync(env->buspath); ASSERT_RETURN(ret == 0); ret = cancel_fd_sync(conn_a, conn_b); ASSERT_RETURN(ret == 0); ret = no_cancel_sync(conn_a, conn_b); ASSERT_RETURN(ret == 0); kdbus_printf("-- closing bus connections\n"); kdbus_conn_free(conn_a); kdbus_conn_free(conn_b); return TEST_OK; }
static void *run_thread(void *data) { struct pollfd fd; int ret; fd.fd = conn_a->fd; fd.events = POLLIN | POLLPRI | POLLHUP; fd.revents = 0; ret = poll(&fd, 1, 3000); if (ret <= 0) goto thread_exit; if (fd.revents & POLLIN) { kdbus_printf("Thread received message, sending reply ...\n"); kdbus_msg_recv(conn_a, NULL, NULL); kdbus_msg_send(conn_a, NULL, 0, 0, cookie, 0, conn_b->id); } thread_exit: pthread_exit(NULL); return NULL; }
/** * Create new threads for receiving from multiple senders, * The 'conn_db' will be populated by newly created connections. * Caller should free all allocated connections. * * return 0 on success, negative errno on failure. */ static int kdbus_recv_in_threads(const char *bus, const char *name, struct kdbus_conn **conn_db) { int ret; bool pool_full = false; unsigned int sent_packets = 0; unsigned int lost_packets = 0; unsigned int i, tid; unsigned long dst_id; unsigned long cookie = 1; unsigned int thread_nr = MAX_CONN - 1; pthread_t thread_id[MAX_CONN - 1] = {'\0'}; dst_id = name ? KDBUS_DST_ID_NAME : conn_db[0]->id; for (tid = 0, i = 1; tid < thread_nr; tid++, i++) { ret = pthread_create(&thread_id[tid], NULL, kdbus_recv_echo, (void *)conn_db[0]); if (ret < 0) { ret = -errno; kdbus_printf("error pthread_create: %d (%m)\n", ret); break; } /* just free before re-using */ kdbus_conn_free(conn_db[i]); conn_db[i] = NULL; /* We need to create connections here */ conn_db[i] = kdbus_hello(bus, 0, NULL, 0); if (!conn_db[i]) { ret = -errno; break; } ret = kdbus_add_match_empty(conn_db[i]); if (ret < 0) break; ret = kdbus_msg_send(conn_db[i], name, cookie++, 0, 0, 0, dst_id); if (ret < 0) { /* * Receivers are not reading their messages, * not scheduled ?! * * So set the pool full here, perhaps the * connection pool or queue was full, later * recheck receivers errors */ if (ret == -ENOBUFS || ret == -EXFULL) pool_full = true; break; } sent_packets++; } for (tid = 0; tid < thread_nr; tid++) { int thread_ret = 0; if (thread_id[tid]) { pthread_join(thread_id[tid], (void *)&thread_ret); if (thread_ret < 0) { /* Update only if send did not fail */ if (ret == 0) ret = thread_ret; lost_packets++; } } } /* * When sending if we did fail with -ENOBUFS or -EXFULL * then we should have set lost_packet and we should at * least have sent_packets set to KDBUS_CONN_MAX_MSGS_PER_USER */ if (pool_full) { ASSERT_RETURN(lost_packets > 0); /* * We should at least send KDBUS_CONN_MAX_MSGS_PER_USER * * For every send operation we create a thread to * recv the packet, so we keep the queue clean */ ASSERT_RETURN(sent_packets >= KDBUS_CONN_MAX_MSGS_PER_USER); /* * Set ret to zero since we only failed due to * the receiving threads that have not been * scheduled */ ret = 0; } return ret; }
/* Return EXIT_SUCCESS, EXIT_FAILURE or negative errno */ static int __kdbus_clone_userns_test(const char *bus, const char *name, struct kdbus_conn **conn_db, int expected_status) { int efd; pid_t pid; int ret = 0; unsigned int uid = 65534; int status; ret = drop_privileges(uid, uid); ASSERT_RETURN_VAL(ret == 0, ret); /* * Since we just dropped privileges, the dumpable flag was just * cleared which makes the /proc/$clone_child/uid_map to be * owned by root, hence any userns uid mapping will fail with * -EPERM since the mapping will be done by uid 65534. * * To avoid this set the dumpable flag again which makes procfs * update the /proc/$clone_child/ inodes owner to 65534. * * Using this we will be able write to /proc/$clone_child/uid_map * as uid 65534 and map the uid 65534 to 0 inside the user * namespace. */ ret = prctl(PR_SET_DUMPABLE, SUID_DUMP_USER); ASSERT_RETURN_VAL(ret == 0, ret); /* sync parent/child */ efd = eventfd(0, EFD_CLOEXEC); ASSERT_RETURN_VAL(efd >= 0, efd); pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWUSER, NULL); if (pid < 0) { ret = -errno; kdbus_printf("error clone: %d (%m)\n", ret); /* * Normal user not allowed to create userns, * so nothing to worry about ? */ if (ret == -EPERM) { kdbus_printf("-- CLONE_NEWUSER TEST Failed for uid: %u\n" "-- Make sure that your kernel do not allow " "CLONE_NEWUSER for unprivileged users\n" "-- Upstream Commit: " "https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=5eaf563e\n", uid); ret = 0; } return ret; } if (pid == 0) { struct kdbus_conn *conn_src; eventfd_t event_status = 0; ret = prctl(PR_SET_PDEATHSIG, SIGKILL); ASSERT_EXIT(ret == 0); ret = eventfd_read(efd, &event_status); ASSERT_EXIT(ret >= 0 && event_status == 1); /* ping connection from the new user namespace */ conn_src = kdbus_hello(bus, 0, NULL, 0); ASSERT_EXIT(conn_src); ret = kdbus_add_match_empty(conn_src); ASSERT_EXIT(ret == 0); ret = kdbus_msg_send(conn_src, name, 0xabcd1234, 0, 0, 0, KDBUS_DST_ID_NAME); kdbus_conn_free(conn_src); _exit(ret == expected_status ? EXIT_SUCCESS : EXIT_FAILURE); } ret = userns_map_uid_gid(pid, "0 65534 1", "0 65534 1"); ASSERT_RETURN_VAL(ret == 0, ret); /* Tell child we are ready */ ret = eventfd_write(efd, 1); ASSERT_RETURN_VAL(ret == 0, ret); ret = waitpid(pid, &status, 0); ASSERT_RETURN_VAL(ret >= 0, ret); close(efd); return status == EXIT_SUCCESS ? TEST_OK : TEST_ERR; }
static int msg_recv_prio(struct kdbus_conn *conn, int64_t requested_prio, int64_t expected_prio) { struct kdbus_cmd_recv recv = { .size = sizeof(recv), .flags = KDBUS_RECV_USE_PRIORITY, .priority = requested_prio, }; struct kdbus_msg *msg; int ret; ret = ioctl(conn->fd, KDBUS_CMD_RECV, &recv); if (ret < 0) { kdbus_printf("error receiving message: %d (%m)\n", -errno); return -errno; } msg = (struct kdbus_msg *)(conn->buf + recv.reply.offset); kdbus_msg_dump(conn, msg); if (msg->priority != expected_prio) { kdbus_printf("expected message prio %lld, got %lld\n", (unsigned long long) expected_prio, (unsigned long long) msg->priority); return -EINVAL; } kdbus_msg_free(msg); ret = kdbus_free(conn, recv.reply.offset); if (ret < 0) return ret; return 0; } int kdbus_test_message_prio(struct kdbus_test_env *env) { struct kdbus_conn *a, *b; uint64_t cookie = 0; a = kdbus_hello(env->buspath, 0, NULL, 0); b = kdbus_hello(env->buspath, 0, NULL, 0); ASSERT_RETURN(a && b); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, 25, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -600, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, 10, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -35, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -100, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, 20, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -15, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -800, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -150, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, 10, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -800, a->id) == 0); ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -10, a->id) == 0); ASSERT_RETURN(msg_recv_prio(a, -200, -800) == 0); ASSERT_RETURN(msg_recv_prio(a, -100, -800) == 0); ASSERT_RETURN(msg_recv_prio(a, -400, -600) == 0); ASSERT_RETURN(msg_recv_prio(a, -400, -600) == -ENOMSG); ASSERT_RETURN(msg_recv_prio(a, 10, -150) == 0); ASSERT_RETURN(msg_recv_prio(a, 10, -100) == 0); kdbus_printf("--- get priority (all)\n"); ASSERT_RETURN(kdbus_msg_recv(a, NULL, NULL) == 0); kdbus_conn_free(a); kdbus_conn_free(b); return TEST_OK; } static int kdbus_test_notify_kernel_quota(struct kdbus_test_env *env) { int ret; unsigned int i; uint64_t offset; struct kdbus_conn *conn; struct kdbus_conn *reader; struct kdbus_msg *msg = NULL; reader = kdbus_hello(env->buspath, 0, NULL, 0); ASSERT_RETURN(reader); conn = kdbus_hello(env->buspath, 0, NULL, 0); ASSERT_RETURN(conn); /* Register for ID signals */ ret = kdbus_add_match_id(reader, 0x1, KDBUS_ITEM_ID_ADD, KDBUS_MATCH_ID_ANY); ASSERT_RETURN(ret == 0); ret = kdbus_add_match_id(reader, 0x2, KDBUS_ITEM_ID_REMOVE, KDBUS_MATCH_ID_ANY); ASSERT_RETURN(ret == 0); /* Each iteration two notifications: add and remove ID */ for (i = 0; i < KDBUS_CONN_MAX_MSGS / 2; i++) { struct kdbus_conn *notifier; notifier = kdbus_hello(env->buspath, 0, NULL, 0); ASSERT_RETURN(notifier); kdbus_conn_free(notifier); } /* * Now the reader queue is full, message will be lost * but it will not be accounted in dropped msgs */ ret = kdbus_msg_send(conn, NULL, 0xdeadbeef, 0, 0, 0, reader->id); ASSERT_RETURN(ret == -ENOBUFS); /* More ID kernel notifications that will be lost */ kdbus_conn_free(conn); conn = kdbus_hello(env->buspath, 0, NULL, 0); ASSERT_RETURN(conn); kdbus_conn_free(conn); ret = kdbus_msg_recv(reader, &msg, &offset); ASSERT_RETURN(ret == -EOVERFLOW); /* * We lost only 3 packet since only broadcast mesg * are accounted. The connection ID add/remove notification */ ASSERT_RETURN(offset == 3); kdbus_msg_free(msg); /* Read our queue */ for (i = 0; i < KDBUS_CONN_MAX_MSGS; i++) { ret = kdbus_msg_recv_poll(reader, 100, &msg, NULL); ASSERT_RETURN(ret == 0); kdbus_msg_free(msg); } ret = kdbus_msg_recv(reader, NULL, NULL); ASSERT_RETURN(ret == -EAGAIN); kdbus_conn_free(reader); return 0; }
int kdbus_test_activator(struct kdbus_test_env *env) { int ret; struct kdbus_conn *activator; struct pollfd fds[2]; bool activator_done = false; struct kdbus_policy_access access[2]; access[0].type = KDBUS_POLICY_ACCESS_USER; access[0].id = 1001; access[0].access = KDBUS_POLICY_OWN; access[1].type = KDBUS_POLICY_ACCESS_WORLD; access[1].access = KDBUS_POLICY_TALK; activator = kdbus_hello_activator(env->buspath, "foo.test.activator", access, 2); ASSERT_RETURN(activator); ret = kdbus_add_match_empty(env->conn); ASSERT_RETURN(ret == 0); ret = kdbus_name_list(env->conn, KDBUS_NAME_LIST_NAMES | KDBUS_NAME_LIST_UNIQUE | KDBUS_NAME_LIST_ACTIVATORS | KDBUS_NAME_LIST_QUEUED); ASSERT_RETURN(ret == 0); ret = kdbus_msg_send(env->conn, "foo.test.activator", 0xdeafbeef, 0, 0, 0, KDBUS_DST_ID_NAME); ASSERT_RETURN(ret == 0); fds[0].fd = activator->fd; fds[1].fd = env->conn->fd; kdbus_printf("-- entering poll loop ...\n"); for (;;) { int i, nfds = sizeof(fds) / sizeof(fds[0]); for (i = 0; i < nfds; i++) { fds[i].events = POLLIN | POLLPRI; fds[i].revents = 0; } ret = poll(fds, nfds, 3000); ASSERT_RETURN(ret >= 0); ret = kdbus_name_list(env->conn, KDBUS_NAME_LIST_NAMES); ASSERT_RETURN(ret == 0); if ((fds[0].revents & POLLIN) && !activator_done) { kdbus_printf("Starter was called back!\n"); ret = kdbus_name_acquire(env->conn, "foo.test.activator", KDBUS_NAME_REPLACE_EXISTING); ASSERT_RETURN(ret == 0); activator_done = true; } if (fds[1].revents & POLLIN) { kdbus_msg_recv(env->conn, NULL); break; } } kdbus_conn_free(activator); return TEST_OK; }
static void *run_thread_byebye(void *data) { struct kdbus_cmd cmd_byebye = { .size = sizeof(cmd_byebye) }; int ret; ret = kdbus_msg_recv_poll(conn_a, 3000, NULL, NULL); if (ret == 0) { kdbus_printf("Thread received message, invoking BYEBYE ...\n"); kdbus_msg_recv(conn_a, NULL, NULL); if (data == BYEBYE_ME) kdbus_cmd_byebye(conn_b->fd, &cmd_byebye); else if (data == BYEBYE_THEM) kdbus_cmd_byebye(conn_a->fd, &cmd_byebye); } pthread_exit(NULL); return NULL; } int kdbus_test_sync_byebye(struct kdbus_test_env *env) { pthread_t thread; int ret; /* * This sends a synchronous message to a thread, which waits until it * received the message and then invokes BYEBYE on the *ORIGINAL* * connection. That is, on the same connection that synchronously waits * for an reply. * This should properly wake the connection up and cause ECONNRESET as * the connection is disconnected now. * * The second time, we do the same but invoke BYEBYE on the *TARGET* * connection. This should also wake up the synchronous sender as the * reply cannot be sent by a disconnected target. */ conn_a = kdbus_hello(env->buspath, 0, NULL, 0); conn_b = kdbus_hello(env->buspath, 0, NULL, 0); ASSERT_RETURN(conn_a && conn_b); pthread_create(&thread, NULL, run_thread_byebye, BYEBYE_ME); ret = kdbus_msg_send_sync(conn_b, NULL, cookie, KDBUS_MSG_EXPECT_REPLY, 5000000000ULL, 0, conn_a->id, -1); ASSERT_RETURN(ret == -ECONNRESET); pthread_join(thread, NULL); kdbus_conn_free(conn_a); kdbus_conn_free(conn_b); conn_a = kdbus_hello(env->buspath, 0, NULL, 0); conn_b = kdbus_hello(env->buspath, 0, NULL, 0); ASSERT_RETURN(conn_a && conn_b); pthread_create(&thread, NULL, run_thread_byebye, BYEBYE_THEM); ret = kdbus_msg_send_sync(conn_b, NULL, cookie, KDBUS_MSG_EXPECT_REPLY, 5000000000ULL, 0, conn_a->id, -1); ASSERT_RETURN(ret == -EPIPE); pthread_join(thread, NULL); kdbus_conn_free(conn_a); kdbus_conn_free(conn_b); return TEST_OK; }
int kdbus_test_policy_ns(struct kdbus_test_env *env) { int i; int ret; struct kdbus_conn *activator = NULL; struct kdbus_conn *policy_holder = NULL; char *bus = env->buspath; if (geteuid() > 0) { kdbus_printf("error geteuid() != 0, %s() needs root\n", __func__); return TEST_SKIP; } /* we require user-namespaces */ if (access("/proc/self/uid_map", F_OK) != 0) return TEST_SKIP; conn_db = calloc(MAX_CONN, sizeof(struct kdbus_conn *)); ASSERT_RETURN(conn_db); memset(conn_db, 0, MAX_CONN * sizeof(struct kdbus_conn *)); conn_db[0] = kdbus_hello(bus, 0, NULL, 0); ASSERT_RETURN(conn_db[0]); ret = kdbus_add_match_empty(conn_db[0]); ASSERT_RETURN(ret == 0); ret = kdbus_fork_test_by_id(bus, conn_db, -EPERM, -EPERM); ASSERT_EXIT(ret == 0); ret = kdbus_register_policy_holder(bus, POLICY_NAME, &policy_holder); ASSERT_RETURN(ret == 0); /* Try to register the same name with an activator */ ret = kdbus_register_same_activator(bus, POLICY_NAME, &activator); ASSERT_RETURN(ret == 0); /* Acquire POLICY_NAME */ ret = kdbus_name_acquire(conn_db[0], POLICY_NAME, NULL); ASSERT_RETURN(ret == 0); ret = kdbus_normal_test(bus, POLICY_NAME, conn_db); ASSERT_RETURN(ret == 0); ret = kdbus_name_list(conn_db[0], KDBUS_NAME_LIST_NAMES | KDBUS_NAME_LIST_UNIQUE | KDBUS_NAME_LIST_ACTIVATORS | KDBUS_NAME_LIST_QUEUED); ASSERT_RETURN(ret == 0); ret = kdbus_fork_test(bus, POLICY_NAME, conn_db, EXIT_SUCCESS); ASSERT_RETURN(ret == 0); /* * childs connections are able to talk to conn_db[0] since * current POLICY_NAME TALK type is KDBUS_POLICY_ACCESS_WORLD, * so expect EXIT_SUCCESS when sending from child. However, * since the child's connection does not own any well-known * name, The parent connection conn_db[0] should fail with * -EPERM but since it is a privileged bus user the TALK is * allowed. */ ret = kdbus_fork_test_by_id(bus, conn_db, EXIT_SUCCESS, EXIT_SUCCESS); ASSERT_EXIT(ret == 0); /* * Connections that can talk are perhaps being destroyed now. * Restrict the policy and purge cache entries where the * conn_db[0] is the destination. * * Now only connections with uid == 0 are allowed to talk. */ ret = kdbus_set_policy_talk(policy_holder, POLICY_NAME, geteuid(), KDBUS_POLICY_ACCESS_USER); ASSERT_RETURN(ret == 0); /* * Testing connections (FORK+DROP) again: * After setting the policy re-check connections * we expect the childs to fail with -EPERM */ ret = kdbus_fork_test(bus, POLICY_NAME, conn_db, -EPERM); ASSERT_RETURN(ret == 0); /* * Now expect that both parent and child to fail. * * Child should fail with -EPERM since we just restricted * the POLICY_NAME TALK to uid 0 and its uid is 65534. * * Since the parent's connection will timeout when receiving * from the child, we never continue. FWIW just put -EPERM. */ ret = kdbus_fork_test_by_id(bus, conn_db, -EPERM, -EPERM); ASSERT_EXIT(ret == 0); /* Check if the name can be reached in a new userns */ ret = kdbus_clone_userns_test(bus, POLICY_NAME, conn_db, -EPERM); ASSERT_RETURN(ret == 0); for (i = 0; i < MAX_CONN; i++) kdbus_conn_free(conn_db[i]); kdbus_conn_free(activator); kdbus_conn_free(policy_holder); free(conn_db); return ret; }
int kdbus_msg_send(const struct kdbus_conn *conn, const char *name, uint64_t cookie, uint64_t flags, uint64_t timeout, int64_t priority, uint64_t dst_id) { struct kdbus_msg *msg; const char ref1[1024 * 128 + 3] = "0123456789_0"; const char ref2[] = "0123456789_1"; struct kdbus_item *item; uint64_t size; int memfd = -1; int ret; size = sizeof(struct kdbus_msg); size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)); size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)); size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)); if (dst_id == KDBUS_DST_ID_BROADCAST) size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) + 64; else { memfd = sys_memfd_create("my-name-is-nice", 1024 * 1024); if (memfd < 0) { kdbus_printf("failed to create memfd: %m\n"); return memfd; } if (write(memfd, "kdbus memfd 1234567", 19) != 19) { ret = -errno; kdbus_printf("writing to memfd failed: %m\n"); return ret; } ret = sys_memfd_seal_set(memfd); if (ret < 0) { ret = -errno; kdbus_printf("memfd sealing failed: %m\n"); return ret; } size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_memfd)); } if (name) size += KDBUS_ITEM_SIZE(strlen(name) + 1); msg = malloc(size); if (!msg) { ret = -errno; kdbus_printf("unable to malloc()!?\n"); return ret; } memset(msg, 0, size); msg->flags = flags; msg->timeout_ns = timeout; msg->priority = priority; msg->size = size; msg->src_id = conn->id; msg->dst_id = name ? 0 : dst_id; msg->cookie = cookie; msg->payload_type = KDBUS_PAYLOAD_DBUS; item = msg->items; if (name) { item->type = KDBUS_ITEM_DST_NAME; item->size = KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1; strcpy(item->str, name); item = KDBUS_ITEM_NEXT(item); } item->type = KDBUS_ITEM_PAYLOAD_VEC; item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); item->vec.address = (uintptr_t)&ref1; item->vec.size = sizeof(ref1); item = KDBUS_ITEM_NEXT(item); /* data padding for ref1 */ item->type = KDBUS_ITEM_PAYLOAD_VEC; item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); item->vec.address = (uintptr_t)NULL; item->vec.size = KDBUS_ALIGN8(sizeof(ref1)) - sizeof(ref1); item = KDBUS_ITEM_NEXT(item); item->type = KDBUS_ITEM_PAYLOAD_VEC; item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); item->vec.address = (uintptr_t)&ref2; item->vec.size = sizeof(ref2); item = KDBUS_ITEM_NEXT(item); if (dst_id == KDBUS_DST_ID_BROADCAST) { item->type = KDBUS_ITEM_BLOOM_FILTER; item->size = KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) + 64; item->bloom_filter.generation = 0; } else { item->type = KDBUS_ITEM_PAYLOAD_MEMFD; item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_memfd); item->memfd.size = 16; item->memfd.fd = memfd; } item = KDBUS_ITEM_NEXT(item); ret = ioctl(conn->fd, KDBUS_CMD_MSG_SEND, msg); if (ret < 0) { ret = -errno; kdbus_printf("error sending message: %d (%m)\n", ret); return ret; } if (memfd >= 0) close(memfd); if (flags & KDBUS_MSG_FLAGS_SYNC_REPLY) { struct kdbus_msg *reply; kdbus_printf("SYNC REPLY @offset %llu:\n", msg->offset_reply); reply = (struct kdbus_msg *)(conn->buf + msg->offset_reply); kdbus_msg_dump(conn, reply); ret = kdbus_free(conn, msg->offset_reply); if (ret < 0) return ret; } free(msg); return 0; }
void kdbus_msg_dump(const struct kdbus_conn *conn, const struct kdbus_msg *msg) { const struct kdbus_item *item = msg->items; char buf_src[32]; char buf_dst[32]; uint64_t timeout = 0; uint64_t cookie_reply = 0; if (msg->flags & KDBUS_MSG_FLAGS_EXPECT_REPLY) timeout = msg->timeout_ns; else cookie_reply = msg->cookie_reply; kdbus_printf("MESSAGE: %s (%llu bytes) flags=0x%08llx, %s → %s, " "cookie=%llu, timeout=%llu cookie_reply=%llu priority=%lli\n", enum_PAYLOAD(msg->payload_type), (unsigned long long)msg->size, (unsigned long long)msg->flags, msg_id(msg->src_id, buf_src), msg_id(msg->dst_id, buf_dst), (unsigned long long)msg->cookie, (unsigned long long)timeout, (unsigned long long)cookie_reply, (long long)msg->priority); KDBUS_ITEM_FOREACH(item, msg, items) { if (item->size < KDBUS_ITEM_HEADER_SIZE) { kdbus_printf(" +%s (%llu bytes) invalid data record\n", enum_MSG(item->type), item->size); break; } switch (item->type) { case KDBUS_ITEM_PAYLOAD_OFF: { char *s; if (item->vec.offset == ~0ULL) s = "[\\0-bytes]"; else s = (char *)msg + item->vec.offset; kdbus_printf(" +%s (%llu bytes) off=%llu size=%llu '%s'\n", enum_MSG(item->type), item->size, (unsigned long long)item->vec.offset, (unsigned long long)item->vec.size, s); break; } case KDBUS_ITEM_PAYLOAD_MEMFD: { char *buf; off_t size; buf = mmap(NULL, item->memfd.size, PROT_READ, MAP_PRIVATE, item->memfd.fd, 0); if (buf == MAP_FAILED) { kdbus_printf("mmap() fd=%i size=%llu failed: %m\n", item->memfd.fd, item->memfd.size); break; } if (sys_memfd_get_size(item->memfd.fd, &size) < 0) { kdbus_printf("KDBUS_CMD_MEMFD_SIZE_GET failed: %m\n"); break; } kdbus_printf(" +%s (%llu bytes) fd=%i size=%llu filesize=%llu '%s'\n", enum_MSG(item->type), item->size, item->memfd.fd, (unsigned long long)item->memfd.size, (unsigned long long)size, buf); munmap(buf, item->memfd.size); break; } case KDBUS_ITEM_CREDS: kdbus_printf(" +%s (%llu bytes) uid=%lld, gid=%lld, pid=%lld, tid=%lld, starttime=%lld\n", enum_MSG(item->type), item->size, item->creds.uid, item->creds.gid, item->creds.pid, item->creds.tid, item->creds.starttime); break; case KDBUS_ITEM_AUXGROUPS: { int i, n; kdbus_printf(" +%s (%llu bytes)\n", enum_MSG(item->type), item->size); n = (item->size - KDBUS_ITEM_HEADER_SIZE) / sizeof(uint64_t); for (i = 0; i < n; i++) kdbus_printf(" gid[%d] = %lld\n", i, item->data64[i]); break; } case KDBUS_ITEM_PID_COMM: case KDBUS_ITEM_TID_COMM: case KDBUS_ITEM_EXE: case KDBUS_ITEM_CGROUP: case KDBUS_ITEM_SECLABEL: case KDBUS_ITEM_DST_NAME: case KDBUS_ITEM_CONN_NAME: kdbus_printf(" +%s (%llu bytes) '%s' (%zu)\n", enum_MSG(item->type), item->size, item->str, strlen(item->str)); break; case KDBUS_ITEM_NAME: { kdbus_printf(" +%s (%llu bytes) '%s' (%zu) flags=0x%08llx\n", enum_MSG(item->type), item->size, item->name.name, strlen(item->name.name), item->name.flags); break; } case KDBUS_ITEM_CMDLINE: { size_t size = item->size - KDBUS_ITEM_HEADER_SIZE; const char *str = item->str; int count = 0; kdbus_printf(" +%s (%llu bytes) ", enum_MSG(item->type), item->size); while (size) { kdbus_printf("'%s' ", str); size -= strlen(str) + 1; str += strlen(str) + 1; count++; } kdbus_printf("(%d string%s)\n", count, (count == 1) ? "" : "s"); break; } case KDBUS_ITEM_AUDIT: kdbus_printf(" +%s (%llu bytes) loginuid=%llu sessionid=%llu\n", enum_MSG(item->type), item->size, (unsigned long long)item->audit.loginuid, (unsigned long long)item->audit.sessionid); break; case KDBUS_ITEM_CAPS: { int n; const uint32_t *cap; int i; kdbus_printf(" +%s (%llu bytes) len=%llu bytes\n", enum_MSG(item->type), item->size, (unsigned long long)item->size - KDBUS_ITEM_HEADER_SIZE); cap = item->data32; n = (item->size - KDBUS_ITEM_HEADER_SIZE) / 4 / sizeof(uint32_t); kdbus_printf(" CapInh="); for (i = 0; i < n; i++) kdbus_printf("%08x", cap[(0 * n) + (n - i - 1)]); kdbus_printf(" CapPrm="); for (i = 0; i < n; i++) kdbus_printf("%08x", cap[(1 * n) + (n - i - 1)]); kdbus_printf(" CapEff="); for (i = 0; i < n; i++) kdbus_printf("%08x", cap[(2 * n) + (n - i - 1)]); kdbus_printf(" CapBnd="); for (i = 0; i < n; i++) kdbus_printf("%08x", cap[(3 * n) + (n - i - 1)]); kdbus_printf("\n"); break; } case KDBUS_ITEM_TIMESTAMP: kdbus_printf(" +%s (%llu bytes) seq=%llu realtime=%lluns monotonic=%lluns\n", enum_MSG(item->type), item->size, (unsigned long long)item->timestamp.seqnum, (unsigned long long)item->timestamp.realtime_ns, (unsigned long long)item->timestamp.monotonic_ns); break; case KDBUS_ITEM_REPLY_TIMEOUT: kdbus_printf(" +%s (%llu bytes) cookie=%llu\n", enum_MSG(item->type), item->size, msg->cookie_reply); break; case KDBUS_ITEM_NAME_ADD: case KDBUS_ITEM_NAME_REMOVE: case KDBUS_ITEM_NAME_CHANGE: kdbus_printf(" +%s (%llu bytes) '%s', old id=%lld, new id=%lld, old_flags=0x%llx new_flags=0x%llx\n", enum_MSG(item->type), (unsigned long long) item->size, item->name_change.name, item->name_change.old.id, item->name_change.new.id, item->name_change.old.flags, item->name_change.new.flags); break; case KDBUS_ITEM_ID_ADD: case KDBUS_ITEM_ID_REMOVE: kdbus_printf(" +%s (%llu bytes) id=%llu flags=%llu\n", enum_MSG(item->type), (unsigned long long) item->size, (unsigned long long) item->id_change.id, (unsigned long long) item->id_change.flags); break; default: kdbus_printf(" +%s (%llu bytes)\n", enum_MSG(item->type), item->size); break; } } if ((char *)item - ((char *)msg + msg->size) >= 8) kdbus_printf("invalid padding at end of message\n"); kdbus_printf("\n"); }
struct kdbus_conn * kdbus_hello(const char *path, uint64_t flags, const struct kdbus_item *item, size_t item_size) { int fd, ret; struct { struct kdbus_cmd_hello hello; uint64_t size; uint64_t type; char comm[16]; uint8_t extra_items[item_size]; } h; struct kdbus_conn *conn; memset(&h, 0, sizeof(h)); if (item_size > 0) memcpy(h.extra_items, item, item_size); kdbus_printf("-- opening bus connection %s\n", path); fd = open(path, O_RDWR|O_CLOEXEC); if (fd < 0) { kdbus_printf("--- error %d (%m)\n", fd); return NULL; } h.hello.conn_flags = flags | KDBUS_HELLO_ACCEPT_FD; h.hello.attach_flags = _KDBUS_ATTACH_ALL; h.type = KDBUS_ITEM_CONN_NAME; h.size = KDBUS_ITEM_HEADER_SIZE + sizeof(h.comm); strcpy(h.comm, "this-is-my-name"); h.hello.size = sizeof(h); h.hello.pool_size = POOL_SIZE; ret = ioctl(fd, KDBUS_CMD_HELLO, &h.hello); if (ret < 0) { kdbus_printf("--- error when saying hello: %d (%m)\n", ret); return NULL; } kdbus_printf("-- Our peer ID for %s: %llu -- bus uuid: '%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x'\n", path, (unsigned long long)h.hello.id, h.hello.id128[0], h.hello.id128[1], h.hello.id128[2], h.hello.id128[3], h.hello.id128[4], h.hello.id128[5], h.hello.id128[6], h.hello.id128[7], h.hello.id128[8], h.hello.id128[9], h.hello.id128[10], h.hello.id128[11], h.hello.id128[12], h.hello.id128[13], h.hello.id128[14], h.hello.id128[15]); conn = malloc(sizeof(*conn)); if (!conn) { kdbus_printf("unable to malloc()!?\n"); return NULL; } conn->buf = mmap(NULL, POOL_SIZE, PROT_READ, MAP_PRIVATE, fd, 0); if (conn->buf == MAP_FAILED) { free(conn); close(fd); kdbus_printf("--- error mmap (%m)\n"); return NULL; } conn->fd = fd; conn->id = h.hello.id; return conn; }