#define _GNU_SOURCE
-#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <libudev.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
-#include <errno.h>
-#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/types.h>
#include <sys/un.h>
+#include <log/log.h>
#include <talloc/talloc.h>
#include <waiter/waiter.h>
+#include <system/system.h>
+#include "event.h"
#include "udev.h"
-#include "log.h"
#include "pb-discover.h"
#include "device-handler.h"
-#define PBOOT_DEVICE_SOCKET "/tmp/petitboot.udev"
+#if defined(DEBUG)
+#define DBG(fmt, args...) pb_log("DBG: " fmt, ## args)
+#define DBGS(fmt, args...) \
+ pb_log("DBG:%s:%d: " fmt, __func__, __LINE__, ## args)
+#else
+#define DBG(fmt, args...)
+#define DBGS(fmt, args...)
+#endif
-#define max(a, b) ((a) > (b) ? (a) : (b))
-
-struct udev {
+struct pb_udev {
+ struct udev *udev;
+ struct udev_monitor *monitor;
struct device_handler *handler;
- int socket;
};
-static void parse_event_params(struct udev_event *event, char *buf, int len)
+static int udev_destructor(void *p)
{
- 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;
+ struct pb_udev *udev = p;
- name_len = sep - buf;
- value_len = param_len - name_len - 1;
+ udev_monitor_unref(udev->monitor);
+ udev->monitor = NULL;
- /* update the params array */
- event->params = talloc_realloc(event, event->params,
- struct param, ++event->n_params);
- param = &event->params[event->n_params - 1];
+ udev_unref(udev->udev);
+ udev->udev = NULL;
- param->name = talloc_strndup(event, buf, name_len);
- param->value = talloc_strndup(event, sep + 1, value_len);
- }
+ return 0;
}
-const char *udev_event_param(struct udev_event *event, const char *name)
+static void print_device_properties(struct udev_device *dev)
{
- int i;
+ struct udev_list_entry *list, *entry;
- for (i = 0; i < event->n_params; i++)
- if (!strcasecmp(event->params[i].name, name))
- return event->params[i].value;
+ assert(dev);
- return NULL;
+ if (1) {
+ list = udev_device_get_properties_list_entry(dev);
+
+ assert(list);
+
+ udev_list_entry_foreach(entry, list)
+ DBG("property: %s - %s\n",
+ udev_list_entry_get_name(entry),
+ udev_device_get_property_value(dev,
+ udev_list_entry_get_name(entry)));
+ }
}
-static void print_event(struct udev_event *event)
+static int udev_handle_dev_action(struct udev_device *dev, const char *action)
{
- const char *action, *params[] = {
- "DEVNAME", "ID_TYPE", "ID_BUS", "ID_FS_UUID", "ID_FS_LABEL",
- NULL,
- };
- int i;
+ const char *devtype;
+ const char *devpath;
+ const char *devnode;
+ struct pb_udev *udev;
+ struct event *event;
+ enum event_action eva = 0;
- action = event->action == UDEV_ACTION_ADD ? "add" : "remove";
+ assert(dev);
+ assert(action);
- pb_log("udev %s event:\n", action);
- printf("\tdevice: %s\n", event->device);
+ devtype = udev_device_get_devtype(dev); /* DEVTYPE */
- for (i = 0; params[i]; i++)
- printf("\t%-12s => %s\n",
- params[i], udev_event_param(event, params[i]));
+ if (!devtype) {
+ pb_log("udev_device_get_devtype failed\n");
+ return -1;
+ }
-}
+ devpath = udev_device_get_devpath(dev); /* DEVPATH */
-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;
+ if (!devpath) {
+ pb_log("udev_device_get_devpath failed\n");
+ return -1;
+ }
+
+ devnode = udev_device_get_devnode(dev); /* DEVNAME */
- /* we should see an <action>@<device>\0 at the head of the buffer */
- sep = strchr(buf, '@');
- if (!sep)
- return;
+ if (!devnode) {
+ pb_log("udev_device_get_devnode failed\n");
+ return -1;
+ }
- /* terminate the action string */
- *sep = '\0';
- len -= sep - buf + 1;
+ print_device_properties(dev);
- if (!strcmp(buf, "add")) {
- action = UDEV_ACTION_ADD;
+ /* Ignore non disk or partition, ram, loop. */
- } else if (!strcmp(buf, "remove")) {
- action = UDEV_ACTION_REMOVE;
+ if (!(strstr(devtype, "disk") || strstr(devtype, "partition"))
+ || strstr(devpath, "virtual/block/loop")
+ || strstr(devpath, "virtual/block/ram")) {
+ pb_log("SKIP: %s - %s\n", devtype, devnode);
+ return 0;
+ }
+ if (!strcmp(action, "add")) {
+ pb_log("ADD: %s - %s\n", devtype, devnode);
+ eva = EVENT_ACTION_ADD;
+ } else if (!strcmp(action, "remove")) {
+ pb_log("REMOVE: %s - %s\n", devtype, devnode);
+ eva = EVENT_ACTION_REMOVE;
} else {
- return;
+ pb_log("SKIP: %s: %s - %s\n", action, devtype, devnode);
+ return 0;
}
- /* initialise the device string */
- device = sep + 1;
- device_len = strnlen(device, len);
- if (!device_len)
- return;
+ event = talloc(NULL, struct event);
- /* 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;
+ event->type = EVENT_TYPE_UDEV;
+ event->action = eva;
+ event->device = devpath;
- len -= device_len + 1;
- parse_event_params(event, device + device_len + 1, len);
+ event->n_params = 1;
+ event->params = talloc(event, struct param);
+ event->params->name = "DEVNAME";
+ event->params->value = devnode;
- print_event(event);
+ udev = udev_get_userdata(udev_device_get_udev(dev));
+ assert(udev);
device_handler_event(udev->handler, event);
talloc_free(event);
-
- return;
+ return 0;
}
-static int udev_process(void *arg)
+static int udev_enumerate(struct udev *udev)
{
- struct udev *udev = arg;
- char buf[4096];
- int len;
+ int result;
+ struct udev_list_entry *list, *entry;
+ struct udev_enumerate *enumerate;
- len = recvfrom(udev->socket, buf, sizeof(buf), 0, NULL, NULL);
+ enumerate = udev_enumerate_new(udev);
- if (len < 0) {
- pb_log("udev socket read failed: %s", strerror(errno));
+ if (!enumerate) {
+ pb_log("udev_enumerate_new failed\n");
return -1;
}
- if (len == 0)
- return 0;
+ result = udev_enumerate_add_match_subsystem(enumerate, "block");
- handle_udev_message(udev, buf, len);
+ if (result) {
+ pb_log("udev_enumerate_add_match_subsystem failed\n");
+ goto fail;
+ }
+
+ udev_enumerate_scan_devices(enumerate);
+
+ list = udev_enumerate_get_list_entry(enumerate);
+
+ if (!list) {
+ pb_log("udev_enumerate_get_list_entry failed\n");
+ goto fail;
+ }
+
+ udev_list_entry_foreach(entry, list) {
+ const char *syspath;
+ struct udev_device *dev;
+ syspath = udev_list_entry_get_name(entry);
+ dev = udev_device_new_from_syspath(udev, syspath);
+
+ udev_handle_dev_action(dev, "add");
+
+ udev_device_unref(dev);
+ }
+
+ udev_enumerate_unref(enumerate);
return 0;
+
+fail:
+ udev_enumerate_unref(enumerate);
+ return -1;
}
-static int udev_destructor(void *p)
+static int udev_setup_monitor(struct udev *udev, struct udev_monitor **monitor)
{
- struct udev *udev = p;
+ int result;
+ struct udev_monitor *m;
+
+ *monitor = NULL;
+ m = udev_monitor_new_from_netlink(udev, "udev");
+
+ if (!m) {
+ pb_log("udev_monitor_new_from_netlink failed\n");
+ goto out_err;
+ }
+
+ result = udev_monitor_filter_add_match_subsystem_devtype(m, "block",
+ NULL);
- if (udev->socket >= 0)
- close(udev->socket);
+ if (result) {
+ pb_log("udev_monitor_filter_add_match_subsystem_devtype failed\n");
+ goto out_err;
+ }
+
+ result = udev_monitor_enable_receiving(m);
+
+ if (result) {
+ pb_log("udev_monitor_enable_receiving failed\n");
+ goto out_err;
+ }
+ *monitor = m;
return 0;
+
+out_err:
+ udev_monitor_unref(m);
+ return -1;
}
-struct udev *udev_init(struct device_handler *handler)
+/*
+ * udev_process - waiter callback for monitor netlink.
+ */
+
+static int udev_process(void *arg)
{
- struct sockaddr_un addr;
- struct udev *udev;
+ struct udev_monitor *monitor = arg;
+ struct udev_device *dev;
+ const char *action;
+ int result;
- unlink(PBOOT_DEVICE_SOCKET);
+ dev = udev_monitor_receive_device(monitor);
- udev = talloc(NULL, struct udev);
+ if (!dev) {
+ pb_log("udev_monitor_receive_device failed\n");
+ return -1;
+ }
- udev->handler = handler;
+ action = udev_device_get_action(dev);
- 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;
+ if (!action) {
+ pb_log("udev_device_get_action failed\n");
+ goto fail;
}
+ result = udev_handle_dev_action(dev, action);
+
+ udev_device_unref(dev);
+ return result;
+
+fail:
+ udev_device_unref(dev);
+ return -1;
+}
+
+static void udev_log_fn(struct udev __attribute__((unused)) *udev,
+ int __attribute__((unused)) priority, const char *file, int line,
+ const char *fn, const char *format, va_list args)
+{
+ pb_log("libudev: %s %s:%d: ", fn, file, line);
+ vfprintf(pb_log_get_stream(), format, args);
+}
+
+struct pb_udev *udev_init(struct waitset *waitset,
+ struct device_handler *handler)
+{
+ int result;
+ struct pb_udev *udev = talloc(NULL, struct pb_udev);
+
talloc_set_destructor(udev, udev_destructor);
+ udev->handler = handler;
- addr.sun_family = AF_UNIX;
- strcpy(addr.sun_path, PBOOT_DEVICE_SOCKET);
+ udev->udev = udev_new();
- if (bind(udev->socket, (struct sockaddr *)&addr, sizeof(addr))) {
- pb_log("Error binding udev socket: %s\n", strerror(errno));
- goto out_err;
+ if (!udev->udev) {
+ pb_log("udev_new failed\n");
+ goto fail_new;
}
- waiter_register(udev->socket, WAIT_IN, udev_process, udev);
+ udev_set_userdata(udev->udev, udev);
+
+ udev_set_log_fn(udev->udev, udev_log_fn);
+
+ result = udev_enumerate(udev->udev);
+
+ if (result)
+ goto fail_enumerate;
+
+ result = udev_setup_monitor(udev->udev, &udev->monitor);
+
+ if (result)
+ goto fail_monitor;
+
+ waiter_register(waitset, udev_monitor_get_fd(udev->monitor), WAIT_IN,
+ udev_process, udev->monitor);
+
+ pb_log("%s: waiting on %s\n", __func__, udev_get_sys_path(udev->udev));
return udev;
-out_err:
+fail_monitor:
+fail_enumerate:
+ udev_unref(udev->udev);
+fail_new:
talloc_free(udev);
return NULL;
}
-void udev_destroy(struct udev *udev)
+int udev_trigger(struct pb_udev __attribute__((unused)) *udev)
+{
+ return 0;
+}
+
+void udev_destroy(struct pb_udev *udev)
{
talloc_free(udev);
}