Initial device handler code
authorJeremy Kerr <jk@ozlabs.org>
Tue, 16 Dec 2008 04:33:59 +0000 (15:33 +1100)
committerJeremy Kerr <jk@ozlabs.org>
Tue, 16 Dec 2008 04:33:59 +0000 (15:33 +1100)
Mount discovered devices, and set up symlinks for UUID and LABELs

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Makefile.in
discover/device-handler.c
discover/paths.c
discover/paths.h
discover/udev.c
discover/udev.h
rules.mk

index e537e9db7251ac54e361348d9c746481cfe482b8..47adfd6a7fd50fb7596161ddfe4db6e8a1dbf817 100644 (file)
@@ -24,6 +24,7 @@ sbindir = @sbindir@
 datarootdir = @datarootdir@
 datadir = @datadir@
 pkgdatadir = ${datadir}/${PACKAGE}
+localstatedir = @localstatedir@
 builddir = @builddir@
 srcdir = @srcdir@
 top_srcdir = @top_srcdir@
index 0e902df6b86ed4c1805868617cf7db85abb08bf7..5d40ebe99642711e6f17d9f4b2d113e94f7bfc4c 100644 (file)
@@ -1,8 +1,20 @@
 
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
 #include <talloc/talloc.h>
 #include <pb-protocol/pb-protocol.h>
 
 #include "device-handler.h"
+#include "udev.h"
+#include "log.h"
+#include "paths.h"
+
+#define MOUNT_BIN "/bin/mount"
 
 struct device_handler {
        struct discover_server *server;
@@ -11,21 +23,20 @@ struct device_handler {
        int n_devices;
 };
 
-struct device_handler *device_handler_init(struct discover_server *server)
-{
-       struct device_handler *handler;
-
-       handler = talloc(NULL, struct device_handler);
-       handler->devices = NULL;
-       handler->n_devices = 0;
+struct discover_context {
+       char *device_path;
+       char *mount_path;
+       struct udev_event *event;
+       struct device *device;
+       char **links;
+       int n_links;
+};
 
-       return handler;
-}
+struct mount_map {
+       char *device_path;
+       char *mount_point;
+};
 
-void device_handler_destroy(struct device_handler *devices)
-{
-       talloc_free(devices);
-}
 
 static struct boot_option options[] = {
        {
@@ -54,9 +65,228 @@ int device_handler_get_current_devices(struct device_handler *handler,
        return 1;
 }
 
+static int mkdir_recursive(const char *dir)
+{
+       struct stat statbuf;
+       char *str, *sep;
+       int mode = 0755;
 
-int device_handler_event(struct device_handler *handler,
+       if (!*dir)
+               return 0;
+
+       if (!stat(dir, &statbuf)) {
+               if (!S_ISDIR(statbuf.st_mode)) {
+                       pb_log("%s: %s exists, but isn't a directory\n",
+                                       __func__, dir);
+                       return -1;
+               }
+               return 0;
+       }
+
+       str = talloc_strdup(NULL, dir);
+       sep = strchr(*str == '/' ? str + 1 : str, '/');
+
+       while (1) {
+
+               /* terminate the path at sep */
+               if (sep)
+                       *sep = '\0';
+
+               if (mkdir(str, mode) && errno != EEXIST) {
+                       pb_log("mkdir(%s): %s\n", str, strerror(errno));
+                       return -1;
+               }
+
+               if (!sep)
+                       break;
+
+               /* reset dir to the full path */
+               strcpy(str, dir);
+               sep = strchr(sep + 1, '/');
+       }
+
+       talloc_free(str);
+
+       return 0;
+}
+
+static void setup_device_links(struct discover_context *ctx)
+{
+       struct link {
+               char *env, *dir;
+       } *link, links[] = {
+               {
+                       .env = "ID_FS_UUID",
+                       .dir = "disk/by-uuid"
+               },
+               {
+                       .env = "ID_FS_LABEL",
+                       .dir = "disk/by-label"
+               },
+               {
+                       .env = NULL
+               }
+       };
+
+       for (link = links; link->env; link++) {
+               char *enc, *dir, *path;
+               const char *value;
+
+               value = udev_event_param(ctx->event, link->env);
+               if (!value)
+                       continue;
+
+               enc = encode_label(ctx, value);
+               dir = join_paths(ctx, mount_base(), link->dir);
+               path = join_paths(ctx, dir, value);
+
+               if (!mkdir_recursive(dir)) {
+                       unlink(path);
+                       if (symlink(ctx->mount_path, path)) {
+                               pb_log("symlink(%s,%s): %s\n",
+                                               ctx->mount_path, path,
+                                               strerror(errno));
+                               talloc_free(path);
+                       } else {
+                               int i = ctx->n_links++;
+                               ctx->links = talloc_realloc(ctx,
+                                               ctx->links, char *,
+                                               ctx->n_links);
+                               ctx->links[i] = path;
+                       }
+
+               }
+
+               talloc_free(dir);
+               talloc_free(enc);
+       }
+}
+
+static int mount_device(struct discover_context *ctx)
+{
+       const char *mountpoint;
+       struct stat statbuf;
+       int status;
+       pid_t pid;
+
+       if (!ctx->mount_path) {
+               mountpoint = mountpoint_for_device(ctx->device_path);
+               ctx->mount_path = talloc_strdup(ctx, mountpoint);
+       }
+
+       if (stat(ctx->mount_path, &statbuf)) {
+               if (mkdir(ctx->mount_path, 0755)) {
+                       pb_log("couldn't create mount directory %s: %s\n",
+                                       ctx->mount_path, strerror(errno));
+                       return -1;
+               }
+       } else {
+               if (!S_ISDIR(statbuf.st_mode)) {
+                       pb_log("mountpoint %s exists, but isn't a directory\n",
+                                       ctx->mount_path);
+                       return -1;
+               }
+       }
+
+       pid = fork();
+       if (pid == -1) {
+               pb_log("%s: fork failed: %s\n", __func__, strerror(errno));
+               return -1;
+       }
+
+       if (pid == 0) {
+               execl(MOUNT_BIN, MOUNT_BIN, ctx->device_path, ctx->mount_path,
+                               "-o", "ro", NULL);
+               exit(EXIT_FAILURE);
+       }
+
+       if (waitpid(pid, &status, 0) == -1) {
+               pb_log("%s: waitpid failed: %s\n", __func__,
+                               strerror(errno));
+               return -1;
+       }
+
+       if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+               return -1;
+
+       setup_device_links(ctx);
+       return 0;
+}
+
+static int handle_add_event(struct device_handler *handler,
+               struct udev_event *event)
+{
+       struct discover_context *ctx;
+       const char *devname;
+       int rc;
+
+       /* create our context */
+       ctx = talloc(NULL, struct discover_context);
+       ctx->event = event;
+       ctx->mount_path = NULL;
+       ctx->links = NULL;
+       ctx->n_links = 0;
+
+       devname = udev_event_param(ctx->event, "DEVNAME");
+       if (!devname) {
+               pb_log("no devname for %s?\n", event->device);
+               return 0;
+       }
+
+       ctx->device_path = talloc_strdup(ctx, devname);
+
+       rc = mount_device(ctx);
+       if (rc) {
+               pb_log("mount_device failed for %s\n", event->device);
+               talloc_free(ctx);
+               return 0;
+       }
+
+       talloc_free(ctx);
+
+       return 0;
+}
+
+static int handle_remove_event(struct device_handler *handler,
                struct udev_event *event)
 {
        return 0;
 }
+
+int device_handler_event(struct device_handler *handler,
+               struct udev_event *event)
+{
+       int rc;
+
+       switch (event->action) {
+       case UDEV_ACTION_ADD:
+               rc = handle_add_event(handler, event);
+               break;
+
+       case UDEV_ACTION_REMOVE:
+               rc = handle_remove_event(handler, event);
+               break;
+       }
+
+       return rc;
+}
+
+struct device_handler *device_handler_init(struct discover_server *server)
+{
+       struct device_handler *handler;
+
+       handler = talloc(NULL, struct device_handler);
+       handler->devices = NULL;
+       handler->n_devices = 0;
+
+       /* set up our mount point base */
+       mkdir_recursive(mount_base());
+
+       return handler;
+}
+
+void device_handler_destroy(struct device_handler *devices)
+{
+       talloc_free(devices);
+}
+
index 2373c28f560555025d7b522733551b5cb3dffe22..72d07b20c4cee09b354510558e4558e405e4b828 100644 (file)
@@ -4,24 +4,41 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <talloc/talloc.h>
+
 #include "paths.h"
 
-static char *mount_base;
+#define DEVICE_MOUNT_BASE (LOCAL_STATE_DIR "/petitboot/mnt")
 
-struct device_map {
+struct mount_map {
        char *dev, *mnt;
 };
 
-#define DEVICE_MAP_SIZE 32
-static struct device_map device_map[DEVICE_MAP_SIZE];
+static struct mount_map *mount_map;
+static int mount_map_size;
+
+static int is_prefix(const char *str, const char *prefix)
+{
+       return !strncmp(str, prefix, strlen(prefix));
+}
+
+static int is_prefix_ignorecase(const char *str, const char *prefix)
+{
+       return !strncasecmp(str, prefix, strlen(prefix));
+}
 
-char *encode_label(const char *label)
+const char *mount_base(void)
+{
+       return DEVICE_MOUNT_BASE;
+}
+
+char *encode_label(void *alloc_ctx, const char *label)
 {
        char *str, *c;
        int i;
 
        /* the label can be expanded by up to four times */
-       str = malloc(strlen(label) * 4 + 1);
+       str = talloc_size(alloc_ctx, strlen(label) * 4 + 1);
        c = str;
 
        for (i = 0; i < strlen(label); i++) {
@@ -40,60 +57,64 @@ char *encode_label(const char *label)
        return str;
 }
 
-char *parse_device_path(const char *dev_str, const char *cur_dev)
+char *parse_device_path(void *alloc_ctx,
+               const char *dev_str, const char *cur_dev)
 {
        char *dev, tmp[256], *enc;
 
-       if (!strncasecmp(dev_str, "uuid=", 5)) {
-               asprintf(&dev, "/dev/disk/by-uuid/%s", dev_str + 5);
+       if (is_prefix_ignorecase(dev_str, "uuid=")) {
+               dev = talloc_asprintf(alloc_ctx, "/dev/disk/by-uuid/%s",
+                               dev_str + strlen("uuid="));
                return dev;
        }
 
-       if (!strncasecmp(dev_str, "label=", 6)) {
-               enc = encode_label(dev_str + 6);
-               asprintf(&dev, "/dev/disk/by-label/%s", enc);
-               free(enc);
+       if (is_prefix_ignorecase(dev_str, "label=")) {
+               enc = encode_label(NULL, dev_str + strlen("label="));
+               dev = talloc_asprintf(alloc_ctx, "/dev/disk/by-label/%s", enc);
+               talloc_free(enc);
                return dev;
        }
 
        /* normalise '/dev/foo' to 'foo' for easy comparisons, we'll expand
         * back before returning.
         */
-       if (!strncmp(dev_str, "/dev/", 5))
-               dev_str += 5;
+       if (is_prefix(dev_str, "/dev/"))
+               dev_str += strlen("/dev/");
 
        /* PS3 hack: if we're reading from a ps3dx device, and we refer to
         * a sdx device, remap to ps3dx */
-       if (cur_dev && !strncmp(cur_dev, "/dev/ps3d", 9)
-                       && !strncmp(dev_str, "sd", 2)) {
+       if (cur_dev && is_prefix(cur_dev, "/dev/ps3d")
+                       && is_prefix(dev_str, "sd")) {
                snprintf(tmp, 255, "ps3d%s", dev_str + 2);
                dev_str = tmp;
        }
 
-       return join_paths("/dev", dev_str);
+       return join_paths(alloc_ctx, "/dev", dev_str);
 }
 
 const char *mountpoint_for_device(const char *dev)
 {
        int i;
 
-       if (!strncmp(dev, "/dev/", 5))
-               dev += 5;
+       if (is_prefix(dev, "/dev/"))
+               dev += strlen("/dev/");
 
        /* check existing entries in the map */
-       for (i = 0; (i < DEVICE_MAP_SIZE) && device_map[i].dev; i++)
-               if (!strcmp(device_map[i].dev, dev))
-                       return device_map[i].mnt;
-
-       if (i == DEVICE_MAP_SIZE)
-               return NULL;
-
-       device_map[i].dev = strdup(dev);
-       device_map[i].mnt = join_paths(mount_base, dev);
-       return device_map[i].mnt;
+       for (i = 0; i < mount_map_size; i++)
+               if (!strcmp(mount_map[i].dev, dev))
+                       return mount_map[i].mnt;
+
+       /* no existing entry, create a new one */
+       i = mount_map_size++;
+       mount_map = talloc_realloc(NULL, mount_map,
+                       struct mount_map, mount_map_size);
+
+       mount_map[i].dev = talloc_strdup(mount_map, dev);
+       mount_map[i].mnt = join_paths(mount_map, DEVICE_MOUNT_BASE, dev);
+       return mount_map[i].mnt;
 }
 
-char *resolve_path(const char *path, const char *current_dev)
+char *resolve_path(void *alloc_ctx, const char *path, const char *current_dev)
 {
        char *ret;
        const char *devpath, *sep;
@@ -101,35 +122,28 @@ char *resolve_path(const char *path, const char *current_dev)
        sep = strchr(path, ':');
        if (!sep) {
                devpath = mountpoint_for_device(current_dev);
-               ret = join_paths(devpath, path);
+               ret = join_paths(alloc_ctx, devpath, path);
        } else {
                /* parse just the device name into dev */
                char *tmp, *dev;
-               tmp = strndup(path, sep - path);
-               dev = parse_device_path(tmp, current_dev);
+               tmp = talloc_strndup(NULL, path, sep - path);
+               dev = parse_device_path(NULL, tmp, current_dev);
 
                devpath = mountpoint_for_device(dev);
-               ret = join_paths(devpath, sep + 1);
+               ret = join_paths(alloc_ctx, devpath, sep + 1);
 
-               free(dev);
-               free(tmp);
+               talloc_free(dev);
+               talloc_free(tmp);
        }
 
        return ret;
 }
 
-void set_mount_base(const char *path)
-{
-       if (mount_base)
-               free(mount_base);
-       mount_base = strdup(path);
-}
-
-char *join_paths(const char *a, const char *b)
+char *join_paths(void *alloc_ctx, const char *a, const char *b)
 {
        char *full_path;
 
-       full_path = malloc(strlen(a) + strlen(b) + 2);
+       full_path = talloc_array(alloc_ctx, char, strlen(a) + strlen(b) + 2);
 
        strcpy(full_path, a);
        if (b[0] != '/' && a[strlen(a) - 1] != '/')
index 26d4ce41eb2a946c8bc9f13b5e5ba7b60637f4a6..e7c23e56848b3dc584640723c36ee9f598d39ebb 100644 (file)
@@ -11,7 +11,8 @@
  *
  * Returns a newly-allocated string.
  */
-char *parse_device_path(const char *dev_str, const char *current_device);
+char *parse_device_path(void *alloc_ctx,
+               const char *dev_str, const char *current_device);
 
 /**
  * Get the mountpoint for a device.
@@ -29,13 +30,8 @@ const char *mountpoint_for_device(const char *dev);
  *
  * Returns a newly-allocated string containing a full path to the file in path
  */
-char *resolve_path(const char *path, const char *current_device);
-
-
-/**
- * Set the base directory for newly-created mountpoints
- */
-void set_mount_base(const char *path);
+char *resolve_path(void *alloc_ctx,
+               const char *path, const char *current_device);
 
 /**
  * Utility function for joining two paths. Adds a / between a and b if
@@ -43,11 +39,16 @@ void set_mount_base(const char *path);
  *
  * Returns a newly-allocated string.
  */
-char *join_paths(const char *a, const char *b);
+char *join_paths(void *alloc_ctx, const char *a, const char *b);
 
 /**
  * encode a disk label (or uuid) for use in a symlink.
  */
-char *encode_label(const char *label);
+char *encode_label(void *alloc_ctx, const char *label);
+
+/**
+ * Returns the base path for mount points
+ */
+const char *mount_base(void);
 
 #endif /* PATHS_H */
index 16d050e7702aae28de901f7ed969494804c3d129..6747e78a8064f7a04a5b377b3535a6a412658a71 100644 (file)
@@ -60,7 +60,7 @@ static void parse_event_params(struct udev_event *event, char *buf, int len)
        }
 }
 
-static const char *event_param(struct udev_event *event, const char *name)
+const char *udev_event_param(struct udev_event *event, const char *name)
 {
        int i;
 
@@ -86,7 +86,7 @@ static void print_event(struct udev_event *event)
 
        for (i = 0; params[i]; i++)
                printf("\t%-12s => %s\n",
-                               params[i], event_param(event, params[i]));
+                               params[i], udev_event_param(event, params[i]));
 
 }
 
index a23eef4861adbad27bc874d350977c4de0464ea4..250273f2722eb27e52774adfb560e7d92cbbac41 100644 (file)
@@ -24,4 +24,5 @@ struct udev *udev_init(struct device_handler *handler);
 
 void udev_destroy(struct udev *udev);
 
+const char *udev_event_param(struct udev_event *event, const char *name);
 #endif /* _UDEV_H */
index 00241c30279a8645d52ea7055c4a00e8b8d909d4..475d096df097cdbab9d45682a76ff012f2c4b310 100644 (file)
--- a/rules.mk
+++ b/rules.mk
@@ -4,7 +4,8 @@ VPATH = $(srcdir)
 CFLAGS += -I$(top_srcdir) -I$(top_srcdir)/lib -I$(builddir)
 
 # we need paths to be overridable at build-time
-DEFS += '-DPREFIX="$(prefix)"' '-DPKG_SHARE_DIR="$(pkgdatadir)"'
+DEFS += '-DPREFIX="$(prefix)"' '-DPKG_SHARE_DIR="$(pkgdatadir)"' \
+       '-DLOCAL_STATE_DIR="$(localstatedir)"'
 
 #uis = ui/twin/pb-twin
 uis = ui/test/pb-test
@@ -43,7 +44,7 @@ ui/test/pb-test: $(pb_test_objs)
 
 pb_discover_objs = discover/pb-discover.o discover/udev.o discover/log.o \
                   discover/waiter.o discover/discover-server.o \
-                  discover/device-handler.o \
+                  discover/device-handler.o discover/paths.o \
                   $(talloc_objs) $(server_objs) $(list_objs)
 
 discover/pb-discover: $(pb_discover_objs)