+static void update_status(boot_status_fn fn, void *arg, int type,
+ char *message)
+{
+ struct boot_status status;
+
+ status.type = type;
+ status.message = message;
+ status.progress = -1;
+ status.detail = NULL;
+
+ fn(arg, &status);
+}
+
+static void boot_hook_update_param(void *ctx, struct boot_task *task,
+ const char *name, const char *value)
+{
+ struct p {
+ const char *name;
+ char **p;
+ } *param, params[] = {
+ { "boot_image", &task->local_image },
+ { "boot_initrd", &task->local_initrd },
+ { "boot_dtb", &task->local_dtb },
+ { "boot_args", &task->args },
+ { NULL, NULL },
+ };
+
+ for (param = params; param->name; param++) {
+ if (strcmp(param->name, name))
+ continue;
+
+ *param->p = talloc_strdup(ctx, value);
+ return;
+ }
+}
+
+static void boot_hook_update(struct boot_task *task, const char *hookname,
+ char *buf)
+{
+ char *line, *name, *val, *sep;
+ char *saveptr;
+
+ for (;; buf = NULL) {
+
+ line = strtok_r(buf, "\n", &saveptr);
+ if (!line)
+ break;
+
+ sep = strchr(line, '=');
+ if (!sep)
+ continue;
+
+ *sep = '\0';
+ name = line;
+ val = sep + 1;
+
+ boot_hook_update_param(task, task, name, val);
+
+ pb_log("boot hook %s specified %s=%s\n",
+ hookname, name, val);
+ }
+}
+
+static void boot_hook_setenv(struct boot_task *task)
+{
+ unsetenv("boot_image");
+ unsetenv("boot_initrd");
+ unsetenv("boot_dtb");
+ unsetenv("boot_args");
+
+ setenv("boot_image", task->local_image, 1);
+ if (task->local_initrd)
+ setenv("boot_initrd", task->local_initrd, 1);
+ if (task->local_dtb)
+ setenv("boot_dtb", task->local_dtb, 1);
+ if (task->args)
+ setenv("boot_args", task->args, 1);
+}
+
+static int hook_filter(const struct dirent *dirent)
+{
+ return dirent->d_type == DT_REG || dirent->d_type == DT_LNK;
+}
+
+static int hook_cmp(const struct dirent **a, const struct dirent **b)
+{
+ return strcmp((*a)->d_name, (*b)->d_name);
+}
+
+static void run_boot_hooks(struct boot_task *task, boot_status_fn status_fn,
+ void *status_arg)