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 --- discover/udev.c | 208 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 discover/udev.c (limited to 'discover/udev.c') diff --git a/discover/udev.c b/discover/udev.c new file mode 100644 index 0000000..9c1b399 --- /dev/null +++ b/discover/udev.c @@ -0,0 +1,208 @@ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "udev.h" +#include "log.h" +#include "waiter.h" +#include "pb-discover.h" + +#define PBOOT_DEVICE_SOCKET "/tmp/petitboot.udev" + +#define max(a, b) ((a) > (b) ? (a) : (b)) + +struct udev { + int socket; +}; + +static void parse_event_params(struct udev_event *event, char *buf, int len) +{ + int param_len, name_len, value_len; + struct param *param; + char *sep; + + for (; len > 0; len -= param_len + 1, buf += param_len + 1) { + + /* find the length of the whole parameter */ + param_len = strnlen(buf, len); + if (!param_len) { + /* multiple NULs? skip over */ + param_len = 1; + continue; + } + + /* find the separator */ + sep = memchr(buf, '=', param_len); + if (!sep) + continue; + + name_len = sep - buf; + value_len = param_len - name_len - 1; + + /* update the params array */ + event->params = talloc_realloc(event, event->params, + struct param, ++event->n_params); + param = &event->params[event->n_params - 1]; + + param->name = talloc_strndup(event, buf, name_len); + param->value = talloc_strndup(event, sep + 1, value_len); + } +} + +static const char *event_param(struct udev_event *event, const char *name) +{ + int i; + + for (i = 0; i < event->n_params; i++) + if (!strcasecmp(event->params[i].name, name)) + return event->params[i].value; + + return NULL; +} + +static void print_event(struct udev_event *event) +{ + const char *action, *params[] = { + "DEVNAME", "ID_TYPE", "ID_BUS", "ID_FS_UUID", "ID_FS_LABEL", + NULL, + }; + int i; + + action = event->action == UDEV_ACTION_ADD ? "add" : "remove"; + + pb_log("udev %s event:\n", action); + printf("\tdevice: %s\n", event->device); + + for (i = 0; params[i]; i++) + printf("\t%-12s => %s\n", + params[i], event_param(event, params[i])); + +} + +static void handle_udev_message(struct udev *udev, char *buf, int len) +{ + char *sep, *device; + enum udev_action action; + struct udev_event *event; + int device_len; + + /* we should see an @\0 at the head of the buffer */ + sep = strchr(buf, '@'); + if (!sep) + return; + + /* terminate the action string */ + *sep = '\0'; + len -= sep - buf + 1; + + if (!strcmp(buf, "add")) { + action = UDEV_ACTION_ADD; + + } else if (!strcmp(buf, "remove")) { + action = UDEV_ACTION_REMOVE; + + } else { + return; + } + + /* initialise the device string */ + device = sep + 1; + device_len = strnlen(device, len); + if (!device_len) + return; + + /* now we have an action and a device, we can construct an event */ + event = talloc(udev, struct udev_event); + event->action = action; + event->device = talloc_strndup(event, device, device_len); + event->n_params = 0; + event->params = NULL; + + len -= device_len + 1; + parse_event_params(event, device + device_len + 1, len); + + print_event(event); + + talloc_free(event); + + return; +} + +static int udev_process(void *arg) +{ + struct udev *udev = arg; + char buf[4096]; + int len; + + len = recvfrom(udev->socket, buf, sizeof(buf), 0, NULL, NULL); + + if (len < 0) { + pb_log("udev socket read failed: %s", strerror(errno)); + return -1; + } + + if (len == 0) + return 0; + + handle_udev_message(udev, buf, len); + + return 0; +} + +static int udev_destructor(void *p) +{ + struct udev *udev = p; + + if (udev->socket >= 0) + close(udev->socket); + + return 0; +} + +struct udev *udev_init(void) +{ + struct sockaddr_un addr; + struct udev *udev; + + unlink(PBOOT_DEVICE_SOCKET); + + udev = talloc(NULL, struct udev); + + udev->socket = socket(PF_UNIX, SOCK_DGRAM, 0); + if (udev->socket < 0) { + pb_log("Error creating udev socket: %s\n", strerror(errno)); + goto out_err; + } + + talloc_set_destructor(udev, udev_destructor); + + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, PBOOT_DEVICE_SOCKET); + + if (bind(udev->socket, (struct sockaddr *)&addr, sizeof(addr))) { + pb_log("Error binding udev socket: %s\n", strerror(errno)); + goto out_err; + } + + waiter_register(udev->socket, WAIT_IN, udev_process, udev); + + return udev; + +out_err: + talloc_free(udev); + return NULL; +} + +void udev_destroy(struct udev *udev) +{ + talloc_free(udev); +} -- cgit v1.2.1