10 #include <talloc/talloc.h>
11 #include <list/list.h>
13 #include <pb-protocol/pb-protocol.h>
15 #include "device-handler.h"
16 #include "discover-server.h"
21 #define MOUNT_BIN "/bin/mount"
23 #define UMOUNT_BIN "/bin/umount"
25 struct device_handler {
26 struct discover_server *server;
28 struct device **devices;
29 unsigned int n_devices;
40 * device_handler_add - Add a device to the handler device array.
43 static void device_handler_add(struct device_handler *handler,
44 struct device *device)
47 handler->devices = talloc_realloc(handler, handler->devices,
48 struct device *, handler->n_devices);
49 handler->devices[handler->n_devices - 1] = device;
53 * device_handler_remove - Remove a device from the handler device array.
56 static void device_handler_remove(struct device_handler *handler,
57 struct device *device)
61 for (i = 0; i < handler->n_devices; i++)
62 if (handler->devices[i] == device)
65 if (i < handler->n_devices) {
66 assert(0 && "unknown device");
71 memmove(&handler->devices[i], &handler->devices[i + 1],
72 (handler->n_devices - i) * sizeof(handler->devices[0]));
73 handler->devices = talloc_realloc(handler, handler->devices,
74 struct device *, handler->n_devices);
78 * device_handler_find - Find a handler device by id.
81 static struct device *device_handler_find(struct device_handler *handler,
88 for (i = 0; i < handler->n_devices; i++)
89 if (handler->devices[i]->id
90 && streq(handler->devices[i]->id, id))
91 return handler->devices[i];
93 assert(0 && "unknown device");
98 * device_handler_get_device_count - Get the count of current handler devices.
101 int device_handler_get_device_count(const struct device_handler *handler)
103 return handler->n_devices;
107 * device_handler_get_device - Get a handler device by index.
110 const struct device *device_handler_get_device(
111 const struct device_handler *handler, unsigned int index)
113 if (index >= handler->n_devices) {
114 assert(0 && "bad index");
118 return handler->devices[index];
121 static int mkdir_recursive(const char *dir)
130 if (!stat(dir, &statbuf)) {
131 if (!S_ISDIR(statbuf.st_mode)) {
132 pb_log("%s: %s exists, but isn't a directory\n",
139 str = talloc_strdup(NULL, dir);
140 sep = strchr(*str == '/' ? str + 1 : str, '/');
144 /* terminate the path at sep */
148 if (mkdir(str, mode) && errno != EEXIST) {
149 pb_log("mkdir(%s): %s\n", str, strerror(errno));
156 /* reset dir to the full path */
158 sep = strchr(sep + 1, '/');
166 static int rmdir_recursive(const char *base, const char *dir)
170 /* sanity check: make sure that dir is within base */
171 if (strncmp(base, dir, strlen(base)))
174 cur = talloc_strdup(NULL, dir);
176 while (strcmp(base, dir)) {
180 /* null-terminate at the last slash */
181 pos = strrchr(dir, '/');
193 static void setup_device_links(struct discover_context *ctx)
200 .dir = "disk/by-uuid"
203 .env = "ID_FS_LABEL",
204 .dir = "disk/by-label"
211 for (link = links; link->env; link++) {
212 char *enc, *dir, *path;
215 value = udev_event_param(ctx->event, link->env);
216 if (!value || !*value)
219 enc = encode_label(ctx, value);
220 dir = join_paths(ctx, mount_base(), link->dir);
221 path = join_paths(ctx, dir, value);
223 if (!mkdir_recursive(dir)) {
225 if (symlink(ctx->mount_path, path)) {
226 pb_log("symlink(%s,%s): %s\n",
227 ctx->mount_path, path,
231 int i = ctx->n_links++;
232 ctx->links = talloc_realloc(ctx,
235 ctx->links[i] = path;
245 static void remove_device_links(struct discover_context *ctx)
249 for (i = 0; i < ctx->n_links; i++)
250 unlink(ctx->links[i]);
253 static int mount_device(struct discover_context *ctx)
255 const char *mountpoint;
259 if (!ctx->mount_path) {
260 mountpoint = mountpoint_for_device(ctx->device_path);
261 ctx->mount_path = talloc_strdup(ctx, mountpoint);
264 if (mkdir_recursive(ctx->mount_path))
265 pb_log("couldn't create mount directory %s: %s\n",
266 ctx->mount_path, strerror(errno));
270 pb_log("%s: fork failed: %s\n", __func__, strerror(errno));
275 execl(MOUNT_BIN, MOUNT_BIN, ctx->device_path, ctx->mount_path,
280 if (waitpid(pid, &status, 0) == -1) {
281 pb_log("%s: waitpid failed: %s\n", __func__,
286 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
287 pb_log("%s: mount failed (%d): %s\n", __func__,
288 WEXITSTATUS(status), ctx->event->device);
292 setup_device_links(ctx);
296 rmdir_recursive(mount_base(), ctx->mount_path);
300 static int umount_device(struct discover_context *ctx)
305 remove_device_links(ctx);
309 pb_log("%s: fork failed: %s\n", __func__, strerror(errno));
314 execl(UMOUNT_BIN, UMOUNT_BIN, ctx->mount_path, NULL);
318 if (waitpid(pid, &status, 0) == -1) {
319 pb_log("%s: waitpid failed: %s\n", __func__,
324 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
327 rmdir_recursive(mount_base(), ctx->mount_path);
332 static struct discover_context *find_context(struct device_handler *handler,
335 struct discover_context *ctx;
337 list_for_each_entry(&handler->contexts, ctx, list) {
338 if (!strcmp(ctx->id, id))
345 static int destroy_context(void *arg)
347 struct discover_context *ctx = arg;
349 list_remove(&ctx->list);
355 static int handle_add_event(struct device_handler *handler,
356 struct udev_event *event)
358 struct discover_context *ctx;
362 /* create our context */
363 ctx = talloc(handler, struct discover_context);
365 ctx->mount_path = NULL;
369 ctx->id = talloc_strdup(ctx, event->device);
371 devname = udev_event_param(ctx->event, "DEVNAME");
373 pb_log("no devname for %s?\n", event->device);
377 ctx->device_path = talloc_strdup(ctx, devname);
379 rc = mount_device(ctx);
385 list_add(&handler->contexts, &ctx->list);
386 talloc_set_destructor(ctx, destroy_context);
388 /* set up the top-level device */
389 ctx->device = talloc_zero(ctx, struct device);
390 ctx->device->id = talloc_strdup(ctx->device, ctx->id);
391 list_init(&ctx->device->boot_options);
393 /* run the parsers */
394 iterate_parsers(ctx);
396 /* add device to handler device array */
397 device_handler_add(handler, ctx->device);
399 discover_server_notify_add(handler->server, ctx->device);
404 static int handle_remove_event(struct device_handler *handler,
405 struct udev_event *event)
407 struct discover_context *ctx;
409 ctx = find_context(handler, event->device);
413 discover_server_notify_remove(handler->server, ctx->device);
415 /* remove device from handler device array */
416 device_handler_remove(handler, ctx->device);
423 int device_handler_event(struct device_handler *handler,
424 struct udev_event *event)
428 switch (event->action) {
429 case UDEV_ACTION_ADD:
430 rc = handle_add_event(handler, event);
433 case UDEV_ACTION_REMOVE:
434 rc = handle_remove_event(handler, event);
441 struct device_handler *device_handler_init(struct discover_server *server)
443 struct device_handler *handler;
445 handler = talloc(NULL, struct device_handler);
446 handler->devices = NULL;
447 handler->n_devices = 0;
448 handler->server = server;
450 list_init(&handler->contexts);
452 /* set up our mount point base */
453 mkdir_recursive(mount_base());
460 void device_handler_destroy(struct device_handler *handler)
462 talloc_free(handler);