2013-09-15 08:09:23 +00:00
|
|
|
/*
|
|
|
|
* kernel-install - install a kernel according to the bootloader spec:
|
|
|
|
* http://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/
|
|
|
|
*
|
|
|
|
* Copyright (C) 2013 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
|
|
|
|
*
|
|
|
|
* 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=<file> or --initrd=<file>.
|
|
|
|
*
|
|
|
|
* 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=<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 <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <libgen.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2013-10-31 16:19:12 +00:00
|
|
|
fprintf(stderr, "cannot mount %s\n", in_path);
|
2013-09-15 08:09:23 +00:00
|
|
|
return NULL;
|
|
|
|
case MOUNT_MOUNT:
|
|
|
|
out_path = mount_path_mount(in_path);
|
|
|
|
if (out_path) {
|
|
|
|
*newmount = 1;
|
|
|
|
return out_path;
|
|
|
|
}
|
2013-10-31 16:19:12 +00:00
|
|
|
fprintf(stderr, "cannot mount %s\n", in_path);
|
2013-09-15 08:09:23 +00:00
|
|
|
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);
|
2013-10-31 16:19:12 +00:00
|
|
|
if (ret) {
|
|
|
|
fprintf(stderr, "failed to create directory %s: %s\n",
|
|
|
|
conf_dir, strerror(errno));
|
2013-09-15 08:09:23 +00:00
|
|
|
return ret;
|
2013-10-31 16:19:12 +00:00
|
|
|
}
|
2013-09-15 08:09:23 +00:00
|
|
|
|
|
|
|
ret = make_directory(host_images_dir);
|
2013-10-31 16:19:12 +00:00
|
|
|
if (ret) {
|
|
|
|
fprintf(stderr, "failed to create directory %s: %s\n",
|
|
|
|
host_images_dir, strerror(errno));
|
2013-09-15 08:09:23 +00:00
|
|
|
return ret;
|
2013-10-31 16:19:12 +00:00
|
|
|
}
|
2013-09-15 08:09:23 +00:00
|
|
|
|
|
|
|
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);
|
2014-06-20 08:58:15 +00:00
|
|
|
if (ret < 0)
|
2013-09-15 08:09:23 +00:00
|
|
|
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 <num> 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 <num> is not\n"
|
|
|
|
" present ask for it interactively\n"
|
|
|
|
"--once[=num] start an entry once\n"
|
|
|
|
"\n"
|
|
|
|
"other options:\n"
|
|
|
|
"\n"
|
|
|
|
"--kernel-version=<version> Specify kernel version, used for generating\n"
|
|
|
|
" config filenames/directories. must be unique\n"
|
|
|
|
" for each installed operating system\n"
|
|
|
|
"--title=<name> Title for the entry. If unspecified defaults\n"
|
|
|
|
" to \"Linux-<version>\"\n"
|
|
|
|
"--machine-id=<id> Specify machine id. Should be unique for each\n"
|
|
|
|
" installation. Can be left unspecified when it\n"
|
|
|
|
" can be read from <rootpath>/etc/machine-id.\n"
|
|
|
|
"--kernel=<kernel> Path to the kernel to install\n"
|
|
|
|
"--initrd=<initrd> Path to the initrd to install, optional\n"
|
|
|
|
"--devicetree=<devicetree> Path to the devicetree to install, optional\n"
|
|
|
|
"-o, --options=<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=<partnum> 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=<devicepath> Specify device to work on\n"
|
|
|
|
"--root=<path|device> Specify path or device to use as '/', defaults to '/'\n"
|
|
|
|
"--boot=<path|device> 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);
|
|
|
|
}
|