commit 17213e5f318e3d51a625b89db55b2b97bf62fbcc
Author: Friedel Schoen <[email protected]>
Date: Wed, 28 Dec 2022 02:35:15 +0100
first commit
Diffstat:
13 files changed, 980 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -0,0 +1,34 @@
+SOURCE_PATH := src
+OBJECT_PATH := dist
+BIN_PATH := bin
+EXECUTABLE := init
+EXEC_SRC_PATH := src/exec
+
+CC := gcc
+CFLAGS := -O2 -Wall -Wextra -Iincl
+LDFLAGS := -static
+
+EXEC_SRC_FILES := $(wildcard $(EXEC_SRC_PATH)/*.c)
+EXEC_FILES := $(patsubst $(EXEC_SRC_PATH)/%.c,$(BIN_PATH)/%,$(EXEC_SRC_FILES))
+
+SOURCE_FILES := $(wildcard $(SOURCE_PATH)/*.c)
+OBJECT_FILES := $(patsubst $(SOURCE_PATH)/%.c,$(OBJECT_PATH)/%.o,$(SOURCE_FILES))
+
+.PRECIOUS: $(OBJECT_PATH)/%.o
+
+all: compile_flags.txt $(EXEC_FILES)
+
+clean:
+ rm -rf $(BIN_PATH) $(OBJECT_PATH)
+
+$(BIN_PATH) $(OBJECT_PATH):
+ mkdir -p $@
+
+$(OBJECT_PATH)/%.o: $(SOURCE_PATH)/%.c Makefile $(HEADER_FILES) | $(OBJECT_PATH)
+ $(CC) -o $@ -c $(CFLAGS) $<
+
+$(BIN_PATH)/%: $(EXEC_SRC_PATH)/%.c $(OBJECT_FILES) $(HEADER_FILES) | $(BIN_PATH)
+ $(CC) -o $@ $(CFLAGS) $< $(OBJECT_FILES) $(LDFLAGS) $(LDFLAGS_$(patsubst $(BIN_PATH)/%,%,$@))
+
+compile_flags.txt: Makefile
+ echo $(CFLAGS) | tr " " "\n" > compile_flags.txt
diff --git a/docs/config.md b/docs/config.md
@@ -0,0 +1,128 @@
+# DUAL-INIT
+
+## Config Commands
+
+### Controlling
+
+#### `include <path>`
+
+> includes an file
+
+| parameter | description |
+| --------- | --------------------- |
+| path | absolute path to init |
+
+---
+
+#### `end`
+
+> ends a section
+
+### General Configuration
+
+#### `section <name> <root>`
+
+> starts the definition of a section (has to be closed with `end`)
+
+| parameter | description |
+| --------- | --------------------------- |
+| name | name of the section |
+| root | path to the root of section |
+
+---
+
+#### `mount`
+
+> starts a mount-section which has to be closed with `end`
+
+---
+
+#### `mount-default <enable>`
+
+> defines if color should be enabled
+
+| parameter | description |
+| ------------------------ | -------------------------- |
+| enable (`true`\|`false`) | if color-output is enabled |
+
+---
+
+#### `mount-master <enable>`
+
+> defines if color should be enabled
+
+| parameter | description |
+| ------------------------ | -------------------------- |
+| enable (`true`\|`false`) | if color-output is enabled |
+
+---
+
+#### `color <enable>`
+
+> defines if color should be enabled
+
+| parameter | description |
+| ------------------------ | -------------------------- |
+| enable (`true`\|`false`) | if color-output is enabled |
+
+---
+
+#### `verbose <enable>`
+
+> defines if color should be enabled
+
+| parameter | description |
+| ------------------------ | ---------------------------- |
+| enable (`true`\|`false`) | if verbose-output is enabled |
+
+---
+
+#### `timeout <duration>`
+
+> set timeout to duration
+
+| parameter | description |
+| --------- | ------------------ |
+| duration | timeout in seconds |
+
+### Section Configuration
+
+#### `mount`
+
+> starts a mount-section which has to be closed with `end`
+
+---
+
+#### `master`
+
+> defines this section as master
+
+---
+
+#### `default`
+
+> defines this section as default
+
+---
+
+#### `init <path> [args...]`
+
+> defines the init with possible args (defaults to `/sbin/init`)
+
+| parameter | description |
+| --------- | ------------------------------------ |
+| path | path to init |
+| args | arguments you want to pass to `init` |
+
+### Mount Configuration
+
+#### `<type> <source> <target> [options]`
+
+> defines a new mount-point
+
+| parameter | description |
+| --------- | ------------------------------- |
+| type | partition-type |
+| source | source |
+| target | target relative to the new root |
+| options | an optional option-list |
diff --git a/incl/common.h b/incl/common.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#define INFO(format...) \
+ { \
+ if (verbose && color) { \
+ printf("\e[36m::\e[0m " format); \
+ } else if (verbose) \
+ printf(":: " format); \
+ }
+
+#define WARN(format...) \
+ { \
+ if (color) \
+ printf("\e[1;35mwarn\e[0m: " format); \
+ else \
+ printf("error: " format); \
+ }
+
+#ifdef IS_CLI
+# define DIE exit(1)
+#else
+# define DIE \
+ while (1) \
+ ;
+#endif
+
+#define PANIC(format...) \
+ { \
+ if (color) \
+ printf("\e[1;31merror\e[0m: " format); \
+ else \
+ printf("error: " format); \
+ DIE; \
+ }
+
+#define STRDUPN(s) \
+ (*(s) != '\0' ? strdup(s) : NULL)
+
+#define streq(a, b) \
+ (strcmp((a), (b)) == 0)
diff --git a/incl/config.h b/incl/config.h
@@ -0,0 +1,53 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#define SECTION_MAX 20
+#define SECTION_MOUNT_MAX 100
+#define PATH_MAX 200
+
+typedef struct mount {
+ const char* type;
+ const char* source;
+ const char* target;
+ const char* options;
+ int flags;
+ bool try;
+} mount_t;
+
+typedef struct section {
+ const char* name;
+ const char* root;
+ const char* init;
+ mount_t mounts[SECTION_MOUNT_MAX];
+ int mount_size;
+} section_t;
+
+typedef enum parse_error {
+ P_ALLOC, // cannot allocate line
+ P_COMMAND, // invalid command
+ P_USAGE, // invalid usage of command
+ P_SCOPE, // invalid scope
+ P_DATA, // parameter has invalid type
+ P_SECTION, // no section defined
+ P_REDEF, // init, master, default called more than once
+} parse_error_t;
+
+
+extern section_t sections[];
+extern int section_size;
+extern section_t* master;
+extern section_t* default_s;
+extern bool color;
+extern bool verbose;
+extern mount_t mounts[];
+extern int mount_size;
+extern bool mount_default;
+extern bool mount_master;
+extern int timeout;
+
+parse_error_t parse_config_f(FILE* file, const char* filename);
+parse_error_t parse_config(int fd, const char* filename);
+void free_mount(mount_t* mnt);
+void free_section(section_t* mnt);
+\ No newline at end of file
diff --git a/incl/console.h b/incl/console.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void init_console();
+\ No newline at end of file
diff --git a/incl/default.h b/incl/default.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "config.h"
+
+#ifndef DEFAULT_CONFIG
+# define DEFAULT_CONFIG "/etc/dualinit.conf"
+#endif
+
+#ifndef DEFAULT_INIT
+# define DEFAULT_INIT "/sbin/init"
+#endif
+
+#ifndef REBOOT_INSTR
+# define REBOOT_INSTR "/etc/dualinit-reboot.txt"
+#endif
+
+#ifndef DEFAULT_EXEC_PATH
+# define DEFAULT_EXEC_PATH "/usr/share/dualinit/bin/init"
+#endif
+
+extern const mount_t DEFAULT_MOUNTS[];
+
+extern const char* DEFAULT_MASTER_MOUNTS[];
+\ No newline at end of file
diff --git a/incl/mount.h b/incl/mount.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <stdbool.h>
+
+
+typedef struct mount_option {
+ const char* name;
+ int flags;
+ bool invert;
+} mount_option_t;
+
+extern const struct mount_option mount_options[];
+
+int mount_flags(const char* options, const char** dest);
+\ No newline at end of file
diff --git a/readme.md b/readme.md
@@ -0,0 +1,22 @@
+## Directory Structure
+
+### Minimal Directure Hierachie
+
+```
+/
+├── boot/
+│ ├── ...
+│ ├── initramfs-x.x.img
+│ └── vmlinuz-x.x
+├── dev/
+├── etc/
+│ └── dualinit.conf
+├── proc/
+├── sbin/
+│ └── init
+├── sys/
+└── <environments>/
+```
+
+By default `/boot` will be binded (mounted) to ≶new_root≫/boot and
+`/sbin/init` to ≶new_root≫/sbin/dualboot linked
+\ No newline at end of file
diff --git a/src/config.c b/src/config.c
@@ -0,0 +1,362 @@
+#include "config.h"
+
+#include "common.h"
+#include "mount.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/fcntl.h>
+#include <unistd.h>
+
+#define CHECK_SECTION \
+ if (current_section == NULL) { \
+ result = P_USAGE; \
+ goto error; \
+ }
+
+#define CHECK_ROOT \
+ if (current_section != NULL) { \
+ result = P_USAGE; \
+ goto error; \
+ }
+
+#define CHECK_PARAMS_EQUALS(n) \
+ if (columns_size != (n)) { \
+ result = P_USAGE; \
+ goto error; \
+ }
+
+#define CHECK_PARAMS_BETWEEN(a, b) \
+ if (columns_size < (a) || columns_size > (b)) { \
+ result = P_USAGE; \
+ goto error; \
+ }
+
+#define CHECK_PARAMS_MORE(a) \
+ if (columns_size < (a)) { \
+ result = P_USAGE; \
+ goto error; \
+ }
+
+section_t sections[SECTION_MAX];
+mount_t mounts[SECTION_MOUNT_MAX];
+int section_size = 0;
+int mount_size = 0;
+section_t* master = NULL;
+section_t* default_s = NULL;
+bool color = false;
+bool verbose = true;
+bool mount_default = true;
+bool mount_master = true;
+int timeout = 10;
+
+parse_error_t parse_config(int fd, const char* filename) {
+ FILE* file = fdopen(fd, "r");
+ return parse_config_f(file, filename);
+}
+
+parse_error_t parse_config_f(FILE* file, const char* filename) {
+ section_t* current_section = NULL;
+ bool in_mount = false;
+ int linenr = 0;
+ parse_error_t result = 0;
+ int columns_size = 0;
+ size_t alloc = 0;
+ char* line_origin = NULL;
+ ssize_t len = 0;
+ ssize_t last_blank = 0;
+ char* line = NULL;
+ char columns[10][100];
+
+ while ((len = getline(&line_origin, &alloc, file)) > 0) {
+ linenr++;
+ if (len + 1 > (ssize_t) alloc) {
+ line = realloc(line_origin, alloc = len + 1);
+ if (line == NULL) {
+ printf("error: cannot allocate line\n");
+ result = P_ALLOC;
+ goto error;
+ }
+ line_origin = line;
+ } else {
+ line = line_origin;
+ }
+ line[len] = '\0';
+
+ last_blank = -1;
+
+ while (isblank(line[0]))
+ line++, len--;
+
+ for (ssize_t i = 0; i < len; i++) {
+ if (isblank(line[i])) {
+ if (last_blank == -1)
+ last_blank = i;
+ } else if (line[i] == '\n' || line[i] == ';' || line[i] == '#') {
+ len = (last_blank != -1) ? last_blank : i;
+ break;
+ } else {
+ last_blank = -1;
+ }
+ }
+
+ if (len == 0)
+ continue;
+
+ columns_size = 0;
+ int column_index = 0;
+ bool string = false;
+ for (ssize_t i = 0; i <= len; i++) {
+ if (i == len || (isblank(line[i]) && !string)) {
+ if (column_index == 1 && columns[columns_size][0] == '-') {
+ columns[columns_size][0] = '\0';
+ columns_size++;
+ column_index = 0;
+ } else if (column_index > 0) {
+ columns[columns_size][column_index] = '\0';
+ columns_size++;
+ column_index = 0;
+ }
+ } else if (line[i] == '"') {
+ string = !string;
+ } else {
+ columns[columns_size][column_index++] = line[i];
+ }
+ }
+
+ if (streq(columns[0], "end")) {
+ CHECK_PARAMS_EQUALS(1);
+
+ if (in_mount) {
+ in_mount = false;
+ } else if (current_section != NULL) {
+ current_section = NULL;
+ } else {
+ result = P_SCOPE;
+ goto error;
+ }
+ } else if (in_mount) {
+ CHECK_PARAMS_BETWEEN(3, 4);
+
+ mount_t* mnt = (current_section != NULL)
+ ? ¤t_section->mounts[current_section->mount_size++]
+ : &mounts[mount_size++];
+
+ mnt->try = columns[0][0] == '*';
+ mnt->type = STRDUPN(mnt->try ? columns[0] + 1 : columns[0]);
+ mnt->source = STRDUPN(columns[1]);
+ mnt->target = STRDUPN(columns[2]);
+ if (columns_size == 4) {
+ mnt->flags = mount_flags(columns[3], &mnt->options);
+ } else {
+ mnt->flags = 0;
+ mnt->options = NULL;
+ }
+ } else if (streq(columns[0], "mount")) {
+ CHECK_PARAMS_EQUALS(1);
+
+ in_mount = true;
+ } else if (streq(columns[0], "section")) {
+ CHECK_ROOT;
+ CHECK_PARAMS_EQUALS(3);
+
+ if (current_section != NULL) {
+ result = P_REDEF;
+ goto error;
+ }
+
+ current_section = §ions[section_size++];
+ current_section->init = NULL;
+ current_section->mount_size = 0;
+ current_section->name = STRDUPN(columns[1]);
+ current_section->root = STRDUPN(columns[2]);
+
+ if (master == NULL)
+ master = current_section;
+ if (default_s == NULL)
+ default_s = current_section;
+ } else if (streq(columns[0], "color")) {
+ CHECK_ROOT;
+ CHECK_PARAMS_EQUALS(2);
+
+ if (streq(columns[1], "true"))
+ color = true;
+ else if (streq(columns[1], "false")) {
+ color = false;
+ } else {
+ result = P_DATA;
+ goto error;
+ }
+ } else if (streq(columns[0], "verbose")) {
+ CHECK_ROOT;
+ CHECK_PARAMS_EQUALS(2);
+
+ if (streq(columns[1], "true"))
+ verbose = true;
+ else if (streq(columns[1], "false")) {
+ verbose = false;
+ } else {
+ result = P_DATA;
+ goto error;
+ }
+ } else if (streq(columns[0], "mount-default")) {
+ CHECK_ROOT;
+ CHECK_PARAMS_EQUALS(2);
+
+ if (streq(columns[1], "true"))
+ mount_default = true;
+ else if (streq(columns[1], "false")) {
+ mount_default = false;
+ } else {
+ result = P_DATA;
+ goto error;
+ }
+ } else if (streq(columns[0], "mount-master")) {
+ CHECK_ROOT;
+ CHECK_PARAMS_EQUALS(2);
+
+ if (streq(columns[1], "true"))
+ mount_master = true;
+ else if (streq(columns[1], "false")) {
+ mount_master = false;
+ } else {
+ result = P_DATA;
+ goto error;
+ }
+ } else if (streq(columns[0], "master")) {
+ CHECK_SECTION;
+ CHECK_PARAMS_EQUALS(1);
+
+ master = current_section;
+ } else if (streq(columns[0], "default")) {
+ CHECK_SECTION;
+ CHECK_PARAMS_EQUALS(1);
+
+ default_s = current_section;
+ } else if (streq(columns[0], "timeout")) {
+ CHECK_ROOT;
+ CHECK_PARAMS_EQUALS(2);
+
+ char* end;
+ timeout = strtol(columns[1], &end, 10);
+ if (end != strchr(columns[1], '\0')) {
+ result = P_DATA;
+ goto error;
+ }
+ } else if (streq(columns[0], "init")) {
+ CHECK_SECTION;
+ CHECK_PARAMS_MORE(2);
+
+ if (current_section->init != NULL) {
+ result = P_REDEF;
+ goto error;
+ }
+
+ current_section->init = STRDUPN(columns[1]);
+ } else if (streq(columns[0], "include")) {
+ CHECK_ROOT;
+ CHECK_PARAMS_EQUALS(2);
+
+ int fd = open(columns[1], O_RDONLY | O_NONBLOCK);
+ result = parse_config(fd, columns[1]);
+ close(fd);
+ if (result != 0)
+ goto cleanup;
+ } else {
+ result = P_COMMAND;
+ goto error;
+ }
+ }
+
+ if (section_size == 0) {
+ result = P_SECTION;
+ goto error;
+ }
+
+ // if you reach this, there were no errors
+ // just skip to `cleanup`
+ goto cleanup;
+
+error:
+ printf("error in %s:%d: ", filename, linenr);
+ switch (result) {
+ case P_ALLOC:
+ printf("cannot allocate line\n");
+ break;
+ case P_SECTION:
+ printf("no section defined\n");
+ break;
+ case P_COMMAND:
+ printf("unknown command '%s'\n", columns[0]);
+ break;
+ case P_USAGE:
+ printf("invalid usage of command '%s'\n", columns[0]);
+ break;
+ case P_SCOPE:
+ printf("invalid scope of command '%s'\n", columns[0]);
+ break;
+ case P_DATA:
+ printf("invalid paramter-type of '%s'\n", columns[0]);
+ break;
+ case P_REDEF:
+ printf("redefinition of '%s'\n", columns[0]);
+ break;
+ }
+
+cleanup:
+ if (line_origin != NULL)
+ free(line_origin);
+
+ return result;
+}
+
+void free_mount(mount_t* mnt) {
+ if (mnt->type != NULL)
+ free((void*) mnt->type);
+ if (mnt->source != NULL)
+ free((void*) mnt->source);
+ if (mnt->target != NULL)
+ free((void*) mnt->target);
+ if (mnt->options != NULL)
+ free((void*) mnt->options);
+}
+
+void free_section(section_t* mnt) {
+ if (mnt->name != NULL)
+ free((void*) mnt->name);
+ if (mnt->root != NULL)
+ free((void*) mnt->root);
+ if (mnt->init != NULL)
+ free((void*) mnt->init);
+}
+
+
+#if 0
+int main() {
+ int fd = open("chinit.conf", O_RDONLY | O_NONBLOCK);
+
+ parse_code_t code = parse_config(fd, "chinit.conf");
+
+ close(fd);
+
+ if (code != P_SUCCESS)
+ return 1;
+
+
+ for (int j = 0; j < mount_size; j++) {
+ printf("- %s%s -> %s [%s] (%s)\n", mounts[j].try ? "try " : "", mounts[j].source, mounts[j].target, mounts[j].type, mounts[j].options);
+ }
+ for (int i = 0; i < section_size; i++) {
+ if (sections[i].name[0] != '\0') {
+ printf("%s at %s (%s)\n", sections[i].name, sections[i].root, sections[i].init);
+ if (§ions[i] == master)
+ printf(" *");
+ }
+ for (int j = 0; j < sections[i].mount_size; j++) {
+ printf("- %s%s -> %s [%s] (%s)\n", sections[i].mounts[j].try ? "try " : "", sections[i].mounts[j].source, sections[i].mounts[j].target, sections[i].mounts[j].type, sections[i].mounts[j].options);
+ }
+ }
+}
+#endif
+\ No newline at end of file
diff --git a/src/console.c b/src/console.c
@@ -0,0 +1,17 @@
+#include "console.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+void init_console() {
+ int in = open("/dev/console", O_RDONLY, 0);
+ int out = open("/dev/console", O_RDWR, 0);
+ dup2(in, 0);
+ dup2(out, 1);
+ dup2(out, 2);
+
+ if (in > 2)
+ close(in);
+ if (out > 2)
+ close(out);
+}
diff --git a/src/default.c b/src/default.c
@@ -0,0 +1,23 @@
+#include "default.h"
+
+#include <sys/mount.h>
+
+const mount_t DEFAULT_MOUNTS[] = {
+ { NULL, "/dev", "/dev", NULL, MS_BIND | MS_REC, false },
+ // - /dev /dev rbind
+ { NULL, "/", "/dualinit", NULL, MS_BIND, false },
+ // - / /dualinit bind
+ { "proc", "proc", "/proc", NULL, MS_RELATIME, false },
+ // proc proc /proc relatime
+ { "tmpfs", "run", "/run", "mode=0755", 0, false },
+ // tmpfs run /run mode=0755
+ { NULL, "/sys", "/sys", NULL, MS_BIND | MS_REC, false },
+ // - /sys /sys rbind
+ { "tmpfs", "tmp", "/tmp", "mode=1777", MS_STRICTATIME, false },
+ // tmpfs tmp /tmp mode=1777,strictatime
+ { 0 },
+};
+
+const char* DEFAULT_MASTER_MOUNTS[] = {
+ "/boot", "/lost+found", NULL
+};
+\ No newline at end of file
diff --git a/src/exec/dualinit.c b/src/exec/dualinit.c
@@ -0,0 +1,137 @@
+#include "common.h"
+#include "config.h"
+#include "console.h"
+#include "default.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <unistd.h>
+
+
+void mount_chroot(const char* root, const mount_t* mnt) {
+ static char dest[100];
+ strcpy(dest, root);
+ strcat(dest, mnt->target);
+
+ INFO("mounting %s -> %s", mnt->source, dest);
+ if (mnt->type != NULL)
+ printf(" (%s)", mnt->type);
+ if (mnt->options != NULL)
+ printf(" [%s]", mnt->options);
+
+ printf("\n");
+
+ if (mount(mnt->source, dest, mnt->type, mnt->flags, mnt->options) != 0) {
+ if (mnt->try) {
+ WARN("mounting %s to %s failed: %s\n", mnt->source, dest, strerror(errno));
+ } else {
+ PANIC("mounting %s to %s failed: %s\n", mnt->source, dest, strerror(errno));
+ }
+ }
+}
+
+int main() {
+ init_console();
+
+ if (getpid() != 1) {
+ PANIC("must run as PID 1\n");
+ }
+
+ int choice_file = open(DEFAULT_CONFIG, O_RDONLY | O_NONBLOCK);
+ if (choice_file == -1) {
+ PANIC("cannot open %s: %s\n", DEFAULT_CONFIG, strerror(errno));
+ }
+
+ parse_error_t parse_code = parse_config(choice_file, DEFAULT_CONFIG);
+ if (parse_code != 0) {
+ PANIC("invalid config");
+ }
+ close(choice_file);
+
+ int section_index;
+ while (1) {
+ printf("which init do you want to start?\n");
+ for (int i = 0; i < section_size; i++) {
+ printf("[%d] %s at %s\n", i, sections[i].name, sections[i].root);
+ }
+
+ printf(": ");
+ fflush(stdout);
+
+ scanf("%d", §ion_index);
+ if (section_index >= 0 && section_index < section_size)
+ break;
+
+ WARN("your choice %d must be lower than %d\n\n", section_index, section_size);
+ }
+
+ section_t* section = §ions[section_index];
+ bool is_root = strcmp(section->root, "/") == 0;
+
+ if (!is_root) {
+ mount_t self_mount = {
+ .type = NULL,
+ .source = section->root,
+ .target = "/",
+ .options = NULL,
+ .flags = MS_BIND,
+ .try = false,
+ };
+
+ mount_chroot(section->root, &self_mount);
+
+ for (int i = 0; i < mount_size; i++) {
+ mount_chroot(section->root, &mounts[i]);
+ free_mount(&mounts[i]);
+ }
+
+ if (mount_default) {
+ for (const mount_t* mnt = DEFAULT_MOUNTS; mnt->target != NULL; mnt++) {
+ mount_chroot(section->root, mnt);
+ }
+ }
+
+ if (mount_master) {
+ for (const char** mnt = DEFAULT_MASTER_MOUNTS; *mnt; mnt++) {
+ self_mount.source = *mnt;
+ self_mount.target = *mnt;
+ mount_chroot(section->root, &self_mount);
+ }
+ }
+ }
+
+ for (int i = 0; i < section->mount_size; i++) {
+ mount_chroot(section->root, §ion->mounts[i]);
+ free_mount(&mounts[i]);
+ }
+
+ if (!is_root) {
+ INFO("chrooting into %s\n", section->root);
+
+ if (chroot(section->root) == -1) {
+ PANIC("cannot chroot into %s: %s\n", section->root, strerror(errno));
+ }
+ }
+
+ if (chdir("/") == -1) {
+ PANIC("error: cannot chdir into '/': %s\n", strerror(errno));
+ }
+
+ char init[PATH_MAX] = DEFAULT_INIT;
+ if (section->init != NULL)
+ strcpy(init, section->init);
+
+ for (int i = 0; i < section_index; i++)
+ free_section(§ion[i]);
+
+ INFO("entering %s\n\n", init);
+
+ if (execlp(section->init, section->init, NULL) == -1) {
+ PANIC("error: cannot execute %s: %s\n", section->init, strerror(errno));
+ }
+}
diff --git a/src/mount.c b/src/mount.c
@@ -0,0 +1,116 @@
+#include "mount.h"
+
+#include <stdbool.h>
+#include <string.h>
+#include <sys/mount.h>
+
+
+const struct mount_option mount_options[] = {
+ { "ro", MS_RDONLY, false }, /* read-only */
+ { "rw", MS_RDONLY, true }, /* read-write */
+ { "exec", MS_NOEXEC, true }, /* permit execution of binaries */
+ { "noexec", MS_NOEXEC, false }, /* don't execute binaries */
+ { "suid", MS_NOSUID, true }, /* honor suid executables */
+ { "nosuid", MS_NOSUID, false }, /* don't honor suid executables */
+ { "dev", MS_NODEV, true }, /* interpret device files */
+ { "nodev", MS_NODEV, false }, /* don't interpret devices */
+
+ { "sync", MS_SYNCHRONOUS, false }, /* synchronous I/O */
+ { "async", MS_SYNCHRONOUS, true }, /* asynchronous I/O */
+
+ { "dirsync", MS_DIRSYNC, false }, /* synchronous directory modifications */
+ { "remount", MS_REMOUNT, true }, /* alter flags of mounted FS */
+ { "bind", MS_BIND, false }, /* Remount part of the tree elsewhere */
+ { "rbind", MS_BIND | MS_REC, false }, /* Idem, plus mounted subtrees */
+#ifdef MS_NOSUB
+ { "sub", MS_NOSUB, MNT_INVERT }, /* allow submounts */
+ { "nosub", MS_NOSUB }, /* don't allow submounts */
+#endif
+#ifdef MS_SILENT
+ { "silent", MS_SILENT, false }, /* be quiet */
+ { "loud", MS_SILENT, true }, /* print out messages. */
+#endif
+#ifdef MS_MANDLOCK
+ { "mand", MS_MANDLOCK, false }, /* Allow mandatory locks on this FS */
+ { "nomand", MS_MANDLOCK, true }, /* Forbid mandatory locks on this FS */
+#endif
+#ifdef MS_NOATIME
+ { "atime", MS_NOATIME, true }, /* Update access time */
+ { "noatime", MS_NOATIME, false }, /* Do not update access time */
+#endif
+#ifdef MS_I_VERSION
+ { "iversion", MS_I_VERSION, false }, /* Update inode I_version time */
+ { "noiversion", MS_I_VERSION, true }, /* Don't update inode I_version time */
+#endif
+#ifdef MS_NODIRATIME
+ { "diratime", MS_NODIRATIME, true }, /* Update dir access times */
+ { "nodiratime", MS_NODIRATIME, false }, /* Do not update dir access times */
+#endif
+#ifdef MS_RELATIME
+ { "relatime", MS_RELATIME, false }, /* Update access times relative to mtime/ctime */
+ { "norelatime", MS_RELATIME, true }, /* Update access time without regard to mtime/ctime */
+#endif
+#ifdef MS_STRICTATIME
+ { "strictatime", MS_STRICTATIME, false }, /* Strict atime semantics */
+ { "nostrictatime", MS_STRICTATIME, true }, /* kernel default atime */
+#endif
+#ifdef MS_LAZYTIME
+ { "lazytime", MS_LAZYTIME, false }, /* Update {a,m,c}time on the in-memory inode only */
+ { "nolazytime", MS_LAZYTIME, true },
+#endif
+#ifdef MS_NOSYMFOLLOW
+ { "symfollow", MS_NOSYMFOLLOW, true }, /* Don't follow symlinks */
+ { "nosymfollow", MS_NOSYMFOLLOW, false },
+#endif
+ { 0 }
+};
+
+int mount_flags(const char* options, const char** dest_ptr) {
+ int flags = 0;
+
+ char option[20];
+ char dest[strlen(options) + 1];
+ int option_size = 0;
+ int dest_size = 0;
+
+ while (1) {
+ if (*options == ',' || *options == '\0') {
+ option[option_size] = '\0';
+
+ for (const mount_option_t* flg = mount_options; flg->name != NULL; flg++) {
+ if (strcmp(flg->name, option) == 0) {
+ if (flg->invert)
+ flags &= ~flg->flags;
+ else
+ flags |= flg->flags;
+ option_size = 0;
+ break;
+ }
+ }
+
+ if (option_size > 0) {
+ if (dest_size > 0)
+ dest[dest_size++] = ',';
+ memcpy(dest + dest_size, option, option_size);
+ dest_size += option_size;
+ option_size = 0;
+ }
+
+ if (*options == '\0')
+ break;
+ } else {
+ option[option_size++] = *options;
+ }
+
+ options++;
+ }
+
+ if (dest_size > 0) {
+ dest[dest_size] = '\0';
+ *dest_ptr = strdup(dest);
+ } else {
+ *dest_ptr = NULL;
+ }
+
+ return flags;
+}
+\ No newline at end of file