1/* 2 * The new sysinstall program. 3 * 4 * This is probably the last program in the `sysinstall' line - the next 5 * generation being essentially a complete rewrite. 6 * 7 * $FreeBSD$ 8 * 9 * Copyright (c) 1995 10 * Jordan Hubbard. All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer, 17 * verbatim and that no modifications are made prior to this 18 * point in the file. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 23 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 */ 36 37#include "sysinstall.h" 38#include <ctype.h> 39#include <errno.h> 40#include <sys/signal.h> 41#include <sys/fcntl.h> 42 43#include "list.h" 44 45static int dispatch_shutdown(dialogMenuItem *unused); 46static int dispatch_systemExecute(dialogMenuItem *unused); 47static int dispatch_msgConfirm(dialogMenuItem *unused); 48static int dispatch_mediaOpen(dialogMenuItem *unused); 49static int dispatch_mediaClose(dialogMenuItem *unused); 50static int cfgModuleFire(dialogMenuItem *self); 51 52static struct _word { 53 char *name; 54 int (*handler)(dialogMenuItem *self); 55} resWords[] = { 56 { "configAnonFTP", configAnonFTP }, 57 { "configRouter", configRouter }, 58 { "configInetd", configInetd }, 59 { "configNFSServer", configNFSServer }, 60 { "configNTP", configNTP }, 61 { "configPCNFSD", configPCNFSD }, 62 { "configPackages", configPackages }, 63 { "configUsers", configUsers }, 64#ifdef WITH_SLICES 65 { "diskPartitionEditor", diskPartitionEditor }, 66#endif 67 { "diskPartitionWrite", diskPartitionWrite }, 68 { "diskLabelEditor", diskLabelEditor }, 69 { "diskLabelCommit", diskLabelCommit }, 70 { "distReset", distReset }, 71 { "distSetCustom", distSetCustom }, 72 { "distUnsetCustom", distUnsetCustom }, 73 { "distSetDeveloper", distSetDeveloper }, 74 { "distSetKernDeveloper", distSetKernDeveloper }, 75 { "distSetUser", distSetUser }, 76 { "distSetMinimum", distSetMinimum }, 77 { "distSetEverything", distSetEverything }, 78 { "distSetSrc", distSetSrc }, 79 { "distExtractAll", distExtractAll }, 80 { "docBrowser", docBrowser }, 81 { "docShowDocument", docShowDocument }, 82 { "installCommit", installCommit }, 83 { "installExpress", installExpress }, 84 { "installStandard", installStandard }, 85 { "installUpgrade", installUpgrade }, 86 { "installFixupBase", installFixupBase }, 87 { "installFixitHoloShell", installFixitHoloShell }, 88 { "installFixitCDROM", installFixitCDROM }, 89 { "installFixitUSB", installFixitUSB }, 90 { "installFixitFloppy", installFixitFloppy }, 91 { "installFilesystems", installFilesystems }, 92 { "installVarDefaults", installVarDefaults }, 93 { "loadConfig", dispatch_load_file }, 94 { "loadFloppyConfig", dispatch_load_floppy }, 95 { "loadCDROMConfig", dispatch_load_cdrom }, 96 { "mediaOpen", dispatch_mediaOpen }, 97 { "mediaClose", dispatch_mediaClose }, 98 { "mediaSetCDROM", mediaSetCDROM }, 99 { "mediaSetFloppy", mediaSetFloppy }, 100 { "mediaSetUSB", mediaSetUSB }, 101 { "mediaSetDOS", mediaSetDOS }, 102 { "mediaSetFTP", mediaSetFTP }, 103 { "mediaSetFTPActive", mediaSetFTPActive }, 104 { "mediaSetFTPPassive", mediaSetFTPPassive }, 105 { "mediaSetHTTP", mediaSetHTTP }, 106 { "mediaSetHTTPDirect", mediaSetHTTPDirect }, 107 { "mediaSetUFS", mediaSetUFS }, 108 { "mediaSetNFS", mediaSetNFS }, 109 { "mediaSetFTPUserPass", mediaSetFTPUserPass }, 110 { "mediaSetCPIOVerbosity", mediaSetCPIOVerbosity }, 111 { "mediaGetType", mediaGetType }, 112 { "msgConfirm", dispatch_msgConfirm }, 113 { "optionsEditor", optionsEditor }, 114 { "packageAdd", packageAdd }, 115 { "addGroup", userAddGroup }, 116 { "addUser", userAddUser }, 117 { "shutdown", dispatch_shutdown }, 118 { "system", dispatch_systemExecute }, 119 { "dumpVariables", dump_variables }, 120 { "tcpMenuSelect", tcpMenuSelect }, 121 { NULL, NULL }, 122}; 123 124/* 125 * Helper routines for buffering data. 126 * 127 * We read an entire configuration into memory before executing it 128 * so that we are truely standalone and can do things like nuke the 129 * file or disk we're working on. 130 */ 131 132typedef struct command_buffer_ { 133 qelement queue; 134 char * string; 135} command_buffer; 136 137static void 138dispatch_free_command(command_buffer *item) 139{ 140 if (item != NULL) { 141 REMQUE(item); 142 free(item->string); 143 item->string = NULL; 144 } 145 146 free(item); 147} 148 149static void 150dispatch_free_all(qelement *head) 151{ 152 command_buffer *item; 153 154 while (!EMPTYQUE(*head)) { 155 item = (command_buffer *) head->q_forw; 156 dispatch_free_command(item); 157 } 158} 159 160static command_buffer * 161dispatch_add_command(qelement *head, char *string) 162{ 163 command_buffer *new = NULL; 164 165 new = malloc(sizeof(command_buffer)); 166 167 if (new != NULL) { 168 169 new->string = strdup(string); 170 171 /* 172 * We failed to copy `string'; clean up the allocated 173 * resources. 174 */ 175 if (new->string == NULL) { 176 free(new); 177 new = NULL; 178 } else { 179 INSQUEUE(new, head->q_back); 180 } 181 } 182 183 return new; 184} 185 186/* 187 * Command processing 188 */ 189 190/* Just convenience */ 191static int 192dispatch_shutdown(dialogMenuItem *unused) 193{ 194 systemShutdown(0); 195 return DITEM_FAILURE; 196} 197 198static int 199dispatch_systemExecute(dialogMenuItem *unused) 200{ 201 char *cmd = variable_get(VAR_COMMAND); 202 203 if (cmd) 204 return systemExecute(cmd) ? DITEM_FAILURE : DITEM_SUCCESS; 205 else 206 msgDebug("_systemExecute: No command passed in `command' variable.\n"); 207 return DITEM_FAILURE; 208} 209 210static int 211dispatch_msgConfirm(dialogMenuItem *unused) 212{ 213 char *msg = variable_get(VAR_COMMAND); 214 215 if (msg) { 216 msgConfirm("%s", msg); 217 return DITEM_SUCCESS; 218 } 219 220 msgDebug("_msgConfirm: No message passed in `command' variable.\n"); 221 return DITEM_FAILURE; 222} 223 224static int 225dispatch_mediaOpen(dialogMenuItem *unused) 226{ 227 return mediaOpen(); 228} 229 230static int 231dispatch_mediaClose(dialogMenuItem *unused) 232{ 233 mediaClose(); 234 return DITEM_SUCCESS; 235} 236 237static int 238call_possible_resword(char *name, dialogMenuItem *value, int *status) 239{ 240 int i, rval; 241 242 rval = 0; 243 for (i = 0; resWords[i].name; i++) { 244 if (!strcmp(name, resWords[i].name)) { 245 *status = resWords[i].handler(value); 246 rval = 1; 247 break; 248 } 249 } 250 return rval; 251} 252 253/* For a given string, call it or spit out an undefined command diagnostic */ 254int 255dispatchCommand(char *str) 256{ 257 int i; 258 char *cp; 259 260 if (!str || !*str) { 261 msgConfirm("Null or zero-length string passed to dispatchCommand"); 262 return DITEM_FAILURE; 263 } 264 265 /* Fixup DOS abuse */ 266 if ((cp = index(str, '\r')) != NULL) 267 *cp = '\0'; 268 269 /* If it's got a `=' sign in there, assume it's a variable setting */ 270 if (index(str, '=')) { 271 if (isDebug()) 272 msgDebug("dispatch: setting variable `%s'\n", str); 273 variable_set(str, 0); 274 i = DITEM_SUCCESS; 275 } 276 else { 277 /* A command might be a pathname if it's encoded in argv[0], which 278 we also support */ 279 if ((cp = rindex(str, '/')) != NULL) 280 str = cp + 1; 281 if (isDebug()) 282 msgDebug("dispatch: calling resword `%s'\n", str); 283 if (!call_possible_resword(str, NULL, &i)) { 284 msgNotify("Warning: No such command ``%s''", str); 285 i = DITEM_FAILURE; 286 } 287 /* 288 * Allow a user to prefix a command with "noError" to cause 289 * us to ignore any errors for that one command. 290 */ 291 if (i != DITEM_SUCCESS && variable_get(VAR_NO_ERROR)) 292 i = DITEM_SUCCESS; 293 variable_unset(VAR_NO_ERROR); 294 } 295 return i; 296} 297 298 299/* 300 * File processing 301 */ 302 303static qelement * 304dispatch_load_fp(FILE *fp) 305{ 306 qelement *head; 307 char buf[BUFSIZ], *cp; 308 309 head = malloc(sizeof(qelement)); 310 311 if (!head) 312 return NULL; 313 314 INITQUE(*head); 315 316 while (fgets(buf, sizeof buf, fp)) { 317 /* Fix up DOS abuse */ 318 if ((cp = index(buf, '\r')) != NULL) 319 *cp = '\0'; 320 /* If it's got a new line, trim it */ 321 if ((cp = index(buf, '\n')) != NULL) 322 *cp = '\0'; 323 if (*buf == '\0' || *buf == '#') 324 continue; 325 326 if (!dispatch_add_command(head, buf)) 327 return NULL; 328 } 329 330 return head; 331} 332 333static int 334dispatch_execute(qelement *head) 335{ 336 int result = DITEM_SUCCESS; 337 command_buffer *item; 338 char *old_interactive; 339 340 if (!head) 341 return result | DITEM_FAILURE; 342 343 old_interactive = variable_get(VAR_NONINTERACTIVE); 344 if (old_interactive) 345 old_interactive = strdup(old_interactive); /* save copy */ 346 347 /* Hint to others that we're running from a script, should they care */ 348 variable_set2(VAR_NONINTERACTIVE, "yes", 0); 349 350 while (!EMPTYQUE(*head)) { 351 item = (command_buffer *) head->q_forw; 352 353 if (DITEM_STATUS(dispatchCommand(item->string)) != DITEM_SUCCESS) { 354 msgConfirm("Command `%s' failed - rest of script aborted.\n", 355 item->string); 356 result |= DITEM_FAILURE; 357 break; 358 } 359 dispatch_free_command(item); 360 } 361 362 dispatch_free_all(head); 363 364 if (!old_interactive) 365 variable_unset(VAR_NONINTERACTIVE); 366 else { 367 variable_set2(VAR_NONINTERACTIVE, old_interactive, 0); 368 free(old_interactive); 369 } 370 371 return result; 372} 373 374int 375dispatch_load_file_int(int quiet) 376{ 377 FILE *fp; 378 char *cp; 379 int i; 380 qelement *list; 381 382 static const char *names[] = { 383 "install.cfg", 384 "/stand/install.cfg", 385 "/tmp/install.cfg", 386 NULL 387 }; 388 389 fp = NULL; 390 cp = variable_get(VAR_CONFIG_FILE); 391 if (!cp) { 392 for (i = 0; names[i]; i++) 393 if ((fp = fopen(names[i], "r")) != NULL) 394 break; 395 } else 396 fp = fopen(cp, "r"); 397 398 if (!fp) { 399 if (!quiet) 400 msgConfirm("Unable to open %s: %s", cp, strerror(errno)); 401 return DITEM_FAILURE; 402 } 403 404 list = dispatch_load_fp(fp); 405 fclose(fp); 406 407 return dispatch_execute(list); 408} 409 410int 411dispatch_load_file(dialogMenuItem *self) 412{ 413 return dispatch_load_file_int(FALSE); 414} 415 416int 417dispatch_load_floppy(dialogMenuItem *self) 418{ 419 int what = DITEM_SUCCESS; 420 extern char *distWanted; 421 char *cp; 422 FILE *fp; 423 qelement *list; 424 425 mediaClose(); 426 cp = variable_get_value(VAR_INSTALL_CFG, 427 "Specify the name of a configuration file", 0); 428 if (!cp || !*cp) { 429 variable_unset(VAR_INSTALL_CFG); 430 what |= DITEM_FAILURE; 431 return what; 432 } 433 434 distWanted = cp; 435 /* Try to open the floppy drive */ 436 if (DITEM_STATUS(mediaSetFloppy(NULL)) == DITEM_FAILURE) { 437 msgConfirm("Unable to set media device to floppy."); 438 what |= DITEM_FAILURE; 439 mediaClose(); 440 return what; 441 } 442 443 if (!DEVICE_INIT(mediaDevice)) { 444 msgConfirm("Unable to mount floppy filesystem."); 445 what |= DITEM_FAILURE; 446 mediaClose(); 447 return what; 448 } 449 450 fp = DEVICE_GET(mediaDevice, cp, TRUE); 451 if (fp) { 452 list = dispatch_load_fp(fp); 453 fclose(fp); 454 mediaClose(); 455 456 what |= dispatch_execute(list); 457 } 458 else { 459 if (!variable_get(VAR_NO_ERROR)) 460 msgConfirm("Configuration file '%s' not found.", cp); 461 variable_unset(VAR_INSTALL_CFG); 462 what |= DITEM_FAILURE; 463 mediaClose(); 464 } 465 return what; 466} 467 468int 469dispatch_load_cdrom(dialogMenuItem *self) 470{ 471 int what = DITEM_SUCCESS; 472 extern char *distWanted; 473 char *cp; 474 FILE *fp; 475 qelement *list; 476 477 mediaClose(); 478 cp = variable_get_value(VAR_INSTALL_CFG, 479 "Specify the name of a configuration file\n" 480 "residing on the CDROM.", 0); 481 if (!cp || !*cp) { 482 variable_unset(VAR_INSTALL_CFG); 483 what |= DITEM_FAILURE; 484 return what; 485 } 486 487 distWanted = cp; 488 /* Try to open the floppy drive */ 489 if (DITEM_STATUS(mediaSetCDROM(NULL)) == DITEM_FAILURE) { 490 msgConfirm("Unable to set media device to CDROM."); 491 what |= DITEM_FAILURE; 492 mediaClose(); 493 return what; 494 } 495 496 if (!DEVICE_INIT(mediaDevice)) { 497 msgConfirm("Unable to CDROM filesystem."); 498 what |= DITEM_FAILURE; 499 mediaClose(); 500 return what; 501 } 502 503 fp = DEVICE_GET(mediaDevice, cp, TRUE); 504 if (fp) { 505 list = dispatch_load_fp(fp); 506 fclose(fp); 507 mediaClose(); 508 509 what |= dispatch_execute(list); 510 } 511 else { 512 if (!variable_get(VAR_NO_ERROR)) 513 msgConfirm("Configuration file '%s' not found.", cp); 514 variable_unset(VAR_INSTALL_CFG); 515 what |= DITEM_FAILURE; 516 mediaClose(); 517 } 518 return what; 519} 520 521/* 522 * Create a menu based on available disk devices 523 */ 524int 525dispatch_load_menu(dialogMenuItem *self) 526{ 527 DMenu *menu; 528 Device **devlist; 529 char *err; 530 int what, i, j, msize, count; 531 DeviceType dtypes[] = {DEVICE_TYPE_FLOPPY, DEVICE_TYPE_CDROM, 532 DEVICE_TYPE_DOS, DEVICE_TYPE_UFS, DEVICE_TYPE_USB}; 533 534 fprintf(stderr, "dispatch_load_menu called\n"); 535 536 msize = sizeof(DMenu) + (sizeof(dialogMenuItem) * 2); 537 count = 0; 538 err = NULL; 539 what = DITEM_SUCCESS; 540 541 if ((menu = malloc(msize)) == NULL) { 542 err = "Failed to allocate memory for menu"; 543 goto errout; 544 } 545 546 bcopy(&MenuConfig, menu, sizeof(DMenu)); 547 548 bzero(&menu->items[count], sizeof(menu->items[0])); 549 menu->items[count].prompt = strdup("X Exit"); 550 menu->items[count].title = strdup("Exit this menu (returning to previous)"); 551 menu->items[count].fire = dmenuExit; 552 count++; 553 554 for (i = 0; i < sizeof(dtypes) / sizeof(dtypes[0]); i++) { 555 if ((devlist = deviceFind(NULL, dtypes[i])) == NULL) { 556 fprintf(stderr, "No devices found for type %d\n", dtypes[i]); 557 continue; 558 } 559 560 for (j = 0; devlist[j] != NULL; j++) { 561 fprintf(stderr, "device type %d device name %s\n", dtypes[i], devlist[j]->name); 562 msize += sizeof(dialogMenuItem); 563 if ((menu = realloc(menu, msize)) == NULL) { 564 err = "Failed to allocate memory for menu item"; 565 goto errout; 566 } 567 568 bzero(&menu->items[count], sizeof(menu->items[0])); 569 menu->items[count].fire = cfgModuleFire; 570 571 menu->items[count].prompt = strdup(devlist[j]->name); 572 menu->items[count].title = strdup(devlist[j]->description); 573 /* XXX: dialog(3) sucks */ 574 menu->items[count].aux = (long)devlist[j]; 575 count++; 576 } 577 } 578 579 menu->items[count].prompt = NULL; 580 menu->items[count].title = NULL; 581 582 dmenuOpenSimple(menu, FALSE); 583 584 errout: 585 for (i = 0; i < count; i++) { 586 free(menu->items[i].prompt); 587 free(menu->items[i].title); 588 } 589 590 free(menu); 591 592 if (err != NULL) { 593 what |= DITEM_FAILURE; 594 if (!variable_get(VAR_NO_ERROR)) 595 msgConfirm("%s", err); 596 } 597 598 return (what); 599 600} 601 602static int 603cfgModuleFire(dialogMenuItem *self) { 604 Device *d; 605 FILE *fp; 606 int what = DITEM_SUCCESS; 607 extern char *distWanted; 608 qelement *list; 609 char *cp; 610 611 d = (Device *)self->aux; 612 613 msgDebug("cfgModuleFire: User selected %s (%s)\n", self->prompt, d->devname); 614 615 mediaClose(); 616 617 cp = variable_get_value(VAR_INSTALL_CFG, 618 "Specify the name of a configuration file", 0); 619 if (!cp || !*cp) { 620 variable_unset(VAR_INSTALL_CFG); 621 what |= DITEM_FAILURE; 622 return what; 623 } 624 625 distWanted = cp; 626 627 mediaDevice = d; 628 if (!DEVICE_INIT(mediaDevice)) { 629 msgConfirm("Unable to mount filesystem."); 630 what |= DITEM_FAILURE; 631 mediaClose(); 632 return what; 633 } 634 msgDebug("getting fp for %s\n", cp); 635 636 fp = DEVICE_GET(mediaDevice, cp, TRUE); 637 if (fp) { 638 msgDebug("opened OK, processing..\n"); 639 640 list = dispatch_load_fp(fp); 641 fclose(fp); 642 mediaClose(); 643 644 what |= dispatch_execute(list); 645 } else { 646 if (!variable_get(VAR_NO_ERROR)) 647 msgConfirm("Configuration file '%s' not found.", cp); 648 variable_unset(VAR_INSTALL_CFG); 649 what |= DITEM_FAILURE; 650 mediaClose(); 651 } 652 653 return(what); 654 } 655