From 32e6a41f33e5576716b351bd473a27939fe94fa1 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Mon, 15 Dec 2008 15:22:34 +1100 Subject: Initial support for multiple UIs Move the device discovery code from separate udev helpers to a single process to listen on two sockets: one SOCK_DGRAM for incoming udev events, and one SOCK_STREAM for UIs to connect. Initial support for client/server infrastructure, still need to wire-up the udev messages. Signed-off-by: Jeremy Kerr --- lib/pb-protocol/pb-protocol.c | 307 ++++++++++++++++++++++++++++++++++++++++++ lib/pb-protocol/pb-protocol.h | 57 ++++++++ 2 files changed, 364 insertions(+) create mode 100644 lib/pb-protocol/pb-protocol.c create mode 100644 lib/pb-protocol/pb-protocol.h (limited to 'lib/pb-protocol') diff --git a/lib/pb-protocol/pb-protocol.c b/lib/pb-protocol/pb-protocol.c new file mode 100644 index 0000000..2fd76c5 --- /dev/null +++ b/lib/pb-protocol/pb-protocol.c @@ -0,0 +1,307 @@ + +#include +#include +#include + +#include + +#include "pb-protocol.h" + + +/* Message format: + * + * 4-byte action, determines the remaining message content + * 4-byte total payload len + * - not including action and payload len header + * + * action = 0x1: device add message + * payload: + * 4-byte len, id + * 4-byte len, name + * 4-byte len, description + * 4-byte len, icon_file + * + * 4-byte option count + * for each option: + * 4-byte len, id + * 4-byte len, name + * 4-byte len, description + * 4-byte len, icon_file + * 4-byte len, boot_image_file + * 4-byte len, initrd_file + * 4-byte len, boot_args + * + * action = 0x2: device remove message + * payload: + * 4-byte len, id + */ + + +/* Write a string into the buffer, starting at pos. + * + * Returns the total length used for the write, including length header. + */ +int pb_protocol_serialise_string(char *pos, const char *str) +{ + int len = 0; + + if (str) + len = strlen(str); + + *(uint32_t *)pos = __cpu_to_be32(len); + pos += sizeof(uint32_t); + + memcpy(pos, str, len); + + return len + sizeof(uint32_t); +} + +/* Read a string from a buffer, allocating the new string as necessary. + * + * @param[in] ctx The talloc context to base the allocation on + * @param[in,out] pos Where to start reading + * @param[in,out] len The amount of data remaining in the buffer + * @param[out] str Pointer to resuling string + * @return zero on success, non-zero on failure + */ +static int read_string(void *ctx, char **pos, int *len, char **str) +{ + uint32_t str_len, read_len; + + if (*len < sizeof(uint32_t)) + return -1; + + str_len = __be32_to_cpu(*(uint32_t *)(*pos)); + read_len = sizeof(uint32_t); + + if (read_len + str_len > *len) + return -1; + + if (str_len == 0) + *str = NULL; + else + *str = talloc_strndup(ctx, *pos + read_len, str_len); + + read_len += str_len; + + /* all ok, update the caller's pointers */ + *pos += read_len; + *len -= read_len; + + return 0; +} + +char *pb_protocol_deserialise_string(void *ctx, + struct pb_protocol_message *message) +{ + char *buf, *str; + int len; + + len = message->payload_len; + buf = message->payload; + + if (read_string(ctx, &buf, &len, &str)) + return NULL; + + return str; +} + +static int optional_strlen(const char *str) +{ + if (!str) + return 0; + return strlen(str); +} + +int pb_protocol_device_len(struct device *dev) +{ + int len, i; + + len = 4 + optional_strlen(dev->id) + + 4 + optional_strlen(dev->name) + + 4 + optional_strlen(dev->description) + + 4 + optional_strlen(dev->icon_file) + + 4; + + for (i = 0; i < dev->n_options; i++) { + struct boot_option *opt = &dev->options[i]; + len += 4 + optional_strlen(opt->id) + + 4 + optional_strlen(opt->name) + + 4 + optional_strlen(opt->description) + + 4 + optional_strlen(opt->icon_file) + + 4 + optional_strlen(opt->boot_image_file) + + 4 + optional_strlen(opt->initrd_file) + + 4 + optional_strlen(opt->boot_args); + } + + return len; +} + +int pb_protocol_serialise_device(struct device *dev, char *buf, int buf_len) +{ + char *pos; + int i; + + pos = buf; + + /* construct payload into buffer */ + pos += pb_protocol_serialise_string(pos, dev->id); + pos += pb_protocol_serialise_string(pos, dev->name); + pos += pb_protocol_serialise_string(pos, dev->description); + pos += pb_protocol_serialise_string(pos, dev->icon_file); + + /* write option count */ + *(uint32_t *)pos = __cpu_to_be32(dev->n_options); + pos += sizeof(uint32_t); + + /* write each option */ + for (i = 0; i < dev->n_options; i++) { + struct boot_option *opt = &dev->options[i]; + pos += pb_protocol_serialise_string(pos, opt->id); + pos += pb_protocol_serialise_string(pos, opt->name); + pos += pb_protocol_serialise_string(pos, opt->description); + pos += pb_protocol_serialise_string(pos, opt->icon_file); + pos += pb_protocol_serialise_string(pos, opt->boot_image_file); + pos += pb_protocol_serialise_string(pos, opt->initrd_file); + pos += pb_protocol_serialise_string(pos, opt->boot_args); + } + + return 0; +} + +int pb_protocol_write_message(int fd, struct pb_protocol_message *message) +{ + int total_len, rc; + char *pos; + + total_len = sizeof(*message) + message->payload_len; + + message->payload_len = __cpu_to_be32(message->payload_len); + message->action = __cpu_to_be32(message->action); + + for (pos = (void *)message; total_len;) { + rc = write(fd, pos, total_len); + + if (rc <= 0) + break; + + total_len -= rc; + pos += rc; + } + + talloc_free(message); + + return total_len ? -1 : 0; +} + +struct pb_protocol_message *pb_protocol_create_message(void *ctx, + int action, int payload_len) +{ + struct pb_protocol_message *message; + + if (payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE) + return NULL; + + message = talloc_size(ctx, sizeof(*message) + payload_len); + + /* we convert these to big-endian in write_message() */ + message->action = action; + message->payload_len = payload_len; + + return message; + +} + +struct pb_protocol_message *pb_protocol_read_message(void *ctx, int fd) +{ + struct pb_protocol_message *message, m; + int rc, len; + + /* use the stack for the initial 8-byte read */ + + rc = read(fd, &m, sizeof(m)); + if (rc != sizeof(m)) + return NULL; + + m.payload_len = __be32_to_cpu(m.payload_len); + m.action = __be32_to_cpu(m.action); + + if (m.payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE) + return NULL; + + message = talloc_size(ctx, sizeof(m) + m.payload_len); + memcpy(message, &m, sizeof(m)); + + for (len = 0; len < m.payload_len;) { + rc = read(fd, message->payload + len, m.payload_len - len); + + if (rc <= 0) { + talloc_free(message); + return NULL; + } + + len += rc; + } + + return message; +} + + +struct device *pb_protocol_deserialise_device(void *ctx, + struct pb_protocol_message *message) +{ + struct device *dev; + char *pos; + int i, len; + + len = message->payload_len; + pos = message->payload; + + dev = talloc(ctx, struct device); + + if (read_string(dev, &pos, &len, &dev->id)) + goto out_err; + + if (read_string(dev, &pos, &len, &dev->name)) + goto out_err; + + if (read_string(dev, &pos, &len, &dev->description)) + goto out_err; + + if (read_string(dev, &pos, &len, &dev->icon_file)) + goto out_err; + + dev->n_options = __be32_to_cpu(*(uint32_t *)pos); + dev->options = talloc_array(dev, struct boot_option, dev->n_options); + pos += sizeof(uint32_t); + + for (i = 0; i < dev->n_options; i++) { + struct boot_option *opt = &dev->options[i]; + + if (read_string(opt, &pos, &len, &opt->id)) + goto out_err; + if (read_string(opt, &pos, &len, &opt->name)) + goto out_err; + if (read_string(opt, &pos, &len, + &opt->description)) + goto out_err; + if (read_string(opt, &pos, &len, + &opt->icon_file)) + goto out_err; + if (read_string(opt, &pos, &len, + &opt->boot_image_file)) + goto out_err; + if (read_string(opt, &pos, &len, + &opt->initrd_file)) + goto out_err; + if (read_string(opt, &pos, &len, + &opt->boot_args)) + goto out_err; + } + + return dev; + +out_err: + talloc_free(dev); + return NULL; +} diff --git a/lib/pb-protocol/pb-protocol.h b/lib/pb-protocol/pb-protocol.h new file mode 100644 index 0000000..7b557d6 --- /dev/null +++ b/lib/pb-protocol/pb-protocol.h @@ -0,0 +1,57 @@ +#ifndef _PB_PROTOCOL_H +#define _PB_PROTOCOL_H + +#include + +#define PB_SOCKET_PATH "/tmp/petitboot.ui" + +#define PB_PROTOCOL_MAX_PAYLOAD_SIZE 4096 + +enum pb_protocol_action { + PB_PROTOCOL_ACTION_ADD = 0x1, + PB_PROTOCOL_ACTION_REMOVE = 0x2, +}; + +struct pb_protocol_message { + uint32_t action; + uint32_t payload_len; + char payload[]; +}; + +struct device { + char *id; + char *name; + char *description; + char *icon_file; + + struct boot_option { + char *id; + char *name; + char *description; + char *icon_file; + char *boot_image_file; + char *initrd_file; + char *boot_args; + } *options; + int n_options; +}; + +int pb_protocol_device_len(struct device *dev); + +int pb_protocol_serialise_string(char *pos, const char *str); +char *pb_protocol_deserialise_string(void *ctx, + struct pb_protocol_message *message); + +int pb_protocol_serialise_device(struct device *dev, char *buf, int buf_len); + +int pb_protocol_write_message(int fd, struct pb_protocol_message *message); + +struct pb_protocol_message *pb_protocol_create_message(void *ctx, + int action, int payload_len); + +struct pb_protocol_message *pb_protocol_read_message(void *ctx, int fd); + +struct device *pb_protocol_deserialise_device(void *ctx, + struct pb_protocol_message *message); + +#endif /* _PB_PROTOCOL_H */ -- cgit v1.2.1