9
0
Fork 0

boot command: make more flexible

With this we can do 'boot <name>' where name is one of:

 - a filename under /env/boot/
 - a full path to a boot script
 - a device name
 - a partition name under /dev/
 - a full path to a directory which
   - contains boot scripts, or
   - contains a loader/entries/ directory containing bootspec entries

Multiple names can be given, they are tried in order. So any mixture
between bootspec entries and bootscripts can be given. bootspec entries
can now also be given as a path to a directory containing bootspec entries.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
This commit is contained in:
Sascha Hauer 2013-11-01 15:04:43 +01:00
parent 4bcb3897fa
commit f63e04f52c
1 changed files with 245 additions and 178 deletions

View File

@ -28,123 +28,9 @@
#include <linux/stat.h>
static int boot_script(char *path);
static int verbose;
static int dryrun;
static void bootsource_action(struct menu *m, struct menu_entry *me)
{
struct blspec_entry *be = container_of(me, struct blspec_entry, me);
int ret;
if (be->scriptpath) {
ret = boot_script(be->scriptpath);
} else {
if (IS_ENABLED(CONFIG_BLSPEC))
ret = blspec_boot(be, 0, 0);
else
ret = -ENOSYS;
}
if (ret)
printf("Booting failed with: %s\n", strerror(-ret));
printf("Press any key to continue\n");
read_key();
}
static int bootsources_menu_env_entries(struct blspec *blspec)
{
const char *path = "/env/boot";
DIR *dir;
struct dirent *d;
struct blspec_entry *be;
dir = opendir(path);
if (!dir)
return -errno;
while ((d = readdir(dir))) {
if (*d->d_name == '.')
continue;
be = blspec_entry_alloc(blspec);
be->me.type = MENU_ENTRY_NORMAL;
be->scriptpath = asprintf("/env/boot/%s", d->d_name);
be->me.display = xstrdup(d->d_name);
}
closedir(dir);
return 0;
}
static struct blspec *bootentries_collect(void)
{
struct blspec *blspec;
blspec = blspec_alloc();
blspec->menu->display = asprintf("boot");
bootsources_menu_env_entries(blspec);
if (IS_ENABLED(CONFIG_BLSPEC))
blspec_scan_devices(blspec);
return blspec;
}
static void bootsources_menu(void)
{
struct blspec *blspec = NULL;
struct blspec_entry *entry;
struct menu_entry *back_entry;
if (!IS_ENABLED(CONFIG_MENU)) {
printf("no menu support available\n");
return;
}
blspec = bootentries_collect();
blspec_for_each_entry(blspec, entry) {
entry->me.action = bootsource_action;
menu_add_entry(blspec->menu, &entry->me);
}
back_entry = xzalloc(sizeof(*back_entry));
back_entry->display = "back";
back_entry->type = MENU_ENTRY_NORMAL;
back_entry->non_re_ent = 1;
menu_add_entry(blspec->menu, back_entry);
menu_show(blspec->menu);
free(back_entry);
blspec_free(blspec);
}
static void bootsources_list(void)
{
struct blspec *blspec;
struct blspec_entry *entry;
blspec = bootentries_collect();
printf("%-20s %-20s %s\n", "device", "hwdevice", "title");
printf("%-20s %-20s %s\n", "------", "--------", "-----");
blspec_for_each_entry(blspec, entry) {
if (entry->scriptpath)
printf("%-40s %s\n", basename(entry->scriptpath), entry->me.display);
else
printf("%s\n", entry->me.display);
}
blspec_free(blspec);
}
/*
* Start a single boot script. 'path' is a full path to a boot script.
*/
@ -156,8 +42,6 @@ static int boot_script(char *path)
.initrd_address = UIMAGE_SOME_ADDRESS,
};
printf("booting %s...\n", basename(path));
globalvar_set_match("linux.bootargs.dyn.", "");
globalvar_set_match("bootm.", "");
@ -184,79 +68,258 @@ out:
return ret;
}
/*
* boot a script. 'name' can either be a filename under /env/boot/,
* a full path to a boot script or a path to a directory. This function
* returns a negative error on failure, or 0 on a successful dryrun boot.
*/
static int boot(const char *name)
static int boot_entry(struct blspec_entry *be)
{
char *path;
DIR *dir;
struct dirent *d;
struct stat s;
int ret;
if (*name == '/')
path = xstrdup(name);
else
path = asprintf("/env/boot/%s", name);
ret = stat(path, &s);
if (ret) {
if (!IS_ENABLED(CONFIG_BLSPEC)) {
pr_err("%s: %s\n", path, strerror(-ret));
goto out;
}
ret = blspec_boot_devicename(name, verbose, dryrun);
pr_err("%s: %s\n", name, strerror(-ret));
goto out;
if (be->scriptpath) {
ret = boot_script(be->scriptpath);
} else {
if (IS_ENABLED(CONFIG_BLSPEC))
ret = blspec_boot(be, verbose, dryrun);
else
ret = -ENOSYS;
}
if (S_ISREG(s.st_mode)) {
ret = boot_script(path);
goto out;
return ret;
}
static void bootsource_action(struct menu *m, struct menu_entry *me)
{
struct blspec_entry *be = container_of(me, struct blspec_entry, me);
int ret;
ret = boot_entry(be);
if (ret)
printf("Booting failed with: %s\n", strerror(-ret));
printf("Press any key to continue\n");
read_key();
}
/*
* bootscript_create_entry - create a boot entry from a script name
*/
static int bootscript_create_entry(struct blspec *blspec, const char *name)
{
struct blspec_entry *be;
be = blspec_entry_alloc(blspec);
be->me.type = MENU_ENTRY_NORMAL;
be->scriptpath = xstrdup(name);
be->me.display = xstrdup(basename(be->scriptpath));
return 0;
}
/*
* bootscript_scan_path - create boot entries from a path
*
* path can either be a full path to a bootscript or a full path to a diretory
* containing bootscripts.
*/
static int bootscript_scan_path(struct blspec *blspec, const char *path)
{
struct stat s;
DIR *dir;
struct dirent *d;
int ret;
int found = 0;
ret = stat(path, &s);
if (ret)
return ret;
if (!S_ISDIR(s.st_mode)) {
ret = bootscript_create_entry(blspec, path);
if (ret)
return ret;
return 1;
}
dir = opendir(path);
if (!dir) {
ret = -errno;
printf("cannot open %s: %s\n", path, strerror(-errno));
goto out;
}
if (!dir)
return -errno;
while ((d = readdir(dir))) {
char *file;
struct stat s;
char *bootscript_path;
if (*d->d_name == '.')
continue;
file = asprintf("%s/%s", path, d->d_name);
ret = stat(file, &s);
if (ret) {
free(file);
continue;
}
if (!S_ISREG(s.st_mode)) {
free(file);
continue;
}
ret = boot_script(file);
free(file);
if (!ret)
break;
bootscript_path = asprintf("%s/%s", path, d->d_name);
bootscript_create_entry(blspec, bootscript_path);
found++;
free(bootscript_path);
}
ret = found;
closedir(dir);
out:
free(path);
return ret;
}
/*
* bootentry_parse_one - create boot entries from a name
*
* name can be:
* - a name of a boot script under /env/boot
* - a full path of a boot script
* - a device name
* - a cdev name
* - a full path of a directory containing bootloader spec entries
* - a full path of a directory containing bootscripts
*
* Returns the number of entries found or a negative error code.
*/
static int bootentry_parse_one(struct blspec *blspec, const char *name)
{
int found = 0, ret;
if (IS_ENABLED(CONFIG_BLSPEC)) {
ret = blspec_scan_devicename(blspec, name);
if (ret > 0)
found += ret;
ret = blspec_scan_directory(blspec, name);
if (ret > 0)
found += ret;
}
if (!found) {
char *path;
if (*name != '/')
path = asprintf("/env/boot/%s", name);
else
path = xstrdup(name);
ret = bootscript_scan_path(blspec, path);
if (ret > 0)
found += ret;
free(path);
}
return found;
}
/*
* bootentries_collect - collect bootentries from an array of names
*/
static struct blspec *bootentries_collect(char *entries[], int num_entries)
{
struct blspec *blspec;
int i;
blspec = blspec_alloc();
blspec->menu->display = asprintf("boot");
if (!num_entries)
bootscript_scan_path(blspec, "/env/boot");
if (IS_ENABLED(CONFIG_BLSPEC) && !num_entries)
blspec_scan_devices(blspec);
for (i = 0; i < num_entries; i++)
bootentry_parse_one(blspec, entries[i]);
return blspec;
}
/*
* bootsources_menu - show a menu from an array of names
*/
static void bootsources_menu(char *entries[], int num_entries)
{
struct blspec *blspec = NULL;
struct blspec_entry *entry;
struct menu_entry *back_entry;
if (!IS_ENABLED(CONFIG_MENU)) {
printf("no menu support available\n");
return;
}
blspec = bootentries_collect(entries, num_entries);
blspec_for_each_entry(blspec, entry) {
entry->me.action = bootsource_action;
menu_add_entry(blspec->menu, &entry->me);
}
back_entry = xzalloc(sizeof(*back_entry));
back_entry->display = "back";
back_entry->type = MENU_ENTRY_NORMAL;
back_entry->non_re_ent = 1;
menu_add_entry(blspec->menu, back_entry);
menu_show(blspec->menu);
free(back_entry);
blspec_free(blspec);
}
/*
* bootsources_list - list boot entries from an array of names
*/
static void bootsources_list(char *entries[], int num_entries)
{
struct blspec *blspec;
struct blspec_entry *entry;
blspec = bootentries_collect(entries, num_entries);
printf("%-20s %-20s %s\n", "device", "hwdevice", "title");
printf("%-20s %-20s %s\n", "------", "--------", "-----");
blspec_for_each_entry(blspec, entry) {
if (entry->scriptpath)
printf("%-40s %s\n", basename(entry->scriptpath), entry->me.display);
else
printf("%s\n", entry->me.display);
}
blspec_free(blspec);
}
/*
* boot a script or a bootspec entry. 'name' can be:
* - a filename under /env/boot/
* - a full path to a boot script
* - a device name
* - a partition name under /dev/
* - a full path to a directory which
* - contains boot scripts, or
* - contains a loader/entries/ directory containing bootspec entries
*
* Returns a negative error on failure, or 0 on a successful dryrun boot.
*/
static int boot(const char *name)
{
struct blspec *blspec;
struct blspec_entry *entry;
int ret = -ENOENT;
blspec = blspec_alloc();
ret = bootentry_parse_one(blspec, name);
if (ret < 0)
return ret;
if (!ret) {
printf("Nothing bootable found on %s\n", name);
return -ENOENT;
}
blspec_for_each_entry(blspec, entry) {
printf("booting %s\n", entry->me.display);
ret = boot_entry(entry);
if (!ret)
break;
printf("booting %s failed: %s\n", entry->me.display, strerror(-ret));
}
return ret;
}
@ -288,12 +351,12 @@ static int do_boot(int argc, char *argv[])
}
if (do_list) {
bootsources_list();
bootsources_list(&argv[optind], argc - optind);
return 0;
}
if (do_menu) {
bootsources_menu();
bootsources_menu(&argv[optind], argc - optind);
return 0;
}
@ -337,10 +400,14 @@ BAREBOX_CMD_HELP_START(boot)
BAREBOX_CMD_HELP_USAGE("boot [OPTIONS] [BOOTSRC...]\n")
BAREBOX_CMD_HELP_SHORT("Boot an operating system.\n")
BAREBOX_CMD_HELP_SHORT("[BOOTSRC...] can be:\n")
BAREBOX_CMD_HELP_SHORT("- a filename from /env/boot/\n")
BAREBOX_CMD_HELP_SHORT("- a full path to a file\n")
BAREBOX_CMD_HELP_SHORT("- a path to a directory. All files in this directory are treated\n")
BAREBOX_CMD_HELP_SHORT(" as boot scripts.\n")
BAREBOX_CMD_HELP_SHORT("- a filename under /env/boot/\n")
BAREBOX_CMD_HELP_SHORT("- a full path to a boot script\n")
BAREBOX_CMD_HELP_SHORT("- a device name\n")
BAREBOX_CMD_HELP_SHORT("- a partition name under /dev/\n")
BAREBOX_CMD_HELP_SHORT("- a full path to a directory which\n")
BAREBOX_CMD_HELP_SHORT(" - contains boot scripts, or\n")
BAREBOX_CMD_HELP_SHORT(" - contains a loader/entries/ directory containing bootspec entries\n")
BAREBOX_CMD_HELP_SHORT("\n")
BAREBOX_CMD_HELP_SHORT("Multiple bootsources may be given which are probed in order until\n")
BAREBOX_CMD_HELP_SHORT("one succeeds.\n")
BAREBOX_CMD_HELP_SHORT("\nOptions:\n")