commit: e1cc51964f3c4bcfe7a7eb40d0e6b0b808f128d8
parent c37486d428a84915ff852f88a8ab6017db26fc85
Author: fosslinux <fosslinux@aussies.space>
Date: Wed, 29 May 2024 08:51:34 +1000
Merge pull request #468 from fosslinux/configurator
Add interactive "configurator"
Diffstat:
15 files changed, 902 insertions(+), 34 deletions(-)
diff --git a/rootfs.py b/rootfs.py
@@ -49,6 +49,7 @@ def create_configuration_file(args):
config.write("DISK=sda1\n")
config.write("KERNEL_BOOTSTRAP=False\n")
config.write(f"BUILD_KERNELS={args.update_checksums or args.build_kernels}\n")
+ config.write(f"CONFIGURATOR={args.configurator}\n")
# pylint: disable=too-many-statements,too-many-branches
def main():
@@ -90,6 +91,9 @@ def main():
parser.add_argument("-i", "--interactive",
help="Use interactive prompts to resolve issues during bootstrap",
action="store_true")
+ parser.add_argument("--configurator",
+ help="Run the interactive configurator",
+ action="store_true")
parser.add_argument("-r", "--repo",
help="Path to prebuilt binary packages", nargs=None)
parser.add_argument("--early-preseed",
diff --git a/seed/configurator.c b/seed/configurator.c
@@ -0,0 +1,784 @@
+/*
+ * SPDX-FileCopyrightText: 2024 fosslinux <fosslinux@aussies.space>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define MAX_STRING 2048
+#define MAX_SHORT 512
+#define MAX_ID 128
+#define MAX_VAR 128
+
+#include <bootstrappable.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+#define TRUE 1
+#define FALSE 0
+
+#define KIND_NONE 0
+#define KIND_MENU 1
+#define KIND_OPTION 2
+
+#define TYPE_NONE 0
+#define TYPE_BOOL 1
+#define TYPE_SIZE 2
+#define TYPE_STRING 3
+#define TYPE_INT 4
+
+struct Entry {
+ int kind; // either menu or option
+ char *env_var; // name of the environment variable this option is stored in
+ char *id; // the id of the configuration item
+ char *short_desc; // short description of the config
+ char *full_desc; // extended description of the config
+
+ int type; // the type of the configuration option
+ char *validation; // any validation rules
+ char *val;
+ char *default_val;
+
+ struct Entry *children; // submenus
+ struct Entry *parent;
+ struct Entry *next;
+};
+typedef struct Entry Entry;
+
+Entry *find_entry(Entry *head, char *id) {
+ char *component = strchr(id, '/');
+ if (component == NULL) {
+ component = id + strlen(id);
+ }
+
+ Entry *current;
+ Entry *final;
+ int len;
+ while (1) {
+ len = component - id;
+
+ current = head;
+ while (current != NULL) {
+ /* ensure that the id isn't just a substring of the component but actually is the component */
+ if (strlen(current->id) == len && strncmp(id, current->id, len) == 0) {
+ /* Found it! */
+ final = current;
+ head = current->children;
+ break;
+ }
+ current = current->next;
+ }
+ if (current == NULL) {
+ /* Did not find it */
+ return NULL;
+ }
+
+ if (component[0] == '\0') {
+ break;
+ }
+
+ component += 1;
+ component = strchr(component, '/');
+ if (component == NULL) {
+ component = id + strlen(id);
+ }
+ }
+
+ return final;
+}
+
+Entry *get_parent(Entry *head, char *id) {
+ char *parent_id = calloc(MAX_ID, sizeof(char));
+ strcpy(parent_id, id);
+ char *final_slash = strrchr(parent_id, '/');
+ final_slash[0] = '\0';
+ Entry *ret = find_entry(head, parent_id);
+ free(parent_id);
+ return ret;
+}
+
+char read_string(FILE *f, char *out, int length) {
+ int i = 0;
+ char c = fgetc(f);
+ while (c != ' ' && c != '\n' && c != EOF && i < length - 1) {
+ out[i] = c;
+ i += 1;
+ c = fgetc(f);
+ }
+ if (i >= length - 1) {
+ fputs("String too long!\n", stdout);
+ fclose(f);
+ exit(1);
+ }
+ out[i] = '\0';
+ return c;
+}
+
+int set_val(Entry *entry, char *val) {
+ if (entry->type == TYPE_BOOL) {
+ if (strcmp(val, "True") != 0 && strcmp(val, "False") != 0) {
+ fputs("Invalid input: ", stdout);
+ fputs(val, stdout);
+ fputs(" is not a boolean value\n", stdout);
+ return 1;
+ }
+ } else if (entry->type == TYPE_INT) {
+ int intval = strtoint(val);
+ if (intval == 0 && strcmp(val, "0") != 0) {
+ fputs("Invalid input: ", stdout);
+ fputs(val, stdout);
+ fputs(" is not an integer\n", stdout);
+ return 1;
+ }
+ } else if (entry->type == TYPE_SIZE) {
+ /* We should have either a K, M, G, T, or no letter, at the end of the size. */
+ char c = val[strlen(val) - 1];
+ if (!(('0' <= c && c <= '9') || c == 'K' || c == 'M' || c == 'G' || c == 'T')) {
+ fputs("Invalid input: ", stdout);
+ fputc(c, stdout);
+ fputs(" is not a valid suffix for a size\n", stdout);
+ return 1;
+ }
+ /* Check it is an integer */
+ char *final_char = val + strlen(val) - 1;
+ if ('A' <= final_char[0] && final_char[0] <= 'Z') {
+ final_char[0] = '\0';
+ }
+ int intval = strtoint(val);
+ if (intval == 0 && strcmp(val, "0") != 0) {
+ fputs("Invalid input: ", stdout);
+ fputs(val, stdout);
+ fputs(" is not a valid size\n", stdout);
+ return 1;
+ }
+ final_char[0] = c;
+ } else if (entry->type == TYPE_STRING) {
+ /* Validation rules. */
+ char *validation = entry->validation;
+ char *next;
+ int found = FALSE;
+ while (validation != NULL) {
+ if (validation[0] == '\0') {
+ found = TRUE;
+ break;
+ }
+ next = strchr(validation, '|');
+ if (next == NULL) {
+ if (strcmp(validation, val) == 0) {
+ found = TRUE;
+ }
+ break;
+ } else {
+ if (strncmp(validation, val, next - validation) == 0) {
+ found = TRUE;
+ }
+ }
+ validation = next + 1;
+ }
+ if (found == FALSE) {
+ fputs("Invalid input: ", stdout);
+ fputs(val, stdout);
+ fputs(" does not match the validation rules ", stdout);
+ fputs(entry->validation, stdout);
+ fputc('\n', stdout);
+ return 1;
+ }
+ }
+
+ entry->val = calloc(strlen(val) + 1, sizeof(char));
+ strcpy(entry->val, val);
+ return 0;
+}
+
+void read_entry(char c, FILE *conf, Entry *head) {
+ Entry *new = calloc(1, sizeof(Entry));
+
+ /* Read the kind */
+ if (c == 'm') {
+ new->kind = KIND_MENU;
+ } else if (c == 'o') {
+ new->kind = KIND_OPTION;
+ } else {
+ fputs("Invalid entry: kind ", stdout);
+ fputc(c, stdout);
+ fputc('\n', stdout);
+ fclose(conf);
+ exit(1);
+ }
+ fgetc(conf);
+
+ /* Read the id */
+ new->id = calloc(MAX_ID, sizeof(char));
+ c = read_string(conf, new->id, MAX_ID);
+ if (c != ' ') {
+ fputs("Invalid entry: no variable\n", stdout);
+ fclose(conf);
+ exit(1);
+ }
+
+ /* Read the environment variable */
+ new->env_var = calloc(MAX_VAR, sizeof(char));
+ c = read_string(conf, new->env_var, MAX_VAR);
+ if (c != ' ') {
+ fputs("Invalid entry: no data type\n", stdout);
+ fclose(conf);
+ exit(1);
+ }
+ if (strcmp(new->env_var, "_") == 0) {
+ free(new->env_var);
+ new->env_var = NULL;
+ }
+
+ /* Read the data type */
+ char *data_type = calloc(MAX_ID, sizeof(char));
+ read_string(conf, data_type, MAX_ID);
+ if (c != ' ') {
+ fputs("Invalid entry: no default value\n", stdout);
+ fclose(conf);
+ exit(1);
+ }
+ if (strcmp(data_type, "_") == 0) {
+ new->type = TYPE_NONE;
+ } else if (strcmp(data_type, "bool") == 0) {
+ new->type = TYPE_BOOL;
+ } else if (strcmp(data_type, "size") == 0) {
+ new->type = TYPE_SIZE;
+ } else if (strcmp(data_type, "int") == 0) {
+ new->type = TYPE_INT;
+ } else if (data_type[0] == '"') {
+ new->type = TYPE_STRING;
+ new->validation = data_type + 1;
+ char *closing_quote = strrchr(data_type, '"');
+ closing_quote[0] = '\0';
+ } else {
+ fputs("Invalid entry: unknown type: ", stdout);
+ fputs(data_type, stdout);
+ fputc('\n', stdout);
+ fclose(conf);
+ exit(1);
+ }
+ if (new->type != TYPE_STRING) {
+ free(data_type);
+ }
+
+ /* Read the default value */
+ char *default_val = calloc(MAX_STRING, sizeof(char));
+ read_string(conf, default_val, MAX_ID);
+ if (strcmp(default_val, "_") != 0) {
+ set_val(new, default_val);
+ new->default_val = default_val;
+ } else {
+ new->default_val = NULL;
+ }
+
+ /* Read the short description */
+ new->short_desc = calloc(MAX_SHORT, sizeof(char));
+ int i = 0;
+ c = fgetc(conf);
+ while (c != '\n' && c != EOF) {
+ new->short_desc[i] = c;
+ c = fgetc(conf);
+ i += 1;
+ }
+
+ /* Read the long description */
+ new->full_desc = calloc(MAX_STRING, sizeof(char));
+ i = 0;
+ c = fgetc(conf);
+ char prev = '\0';
+ while (!(c == '\n' && prev == '\n') && c != EOF) {
+ new->full_desc[i] = c;
+ prev = c;
+ c = fgetc(conf);
+ i += 1;
+ }
+
+ new->children = NULL;
+ new->next = NULL;
+
+ Entry *parent = get_parent(head, new->id);
+ new->parent = parent;
+ if (parent->children == NULL) {
+ parent->children = new;
+ } else {
+ Entry *current = parent->children;
+ while (current->next != NULL) {
+ current = current->next;
+ }
+ current->next = new;
+ }
+}
+
+Entry *read_config(char *filename) {
+ FILE *conf = fopen(filename, "r");
+ if (conf == NULL) {
+ fputs("Unable to open ", stdout);
+ fputs(filename, stdout);
+ fputc('\n', stdout);
+ exit(0);
+ }
+ char c = fgetc(conf);
+
+ Entry *head = calloc(1, sizeof(Entry));
+ head->id = "";
+ head->env_var = "";
+ head->next = NULL;
+ Entry *current = head;
+
+ while (c != EOF) {
+ if (c == '#' || c == '\n') {
+ /* Skip comments or empty lines. */
+ while (c != '\n' && c != EOF) {
+ c = fgetc(conf);
+ }
+ } else {
+ read_entry(c, conf, head);
+ }
+ c = fgetc(conf);
+ }
+
+ fclose(conf);
+
+ return head;
+}
+
+Entry *get_env_var(Entry *head, char *var) {
+ Entry *ret;
+ Entry *current;
+ for (current = head->children; current != NULL; current = current->next) {
+ if (current->env_var != NULL) {
+ if (strcmp(current->env_var, var) == 0) {
+ return current;
+ }
+ }
+ if (current->children != NULL) {
+ ret = get_env_var(current, var);
+ if (ret != NULL) {
+ return ret;
+ }
+ }
+ }
+ return NULL;
+}
+
+int set_cfg_varline(Entry *head, char *line) {
+ char *var = calloc(strlen(line) + 1, sizeof(char));
+ strcpy(var, line);
+ char *val = strchr(var, '=');
+ val[0] = '\0';
+ val += 1;
+ char *newline = strchr(val, '\n');
+ if (newline != NULL) {
+ newline[0] = '\0';
+ }
+ Entry *entry = get_env_var(head, var);
+ if (entry != NULL) {
+ int not_ok = set_val(entry, val);
+ if (not_ok) {
+ fputs("^ Originated from ", stdout);
+ fputs(var, stdout);
+ fputs("=", stdout);
+ fputs(val, stdout);
+ fputs("\n", stdout);
+ }
+ }
+ return entry == NULL;
+}
+
+char *set_cfg_values(Entry *head, char **envp) {
+ int i = 0;
+
+ FILE *cfg = fopen("/steps/bootstrap.cfg", "r");
+ if (cfg == NULL) {
+ return "";
+ }
+
+ char *extra = calloc(MAX_STRING, sizeof(char));
+ char *line = calloc(MAX_STRING, sizeof(char));
+ while (fgets(line, MAX_STRING, cfg) != NULL) {
+ if (set_cfg_varline(head, line)) {
+ if (strncmp("CONFIGURATOR=", line, 13) != 0) {
+ strcat(extra, line);
+ }
+ }
+ free(line);
+ line = calloc(MAX_STRING, sizeof(char));
+ }
+
+ fclose(cfg);
+
+ return extra;
+}
+
+void write_cfg_list(Entry *head, FILE *cfg) {
+ Entry *current;
+ for (current = head->children; current != NULL; current = current->next) {
+ if (current->kind == KIND_OPTION && current->val != NULL) {
+ fputs(current->env_var, cfg);
+ fputs("=", cfg);
+ fputs(current->val, cfg);
+ fputs("\n", cfg);
+ }
+ if (current->children != NULL) {
+ write_cfg_list(current, cfg);
+ }
+ }
+}
+
+void write_cfg_values(Entry *head, char *extra, int configurator_done) {
+ FILE *cfg = fopen("/steps/bootstrap.cfg", "w");
+ if (cfg == NULL) {
+ fputs("Unable to open /steps/bootstrap.cfg", stderr);
+ exit(1);
+ }
+ if (configurator_done == TRUE) {
+ fputs("CONFIGURATOR=False\n", cfg);
+ }
+ fputs(extra, cfg);
+ write_cfg_list(head, cfg);
+ fclose(cfg);
+}
+
+void print_short_desc(char *short_desc) {
+ char *post_markers = strrchr(short_desc, ']');
+ if (post_markers == NULL) {
+ post_markers = short_desc;
+ } else {
+ post_markers += 1;
+ while (post_markers[0] == ' ') {
+ post_markers += 1;
+ }
+ }
+ fputs(post_markers, stdout);
+}
+
+void print_recursive_desc(Entry *entry) {
+ if (entry->parent != NULL) {
+ if (strcmp(entry->parent->id, "") != 0) {
+ print_recursive_desc(entry->parent);
+ fputs("/", stdout);
+ print_short_desc(entry->short_desc);
+ return;
+ }
+ }
+ print_short_desc(entry->short_desc);
+}
+
+int any_unset(Entry *head);
+
+int check_set(Entry *entry, Entry *head) {
+ int ret = 0;
+ if (entry->kind == KIND_OPTION && entry->val == NULL) {
+ fputs("The configuration option ", stdout);
+ print_recursive_desc(entry);
+ fputs(" is unset\n", stdout);
+ ret = 1;
+ }
+ if (entry->children != NULL) {
+ ret |= any_unset(entry);
+ }
+ return ret;
+}
+
+int any_unset(Entry *head) {
+ int ret = 0;
+ Entry *current;
+ for (current = head->children; current != NULL; current = current->next) {
+ ret |= check_set(current, head);
+ }
+ return ret;
+}
+
+void print_menu(Entry *menu, int is_toplevel) {
+ if (!is_toplevel) {
+ fputs("(0) [MENU] Go up\n", stdout);
+ }
+ int i = 1;
+ Entry *current;
+ for (current = menu->children; current != NULL; current = current->next) {
+ fputs("(", stdout);
+ fputs(int2str(i, 10, FALSE), stdout);
+ fputs(") ", stdout);
+ if (current->kind == KIND_MENU) {
+ fputs("[MENU] ", stdout);
+ }
+ fputs(current->short_desc, stdout);
+ fputc('\n', stdout);
+ i += 1;
+ }
+}
+
+Entry *get_nth_option(Entry *menu, int n) {
+ int i = 1;
+ Entry *current;
+ for (current = menu->children; current != NULL && i < n; current = current->next) {
+ i += 1;
+ }
+ if (current == NULL) {
+ fputs("There is no option ", stdout);
+ fputs(int2str(n, 10, FALSE), stdout);
+ fputs("!\n", stdout);
+ }
+ return current;
+}
+
+void how_to_use(void) {
+ fputs(
+ "How to navigate around this configuration menu:\n"
+ "h or help: at any time, will reprint this help message\n"
+ "l or list: shows the current menu options\n"
+ "o <num> or open <num>: open a (sub)menu\n"
+ "? <num> or describe <num>: provides a more detailed description of an option or menu\n"
+ "s <num> <val> or set <num> <val>: set the value of an option\n"
+ "g <num> or get <num>: get the value of an option\n"
+ "g all or get all: get the value of all options in the menu\n"
+ "r <num> or reset <num>: reset the value of an option to the default (if there is one)\n"
+ "e or exit: exits the program\n",
+ stdout);
+}
+
+Entry *extract_num(char **command, Entry *menu) {
+ command[0] = strchr(command[0], ' ');
+ if (command[0] == NULL) {
+ fputs("Expected menu number to operate on!\n", stdout);
+ }
+ command[0] += 1;
+ char *num = command[0];
+ char *new = strchr(command[0], ' ');
+ if (new == NULL) {
+ new = strchr(command[0], '\n');
+ }
+ command[0] = new;
+ command[0][0] = '\0';
+ command[0] += 1;
+ /* strtoint does not check if it is not a number */
+ int i;
+ for (i = 0; i < strlen(num); i += 1) {
+ if (!('0' <= num[i] && num[i] <= '9')) {
+ fputs(num, stdout);
+ fputs(" is not a menu number!\n", stdout);
+ return NULL;
+ }
+ }
+ int n = strtoint(num);
+ return get_nth_option(menu, n);
+}
+
+Entry *submenu(char *command, Entry *menu, Entry *head) {
+ command = strchr(command, ' ');
+ if (strlen(command) < 1) {
+ fputs("Expected menu number to operate on!\n", stdout);
+ }
+ /* 0 is the "go up" menu option */
+ if (command[1] == '0') {
+ if (strcmp(menu->id, "") == 0) {
+ fputs("There is no option 0!\n", stdout);
+ return menu;
+ }
+ return menu->parent;
+ }
+
+ Entry *new = extract_num(&command, menu);
+ if (new == NULL) {
+ return menu;
+ }
+ if (new->kind != KIND_MENU) {
+ fputs("This is not a menu!\n", stdout);
+ return menu;
+ }
+ return new;
+}
+
+void print_description(char *command, Entry *menu) {
+ Entry *opt = extract_num(&command, menu);
+ if (opt != NULL) {
+ fputs(opt->full_desc, stdout);
+ }
+}
+
+void set_opt_value(char *command, Entry *menu) {
+ Entry *opt = extract_num(&command, menu);
+ if (opt == NULL) {
+ return;
+ }
+ if (opt->kind != KIND_OPTION) {
+ fputs("Cannot set a menu's value!\n", stdout);
+ return;
+ }
+
+ /* Remove the newline */
+ char *newline = strchr(command, '\n');
+ newline[0] = '\0';
+ set_val(opt, command);
+}
+
+void print_opt_value(Entry *opt) {
+ print_short_desc(opt->short_desc);
+
+ fputs(": ", stdout);
+ if (opt->val == NULL) {
+ fputs("unset", stdout);
+ } else {
+ fputs(opt->val, stdout);
+ }
+ fputc('\n', stdout);
+}
+
+void get_opt_value(char *command, Entry *menu) {
+ Entry *opt = extract_num(&command, menu);
+ if (opt == NULL) {
+ return;
+ }
+ if (opt->kind != KIND_OPTION) {
+ fputs("Cannot get a menu's value!\n", stdout);
+ return;
+ }
+
+ print_opt_value(opt);
+}
+
+void get_all_values(Entry *menu) {
+ Entry *current;
+ for (current = menu->children; current != NULL; current = current->next) {
+ if (current->kind == KIND_OPTION) {
+ print_opt_value(current);
+ }
+ }
+}
+
+void reset_value(char *command, Entry *menu) {
+ Entry *opt = extract_num(&command, menu);
+ if (opt == NULL) {
+ return;
+ }
+ if (opt->kind != KIND_OPTION) {
+ fputs("Cannot reset a menu's value!\n", stdout);
+ return;
+ }
+ opt->val = opt->default_val;
+}
+
+void no_input(Entry *head) {
+ fputs("You don't seem to be running under Fiwix or Linux currently.\n", stdout);
+ fputs("Likely, you are currently running under builder-hex0.\n", stdout);
+ fputs("That's ok! We're going to make some assumptions; namely, that you do need\n", stdout);
+ fputs("the kernel bootstrap, and that you'll get a chance to configure later.\n", stdout);
+ write_cfg_values(head, "KERNEL_BOOTSTRAP=True\nBUILD_KERNELS=True\n", FALSE);
+}
+
+int main(int argc, char **argv, char **envp) {
+ /*
+ * Check we are being non-interactive and bootstrap.cfg exists in
+ * which case we do not need to do anything.
+ */
+ char *interactivity = getenv("CONFIGURATOR");
+ if (interactivity != NULL) {
+ if (strcmp(interactivity, "False") == 0) {
+ return 0;
+ }
+ }
+ FILE *bootstrap_cfg = fopen("/steps/bootstrap.cfg", "r");
+ if (bootstrap_cfg != NULL) {
+ char *line = calloc(MAX_STRING, sizeof(char));
+ while (fgets(line, MAX_STRING, bootstrap_cfg) != NULL) {
+ if (strcmp(line, "CONFIGURATOR=False\n") == 0) {
+ fclose(bootstrap_cfg);
+ return 0;
+ }
+ free(line);
+ line = calloc(MAX_STRING, sizeof(char));
+ }
+ fclose(bootstrap_cfg);
+ }
+
+ if (argc != 2) {
+ fputs("Usage: ", stdout);
+ fputs(argv[0], stdout);
+ fputs(" <configuration>\n", stdout);
+ exit(1);
+ }
+ Entry *head = read_config(argv[1]);
+ char *extra = set_cfg_values(head, envp);
+
+ /*
+ * Check if we are NOT running under fiwix or linux.
+ * If we are not, and need configuration to occur, then we presume that
+ * we will not be able to get any input from the user.
+ */
+ struct utsname *kernel = calloc(1, sizeof(struct utsname));
+ uname(kernel);
+ if (kernel->sysname == NULL) {
+ no_input(head);
+ return 0;
+ } else if (strcmp(kernel->sysname, "Linux") != 0 && strcmp(kernel->sysname, "Fiwix") != 0) {
+ no_input(head);
+ return 0;
+ }
+
+ fputs("Welcome to live-bootstrap!\n", stdout);
+ fputs("We need to do some brief configuration before continuing.\n\n", stdout);
+ how_to_use();
+ fputc('\n', stdout);
+
+ Entry *menu = head;
+ print_menu(menu, menu == head);
+ char *command = calloc(MAX_STRING, sizeof(char));
+ fputs("\nCommand: ", stdout);
+ fflush(stdout);
+ command = fgets(command, MAX_STRING, stdin);
+ while (command != NULL) {
+ if (strcmp("h\n", command) == 0 || strcmp("help\n", command) == 0) {
+ how_to_use();
+ } else if (strcmp("l\n", command) == 0 || strcmp("list\n", command) == 0) {
+ print_menu(menu, menu == head);
+ } else if (strncmp("o ", command, 2) == 0 || strncmp("open ", command, 5) == 0) {
+ menu = submenu(command, menu, head);
+ print_menu(menu, menu == head);
+ } else if (strncmp("? ", command, 2) == 0 || strncmp("describe ", command, 9) == 0) {
+ print_description(command, menu);
+ } else if (strcmp("g all\n", command) == 0 || strcmp("get all\n", command) == 0) {
+ get_all_values(menu);
+ } else if (strncmp("g ", command, 2) == 0 || strncmp("get ", command, 4) == 0) {
+ get_opt_value(command, menu);
+ } else if (strncmp("s ", command, 2) == 0 || strncmp("set ", command, 4) == 0) {
+ set_opt_value(command, menu);
+ } else if (strncmp("r ", command, 2) == 0 || strncmp("reset ", command, 6) == 0) {
+ reset_value(command, menu);
+ } else if (strcmp("e\n", command) == 0 || strcmp("exit\n", command) == 0) {
+ if (!any_unset(head)) {
+ break;
+ }
+ } else {
+ fputs("Unknown command ", stdout);
+ fputs(command, stdout);
+ }
+ fputs("\nCommand: ", stdout);
+ fflush(stdout);
+ /*
+ * M2-Planet's fgets does not properly terminate the buffer if there is
+ * already data in it
+ */
+ free(command);
+ command = calloc(MAX_STRING, sizeof(char));
+ command = fgets(command, MAX_STRING, stdin);
+ }
+
+ if (any_unset(head)) {
+ fputs(
+ "Uh oh! You have left me in a tough position - you can't input further because you\n"
+ "closed the input stream. But the inputs you gave me are not valid!\n"
+ "I'm going to re-exec myself and hope you are able to start again from scratch.\n",
+ stderr
+ );
+ execve(argv[0], argv, envp);
+ return 0;
+ }
+
+ write_cfg_values(head, extra, TRUE);
+
+ fputs("\nThank you! We will now continue with the bootstrap.\n", stdout);
+
+ return 0;
+}
diff --git a/seed/configurator.x86.checksums b/seed/configurator.x86.checksums
@@ -0,0 +1 @@
+8235581c60334314b5a8321b0b07d6fb905669dce81878c8cfed909377573e91 configurator
diff --git a/seed/script-generator.c b/seed/script-generator.c
@@ -176,30 +176,8 @@ char *get_var(char *name) {
last = var;
}
- /* If the variable is unset, prompt the user. */
- if (variables == NULL) {
- variables = calloc(1, sizeof(Variable));
- var = variables;
- } else {
- last->next = calloc(1, sizeof(Variable));
- var = last->next;
- }
- var->name = calloc(strlen(name) + 1, sizeof(char));
- strcpy(var->name, name);
- var->val = calloc(MAX_STRING, sizeof(char));
- fputs("You have not set a value for ", stdout);
- fputs(name, stdout);
- fputs(" in bootstrap.cfg. Please set it now:\n", stdout);
- while (fgets(var->val, MAX_STRING, stdin) == 0 || var->val[0] == '\n') {
- fputs("Error inputting, try again:\n", stdout);
- }
- if (var->val[0] == 0) {
- fputs("You put in an EOF!\n", stderr);
- exit(1);
- }
- /* Trim the newline. */
- var->val[strlen(var->val)] = 0;
- return var->val;
+ /* If the variable is unset, take it to be the empty string. */
+ return "";
}
/* Recursive descent interpreter. */
diff --git a/seed/script-generator.x86.checksums b/seed/script-generator.x86.checksums
@@ -1 +1 @@
-dc6106dbc02839cdc9e3e2348432242eb6d33d840ab74badfd63c3c9997462b9 script-generator
+a0ad0938f7a66b44674db2b0a2a0410966098b3cc511d8b1a4dadc77b1828088 script-generator
diff --git a/seed/seed.kaem b/seed/seed.kaem
@@ -64,6 +64,15 @@ MES_PKG=mes-0.26
MES_PREFIX=${SRCDIR}/${MES_PKG}/build/${MES_PKG}
GUILE_LOAD_PATH=${MES_PREFIX}/mes/module:${MES_PREFIX}/module:${SRCDIR}/${MES_PKG}/build/${NYACC_PKG}/module
+M2-Mesoplanet --architecture ${ARCH} -f configurator.c -o configurator
+# Checksums
+if match x${UPDATE_CHECKSUMS} xTrue; then
+ sha256sum -o configurator.${ARCH}.checksums configurator
+else
+ sha256sum -c configurator.${ARCH}.checksums
+fi
+./configurator /steps/configurator
+
M2-Mesoplanet --architecture ${ARCH} -f script-generator.c -o script-generator
# Checksums
if match x${UPDATE_CHECKSUMS} xTrue; then
@@ -72,4 +81,5 @@ else
sha256sum -c script-generator.${ARCH}.checksums
fi
./script-generator /steps/manifest
+
kaem --file /steps/0.sh
diff --git a/steps/SHA256SUMS.pkgs b/steps/SHA256SUMS.pkgs
@@ -77,7 +77,7 @@ b39826742e236890f3562cdf19492e7ef4224b271f3e75ddeab1f07982b03ebe libffi-3.3_0.t
daae709e98d2df2190d1d13b4e86f7f3fe90fa7a975282fe0bb03289b6539f29 libtool-2.2.4_0.tar.bz2
6cefa575362149620f8008a32c8af54f0198a18bc6ab910bd3cead196c1507d7 libtool-2.4.7_0.tar.bz2
503007bbcddcf4e49d26514c59b4c9501f8b42f0c994a59dfdc388b1ae6b7900 libunistring-0.9.10_0.tar.bz2
-5787f84a49e1d22560d0398e4f9075d6021017eb2a757697dc2877e7565d0199 linux-4.14.341-openela_0.tar.bz2
+540927c71fb1682175e32a655dfd4a987c494577549bf30e79ef3b1e4f039a4d linux-4.14.341-openela_0.tar.bz2
c97644d0db5b3de127b048683afee6d31453441d97ba5dea71df5838b13542a4 linux-headers-4.14.341-openela_0.tar.bz2
78b0cf6d9312e53c613186cbddd5f747310f375c1f322f33a6ac33682d2f3389 m4-1.4.19_0.tar.bz2
0e3c21b0a1d8ca0c3f74a98ebe268809def62778ff4a486ff20c1d6e8247dc49 m4-1.4.7_0.tar.bz2
diff --git a/steps/checksum-transcriber-1.0/checksum-transcriber-1.0.x86.checksums b/steps/checksum-transcriber-1.0/checksum-transcriber-1.0.x86.checksums
@@ -1 +1 @@
-19fd50d727e8a7feba8b6e369e68287d1922d8f623a156fb113d994891e96998 /usr/bin/checksum-transcriber
+59cc0fb361f84e81a1cda6111ef847188d6c7c839c5a52166d9185ca767cf920 /usr/bin/checksum-transcriber
diff --git a/steps/configurator b/steps/configurator
@@ -0,0 +1,81 @@
+# SPDX-FileCopyrightText: 2024 fosslinux <fosslinux@aussies.space>
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# The structure of this file:
+# <option type> <id> <variable> <type> <default> <short description>
+# <long description>
+#
+# repeat
+
+m /general _ _ _ General configuration
+Settings for how you would like live-bootstrap to run.
+
+o /general/timestamps FORCE_TIMESTAMPS bool False Zero timestamps at end
+At the end of the bootstrap, set the timestamps of all files to 0 UNIX time.
+
+o /general/swap SWAP_SIZE size 0 Swap size
+The size of a swapfile to be created once swap is supported in live-bootsrap.
+Intended for systems with low RAM.
+
+o /general/kernel_bootstrap KERNEL_BOOTSTRAP bool True Use the kernel bootstrap
+This only applies if you are not in a chroot-like environment. You should
+already know whether you want this flag or not. If you booted using
+builder-hex0, this flag should be true; if you booted using your own Linux
+kernel, this flag should be false.
+
+o /general/interactive INTERACTIVE bool True Drop to a shell post-bootstrap
+At the end of the bootstrap, drop to a shell which you can do further things
+in.
+
+o /general/disk DISK "" _ [IMPORTANT] [DANGER] Disk
+live-bootstrap needs a disk which the final system will be installed onto.
+This should be in the form of the name of the device node, eg sda. If you
+have externally downloaded sources or the like, place in here the device
+node with the appropriate partition, eg sda1.
+Disclaimer for bare metal users:
+It is highly recommended that all disks other than this disk are removed
+from the system to avoid accidental data loss. It is vital that you choose
+this correctly, otherwise you risk overwriting an existing disk on your
+system. LIVE-BOOTSTRAP TAKES NO LIABILITY FOR ANY DATA LOSS RESULTING FROM
+ITS USE.
+
+m /sysinfo _ _ _ System information
+Details about your specific system and the environment live-bootstrap is
+running in.
+
+o /sysinfo/chroot CHROOT bool False Chroot-like environment
+Only set to True if live-bootstrap is running in a chroot-like environment.
+This might be in a chroot, on bubblewrap, or similar. QEMU is not a
+chroot-like environment.
+
+o /sysinfo/baremetal BARE_METAL bool False Bare metal environment
+Only set to True if live-bootstrap is running directly on bare metal, without
+another kernel or virtualisation layer in between. A chroot, bubblewrap, or
+QEMU is not bare metal.
+
+o /sysinfo/jobs FINAL_JOBS int _ Number of jobs
+The number of jobs that packages should be compiled with. A sensible value
+would be the number of threads on your system.
+
+o /sysinfo/arch ARCH "x86|amd64|riscv64" _ Architecture
+The architecture live-bootstrap is running on.
+
+m /sysinfo/internal _ _ _ [INTERNAL] Advanced configuration
+Internal configuration. You should not touch this unless you know what you
+are doing!
+
+o /sysinfo/internal/ci INTERNAL_CI bool False Internal CI
+If you are seeing this, it should not be set to true. (Flag for live-bootstrap
+CI).
+
+m /dev _ _ _ [DEV] Development options
+Options intended for primarily live-bootstrap development or debugging.
+
+o /dev/update_checksums UPDATE_CHECKSUMS bool False [DEV] Update checksums of packages
+Rather than checking checksums of packages against the existing list, generate
+a new list for the checksums of packages.
+
+o /dev/build_kernels BUILD_KERNELS bool _ Build kernels
+Even when they are not required, still build kernels/kernel adjacent packages.
+This option has no effect when using KERNEL_BOOTSTRAP.
diff --git a/steps/improve/reconfigure.sh b/steps/improve/reconfigure.sh
@@ -0,0 +1,6 @@
+# SPDX-FileCopyrightText: 2024 fosslinux <fosslinux@aussies.space>
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+set -ex
+/configurator /steps/configurator
+/script-generator /steps/manifest
diff --git a/steps/jump/fiwix.sh b/steps/jump/fiwix.sh
@@ -7,11 +7,14 @@
set -ex
# Build the ext2 image
-make_fiwix_initrd -s 1376256 /boot/fiwix.ext2
+# 1392640 = 1360 MB
+make_fiwix_initrd -s 1381376 /boot/fiwix.ext2
# Boot Fiwix
+# 199680 = 195 MB
+# as of 2024-05-27, Initrd = ~183 MB, kernel = ~10.5MB for Linux
if match x${BARE_METAL} xTrue; then
- kexec-fiwix /boot/fiwix -i /boot/fiwix.ext2 -m /e820 -c "fiwix console=/dev/tty1 root=/dev/ram0 initrd=fiwix.ext2 kexec_proto=linux kexec_size=204800 kexec_cmdline=\"init=/init consoleblank=0\""
+ kexec-fiwix /boot/fiwix -i /boot/fiwix.ext2 -m /e820 -c "fiwix console=/dev/tty1 root=/dev/ram0 initrd=fiwix.ext2 kexec_proto=linux kexec_size=199680 kexec_cmdline=\"init=/init consoleblank=0\""
else
- kexec-fiwix /boot/fiwix -i /boot/fiwix.ext2 -m /e820 -c "fiwix console=/dev/ttyS0 root=/dev/ram0 initrd=fiwix.ext2 kexec_proto=linux kexec_size=204800 kexec_cmdline=\"init=/init console=ttyS0\""
+ kexec-fiwix /boot/fiwix -i /boot/fiwix.ext2 -m /e820 -c "fiwix console=/dev/ttyS0 root=/dev/ram0 initrd=fiwix.ext2 kexec_proto=linux kexec_size=199680 kexec_cmdline=\"init=/init console=ttyS0\""
fi
diff --git a/steps/jump/linux.sh b/steps/jump/linux.sh
@@ -17,7 +17,7 @@ if [ "${KERNEL_BOOTSTRAP}" = True ]; then
find / -xdev -type d -printf "dir %p %m %U %G\n" >> /initramfs.list
find / -xdev -type f -printf "file %p %p %m %U %G\n" >> /initramfs.list
find / -xdev -type l -printf "slink %p %l %m %U %G\n" >> /initramfs.list
- kexec-linux "/dev/ram1" "/boot/vmlinuz" "!gen_init_cpio /initramfs.list | gzip -c"
+ kexec-linux "/dev/ram1" "/boot/vmlinuz" "!gen_init_cpio /initramfs.list | bzip2 -c"
else
mkdir /etc
# kexec time
diff --git a/steps/linux-4.14.341-openela/files/config b/steps/linux-4.14.341-openela/files/config
@@ -62,8 +62,8 @@ CONFIG_HAVE_KERNEL_LZMA=y
CONFIG_HAVE_KERNEL_XZ=y
CONFIG_HAVE_KERNEL_LZO=y
CONFIG_HAVE_KERNEL_LZ4=y
-CONFIG_KERNEL_GZIP=y
-# CONFIG_KERNEL_BZIP2 is not set
+# CONFIG_KERNEL_GZIP is not set
+CONFIG_KERNEL_BZIP2=y
# CONFIG_KERNEL_LZMA is not set
# CONFIG_KERNEL_XZ is not set
# CONFIG_KERNEL_LZO is not set
diff --git a/steps/manifest b/steps/manifest
@@ -41,6 +41,7 @@ build: fiwix-1.5.0-lb1 ( BUILD_FIWIX == True )
build: lwext4-1.0.0-lb1 ( BUILD_FIWIX == True )
build: kexec-fiwix-1.0 ( BUILD_FIWIX == True )
jump: fiwix ( KERNEL_BOOTSTRAP == True )
+improve: reconfigure ( CONFIGURATOR != True )
define: JOBS = 1 ( KERNEL_BOOTSTRAP == True )
build: make-3.82
build: patch-2.5.9
diff --git a/steps/simple-patch-1.0/simple-patch-1.0.x86.checksums b/steps/simple-patch-1.0/simple-patch-1.0.x86.checksums
@@ -1 +1 @@
-f66b8200c9237a7d5fe6a3d4d94f1ae661009d8ad14efdc7e95ac4cb8c4775e8 /usr/bin/simple-patch
+e6826990991981a959646355b5418ce2561a2fd1724004dd5e0a845ebb387373 /usr/bin/simple-patch