#include <sys/types.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/reboot.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <util.h>
#include <machine/kexec.h>
#include <machine/param.h>
#include "cmd.h"
#include "disk.h"
#define DEVRANDOM "/dev/random"
#define BOOTRANDOM "/etc/random.seed"
#define BOOTRANDOM_MAX 256
#define KERNEL "/bsd"
int loadrandom(void);
void kexec(int);
struct cmd_state cmd;
int kexecfd = -1;
const char version[] = "0.4";
int
main(void)
{
u_char bootduid[8];
int fd, hasboot, isupgrade = 0;
fd = open(_PATH_CONSOLE, O_RDWR);
login_tty(fd);
setvbuf(stdout, NULL, _IONBF, 0);
printf(">> OpenBSD/" MACHINE " BOOT %s\n", version);
kexecfd = open("/dev/kexec", O_WRONLY);
if (kexecfd == -1)
err(1, "cannot open boot control device");
memset(&cmd, 0, sizeof(cmd));
cmd.boothowto = 0;
cmd.conf = "/etc/boot.conf";
strlcpy(cmd.image, KERNEL, sizeof(cmd.image));
cmd.timeout = 5;
if (ioctl(kexecfd, KIOC_GETBOOTDUID, bootduid) == -1) {
fprintf(stderr, "cannot get bootduid from kernel: %s\n",
strerror(errno));
} else {
memcpy(cmd.bootduid, bootduid, sizeof(cmd.bootduid));
}
disk_init();
if (upgrade()) {
strlcpy(cmd.image, "/bsd.upgrade", sizeof(cmd.image));
printf("upgrade detected: switching to %s\n", cmd.image);
isupgrade = 1;
}
hasboot = read_conf();
for (;;) {
if (hasboot <= 0) {
do {
printf("boot> ");
} while (!getcmd());
}
if (loadrandom() == 0)
cmd.boothowto |= RB_GOODRANDOM;
kexec(isupgrade);
hasboot = 0;
strlcpy(cmd.image, KERNEL, sizeof(cmd.image));
printf("will try %s\n", cmd.image);
}
return 0;
}
int
loadrandom(void)
{
char buf[BOOTRANDOM_MAX];
struct stat sb;
int fd, ret = 0;
if (disk_open(cmd.path) == NULL)
return -1;
fd = open(BOOTRANDOM, O_RDONLY);
if (fd == -1) {
fprintf(stderr, "%s: cannot open %s: %s", __func__, BOOTRANDOM,
strerror(errno));
disk_close();
return -1;
}
if (fstat(fd, &sb) == 0) {
if (sb.st_mode & S_ISTXT) {
printf("NOTE: random seed is being reused.\n");
ret = -1;
}
if (read(fd, buf, sizeof(buf)) != sizeof(buf))
ret = -1;
fchmod(fd, sb.st_mode | S_ISTXT);
} else {
ret = -1;
}
close(fd);
disk_close();
fd = open(DEVRANDOM, O_WRONLY);
if (fd == -1) {
fprintf(stderr, "%s: cannot open %s: %s", __func__,
DEVRANDOM, strerror(errno));
return -1;
}
write(fd, buf, sizeof(buf));
close(fd);
return ret;
}
void
kexec(int isupgrade)
{
struct kexec_args kargs;
struct stat sb;
char *kimg = NULL;
const char *path;
ssize_t n;
off_t pos;
int fd = -1, ret;
path = disk_open(cmd.path);
if (path == NULL)
return;
fd = open(path, O_RDONLY);
if (fd == -1)
goto load_failed;
if (fstat(fd, &sb) == -1)
goto load_failed;
if (!S_ISREG(sb.st_mode) || sb.st_size == 0) {
errno = ENOEXEC;
goto load_failed;
}
if (isupgrade) {
sb.st_mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
if (fchmod(fd, sb.st_mode) == -1)
printf("fchmod a-x %s: failed\n", path);
}
kimg = malloc(sb.st_size);
if (kimg == NULL)
goto load_failed;
pos = 0;
while (pos < sb.st_size) {
n = read(fd, kimg + pos, sb.st_size - pos);
if (n == -1)
goto load_failed;
pos += n;
}
close(fd);
disk_close();
memset(&kargs, 0, sizeof(kargs));
kargs.kimg = kimg;
kargs.klen = sb.st_size;
kargs.boothowto = cmd.boothowto;
memcpy(kargs.bootduid, cmd.bootduid, sizeof(kargs.bootduid));
printf("booting %s\n", cmd.path);
ret = ioctl(kexecfd, KIOC_KEXEC, &kargs);
if (ret == -1)
fprintf(stderr, "failed to execute kernel %s: %s\n",
cmd.path, strerror(errno));
else
fprintf(stderr, "kexec() returned unexpectedly\n");
free(kimg);
return;
load_failed:
fprintf(stderr, "failed to load kernel %s: %s\n",
cmd.path, strerror(errno));
if (fd != -1)
close(fd);
disk_close();
free(kimg);
}