X-Git-Url: http://git.ozlabs.org/?p=petitboot;a=blobdiff_plain;f=discover%2Fdevice-handler.c;h=38584440801963e193a7aee2ad9c7a62cb89f9d3;hp=d038c5b641e8c4e42420e615612542bde5cadaf3;hb=4c55c0778e983c41418effecdbd96437b43a5513;hpb=789ba5f256f2b9f98d3eaad8c39c894c8e8c86f6 diff --git a/discover/device-handler.c b/discover/device-handler.c index d038c5b..3858444 100644 --- a/discover/device-handler.c +++ b/discover/device-handler.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -46,6 +47,14 @@ enum default_priority { DEFAULT_PRIORITY_DISABLED = 0xff, }; +struct progress_info { + unsigned int percentage; + unsigned long size; /* size in bytes */ + + const struct process_info *procinfo; + struct list_item list; +}; + struct device_handler { struct discover_server *server; int dry_run; @@ -72,6 +81,9 @@ struct device_handler { struct boot_task *pending_boot; bool pending_boot_is_default; + + struct list progress; + unsigned int n_progress; }; static int mount_device(struct discover_device *dev); @@ -310,10 +322,12 @@ struct device_handler *device_handler_init(struct discover_server *server, handler->server = server; handler->waitset = waitset; handler->dry_run = dry_run; - handler->autoboot_enabled = config_get()->autoboot_enabled; + handler->autoboot_enabled = config_autoboot_active(config_get()); list_init(&handler->unresolved_boot_options); + list_init(&handler->progress); + /* set up our mount point base */ pb_mkdir_recursive(mount_base()); @@ -338,6 +352,16 @@ void device_handler_reinit(struct device_handler *handler) unsigned int i; device_handler_cancel_default(handler); + /* Cancel any pending non-default boot */ + if (handler->pending_boot) { + boot_cancel(handler->pending_boot); + handler->pending_boot = NULL; + handler->pending_boot_is_default = false; + } + + /* Cancel any remaining async jobs */ + process_stop_async_all(); + pending_network_jobs_cancel(); /* free unresolved boot options */ list_for_each_entry_safe(&handler->unresolved_boot_options, @@ -429,6 +453,7 @@ static void _device_handler_vstatus(struct device_handler *handler, status.type = type; status.message = talloc_vasprintf(handler, fmt, ap); + status.backlog = false; device_handler_status(handler, &status); @@ -487,6 +512,110 @@ void device_handler_status_err(struct device_handler *handler, va_end(ap); } +void device_handler_status_download(struct device_handler *handler, + const struct process_info *procinfo, + unsigned int percentage, unsigned int size, char suffix) +{ + struct progress_info *p, *progress = NULL; + uint64_t current_converted, current = 0; + const char *units = " kMGTP"; + unsigned long size_bytes; + char *update = NULL; + double total = 0; + unsigned int i; + int unit = 0; + + list_for_each_entry(&handler->progress, p, list) + if (p->procinfo == procinfo) + progress = p; + + if (!progress) { + pb_log("Registering new progress struct\n"); + progress = talloc_zero(handler, struct progress_info); + if (!progress) { + pb_log("Failed to allocate room for progress struct\n"); + return; + } + progress->procinfo = procinfo; + list_add(&handler->progress, &progress->list); + handler->n_progress++; + } + + size_bytes = size; + for (i = 0; i < strlen(units); i++) { + if (units[i] == suffix) + break; + } + + if (i >= strlen(units)) { + pb_log("Couldn't recognise suffix '%c'\n", suffix); + size_bytes = 0; + } else { + while (i--) + size_bytes <<= 10; + } + + progress->percentage = percentage; + progress->size = size_bytes; + + /* + * Aggregate the info we have and update status. If a progress struct + * has zero for both percentage and size we assume progress information + * is unavailable and fall back to a generic progress message. + */ + list_for_each_entry(&handler->progress, p, list) { + uint64_t c; + double t; + if (!p->percentage || !p->size) { + update = talloc_asprintf(handler, + _("%u downloads in progress..."), + handler->n_progress); + current = total = 0; + break; + } + + c = p->size; + t = (100 * c) / p->percentage; + + current += c; + total += t; + } + + if (total) { + current_converted = current; + while (current_converted >= 1000) { + current_converted >>= 10; + unit++; + } + update = talloc_asprintf(handler, + _("%u %s downloading: %.0f%% - %lu%cB"), + handler->n_progress, + ngettext("item", "items", handler->n_progress), + (current / total) * 100, current_converted, + units[unit]); + } + + if (!update) { + pb_log("%s: failed to allocate new status\n", __func__); + } else { + device_handler_status_info(handler, "%s\n", update); + talloc_free(update); + } +} + +void device_handler_status_download_remove(struct device_handler *handler, + struct process_info *procinfo) +{ + struct progress_info *p, *tmp; + + list_for_each_entry_safe(&handler->progress, p, tmp, list) + if (p->procinfo == procinfo) { + list_remove(&p->list); + talloc_free(p); + handler->n_progress--; + } +} + static void device_handler_boot_status_cb(void *arg, struct status *status) { device_handler_status(arg, status); @@ -500,6 +629,7 @@ static void countdown_status(struct device_handler *handler, status.type = STATUS_INFO; status.message = talloc_asprintf(handler, _("Booting in %d sec: %s"), sec, opt->option->name); + status.backlog = false; device_handler_status(handler, &status); @@ -920,8 +1050,6 @@ int device_handler_discover(struct device_handler *handler, _("Processing new %s device"), device_type_display_name(dev->device->type)); - process_boot_option_queue(handler); - /* create our context */ ctx = device_handler_discover_context_create(handler, dev); @@ -939,6 +1067,7 @@ int device_handler_discover(struct device_handler *handler, /* add discovered stuff to the handler */ device_handler_discover_context_commit(handler, ctx); + process_boot_option_queue(handler); out: talloc_unlink(handler, ctx); @@ -955,6 +1084,8 @@ int device_handler_dhcp(struct device_handler *handler, _("Processing DHCP lease response (ip: %s)"), event_get_param(event, "ip")); + pending_network_jobs_start(); + /* create our context */ ctx = device_handler_discover_context_create(handler, dev); talloc_steal(ctx, event); @@ -1113,62 +1244,101 @@ static char *device_from_addr(void *ctx, struct pb_url *url) return dev; } +static void process_url_cb(struct load_url_result *result, void *data) +{ + struct device_handler *handler; + struct discover_context *ctx; + struct discover_device *dev; + struct event *event = data; + const char *mac; + + if (result->status != LOAD_OK) { + pb_log("%s: Load failed for %s\n", __func__, result->url->full); + return; + } + + if (!event) + return; + + handler = talloc_parent(event); + if (!handler) + return; + + event->device = device_from_addr(event, result->url); + if (!event->device) { + pb_log("Downloaded a file but can't find its interface - pretending it was local\n"); + event->device = talloc_asprintf(event, "local"); + } + + mac = event_get_param(event, "mac"); + char *url = talloc_asprintf(event, "file://%s", result->local); + event_set_param(event, "pxeconffile", url); + + dev = discover_device_create(handler, mac, event->device); + ctx = device_handler_discover_context_create(handler, dev); + talloc_steal(ctx, event); + ctx->event = event; + + iterate_parsers(ctx); + + device_handler_discover_context_commit(handler, ctx); + + talloc_unlink(handler, ctx); +} + void device_handler_process_url(struct device_handler *handler, const char *url, const char *mac, const char *ip) { struct discover_context *ctx; struct discover_device *dev; + bool allow_async = false; struct pb_url *pb_url; struct event *event; - struct param *param; - - if (!handler->network) { - device_handler_status_err(handler, _("No network configured")); - return; - } - event = talloc(handler, struct event); + event = talloc_zero(handler, struct event); event->type = EVENT_TYPE_USER; event->action = EVENT_ACTION_URL; - if (url[strlen(url) - 1] == '/') { - event->params = talloc_array(event, struct param, 3); - param = &event->params[0]; - param->name = talloc_strdup(event, "pxepathprefix"); - param->value = talloc_strdup(event, url); - param = &event->params[1]; - param->name = talloc_strdup(event, "mac"); - param->value = talloc_strdup(event, mac); - param = &event->params[2]; - param->name = talloc_strdup(event, "ip"); - param->value = talloc_strdup(event, ip); - event->n_params = 3; - } else { - event->params = talloc_array(event, struct param, 1); - param = &event->params[0]; - param->name = talloc_strdup(event, "pxeconffile"); - param->value = talloc_strdup(event, url); - event->n_params = 1; - } - - pb_url = pb_url_parse(event, event->params->value); + pb_url = pb_url_parse(event, url); if (!pb_url || (pb_url->scheme != pb_url_file && !pb_url->host)) { device_handler_status_err(handler, _("Invalid config URL!")); + talloc_free(event); return; } - if (pb_url->scheme == pb_url_file) - event->device = talloc_asprintf(event, "local"); - else + if (url[strlen(url) - 1] == '/') { + event_set_param(event, "pxepathprefix", url); + event_set_param(event, "mac", mac); + event_set_param(event, "ip", ip); event->device = device_from_addr(event, pb_url); - - if (!event->device) { - device_handler_status_err(handler, + if (!event->device) { + device_handler_status_err(handler, _("Unable to route to host %s"), pb_url->host); + talloc_free(event); + return; + } + } else { + event_set_param(event, "pxeconffile", url); + allow_async = true; + } + + if (pb_url->scheme == pb_url_file) + event->device = talloc_asprintf(event, "local"); + else if (allow_async) { + /* If file is remote load asynchronously before passing to + * parser. This allows us to wait for network to be available */ + if (!load_url_async(handler, pb_url, process_url_cb, event, + NULL, handler)) { + pb_log("Failed to load url %s\n", pb_url->full); + device_handler_status_err(handler, _("Failed to load URL!")); + talloc_free(event); + } return; } + /* If path is local we can parse straight away */ + dev = discover_device_create(handler, mac, event->device); if (pb_url->scheme == pb_url_file) dev->device->type = DEVICE_TYPE_ANY; @@ -1279,6 +1449,8 @@ static void device_handler_reinit_sources(struct device_handler *handler) return; } + system_info_reinit(); + udev_reinit(handler->udev); network_shutdown(handler->network);