summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.in1
-rw-r--r--discover/device-handler.c258
-rw-r--r--discover/paths.c106
-rw-r--r--discover/paths.h21
-rw-r--r--discover/udev.c4
-rw-r--r--discover/udev.h1
-rw-r--r--rules.mk5
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 <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);
+}
+
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 <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] != '/')
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)
OpenPOWER on IntegriCloud