int kdbus_msg_recv(struct kdbus_conn *conn, struct kdbus_msg **msg_out, uint64_t *offset) { struct kdbus_cmd_recv recv = {}; struct kdbus_msg *msg; int ret; ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &recv); if (ret < 0) { ret = -errno; return ret; } msg = (struct kdbus_msg *)(conn->buf + recv.offset); kdbus_msg_dump(conn, msg); if (msg_out) { *msg_out = msg; if (offset) *offset = recv.offset; } else { kdbus_msg_free(msg); ret = kdbus_free(conn, recv.offset); if (ret < 0) return ret; } return 0; }
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; }
int kdbus_test_message_basic(struct kdbus_test_env *env) { struct kdbus_conn *conn; struct kdbus_conn *sender; struct kdbus_msg *msg; uint64_t cookie = 0x1234abcd5678eeff; uint64_t offset; int ret; sender = kdbus_hello(env->buspath, 0, NULL, 0); ASSERT_RETURN(sender != NULL); /* create a 2nd connection */ conn = kdbus_hello(env->buspath, 0, NULL, 0); ASSERT_RETURN(conn != NULL); ret = kdbus_add_match_empty(conn); ASSERT_RETURN(ret == 0); ret = kdbus_add_match_empty(sender); ASSERT_RETURN(ret == 0); /* send over 1st connection */ ret = kdbus_msg_send(sender, NULL, cookie, 0, 0, 0, KDBUS_DST_ID_BROADCAST); ASSERT_RETURN(ret == 0); /* Make sure that we do not get our own broadcasts */ ret = kdbus_msg_recv(sender, NULL, NULL); ASSERT_RETURN(ret == -EAGAIN); /* ... and receive on the 2nd */ ret = kdbus_msg_recv_poll(conn, 100, &msg, &offset); ASSERT_RETURN(ret == 0); ASSERT_RETURN(msg->cookie == cookie); kdbus_msg_free(msg); ret = kdbus_free(conn, offset); ASSERT_RETURN(ret == 0); kdbus_conn_free(sender); kdbus_conn_free(conn); return TEST_OK; }
int kdbus_test_byebye(struct kdbus_test_env *env) { struct kdbus_conn *conn; struct kdbus_cmd_recv recv = {}; int ret; /* create a 2nd connection */ conn = kdbus_hello(env->buspath, 0, NULL, 0); ASSERT_RETURN(conn != NULL); ret = kdbus_add_match_empty(conn); ASSERT_RETURN(ret == 0); ret = kdbus_add_match_empty(env->conn); ASSERT_RETURN(ret == 0); /* send over 1st connection */ ret = kdbus_msg_send(env->conn, NULL, 0, 0, 0, 0, KDBUS_DST_ID_BROADCAST); ASSERT_RETURN(ret == 0); /* say byebye on the 2nd, which must fail */ ret = ioctl(conn->fd, KDBUS_CMD_BYEBYE, 0); ASSERT_RETURN(ret == -1 && errno == EBUSY); /* receive the message */ ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &recv); ASSERT_RETURN(ret == 0); ret = kdbus_free(conn, recv.offset); ASSERT_RETURN(ret == 0); /* and try again */ ret = ioctl(conn->fd, KDBUS_CMD_BYEBYE, 0); ASSERT_RETURN(ret == 0); /* a 2nd try should result in -EALREADY */ ret = ioctl(conn->fd, KDBUS_CMD_BYEBYE, 0); ASSERT_RETURN(ret == -1 && errno == EOPNOTSUPP); kdbus_conn_free(conn); return TEST_OK; }
static int kdbus_fork_test_by_id(const char *bus, struct kdbus_conn **conn_db, int parent_status, int child_status) { int ret; pid_t pid; uint64_t cookie = 0x9876ecba; struct kdbus_msg *msg = NULL; uint64_t offset = 0; int status = 0; /* * If the child_status is not EXIT_SUCCESS, then we expect * that sending from the child will fail, thus receiving * from parent must error with -ETIMEDOUT, and vice versa. */ bool parent_timedout = !!child_status; bool child_timedout = !!parent_status; pid = fork(); ASSERT_RETURN_VAL(pid >= 0, pid); if (pid == 0) { struct kdbus_conn *conn_src; ret = prctl(PR_SET_PDEATHSIG, SIGKILL); ASSERT_EXIT(ret == 0); ret = drop_privileges(65534, 65534); ASSERT_EXIT(ret == 0); conn_src = kdbus_hello(bus, 0, NULL, 0); ASSERT_EXIT(conn_src); ret = kdbus_add_match_empty(conn_src); ASSERT_EXIT(ret == 0); /* * child_status is always checked against send * operations, in case it fails always return * EXIT_FAILURE. */ ret = kdbus_msg_send(conn_src, NULL, cookie, 0, 0, 0, conn_db[0]->id); ASSERT_EXIT(ret == child_status); ret = kdbus_msg_recv_poll(conn_src, 100, NULL, NULL); kdbus_conn_free(conn_src); /* * Child kdbus_msg_recv_poll() should timeout since * the parent_status was set to a non EXIT_SUCCESS * value. */ if (child_timedout) _exit(ret == -ETIMEDOUT ? EXIT_SUCCESS : EXIT_FAILURE); _exit(ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE); } ret = kdbus_msg_recv_poll(conn_db[0], 100, &msg, &offset); /* * If parent_timedout is set then this should fail with * -ETIMEDOUT since the child_status was set to a non * EXIT_SUCCESS value. Otherwise, assume * that kdbus_msg_recv_poll() has succeeded. */ if (parent_timedout) { ASSERT_RETURN_VAL(ret == -ETIMEDOUT, TEST_ERR); /* timedout no need to continue, we don't have the * child connection ID, so just terminate. */ goto out; } else { ASSERT_RETURN_VAL(ret == 0, ret); } ret = kdbus_msg_send(conn_db[0], NULL, ++cookie, 0, 0, 0, msg->src_id); /* * parent_status is checked against send operations, * on failures always return TEST_ERR. */ ASSERT_RETURN_VAL(ret == parent_status, TEST_ERR); kdbus_msg_free(msg); kdbus_free(conn_db[0], offset); out: ret = waitpid(pid, &status, 0); ASSERT_RETURN_VAL(ret >= 0, ret); return (status == EXIT_SUCCESS) ? TEST_OK : TEST_ERR; }
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; }
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; }