diff options
Diffstat (limited to 'samples')
-rw-r--r-- | samples/Kconfig | 7 | ||||
-rw-r--r-- | samples/Makefile | 4 | ||||
-rw-r--r-- | samples/kdbus/.gitignore | 1 | ||||
-rw-r--r-- | samples/kdbus/Makefile | 9 | ||||
-rw-r--r-- | samples/kdbus/kdbus-api.h | 114 | ||||
-rw-r--r-- | samples/kdbus/kdbus-workers.c | 1346 |
6 files changed, 2 insertions, 1479 deletions
diff --git a/samples/Kconfig b/samples/Kconfig index 72051e348..d54f28c6d 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -55,13 +55,6 @@ config SAMPLE_KDB Build an example of how to dynamically add the hello command to the kdb shell. -config SAMPLE_KDBUS - bool "Build kdbus API example" - depends on KDBUS - help - Build an example of how the kdbus API can be used from - userspace. - config SAMPLE_RPMSG_CLIENT tristate "Build rpmsg client sample -- loadable modules only" depends on RPMSG && m diff --git a/samples/Makefile b/samples/Makefile index d0c9b3c67..48001d7e2 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -1,5 +1,5 @@ # Makefile for Linux samples code obj-$(CONFIG_SAMPLES) += kobject/ kprobes/ trace_events/ livepatch/ \ - hw_breakpoint/ kfifo/ kdb/ kdbus/ hidraw/ rpmsg/ \ - seccomp/ configfs/ + hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/ \ + configfs/ diff --git a/samples/kdbus/.gitignore b/samples/kdbus/.gitignore deleted file mode 100644 index ee07d9857..000000000 --- a/samples/kdbus/.gitignore +++ /dev/null @@ -1 +0,0 @@ -kdbus-workers diff --git a/samples/kdbus/Makefile b/samples/kdbus/Makefile deleted file mode 100644 index 137f84272..000000000 --- a/samples/kdbus/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# kbuild trick to avoid linker error. Can be omitted if a module is built. -obj- := dummy.o - -hostprogs-$(CONFIG_SAMPLE_KDBUS) += kdbus-workers - -always := $(hostprogs-y) - -HOSTCFLAGS_kdbus-workers.o += -I$(objtree)/usr/include -HOSTLOADLIBES_kdbus-workers := -lrt diff --git a/samples/kdbus/kdbus-api.h b/samples/kdbus/kdbus-api.h deleted file mode 100644 index 7f3abae18..000000000 --- a/samples/kdbus/kdbus-api.h +++ /dev/null @@ -1,114 +0,0 @@ -#ifndef KDBUS_API_H -#define KDBUS_API_H - -#include <sys/ioctl.h> -#include <linux/kdbus.h> - -#define KDBUS_ALIGN8(l) (((l) + 7) & ~7) -#define KDBUS_ITEM_HEADER_SIZE offsetof(struct kdbus_item, data) -#define KDBUS_ITEM_SIZE(s) KDBUS_ALIGN8((s) + KDBUS_ITEM_HEADER_SIZE) -#define KDBUS_ITEM_NEXT(item) \ - (typeof(item))((uint8_t *)(item) + KDBUS_ALIGN8((item)->size)) -#define KDBUS_FOREACH(iter, first, _size) \ - for ((iter) = (first); \ - ((uint8_t *)(iter) < (uint8_t *)(first) + (_size)) && \ - ((uint8_t *)(iter) >= (uint8_t *)(first)); \ - (iter) = (void *)((uint8_t *)(iter) + KDBUS_ALIGN8((iter)->size))) - -static inline int kdbus_cmd_bus_make(int control_fd, struct kdbus_cmd *cmd) -{ - int ret = ioctl(control_fd, KDBUS_CMD_BUS_MAKE, cmd); - return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; -} - -static inline int kdbus_cmd_endpoint_make(int bus_fd, struct kdbus_cmd *cmd) -{ - int ret = ioctl(bus_fd, KDBUS_CMD_ENDPOINT_MAKE, cmd); - return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; -} - -static inline int kdbus_cmd_endpoint_update(int ep_fd, struct kdbus_cmd *cmd) -{ - int ret = ioctl(ep_fd, KDBUS_CMD_ENDPOINT_UPDATE, cmd); - return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; -} - -static inline int kdbus_cmd_hello(int bus_fd, struct kdbus_cmd_hello *cmd) -{ - int ret = ioctl(bus_fd, KDBUS_CMD_HELLO, cmd); - return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; -} - -static inline int kdbus_cmd_update(int fd, struct kdbus_cmd *cmd) -{ - int ret = ioctl(fd, KDBUS_CMD_UPDATE, cmd); - return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; -} - -static inline int kdbus_cmd_byebye(int conn_fd, struct kdbus_cmd *cmd) -{ - int ret = ioctl(conn_fd, KDBUS_CMD_BYEBYE, cmd); - return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; -} - -static inline int kdbus_cmd_free(int conn_fd, struct kdbus_cmd_free *cmd) -{ - int ret = ioctl(conn_fd, KDBUS_CMD_FREE, cmd); - return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; -} - -static inline int kdbus_cmd_conn_info(int conn_fd, struct kdbus_cmd_info *cmd) -{ - int ret = ioctl(conn_fd, KDBUS_CMD_CONN_INFO, cmd); - return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; -} - -static inline int kdbus_cmd_bus_creator_info(int conn_fd, struct kdbus_cmd_info *cmd) -{ - int ret = ioctl(conn_fd, KDBUS_CMD_BUS_CREATOR_INFO, cmd); - return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; -} - -static inline int kdbus_cmd_list(int fd, struct kdbus_cmd_list *cmd) -{ - int ret = ioctl(fd, KDBUS_CMD_LIST, cmd); - return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; -} - -static inline int kdbus_cmd_send(int conn_fd, struct kdbus_cmd_send *cmd) -{ - int ret = ioctl(conn_fd, KDBUS_CMD_SEND, cmd); - return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; -} - -static inline int kdbus_cmd_recv(int conn_fd, struct kdbus_cmd_recv *cmd) -{ - int ret = ioctl(conn_fd, KDBUS_CMD_RECV, cmd); - return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; -} - -static inline int kdbus_cmd_name_acquire(int conn_fd, struct kdbus_cmd *cmd) -{ - int ret = ioctl(conn_fd, KDBUS_CMD_NAME_ACQUIRE, cmd); - return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; -} - -static inline int kdbus_cmd_name_release(int conn_fd, struct kdbus_cmd *cmd) -{ - int ret = ioctl(conn_fd, KDBUS_CMD_NAME_RELEASE, cmd); - return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; -} - -static inline int kdbus_cmd_match_add(int conn_fd, struct kdbus_cmd_match *cmd) -{ - int ret = ioctl(conn_fd, KDBUS_CMD_MATCH_ADD, cmd); - return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; -} - -static inline int kdbus_cmd_match_remove(int conn_fd, struct kdbus_cmd_match *cmd) -{ - int ret = ioctl(conn_fd, KDBUS_CMD_MATCH_REMOVE, cmd); - return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; -} - -#endif /* KDBUS_API_H */ diff --git a/samples/kdbus/kdbus-workers.c b/samples/kdbus/kdbus-workers.c deleted file mode 100644 index 5a6dfdce3..000000000 --- a/samples/kdbus/kdbus-workers.c +++ /dev/null @@ -1,1346 +0,0 @@ -/* - * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> - * - * kdbus is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - */ - -/* - * Example: Workers - * This program computes prime-numbers based on the sieve of Eratosthenes. The - * master sets up a shared memory region and spawns workers which clear out the - * non-primes. The master reacts to keyboard input and to client-requests to - * control what each worker does. Note that this is in no way meant as efficient - * way to compute primes. It should only serve as example how a master/worker - * concept can be implemented with kdbus used as control messages. - * - * The main process is called the 'master'. It creates a new, private bus which - * will be used between the master and its workers to communicate. The master - * then spawns a fixed number of workers. Whenever a worker dies (detected via - * SIGCHLD), the master spawns a new worker. When done, the master waits for all - * workers to exit, prints a status report and exits itself. - * - * The master process does *not* keep track of its workers. Instead, this - * example implements a PULL model. That is, the master acquires a well-known - * name on the bus which each worker uses to request tasks from the master. If - * there are no more tasks, the master will return an empty task-list, which - * casues a worker to exit immediately. - * - * As tasks can be computationally expensive, we support cancellation. Whenever - * the master process is interrupted, it will drop its well-known name on the - * bus. This causes kdbus to broadcast a name-change notification. The workers - * check for broadcast messages regularly and will exit if they receive one. - * - * This example exists of 4 objects: - * * master: The master object contains the context of the master process. This - * process manages the prime-context, spawns workers and assigns - * prime-ranges to each worker to compute. - * The master itself does not do any prime-computations itself. - * * child: The child object contains the context of a worker. It inherits the - * prime context from its parent (the master) and then creates a new - * bus context to request prime-ranges to compute. - * * prime: The "prime" object is used to abstract how we compute primes. When - * allocated, it prepares a memory region to hold 1 bit for each - * natural number up to a fixed maximum ('MAX_PRIMES'). - * The memory region is backed by a memfd which we share between - * processes. Each worker now gets assigned a range of natural - * numbers which it clears multiples of off the memory region. The - * master process is responsible of distributing all natural numbers - * up to the fixed maximum to its workers. - * * bus: The bus object is an abstraction of the kdbus API. It is pretty - * straightfoward and only manages the connection-fd plus the - * memory-mapped pool in a single object. - * - * This example is in reversed order, which should make it easier to read - * top-down, but requires some forward-declarations. Just ignore those. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <sys/syscall.h> - -/* glibc < 2.7 does not ship sys/signalfd.h */ -/* we require kernels with __NR_memfd_create */ -#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 7 && defined(__NR_memfd_create) - -#include <ctype.h> -#include <errno.h> -#include <fcntl.h> -#include <linux/memfd.h> -#include <signal.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/poll.h> -#include <sys/signalfd.h> -#include <sys/time.h> -#include <sys/wait.h> -#include <time.h> -#include <unistd.h> -#include "kdbus-api.h" - -/* FORWARD DECLARATIONS */ - -#define POOL_SIZE (16 * 1024 * 1024) -#define MAX_PRIMES (2UL << 24) -#define WORKER_COUNT (16) -#define PRIME_STEPS (65536 * 4) - -static const char *arg_busname = "example-workers"; -static const char *arg_modname = "kdbus"; -static const char *arg_master = "org.freedesktop.master"; - -static int err_assert(int r_errno, const char *msg, const char *func, int line, - const char *file) -{ - r_errno = (r_errno != 0) ? -abs(r_errno) : -EFAULT; - if (r_errno < 0) { - errno = -r_errno; - fprintf(stderr, "ERR: %s: %m (%s:%d in %s)\n", - msg, func, line, file); - } - return r_errno; -} - -#define err_r(_r, _msg) err_assert((_r), (_msg), __func__, __LINE__, __FILE__) -#define err(_msg) err_r(errno, (_msg)) - -struct prime; -struct bus; -struct master; -struct child; - -struct prime { - int fd; - uint8_t *area; - size_t max; - size_t done; - size_t status; -}; - -static int prime_new(struct prime **out); -static void prime_free(struct prime *p); -static bool prime_done(struct prime *p); -static void prime_consume(struct prime *p, size_t amount); -static int prime_run(struct prime *p, struct bus *cancel, size_t number); -static void prime_print(struct prime *p); - -struct bus { - int fd; - uint8_t *pool; -}; - -static int bus_open_connection(struct bus **out, uid_t uid, const char *name, - uint64_t recv_flags); -static void bus_close_connection(struct bus *b); -static void bus_poool_free_slice(struct bus *b, uint64_t offset); -static int bus_acquire_name(struct bus *b, const char *name); -static int bus_install_name_loss_match(struct bus *b, const char *name); -static int bus_poll(struct bus *b); -static int bus_make(uid_t uid, const char *name); - -struct master { - size_t n_workers; - size_t max_workers; - - int signal_fd; - int control_fd; - - struct prime *prime; - struct bus *bus; -}; - -static int master_new(struct master **out); -static void master_free(struct master *m); -static int master_run(struct master *m); -static int master_poll(struct master *m); -static int master_handle_stdin(struct master *m); -static int master_handle_signal(struct master *m); -static int master_handle_bus(struct master *m); -static int master_reply(struct master *m, const struct kdbus_msg *msg); -static int master_waitpid(struct master *m); -static int master_spawn(struct master *m); - -struct child { - struct bus *bus; - struct prime *prime; -}; - -static int child_new(struct child **out, struct prime *p); -static void child_free(struct child *c); -static int child_run(struct child *c); - -/* END OF FORWARD DECLARATIONS */ - -/* - * This is the main entrypoint of this example. It is pretty straightforward. We - * create a master object, run the computation, print a status report and then - * exit. Nothing particularly interesting here, so lets look into the master - * object... - */ -int main(int argc, char **argv) -{ - struct master *m = NULL; - int r; - - r = master_new(&m); - if (r < 0) - goto out; - - r = master_run(m); - if (r < 0) - goto out; - - if (0) - prime_print(m->prime); - -out: - master_free(m); - if (r < 0 && r != -EINTR) - fprintf(stderr, "failed\n"); - else - fprintf(stderr, "done\n"); - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} - -/* - * ...this will allocate a new master context. It keeps track of the current - * number of children/workers that are running, manages a signalfd to track - * SIGCHLD, and creates a private kdbus bus. Afterwards, it opens its connection - * to the bus and acquires a well known-name (arg_master). - */ -static int master_new(struct master **out) -{ - struct master *m; - sigset_t smask; - int r; - - m = calloc(1, sizeof(*m)); - if (!m) - return err("cannot allocate master"); - - m->max_workers = WORKER_COUNT; - m->signal_fd = -1; - m->control_fd = -1; - - /* Block SIGINT and SIGCHLD signals */ - sigemptyset(&smask); - sigaddset(&smask, SIGINT); - sigaddset(&smask, SIGCHLD); - sigprocmask(SIG_BLOCK, &smask, NULL); - - m->signal_fd = signalfd(-1, &smask, SFD_CLOEXEC); - if (m->signal_fd < 0) { - r = err("cannot create signalfd"); - goto error; - } - - r = prime_new(&m->prime); - if (r < 0) - goto error; - - m->control_fd = bus_make(getuid(), arg_busname); - if (m->control_fd < 0) { - r = m->control_fd; - goto error; - } - - /* - * Open a bus connection for the master, and require each received - * message to have a metadata item of type KDBUS_ITEM_PIDS attached. - * The current UID is needed to compute the name of the bus node to - * connect to. - */ - r = bus_open_connection(&m->bus, getuid(), - arg_busname, KDBUS_ATTACH_PIDS); - if (r < 0) - goto error; - - /* - * Acquire a well-known name on the bus, so children can address - * messages to the master using KDBUS_DST_ID_NAME as destination-ID - * of messages. - */ - r = bus_acquire_name(m->bus, arg_master); - if (r < 0) - goto error; - - *out = m; - return 0; - -error: - master_free(m); - return r; -} - -/* pretty straightforward destructor of a master object */ -static void master_free(struct master *m) -{ - if (!m) - return; - - bus_close_connection(m->bus); - if (m->control_fd >= 0) - close(m->control_fd); - prime_free(m->prime); - if (m->signal_fd >= 0) - close(m->signal_fd); - free(m); -} - -static int master_run(struct master *m) -{ - int res, r = 0; - - while (!prime_done(m->prime)) { - while (m->n_workers < m->max_workers) { - r = master_spawn(m); - if (r < 0) - break; - } - - r = master_poll(m); - if (r < 0) - break; - } - - if (r < 0) { - bus_close_connection(m->bus); - m->bus = NULL; - } - - while (m->n_workers > 0) { - res = master_poll(m); - if (res < 0) { - if (m->bus) { - bus_close_connection(m->bus); - m->bus = NULL; - } - r = res; - } - } - - return r == -EINTR ? 0 : r; -} - -static int master_poll(struct master *m) -{ - struct pollfd fds[3] = {}; - int r = 0, n = 0; - - /* - * Add stdin, the eventfd and the connection owner file descriptor to - * the pollfd table, and handle incoming traffic on the latter in - * master_handle_bus(). - */ - fds[n].fd = STDIN_FILENO; - fds[n++].events = POLLIN; - fds[n].fd = m->signal_fd; - fds[n++].events = POLLIN; - if (m->bus) { - fds[n].fd = m->bus->fd; - fds[n++].events = POLLIN; - } - - r = poll(fds, n, -1); - if (r < 0) - return err("poll() failed"); - - if (fds[0].revents & POLLIN) - r = master_handle_stdin(m); - else if (fds[0].revents) - r = err("ERR/HUP on stdin"); - if (r < 0) - return r; - - if (fds[1].revents & POLLIN) - r = master_handle_signal(m); - else if (fds[1].revents) - r = err("ERR/HUP on signalfd"); - if (r < 0) - return r; - - if (fds[2].revents & POLLIN) - r = master_handle_bus(m); - else if (fds[2].revents) - r = err("ERR/HUP on bus"); - - return r; -} - -static int master_handle_stdin(struct master *m) -{ - char buf[128]; - ssize_t l; - int r = 0; - - l = read(STDIN_FILENO, buf, sizeof(buf)); - if (l < 0) - return err("cannot read stdin"); - if (l == 0) - return err_r(-EINVAL, "EOF on stdin"); - - while (l-- > 0) { - switch (buf[l]) { - case 'q': - /* quit */ - r = -EINTR; - break; - case '\n': - case ' ': - /* ignore */ - break; - default: - if (isgraph(buf[l])) - fprintf(stderr, "invalid input '%c'\n", buf[l]); - else - fprintf(stderr, "invalid input 0x%x\n", buf[l]); - break; - } - } - - return r; -} - -static int master_handle_signal(struct master *m) -{ - struct signalfd_siginfo val; - ssize_t l; - - l = read(m->signal_fd, &val, sizeof(val)); - if (l < 0) - return err("cannot read signalfd"); - if (l != sizeof(val)) - return err_r(-EINVAL, "invalid data from signalfd"); - - switch (val.ssi_signo) { - case SIGCHLD: - return master_waitpid(m); - case SIGINT: - return err_r(-EINTR, "interrupted"); - default: - return err_r(-EINVAL, "caught invalid signal"); - } -} - -static int master_handle_bus(struct master *m) -{ - struct kdbus_cmd_recv recv = { .size = sizeof(recv) }; - const struct kdbus_msg *msg = NULL; - const struct kdbus_item *item; - const struct kdbus_vec *vec = NULL; - int r = 0; - - /* - * To receive a message, the KDBUS_CMD_RECV ioctl is used. - * It takes an argument of type 'struct kdbus_cmd_recv', which - * will contain information on the received message when the call - * returns. See kdbus.message(7). - */ - r = kdbus_cmd_recv(m->bus->fd, &recv); - /* - * EAGAIN is returned when there is no message waiting on this - * connection. This is not an error - simply bail out. - */ - if (r == -EAGAIN) - return 0; - if (r < 0) - return err_r(r, "cannot receive message"); - - /* - * Messages received by a connection are stored inside the connection's - * pool, at an offset that has been returned in the 'recv' command - * struct above. The value describes the relative offset from the - * start address of the pool. A message is described with - * 'struct kdbus_msg'. See kdbus.message(7). - */ - msg = (void *)(m->bus->pool + recv.msg.offset); - - /* - * A messages describes its actual payload in an array of items. - * KDBUS_FOREACH() is a simple iterator that walks such an array. - * struct kdbus_msg has a field to denote its total size, which is - * needed to determine the number of items in the array. - */ - KDBUS_FOREACH(item, msg->items, - msg->size - offsetof(struct kdbus_msg, items)) { - /* - * An item of type PAYLOAD_OFF describes in-line memory - * stored in the pool at a described offset. That offset is - * relative to the start address of the message header. - * This example program only expects one single item of that - * type, remembers the struct kdbus_vec member of the item - * when it sees it, and bails out if there is more than one - * of them. - */ - if (item->type == KDBUS_ITEM_PAYLOAD_OFF) { - if (vec) { - r = err_r(-EEXIST, - "message with multiple vecs"); - break; - } - vec = &item->vec; - if (vec->size != 1) { - r = err_r(-EINVAL, "invalid message size"); - break; - } - - /* - * MEMFDs are transported as items of type PAYLOAD_MEMFD. - * If such an item is attached, a new file descriptor was - * installed into the task when KDBUS_CMD_RECV was called, and - * its number is stored in item->memfd.fd. - * Implementers *must* handle this item type and close the - * file descriptor when no longer needed in order to prevent - * file descriptor exhaustion. This example program just bails - * out with an error in this case, as memfds are not expected - * in this context. - */ - } else if (item->type == KDBUS_ITEM_PAYLOAD_MEMFD) { - r = err_r(-EINVAL, "message with memfd"); - break; - } - } - if (r < 0) - goto exit; - if (!vec) { - r = err_r(-EINVAL, "empty message"); - goto exit; - } - - switch (*((const uint8_t *)msg + vec->offset)) { - case 'r': { - r = master_reply(m, msg); - break; - } - default: - r = err_r(-EINVAL, "invalid message type"); - break; - } - -exit: - /* - * We are done with the memory slice that was given to us through - * recv.msg.offset. Tell the kernel it can use it for other content - * in the future. See kdbus.pool(7). - */ - bus_poool_free_slice(m->bus, recv.msg.offset); - return r; -} - -static int master_reply(struct master *m, const struct kdbus_msg *msg) -{ - struct kdbus_cmd_send cmd; - struct kdbus_item *item; - struct kdbus_msg *reply; - size_t size, status, p[2]; - int r; - - /* - * This functions sends a message over kdbus. To do this, it uses the - * KDBUS_CMD_SEND ioctl, which takes a command struct argument of type - * 'struct kdbus_cmd_send'. This struct stores a pointer to the actual - * message to send. See kdbus.message(7). - */ - p[0] = m->prime->done; - p[1] = prime_done(m->prime) ? 0 : PRIME_STEPS; - - size = sizeof(*reply); - size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)); - - /* Prepare the message to send */ - reply = alloca(size); - memset(reply, 0, size); - reply->size = size; - - /* Each message has a cookie that can be used to send replies */ - reply->cookie = 1; - - /* The payload_type is arbitrary, but it must be non-zero */ - reply->payload_type = 0xdeadbeef; - - /* - * We are sending a reply. Let the kernel know the cookie of the - * message we are replying to. - */ - reply->cookie_reply = msg->cookie; - - /* - * Messages can either be directed to a well-known name (stored as - * string) or to a unique name (stored as number). This example does - * the latter. If the message would be directed to a well-known name - * instead, the message's dst_id field would be set to - * KDBUS_DST_ID_NAME, and the name would be attaches in an item of type - * KDBUS_ITEM_DST_NAME. See below for an example, and also refer to - * kdbus.message(7). - */ - reply->dst_id = msg->src_id; - - /* Our message has exactly one item to store its payload */ - item = reply->items; - item->type = KDBUS_ITEM_PAYLOAD_VEC; - item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); - item->vec.address = (uintptr_t)p; - item->vec.size = sizeof(p); - - /* - * Now prepare the command struct, and reference the message we want - * to send. - */ - memset(&cmd, 0, sizeof(cmd)); - cmd.size = sizeof(cmd); - cmd.msg_address = (uintptr_t)reply; - - /* - * Finally, employ the command on the connection owner - * file descriptor. - */ - r = kdbus_cmd_send(m->bus->fd, &cmd); - if (r < 0) - return err_r(r, "cannot send reply"); - - if (p[1]) { - prime_consume(m->prime, p[1]); - status = m->prime->done * 10000 / m->prime->max; - if (status != m->prime->status) { - m->prime->status = status; - fprintf(stderr, "status: %7.3lf%%\n", - (double)status / 100); - } - } - - return 0; -} - -static int master_waitpid(struct master *m) -{ - pid_t pid; - int r; - - while ((pid = waitpid(-1, &r, WNOHANG)) > 0) { - if (m->n_workers > 0) - --m->n_workers; - if (!WIFEXITED(r)) - r = err_r(-EINVAL, "child died unexpectedly"); - else if (WEXITSTATUS(r) != 0) - r = err_r(-WEXITSTATUS(r), "child failed"); - } - - return r; -} - -static int master_spawn(struct master *m) -{ - struct child *c = NULL; - struct prime *p = NULL; - pid_t pid; - int r; - - /* Spawn off one child and call child_run() inside it */ - - pid = fork(); - if (pid < 0) - return err("cannot fork"); - if (pid > 0) { - /* parent */ - ++m->n_workers; - return 0; - } - - /* child */ - - p = m->prime; - m->prime = NULL; - master_free(m); - - r = child_new(&c, p); - if (r < 0) - goto exit; - - r = child_run(c); - -exit: - child_free(c); - exit(abs(r)); -} - -static int child_new(struct child **out, struct prime *p) -{ - struct child *c; - int r; - - c = calloc(1, sizeof(*c)); - if (!c) - return err("cannot allocate child"); - - c->prime = p; - - /* - * Open a connection to the bus and require each received message to - * carry a list of the well-known names the sendind connection currently - * owns. The current UID is needed in order to determine the name of the - * bus node to connect to. - */ - r = bus_open_connection(&c->bus, getuid(), - arg_busname, KDBUS_ATTACH_NAMES); - if (r < 0) - goto error; - - /* - * Install a kdbus match so the child's connection gets notified when - * the master loses its well-known name. - */ - r = bus_install_name_loss_match(c->bus, arg_master); - if (r < 0) - goto error; - - *out = c; - return 0; - -error: - child_free(c); - return r; -} - -static void child_free(struct child *c) -{ - if (!c) - return; - - bus_close_connection(c->bus); - prime_free(c->prime); - free(c); -} - -static int child_run(struct child *c) -{ - struct kdbus_cmd_send cmd; - struct kdbus_item *item; - struct kdbus_vec *vec = NULL; - struct kdbus_msg *msg; - struct timespec spec; - size_t n, steps, size; - int r = 0; - - /* - * Let's send a message to the master and ask for work. To do this, - * we use the KDBUS_CMD_SEND ioctl, which takes an argument of type - * 'struct kdbus_cmd_send'. This struct stores a pointer to the actual - * message to send. See kdbus.message(7). - */ - size = sizeof(*msg); - size += KDBUS_ITEM_SIZE(strlen(arg_master) + 1); - size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)); - - msg = alloca(size); - memset(msg, 0, size); - msg->size = size; - - /* - * Tell the kernel that we expect a reply to this message. This means - * that - * - * a) The remote peer will gain temporary permission to talk to us - * even if it would not be allowed to normally. - * - * b) A timeout value is required. - * - * For asynchronous send commands, if no reply is received, we will - * get a kernel notification with an item of type - * KDBUS_ITEM_REPLY_TIMEOUT attached. - * - * For synchronous send commands (which this example does), the - * ioctl will block until a reply is received or the timeout is - * exceeded. - */ - msg->flags = KDBUS_MSG_EXPECT_REPLY; - - /* Set our cookie. Replies must use this cookie to send their reply. */ - msg->cookie = 1; - - /* The payload_type is arbitrary, but it must be non-zero */ - msg->payload_type = 0xdeadbeef; - - /* - * We are sending our message to the current owner of a well-known - * name. This makes an item of type KDBUS_ITEM_DST_NAME mandatory. - */ - msg->dst_id = KDBUS_DST_ID_NAME; - - /* - * Set the reply timeout to 5 seconds. Timeouts are always set in - * absolute timestamps, based con CLOCK_MONOTONIC. See kdbus.message(7). - */ - clock_gettime(CLOCK_MONOTONIC_COARSE, &spec); - msg->timeout_ns += (5 + spec.tv_sec) * 1000ULL * 1000ULL * 1000ULL; - msg->timeout_ns += spec.tv_nsec; - - /* - * Fill the appended items. First, set the well-known name of the - * destination we want to talk to. - */ - item = msg->items; - item->type = KDBUS_ITEM_DST_NAME; - item->size = KDBUS_ITEM_HEADER_SIZE + strlen(arg_master) + 1; - strcpy(item->str, arg_master); - - /* - * The 2nd item contains a vector to memory we want to send. It - * can be content of any type. In our case, we're sending a one-byte - * string only. The memory referenced by this item will be copied into - * the pool of the receiver connection, and does not need to be valid - * after the command is employed. - */ - 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)"r"; - item->vec.size = 1; - - /* Set up the command struct and reference the message we prepared */ - memset(&cmd, 0, sizeof(cmd)); - cmd.size = sizeof(cmd); - cmd.msg_address = (uintptr_t)msg; - - /* - * The send commands knows a mode in which it will block until a - * reply to a message is received. This example uses that mode. - * The pool offset to the received reply will be stored in the command - * struct after the send command returned. See below. - */ - cmd.flags = KDBUS_SEND_SYNC_REPLY; - - /* - * Finally, employ the command on the connection owner - * file descriptor. - */ - r = kdbus_cmd_send(c->bus->fd, &cmd); - if (r == -ESRCH || r == -EPIPE || r == -ECONNRESET) - return 0; - if (r < 0) - return err_r(r, "cannot send request to master"); - - /* - * The command was sent with the KDBUS_SEND_SYNC_REPLY flag set, - * and returned successfully, which means that cmd.reply.offset now - * points to a message inside our connection's pool where the reply - * is found. This is equivalent to receiving the reply with - * KDBUS_CMD_RECV, but it doesn't require waiting for the reply with - * poll() and also saves the ioctl to receive the message. - */ - msg = (void *)(c->bus->pool + cmd.reply.offset); - - /* - * A messages describes its actual payload in an array of items. - * KDBUS_FOREACH() is a simple iterator that walks such an array. - * struct kdbus_msg has a field to denote its total size, which is - * needed to determine the number of items in the array. - */ - KDBUS_FOREACH(item, msg->items, - msg->size - offsetof(struct kdbus_msg, items)) { - /* - * An item of type PAYLOAD_OFF describes in-line memory - * stored in the pool at a described offset. That offset is - * relative to the start address of the message header. - * This example program only expects one single item of that - * type, remembers the struct kdbus_vec member of the item - * when it sees it, and bails out if there is more than one - * of them. - */ - if (item->type == KDBUS_ITEM_PAYLOAD_OFF) { - if (vec) { - r = err_r(-EEXIST, - "message with multiple vecs"); - break; - } - vec = &item->vec; - if (vec->size != 2 * sizeof(size_t)) { - r = err_r(-EINVAL, "invalid message size"); - break; - } - /* - * MEMFDs are transported as items of type PAYLOAD_MEMFD. - * If such an item is attached, a new file descriptor was - * installed into the task when KDBUS_CMD_RECV was called, and - * its number is stored in item->memfd.fd. - * Implementers *must* handle this item type close the - * file descriptor when no longer needed in order to prevent - * file descriptor exhaustion. This example program just bails - * out with an error in this case, as memfds are not expected - * in this context. - */ - } else if (item->type == KDBUS_ITEM_PAYLOAD_MEMFD) { - r = err_r(-EINVAL, "message with memfd"); - break; - } - } - if (r < 0) - goto exit; - if (!vec) { - r = err_r(-EINVAL, "empty message"); - goto exit; - } - - n = ((size_t *)((const uint8_t *)msg + vec->offset))[0]; - steps = ((size_t *)((const uint8_t *)msg + vec->offset))[1]; - - while (steps-- > 0) { - ++n; - r = prime_run(c->prime, c->bus, n); - if (r < 0) - break; - r = bus_poll(c->bus); - if (r != 0) { - r = r < 0 ? r : -EINTR; - break; - } - } - -exit: - /* - * We are done with the memory slice that was given to us through - * cmd.reply.offset. Tell the kernel it can use it for other content - * in the future. See kdbus.pool(7). - */ - bus_poool_free_slice(c->bus, cmd.reply.offset); - return r; -} - -/* - * Prime Computation - * - */ - -static int prime_new(struct prime **out) -{ - struct prime *p; - int r; - - p = calloc(1, sizeof(*p)); - if (!p) - return err("cannot allocate prime memory"); - - p->fd = -1; - p->area = MAP_FAILED; - p->max = MAX_PRIMES; - - /* - * Prepare and map a memfd to store the bit-fields for the number - * ranges we want to perform the prime detection on. - */ - p->fd = syscall(__NR_memfd_create, "prime-area", MFD_CLOEXEC); - if (p->fd < 0) { - r = err("cannot create memfd"); - goto error; - } - - r = ftruncate(p->fd, p->max / 8 + 1); - if (r < 0) { - r = err("cannot ftruncate area"); - goto error; - } - - p->area = mmap(NULL, p->max / 8 + 1, PROT_READ | PROT_WRITE, - MAP_SHARED, p->fd, 0); - if (p->area == MAP_FAILED) { - r = err("cannot mmap memfd"); - goto error; - } - - *out = p; - return 0; - -error: - prime_free(p); - return r; -} - -static void prime_free(struct prime *p) -{ - if (!p) - return; - - if (p->area != MAP_FAILED) - munmap(p->area, p->max / 8 + 1); - if (p->fd >= 0) - close(p->fd); - free(p); -} - -static bool prime_done(struct prime *p) -{ - return p->done >= p->max; -} - -static void prime_consume(struct prime *p, size_t amount) -{ - p->done += amount; -} - -static int prime_run(struct prime *p, struct bus *cancel, size_t number) -{ - size_t i, n = 0; - int r; - - if (number < 2 || number > 65535) - return 0; - - for (i = number * number; - i < p->max && i > number; - i += number) { - p->area[i / 8] |= 1 << (i % 8); - - if (!(++n % (1 << 20))) { - r = bus_poll(cancel); - if (r != 0) - return r < 0 ? r : -EINTR; - } - } - - return 0; -} - -static void prime_print(struct prime *p) -{ - size_t i, l = 0; - - fprintf(stderr, "PRIMES:"); - for (i = 0; i < p->max; ++i) { - if (!(p->area[i / 8] & (1 << (i % 8)))) - fprintf(stderr, "%c%7zu", !(l++ % 16) ? '\n' : ' ', i); - } - fprintf(stderr, "\nEND\n"); -} - -static int bus_open_connection(struct bus **out, uid_t uid, const char *name, - uint64_t recv_flags) -{ - struct kdbus_cmd_hello hello; - char path[128]; - struct bus *b; - int r; - - /* - * The 'bus' object is our representation of a kdbus connection which - * stores two details: the connection owner file descriptor, and the - * mmap()ed memory of its associated pool. See kdbus.connection(7) and - * kdbus.pool(7). - */ - b = calloc(1, sizeof(*b)); - if (!b) - return err("cannot allocate bus memory"); - - b->fd = -1; - b->pool = MAP_FAILED; - - /* Compute the name of the bus node to connect to. */ - snprintf(path, sizeof(path), "/sys/fs/%s/%lu-%s/bus", - arg_modname, (unsigned long)uid, name); - b->fd = open(path, O_RDWR | O_CLOEXEC); - if (b->fd < 0) { - r = err("cannot open bus"); - goto error; - } - - /* - * To make a connection to the bus, the KDBUS_CMD_HELLO ioctl is used. - * It takes an argument of type 'struct kdbus_cmd_hello'. - */ - memset(&hello, 0, sizeof(hello)); - hello.size = sizeof(hello); - - /* - * Specify a mask of metadata attach flags, describing metadata items - * that this new connection allows to be sent. - */ - hello.attach_flags_send = _KDBUS_ATTACH_ALL; - - /* - * Specify a mask of metadata attach flags, describing metadata items - * that this new connection wants to be receive along with each message. - */ - hello.attach_flags_recv = recv_flags; - - /* - * A connection may choose the size of its pool, but the number has to - * comply with two rules: a) it must be greater than 0, and b) it must - * be a mulitple of PAGE_SIZE. See kdbus.pool(7). - */ - hello.pool_size = POOL_SIZE; - - /* - * Now employ the command on the file descriptor opened above. - * This command will turn the file descriptor into a connection-owner - * file descriptor that controls the life-time of the connection; once - * it's closed, the connection is shut down. - */ - r = kdbus_cmd_hello(b->fd, &hello); - if (r < 0) { - err_r(r, "HELLO failed"); - goto error; - } - - bus_poool_free_slice(b, hello.offset); - - /* - * Map the pool of the connection. Its size has been set in the - * command struct above. See kdbus.pool(7). - */ - b->pool = mmap(NULL, POOL_SIZE, PROT_READ, MAP_SHARED, b->fd, 0); - if (b->pool == MAP_FAILED) { - r = err("cannot mmap pool"); - goto error; - } - - *out = b; - return 0; - -error: - bus_close_connection(b); - return r; -} - -static void bus_close_connection(struct bus *b) -{ - if (!b) - return; - - /* - * A bus connection is closed by simply calling close() on the - * connection owner file descriptor. The unique name and all owned - * well-known names of the conneciton will disappear. - * See kdbus.connection(7). - */ - if (b->pool != MAP_FAILED) - munmap(b->pool, POOL_SIZE); - if (b->fd >= 0) - close(b->fd); - free(b); -} - -static void bus_poool_free_slice(struct bus *b, uint64_t offset) -{ - struct kdbus_cmd_free cmd = { - .size = sizeof(cmd), - .offset = offset, - }; - int r; - - /* - * Once we're done with a piece of pool memory that was returned - * by a command, we have to call the KDBUS_CMD_FREE ioctl on it so it - * can be reused. The command takes an argument of type - * 'struct kdbus_cmd_free', in which the pool offset of the slice to - * free is stored. The ioctl is employed on the connection owner - * file descriptor. See kdbus.pool(7), - */ - r = kdbus_cmd_free(b->fd, &cmd); - if (r < 0) - err_r(r, "cannot free pool slice"); -} - -static int bus_acquire_name(struct bus *b, const char *name) -{ - struct kdbus_item *item; - struct kdbus_cmd *cmd; - size_t size; - int r; - - /* - * This function acquires a well-known name on the bus through the - * KDBUS_CMD_NAME_ACQUIRE ioctl. This ioctl takes an argument of type - * 'struct kdbus_cmd', which is assembled below. See kdbus.name(7). - */ - size = sizeof(*cmd); - size += KDBUS_ITEM_SIZE(strlen(name) + 1); - - cmd = alloca(size); - memset(cmd, 0, size); - cmd->size = size; - - /* - * The command requires an item of type KDBUS_ITEM_NAME, and its - * content must be a valid bus name. - */ - item = cmd->items; - item->type = KDBUS_ITEM_NAME; - item->size = KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1; - strcpy(item->str, name); - - /* - * Employ the command on the connection owner file descriptor. - */ - r = kdbus_cmd_name_acquire(b->fd, cmd); - if (r < 0) - return err_r(r, "cannot acquire name"); - - return 0; -} - -static int bus_install_name_loss_match(struct bus *b, const char *name) -{ - struct kdbus_cmd_match *match; - struct kdbus_item *item; - size_t size; - int r; - - /* - * In order to install a match for signal messages, we have to - * assemble a 'struct kdbus_cmd_match' and use it along with the - * KDBUS_CMD_MATCH_ADD ioctl. See kdbus.match(7). - */ - size = sizeof(*match); - size += KDBUS_ITEM_SIZE(sizeof(item->name_change) + strlen(name) + 1); - - match = alloca(size); - memset(match, 0, size); - match->size = size; - - /* - * A match is comprised of many 'rules', each of which describes a - * mandatory detail of the message. All rules of a match must be - * satified in order to make a message pass. - */ - item = match->items; - - /* - * In this case, we're interested in notifications that inform us - * about a well-known name being removed from the bus. - */ - item->type = KDBUS_ITEM_NAME_REMOVE; - item->size = KDBUS_ITEM_HEADER_SIZE + - sizeof(item->name_change) + strlen(name) + 1; - - /* - * We could limit the match further and require a specific unique-ID - * to be the new or the old owner of the name. In this case, however, - * we don't, and allow 'any' id. - */ - item->name_change.old_id.id = KDBUS_MATCH_ID_ANY; - item->name_change.new_id.id = KDBUS_MATCH_ID_ANY; - - /* Copy in the well-known name we're interested in */ - strcpy(item->name_change.name, name); - - /* - * Add the match through the KDBUS_CMD_MATCH_ADD ioctl, employed on - * the connection owner fd. - */ - r = kdbus_cmd_match_add(b->fd, match); - if (r < 0) - return err_r(r, "cannot add match"); - - return 0; -} - -static int bus_poll(struct bus *b) -{ - struct pollfd fds[1] = {}; - int r; - - /* - * A connection endpoint supports poll() and will wake-up the - * task with POLLIN set once a message has arrived. - */ - fds[0].fd = b->fd; - fds[0].events = POLLIN; - r = poll(fds, sizeof(fds) / sizeof(*fds), 0); - if (r < 0) - return err("cannot poll bus"); - return !!(fds[0].revents & POLLIN); -} - -static int bus_make(uid_t uid, const char *name) -{ - struct kdbus_item *item; - struct kdbus_cmd *make; - char path[128], busname[128]; - size_t size; - int r, fd; - - /* - * Compute the full path to the 'control' node. 'arg_modname' may be - * set to a different value than 'kdbus' for development purposes. - * The 'control' node is the primary entry point to kdbus that must be - * used in order to create a bus. See kdbus(7) and kdbus.bus(7). - */ - snprintf(path, sizeof(path), "/sys/fs/%s/control", arg_modname); - - /* - * Compute the bus name. A valid bus name must always be prefixed with - * the EUID of the currently running process in order to avoid name - * conflicts. See kdbus.bus(7). - */ - snprintf(busname, sizeof(busname), "%lu-%s", (unsigned long)uid, name); - - fd = open(path, O_RDWR | O_CLOEXEC); - if (fd < 0) - return err("cannot open control file"); - - /* - * The KDBUS_CMD_BUS_MAKE ioctl takes an argument of type - * 'struct kdbus_cmd', and expects at least two items attached to - * it: one to decribe the bloom parameters to be propagated to - * connections of the bus, and the name of the bus that was computed - * above. Assemble this struct now, and fill it with values. - */ - size = sizeof(*make); - size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_parameter)); - size += KDBUS_ITEM_SIZE(strlen(busname) + 1); - - make = alloca(size); - memset(make, 0, size); - make->size = size; - - /* - * Each item has a 'type' and 'size' field, and must be stored at an - * 8-byte aligned address. The KDBUS_ITEM_NEXT macro is used to advance - * the pointer. See kdbus.item(7) for more details. - */ - item = make->items; - item->type = KDBUS_ITEM_BLOOM_PARAMETER; - item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(item->bloom_parameter); - item->bloom_parameter.size = 8; - item->bloom_parameter.n_hash = 1; - - /* The name of the new bus is stored in the next item. */ - item = KDBUS_ITEM_NEXT(item); - item->type = KDBUS_ITEM_MAKE_NAME; - item->size = KDBUS_ITEM_HEADER_SIZE + strlen(busname) + 1; - strcpy(item->str, busname); - - /* - * Now create the bus via the KDBUS_CMD_BUS_MAKE ioctl and return the - * fd that was used back to the caller of this function. This fd is now - * called a 'bus owner file descriptor', and it controls the life-time - * of the newly created bus; once the file descriptor is closed, the - * bus goes away, and all connections are shut down. See kdbus.bus(7). - */ - r = kdbus_cmd_bus_make(fd, make); - if (r < 0) { - err_r(r, "cannot make bus"); - close(fd); - return r; - } - - return fd; -} - -#else - -#warning "Skipping compilation due to unsupported libc version" - -int main(int argc, char **argv) -{ - fprintf(stderr, - "Compilation of %s was skipped due to unsupported libc.\n", - argv[0]); - - return EXIT_FAILURE; -} - -#endif /* libc sanity check */ |