/* * kernel-install - install a kernel according to the bootloader spec: * http://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/ * * Copyright (C) 2013 Sascha Hauer, Pengutronix * * This tool is useful for installing kernels in a bootloader spec * conformant way. It can be used to install kernels for the currently * running system, but also to install kernels for another system which * is available as a removable media such as an SD card. * * Some examples: * * kernel-install --add --kernel-version=3.11 --kernel=/somewhere/zImage \ * --title "Linux-3.11" * * This is the simplest example. It assumes we want to install a kernel for the * currently running system. Usually the kernel should get some commandline * options which can be passed using the -o option. Devicetree and initrd can be * specified with --devicetree= or --initrd=. * * For preparing boot media from another host (or the same host, but another * rootfs) things get slightly more complicated. Apart from the image files * kernel-install generally needs a machine-id (which is, in native mode, read * from /etc/machine-id) and access to /boot of newly installed entry. * /boot can be specified in different ways: * * --boot=/boot - specify the path where /boot is mounted * --boot=/dev/sdd1 - specify the partition which contains /boot. * It is mounted using pmount or mount * --device=/dev/sdd - If this option is given kernel-install tries * to find /boot on this device using the mechanisms * described in the bootloader spec. * * machine-id can be specified with: * --machine-id= - explicitly specify a machine-id * --root=/root or * --root=/dev/sdd2 - specify where the root of the installed system * can be found. The machine id is then taken * from /etc/machine-id from this filesystem/path * * Optionally kernel-install can automatically generate a root=PARTUUID= kernel * parameter for the kernel to find its root filesystem. This is done with the * --add-root-option parameter. Additionally the --device= parameter must be * specified so that kernel-install can determine the UUID of the device. * * Now for an example using most of the available features: * * kernel-install --device=/dev/sdd --root=/dev/sdd2 --title="Linux-3.12" \ * --kernel-version="3.12" --kernel=/some/zImage \ * --devicetree=/some/devicetree --initrd=/some/initrd \ * --add-root-option --options="console=ttyS0,115200" * * This would install a kernel on /dev/sdd. The /boot partition would be found * automatically, the root partition has to be specified due to the usage of * --add-root-option * * BUGS: * - Currently only DOS partition tables are supported. There's no support * for GPT yet. * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int verbose; static int force; static int interactive = 1; static int remove_kernel_num = -1; static int set_default_num = -1; static int set_once_num = -1; static char *host_root_path, *kernel_image, *options, *device_path; static char *host_boot_path, *kernel_version, *title, *machine_id; static char *initrd_image, *devicetree_image; static char *host_mount_root_path, *host_mount_boot_path; static uint32_t nt_disk_signature; static int root_partition_num; struct loader_entry { char *title; char *machine_id; char *options; char *kernel; char *devicetree; char *initrd; char *version; char *host_path; char *config_file; int num; struct loader_entry *next; }; static struct loader_entry *loader_entries; static void loader_entry_var_set(struct loader_entry *e, const char *name, char *val) { if (!strcmp(name, "title")) e->title = val; else if (!strcmp(name, "machine-id")) e->machine_id = val; else if (!strcmp(name, "options")) e->options = val; else if (!strcmp(name, "linux")) e->kernel = val; else if (!strcmp(name, "devicetree")) e->devicetree = val; else if (!strcmp(name, "initrd")) e->initrd = val; else if (!strcmp(name, "version")) e->version = val; } static struct loader_entry *loader_entry_open(const char *path) { FILE *f; struct loader_entry *e; int ret; f = fopen(path, "r"); if (!f) return NULL; e = calloc(sizeof(*e), 1); e->host_path = strdup(path); while (1) { char *line = NULL; char *name, *val, *end; size_t s; ret = getline(&line, &s, f); if (ret < 0) break; if (line[strlen(line) - 1] == '\n') line[strlen(line) - 1] = '\0'; name = line; end = name; while (*end && (*end != ' ' && *end != '\t')) end++; if (*line == '#') { free(line); continue; } if (!*end) { loader_entry_var_set(e, name, NULL); continue; } *end = 0; end++; while (*end == ' ' || *end == '\t') end++; if (!*end) { loader_entry_var_set(e, name, NULL); continue; } val = end; loader_entry_var_set(e, name, val); } fclose(f); return e; } /* * printf wrapper around 'system' */ static int systemp(const char *fmt, ...) { va_list args; char *buf; int ret; va_start (args, fmt); ret = vasprintf(&buf, fmt, args); va_end (args); if (ret < 0) { fprintf(stderr, "out of memory\n"); exit(1); } if (verbose) fprintf(stderr, "executing command: %s\n", buf); ret = system(buf); if (ret > 0) ret = WEXITSTATUS(ret); free(buf); return ret; } static void *safe_asprintf(const char *fmt, ...) { va_list args; char *buf = NULL; int ret; va_start (args, fmt); ret = vasprintf(&buf, fmt, args); va_end (args); if (ret < 0) { fprintf(stderr, "out of memory\n"); exit(1); } return buf; } static void verbose_printf(const char *fmt, ...) { va_list args; if (!verbose) return; va_start (args, fmt); vprintf(fmt, args); va_end (args); } static int make_directory(const char *dir) { char *s = strdup(dir); char *path = s; char c; int ret = 0; do { c = 0; /* Bypass leading non-'/'s and then subsequent '/'s. */ while (*s) { if (*s == '/') { do { ++s; } while (*s == '/'); c = *s; /* Save the current char */ *s = 0; /* and replace it with nul. */ break; } ++s; } if (mkdir(path, 0777) < 0) { /* If we failed for any other reason than the directory * already exists, output a diagnostic and return -1.*/ if (errno != EEXIST) { ret = -errno; break; } } if (!c) goto out; /* Remove any inserted nul from the path (recursive mode). */ *s = c; } while (1); out: free(path); if (ret) errno = -ret; return ret; } static int append_option(const char *fmt, ...) { va_list args; char *buf; int ret; va_start (args, fmt); ret = vasprintf(&buf, fmt, args); va_end (args); if (ret < 0) { fprintf(stderr, "out of memory\n"); exit (1); } if (options) { char *new_options = safe_asprintf("%s %s", options, buf); free(options); free(buf); options = new_options; } else { options = buf; } return 0; } static char *get_mount_path(char *path) { FILE *f; int ret; char *out_path = NULL; f = fopen("/proc/mounts", "r"); if (!f) { fprintf(stderr, "Cannot open /proc/mounts: %s\n", strerror(errno)); return NULL; } while (1) { char *line = NULL, *delim; size_t insize; ret = getline(&line, &insize, f); if (ret < 0) break; delim = strchr(line, ' '); if (!delim) { free(line); continue; } *delim = 0; if (strcmp(line, path)) { free(line); continue; } delim++; out_path = delim; delim = strchr(delim, ' '); if (!delim) { free(line); out_path = NULL; break; } *delim = 0; break; } fclose(f); if (out_path) return strdup(out_path); else return NULL; } enum mount_type { MOUNT_UNKNOWN, MOUNT_PMOUNT, MOUNT_MOUNT, MOUNT_ERROR, }; static enum mount_type get_mount_type(void) { static enum mount_type mount_type = MOUNT_UNKNOWN; int ret; uid_t uid; if (mount_type != MOUNT_UNKNOWN) return mount_type; ret = systemp("which pmount"); if (!ret) { mount_type = MOUNT_PMOUNT; goto out; } verbose_printf("pmount not found\n"); uid = getuid(); if (uid == 0) { mount_type = MOUNT_MOUNT; goto out; } fprintf(stderr, "'pmount' not found and I am not root. Unable to mount\n"); mount_type = MOUNT_ERROR; out: return mount_type; } static char *mount_path_pmount(char *in_path) { char *out_path; int ret; ret = systemp("pmount %s", in_path); if (ret) { fprintf(stderr, "failed to pmount %s\n", in_path); return NULL; } out_path = safe_asprintf("/media/%s", basename(in_path)); return out_path; } static char *mount_path_mount(char *in_path) { char *out_path, *str; int ret; str = safe_asprintf("/tmp/kernel-install-%s-XXXXXX", basename(in_path)); out_path = mkdtemp(str); if (!out_path) { fprintf(stderr, "unable to create temporary directory: %s\n", strerror(errno)); free(str); return NULL; } ret = systemp("mount %s %s", in_path, out_path); if (ret) { fprintf(stderr, "failed to mount %s: %s\n", in_path, strerror(errno)); rmdir(out_path); free(out_path); return NULL; } return out_path; } /* * mount_path - make a device or directory available. * @in_path: the input device or directory * @newmount: if this function mounts a device, this variable is true * on exit. * * returns the path under which the device is available. * * We do our best to make a device or directory available. If the input * path is a directory, just return it. If it is a block device and the * device is already mounted according to /proc/mounts, return the path * where it's mounted. If it's not mounted already try to mount it. We * first try pmount if that's available. If not, see if we are root and * can use regular 'mount'. */ static char *mount_path(char *in_path, int *newmount) { struct stat s; int ret; char *out_path; *newmount = 0; ret = stat(in_path, &s); if (ret) { fprintf(stderr, "Cannot mount %s: %s\n", in_path, strerror(errno)); return NULL; } if (S_ISDIR(s.st_mode)) return strdup(in_path); if (!S_ISBLK(s.st_mode)) { fprintf(stderr, "%s is not a directory and not a block device\n", in_path); return NULL; } out_path = get_mount_path(in_path); if (out_path) { verbose_printf("%s already mounted at %s\n", in_path, out_path); return out_path; } switch (get_mount_type()) { default: case MOUNT_ERROR: return NULL; case MOUNT_PMOUNT: out_path = mount_path_pmount(in_path); if (out_path) { *newmount = 1; return out_path; } fprintf(stderr, "cannot mount %s\n", in_path); return NULL; case MOUNT_MOUNT: out_path = mount_path_mount(in_path); if (out_path) { *newmount = 1; return out_path; } fprintf(stderr, "cannot mount %s\n", in_path); return NULL; } return NULL; } static void detect_root_partition_num(char *device) { struct stat s; int ret; char digit; ret = stat(device, &s); if (ret) { fprintf(stderr, "%s: %s\n", device, strerror(errno)); return; } if (!S_ISBLK(s.st_mode)) return; digit = device[strlen(device) - 1]; if (!isdigit(digit)) return; root_partition_num = digit - '0'; printf("rootnum: %d\n", root_partition_num); } static void umount_path(const char *path) { switch (get_mount_type()) { case MOUNT_PMOUNT: systemp("pumount %s", path); break; case MOUNT_MOUNT: systemp("umount %s", path); break; default: case MOUNT_ERROR: break; } } static int determine_root_boot_path(const char *device_path) { unsigned char *buf; int ret, fd, i; char *partname; struct stat s; buf = malloc(512); if (!buf) return -ENOMEM; fd = open(device_path, O_RDONLY); if (fd < 0) { perror("open"); return -errno; } ret = read(fd, buf, 512); if (ret < 512) perror("read"); close(fd); if (ret < 512) return -errno; if (buf[510] != 0x55 || buf[511] != 0xaa) { fprintf(stderr, "not a DOS bootsector\n"); return EINVAL; } nt_disk_signature = buf[440] | (buf[441] << 8) | (buf[442] << 16) | (buf[443] << 24); for (i = 0; i < 4; i++) { uint8_t type = buf[446 + 4 + i * 64]; if (type == 0xea) { verbose_printf("using partition %d as /boot\n", i); break; } } if (i == 4 && !host_boot_path) { fprintf(stderr, "cannot find a valid /boot partition on %s\n", device_path); return -EINVAL; } /* /dev/sdgx */ partname = safe_asprintf("%s%c", device_path, '1' + i); ret = stat(partname, &s); if (!ret) { host_boot_path = partname; return 0; } free(partname); /* /dev/mmcblkxpy */ partname = safe_asprintf("%sp%c", device_path, '1' + i); ret = stat(partname, &s); if (!ret) { host_boot_path = partname; return 0; } free(partname); /* /dev/disk/by-xxx/xxx-party */ partname = safe_asprintf("%s-part%c", device_path, '1' + i); ret = stat(partname, &s); if (!ret) { host_boot_path = partname; return 0; } free(partname); return 0; } static int determine_machine_id(void) { char buf[512] = {}; int fd, ret; char *path, *tmp; if (machine_id) return 0; if (!host_root_path) return -EINVAL; path = safe_asprintf("%s/etc/machine-id", host_root_path); fd = open(path, O_RDONLY); if (fd < 0) { perror("open"); return -errno; } ret = read(fd, buf, 512); if (ret < 0) { perror("read"); goto out; } if (ret == 512) { fprintf(stderr, "machine-id file too big\n"); ret = -EINVAL; goto out; } tmp = buf; while (*tmp) { if (!isalnum(*tmp)) { *tmp = '\0'; break; } tmp++; } machine_id = strdup(buf); ret = 0; out: close(fd); return ret; } static void cleanup(void) { if (host_mount_root_path) umount_path(host_mount_root_path); if (host_mount_boot_path) umount_path(host_mount_boot_path); } static int yesno(const char *str) { int ch; if (force) return 0; if (!interactive) return 1; printf("%s", str); ch = getchar(); if (ch == 'y') return 0; return 1; } static int do_add_kernel(void) { char *conf_path, *conf_file, *conf_dir, *images_dir; char *kernel_path, *host_images_dir, *host_kernel_path; char *initrd_path, *host_initrd_path; char *devicetree_path, *host_devicetree_path; int ret, fd; struct stat s; ret = determine_machine_id(); if (ret) { fprintf(stderr, "failed to determine machine-id\n"); return -EINVAL; } if (!machine_id) { fprintf(stderr, "No machine-id given\n"); return -EINVAL; } if (!kernel_version) { fprintf(stderr, "no Kernel version given\n"); return -EINVAL; } if (!kernel_image) { fprintf(stderr, "No Linux image given\n"); return -EINVAL; } conf_dir = safe_asprintf("%s/loader/entries", host_boot_path); conf_file = safe_asprintf("%s-%s.conf", machine_id, kernel_version); conf_path = safe_asprintf("%s/%s", conf_dir, conf_file); images_dir = safe_asprintf("%s/%s", machine_id, kernel_version); host_images_dir = safe_asprintf("%s/%s", host_boot_path, images_dir); kernel_path = safe_asprintf("%s/linux", images_dir); host_kernel_path = safe_asprintf("%s/linux", host_images_dir); initrd_path = safe_asprintf("%s/initrd", images_dir); host_initrd_path = safe_asprintf("%s/initrd", host_images_dir); devicetree_path = safe_asprintf("%s/devicetree", images_dir); host_devicetree_path = safe_asprintf("%s/devicetree", host_images_dir); ret = stat(conf_path, &s); if (!ret) { fprintf(stderr, "entry %s already exists.\n", conf_file); ret = yesno("overwrite? (y/n) "); if (ret) return -EINVAL; } ret = make_directory(conf_dir); if (ret) { fprintf(stderr, "failed to create directory %s: %s\n", conf_dir, strerror(errno)); return ret; } ret = make_directory(host_images_dir); if (ret) { fprintf(stderr, "failed to create directory %s: %s\n", host_images_dir, strerror(errno)); return ret; } fd = open(conf_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) { fprintf(stderr, "failed to create %s: %s\n", conf_path, strerror(errno)); return -errno; } dprintf(fd, "title %s\n", title); dprintf(fd, "version %s\n", kernel_version); dprintf(fd, "machine-id %s\n", machine_id); if (options) dprintf(fd, "options %s\n", options); dprintf(fd, "linux %s\n", kernel_path); if (initrd_image) dprintf(fd, "initrd %s\n", initrd_path); if (devicetree_image) dprintf(fd, "devicetree %s\n", devicetree_path); ret = close(fd); if (ret) return ret; ret = systemp("cp %s %s", kernel_image, host_kernel_path); if (ret) { fprintf(stderr, "unable to copy kernel image\n"); return ret; } if (initrd_image) { ret = systemp("cp %s %s", initrd_image, host_initrd_path); if (ret) { fprintf(stderr, "unable to copy initrd image\n"); return ret; } } if (devicetree_image) { ret = systemp("cp %s %s", devicetree_image, host_devicetree_path); if (ret) { fprintf(stderr, "unable to copy devicetree image\n"); return ret; } } printf("written config file: %s\n", conf_path); return 0; } static int do_open_entries(void) { DIR *dir; char *entries, *entry_path; struct loader_entry *e = NULL, *first = NULL; int i = 0; if (loader_entries) return 0; entries = safe_asprintf("%s/loader/entries", host_boot_path); dir = opendir(entries); if (!dir) { fprintf(stderr, "cannot open %s\n", entries); return -errno; } while (1) { struct dirent *ent; struct loader_entry *tmp; ent = readdir(dir); if (!ent) break; if (ent->d_name[0] == '.') continue; entry_path = safe_asprintf("%s/%s", entries, ent->d_name); tmp = loader_entry_open(entry_path); if (!tmp) { fprintf(stderr, "cannot open %s\n", entry_path); break; } tmp->config_file = strdup(ent->d_name); tmp->num = i++; if (first) e->next = tmp; else first = tmp; e = tmp; } closedir(dir); loader_entries = first; return 0; } static struct loader_entry *loader_entry_by_num(int num) { struct loader_entry *e; e = loader_entries; while (e) { if (e->num == num) return e; e = e->next; } return NULL; } static int do_list_entries(void) { struct loader_entry *e; int ret; ret = do_open_entries(); if (ret) return ret; e = loader_entries; while (e) { printf("Entry %d:\n", e->num); if (e->title) printf("\ttitle: %s\n", e->title); if (e->version) printf("\tversion: %s\n", e->version); if (e->machine_id) printf("\tmachine_id: %s\n", e->machine_id); if (e->options) printf("\toptions: %s\n", e->options); if (e->kernel) printf("\tlinux: %s\n", e->kernel); if (e->devicetree) printf("\tdevicetree: %s\n", e->devicetree); if (e->initrd) printf("\tinitrd: %s\n", e->initrd); e = e->next; } return 0; } static int is_file_referenced(const char *filename) { struct loader_entry *e = loader_entries; while (e) { if (e->kernel && !strcmp(e->kernel, filename)) return 1; if (e->initrd && !strcmp(e->initrd, filename)) return 1; if (e->devicetree && !strcmp(e->devicetree, filename)) return 1; e = e->next; } return 0; } static int remove_if_unreferenced(const char *filename) { char *path, *dir; int ret; if (!filename) return -EINVAL; if (is_file_referenced(filename)) return -EBUSY; path = safe_asprintf("%s/%s", host_boot_path, filename); verbose_printf("removing unrefenced %s\n", path); ret = unlink(path); if (ret) { fprintf(stderr, "cannot remove %s: %s\n", path, strerror(errno)); return ret; } dir = dirname(path); rmdir(dir); dir = dirname(path); rmdir(dir); free(path); return 0; } static int do_remove_kernel(void) { char *input = NULL; size_t insize; int remove_num = -1; struct loader_entry *e; int ret; char *kernel, *devicetree, *initrd; do_open_entries(); if (!loader_entries) { fprintf(stderr, "No entries to remove\n"); return -ENOENT; } if (remove_kernel_num >= 0) remove_num = remove_kernel_num; if (remove_num < 0 && interactive) { do_list_entries(); printf("which kernel do you like to remove?\n"); ret = getline(&input, &insize, stdin); if (ret < 0) return -errno; if (!strlen(input)) return -EINVAL; if (!isdigit(*input)) return -EINVAL; remove_num = atoi(input); } if (remove_num < 0) { fprintf(stderr, "no entry number given\n"); return -EINVAL; } e = loader_entry_by_num(remove_num); if (!e) { fprintf(stderr, "no entry with num %d\n", remove_num); return -ENOENT; } verbose_printf("removing entry %s\n", e->host_path); ret = unlink(e->host_path); if (ret) { fprintf(stderr, "cannot remove %s\n", e->host_path); return -errno; } kernel = e->kernel; devicetree = e->devicetree; initrd = e->initrd; e->kernel = NULL; e->devicetree = NULL; e->initrd = NULL; remove_if_unreferenced(kernel); remove_if_unreferenced(initrd); remove_if_unreferenced(devicetree); return 0; } static int do_set_once_default(const char *name, int entry) { int fd, ret; struct loader_entry *e; char *host_default_path; do_open_entries(); if (!loader_entries) { fprintf(stderr, "No entries found\n"); return -ENOENT; } if (entry < 0 && interactive) { char *input = NULL; size_t insize; do_list_entries(); printf("\nwhich entry shall be used?\n"); ret = getline(&input, &insize, stdin); if (ret < 0) return -errno; if (!strlen(input)) return -EINVAL; if (!isdigit(*input)) return -EINVAL; entry = atoi(input); } if (entry < 0) { fprintf(stderr, "no entry number given\n"); return -EINVAL; } e = loader_entry_by_num(entry); if (!e) { fprintf(stderr, "no entry with num %d\n", entry); return -ENOENT; } host_default_path = safe_asprintf("%s/%s", host_boot_path, name); fd = open(host_default_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) { fprintf(stderr, "failed to create %s: %s\n", host_default_path, strerror(errno)); return -errno; } dprintf(fd, "loader/entries/%s\n", e->config_file); ret = close(fd); if (ret) return ret; return 0; } static int do_set_default(void) { return do_set_once_default("default", set_default_num); } static int do_set_once(void) { return do_set_once_default("once", set_once_num); } enum opt { OPT_KERNEL = 1, OPT_INITRD, OPT_DEVICETREE, OPT_ONCE, OPT_ROOT_PARTITION_NO, OPT_DEVICE, OPT_ADD_ROOT_ARGUMENT, }; static struct option long_options[] = { {"add", no_argument, 0, 'a' }, {"remove", optional_argument, 0, 'r' }, {"list", no_argument, 0, 'l' }, {"default", optional_argument, 0, 'd' }, {"once", optional_argument, 0, OPT_ONCE }, {"device", required_argument, 0, OPT_DEVICE }, {"root", required_argument, 0, 'R' }, {"boot", required_argument, 0, 'b' }, {"kernel-version", required_argument, 0, 'k' }, {"title", required_argument, 0, 't' }, {"machine-id", required_argument, 0, 'm' }, {"kernel", required_argument, 0, OPT_KERNEL }, {"initrd", required_argument, 0, OPT_INITRD }, {"devicetree", required_argument, 0, OPT_DEVICETREE }, {"options", required_argument, 0, 'o' }, {"verbose", no_argument, 0, 'v' }, {"add-root-option", no_argument, 0, OPT_ADD_ROOT_ARGUMENT }, {"num-root-part", required_argument, 0, OPT_ROOT_PARTITION_NO }, {"help", no_argument, 0, 'h' }, {0, 0, 0, 0 } }; static void usage(char *name) { printf( "Usage: %s [OPTIONS]\n" "Install, uninstall and list kernels according to the bootloader spec\n" "\n" "command options, exactly one must be present:\n" "\n" "-a, --add Add a new boot entry\n" "-r, --remove[=num] Remove a boot entry. If is not present\n" " ask for it interactively\n" "-l, --list List all available entries\n" "-d, --default[=num] Make an entry the default. If is not\n" " present ask for it interactively\n" "--once[=num] start an entry once\n" "\n" "other options:\n" "\n" "--kernel-version= Specify kernel version, used for generating\n" " config filenames/directories. must be unique\n" " for each installed operating system\n" "--title= Title for the entry. If unspecified defaults\n" " to \"Linux-\"\n" "--machine-id= Specify machine id. Should be unique for each\n" " installation. Can be left unspecified when it\n" " can be read from /etc/machine-id.\n" "--kernel= Path to the kernel to install\n" "--initrd= Path to the initrd to install, optional\n" "--devicetree= Path to the devicetree to install, optional\n" "-o, --options= Commandline options for the kernel, can be\n" " given multiple times\n" "-v, --verbose Be more verbose\n" "--add-root-option If present, add a \"root=PARTUUID=xxxxxxxx-yy\"\n" " option to the kernel commandline. The partuuid\n" " is determined from the device given with the\n" " --device option and the partition number\n" " determined from either the device specified\n" " with --root or from the --num-root-part option.\n" "--num-root-part= Specify partition number for --add-root-option\n" "-h, --help This help\n" "\n" "Options for non-native mode:\n" "\n" "Each of the following options disables native mode. Useful for preparing\n" "boot media on another host.\n" "--device= Specify device to work on\n" "--root= Specify path or device to use as '/', defaults to '/'\n" "--boot= Specify path or device to use as '/boot', defaults to '/boot'\n", name); } int main(int argc, char *argv[]) { int c; char *root = NULL; int option_index, add_kernel = 0, remove_kernel = 0, add_root_argument = 0; int ret, list = 0, set_default = 0, newmount; int native_mode = 1, set_once = 0; while (1) { c = getopt_long(argc, argv, "b:R:d:k:p:m:lo:aruvh", long_options, &option_index); if (c < 0) break; switch (c) { case 'h': usage(argv[0]); exit(0); case 'b': native_mode = 0; host_boot_path = optarg; break; case 'R': native_mode = 0; root = optarg; break; case 'l': list = 1; break; case 'd': set_default = 1; if (optarg) set_default_num = atoi(optarg); break; case OPT_ONCE: set_once = 1; if (optarg) set_once_num = atoi(optarg); break; case OPT_DEVICE: native_mode = 0; device_path = optarg; break; case 'k': kernel_version = optarg; break; case 't': title = optarg; break; case 'm': machine_id = optarg; break; case OPT_KERNEL: kernel_image = optarg; break; case 'o': append_option("%s", optarg); break; case 'a': add_kernel = 1; break; case 'r': remove_kernel = 1; if (optarg) remove_kernel_num = atoi(optarg); break; case OPT_ADD_ROOT_ARGUMENT: add_root_argument = 1; break; case OPT_ROOT_PARTITION_NO: root_partition_num = atoi(optarg); break; case OPT_INITRD: initrd_image = optarg; break; case OPT_DEVICETREE: devicetree_image = optarg; break; case 'v': verbose++; break; } } if (!list && !remove_kernel && !set_default && !add_kernel && !set_once) { fprintf(stderr, "no command given\n"); exit (1); } if (native_mode) { host_boot_path = "/boot"; host_root_path = ""; } if (device_path) { ret = determine_root_boot_path(device_path); if (ret) exit(1); } if (host_boot_path) { verbose_printf("using partition %s for /boot as determined by device argument\n", host_boot_path); } if (!host_boot_path) { fprintf(stderr, "No partition or directory given for /boot\n"); goto out; } host_boot_path = mount_path(host_boot_path, &newmount); if (!host_boot_path) goto out; if (newmount) host_mount_boot_path = host_boot_path; if (root) { host_root_path = mount_path(root, &newmount); if (!host_root_path) goto out; if (newmount) host_mount_root_path = host_root_path; } if (!title) title = safe_asprintf("Linux-%s", kernel_version); if (add_root_argument) { if (!nt_disk_signature) { fprintf(stderr, "no nt disk signature found for root-uuid\n" "Cannot add root argument\n"); goto out; } if (!root_partition_num) { if (!root) { fprintf(stderr, "no root partition number and no device for / given\n" "Cannot add root argument\n"); goto out; } detect_root_partition_num(root); } if (!root_partition_num) { fprintf(stderr, "no root partition number given\n" "Cannot add root argument\n"); goto out; } append_option("root=PARTUUID=%08X-%02d", nt_disk_signature, root_partition_num); } if (list) { ret = do_list_entries(); goto out; } if (remove_kernel) { ret = do_remove_kernel(); goto out; } if (set_default) { ret = do_set_default(); goto out; } if (set_once) { ret = do_set_once(); goto out; } if (add_kernel) { ret = do_add_kernel(); goto out; } ret = 0; out: cleanup(); exit(ret == 0 ? 0 : 1); }