From c30ace4d4c27c42ed94adbe2b0f9153a9b6504b7 Mon Sep 17 00:00:00 2001 From: fosslinux Date: Sun, 26 May 2024 20:33:47 +1000 Subject: [PATCH 1/7] Update stage0-posix modules --- seed/stage0-posix | 2 +- .../checksum-transcriber-1.0.x86.checksums | 2 +- steps/simple-patch-1.0/simple-patch-1.0.x86.checksums | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/seed/stage0-posix b/seed/stage0-posix index 779e5424..18eac1d0 160000 --- a/seed/stage0-posix +++ b/seed/stage0-posix @@ -1 +1 @@ -Subproject commit 779e5424d4b55fe9b7faea2285ae8b6486df0433 +Subproject commit 18eac1d08535a685c817247fb8c41a1dba9bbf6f 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 index 39440d64..bd0d28c9 100644 --- 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/simple-patch-1.0/simple-patch-1.0.x86.checksums b/steps/simple-patch-1.0/simple-patch-1.0.x86.checksums index 73a7cdc0..33d1783a 100644 --- 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 From 7937797ea506d5ee3459b457ac93ffbc635d78f2 Mon Sep 17 00:00:00 2001 From: fosslinux Date: Tue, 28 May 2024 21:53:15 +1000 Subject: [PATCH 2/7] Bzip2 compression in Linux initramfs/kernel There isn't really any reason to use gzip instead of bzip2 for the Linux initramfs/kernel, since we have it! Saves a few MB (~13MB as far as I can tell) --- steps/SHA256SUMS.pkgs | 2 +- steps/jump/linux.sh | 2 +- steps/linux-4.14.341-openela/files/config | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/steps/SHA256SUMS.pkgs b/steps/SHA256SUMS.pkgs index 415c998e..22c0d09b 100644 --- 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/jump/linux.sh b/steps/jump/linux.sh index da2d616b..5ecd22e1 100755 --- 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 index 31f445a4..e0a84bde 100644 --- 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 From e2d4782cc2d8e1e4718069b839952b79fbf57862 Mon Sep 17 00:00:00 2001 From: fosslinux Date: Tue, 28 May 2024 21:54:21 +1000 Subject: [PATCH 3/7] Reclaim 5MB to the Fiwix rootfs from kexec We need just a couple more MB for configurator to be added! The tolerances are *very* tight now... --- steps/jump/fiwix.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/steps/jump/fiwix.sh b/steps/jump/fiwix.sh index 250a66b7..584dcf1f 100755 --- 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 From e1077cbed3989c1bd7a5cb0641ebd832f4b96284 Mon Sep 17 00:00:00 2001 From: fosslinux Date: Sun, 12 May 2024 16:33:56 +1000 Subject: [PATCH 4/7] Add interactive "configurator" This is a more UX-friendly glorified bootstrap.cfg editor. Somewhat inspired by kconfig. --- seed/configurator.c | 784 ++++++++++++++++++++++++++++ seed/configurator.x86.checksums | 1 + seed/script-generator.c | 26 +- seed/script-generator.x86.checksums | 2 +- seed/seed.kaem | 10 + steps/configurator | 80 +++ 6 files changed, 878 insertions(+), 25 deletions(-) create mode 100644 seed/configurator.c create mode 100644 seed/configurator.x86.checksums create mode 100644 steps/configurator diff --git a/seed/configurator.c b/seed/configurator.c new file mode 100644 index 00000000..eae242f1 --- /dev/null +++ b/seed/configurator.c @@ -0,0 +1,784 @@ +/* + * SPDX-FileCopyrightText: 2024 fosslinux + * + * 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 +#include +#include +#include +#include +#include + +#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 or open : open a (sub)menu\n" + "? or describe : provides a more detailed description of an option or menu\n" + "s or set : set the value of an option\n" + "g or get : get the value of an option\n" + "g all or get all: get the value of all options in the menu\n" + "r or reset : 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\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(" \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 new file mode 100644 index 00000000..b1603cb2 --- /dev/null +++ b/seed/configurator.x86.checksums @@ -0,0 +1 @@ +bc373892becaa92c52bda2d0e8389931d0859e97848e53b3b9d10ebf7cba9651 configurator diff --git a/seed/script-generator.c b/seed/script-generator.c index eccc7918..adc093e8 100644 --- 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 index 4ee2e439..e9bc189e 100644 --- a/seed/script-generator.x86.checksums +++ b/seed/script-generator.x86.checksums @@ -1 +1 @@ -dc6106dbc02839cdc9e3e2348432242eb6d33d840ab74badfd63c3c9997462b9 script-generator +475ee131b41f42f158b18dfa5461eb860d2a2e895bb1593b1957b7266035b14c script-generator diff --git a/seed/seed.kaem b/seed/seed.kaem index 04004200..8445b123 100755 --- 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/configurator b/steps/configurator new file mode 100644 index 00000000..fbde73b7 --- /dev/null +++ b/steps/configurator @@ -0,0 +1,80 @@ +# SPDX-FileCopyrightText: 2024 fosslinux +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# The structure of this file: +#