[3/4] discover/paths: Add network jobs to queue
diff mbox

Message ID 20170623055115.23963-3-sam@mendozajonas.com
State Accepted
Headers show

Commit Message

Samuel Mendoza-Jonas June 23, 2017, 5:51 a.m. UTC
Load tasks that start before the network is available will fail. Rather
than just fail these tasks, add them to a queue that is processed once
the network is ready. This helps users who try to request files early in
setup, as well as very early running load tasks.

Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com>
---
 discover/device-handler.c |   3 ++
 discover/network.c        |   3 ++
 discover/paths.c          | 100 ++++++++++++++++++++++++++++++++++++++++++++++
 discover/paths.h          |   4 ++
 test/parser/handler.c     |   8 ++++
 5 files changed, 118 insertions(+)

Patch
diff mbox

diff --git a/discover/device-handler.c b/discover/device-handler.c
index a0c21b7..730be85 100644
--- a/discover/device-handler.c
+++ b/discover/device-handler.c
@@ -361,6 +361,7 @@  void device_handler_reinit(struct device_handler *handler)
 
 	/* 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,
@@ -1083,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);
diff --git a/discover/network.c b/discover/network.c
index 8ca4561..5035b7c 100644
--- a/discover/network.c
+++ b/discover/network.c
@@ -23,6 +23,7 @@ 
 #include "sysinfo.h"
 #include "platform.h"
 #include "device-handler.h"
+#include "paths.h"
 
 #define HWADDR_SIZE	6
 #define PIDFILE_BASE	(LOCAL_STATE_DIR "/petitboot/")
@@ -489,6 +490,8 @@  static void configure_interface(struct network *network,
 
 	} else if (config->method == CONFIG_METHOD_STATIC) {
 		configure_interface_static(network, interface, config);
+		/* Nothing left to do for static interfaces */
+		pending_network_jobs_start();
 	}
 
 	interface->state = IFSTATE_CONFIGURED;
diff --git a/discover/paths.c b/discover/paths.c
index e76dc35..3a69488 100644
--- a/discover/paths.c
+++ b/discover/paths.c
@@ -18,9 +18,20 @@ 
 
 #include "paths.h"
 #include "device-handler.h"
+#include "sysinfo.h"
 
 #define DEVICE_MOUNT_BASE (LOCAL_STATE_DIR "/petitboot/mnt")
 
+
+struct list	pending_network_jobs;
+
+struct network_job {
+	struct load_task	*task;
+	int			flags;
+
+	struct list_item	list;
+};
+
 struct load_task {
 	struct pb_url		*url;
 	struct process		*process;
@@ -437,6 +448,86 @@  static void load_local(struct load_task *task)
 	}
 }
 
+static void load_url_async_start_pending(struct load_task *task, int flags)
+{
+	pb_log("Starting pending job for %s\n", task->url->full);
+
+	switch (task->url->scheme) {
+	case pb_url_ftp:
+	case pb_url_http:
+		load_wget(task, flags);
+		break;
+	case pb_url_https:
+		flags |= wget_no_check_certificate;
+		load_wget(task, flags);
+		break;
+	case pb_url_nfs:
+		load_nfs(task);
+		break;
+	case pb_url_sftp:
+		load_sftp(task);
+		break;
+	case pb_url_tftp:
+		load_tftp(task);
+		break;
+	default:
+		/* Shouldn't be a need via this path but.. */
+		load_local(task);
+		break;
+	}
+
+	if (task->result->status == LOAD_ERROR) {
+		pb_log("Pending job failed for %s\n", task->url->full);
+		load_url_result_cleanup_local(task->result);
+		talloc_free(task->result);
+		talloc_free(task);
+	}
+}
+
+void pending_network_jobs_start(void)
+{
+	struct network_job *job, *tmp;
+
+	if (!pending_network_jobs.head.next)
+		return;
+
+	list_for_each_entry_safe(&pending_network_jobs, job, tmp, list) {
+		load_url_async_start_pending(job->task, job->flags);
+		list_remove(&job->list);
+	}
+}
+
+void pending_network_jobs_cancel(void)
+{
+	struct network_job *job, *tmp;
+
+	if (!pending_network_jobs.head.next)
+		return;
+
+	list_for_each_entry_safe(&pending_network_jobs, job, tmp, list)
+		talloc_free(job);
+	list_init(&pending_network_jobs);
+}
+
+static void pending_network_jobs_add(struct load_task *task, int flags)
+{
+	struct network_job *job;
+
+	if (!pending_network_jobs.head.next)
+		list_init(&pending_network_jobs);
+
+	job = talloc(task, struct network_job);
+	if (!job) {
+		pb_log("Failed to allocate space for pending job\n");
+		return;
+	}
+
+	job->task = task;
+	job->flags = flags;
+	list_add_tail(&pending_network_jobs, &job->list);
+}
+
+
 /**
  * load_url - Loads a (possibly) remote URL and returns the local file
  * path.
@@ -487,6 +578,15 @@  struct load_url_result *load_url_async(void *ctx, struct pb_url *url,
 		task->process->keep_stdout = true;
 	}
 
+	/* If the url is remote but network is not yet available queue up this
+	 * load for later */
+	if (!system_info_network_available() && url->scheme != pb_url_file) {
+		pb_log("load task for %s queued pending network\n", url->full);
+		pending_network_jobs_add(task, flags);
+		task->result->status = LOAD_ASYNC;
+		return task->result;
+	}
+
 	switch (url->scheme) {
 	case pb_url_ftp:
 	case pb_url_http:
diff --git a/discover/paths.h b/discover/paths.h
index 35673b7..67fe8a3 100644
--- a/discover/paths.h
+++ b/discover/paths.h
@@ -43,6 +43,10 @@  struct load_url_result {
  */
 typedef void (*load_url_complete)(struct load_url_result *result, void *data);
 
+/* Start transfers that were waiting for network connectivity */
+void pending_network_jobs_start(void);
+void pending_network_jobs_cancel(void);
+
 /* Load a (potentially remote) file, and return a guaranteed-local name */
 struct load_url_result *load_url_async(void *ctx, struct pb_url *url,
 		load_url_complete complete, void *data,
diff --git a/test/parser/handler.c b/test/parser/handler.c
index e356407..e455c22 100644
--- a/test/parser/handler.c
+++ b/test/parser/handler.c
@@ -102,3 +102,11 @@  void boot_cancel(struct boot_task *task)
 {
 	(void)task;
 }
+
+void pending_network_jobs_start(void)
+{
+}
+
+void pending_network_jobs_cancel(void)
+{
+}