#include <assert.h>
#include <stdlib.h>
+#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
+#include <mntent.h>
#include <sys/stat.h>
#include <sys/wait.h>
return !strcmp(dev->device->id, id);
}
+static int device_match_serial(struct discover_device *dev, const char *serial)
+{
+ const char *val = discover_device_get_param(dev, "ID_SERIAL");
+ return val && !strcmp(val, serial);
+}
+
static struct discover_device *device_lookup(
struct device_handler *device_handler,
int (match_fn)(struct discover_device *, const char *),
return device_lookup(device_handler, device_match_id, id);
}
+struct discover_device *device_lookup_by_serial(
+ struct device_handler *device_handler,
+ const char *serial)
+{
+ return device_lookup(device_handler, device_match_serial, serial);
+}
+
void device_handler_destroy(struct device_handler *handler)
{
talloc_free(handler);
return 0;
}
+ handler->timeout_waiter = NULL;
+
pb_log("Timeout expired, booting default option %s\n", opt->option->id);
boot(handler, handler->default_boot_option, NULL,
return 0;
}
+static bool priority_match(struct boot_priority *prio,
+ struct discover_boot_option *opt)
+{
+ return prio->type == opt->device->device->type;
+}
+
+static int default_option_priority(struct discover_boot_option *opt)
+{
+ const struct config *config;
+ struct boot_priority *prio;
+ int i;
+
+ config = config_get();
+
+ for (i = 0; i < config->n_boot_priorities; i++) {
+ prio = &config->boot_priorities[i];
+ if (priority_match(prio, opt))
+ break;
+ }
+
+ return i;
+}
+
static void set_default(struct device_handler *handler,
struct discover_boot_option *opt)
{
- if (handler->default_boot_option)
+ if (!handler->autoboot_enabled)
return;
- if (!handler->autoboot_enabled)
+ /* Resolve any conflicts: if we have a new default option, it only
+ * replaces the current if it has a higher priority. */
+ if (handler->default_boot_option) {
+ int new_prio, cur_prio;
+
+ new_prio = default_option_priority(opt);
+ cur_prio = default_option_priority(
+ handler->default_boot_option);
+
+ if (new_prio < cur_prio) {
+ handler->default_boot_option = opt;
+ /* extend the timeout a little, so the user sees some
+ * indication of the change */
+ handler->sec_to_boot += 2;
+ }
+
return;
+ }
- handler->default_boot_option = opt;
handler->sec_to_boot = config_get()->autoboot_timeout_sec;
+ handler->default_boot_option = opt;
pb_log("Boot option %s set as default, timeout %u sec.\n",
opt->option->id, handler->sec_to_boot);
ctx = talloc(handler, struct discover_context);
ctx->device = device;
ctx->conf_url = NULL;
+ ctx->test_data = NULL;
list_init(&ctx->boot_options);
return ctx;
struct discover_device *dev, enum conf_method method)
{
struct discover_context *ctx;
+ int rc;
process_boot_option_queue(handler);
/* create our context */
ctx = device_handler_discover_context_create(handler, dev);
- mount_device(dev);
+ rc = mount_device(dev);
+ if (rc)
+ goto out;
/* run the parsers. This will populate the ctx's boot_option list. */
iterate_parsers(ctx, method);
/* add discovered stuff to the handler */
device_handler_discover_context_commit(handler, ctx);
+out:
talloc_free(ctx);
return 0;
}
#ifndef PETITBOOT_TEST
+static bool check_existing_mount(struct discover_device *dev)
+{
+ struct stat devstat, mntstat;
+ struct mntent *mnt;
+ FILE *fp;
+ int rc;
+
+ rc = stat(dev->device_path, &devstat);
+ if (rc) {
+ pb_debug("%s: stat failed: %s\n", __func__, strerror(errno));
+ return false;
+ }
+
+ if (!S_ISBLK(devstat.st_mode)) {
+ pb_debug("%s: %s isn't a block device?\n", __func__,
+ dev->device_path);
+ return false;
+ }
+
+ fp = fopen("/proc/self/mounts", "r");
+
+ for (;;) {
+ mnt = getmntent(fp);
+ if (!mnt)
+ break;
+
+ if (!mnt->mnt_fsname || mnt->mnt_fsname[0] != '/')
+ continue;
+
+ rc = stat(mnt->mnt_fsname, &mntstat);
+ if (rc)
+ continue;
+
+ if (!S_ISBLK(mntstat.st_mode))
+ continue;
+
+ if (mntstat.st_rdev == devstat.st_rdev) {
+ dev->mount_path = talloc_strdup(dev, mnt->mnt_dir);
+ dev->mounted_rw = !!hasmntopt(mnt, "rw");
+ dev->mounted = true;
+ dev->unmount = false;
+
+ pb_debug("%s: %s is already mounted (r%c) at %s\n",
+ __func__, dev->device_path,
+ dev->mounted_rw ? 'w' : 'o',
+ mnt->mnt_dir);
+ break;
+ }
+ }
+
+ fclose(fp);
+
+ return mnt != NULL;
+}
+
static int mount_device(struct discover_device *dev)
{
int rc;
if (!dev->device_path)
return -1;
- if (!dev->mount_path)
- dev->mount_path = join_paths(dev, mount_base(),
- dev->device_path);
+ if (dev->mounted)
+ return 0;
- if (pb_mkdir_recursive(dev->mount_path))
+ if (check_existing_mount(dev))
+ return 0;
+
+ dev->mount_path = join_paths(dev, mount_base(),
+ dev->device_path);
+
+ if (pb_mkdir_recursive(dev->mount_path)) {
pb_log("couldn't create mount directory %s: %s\n",
dev->mount_path, strerror(errno));
+ goto err_free;
+ }
rc = process_run_simple(dev, pb_system_apps.mount,
dev->device_path, dev->mount_path,
"-o", "ro", NULL);
-
- if (!rc)
+ if (!rc) {
+ dev->mounted = true;
+ dev->mounted_rw = false;
+ dev->unmount = true;
return 0;
+ }
/* Retry mount without ro option. */
rc = process_run_simple(dev, pb_system_apps.mount,
dev->device_path, dev->mount_path, NULL);
- if (!rc)
+ if (!rc) {
+ dev->mounted = true;
+ dev->mounted_rw = true;
+ dev->unmount = true;
return 0;
+ }
pb_rmdir_recursive(mount_base(), dev->mount_path);
+err_free:
+ talloc_free(dev->mount_path);
+ dev->mount_path = NULL;
return -1;
}
{
int status;
- if (!dev->mount_path)
+ if (!dev->mounted || !dev->unmount)
return 0;
status = process_run_simple(dev, pb_system_apps.umount,
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
return -1;
+ dev->mounted = false;
+ talloc_free(dev->mount_path);
+ dev->mount_path = NULL;
+
pb_rmdir_recursive(mount_base(), dev->mount_path);
return 0;
}
+
+int device_request_write(struct discover_device *dev, bool *release)
+{
+ int rc;
+
+ *release = false;
+
+ if (!dev->mounted)
+ return -1;
+
+ if (dev->mounted_rw)
+ return 0;
+
+ rc = process_run_simple(dev, pb_system_apps.mount, dev->mount_path,
+ "-o", "remount,rw", NULL);
+ if (rc)
+ return -1;
+
+ dev->mounted_rw = true;
+ *release = true;
+ return 0;
+}
+
+void device_release_write(struct discover_device *dev, bool release)
+{
+ if (!release)
+ return;
+
+ process_run_simple(dev, pb_system_apps.mount, dev->mount_path,
+ "-o", "remount,ro", NULL);
+ dev->mounted_rw = false;
+}
+
#else
static int umount_device(struct discover_device *dev __attribute__((unused)))
return 0;
}
+int device_request_write(struct discover_device *dev __attribute__((unused)),
+ bool *release)
+{
+ *release = true;
+ return 0;
+}
+
+void device_release_write(struct discover_device *dev __attribute__((unused)),
+ bool release __attribute__((unused)))
+{
+}
+
#endif