Refactor kernel command line parsing.

This commit is contained in:
Jonas 'Sortie' Termansen 2017-02-25 23:25:53 +01:00
parent 35d3c7e0b5
commit d45417651f
5 changed files with 118 additions and 98 deletions

View File

@ -218,9 +218,9 @@ menuentry() {
printf "}\n"
}
menuentry "live environment" ''
menuentry "new installation" '--init="/sbin/init --target=sysinstall"'
menuentry "upgrade existing installation" '--init="/sbin/init --target=sysupgrade"'
menuentry "live environment" '-- /sbin/init'
menuentry "new installation" '-- /sbin/init --target=sysinstall'
menuentry "upgrade existing installation" '-- /sbin/init --target=sysupgrade'
echo
cat << EOF

View File

@ -7,6 +7,8 @@
.Sh SYNOPSIS
.Nm init
.Op Fl \-target Ns "=" Ns Ar init-target
.Op Fl \-
.Op Ar chain-init ...
.Sh DESCRIPTION
.Nm
is the first program run after system startup and is responsible for
@ -93,7 +95,8 @@ The
target mounts the root filesystem as in
.Pa /etc/fstab
(see
.Xr fstab 5) and runs the next
.Xr fstab 5 )
and runs the next
.Nm
program.
This is used by
@ -113,7 +116,9 @@ filesystem directory is bound at
.Pp
Finally the
.Pa /sbin/init
program of the target root filesystem is run inside a chroot.
program (or
.Ar chain-init
if specified) of the target root filesystem is run inside a chroot.
.Ss Configuration
Once the
.Nm

View File

@ -813,8 +813,10 @@ static void niht(void)
}
}
static int init(const char* target)
static int init(int argc, char** argv, const char* target)
{
if ( 1 < argc )
fatal("unexpected extra operand: %s", argv[1]);
init_early();
set_hostname();
set_kblayout();
@ -938,8 +940,10 @@ static int init(const char* target)
return result;
}
static int init_chain(const char* target)
static int init_chain(int argc, char** argv, const char* target)
{
int next_argc = argc - 1;
char** next_argv = argv + 1;
init_early();
prepare_block_devices();
load_fstab();
@ -986,17 +990,27 @@ static int init_chain(const char* target)
if ( chdir("/") < 0 )
fatal("chdir: %s: %m", chain_location);
unsetenv("INIT_PID");
const char* program = next_argv[0];
if ( !strcmp(target, "chain-merge") )
{
const char* argv[] = { "init", "--target=merge", NULL };
execv("/sysmerge/sbin/init", (char* const*) argv);
fatal("Failed to load automatic update chain init: %s: %m", argv[0]);
if ( next_argc < 1 )
{
program = "/sysmerge/sbin/init";
next_argv = (char*[]) { "init", "--target=merge", NULL };
}
execvp(program, (char* const*) next_argv);
fatal("Failed to load automatic update chain init: %s: %m",
next_argv[0]);
}
else
{
const char* argv[] = { "init", NULL };
execv("/sbin/init", (char* const*) argv);
fatal("Failed to load chain init: %s: %m", argv[0]);
if ( next_argc < 1 )
{
program = "/sbin/init";
next_argv = (char*[]) { "init", NULL };
}
execvp(program, (char* const*) next_argv);
fatal("Failed to load chain init: %s: %m", next_argv[0]);
}
}
int status;
@ -1102,11 +1116,11 @@ int main(int argc, char* argv[])
!strcmp(target, "sysinstall") ||
!strcmp(target, "sysupgrade") ||
!strcmp(target, "merge") )
return init(target);
return init(argc, argv, target);
if ( !strcmp(target, "chain") ||
!strcmp(target, "chain-merge") )
return init_chain(target);
return init_chain(argc, argv, target);
fatal("Unknown initialization target `%s'", target);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
* Copyright (c) 2011-2017 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -108,8 +108,9 @@ static void BootThread(void* user);
static void InitThread(void* user);
static void SystemIdleThread(void* user);
static int argc;
static char** argv;
static multiboot_info_t* bootinfo;
static char* init_cmdline;
static char* cmdline_tokenize(char** saved)
{
@ -156,6 +157,19 @@ static char* cmdline_tokenize(char** saved)
return data;
}
static void compact_arguments(int* argc, char*** argv)
{
for ( int i = 0; i < *argc; i++ )
{
while ( i < *argc && !(*argv)[i] )
{
for ( int n = i; n < *argc; n++ )
(*argv)[n] = (*argv)[n+1];
(*argc)--;
}
}
}
extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo_p)
{
(void) magic;
@ -235,59 +249,65 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo_p)
FreeKernelAddress(&alloc);
}
bool no_random_seed = false;
int argmax = 1;
argv = new char*[argmax + 1];
if ( !argv )
Panic("Failed to allocate kernel command line");
char* arg_saved = cmdline;
char* arg;
struct kernel_option
{
const char* name;
bool has_parameter;
} options[] =
{
{ "--init", true },
{ "--no-random-seed", false },
};
size_t options_count = sizeof(options) / sizeof(options[0]);
while ( (arg = cmdline_tokenize(&arg_saved)) )
{
struct kernel_option* option = NULL;
char* parameter = NULL;
for ( size_t i = 0; i < options_count; i++ )
if ( argc == argmax )
{
struct kernel_option* candidate = &options[i];
if ( candidate->has_parameter )
{
size_t name_length = strlen(candidate->name);
if ( strncmp(arg, candidate->name, name_length) != 0 )
continue;
if ( arg[name_length] == '=' )
parameter = arg + name_length + 1;
else if ( !arg[name_length] )
{
if ( !(parameter = cmdline_tokenize(&arg_saved)) )
{
Log::PrintF("\r\e[J");
Log::PrintF("kernel: fatal: option '%s' requires an argument\n", arg);
HaltKernel();
}
}
else
continue;
}
else if ( strcmp(arg, candidate->name) != 0 )
continue;
option = candidate;
argmax = argmax ? 2 * argmax : 8;
char** new_argv = new char*[argmax + 1];
if ( !new_argv )
Panic("Failed to allocate kernel command line");
for ( int i = 0; i < argc; i++ )
new_argv[i] = argv[i];
argv = new_argv;
}
if ( !option )
argv[argc++] = arg;
}
argv[argc] = NULL;
bool no_random_seed = false;
for ( int i = 0; i < argc; i++ )
{
const char* arg = argv[i];
if ( arg[0] != '-' || !arg[1] )
continue;
argv[i] = NULL;
if ( !strcmp(arg, "--") )
break;
if ( arg[1] != '-' )
{
char c;
while ( (c = *++arg) ) switch ( c )
{
default:
Log::PrintF("\r\e[J");
Log::PrintF("kernel: fatal: unknown option -- '%c'\n", c);
HaltKernel();
}
}
else if ( !strcmp(arg, "--no-random-seed") )
no_random_seed = true;
else
{
Log::PrintF("\r\e[J");
Log::PrintF("kernel: fatal: unrecognized option '%s'\n", arg);
HaltKernel();
}
if ( !strcmp(option->name, "--init") )
init_cmdline = parameter;
else if ( !strcmp(option->name, "--no-random-seed") )
no_random_seed = true;
}
compact_arguments(&argc, &argv);
if ( argc == 0 )
{
argv[argc++] = (char*) "/sbin/init";
argv[argc] = NULL;
}
// Initialize the interrupt handler table and enable interrupts.
@ -684,33 +704,6 @@ static void InitThread(void* /*user*/)
dtable.Reset();
static char default_init_cmdline[] = "/sbin/init";
if ( !init_cmdline )
init_cmdline = default_init_cmdline;
char* init_cmdline_dup = strdup(init_cmdline);
if ( !init_cmdline_dup )
PanicF("strdup: %m");
size_t init_cmdline_tokens = 0;
char* saved = init_cmdline_dup;
char* arg;
while ( (arg = cmdline_tokenize(&saved)) )
init_cmdline_tokens++;
free(init_cmdline_dup);
if ( INT_MAX - 1 < init_cmdline_tokens )
Panic("Too many tokens in init command line");
int argc = init_cmdline_tokens;
char** argv = new char*[argc + 1];
if ( !argv )
PanicF("operator new: %m");
saved = init_cmdline;
for ( int i = 0; i <= argc; i++ )
argv[i] = cmdline_tokenize(&saved);
if ( argc == 0 )
Panic("No init specified");
const char* initpath = argv[0];
Ref<Descriptor> init = root->open(&ctx, initpath, O_EXEC | O_READ);
if ( !init )

View File

@ -6,7 +6,9 @@
.Nd operating system kernel
.Sh SYNOPSIS
.Pa /boot/sortix.bin
.Op Fl \-init Ns "=" Ns Ar init-command-line
.Op Fl \-no-random-seed
.Op Fl \-
.Op Ar init ...
.Sh DESCRIPTION
.Pa /boot/sortix.bin
is the operating system
@ -19,22 +21,28 @@ compatible executable loaded by the bootloader along with a companion
.Xr initrd 7
that contains a userland.
.Pp
The kernel extracts the initrd into the initial kernel memory root filesystem
and executes
The kernel extracts the initrd into the initial kernel memory root filesystem.
The specified
.Ar init
program is invoked with the given arguments, defaulting to the
.Xr init 8
as
.Pa /sbin/init .
The computer is powered off if this process exits 0, rebooted if it exits 1,
at
.Pa /sbin/init
if
.Ar init
is not specified.
If the
.Nm init
is to receive any argument starting with a dash, first pass the
.Fl \-
delimiter to stop kernel option parsing.
The computer is powered off if the
.Nm init
process exits 0, rebooted if it exits 1,
halted if it exits 2, and paniced otherwise.
.Pp
The options are as follows:
.Bl -tag -width "12345678"
.It Fl \-init Ns "=" Ns Ar init-command-line
The
.Ar init-command-line
argument is split into tokens and used as the command line to invoke the
specified
.Xr init 8 .
.It Fl \-no-random-seed
Don't warn if no random seed file was loaded by the bootloader (usually from
.Pa /boot/random.seed ) .