]> git.ozlabs.org Git - petitboot/blobdiff - discover/device-handler.c
discover: implement a periodic requery for network devices
[petitboot] / discover / device-handler.c
index ce3fc465ec03fadef829052eda8fe1ad8644ed7a..42c95bb892ebba47985ae437c011e6f95b344b4d 100644 (file)
@@ -1,4 +1,5 @@
 #include <assert.h>
+#include <inttypes.h>
 #include <stdlib.h>
 #include <stdbool.h>
 #include <unistd.h>
@@ -18,6 +19,7 @@
 #include <process/process.h>
 #include <url/url.h>
 #include <i18n/i18n.h>
+#include <pb-config/pb-config.h>
 
 #include <sys/sysmacros.h>
 #include <sys/types.h>
@@ -47,6 +49,8 @@ enum default_priority {
        DEFAULT_PRIORITY_DISABLED       = 0xff,
 };
 
+static int default_rescan_timeout = 5 * 60; /* seconds */
+
 struct progress_info {
        unsigned int                    percentage;
        unsigned long                   size;           /* size in bytes */
@@ -393,6 +397,7 @@ void device_handler_reinit(struct device_handler *handler)
 {
        struct discover_boot_option *opt, *tmp;
        struct ramdisk_device *ramdisk;
+       struct config *config;
        unsigned int i;
 
        device_handler_cancel_default(handler);
@@ -415,10 +420,13 @@ void device_handler_reinit(struct device_handler *handler)
 
        /* drop all devices */
        for (i = 0; i < handler->n_devices; i++) {
+               struct discover_device *device = handler->devices[i];
                discover_server_notify_device_remove(handler->server,
-                               handler->devices[i]->device);
-               ramdisk = handler->devices[i]->ramdisk;
-               talloc_free(handler->devices[i]);
+                               device->device);
+               ramdisk = device->ramdisk;
+               if (device->requery_waiter)
+                       waiter_remove(device->requery_waiter);
+               talloc_free(device);
                talloc_free(ramdisk);
        }
 
@@ -440,6 +448,17 @@ void device_handler_reinit(struct device_handler *handler)
 
        set_env_variables(config_get());
 
+       /* If the safe mode warning was active disable it now */
+       if (config_get()->safe_mode) {
+               config = config_copy(handler, config_get());
+               config->safe_mode = false;
+               config_set(config);
+               discover_server_notify_config(handler->server, config);
+       }
+
+       /* Force rediscovery on SCSI devices */
+       process_run_simple(handler, pb_system_apps.scsi_rescan, NULL);
+
        device_handler_reinit_sources(handler);
 }
 
@@ -449,6 +468,9 @@ void device_handler_remove(struct device_handler *handler,
        struct discover_boot_option *opt, *tmp;
        unsigned int i;
 
+       if (device->requery_waiter)
+               waiter_remove(device->requery_waiter);
+
        list_for_each_entry_safe(&device->boot_options, opt, tmp, list) {
                if (opt == handler->default_boot_option) {
                        pb_log("Default option %s cancelled since device removed",
@@ -643,7 +665,7 @@ void device_handler_status_download(struct device_handler *handler,
                        unit++;
                }
                update = talloc_asprintf(handler,
-                               _("%u %s downloading: %.0f%% - %lu%cB"),
+                               _("%u %s downloading: %.0f%% - %" PRIu64 "%cB"),
                                handler->n_progress,
                                ngettext("item", "items", handler->n_progress),
                                (current / total) * 100, current_converted,
@@ -688,7 +710,17 @@ void device_handler_status_download_remove(struct device_handler *handler,
 
 static void device_handler_boot_status_cb(void *arg, struct status *status)
 {
-       device_handler_status(arg, status);
+       struct device_handler *handler = arg;
+
+       /* boot had failed; update handler state to allow a new default if one
+        * is found later
+        */
+       if (status->type == STATUS_ERROR) {
+               handler->pending_boot = NULL;
+               handler->default_boot_option = NULL;
+       }
+
+       device_handler_status(handler, status);
 }
 
 static void countdown_status(struct device_handler *handler,
@@ -821,6 +853,9 @@ static enum default_priority default_option_priority(
                int boot_match = autoboot_option_priority(config, opt);
                if (boot_match > 0)
                        return boot_match;
+       } else {
+               /* If there is no specific boot order, boot any device */
+               return DEFAULT_PRIORITY_LOCAL_FIRST;
        }
 
        /* If the option didn't match any entry in the array, it is disabled */
@@ -1148,6 +1183,109 @@ out:
        return 0;
 }
 
+struct requery_data {
+       struct device_handler   *handler;
+       struct discover_device  *device;
+};
+
+static int device_handler_requery_timeout_fn(void *data)
+{
+       struct discover_boot_option *opt, *tmp;
+       struct requery_data *rqd = data;
+       struct device_handler *handler;
+       struct discover_device *device;
+
+       handler = rqd->handler;
+       device = rqd->device;
+
+       talloc_free(rqd);
+
+       /* network_requery_device may re-add a timeout, so clear the device
+        * waiter here, so we can potentially start a new one. */
+       device->requery_waiter = NULL;
+
+       /* We keep the device around, but get rid of the parsed boot
+        * options on that device. That involves delaring out the lists,
+        * and potentially cancelling a default.
+        */
+       list_for_each_entry_safe(&handler->unresolved_boot_options,
+                       opt, tmp, list) {
+               if (opt->device != device)
+                       continue;
+               list_remove(&opt->list);
+               talloc_free(opt);
+       }
+
+       list_for_each_entry_safe(&device->boot_options, opt, tmp, list) {
+               if (opt == handler->default_boot_option) {
+                       pb_log("Default option %s cancelled since device is being requeried",
+                                       opt->option->name);
+                       device_handler_cancel_default(handler);
+               }
+               list_remove(&opt->list);
+               talloc_free(opt);
+       }
+
+       discover_server_notify_device_remove(handler->server, device->device);
+       device->notified = false;
+
+       network_requery_device(handler->network, device);
+
+       return 0;
+}
+
+/* Schedule a requery in timeout (seconds).
+ *
+ * Special values of timeout:
+ *   0: no requery
+ *  -1: use default
+ */
+void device_handler_start_requery_timeout( struct device_handler *handler,
+               struct discover_device *dev, int timeout)
+{
+       struct requery_data *rqd;
+
+       if (dev->requery_waiter)
+               return;
+
+       if (timeout == -1)
+               timeout = default_rescan_timeout;
+       else if (timeout == 0)
+               return;
+
+       rqd = talloc(dev, struct requery_data);
+       rqd->handler = handler;
+       rqd->device = dev;
+
+       pb_debug("starting requery timeout for device %s, in %d sec\n",
+                       dev->device->id, timeout);
+
+       dev->requery_waiter = waiter_register_timeout(handler->waitset,
+                       timeout * 1000, device_handler_requery_timeout_fn, rqd);
+}
+
+static int event_requery_timeout(struct event *event)
+{
+       int timeout = -1;
+       unsigned long x;
+       const char *str;
+       char *endp;
+
+       if (!event)
+               return timeout;
+
+       str = event_get_param(event, "reboottime");
+       if (!str)
+               return timeout;
+
+       x = strtoul(str, &endp, 0);
+       if (endp != str)
+               timeout = x;
+
+       return timeout;
+}
+
+
 /* Incoming dhcp event */
 int device_handler_dhcp(struct device_handler *handler,
                struct discover_device *dev, struct event *event)
@@ -1165,6 +1303,9 @@ int device_handler_dhcp(struct device_handler *handler,
        talloc_steal(ctx, event);
        ctx->event = event;
 
+       device_handler_start_requery_timeout(handler, dev,
+                       event_requery_timeout(event));
+
        iterate_parsers(ctx);
 
        device_handler_discover_context_commit(handler, ctx);
@@ -1346,7 +1487,7 @@ static void process_url_cb(struct load_url_result *result, void *data)
 
        mac = event_get_param(event, "mac");
        char *url = talloc_asprintf(event, "file://%s", result->local);
-       event_set_param(event, "pxeconffile", url);
+       event_set_param(event, "pxeconffile-local", url);
 
        dev = discover_device_create(handler, mac, event->device);
        ctx = device_handler_discover_context_create(handler, dev);
@@ -1546,8 +1687,7 @@ void device_handler_add_plugin_option(struct device_handler *handler,
                tmp = handler->plugins[i];
                /* If both id and version match, ignore */
                if (strncmp(opt->id, tmp->id, strlen(opt->id)) == 0 &&
-                               strncmp(opt->version, tmp->version,
-                                       strlen(opt->version) == 0)) {
+                               strcmp(opt->version, tmp->version) == 0) {
                        pb_log("discover: Plugin '%s' already exists, ignoring\n",
                                        opt->id);
                        return;
@@ -1782,6 +1922,20 @@ static int mount_device(struct discover_device *dev)
        rc = try_mount(device_path, dev->mount_path, fstype,
                       MS_RDONLY | MS_SILENT, dev->ramdisk);
 
+       /* If mount fails clean up any snapshot and try again */
+       if (rc && dev->ramdisk) {
+               pb_log("couldn't mount snapshot for %s: mount failed: %s\n",
+                               device_path, strerror(errno));
+               pb_log("falling back to actual device\n");
+
+               devmapper_destroy_snapshot(dev);
+
+               device_path = get_device_path(dev);
+               pb_log("mounting device %s read-only\n", dev->device_path);
+               rc = try_mount(device_path, dev->mount_path, fstype,
+                              MS_RDONLY | MS_SILENT, dev->ramdisk);
+       }
+
        if (!rc) {
                dev->mounted = true;
                dev->mounted_rw = false;
@@ -1793,9 +1947,6 @@ static int mount_device(struct discover_device *dev)
        pb_log("couldn't mount device %s: mount failed: %s\n",
                        device_path, strerror(errno));
 
-       /* If mount fails clean up any snapshot */
-       devmapper_destroy_snapshot(dev);
-
        pb_rmdir_recursive(mount_base(), dev->mount_path);
 err_free:
        talloc_free(dev->mount_path);