mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
Add support for multiple mount points to init(8).
This commit is contained in:
parent
ebdb2a844f
commit
e32ca1d7b9
2 changed files with 406 additions and 322 deletions
28
init/init.8
28
init/init.8
|
@ -7,7 +7,6 @@
|
|||
.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
|
||||
|
@ -69,24 +68,19 @@ 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
|
||||
The chain target mounts the root filesystem as in
|
||||
.Pa /etc/fstab
|
||||
is used as described in
|
||||
.Xr fstab 5 .
|
||||
(see
|
||||
.Xr fstab 5) and runs the next
|
||||
.Nm
|
||||
program. This is used by
|
||||
.Xr update-initrd 8
|
||||
to make a bootstrap
|
||||
.Xr initrd 7 .
|
||||
.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
|
||||
Every block device and partition is scanned to determine if it is the root
|
||||
filesystem. It is checked for consistency if nessesary. It is
|
||||
mounted at
|
||||
.Pa /tmp/fs.XXXXXX
|
||||
and the
|
||||
.Pa /dev
|
||||
|
|
700
init/init.c++
700
init/init.c++
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014, 2015.
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014, 2015, 2016.
|
||||
|
||||
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
|
||||
|
@ -84,6 +84,8 @@ static char* join_paths(const char* a, const char* b)
|
|||
return result;
|
||||
}
|
||||
|
||||
static pid_t main_pid;
|
||||
|
||||
__attribute__((noreturn))
|
||||
__attribute__((format(printf, 1, 2)))
|
||||
static void fatal(const char* format, ...)
|
||||
|
@ -95,6 +97,8 @@ static void fatal(const char* format, ...)
|
|||
fprintf(stderr, "\n");
|
||||
fflush(stderr);
|
||||
va_end(ap);
|
||||
if ( getpid() == main_pid )
|
||||
exit(2);
|
||||
_exit(2);
|
||||
}
|
||||
|
||||
|
@ -122,11 +126,11 @@ static void note(const char* format, ...)
|
|||
va_end(ap);
|
||||
}
|
||||
|
||||
struct harddisk** hds = NULL;
|
||||
size_t hds_used = 0;
|
||||
size_t hds_length = 0;
|
||||
static struct harddisk** hds = NULL;
|
||||
static size_t hds_used = 0;
|
||||
static size_t hds_length = 0;
|
||||
|
||||
void prepare_filesystem(const char* path, struct blockdevice* bdev)
|
||||
static void prepare_filesystem(const char* path, struct blockdevice* bdev)
|
||||
{
|
||||
enum filesystem_error fserr = blockdevice_inspect_filesystem(&bdev->fs, bdev);
|
||||
if ( fserr == FILESYSTEM_ERROR_ABSENT ||
|
||||
|
@ -136,7 +140,7 @@ void prepare_filesystem(const char* path, struct blockdevice* bdev)
|
|||
return warning("probing: %s: %s", path, filesystem_error_string(fserr));
|
||||
}
|
||||
|
||||
bool prepare_block_device(void* ctx, const char* path)
|
||||
static bool prepare_block_device(void* ctx, const char* path)
|
||||
{
|
||||
(void) ctx;
|
||||
struct harddisk* hd = harddisk_openat(AT_FDCWD, path, O_RDONLY);
|
||||
|
@ -223,7 +227,7 @@ bool prepare_block_device(void* ctx, const char* path)
|
|||
return true;
|
||||
}
|
||||
|
||||
void prepare_block_devices()
|
||||
static void prepare_block_devices()
|
||||
{
|
||||
static bool done = false;
|
||||
if ( done )
|
||||
|
@ -240,9 +244,9 @@ struct device_match
|
|||
struct blockdevice* bdev;
|
||||
};
|
||||
|
||||
void search_by_uuid(const char* uuid_string,
|
||||
void (*cb)(void*, struct device_match*),
|
||||
void* ctx)
|
||||
static void search_by_uuid(const char* uuid_string,
|
||||
void (*cb)(void*, struct device_match*),
|
||||
void* ctx)
|
||||
{
|
||||
unsigned char uuid[16];
|
||||
uuid_from_string(uuid, uuid_string);
|
||||
|
@ -282,7 +286,7 @@ void search_by_uuid(const char* uuid_string,
|
|||
}
|
||||
}
|
||||
|
||||
void ensure_single_device_match(void* ctx, struct device_match* match)
|
||||
static void ensure_single_device_match(void* ctx, struct device_match* match)
|
||||
{
|
||||
struct device_match* result = (struct device_match*) ctx;
|
||||
if ( result->path )
|
||||
|
@ -296,7 +300,74 @@ void ensure_single_device_match(void* ctx, struct device_match* match)
|
|||
*result = *match;
|
||||
}
|
||||
|
||||
void set_hostname()
|
||||
struct mountpoint
|
||||
{
|
||||
struct fstab entry;
|
||||
char* entry_line;
|
||||
pid_t pid;
|
||||
char* absolute;
|
||||
};
|
||||
|
||||
static struct mountpoint* mountpoints = NULL;
|
||||
static size_t mountpoints_used = 0;
|
||||
static size_t mountpoints_length = 0;
|
||||
|
||||
static int sort_mountpoint(const void* a_ptr, const void* b_ptr)
|
||||
{
|
||||
const struct mountpoint* a = (const struct mountpoint*) a_ptr;
|
||||
const struct mountpoint* b = (const struct mountpoint*) b_ptr;
|
||||
return strcmp(a->entry.fs_file, b->entry.fs_file);
|
||||
}
|
||||
|
||||
static void load_fstab(void)
|
||||
{
|
||||
FILE* fp = fopen("/etc/fstab", "r");
|
||||
if ( !fp )
|
||||
{
|
||||
if ( errno == ENOENT )
|
||||
return;
|
||||
fatal("/etc/fstab: %m");
|
||||
}
|
||||
char* line = NULL;
|
||||
size_t line_size;
|
||||
ssize_t line_length;
|
||||
while ( 0 < (errno = 0, line_length = getline(&line, &line_size, fp)) )
|
||||
{
|
||||
if ( line[line_length - 1] == '\n' )
|
||||
line[--line_length] = '\0';
|
||||
struct fstab fstabent;
|
||||
if ( !scanfsent(line, &fstabent) )
|
||||
continue;
|
||||
if ( mountpoints_used == mountpoints_length )
|
||||
{
|
||||
size_t new_length = 2 * mountpoints_length;
|
||||
if ( !new_length )
|
||||
new_length = 16;
|
||||
struct mountpoint* new_mountpoints = (struct mountpoint*)
|
||||
reallocarray(mountpoints, new_length, sizeof(struct mountpoint));
|
||||
if ( !new_mountpoints )
|
||||
fatal("malloc: %m");
|
||||
mountpoints = new_mountpoints;
|
||||
mountpoints_length = new_length;
|
||||
}
|
||||
struct mountpoint* mountpoint = &mountpoints[mountpoints_used++];
|
||||
memcpy(&mountpoint->entry, &fstabent, sizeof(fstabent));
|
||||
mountpoint->entry_line = line;
|
||||
mountpoint->pid = -1;
|
||||
if ( !(mountpoint->absolute = strdup(mountpoint->entry.fs_file)) )
|
||||
fatal("malloc: %m");
|
||||
line = NULL;
|
||||
line_size = 0;
|
||||
}
|
||||
if ( errno )
|
||||
fatal("/etc/fstab: %m");
|
||||
free(line);
|
||||
fclose(fp);
|
||||
qsort(mountpoints, mountpoints_used, sizeof(struct mountpoint),
|
||||
sort_mountpoint);
|
||||
}
|
||||
|
||||
static void set_hostname()
|
||||
{
|
||||
FILE* fp = fopen("/etc/hostname", "r");
|
||||
if ( !fp && errno == ENOENT )
|
||||
|
@ -313,7 +384,7 @@ void set_hostname()
|
|||
return warning("unable to set hostname: `%s': %m", hostname);
|
||||
}
|
||||
|
||||
void set_kblayout()
|
||||
static void set_kblayout()
|
||||
{
|
||||
FILE* fp = fopen("/etc/kblayout", "r");
|
||||
if ( !fp && errno == ENOENT )
|
||||
|
@ -338,7 +409,7 @@ void set_kblayout()
|
|||
free(kblayout);
|
||||
}
|
||||
|
||||
void set_videomode()
|
||||
static void set_videomode()
|
||||
{
|
||||
FILE* fp = fopen("/etc/videomode", "r");
|
||||
if ( !fp && errno == ENOENT )
|
||||
|
@ -384,7 +455,7 @@ void set_videomode()
|
|||
xres, yres, bpp);
|
||||
}
|
||||
|
||||
void init_early()
|
||||
static void init_early()
|
||||
{
|
||||
static bool done = false;
|
||||
if ( done )
|
||||
|
@ -407,18 +478,252 @@ void init_early()
|
|||
fatal("setenv: %m");
|
||||
}
|
||||
|
||||
int init(const char* target)
|
||||
static bool is_chain_init_mountpoint(const struct mountpoint* mountpoint)
|
||||
{
|
||||
return !strcmp(mountpoint->entry.fs_file, "/");
|
||||
}
|
||||
|
||||
static struct filesystem* mountpoint_lookup(const struct mountpoint* mountpoint)
|
||||
{
|
||||
const char* path = mountpoint->entry.fs_file;
|
||||
const char* spec = mountpoint->entry.fs_spec;
|
||||
if ( strncmp(spec, "UUID=", strlen("UUID=")) == 0 )
|
||||
{
|
||||
const char* uuid = spec + strlen("UUID=");
|
||||
if ( !uuid_validate(uuid) )
|
||||
{
|
||||
warning("%s: `%s' is not a valid uuid", path, uuid);
|
||||
return NULL;
|
||||
}
|
||||
struct device_match match;
|
||||
memset(&match, 0, sizeof(match));
|
||||
search_by_uuid(uuid, ensure_single_device_match, &match);
|
||||
if ( !match.path )
|
||||
{
|
||||
warning("%s: No devices matching uuid %s were found", path, uuid);
|
||||
return NULL;
|
||||
}
|
||||
if ( !match.bdev )
|
||||
{
|
||||
warning("%s: Don't know which particular device to boot with uuid "
|
||||
"%s", path, uuid);
|
||||
return NULL;
|
||||
}
|
||||
assert(match.bdev->fs);
|
||||
return match.bdev->fs;
|
||||
}
|
||||
// TODO: Lookup by device name.
|
||||
// TODO: Use this function in the chain init case too.
|
||||
warning("%s: Don't know how to resolve `%s' to a filesystem", path, spec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool mountpoint_mount(struct mountpoint* mountpoint)
|
||||
{
|
||||
struct filesystem* fs = mountpoint_lookup(mountpoint);
|
||||
if ( !fs )
|
||||
return false;
|
||||
// TODO: It would be ideal to get an exclusive lock so that no other
|
||||
// processes have currently mounted that filesystem.
|
||||
struct blockdevice* bdev = fs->bdev;
|
||||
const char* bdev_path = bdev->p ? bdev->p->path : bdev->hd->path;
|
||||
assert(bdev_path);
|
||||
do if ( fs->flags & (FILESYSTEM_FLAG_FSCK_SHOULD | FILESYSTEM_FLAG_FSCK_MUST) )
|
||||
{
|
||||
assert(fs->fsck);
|
||||
if ( fs->flags & FILESYSTEM_FLAG_FSCK_MUST )
|
||||
note("%s: Repairing filesystem due to inconsistency...", bdev_path);
|
||||
else
|
||||
note("%s: Checking filesystem consistency...", bdev_path);
|
||||
pid_t child_pid = fork();
|
||||
if ( child_pid < 0 )
|
||||
{
|
||||
if ( fs->flags & FILESYSTEM_FLAG_FSCK_MUST )
|
||||
{
|
||||
warning("%s: Mandatory repair failed: fork: %m", bdev_path);
|
||||
return false;
|
||||
}
|
||||
warning("%s: Skipping filesystem check: fork: %m:", bdev_path);
|
||||
break;
|
||||
}
|
||||
if ( child_pid == 0 )
|
||||
{
|
||||
execlp(fs->fsck, fs->fsck, "-fp", "--", bdev_path, (const char*) NULL);
|
||||
note("%s: Failed to load filesystem checker: %s: %m", bdev_path, fs->fsck);
|
||||
_exit(127);
|
||||
}
|
||||
int code;
|
||||
if ( waitpid(child_pid, &code, 0) < 0 )
|
||||
fatal("waitpid: %m");
|
||||
if ( WIFEXITED(code) &&
|
||||
(WEXITSTATUS(code) == 0 || WEXITSTATUS(code) == 1) )
|
||||
{
|
||||
// Successfully checked filesystem.
|
||||
}
|
||||
else if ( fs->flags & FILESYSTEM_FLAG_FSCK_MUST )
|
||||
{
|
||||
if ( WIFSIGNALED(code) )
|
||||
warning("%s: Mandatory repair failed: %s: %s", bdev_path,
|
||||
fs->fsck, strsignal(WTERMSIG(code)));
|
||||
else if ( !WIFEXITED(code) )
|
||||
warning("%s: Mandatory repair failed: %s: %s", bdev_path,
|
||||
fs->fsck, "Unexpected unusual termination");
|
||||
else if ( WEXITSTATUS(code) == 127 )
|
||||
warning("%s: Mandatory repair failed: %s: %s", bdev_path,
|
||||
fs->fsck, "Filesystem checker is absent");
|
||||
else if ( WEXITSTATUS(code) & 2 )
|
||||
warning("%s: Mandatory repair: %s: %s", bdev_path,
|
||||
fs->fsck, "System reboot is necessary");
|
||||
else
|
||||
warning("%s: Mandatory repair failed: %s: %s", bdev_path,
|
||||
fs->fsck, "Filesystem checker was unsuccessful");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool ignore = false;
|
||||
if ( WIFSIGNALED(code) )
|
||||
warning("%s: Filesystem check failed: %s: %s", bdev_path,
|
||||
fs->fsck, strsignal(WTERMSIG(code)));
|
||||
else if ( !WIFEXITED(code) )
|
||||
warning("%s: Filesystem check failed: %s: %s", bdev_path,
|
||||
fs->fsck, "Unexpected unusual termination");
|
||||
else if ( WEXITSTATUS(code) == 127 )
|
||||
{
|
||||
warning("%s: Skipping filesystem check: %s: %s", bdev_path,
|
||||
fs->fsck, "Filesystem checker is absent");
|
||||
ignore = true;
|
||||
}
|
||||
else if ( WEXITSTATUS(code) & 2 )
|
||||
warning("%s: Filesystem check: %s: %s", bdev_path,
|
||||
fs->fsck, "System reboot is necessary");
|
||||
else
|
||||
warning("%s: Filesystem check failed: %s: %s", bdev_path,
|
||||
fs->fsck, "Filesystem checker was unsuccessful");
|
||||
if ( !ignore )
|
||||
return false;
|
||||
}
|
||||
} while ( 0 );
|
||||
if ( !fs->driver )
|
||||
{
|
||||
warning("%s: Don't know how to mount a %s filesystem",
|
||||
bdev_path, fs->fstype_name);
|
||||
return false;
|
||||
}
|
||||
const char* pretend_where = mountpoint->entry.fs_file;
|
||||
const char* where = mountpoint->absolute;
|
||||
struct stat st;
|
||||
if ( stat(where, &st) < 0 )
|
||||
{
|
||||
warning("stat: %s: %m", where);
|
||||
return false;
|
||||
}
|
||||
if ( (mountpoint->pid = fork()) < 0 )
|
||||
{
|
||||
warning("%s: Unable to mount: fork: %m", bdev_path);
|
||||
return false;
|
||||
}
|
||||
// TODO: This design is broken. The filesystem should tell us when it is
|
||||
// ready instead of having to poll like this.
|
||||
if ( mountpoint->pid == 0 )
|
||||
{
|
||||
execlp(fs->driver, fs->driver, "--foreground", bdev_path, where,
|
||||
"--pretend-mount-path", pretend_where, (const char*) NULL);
|
||||
warning("%s: Failed to load filesystem driver: %s: %m", bdev_path, fs->driver);
|
||||
_exit(127);
|
||||
}
|
||||
while ( true )
|
||||
{
|
||||
struct stat newst;
|
||||
if ( stat(where, &newst) < 0 )
|
||||
{
|
||||
warning("stat: %s: %m", where);
|
||||
if ( unmount(where, 0) < 0 && errno != ENOMOUNT )
|
||||
warning("unmount: %s: %m", where);
|
||||
else if ( errno == ENOMOUNT )
|
||||
kill(mountpoint->pid, SIGQUIT);
|
||||
int code;
|
||||
waitpid(mountpoint->pid, &code, 0);
|
||||
mountpoint->pid = -1;
|
||||
return false;
|
||||
}
|
||||
if ( newst.st_dev != st.st_dev || newst.st_ino != st.st_ino )
|
||||
break;
|
||||
int code;
|
||||
pid_t child = waitpid(mountpoint->pid, &code, WNOHANG);
|
||||
if ( child < 0 )
|
||||
fatal("waitpid: %m");
|
||||
if ( child != 0 )
|
||||
{
|
||||
mountpoint->pid = -1;
|
||||
if ( WIFSIGNALED(code) )
|
||||
warning("%s: Mount failed: %s: %s", bdev_path, fs->driver,
|
||||
strsignal(WTERMSIG(code)));
|
||||
else if ( !WIFEXITED(code) )
|
||||
warning("%s: Mount failed: %s: %s", bdev_path, fs->driver,
|
||||
"Unexpected unusual termination");
|
||||
else if ( WEXITSTATUS(code) == 127 )
|
||||
warning("%s: Mount failed: %s: %s", bdev_path, fs->driver,
|
||||
"Filesystem driver is absent");
|
||||
else if ( WEXITSTATUS(code) == 0 )
|
||||
warning("%s: Mount failed: %s: Unexpected successful exit",
|
||||
bdev_path, fs->driver);
|
||||
else
|
||||
warning("%s: Mount failed: %s: Exited with status %i", bdev_path,
|
||||
fs->driver, WEXITSTATUS(code));
|
||||
return false;
|
||||
}
|
||||
struct timespec delay = timespec_make(0, 50L * 1000L * 1000L);
|
||||
nanosleep(&delay, NULL);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void mountpoints_mount(bool is_chain_init)
|
||||
{
|
||||
for ( size_t i = 0; i < mountpoints_used; i++ )
|
||||
{
|
||||
struct mountpoint* mountpoint = &mountpoints[i];
|
||||
if ( is_chain_init_mountpoint(mountpoint) != is_chain_init )
|
||||
continue;
|
||||
mountpoint_mount(mountpoint);
|
||||
}
|
||||
}
|
||||
|
||||
static void mountpoints_unmount(void)
|
||||
{
|
||||
for ( size_t n = mountpoints_used; n != 0; n-- )
|
||||
{
|
||||
size_t i = n - 1;
|
||||
struct mountpoint* mountpoint = &mountpoints[i];
|
||||
if ( mountpoint->pid < 0 )
|
||||
continue;
|
||||
if ( unmount(mountpoint->absolute, 0) < 0 && errno != ENOMOUNT )
|
||||
warning("unmount: %s: %m", mountpoint->entry.fs_file);
|
||||
else if ( errno == ENOMOUNT )
|
||||
kill(mountpoint->pid, SIGQUIT);
|
||||
int code;
|
||||
if ( waitpid(mountpoint->pid, &code, 0) < 0 )
|
||||
note("waitpid: %m");
|
||||
mountpoint->pid = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int init(const char* target)
|
||||
{
|
||||
if ( getenv("INIT_PID") )
|
||||
fatal("System is already managed by an init process");
|
||||
init_early();
|
||||
set_hostname();
|
||||
set_kblayout();
|
||||
set_videomode();
|
||||
prepare_block_devices();
|
||||
load_fstab();
|
||||
if ( atexit(mountpoints_unmount) != 0 )
|
||||
fatal("atexit: %m");
|
||||
mountpoints_mount(false);
|
||||
sigset_t oldset, sigttou;
|
||||
sigemptyset(&sigttou);
|
||||
sigaddset(&sigttou, SIGTTOU);
|
||||
int result;
|
||||
while ( true )
|
||||
{
|
||||
struct termios tio;
|
||||
|
@ -481,59 +786,77 @@ int init(const char* target)
|
|||
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||
const char* back = ": Trying to bring it back up again";
|
||||
if ( WIFEXITED(status) )
|
||||
return WEXITSTATUS(status);
|
||||
{
|
||||
result = WEXITSTATUS(status);
|
||||
break;
|
||||
}
|
||||
else if ( WIFSIGNALED(status) )
|
||||
note("session: %s%s", strsignal(WTERMSIG(status)), back);
|
||||
else
|
||||
note("session: Unexpected unusual termination%s", back);
|
||||
}
|
||||
mountpoints_unmount();
|
||||
return result;
|
||||
}
|
||||
|
||||
static pid_t chain_init_pid_by_path = -1;
|
||||
static char* chain_mount_point_dev = NULL;
|
||||
static void init_chain_by_path_unmount(void)
|
||||
static bool chain_location_made = false;
|
||||
static char chain_location[] = "/tmp/fs.XXXXXX";
|
||||
static bool chain_location_dev_made = false;
|
||||
static char chain_location_dev[] = "/tmp/fs.XXXXXX/dev";
|
||||
static void init_chain_atexit(void)
|
||||
{
|
||||
if ( chain_init_pid_by_path != getpid() )
|
||||
return;
|
||||
if ( chain_mount_point_dev )
|
||||
if ( chain_location_dev_made )
|
||||
{
|
||||
unmount(chain_mount_point_dev, 0);
|
||||
free(chain_mount_point_dev);
|
||||
chain_mount_point_dev = NULL;
|
||||
unmount(chain_location_dev, 0);
|
||||
chain_location_dev_made = false;
|
||||
}
|
||||
if ( chain_location_made )
|
||||
{
|
||||
rmdir(chain_location);
|
||||
chain_location_made = false;
|
||||
}
|
||||
}
|
||||
|
||||
int init_chain_by_path(const char* path)
|
||||
static int init_chain(const char* target)
|
||||
{
|
||||
struct stat rootst;
|
||||
struct stat pathst;
|
||||
if ( stat("/", &rootst) < 0 )
|
||||
fatal("stat: /: %m");
|
||||
if ( stat(path, &pathst) < 0 )
|
||||
fatal("stat: %s: %m", path);
|
||||
if ( rootst.st_dev == pathst.st_dev && rootst.st_ino == pathst.st_ino )
|
||||
{
|
||||
if ( getenv("INIT_PID") )
|
||||
fatal("System is already managed by an init process");
|
||||
}
|
||||
init_early();
|
||||
chain_init_pid_by_path = getpid();
|
||||
if ( atexit(init_chain_by_path_unmount) != 0 )
|
||||
prepare_block_devices();
|
||||
load_fstab();
|
||||
if ( atexit(init_chain_atexit) != 0 )
|
||||
fatal("atexit: %m");
|
||||
if ( !(chain_mount_point_dev = join_paths(path, "dev")) )
|
||||
fatal("asprintf: %m");
|
||||
if ( mkdir(chain_mount_point_dev, 755) < 0 && errno != EEXIST )
|
||||
fatal("mkdir: %s: %m", chain_mount_point_dev);
|
||||
if ( !mkdtemp(chain_location) )
|
||||
fatal("mkdtemp: /tmp/fs.XXXXXX: %m");
|
||||
chain_location_made = true;
|
||||
bool found_root = false;
|
||||
for ( size_t i = 0; i < mountpoints_used; i++ )
|
||||
{
|
||||
struct mountpoint* mountpoint = &mountpoints[i];
|
||||
if ( !strcmp(mountpoint->entry.fs_file, "/") )
|
||||
found_root = true;
|
||||
char* absolute = join_paths(chain_location, mountpoint->absolute);
|
||||
free(mountpoint->absolute);
|
||||
mountpoint->absolute = absolute;
|
||||
}
|
||||
if ( !found_root )
|
||||
fatal("/etc/fstab: Root filesystem not found in filesystem table");
|
||||
if ( atexit(mountpoints_unmount) != 0 )
|
||||
fatal("atexit: %m");
|
||||
mountpoints_mount(true);
|
||||
snprintf(chain_location_dev, sizeof(chain_location_dev), "%s/dev",
|
||||
chain_location);
|
||||
if ( mkdir(chain_location_dev, 755) < 0 && errno != EEXIST )
|
||||
fatal("mkdir: %s: %m", chain_location_dev);
|
||||
int old_dev_fd = open("/dev", O_DIRECTORY | O_RDONLY);
|
||||
if ( old_dev_fd < 0 )
|
||||
fatal("%s: %m", "/dev");
|
||||
int new_dev_fd = open(chain_mount_point_dev, O_DIRECTORY | O_RDONLY);
|
||||
int new_dev_fd = open(chain_location_dev, O_DIRECTORY | O_RDONLY);
|
||||
if ( new_dev_fd < 0 )
|
||||
fatal("%s: %m", chain_mount_point_dev);
|
||||
fatal("%s: %m", chain_location_dev);
|
||||
if ( fsm_fsbind(old_dev_fd, new_dev_fd, 0) < 0 )
|
||||
fatal("mount: `%s' onto `%s': %m", "/dev", chain_mount_point_dev);
|
||||
fatal("mount: `%s' onto `%s': %m", "/dev", chain_location_dev);
|
||||
close(new_dev_fd);
|
||||
close(old_dev_fd);
|
||||
int result;
|
||||
while ( true )
|
||||
{
|
||||
pid_t child_pid = fork();
|
||||
|
@ -541,10 +864,11 @@ int init_chain_by_path(const char* path)
|
|||
fatal("fork: %m");
|
||||
if ( !child_pid )
|
||||
{
|
||||
if ( chroot(path) < 0 )
|
||||
fatal("chroot: %s: %m", path);
|
||||
if ( chroot(chain_location) < 0 )
|
||||
fatal("chroot: %s: %m", chain_location);
|
||||
if ( chdir("/") < 0 )
|
||||
fatal("chdir: %s: %m", path);
|
||||
fatal("chdir: %s: %m", chain_location);
|
||||
(void) target;
|
||||
unsetenv("INIT_PID");
|
||||
const char* argv[] = { "init", NULL };
|
||||
execv("/sbin/init", (char* const*) argv);
|
||||
|
@ -555,241 +879,20 @@ int init_chain_by_path(const char* path)
|
|||
fatal("waitpid");
|
||||
const char* back = ": Trying to bring it back up again";
|
||||
if ( WIFEXITED(status) )
|
||||
return WEXITSTATUS(status);
|
||||
{
|
||||
result = WEXITSTATUS(status);
|
||||
break;
|
||||
}
|
||||
else if ( WIFSIGNALED(status) )
|
||||
note("chain init: %s%s", strsignal(WTERMSIG(status)), back);
|
||||
else
|
||||
note("chain init: Unexpected unusual termination%s", back);
|
||||
}
|
||||
}
|
||||
|
||||
static pid_t chain_init_pid_by_filesystem = -1;
|
||||
static char* chain_mount_point = NULL;
|
||||
static pid_t chain_mount_pid = -1;
|
||||
static void init_chain_by_filesystem_unmount(void)
|
||||
{
|
||||
if ( chain_init_pid_by_filesystem != getpid() )
|
||||
return;
|
||||
if ( chain_mount_point )
|
||||
{
|
||||
if ( unmount(chain_mount_point, 0) < 0 && errno != ENOMOUNT )
|
||||
warning("unmount: %s: %m", chain_mount_point);
|
||||
else if ( errno == ENOMOUNT && 0 <= chain_mount_pid )
|
||||
kill(chain_mount_pid, SIGQUIT);
|
||||
}
|
||||
if ( 0 <= chain_mount_pid )
|
||||
{
|
||||
int code;
|
||||
if ( waitpid(chain_mount_pid, &code, 0) < 0 )
|
||||
note("waitpid: %m");
|
||||
chain_mount_pid = -1;
|
||||
}
|
||||
if ( chain_mount_point )
|
||||
{
|
||||
if ( chain_mount_point && rmdir(chain_mount_point) < 0 )
|
||||
warning("rmdir: %s: %m", chain_mount_point);
|
||||
free(chain_mount_point);
|
||||
}
|
||||
chain_mount_point = NULL;
|
||||
}
|
||||
|
||||
int init_chain_by_filesystem(struct filesystem* fs)
|
||||
{
|
||||
// TODO: It would be ideal to get an exclusive lock so that no other
|
||||
// processes have currently mounted that filesystem.
|
||||
struct blockdevice* bdev = fs->bdev;
|
||||
const char* bdev_path = bdev->p ? bdev->p->path : bdev->hd->path;
|
||||
assert(bdev_path);
|
||||
do if ( fs->flags & (FILESYSTEM_FLAG_FSCK_SHOULD | FILESYSTEM_FLAG_FSCK_MUST) )
|
||||
{
|
||||
assert(fs->fsck);
|
||||
if ( fs->flags & FILESYSTEM_FLAG_FSCK_MUST )
|
||||
note("%s: Repairing filesystem due to inconsistency...", bdev_path);
|
||||
else
|
||||
note("%s: Checking filesystem consistency...", bdev_path);
|
||||
pid_t child_pid = fork();
|
||||
if ( child_pid < 0 )
|
||||
{
|
||||
if ( fs->flags & FILESYSTEM_FLAG_FSCK_MUST )
|
||||
fatal("%s: Mandatory repair failed: fork: %m", bdev_path);
|
||||
warning("%s: Skipping filesystem check: fork: %m:", bdev_path);
|
||||
break;
|
||||
}
|
||||
if ( child_pid == 0 )
|
||||
{
|
||||
execlp(fs->fsck, fs->fsck, "-fp", "--", bdev_path, (const char*) NULL);
|
||||
note("%s: Failed to load filesystem checker: %s: %m", bdev_path, fs->fsck);
|
||||
_Exit(127);
|
||||
}
|
||||
int code;
|
||||
if ( waitpid(child_pid, &code, 0) < 0 )
|
||||
fatal("waitpid: %m");
|
||||
if ( fs->flags & FILESYSTEM_FLAG_FSCK_MUST )
|
||||
{
|
||||
if ( WIFSIGNALED(code) )
|
||||
fatal("%s: Mandatory repair failed: %s: %s", bdev_path,
|
||||
fs->fsck, strsignal(WTERMSIG(code)));
|
||||
else if ( !WIFEXITED(code) )
|
||||
fatal("%s: Mandatory repair failed: %s: %s", bdev_path,
|
||||
fs->fsck, "Unexpected unusual termination");
|
||||
else if ( WEXITSTATUS(code) == 127 )
|
||||
fatal("%s: Mandatory repair failed: %s: %s", bdev_path,
|
||||
fs->fsck, "Filesystem checker is absent");
|
||||
else if ( WEXITSTATUS(code) & 2 )
|
||||
fatal("%s: Mandatory repair: %s: %s", bdev_path,
|
||||
fs->fsck, "System reboot is necessary");
|
||||
else if ( WEXITSTATUS(code) != 0 && WEXITSTATUS(code) != 1 )
|
||||
fatal("%s: Mandatory repair failed: %s: %s", bdev_path,
|
||||
fs->fsck, "Filesystem checker was unsuccessful");
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( WIFSIGNALED(code) )
|
||||
fatal("%s: Filesystem check failed: %s: %s", bdev_path,
|
||||
fs->fsck, strsignal(WTERMSIG(code)));
|
||||
else if ( !WIFEXITED(code) )
|
||||
fatal("%s: Filesystem check failed: %s: %s", bdev_path,
|
||||
fs->fsck, "Unexpected unusual termination");
|
||||
else if ( WEXITSTATUS(code) == 127 )
|
||||
warning("%s: Skipping filesystem check: %s: %s", bdev_path,
|
||||
fs->fsck, "Filesystem checker is absent");
|
||||
else if ( WEXITSTATUS(code) & 2 )
|
||||
fatal("%s: Filesystem check: %s: %s", bdev_path,
|
||||
fs->fsck, "System reboot is necessary");
|
||||
else if ( WEXITSTATUS(code) != 0 && WEXITSTATUS(code) != 1 )
|
||||
fatal("%s: Filesystem check failed: %s: %s", bdev_path,
|
||||
fs->fsck, "Filesystem checker was unsuccessful");
|
||||
}
|
||||
} while ( 0 );
|
||||
if ( !fs->driver )
|
||||
fatal("%s: Don't know how to mount a %s filesystem",
|
||||
bdev_path, fs->fstype_name);
|
||||
chain_init_pid_by_filesystem = getpid();
|
||||
if ( atexit(init_chain_by_filesystem_unmount) != 0 )
|
||||
fatal("atexit: %m");
|
||||
if ( !(chain_mount_point = strdup("/tmp/fs.XXXXXX")) )
|
||||
fatal("strdup: %m");
|
||||
if ( !mkdtemp(chain_mount_point) )
|
||||
fatal("mkdtemp: %s: %m", "/tmp/fs.XXXXXX");
|
||||
struct stat st;
|
||||
if ( stat(chain_mount_point, &st) < 0 )
|
||||
fatal("stat: %s: %m", chain_mount_point);
|
||||
if ( (chain_mount_pid = fork()) < 0 )
|
||||
fatal("%s: Unable to mount: fork: %m", bdev_path);
|
||||
// TODO: This design is broken. The filesystem should tell us when it is
|
||||
// ready instead of having to poll like this.
|
||||
if ( chain_mount_pid == 0 )
|
||||
{
|
||||
execlp(fs->driver, fs->driver, "--foreground", "--pretend-mount-path=/",
|
||||
bdev_path, chain_mount_point, (const char*) NULL);
|
||||
warning("%s: Failed to load filesystem driver: %s: %m", bdev_path, fs->driver);
|
||||
_exit(127);
|
||||
}
|
||||
while ( true )
|
||||
{
|
||||
struct stat newst;
|
||||
if ( stat(chain_mount_point, &newst) < 0 )
|
||||
fatal("stat: %s: %m", chain_mount_point);
|
||||
if ( newst.st_dev != st.st_dev || newst.st_ino != st.st_ino )
|
||||
break;
|
||||
int code;
|
||||
pid_t child = waitpid(chain_mount_pid, &code, WNOHANG);
|
||||
if ( child < 0 )
|
||||
fatal("waitpid: %m");
|
||||
if ( child != 0 )
|
||||
{
|
||||
chain_mount_pid = -1;
|
||||
if ( WIFSIGNALED(code) )
|
||||
fatal("%s: Mount failed: %s: %s", bdev_path, fs->driver,
|
||||
strsignal(WTERMSIG(code)));
|
||||
else if ( !WIFEXITED(code) )
|
||||
fatal("%s: Mount failed: %s: %s", bdev_path, fs->driver,
|
||||
"Unexpected unusual termination");
|
||||
else if ( WEXITSTATUS(code) == 127 )
|
||||
fatal("%s: Mount failed: %s: %s", bdev_path, fs->driver,
|
||||
"Filesystem driver is absent");
|
||||
else if ( WEXITSTATUS(code) == 0 )
|
||||
fatal("%s: Mount failed: %s: Unexpected successful exit",
|
||||
bdev_path, fs->driver);
|
||||
else
|
||||
fatal("%s: Mount failed: %s: Exited with status %i", bdev_path,
|
||||
fs->driver, WEXITSTATUS(code));
|
||||
}
|
||||
struct timespec delay = timespec_make(0, 50L * 1000L * 1000L);
|
||||
nanosleep(&delay, NULL);
|
||||
}
|
||||
int result = init_chain_by_path(chain_mount_point);
|
||||
init_chain_by_filesystem_unmount();
|
||||
mountpoints_unmount();
|
||||
init_chain_atexit();
|
||||
return result;
|
||||
}
|
||||
|
||||
int init_chain_by_uuid(const char* uuid)
|
||||
{
|
||||
init_early();
|
||||
prepare_block_devices();
|
||||
if ( !uuid_validate(uuid) )
|
||||
fatal("`%s' is not a valid uuid", uuid);
|
||||
struct device_match match;
|
||||
memset(&match, 0, sizeof(match));
|
||||
search_by_uuid(uuid, ensure_single_device_match, &match);
|
||||
if ( !match.path )
|
||||
fatal("No devices matching uuid %s were found", uuid);
|
||||
if ( !match.bdev )
|
||||
fatal("Don't know which particular device to boot with uuid %s", uuid);
|
||||
assert(match.bdev->fs);
|
||||
return init_chain_by_filesystem(match.bdev->fs);
|
||||
}
|
||||
|
||||
int init_chain_by_uuid_file(const char* path)
|
||||
{
|
||||
// Find the uuid of the root filesystem.
|
||||
FILE* fp = fopen(path, "r");
|
||||
if ( !fp )
|
||||
fatal("%s: %m", path);
|
||||
char* uuid = read_single_line(fp);
|
||||
if ( !uuid )
|
||||
fatal("read: %s: %m", path);
|
||||
fclose(fp);
|
||||
if ( !uuid_validate(uuid) )
|
||||
fatal("%s: `%s' is not a valid uuid", path, uuid);
|
||||
int result = init_chain_by_uuid(uuid);
|
||||
free(uuid);
|
||||
return result;
|
||||
}
|
||||
|
||||
int init_chain(const char* parameter)
|
||||
{
|
||||
bool parameter_was_null = parameter == NULL;
|
||||
if ( !parameter )
|
||||
{
|
||||
if ( !setfsent() )
|
||||
fatal("/etc/fstab: %m");
|
||||
errno = 0;
|
||||
struct fstab* fs = getfsfile("/");
|
||||
if ( !fs && errno )
|
||||
fatal("/etc/fstab: %m");
|
||||
if ( !fs )
|
||||
fatal("/etc/fstab: Root filesystem not found in filesystem table");
|
||||
parameter = fs->fs_spec;
|
||||
if ( strncmp(parameter, "UUID=", strlen("UUID=")) == 0 )
|
||||
parameter += strlen("UUID=");
|
||||
}
|
||||
if ( uuid_validate(parameter) )
|
||||
return init_chain_by_uuid(parameter);
|
||||
struct stat st;
|
||||
if ( stat(parameter, &st) < 0 )
|
||||
error(2, errno, "%s", parameter);
|
||||
if ( S_ISDIR(st.st_mode) && !parameter_was_null )
|
||||
return init_chain_by_path(parameter);
|
||||
// TODO: Implement this case so it works in /etc/fstab.
|
||||
//else if ( S_ISBLK(st.st_mode) )
|
||||
// return init_chain_by_device(parameter);
|
||||
else if ( S_ISREG(st.st_mode) && !parameter_was_null )
|
||||
return init_chain_by_uuid_file(parameter);
|
||||
else
|
||||
fatal("%s: Don't how how to chain initialize this", parameter);
|
||||
}
|
||||
|
||||
static void compact_arguments(int* argc, char*** argv)
|
||||
{
|
||||
for ( int i = 0; i < *argc; i++ )
|
||||
|
@ -819,9 +922,10 @@ static void version(FILE* fp, const char* argv0)
|
|||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
main_pid = getpid();
|
||||
|
||||
setlocale(LC_ALL, "");
|
||||
|
||||
const char* chain = NULL;
|
||||
const char* target = NULL;
|
||||
|
||||
const char* argv0 = argv[0];
|
||||
|
@ -843,19 +947,6 @@ int main(int argc, char* argv[])
|
|||
exit(1);
|
||||
}
|
||||
}
|
||||
else if ( !strncmp(arg, "--chain=", strlen("--chain=")) )
|
||||
chain = arg + strlen("--chain=");
|
||||
else if ( !strcmp(arg, "--chain") )
|
||||
{
|
||||
if ( i + 1 == argc )
|
||||
{
|
||||
error(0, 0, "option '--chain' requires an argument");
|
||||
fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]);
|
||||
exit(125);
|
||||
}
|
||||
chain = argv[i+1];
|
||||
argv[++i] = NULL;
|
||||
}
|
||||
else if ( !strncmp(arg, "--target=", strlen("--target=")) )
|
||||
target = arg + strlen("--target=");
|
||||
else if ( !strcmp(arg, "--target") )
|
||||
|
@ -887,9 +978,7 @@ int main(int argc, char* argv[])
|
|||
if ( !target )
|
||||
{
|
||||
const char* target_path = "/etc/init/target";
|
||||
if ( chain )
|
||||
target = "chain";
|
||||
else if ( access(target_path, F_OK) == 0 )
|
||||
if ( access(target_path, F_OK) == 0 )
|
||||
{
|
||||
FILE* target_fp = fopen(target_path, "r");
|
||||
if ( !target_fp )
|
||||
|
@ -904,14 +993,15 @@ int main(int argc, char* argv[])
|
|||
target = "single-user";
|
||||
}
|
||||
|
||||
int result;
|
||||
if ( !strcmp(target, "chain") )
|
||||
return init_chain(chain);
|
||||
else if ( !strcmp(target, "single-user") ||
|
||||
!strcmp(target, "multi-user") )
|
||||
if ( getenv("INIT_PID") )
|
||||
fatal("System is already managed by an init process");
|
||||
|
||||
if ( !strcmp(target, "single-user") ||
|
||||
!strcmp(target, "multi-user") )
|
||||
return init(target);
|
||||
else
|
||||
fatal("Unknown initialization target `%s'", target);
|
||||
free(target_string);
|
||||
return result;
|
||||
|
||||
if ( !strcmp(target, "chain") )
|
||||
return init_chain(target);
|
||||
|
||||
fatal("Unknown initialization target `%s'", target);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue