-#define _GNU_SOURCE
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <talloc/talloc.h>
#include <system/system.h>
#include <log/log.h>
#include "paths.h"
+#include "device-handler.h"
#define DEVICE_MOUNT_BASE (LOCAL_STATE_DIR "/petitboot/mnt")
void *async_data;
};
+static inline bool have_busybox(void)
+{
+#ifdef WITH_BUSYBOX
+ return true;
+#else
+ return false;
+#endif
+}
+
const char *mount_base(void)
{
return DEVICE_MOUNT_BASE;
return full_path;
}
+#ifndef PETITBOOT_TEST
static char *local_name(void *ctx)
{
- char *tmp, *ret;
+ char *ret, tmp[] = "/tmp/pb-XXXXXX";
+ mode_t oldmask;
+ int fd;
- tmp = tempnam(NULL, "pb-");
+ oldmask = umask(0644);
+ fd = mkstemp(tmp);
+ umask(oldmask);
- if (!tmp)
+ if (fd < 0)
return NULL;
+ close(fd);
+
ret = talloc_strdup(ctx, tmp);
- free(tmp);
return ret;
}
if (result->status == LOAD_CANCELLED) {
load_url_result_cleanup_local(result);
- } else if (process->exit_status == 0) {
+ } else if (process_exit_ok(process)) {
result->status = LOAD_OK;
} else {
result->status = LOAD_ERROR;
cb(result, data);
}
+/*
+ * Callback to retrieve progress information from Busybox utilities.
+ * Busybox utilities use a common progress bar format which progress percentage
+ * and current size can be can be parsed from.
+ */
+static int busybox_progress_cb(void *arg)
+{
+ const char *busybox_fmt = "%*s %u%*[%* |]%u%c %*u:%*u:%*u ETA\n";
+ struct process_info *procinfo = arg;
+ char *n, *s, suffix, *line = NULL;
+ struct device_handler *handler;
+ unsigned int percentage, size;
+ struct process *p;
+ int rc;
+
+ if (!arg)
+ return -1;
+
+ p = procinfo_get_process(procinfo);
+ handler = p->stdout_data;
+
+ rc = process_stdout_custom(procinfo, &line);
+
+ if (rc) {
+ /* Unregister ourselves from progress tracking */
+ device_handler_status_download_remove(handler, procinfo);
+ }
+
+ if (rc || !line)
+ return rc;
+
+ rc = sscanf(line, busybox_fmt, &percentage, &size, &suffix);
+
+ /*
+ * Many unrecognised lines are partial updates. If we see a partial
+ * line with a newline character, see if we can match a valid line
+ * at the end of stdout_buf
+ */
+ if (rc != 3) {
+ n = strchr(line, '\n');
+ if (n)
+ for (s = n - 1; s >= p->stdout_buf; s--)
+ if (*s == '\n') {
+ rc = sscanf(s + 1, busybox_fmt,
+ &percentage, &size, &suffix);
+ break;
+ }
+ }
+
+ if (rc != 3)
+ percentage = size = 0;
+
+ device_handler_status_download(handler, procinfo,
+ percentage, size, suffix);
+
+ return 0;
+}
+
+
+
static void load_process_to_local_file(struct load_task *task,
const char **argv, int argv_local_idx)
{
task->result->status = rc ? LOAD_ERROR : LOAD_ASYNC;
} else {
rc = process_run_sync(task->process);
- task->result->status = rc ? LOAD_ERROR : LOAD_OK;
+ if (rc || !process_exit_ok(task->process))
+ task->result->status = LOAD_ERROR;
+ else
+ task->result->status = LOAD_OK;
process_release(task->process);
task->process = NULL;
}
const char *argv[] = { pb_system_apps.tftp, "-V", NULL };
struct process *process;
enum tftp_type type;
+ int rc;
process = process_create(ctx);
process->path = pb_system_apps.tftp;
process->argv = argv;
process->keep_stdout = true;
- process_run_sync(process);
+ process->add_stderr = true;
+ rc = process_run_sync(process);
- if (!process->stdout_buf || process->stdout_len == 0) {
+ if (rc || !process->stdout_buf || process->stdout_len == 0) {
pb_log("Can't check TFTP client type!\n");
type = TFTP_TYPE_BROKEN;
}
enum wget_flags {
- wget_empty = 0,
- wget_no_check_certificate = 1,
+ wget_empty = 0x1,
+ wget_no_check_certificate = 0x2,
+ wget_verbose = 0x4,
};
/**
pb_system_apps.wget,
"-O",
NULL, /* 2: local file */
- NULL,
- NULL,
+ NULL, /* 3 (optional): --quiet */
+ NULL, /* 4 (optional): --no-check-certificate */
+ NULL, /* 5: URL */
NULL,
};
int i;
+ if (task->process->stdout_cb)
+ flags |= wget_verbose;
+
i = 3;
-#if !defined(DEBUG)
- argv[i++] = "--quiet";
+#if defined(DEBUG)
+ flags |= wget_verbose;
#endif
+ if ((flags & wget_verbose) == 0)
+ argv[i++] = "--quiet";
+
if (flags & wget_no_check_certificate)
argv[i++] = "--no-check-certificate";
*/
struct load_url_result *load_url_async(void *ctx, struct pb_url *url,
- load_url_complete async_cb, void *async_data)
+ load_url_complete async_cb, void *async_data,
+ waiter_cb stdout_cb, void *stdout_data)
{
struct load_url_result *result;
struct load_task *task;
+ int flags = 0;
if (!url)
return NULL;
task->async = async_cb != NULL;
task->result = talloc_zero(ctx, struct load_url_result);
task->result->task = task;
+ task->result->url = url;
task->process = process_create(task);
if (task->async) {
task->async_cb = async_cb;
task->async_data = async_data;
task->process->exit_cb = load_url_process_exit;
task->process->data = task;
+ task->process->stdout_cb = stdout_cb;
+ task->process->stdout_data = stdout_data;
+ }
+
+ if (!stdout_cb && stdout_data && have_busybox())
+ task->process->stdout_cb = busybox_progress_cb;
+
+ /* Make sure we save output for any task that has a custom handler */
+ if (task->process->stdout_cb) {
+ task->process->add_stderr = true;
+ task->process->keep_stdout = true;
}
switch (url->scheme) {
case pb_url_ftp:
case pb_url_http:
- load_wget(task, 0);
+ load_wget(task, flags);
break;
case pb_url_https:
- load_wget(task, wget_no_check_certificate);
+ flags |= wget_no_check_certificate;
+ load_wget(task, flags);
break;
case pb_url_nfs:
load_nfs(task);
struct load_url_result *load_url(void *ctx, struct pb_url *url)
{
- return load_url_async(ctx, url, NULL, NULL);
+ return load_url_async(ctx, url, NULL, NULL, NULL, NULL);
}
void load_url_async_cancel(struct load_url_result *res)
res->status = LOAD_CANCELLED;
process_stop_async(task->process);
}
+
+#else
+
+static void __attribute__((unused)) load_local(
+ struct load_task *task __attribute__((unused)))
+{
+}
+static void __attribute__((unused)) load_wget(
+ struct load_task *task __attribute__((unused)),
+ int flags __attribute__((unused)))
+{
+}
+static void __attribute__((unused)) load_tftp(
+ struct load_task *task __attribute__((unused)))
+{
+}
+static void __attribute__((unused)) load_sftp(
+ struct load_task *task __attribute__((unused)))
+{
+}
+static void __attribute__((unused)) load_nfs(
+ struct load_task *task __attribute__((unused)))
+{
+}
+static void __attribute__((unused)) load_url_process_exit(
+ struct process *process __attribute__((unused)))
+{
+}
+
+#endif