Rewrite init(8).

This commit is contained in:
Jonas 'Sortie' Termansen 2015-07-24 02:24:49 +02:00
parent 70687ac610
commit 9fe234d4d8
10 changed files with 1053 additions and 569 deletions

View File

@ -88,12 +88,15 @@ endif
rm -rf "$(INSTALL_ROOTFS)/boot/sortix.initrd.d"
mkdir -p "$(INSTALL_ROOTFS)/boot/sortix.initrd.d"
mkdir -p "$(INSTALL_ROOTFS)/boot/sortix.initrd.d/bin"
for PROGRAM in init mbrfs extfs; do \
cp "$(INSTALL_ROOTFS)/bin/$$PROGRAM" "$(INSTALL_ROOTFS)/boot/sortix.initrd.d/bin/$$PROGRAM"; \
done
mkdir -p "$(INSTALL_ROOTFS)/boot/sortix.initrd.d/sbin"
test ! -e "$(INSTALL_ROOTFS)/bin/fsck.ext2" || \
cp "$(INSTALL_ROOTFS)/bin/fsck.ext2" "$(INSTALL_ROOTFS)/boot/sortix.initrd.d/bin/fsck.ext2"
cp "$(INSTALL_ROOTFS)/sbin/extfs" "$(INSTALL_ROOTFS)/boot/sortix.initrd.d/sbin/extfs"
cp "$(INSTALL_ROOTFS)/sbin/init" "$(INSTALL_ROOTFS)/boot/sortix.initrd.d/sbin/init"
mkdir -p "$(INSTALL_ROOTFS)/boot/sortix.initrd.d/etc"
mkdir -p "$(INSTALL_ROOTFS)/boot/sortix.initrd.d/etc/init"
cp "$(INSTALL_ROOTFS)/etc/rootfs.uuid" "$(INSTALL_ROOTFS)/boot/sortix.initrd.d/etc/init/rootfs.uuid"
cp "$(INSTALL_ROOTFS)/etc/fstab" "$(INSTALL_ROOTFS)/boot/sortix.initrd.d/etc/fstab"
echo chain > "$(INSTALL_ROOTFS)/boot/sortix.initrd.d/etc/init/target"
mkinitrd --format=sortix-initrd-2 "$(INSTALL_ROOTFS)/boot/sortix.initrd.d" -o "$(INSTALL_ROOTFS)/boot/sortix.initrd"
rm -rf "$(INSTALL_ROOTFS)/boot/sortix.initrd.d"

View File

@ -24,8 +24,8 @@ all: $(BINARIES)
.PHONY: all install clean
install: all
mkdir -p $(DESTDIR)$(BINDIR)
install $(BINARIES) $(DESTDIR)$(BINDIR)
mkdir -p $(DESTDIR)$(SBINDIR)
install $(BINARIES) $(DESTDIR)$(SBINDIR)
extfs: *.cpp *.h
$(CXX) $(PTHREAD_OPTION) -std=gnu++11 $(CPPFLAGS) $(CXXFLAGS) *.cpp -o $@ $(LIBS)

View File

@ -128,66 +128,6 @@ void StatInode(Inode* inode, struct stat* st)
st->st_blocks = inode->data->i_blocks;
}
static bool is_hex_digit(char c)
{
return ('0' <= c && c <= '9') ||
('a' <= c && c <= 'f') ||
('A' <= c && c <= 'F');
}
static bool is_valid_uuid(const char* uuid)
{
if ( strlen(uuid) != 36 )
return false;
// Format: 01234567-0123-0123-0123-0123456789AB
for ( size_t i = 0; i < 36; i++ )
{
if ( i == 8 || i == 13 || i == 18 || i == 23 )
{
if ( uuid[i] != '-' )
return false;
}
else
{
if ( !is_hex_digit(uuid[i]) )
return false;
}
}
return true;
}
static unsigned char debase(char c)
{
if ( '0' <= c && c <= '9' )
return (unsigned char) (c - '0');
if ( 'a' <= c && c <= 'f' )
return (unsigned char) (c - 'a' + 10);
if ( 'A' <= c && c <= 'F' )
return (unsigned char) (c - 'A' + 10);
return 0;
}
static void uuid_from_string(uint8_t uuid[16], const char* string)
{
assert(is_valid_uuid(string));
size_t output_index = 0;
size_t i = 0;
while ( i < 36 )
{
assert(string[i + 0] != '\0');
if ( i == 8 || i == 13 || i == 18 || i == 23 )
{
i++;
continue;
}
assert(string[i + 1] != '\0');
uuid[output_index++] = debase(string[i + 0]) << 4 |
debase(string[i + 1]) << 0;
i += 2;
}
assert(string[i] == '\0');
}
static void compact_arguments(int* argc, char*** argv)
{
for ( int i = 0; i < *argc; i++ )
@ -217,9 +157,8 @@ static void version(FILE* fp, const char* argv0)
int main(int argc, char* argv[])
{
const char* argv0 = argv[0];
const char* test_uuid = NULL;
const char* pretend_mount_path = NULL;
bool foreground = false;
bool probe = false;
bool read = false;
bool write = false;
for ( int i = 1; i < argc; i++ )
@ -250,20 +189,21 @@ int main(int argc, char* argv[])
foreground = false;
else if ( !strcmp(arg, "--foreground") )
foreground = true;
else if ( !strcmp(arg, "--probe") )
probe = true;
else if ( !strcmp(arg, "--read") )
read = true;
else if ( !strcmp(arg, "--write") )
write = true;
else if ( !strcmp(arg, "--test-uuid") )
else if ( !strncmp(arg, "--pretend-mount-path=", strlen("--pretend-mount-path=")) )
pretend_mount_path = arg + strlen("--pretend-mount-path=");
else if ( !strcmp(arg, "--pretend-mount-path") )
{
if ( i+1 == argc )
{
fprintf(stderr, "%s: --test-uuid: Missing operand\n", argv0);
fprintf(stderr, "%s: --pretend-mount-path: Missing operand\n", argv0);
exit(1);
}
test_uuid = argv[++i], argv[i] = NULL;
pretend_mount_path = argv[++i];
argv[i] = NULL;
}
else
{
@ -296,6 +236,9 @@ int main(int argc, char* argv[])
exit(1);
}
if ( !pretend_mount_path )
pretend_mount_path = mount_path;
int fd = open(device_path, write ? O_RDWR : O_RDONLY);
if ( fd < 0 )
error(1, errno, "`%s'", device_path);
@ -304,98 +247,50 @@ int main(int argc, char* argv[])
struct ext_superblock sb;
if ( preadall(fd, &sb, sizeof(sb), 1024) != sizeof(sb) )
{
if ( probe )
exit(1);
else if ( errno == EEOF )
if ( errno == EEOF )
error(1, 0, "`%s' isn't a valid extended filesystem", device_path);
else
error(1, errno, "read: `%s'", device_path);
}
// Verify the magic value to detect a compatible filesystem.
if ( !probe && sb.s_magic != EXT2_SUPER_MAGIC )
if ( sb.s_magic != EXT2_SUPER_MAGIC )
error(1, 0, "`%s' isn't a valid extended filesystem", device_path);
if ( probe && sb.s_magic != EXT2_SUPER_MAGIC )
exit(1);
// Test whether this was the filesystem the user was looking for.
if ( test_uuid )
{
if ( !is_valid_uuid(test_uuid) )
{
if ( !probe )
error(1, 0, "`%s' isn't a valid uuid", test_uuid);
exit(1);
}
uint8_t uuid[16];
uuid_from_string(uuid, test_uuid);
if ( memcmp(sb.s_uuid, uuid, 16) != 0 )
{
if ( !probe )
error(1, 0, "uuid `%s' did not match the ext2 filesystem at `%s'", test_uuid, device_path);
exit(1);
}
}
// Test whether this revision of the extended filesystem is supported.
if ( sb.s_rev_level == EXT2_GOOD_OLD_REV )
{
if ( probe )
exit(1);
error(1, 0, "`%s' is formatted with an obsolete filesystem revision",
device_path);
}
// Verify that no incompatible features are in use.
if ( sb.s_feature_compat & ~EXT2_FEATURE_INCOMPAT_SUPPORTED )
{
if ( probe )
exit(1);
error(1, 0, "`%s' uses unsupported and incompatible features",
device_path);
}
// Verify that no incompatible features are in use if opening for write.
if ( !default_access && write &&
sb.s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPPORTED )
{
if ( probe )
exit(1);
error(1, 0, "`%s' uses unsupported and incompatible features, "
"read-only access is possible, but write-access was "
"requested", device_path);
}
if ( write && sb.s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPPORTED )
{
if ( !probe )
fprintf(stderr, "Warning: `%s' uses unsupported and incompatible "
"features, falling back to read-only access\n",
device_path);
fprintf(stderr, "Warning: `%s' uses unsupported and incompatible "
"features, falling back to read-only access\n",
device_path);
// TODO: Modify the file descriptor such that writing fails!
read = true;
}
// Check whether any features are in use that we can safely disregard.
if ( !probe && sb.s_feature_compat & ~EXT2_FEATURE_COMPAT_SUPPORTED )
if ( sb.s_feature_compat & ~EXT2_FEATURE_COMPAT_SUPPORTED )
fprintf(stderr, "Note: filesystem uses unsupported but compatible "
"features\n");
// Check the block size is sane. 64 KiB may have issues, 32 KiB then.
if ( sb.s_log_block_size > (15-10) /* 32 KiB blocks */ )
{
if ( probe )
exit(1);
error(1, 0, "`%s': excess block size", device_path);
}
// We have found no critical problems, so let the caller know that this
// filesystem satisfies the probe request.
if ( probe )
exit(0);
// Check whether the filesystem was unmounted cleanly.
if ( sb.s_state != EXT2_VALID_FS )
@ -407,7 +302,7 @@ int main(int argc, char* argv[])
Device* dev = new Device(fd, device_path, block_size, write);
if ( !dev ) // TODO: Use operator new nothrow!
error(1, errno, "malloc");
Filesystem* fs = new Filesystem(dev, mount_path);
Filesystem* fs = new Filesystem(dev, pretend_mount_path);
if ( !fs ) // TODO: Use operator new nothrow!
error(1, errno, "malloc");

View File

@ -19,14 +19,16 @@ all: $(BINARY)
.PHONY: all install clean
$(BINARY): $(OBJS)
$(CXX) $(OBJS) -o $(BINARY) $(CXXFLAGS) $(LIBS)
$(CXX) $(OBJS) -o $(BINARY) $(CXXFLAGS) -lmount $(LIBS)
%.o: %.c++
$(CXX) -std=gnu++11 $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
install: all
mkdir -p $(DESTDIR)$(BINDIR)
install $(BINARY) $(DESTDIR)$(BINDIR)
mkdir -p $(DESTDIR)$(SBINDIR)
install $(BINARY) $(DESTDIR)$(SBINDIR)
mkdir -p $(DESTDIR)$(MANDIR)/man8
cp init.8 $(DESTDIR)$(MANDIR)/man8/init.8
clean:
rm -f $(BINARY) $(OBJS) *.o

182
init/init.8 Normal file
View File

@ -0,0 +1,182 @@
.Dd $Mdocdate: October 5 2015 $
.Dt INIT 8
.Os
.Sh NAME
.Nm init
.Nd system initialization
.Sh SYNOPSIS
.Nm init
.Op Fl \-target Ns "=" Ns Ar init-target
.Op Fl \-chain Ns "=" Ns Ar path-or-uuid
.Sh DESCRIPTION
.Nm
is the first program run after system startup and is responsible for
initializing the operating system and starting the specified
.Ar init-target .
This is normally a login screen, a root shell, or a dedicated special purpose
program.
.Pp
The
.Xr kernel 7
starts the system in a temporary environment with a root filesystem
backed by system memory and extracts the
.Xr initrd 7
into it. The kernel runs the
.Pa /sbin/init
program of the system memory root filesystem as the first process. If the
system is bootable cdrom, then the initrd will be a fully functional system and
.Nm
will start a live environment or an operating system installer. If the
system is installed on a harddisk, then the initrd is a minimal system made with
.Xr update-initrd 8
that will search for the actual root filesystem and chain init it. The next
stage init will recognize it as the intended system and complete the system
startup.
.Ss Initialization Target
.Nm
first determines its target from the
.Fl \-target
option if specified or
.Pa /etc/init/target
otherwise. Supported targets are:
.Pp
.Bl -tag -width "single-user" -compact -offset indent
.It chain
mount real root filesystem and run its
.Nm
.It multi-user
boot to
.Xr login 8
.It single-user
boot to root shell without password (not secure)
.It sysinstall
boot to operating system installer (not secure)
.It sysupgrade
boot to operating system upgrader (not secure)
.El
.Pp
It is a full system compromise if unauthenticated users are able to boot the
wrong target. The kernel command line can specify the path to
.Nm
and its arguments. Unprivileged users can change the kernel command line from
the bootloader command line if it hasn't been password protected. Likewise
unprivileged users can use their own replacement bootloader by booting a
portable device under their control if the firmware configuration has not been
password protected.
.Ss Partition Creation
.Nm
will scan every block device for valid partition tables and create the
corresponding partition devices in
.Pa /dev .
.Ss Chain Initialization
The chain target triggers a search for the root filesystem. The
.Fl \-chain Ns "=" Ns Ar path-or-uuid
option implies
.Fl \-target Ns "=" Ns chain
if it is not set and specifies the method to locate the root filesystem. The
.Ar path-or-uuid
value is either a path to a directory, in which case it is used as the root
filesystem, or a file that contains a uuid of the root filesystem, or it can be
a valid uuid of the intended root filesystem. If the
.Fl \-chain
option is not specified, the root filesystem of
.Pa /etc/fstab
is used as described in
.Xr fstab 5 .
.Pp
Every block device and partition is scanned to determine if it is the filesystem
by matching the desired uuid. The root filesystem is checked for consistency
and mounted at
.Pa /tmp/fs.XXXXXX
and the
.Pa /dev
filesystem directory is bound at
.Pa /tmp/fs.XXXXXX/dev .
.Pp
Finally the
.Pa /sbin/init
program of the target root filesystem is run inside a chroot.
.Ss Configuration
Once the
.Nm
of the real root filesystem runs, it will process basic configuration files and
apply them:
.Pp
.Bl -tag -width "/etc/videomode" -compact -offset indent
.It Pa /etc/hostname
set hostname (see
.Xr hostname 5 )
.It Pa /etc/kblayout
set keyboard layout (see
.Xr kblayout 5 )
.It Pa /etc/videomode
set graphics resolution (see
.Xr videomode 5 )
.El
.Ss Session
Finally
.Nm
will start the target program according to its initialization target. This will
be a login screen, a root shell, or something else. If the process exits
abnormally
.Nm
will automatically restart it.
.Nm
will exit with the same exit status as the process if it exits
normally. The kernel decides whether to power off, reboot or halt based on this
exit status.
.Sh ENVIRONMENT
.Nm
sets the following environment variables.
.Bl -tag -width "INIT_PID"
.It Ev HOME
root's home directory
.It Ev INIT_PID
.Nm Ns 's
process id
.It Ev LOGNAME
root
.It Ev PATH
.Pa /bin:/sbin
.It Ev SHELL
root's shell
.It Ev TERM
sortix
.It Ev USER
root
.El
.Sh FILES
.Bl -tag -width "/etc/init/target" -compact
.It Pa /etc/init/target
default initialization target
.It Pa /etc/fstab
filesystem table (see
.Xr fstab 5 )
.It Pa /etc/hostname
hostname (see
.Xr hostname 5 )
.It Pa /etc/kblayout
keyboard layout (see
.Xr kblayout 5 )
.It Pa /etc/videomode
graphics resolution (see
.Xr videomode 5 )
.El
.Sh EXIT STATUS
.Nm
exits 0 if the kernel should power off, exits 1 if the kernel should reboot, or
exits 2 if the boot failed and the kernel should halt. Any other exit by the
initial
.Nm
will trigger a kernel panic.
.Nm
exits with the same exit status as its target session if it terminates normally.
.Sh SEE ALSO
.Xr fstab 5 ,
.Xr hostname 5 ,
.Xr kblayout 5 ,
.Xr videomode 5 ,
.Xr initrd 7 ,
.Xr kernel 7 ,
.Xr login 8 ,
.Xr update-initrd 8

File diff suppressed because it is too large Load Diff

View File

@ -586,8 +586,14 @@ static void BootThread(void* /*user*/)
switch ( status )
{
case 0: CPU::ShutDown();
case 1: CPU::Reboot();
case 0:
CPU::ShutDown();
case 1:
CPU::Reboot();
case 2:
Log::Print("kernel: fatal: Halting system due to init fatality\n");
Log::Sync();
HaltKernel();
default:
PanicF("Init returned with unexpected return code %i", status);
}
@ -618,7 +624,7 @@ static void InitThread(void* /*user*/)
dtable.Reset();
static char default_init_cmdline[] = "/bin/init";
static char default_init_cmdline[] = "/sbin/init";
if ( !init_cmdline )
init_cmdline = default_init_cmdline;

View File

@ -0,0 +1,46 @@
.Dd $Mdocdate: October 5 2015 $
.Dt VIDEOMODE 5
.Os
.Sh NAME
.Nm videomode
.Nd initial graphics resolution
.Sh SYNOPSIS
.Nm /etc/videomode
.Sh DESCRIPTION
The
.Nm videomode
file is read on boot by
.Xr init 8
and is used as the initial graphics resolution on the primary monitor. The
resolution provided by the bootloader remains in effect if the file is missing.
The resolution is usable only if the graphics card has a driver and the
resolution is supported.
.Sh FORMAT
.Ar width Ns x Ns Ar height Ns x Ns Ar bits-per-pixel
.Pp
The file specifies a graphics resolution as a single line with three numeric
fields separated by x characters and no whitespace whatsoever. The first field
is used as the
.Ar width
in pixels, the second field is used as the
.Ar height
in pixels, and the third field is used as the
.Ar bits-per-pixel .
.Sh FILES
.Bl -tag -width "/etc/videomode" -compact
.It Pa /etc/videomode
primary monitor graphics resolution.
.El
.Sh EXAMPLES
.Bd -literal
1920x1080x32
.Ed
.Sh SEE ALSO
.Xr chvideomode 1 ,
.Xr dispmsg_issue 2 ,
.Xr init 8
.Sh BUGS
This scheme only supports a single monitor. The kernel console doesn't handle
resolution changes gracefully, the console will be cleared and the console
cursor might be out of the screen upon resolution shrink. Early boot messages
will be unavailable.

View File

@ -14,7 +14,7 @@ that extracts it into the initial kernel memory root filesystem. The kernel
invokes the
.Xr init 8
extracted from the initrd as
.Pa /bin/init .
.Pa /sbin/init .
.Pp
The initrd is in the custom format made by
.Xr mkinitrd 8

View File

@ -22,7 +22,7 @@ The kernel extracts the initrd into the initial kernel memory root filesystem
and executes
.Xr init 8
as
.Pa /bin/init .
.Pa /sbin/init .
The computer is powered off if this process exits 0, rebooted if it exits 1,
halted if it exits 2, and paniced otherwise.
.Pp