From 5eb7f7bcd3431cd8f634b02b71bd78f6162c2af3 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Tue, 16 Dec 2008 15:33:59 +1100 Subject: [PATCH] Initial device handler code Mount discovered devices, and set up symlinks for UUID and LABELs Signed-off-by: Jeremy Kerr --- Makefile.in | 1 + discover/device-handler.c | 258 +++++++++++++++++++++++++++++++++++--- discover/paths.c | 106 +++++++++------- discover/paths.h | 21 ++-- discover/udev.c | 4 +- discover/udev.h | 1 + rules.mk | 5 +- 7 files changed, 322 insertions(+), 74 deletions(-) diff --git a/Makefile.in b/Makefile.in index e537e9d..47adfd6 100644 --- a/Makefile.in +++ b/Makefile.in @@ -24,6 +24,7 @@ sbindir = @sbindir@ datarootdir = @datarootdir@ datadir = @datadir@ pkgdatadir = ${datadir}/${PACKAGE} +localstatedir = @localstatedir@ builddir = @builddir@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ diff --git a/discover/device-handler.c b/discover/device-handler.c index 0e902df..5d40ebe 100644 --- a/discover/device-handler.c +++ b/discover/device-handler.c @@ -1,8 +1,20 @@ +#include +#include +#include +#include +#include +#include + #include #include #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); +} + diff --git a/discover/paths.c b/discover/paths.c index 2373c28..72d07b2 100644 --- a/discover/paths.c +++ b/discover/paths.c @@ -4,24 +4,41 @@ #include #include +#include + #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] != '/') diff --git a/discover/paths.h b/discover/paths.h index 26d4ce4..e7c23e5 100644 --- a/discover/paths.h +++ b/discover/paths.h @@ -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 */ diff --git a/discover/udev.c b/discover/udev.c index 16d050e..6747e78 100644 --- a/discover/udev.c +++ b/discover/udev.c @@ -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])); } diff --git a/discover/udev.h b/discover/udev.h index a23eef4..250273f 100644 --- a/discover/udev.h +++ b/discover/udev.h @@ -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 */ diff --git a/rules.mk b/rules.mk index 00241c3..475d096 100644 --- 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) -- 2.39.2