logo

live-bootstrap

Mirror of <https://github.com/fosslinux/live-bootstrap>

configurator.c (19299B)


  1. /*
  2. * SPDX-FileCopyrightText: 2024 fosslinux <fosslinux@aussies.space>
  3. *
  4. * SPDX-License-Identifier: GPL-3.0-or-later
  5. */
  6. #define MAX_STRING 2048
  7. #define MAX_SHORT 512
  8. #define MAX_ID 128
  9. #define MAX_VAR 128
  10. #include <bootstrappable.h>
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <unistd.h>
  15. #include <sys/utsname.h>
  16. #define TRUE 1
  17. #define FALSE 0
  18. #define KIND_NONE 0
  19. #define KIND_MENU 1
  20. #define KIND_OPTION 2
  21. #define TYPE_NONE 0
  22. #define TYPE_BOOL 1
  23. #define TYPE_SIZE 2
  24. #define TYPE_STRING 3
  25. #define TYPE_INT 4
  26. struct Entry {
  27. int kind; // either menu or option
  28. char *env_var; // name of the environment variable this option is stored in
  29. char *id; // the id of the configuration item
  30. char *short_desc; // short description of the config
  31. char *full_desc; // extended description of the config
  32. int type; // the type of the configuration option
  33. char *validation; // any validation rules
  34. char *val;
  35. char *default_val;
  36. struct Entry *children; // submenus
  37. struct Entry *parent;
  38. struct Entry *next;
  39. };
  40. typedef struct Entry Entry;
  41. Entry *find_entry(Entry *head, char *id) {
  42. char *component = strchr(id, '/');
  43. if (component == NULL) {
  44. component = id + strlen(id);
  45. }
  46. Entry *current;
  47. Entry *final;
  48. int len;
  49. while (1) {
  50. len = component - id;
  51. current = head;
  52. while (current != NULL) {
  53. /* ensure that the id isn't just a substring of the component but actually is the component */
  54. if (strlen(current->id) == len && strncmp(id, current->id, len) == 0) {
  55. /* Found it! */
  56. final = current;
  57. head = current->children;
  58. break;
  59. }
  60. current = current->next;
  61. }
  62. if (current == NULL) {
  63. /* Did not find it */
  64. return NULL;
  65. }
  66. if (component[0] == '\0') {
  67. break;
  68. }
  69. component += 1;
  70. component = strchr(component, '/');
  71. if (component == NULL) {
  72. component = id + strlen(id);
  73. }
  74. }
  75. return final;
  76. }
  77. Entry *get_parent(Entry *head, char *id) {
  78. char *parent_id = calloc(MAX_ID, sizeof(char));
  79. strcpy(parent_id, id);
  80. char *final_slash = strrchr(parent_id, '/');
  81. final_slash[0] = '\0';
  82. Entry *ret = find_entry(head, parent_id);
  83. free(parent_id);
  84. return ret;
  85. }
  86. char read_string(FILE *f, char *out, int length) {
  87. int i = 0;
  88. char c = fgetc(f);
  89. while (c != ' ' && c != '\n' && c != EOF && i < length - 1) {
  90. out[i] = c;
  91. i += 1;
  92. c = fgetc(f);
  93. }
  94. if (i >= length - 1) {
  95. fputs("String too long!\n", stdout);
  96. fclose(f);
  97. exit(1);
  98. }
  99. out[i] = '\0';
  100. return c;
  101. }
  102. int set_val(Entry *entry, char *val) {
  103. if (entry->type == TYPE_BOOL) {
  104. if (strcmp(val, "True") != 0 && strcmp(val, "False") != 0) {
  105. fputs("Invalid input: ", stdout);
  106. fputs(val, stdout);
  107. fputs(" is not a boolean value\n", stdout);
  108. return 1;
  109. }
  110. } else if (entry->type == TYPE_INT) {
  111. int intval = strtoint(val);
  112. if (intval == 0 && strcmp(val, "0") != 0) {
  113. fputs("Invalid input: ", stdout);
  114. fputs(val, stdout);
  115. fputs(" is not an integer\n", stdout);
  116. return 1;
  117. }
  118. } else if (entry->type == TYPE_SIZE) {
  119. /* We should have either a K, M, G, T, or no letter, at the end of the size. */
  120. char c = val[strlen(val) - 1];
  121. if (!(('0' <= c && c <= '9') || c == 'K' || c == 'M' || c == 'G' || c == 'T')) {
  122. fputs("Invalid input: ", stdout);
  123. fputc(c, stdout);
  124. fputs(" is not a valid suffix for a size\n", stdout);
  125. return 1;
  126. }
  127. /* Check it is an integer */
  128. char *final_char = val + strlen(val) - 1;
  129. if ('A' <= final_char[0] && final_char[0] <= 'Z') {
  130. final_char[0] = '\0';
  131. }
  132. int intval = strtoint(val);
  133. if (intval == 0 && strcmp(val, "0") != 0) {
  134. fputs("Invalid input: ", stdout);
  135. fputs(val, stdout);
  136. fputs(" is not a valid size\n", stdout);
  137. return 1;
  138. }
  139. final_char[0] = c;
  140. } else if (entry->type == TYPE_STRING) {
  141. /* Validation rules. */
  142. char *validation = entry->validation;
  143. char *next;
  144. int found = FALSE;
  145. while (validation != NULL) {
  146. if (validation[0] == '\0') {
  147. found = TRUE;
  148. break;
  149. }
  150. next = strchr(validation, '|');
  151. if (next == NULL) {
  152. if (strcmp(validation, val) == 0) {
  153. found = TRUE;
  154. }
  155. break;
  156. } else {
  157. if (strncmp(validation, val, next - validation) == 0) {
  158. found = TRUE;
  159. }
  160. }
  161. validation = next + 1;
  162. }
  163. if (found == FALSE) {
  164. fputs("Invalid input: ", stdout);
  165. fputs(val, stdout);
  166. fputs(" does not match the validation rules ", stdout);
  167. fputs(entry->validation, stdout);
  168. fputc('\n', stdout);
  169. return 1;
  170. }
  171. }
  172. entry->val = calloc(strlen(val) + 1, sizeof(char));
  173. strcpy(entry->val, val);
  174. return 0;
  175. }
  176. void read_entry(char c, FILE *conf, Entry *head) {
  177. Entry *new = calloc(1, sizeof(Entry));
  178. /* Read the kind */
  179. if (c == 'm') {
  180. new->kind = KIND_MENU;
  181. } else if (c == 'o') {
  182. new->kind = KIND_OPTION;
  183. } else {
  184. fputs("Invalid entry: kind ", stdout);
  185. fputc(c, stdout);
  186. fputc('\n', stdout);
  187. fclose(conf);
  188. exit(1);
  189. }
  190. fgetc(conf);
  191. /* Read the id */
  192. new->id = calloc(MAX_ID, sizeof(char));
  193. c = read_string(conf, new->id, MAX_ID);
  194. if (c != ' ') {
  195. fputs("Invalid entry: no variable\n", stdout);
  196. fclose(conf);
  197. exit(1);
  198. }
  199. /* Read the environment variable */
  200. new->env_var = calloc(MAX_VAR, sizeof(char));
  201. c = read_string(conf, new->env_var, MAX_VAR);
  202. if (c != ' ') {
  203. fputs("Invalid entry: no data type\n", stdout);
  204. fclose(conf);
  205. exit(1);
  206. }
  207. if (strcmp(new->env_var, "_") == 0) {
  208. free(new->env_var);
  209. new->env_var = NULL;
  210. }
  211. /* Read the data type */
  212. char *data_type = calloc(MAX_ID, sizeof(char));
  213. read_string(conf, data_type, MAX_ID);
  214. if (c != ' ') {
  215. fputs("Invalid entry: no default value\n", stdout);
  216. fclose(conf);
  217. exit(1);
  218. }
  219. if (strcmp(data_type, "_") == 0) {
  220. new->type = TYPE_NONE;
  221. } else if (strcmp(data_type, "bool") == 0) {
  222. new->type = TYPE_BOOL;
  223. } else if (strcmp(data_type, "size") == 0) {
  224. new->type = TYPE_SIZE;
  225. } else if (strcmp(data_type, "int") == 0) {
  226. new->type = TYPE_INT;
  227. } else if (data_type[0] == '"') {
  228. new->type = TYPE_STRING;
  229. new->validation = data_type + 1;
  230. char *closing_quote = strrchr(data_type, '"');
  231. closing_quote[0] = '\0';
  232. } else {
  233. fputs("Invalid entry: unknown type: ", stdout);
  234. fputs(data_type, stdout);
  235. fputc('\n', stdout);
  236. fclose(conf);
  237. exit(1);
  238. }
  239. if (new->type != TYPE_STRING) {
  240. free(data_type);
  241. }
  242. /* Read the default value */
  243. char *default_val = calloc(MAX_STRING, sizeof(char));
  244. read_string(conf, default_val, MAX_ID);
  245. if (strcmp(default_val, "_") != 0) {
  246. set_val(new, default_val);
  247. new->default_val = default_val;
  248. } else {
  249. new->default_val = NULL;
  250. }
  251. /* Read the short description */
  252. new->short_desc = calloc(MAX_SHORT, sizeof(char));
  253. int i = 0;
  254. c = fgetc(conf);
  255. while (c != '\n' && c != EOF) {
  256. new->short_desc[i] = c;
  257. c = fgetc(conf);
  258. i += 1;
  259. }
  260. /* Read the long description */
  261. new->full_desc = calloc(MAX_STRING, sizeof(char));
  262. i = 0;
  263. c = fgetc(conf);
  264. char prev = '\0';
  265. while (!(c == '\n' && prev == '\n') && c != EOF) {
  266. new->full_desc[i] = c;
  267. prev = c;
  268. c = fgetc(conf);
  269. i += 1;
  270. }
  271. new->children = NULL;
  272. new->next = NULL;
  273. Entry *parent = get_parent(head, new->id);
  274. new->parent = parent;
  275. if (parent->children == NULL) {
  276. parent->children = new;
  277. } else {
  278. Entry *current = parent->children;
  279. while (current->next != NULL) {
  280. current = current->next;
  281. }
  282. current->next = new;
  283. }
  284. }
  285. Entry *read_config(char *filename) {
  286. FILE *conf = fopen(filename, "r");
  287. if (conf == NULL) {
  288. fputs("Unable to open ", stdout);
  289. fputs(filename, stdout);
  290. fputc('\n', stdout);
  291. exit(0);
  292. }
  293. char c = fgetc(conf);
  294. Entry *head = calloc(1, sizeof(Entry));
  295. head->id = "";
  296. head->env_var = "";
  297. head->next = NULL;
  298. Entry *current = head;
  299. while (c != EOF) {
  300. if (c == '#' || c == '\n') {
  301. /* Skip comments or empty lines. */
  302. while (c != '\n' && c != EOF) {
  303. c = fgetc(conf);
  304. }
  305. } else {
  306. read_entry(c, conf, head);
  307. }
  308. c = fgetc(conf);
  309. }
  310. fclose(conf);
  311. return head;
  312. }
  313. Entry *get_env_var(Entry *head, char *var) {
  314. Entry *ret;
  315. Entry *current;
  316. for (current = head->children; current != NULL; current = current->next) {
  317. if (current->env_var != NULL) {
  318. if (strcmp(current->env_var, var) == 0) {
  319. return current;
  320. }
  321. }
  322. if (current->children != NULL) {
  323. ret = get_env_var(current, var);
  324. if (ret != NULL) {
  325. return ret;
  326. }
  327. }
  328. }
  329. return NULL;
  330. }
  331. int set_cfg_varline(Entry *head, char *line) {
  332. char *var = calloc(strlen(line) + 1, sizeof(char));
  333. strcpy(var, line);
  334. char *val = strchr(var, '=');
  335. val[0] = '\0';
  336. val += 1;
  337. char *newline = strchr(val, '\n');
  338. if (newline != NULL) {
  339. newline[0] = '\0';
  340. }
  341. Entry *entry = get_env_var(head, var);
  342. if (entry != NULL) {
  343. int not_ok = set_val(entry, val);
  344. if (not_ok) {
  345. fputs("^ Originated from ", stdout);
  346. fputs(var, stdout);
  347. fputs("=", stdout);
  348. fputs(val, stdout);
  349. fputs("\n", stdout);
  350. }
  351. }
  352. return entry == NULL;
  353. }
  354. char *set_cfg_values(Entry *head, char **envp) {
  355. int i = 0;
  356. FILE *cfg = fopen("/steps/bootstrap.cfg", "r");
  357. if (cfg == NULL) {
  358. return "";
  359. }
  360. char *extra = calloc(MAX_STRING, sizeof(char));
  361. char *line = calloc(MAX_STRING, sizeof(char));
  362. while (fgets(line, MAX_STRING, cfg) != NULL) {
  363. if (set_cfg_varline(head, line)) {
  364. if (strncmp("CONFIGURATOR=", line, 13) != 0) {
  365. strcat(extra, line);
  366. }
  367. }
  368. free(line);
  369. line = calloc(MAX_STRING, sizeof(char));
  370. }
  371. fclose(cfg);
  372. return extra;
  373. }
  374. void write_cfg_list(Entry *head, FILE *cfg) {
  375. Entry *current;
  376. for (current = head->children; current != NULL; current = current->next) {
  377. if (current->kind == KIND_OPTION && current->val != NULL) {
  378. fputs(current->env_var, cfg);
  379. fputs("=", cfg);
  380. fputs(current->val, cfg);
  381. fputs("\n", cfg);
  382. }
  383. if (current->children != NULL) {
  384. write_cfg_list(current, cfg);
  385. }
  386. }
  387. }
  388. void write_cfg_values(Entry *head, char *extra, int configurator_done) {
  389. FILE *cfg = fopen("/steps/bootstrap.cfg", "w");
  390. if (cfg == NULL) {
  391. fputs("Unable to open /steps/bootstrap.cfg", stderr);
  392. exit(1);
  393. }
  394. if (configurator_done == TRUE) {
  395. fputs("CONFIGURATOR=False\n", cfg);
  396. }
  397. fputs(extra, cfg);
  398. write_cfg_list(head, cfg);
  399. fclose(cfg);
  400. }
  401. void print_short_desc(char *short_desc) {
  402. char *post_markers = strrchr(short_desc, ']');
  403. if (post_markers == NULL) {
  404. post_markers = short_desc;
  405. } else {
  406. post_markers += 1;
  407. while (post_markers[0] == ' ') {
  408. post_markers += 1;
  409. }
  410. }
  411. fputs(post_markers, stdout);
  412. }
  413. void print_recursive_desc(Entry *entry) {
  414. if (entry->parent != NULL) {
  415. if (strcmp(entry->parent->id, "") != 0) {
  416. print_recursive_desc(entry->parent);
  417. fputs("/", stdout);
  418. print_short_desc(entry->short_desc);
  419. return;
  420. }
  421. }
  422. print_short_desc(entry->short_desc);
  423. }
  424. int any_unset(Entry *head);
  425. int check_set(Entry *entry, Entry *head) {
  426. int ret = 0;
  427. if (entry->kind == KIND_OPTION && entry->val == NULL) {
  428. fputs("The configuration option ", stdout);
  429. print_recursive_desc(entry);
  430. fputs(" is unset\n", stdout);
  431. ret = 1;
  432. }
  433. if (entry->children != NULL) {
  434. ret |= any_unset(entry);
  435. }
  436. return ret;
  437. }
  438. int any_unset(Entry *head) {
  439. int ret = 0;
  440. Entry *current;
  441. for (current = head->children; current != NULL; current = current->next) {
  442. ret |= check_set(current, head);
  443. }
  444. return ret;
  445. }
  446. void print_menu(Entry *menu, int is_toplevel) {
  447. if (!is_toplevel) {
  448. fputs("(0) [MENU] Go up\n", stdout);
  449. }
  450. int i = 1;
  451. Entry *current;
  452. for (current = menu->children; current != NULL; current = current->next) {
  453. fputs("(", stdout);
  454. fputs(int2str(i, 10, FALSE), stdout);
  455. fputs(") ", stdout);
  456. if (current->kind == KIND_MENU) {
  457. fputs("[MENU] ", stdout);
  458. }
  459. fputs(current->short_desc, stdout);
  460. fputc('\n', stdout);
  461. i += 1;
  462. }
  463. }
  464. Entry *get_nth_option(Entry *menu, int n) {
  465. int i = 1;
  466. Entry *current;
  467. for (current = menu->children; current != NULL && i < n; current = current->next) {
  468. i += 1;
  469. }
  470. if (current == NULL) {
  471. fputs("There is no option ", stdout);
  472. fputs(int2str(n, 10, FALSE), stdout);
  473. fputs("!\n", stdout);
  474. }
  475. return current;
  476. }
  477. void how_to_use(void) {
  478. fputs(
  479. "How to navigate around this configuration menu:\n"
  480. "h or help: at any time, will reprint this help message\n"
  481. "l or list: shows the current menu options\n"
  482. "o <num> or open <num>: open a (sub)menu\n"
  483. "? <num> or describe <num>: provides a more detailed description of an option or menu\n"
  484. "s <num> <val> or set <num> <val>: set the value of an option\n"
  485. "g <num> or get <num>: get the value of an option\n"
  486. "g all or get all: get the value of all options in the menu\n"
  487. "r <num> or reset <num>: reset the value of an option to the default (if there is one)\n"
  488. "e or exit: exits the program\n",
  489. stdout);
  490. }
  491. Entry *extract_num(char **command, Entry *menu) {
  492. command[0] = strchr(command[0], ' ');
  493. if (command[0] == NULL) {
  494. fputs("Expected menu number to operate on!\n", stdout);
  495. }
  496. command[0] += 1;
  497. char *num = command[0];
  498. char *new = strchr(command[0], ' ');
  499. if (new == NULL) {
  500. new = strchr(command[0], '\n');
  501. }
  502. command[0] = new;
  503. command[0][0] = '\0';
  504. command[0] += 1;
  505. /* strtoint does not check if it is not a number */
  506. int i;
  507. for (i = 0; i < strlen(num); i += 1) {
  508. if (!('0' <= num[i] && num[i] <= '9')) {
  509. fputs(num, stdout);
  510. fputs(" is not a menu number!\n", stdout);
  511. return NULL;
  512. }
  513. }
  514. int n = strtoint(num);
  515. return get_nth_option(menu, n);
  516. }
  517. Entry *submenu(char *command, Entry *menu, Entry *head) {
  518. command = strchr(command, ' ');
  519. if (strlen(command) < 1) {
  520. fputs("Expected menu number to operate on!\n", stdout);
  521. }
  522. /* 0 is the "go up" menu option */
  523. if (command[1] == '0') {
  524. if (strcmp(menu->id, "") == 0) {
  525. fputs("There is no option 0!\n", stdout);
  526. return menu;
  527. }
  528. return menu->parent;
  529. }
  530. Entry *new = extract_num(&command, menu);
  531. if (new == NULL) {
  532. return menu;
  533. }
  534. if (new->kind != KIND_MENU) {
  535. fputs("This is not a menu!\n", stdout);
  536. return menu;
  537. }
  538. return new;
  539. }
  540. void print_description(char *command, Entry *menu) {
  541. Entry *opt = extract_num(&command, menu);
  542. if (opt != NULL) {
  543. fputs(opt->full_desc, stdout);
  544. }
  545. }
  546. void set_opt_value(char *command, Entry *menu) {
  547. Entry *opt = extract_num(&command, menu);
  548. if (opt == NULL) {
  549. return;
  550. }
  551. if (opt->kind != KIND_OPTION) {
  552. fputs("Cannot set a menu's value!\n", stdout);
  553. return;
  554. }
  555. /* Remove the newline */
  556. char *newline = strchr(command, '\n');
  557. newline[0] = '\0';
  558. set_val(opt, command);
  559. }
  560. void print_opt_value(Entry *opt) {
  561. print_short_desc(opt->short_desc);
  562. fputs(": ", stdout);
  563. if (opt->val == NULL) {
  564. fputs("unset", stdout);
  565. } else {
  566. fputs(opt->val, stdout);
  567. }
  568. fputc('\n', stdout);
  569. }
  570. void get_opt_value(char *command, Entry *menu) {
  571. Entry *opt = extract_num(&command, menu);
  572. if (opt == NULL) {
  573. return;
  574. }
  575. if (opt->kind != KIND_OPTION) {
  576. fputs("Cannot get a menu's value!\n", stdout);
  577. return;
  578. }
  579. print_opt_value(opt);
  580. }
  581. void get_all_values(Entry *menu) {
  582. Entry *current;
  583. for (current = menu->children; current != NULL; current = current->next) {
  584. if (current->kind == KIND_OPTION) {
  585. print_opt_value(current);
  586. }
  587. }
  588. }
  589. void reset_value(char *command, Entry *menu) {
  590. Entry *opt = extract_num(&command, menu);
  591. if (opt == NULL) {
  592. return;
  593. }
  594. if (opt->kind != KIND_OPTION) {
  595. fputs("Cannot reset a menu's value!\n", stdout);
  596. return;
  597. }
  598. opt->val = opt->default_val;
  599. }
  600. void no_input(Entry *head) {
  601. fputs("You don't seem to be running under Fiwix or Linux currently.\n", stdout);
  602. fputs("Likely, you are currently running under builder-hex0.\n", stdout);
  603. fputs("That's ok! We're going to make some assumptions; namely, that you do need\n", stdout);
  604. fputs("the kernel bootstrap, and that you'll get a chance to configure later.\n", stdout);
  605. write_cfg_values(head, "KERNEL_BOOTSTRAP=True\nBUILD_KERNELS=True\n", FALSE);
  606. }
  607. int main(int argc, char **argv, char **envp) {
  608. /*
  609. * Check we are being non-interactive and bootstrap.cfg exists in
  610. * which case we do not need to do anything.
  611. */
  612. char *interactivity = getenv("CONFIGURATOR");
  613. if (interactivity != NULL) {
  614. if (strcmp(interactivity, "False") == 0) {
  615. return 0;
  616. }
  617. }
  618. FILE *bootstrap_cfg = fopen("/steps/bootstrap.cfg", "r");
  619. if (bootstrap_cfg != NULL) {
  620. char *line = calloc(MAX_STRING, sizeof(char));
  621. while (fgets(line, MAX_STRING, bootstrap_cfg) != NULL) {
  622. if (strcmp(line, "CONFIGURATOR=False\n") == 0) {
  623. fclose(bootstrap_cfg);
  624. return 0;
  625. }
  626. free(line);
  627. line = calloc(MAX_STRING, sizeof(char));
  628. }
  629. fclose(bootstrap_cfg);
  630. }
  631. if (argc != 2) {
  632. fputs("Usage: ", stdout);
  633. fputs(argv[0], stdout);
  634. fputs(" <configuration>\n", stdout);
  635. exit(1);
  636. }
  637. Entry *head = read_config(argv[1]);
  638. char *extra = set_cfg_values(head, envp);
  639. /*
  640. * Check if we are NOT running under fiwix or linux.
  641. * If we are not, and need configuration to occur, then we presume that
  642. * we will not be able to get any input from the user.
  643. */
  644. struct utsname *kernel = calloc(1, sizeof(struct utsname));
  645. uname(kernel);
  646. if (kernel->sysname == NULL) {
  647. no_input(head);
  648. return 0;
  649. } else if (strcmp(kernel->sysname, "Linux") != 0 && strcmp(kernel->sysname, "Fiwix") != 0) {
  650. no_input(head);
  651. return 0;
  652. }
  653. fputs("Welcome to live-bootstrap!\n", stdout);
  654. fputs("We need to do some brief configuration before continuing.\n\n", stdout);
  655. how_to_use();
  656. fputc('\n', stdout);
  657. Entry *menu = head;
  658. print_menu(menu, menu == head);
  659. char *command = calloc(MAX_STRING, sizeof(char));
  660. fputs("\nCommand: ", stdout);
  661. fflush(stdout);
  662. command = fgets(command, MAX_STRING, stdin);
  663. while (command != NULL) {
  664. if (strcmp("h\n", command) == 0 || strcmp("help\n", command) == 0) {
  665. how_to_use();
  666. } else if (strcmp("l\n", command) == 0 || strcmp("list\n", command) == 0) {
  667. print_menu(menu, menu == head);
  668. } else if (strncmp("o ", command, 2) == 0 || strncmp("open ", command, 5) == 0) {
  669. menu = submenu(command, menu, head);
  670. print_menu(menu, menu == head);
  671. } else if (strncmp("? ", command, 2) == 0 || strncmp("describe ", command, 9) == 0) {
  672. print_description(command, menu);
  673. } else if (strcmp("g all\n", command) == 0 || strcmp("get all\n", command) == 0) {
  674. get_all_values(menu);
  675. } else if (strncmp("g ", command, 2) == 0 || strncmp("get ", command, 4) == 0) {
  676. get_opt_value(command, menu);
  677. } else if (strncmp("s ", command, 2) == 0 || strncmp("set ", command, 4) == 0) {
  678. set_opt_value(command, menu);
  679. } else if (strncmp("r ", command, 2) == 0 || strncmp("reset ", command, 6) == 0) {
  680. reset_value(command, menu);
  681. } else if (strcmp("e\n", command) == 0 || strcmp("exit\n", command) == 0) {
  682. if (!any_unset(head)) {
  683. break;
  684. }
  685. } else {
  686. fputs("Unknown command ", stdout);
  687. fputs(command, stdout);
  688. }
  689. fputs("\nCommand: ", stdout);
  690. fflush(stdout);
  691. /*
  692. * M2-Planet's fgets does not properly terminate the buffer if there is
  693. * already data in it
  694. */
  695. free(command);
  696. command = calloc(MAX_STRING, sizeof(char));
  697. command = fgets(command, MAX_STRING, stdin);
  698. }
  699. if (any_unset(head)) {
  700. fputs(
  701. "Uh oh! You have left me in a tough position - you can't input further because you\n"
  702. "closed the input stream. But the inputs you gave me are not valid!\n"
  703. "I'm going to re-exec myself and hope you are able to start again from scratch.\n",
  704. stderr
  705. );
  706. execve(argv[0], argv, envp);
  707. return 0;
  708. }
  709. write_cfg_values(head, extra, TRUE);
  710. fputs("\nThank you! We will now continue with the bootstrap.\n", stdout);
  711. return 0;
  712. }