diff --git a/build-aux/iso-grub-cfg.sh b/build-aux/iso-grub-cfg.sh index 058f5596..1f34d235 100755 --- a/build-aux/iso-grub-cfg.sh +++ b/build-aux/iso-grub-cfg.sh @@ -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 diff --git a/init/init.8 b/init/init.8 index bdba1c00..2649bed8 100644 --- a/init/init.8 +++ b/init/init.8 @@ -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 diff --git a/init/init.c b/init/init.c index eca14eb0..221831f7 100644 --- a/init/init.c +++ b/init/init.c @@ -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); } diff --git a/kernel/kernel.cpp b/kernel/kernel.cpp index ce44312a..ac4e7baf 100644 --- a/kernel/kernel.cpp +++ b/kernel/kernel.cpp @@ -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 init = root->open(&ctx, initpath, O_EXEC | O_READ); if ( !init ) diff --git a/share/man/man7/kernel.7 b/share/man/man7/kernel.7 index bc3ee5fd..e2339cb4 100644 --- a/share/man/man7/kernel.7 +++ b/share/man/man7/kernel.7 @@ -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 ) .