Add interactive "configurator"

This is a more UX-friendly glorified bootstrap.cfg editor.
Somewhat inspired by kconfig.
This commit is contained in:
fosslinux 2024-05-12 16:33:56 +10:00
parent e2d4782cc2
commit e1077cbed3
6 changed files with 878 additions and 25 deletions

784
seed/configurator.c Normal file
View file

@ -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\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;
}

View file

@ -0,0 +1 @@
bc373892becaa92c52bda2d0e8389931d0859e97848e53b3b9d10ebf7cba9651 configurator

View file

@ -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. */

View file

@ -1 +1 @@
dc6106dbc02839cdc9e3e2348432242eb6d33d840ab74badfd63c3c9997462b9 script-generator
475ee131b41f42f158b18dfa5461eb860d2a2e895bb1593b1957b7266035b14c script-generator

View file

@ -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

80
steps/configurator Normal file
View file

@ -0,0 +1,80 @@
# 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 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.
m /sysinfo/internal _ _ _ [INTERNAL] Advanced configuration
Internal configuration. You should not touch this unless you know what you
are doing!
o /sysinfo/internal/arch ARCH "x86|amd64|riscv64" _ Architecture
The architecture live-bootstrap is running on. This should already be correct!
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 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 _ [DEV] Build kernels
Even when they are not required, still build kernels/kernel adjacent packages.