X-Git-Url: http://git.ozlabs.org/?p=petitboot;a=blobdiff_plain;f=discover%2Fdevice-handler.c;h=0fc6d75b0e64db0ce0a01773a779fd269663acfc;hp=2b16b2ea2f7a9c937b864b66d28996cca9bae0cf;hb=d70b3460fbbcc9acb4a80e1e6fe51d564ae607aa;hpb=e4f5bd235894c11823ac1befe8c8c43063cad026 diff --git a/discover/device-handler.c b/discover/device-handler.c index 2b16b2e..0fc6d75 100644 --- a/discover/device-handler.c +++ b/discover/device-handler.c @@ -12,11 +12,13 @@ #include #include #include +#include #include "device-handler.h" #include "discover-server.h" #include "event.h" #include "parser.h" +#include "resource.h" #include "udev.h" #include "paths.h" #include "boot.h" @@ -27,8 +29,80 @@ struct device_handler { struct discover_device **devices; unsigned int n_devices; + + struct list unresolved_boot_options; }; +static bool resource_is_resolved(struct resource *res) +{ + return !res || res->resolved; +} + +/* We only use this in an assert, which will disappear if we're compiling + * with NDEBUG, so we need the 'used' attribute for these builds */ +static bool __attribute__((used)) boot_option_is_resolved( + struct discover_boot_option *opt) +{ + return resource_is_resolved(opt->boot_image) && + resource_is_resolved(opt->initrd) && + resource_is_resolved(opt->icon); +} + +static bool resource_resolve(struct resource *res, struct parser *parser, + struct device_handler *handler) +{ + if (resource_is_resolved(res)) + return true; + + parser->resolve_resource(handler, res); + + return res->resolved; +} + +static bool boot_option_resolve(struct discover_boot_option *opt, + struct device_handler *handler) +{ + return resource_resolve(opt->boot_image, opt->source, handler) && + resource_resolve(opt->initrd, opt->source, handler) && + resource_resolve(opt->icon, opt->source, handler); +} + +static void boot_option_finalise(struct discover_boot_option *opt) +{ + assert(boot_option_is_resolved(opt)); + + /* check that the parsers haven't set any of the final data */ + assert(!opt->option->boot_image_file); + assert(!opt->option->initrd_file); + assert(!opt->option->icon_file); + + if (opt->boot_image) + opt->option->boot_image_file = opt->boot_image->url->full; + if (opt->initrd) + opt->option->initrd_file = opt->initrd->url->full; + if (opt->icon) + opt->option->icon_file = opt->icon->url->full; +} + +static void process_boot_option_queue(struct device_handler *handler) +{ + struct discover_boot_option *opt, *tmp; + + list_for_each_entry_safe(&handler->unresolved_boot_options, + opt, tmp, list) { + + if (!boot_option_resolve(opt, handler)) + continue; + + list_remove(&opt->list); + list_add(&opt->device->boot_options, &opt->list); + talloc_steal(opt->device, opt); + boot_option_finalise(opt); + discover_server_notify_boot_option_add(handler->server, + opt->option); + } +} + /** * context_commit - Commit a temporary discovery context to the handler, * and notify the clients about any new options / devices @@ -38,7 +112,7 @@ static void context_commit(struct device_handler *handler, { struct discover_device *dev = ctx->device; struct discover_boot_option *opt, *tmp; - unsigned int i, existing_device; + unsigned int i, existing_device = 0; /* do we already have this device? */ for (i = 0; i < handler->n_devices; i++) { @@ -57,23 +131,47 @@ static void context_commit(struct device_handler *handler, talloc_steal(handler, dev); discover_server_notify_device_add(handler->server, dev->device); + + /* this new device might be able to resolve existing boot + * options */ + process_boot_option_queue(handler); } /* move boot options from the context to the device */ list_for_each_entry_safe(&ctx->boot_options, opt, tmp, list) { list_remove(&opt->list); - list_add(&dev->boot_options, &opt->list); - talloc_steal(dev, opt); - discover_server_notify_boot_option_add(handler->server, - opt->option); + + if (boot_option_resolve(opt, handler)) { + list_add(&dev->boot_options, &opt->list); + talloc_steal(dev, opt); + boot_option_finalise(opt); + discover_server_notify_boot_option_add(handler->server, + opt->option); + } else { + if (!opt->source->resolve_resource) { + pb_log("parser %s gave us an unresolved " + "resource (%s), but no way to " + "resolve it\n", + opt->source->name, opt->option->id); + talloc_free(opt); + } else { + pb_log("boot option %s is unresolved, " + "adding to queue\n", + opt->option->id); + list_add(&handler->unresolved_boot_options, + &opt->list); + talloc_steal(handler, opt); + } + } } } void discover_context_add_boot_option(struct discover_context *ctx, struct discover_boot_option *boot_option) { - list_add(&ctx->boot_options, &boot_option->list); + boot_option->source = ctx->parser; + list_add_tail(&ctx->boot_options, &boot_option->list); talloc_steal(ctx, boot_option); } @@ -130,73 +228,13 @@ const struct discover_device *device_handler_get_device( return handler->devices[index]; } -static void setup_device_links(struct discover_device *dev) -{ - struct link { - const char *dir, *val; - } *link, links[] = { - { - .dir = "disk/by-uuid", - .val = dev->uuid, - }, - { - .dir = "disk/by-label", - .val = dev->label, - }, - { - .dir = NULL - } - }; - - for (link = links; link->dir; link++) { - char *enc, *dir, *path; - - if (!link->val || !*link->val) - continue; - - enc = encode_label(dev, link->val); - dir = join_paths(dev, mount_base(), link->dir); - path = join_paths(dev, dir, enc); - - if (!pb_mkdir_recursive(dir)) { - unlink(path); - if (symlink(dev->mount_path, path)) { - pb_log("symlink(%s,%s): %s\n", - dev->mount_path, path, - strerror(errno)); - talloc_free(path); - } else { - int i = dev->n_links++; - dev->links = talloc_realloc(dev, - dev->links, char *, - dev->n_links); - dev->links[i] = path; - } - - } - - talloc_free(dir); - talloc_free(enc); - } -} - -static void remove_device_links(struct discover_device *dev) -{ - int i; - - for (i = 0; i < dev->n_links; i++) - unlink(dev->links[i]); -} - static int mount_device(struct discover_device *dev) { - const char *mountpoint; const char *argv[6]; - if (!dev->mount_path) { - mountpoint = mountpoint_for_device(dev->device_path); - dev->mount_path = talloc_strdup(dev, mountpoint); - } + if (!dev->mount_path) + dev->mount_path = join_paths(dev, mount_base(), + dev->device_path); if (pb_mkdir_recursive(dev->mount_path)) pb_log("couldn't create mount directory %s: %s\n", @@ -222,7 +260,6 @@ static int mount_device(struct discover_device *dev) goto out_rmdir; } - setup_device_links(dev); return 0; out_rmdir: @@ -235,8 +272,6 @@ static int umount_device(struct discover_device *dev) int status; pid_t pid; - remove_device_links(dev); - if (!dev->mount_path) return 0; @@ -365,7 +400,7 @@ static int handle_add_udev_event(struct device_handler *handler, } /* run the parsers. This will populate the ctx's boot_option list. */ - iterate_parsers(ctx); + iterate_parsers(ctx, CONF_METHOD_LOCAL_FILE); /* add discovered stuff to the handler */ context_commit(handler, ctx); @@ -428,6 +463,61 @@ static int handle_remove_user_event(struct device_handler *handler, return 0; } +static enum conf_method parse_conf_method(const char *str) +{ + + if (!strcasecmp(str, "dhcp")) { + return CONF_METHOD_DHCP; + } + return CONF_METHOD_UNKNOWN; +} + +static int handle_conf_user_event(struct device_handler *handler, + struct event *event) +{ + struct discover_context *ctx; + struct discover_device *dev; + enum conf_method method; + const char *val; + + ctx = talloc(handler, struct discover_context); + ctx->event = event; + list_init(&ctx->boot_options); + + val = event_get_param(event, "url"); + if (!val) { + talloc_free(ctx); + return 0; + } + + ctx->conf_url = pb_url_parse(ctx, val); + if (!ctx->conf_url) { + talloc_free(ctx); + return 0; + } + + val = event_get_param(event, "method"); + if (!val) { + talloc_free(ctx); + return 0; + } + + method = parse_conf_method(val); + if (method == CONF_METHOD_UNKNOWN) { + talloc_free(ctx); + return 0; + } + + dev = discover_device_create(handler, ctx, event); + ctx->device = dev; + + iterate_parsers(ctx, method); + + context_commit(handler, ctx); + + return 0; +} + typedef int (*event_handler)(struct device_handler *, struct event *); static event_handler handlers[EVENT_TYPE_MAX][EVENT_ACTION_MAX] = { @@ -438,6 +528,7 @@ static event_handler handlers[EVENT_TYPE_MAX][EVENT_ACTION_MAX] = { [EVENT_TYPE_USER] = { [EVENT_ACTION_ADD] = handle_add_user_event, [EVENT_ACTION_REMOVE] = handle_remove_user_event, + [EVENT_ACTION_CONF] = handle_conf_user_event, } }; @@ -465,6 +556,7 @@ struct device_handler *device_handler_init(struct discover_server *server, handler->n_devices = 0; handler->server = server; handler->dry_run = dry_run; + list_init(&handler->unresolved_boot_options); /* set up our mount point base */ pb_mkdir_recursive(mount_base()); @@ -583,6 +675,13 @@ static struct discover_boot_option *find_boot_option_by_id( return NULL; } +static void boot_status(void *arg, struct boot_status *status) +{ + struct device_handler *handler = arg; + + discover_server_notify_boot_status(handler->server, status); +} + void device_handler_boot(struct device_handler *handler, struct boot_command *cmd) { @@ -590,5 +689,5 @@ void device_handler_boot(struct device_handler *handler, opt = find_boot_option_by_id(handler, cmd->option_id); - boot(handler, opt->option, cmd, handler->dry_run); + boot(handler, opt, cmd, handler->dry_run, boot_status, handler); }