8 #include <talloc/talloc.h>
9 #include <system/system.h>
10 #include <process/process.h>
16 #define DEVICE_MOUNT_BASE (LOCAL_STATE_DIR "/petitboot/mnt")
20 struct process *process;
21 struct load_url_result *result;
23 load_url_complete async_cb;
27 const char *mount_base(void)
29 return DEVICE_MOUNT_BASE;
32 char *join_paths(void *alloc_ctx, const char *a, const char *b)
36 full_path = talloc_array(alloc_ctx, char, strlen(a) + strlen(b) + 2);
39 if (b[0] != '/' && a[strlen(a) - 1] != '/')
40 strcat(full_path, "/");
47 static char *local_name(void *ctx)
51 tmp = tempnam(NULL, "pb-");
56 ret = talloc_strdup(ctx, tmp);
62 static void load_url_result_cleanup_local(struct load_url_result *result)
64 if (result->cleanup_local)
65 unlink(result->local);
68 static void load_url_process_exit(struct process *process)
70 struct load_task *task = process->data;
71 struct load_url_result *result;
75 pb_debug("The download client '%s' [pid %d, url %s] exited, rc %d\n",
76 process->path, process->pid, task->url->full,
77 process->exit_status);
79 result = task->result;
80 data = task->async_data;
83 if (result->status == LOAD_CANCELLED) {
84 load_url_result_cleanup_local(result);
85 } else if (process->exit_status == 0) {
86 result->status = LOAD_OK;
88 result->status = LOAD_ERROR;
89 load_url_result_cleanup_local(result);
92 /* The load callback may well free the ctx, which was the
93 * talloc parent of the task. Therefore, we want to do our cleanup
96 process_release(process);
103 static void load_process_to_local_file(struct load_task *task,
104 const char **argv, int argv_local_idx)
108 task->result->local = local_name(task->result);
109 if (!task->result->local) {
110 task->result->status = LOAD_ERROR;
113 task->result->cleanup_local = true;
116 argv[argv_local_idx] = task->result->local;
118 task->process->argv = argv;
119 task->process->path = argv[0];
122 rc = process_run_async(task->process);
124 process_release(task->process);
125 task->process = NULL;
127 task->result->status = rc ? LOAD_ERROR : LOAD_ASYNC;
129 rc = process_run_sync(task->process);
130 task->result->status = rc ? LOAD_ERROR : LOAD_OK;
131 process_release(task->process);
132 task->process = NULL;
137 * pb_load_nfs - Create a mountpoint, set the local file within that
138 * mountpoint, and run the appropriate mount command
141 static void load_nfs(struct load_task *task)
143 char *mountpoint, *opts;
145 const char *argv[] = {
146 pb_system_apps.mount,
151 NULL, /* 6: mountpoint */
155 task->result->status = LOAD_ERROR;
156 mountpoint = local_name(task->result);
159 task->result->cleanup_local = true;
160 argv[6] = mountpoint;
162 rc = pb_mkdir_recursive(mountpoint);
166 opts = talloc_strdup(NULL, "ro,nolock,nodiratime");
170 opts = talloc_asprintf_append(opts, ",port=%s",
173 task->result->local = talloc_asprintf(task->result, "%s/%s",
177 task->process->path = pb_system_apps.mount;
178 task->process->argv = argv;
181 rc = process_run_async(task->process);
183 process_release(task->process);
184 task->process = NULL;
186 task->result->status = rc ? LOAD_ERROR : LOAD_ASYNC;
188 rc = process_run_sync(task->process);
189 task->result->status = rc ? LOAD_ERROR : LOAD_OK;
190 process_release(task->process);
191 task->process = NULL;
197 static void load_sftp(struct load_task *task)
199 const char *argv[] = {
201 NULL, /* 1: host:path */
202 NULL, /* 2: local file */
206 argv[1] = talloc_asprintf(task, "%s:%s",
207 task->url->host, task->url->path);
208 load_process_to_local_file(task, argv, 2);
211 static enum tftp_type check_tftp_type(void *ctx)
213 const char *argv[] = { pb_system_apps.tftp, "-V", NULL };
214 struct process *process;
217 process = process_create(ctx);
218 process->path = pb_system_apps.tftp;
219 process->argv = argv;
220 process->keep_stdout = true;
221 process_run_sync(process);
223 if (!process->stdout_buf || process->stdout_len == 0) {
224 pb_log("Can't check TFTP client type!\n");
225 type = TFTP_TYPE_BROKEN;
227 } else if (memmem(process->stdout_buf, process->stdout_len,
228 "tftp-hpa", strlen("tftp-hpa"))) {
229 pb_debug("Found TFTP client type: tftp-hpa\n");
230 type = TFTP_TYPE_HPA;
232 } else if (memmem(process->stdout_buf, process->stdout_len,
233 "BusyBox", strlen("BusyBox"))) {
234 pb_debug("Found TFTP client type: BusyBox tftp\n");
235 type = TFTP_TYPE_BUSYBOX;
238 pb_log("Unknown TFTP client type!\n");
239 type = TFTP_TYPE_BROKEN;
242 process_release(process);
246 static void load_tftp(struct load_task *task)
248 const char *port = "69";
249 const char *argv[10] = {
254 port = task->url->port;
256 if (tftp_type == TFTP_TYPE_UNKNOWN)
257 tftp_type = check_tftp_type(task);
259 if (tftp_type == TFTP_TYPE_BUSYBOX) {
262 argv[3] = NULL; /* 3: local file */
264 argv[5] = task->url->path;
265 argv[6] = task->url->host;
269 load_process_to_local_file(task, argv, 3);
271 } else if (tftp_type == TFTP_TYPE_HPA) {
274 argv[3] = task->url->host;
278 argv[7] = task->url->path;
279 argv[8] = NULL; /* 8: local file */
281 load_process_to_local_file(task, argv, 8);
284 task->result->status = LOAD_ERROR;
289 wget_no_check_certificate = 1,
293 * pb_load_wget - Loads a remote file via wget and returns the local file path.
295 * Returns the local file path in a talloc'ed character string on success,
299 static void load_wget(struct load_task *task, int flags)
301 const char *argv[] = {
304 NULL, /* 2: local file */
313 argv[i++] = "--quiet";
315 if (flags & wget_no_check_certificate)
316 argv[i++] = "--no-check-certificate";
318 argv[i] = task->url->full;
320 load_process_to_local_file(task, argv, 2);
323 /* Although we don't need to load anything for a local path (we just return
324 * the path from the file:// URL), the other load helpers will error-out on
325 * non-existant files. So, do the same here with an access() check on the local
328 static void load_local(struct load_task *task)
332 rc = access(task->url->path, F_OK);
334 task->result->status = LOAD_ERROR;
336 task->result->local = talloc_strdup(task->result,
338 task->result->status = LOAD_OK;
343 * load_url - Loads a (possibly) remote URL and returns the local file
345 * @ctx: The talloc context to associate with the returned string.
346 * @url: The remote file URL.
347 * @tempfile: An optional variable pointer to be set when a temporary local
349 * @url_cb: An optional callback pointer if the caller wants to load url
352 * Returns the local file path in a talloc'ed character string on success,
356 struct load_url_result *load_url_async(void *ctx, struct pb_url *url,
357 load_url_complete async_cb, void *async_data)
359 struct load_url_result *result;
360 struct load_task *task;
365 task = talloc_zero(ctx, struct load_task);
367 task->async = async_cb != NULL;
368 task->result = talloc_zero(ctx, struct load_url_result);
369 task->result->task = task;
370 task->process = process_create(task);
372 task->async_cb = async_cb;
373 task->async_data = async_data;
374 task->process->exit_cb = load_url_process_exit;
375 task->process->data = task;
378 switch (url->scheme) {
384 load_wget(task, wget_no_check_certificate);
400 result = task->result;
401 if (result->status == LOAD_ERROR) {
402 load_url_result_cleanup_local(task->result);
414 struct load_url_result *load_url(void *ctx, struct pb_url *url)
416 return load_url_async(ctx, url, NULL, NULL);
419 void load_url_async_cancel(struct load_url_result *res)
421 struct load_task *task = res->task;
423 /* the completion callback may have already been called; this clears
428 if (res->status == LOAD_CANCELLED)
432 assert(task->process);
434 res->status = LOAD_CANCELLED;
435 process_stop_async(task->process);