2 * The new sysinstall program.
4 * This is probably the last program in the `sysinstall' line - the next
5 * generation being essentially a complete rewrite.
7 * $Id: config.c,v 1.107 1998/03/09 15:00:43 jkh Exp $
10 * Jordan Hubbard. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
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
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.
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
37 #include "sysinstall.h"
38 #include <sys/disklabel.h>
40 #include <sys/errno.h>
41 #include <sys/ioctl.h>
42 #include <sys/fcntl.h>
43 #include <sys/param.h>
46 #include <sys/mount.h>
48 static Chunk *chunk_list[MAX_CHUNKS];
50 static int rootdev_is_od;
54 chunk_compare(Chunk *c1, Chunk *c2)
62 else if (!c1->private_data && !c2->private_data)
64 else if (c1->private_data && !c2->private_data)
66 else if (!c1->private_data && c2->private_data)
69 return strcmp(((PartInfo *)(c1->private_data))->mountpoint, ((PartInfo *)(c2->private_data))->mountpoint);
77 for (i = 0; i < nchunks; i++) {
78 for (j = 0; j < nchunks; j++) {
79 if (chunk_compare(chunk_list[j], chunk_list[j + 1]) > 0) {
80 Chunk *tmp = chunk_list[j];
82 chunk_list[j] = chunk_list[j + 1];
83 chunk_list[j + 1] = tmp;
90 check_rootdev(Chunk **list, int n)
96 for (i = 0; i < n; i++) {
98 if (c->type == part && (c->flags & CHUNK_IS_ROOT)
99 && strncmp(c->disk->name, "od", 2) == 0)
111 mount_point(Chunk *c1)
113 if (c1->type == part && c1->subtype == FS_SWAP)
115 else if (c1->type == part || c1->type == fat)
116 return ((PartInfo *)c1->private_data)->mountpoint;
125 else if (c1->type == part) {
126 if (c1->subtype != FS_SWAP)
135 fstype_short(Chunk *c1)
137 if (c1->type == part) {
138 if (c1->subtype != FS_SWAP) {
139 if (rootdev_is_od == 0 && strncmp(c1->name, "od", 2) == 0)
147 else if (c1->type == fat) {
148 if (strncmp(c1->name, "od", 2) == 0)
159 if (c1->type == part && c1->subtype != FS_SWAP) {
160 if (rootdev_is_od == 0 && strncmp(c1->name, "od", 2) == 0)
162 else if (c1->flags & CHUNK_IS_ROOT)
179 if (!RunningAsInit) {
180 if (file_readable("/etc/fstab"))
181 return DITEM_SUCCESS;
183 msgConfirm("Attempting to rebuild your /etc/fstab file. Warning: If you had\n"
184 "any CD devices in use before running sysinstall then they may NOT\n"
185 "be found by this run!");
189 devs = deviceFind(NULL, DEVICE_TYPE_DISK);
191 msgConfirm("No disks found!");
192 return DITEM_FAILURE;
195 /* Record all the chunks */
197 for (i = 0; devs[i]; i++) {
198 if (!devs[i]->enabled)
200 disk = (Disk *)devs[i]->private;
202 msgFatal("No chunk list found for %s!", disk->name);
203 for (c1 = disk->chunks->part; c1; c1 = c1->next) {
204 if (c1->type == freebsd) {
205 for (c2 = c1->part; c2; c2 = c2->next) {
206 if (c2->type == part && (c2->subtype == FS_SWAP || c2->private_data))
207 chunk_list[nchunks++] = c2;
210 else if (c1->type == fat && c1->private_data)
211 chunk_list[nchunks++] = c1;
214 chunk_list[nchunks] = 0;
217 fstab = fopen("/etc/fstab", "w");
219 msgConfirm("Unable to create a new /etc/fstab file! Manual intervention\n"
220 "will be required.");
221 return DITEM_FAILURE;
224 check_rootdev(chunk_list, nchunks);
226 /* Go for the burn */
227 msgDebug("Generating /etc/fstab file\n");
228 fprintf(fstab, "# Device\t\tMountpoint\tFStype\tOptions\t\tDump\tPass#\n");
229 for (i = 0; i < nchunks; i++)
230 fprintf(fstab, "/dev/%s\t\t%s\t\t%s\t%s\t\t%d\t%d\n", name_of(chunk_list[i]), mount_point(chunk_list[i]),
231 fstype(chunk_list[i]), fstype_short(chunk_list[i]), seq_num(chunk_list[i]), seq_num(chunk_list[i]));
233 fprintf(fstab, "proc\t\t\t/proc\t\tprocfs\trw\t\t0\t0\n");
235 /* Now look for the CDROMs */
236 devs = deviceFind(NULL, DEVICE_TYPE_CDROM);
237 cnt = deviceCount(devs);
239 /* Write the first one out as /cdrom */
241 if (Mkdir("/cdrom")) {
242 msgConfirm("Unable to make mount point for: /cdrom");
245 fprintf(fstab, "/dev/%s\t\t/cdrom\t\tcd9660\tro,noauto\t0\t0\n", devs[0]->name);
248 /* Write the others out as /cdrom<n> */
249 for (i = 1; i < cnt; i++) {
252 sprintf(cdname, "/cdrom%d", i);
254 msgConfirm("Unable to make mount point for: %s", cdname);
257 fprintf(fstab, "/dev/%s\t\t%s\tcd9660\tro,noauto\t0\t0\n", devs[i]->name, cdname);
261 msgDebug("Wrote out /etc/fstab file\n");
262 return DITEM_SUCCESS;
265 /* Do the work of sucking in a config file.
266 * config is the filename to read in.
267 * lines is a fixed (max) sized array of char *.
268 * returns number of lines read. line contents
269 * are malloc'd and must be freed by the caller.
272 readConfig(char *config, char **lines, int max)
278 fp = fopen(config, "r");
283 /* Read in the entire file */
284 for (i = 0; i < max; i++) {
285 if (!fgets(line, sizeof line, fp))
287 lines[nlines++] = strdup(line);
291 msgDebug("readConfig: Read %d lines from %s.\n", nlines, config);
295 #define MAX_LINES 2000 /* Some big number we're not likely to ever reach - I'm being really lazy here, I know */
297 /* Load the environment from an rc.conf file */
299 configEnvironmentRC_conf(char *config)
301 char *lines[MAX_LINES], *cp, *cp2;
304 nlines = readConfig(config, lines, MAX_LINES);
308 for (i = 0; i < nlines; i++) {
309 /* Skip the comments & non-variable settings */
310 if (lines[i][0] == '#' || !(cp = index(lines[i], '='))) {
316 if ((cp2 = index(cp, '"')) || (cp2 = index(cp, '\047'))) {
318 cp2 = index(cp, *cp2);
320 /* If valid quotes, use it */
323 /* If we have a legit value and it's not already set, set it */
324 if (strlen(cp) && !variable_get(lines[i]))
325 variable_set2(lines[i], cp);
331 /* Load the environment from a resolv.conf file */
333 configEnvironmentResolv(char *config)
335 char *lines[MAX_LINES];
338 nlines = readConfig(config, lines, MAX_LINES);
341 for (i = 0; i < nlines; i++) {
342 Boolean name_set = (Boolean)variable_get(VAR_NAMESERVER);
344 if (!strncmp(lines[i], "domain", 6) && !variable_get(VAR_DOMAINNAME))
345 variable_set2(VAR_DOMAINNAME, string_skipwhite(string_prune(lines[i] + 6)));
346 else if (!strncmp(lines[i], "nameserver", 10) && !name_set) {
347 /* Only take the first nameserver setting - we're lame */
348 variable_set2(VAR_NAMESERVER, string_skipwhite(string_prune(lines[i] + 10)));
355 /* Version of below for dispatch routines */
357 configRC(dialogMenuItem *unused)
359 configRC_conf("/etc/rc.conf");
360 return DITEM_SUCCESS;
364 * This sucks in /etc/rc.conf, substitutes anything needing substitution, then
365 * writes it all back out. It's pretty gross and needs re-writing at some point.
368 configRC_conf(char *config)
371 char *lines[MAX_LINES], *cp;
375 nlines = readConfig(config, lines, MAX_LINES);
379 /* Now do variable substitutions */
380 for (v = VarHead; v; v = v->next) {
381 for (i = 0; i < nlines; i++) {
382 /* Skip the comments & non-variable settings */
383 if (lines[i][0] == '#' || !(cp = index(lines[i], '=')))
385 len = strlen(v->name);
386 if (!strncmp(lines[i], v->name, cp - lines[i]) && (cp - lines[i]) == len) {
387 char *cp2, *comment = NULL;
389 /* If trailing comment, try and preserve it */
390 if ((index(lines[i], '#')) != NULL) {
392 if ((cp2 = index(cp, '"')) || (cp2 = index(cp, '\047')))
393 cp2 = index(cp2 + 1, *cp2);
394 if (cp2 && strlen(cp2 + 1)) {
395 comment = alloca(strlen(cp2));
396 strcpy(comment, cp2 + 1);
400 lines[i] = (char *)malloc(strlen(v->name) + strlen(v->value) + (comment ? strlen(comment) : 0) + 10);
402 sprintf(lines[i], "%s=\"%s\"%s", v->name, v->value, comment);
404 sprintf(lines[i], "%s=\"%s\"\n", v->name, v->value);
409 /* Now write it all back out again */
411 msgDebug("Writing configuration changes to %s file..", config);
413 fp = fdopen(DebugFD, "w");
415 (void)vsystem("cp %s %s.previous", config, config);
416 fp = fopen(config, "w");
418 for (i = 0; i < nlines; i++) {
419 fprintf(fp, lines[i]);
420 /* Stand by for bogus special case handling - we try to dump the interface specs here */
421 if (!strncmp(lines[i], VAR_INTERFACES, strlen(VAR_INTERFACES))) {
425 devp = deviceFind(NULL, DEVICE_TYPE_NETWORK);
426 cnt = deviceCount(devp);
427 for (j = 0; j < cnt; j++) {
428 char iname[255], toadd[512];
431 if (!strncmp(devp[j]->name, "ppp", 3) || !strncmp(devp[j]->name, "tun", 3))
434 snprintf(iname, 255, "%s%s", VAR_IFCONFIG, devp[j]->name);
435 if ((cp = variable_get(iname))) {
436 snprintf(toadd, sizeof toadd, "%s=\"%s\"\n", iname, cp);
437 for (k = 0; k < nlines; k++) {
438 if (!strcmp(lines[k], toadd)) {
454 configSaver(dialogMenuItem *self)
456 variable_set((char *)self->data);
457 if (!variable_get(VAR_BLANKTIME))
458 variable_set2(VAR_BLANKTIME, "300");
459 return DITEM_SUCCESS;
463 configSaverTimeout(dialogMenuItem *self)
465 return (variable_get_value(VAR_BLANKTIME,
466 "Enter time-out period in seconds for screen saver") ?
467 DITEM_SUCCESS : DITEM_FAILURE) | DITEM_RESTORE;
471 configRegister(dialogMenuItem *self)
473 return DITEM_STATUS(registerOpenDialog()) | DITEM_RESTORE;
477 configNTP(dialogMenuItem *self)
481 status = variable_get_value(VAR_NTPDATE_FLAGS,
482 "Enter the name of an NTP server")
483 ? DITEM_SUCCESS : DITEM_FAILURE;
484 if (status == DITEM_SUCCESS) {
485 static char tmp[255];
487 snprintf(tmp, sizeof(tmp), "ntpdate_enable=YES,ntpdate_flags=%s",
488 variable_get(VAR_NTPDATE_FLAGS));
490 dmenuSetVariables(self);
492 return status | DITEM_RESTORE;
496 configUsers(dialogMenuItem *self)
498 dialog_clear_norefresh();
499 dmenuOpenSimple(&MenuUsermgmt, FALSE);
501 return DITEM_SUCCESS | DITEM_RESTORE;
505 configXEnvironment(dialogMenuItem *self)
507 char *config, *execfile;
510 dialog_clear_norefresh();
511 if (!dmenuOpenSimple(&MenuXF86Config, FALSE))
512 return DITEM_FAILURE | DITEM_RESTORE;
513 systemExecute("/sbin/ldconfig /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
514 config = variable_get(VAR_XF86_CONFIG);
516 return DITEM_FAILURE | DITEM_RESTORE;
517 execfile = string_concat("/usr/X11R6/bin/", config);
518 if (file_executable(execfile)) {
519 dialog_clear_norefresh();
520 moused = variable_get(VAR_MOUSED);
521 while (!moused || strcmp(moused, "YES")) {
522 if (msgYesNo("The X server may access the mouse in two ways: direct access\n"
523 "or indirect access via the mouse daemon. You have not\n"
524 "configured the mouse daemon. Would you like to configure it\n"
525 "now? If you intend to let the X server access the mouse\n"
526 "directly, choose \"No\" at this time."))
528 dmenuOpenSimple(&MenuMouse, FALSE);
530 moused = variable_get(VAR_MOUSED);
532 if (moused && !strcmp(moused, "YES"))
533 msgConfirm("You have configured and been running the mouse daemon.\n"
534 "Choose \"/dev/sysmouse\" as the mouse port and \"SysMouse\" or\n"
535 "\"MouseSystems\" as the mouse protocol in the X configuration\n"
538 systemExecute(execfile);
539 return DITEM_SUCCESS | DITEM_RESTORE;
542 dialog_clear_norefresh();
543 msgConfirm("XFree86 does not appear to be installed! Please install\n"
544 "The XFree86 distribution before attempting to configure it.");
545 return DITEM_FAILURE | DITEM_RESTORE;
555 cp = variable_get(VAR_NAMESERVER);
559 fp = fopen("/etc/resolv.conf", "w");
562 if (variable_get(VAR_DOMAINNAME))
563 fprintf(fp, "domain\t%s\n", variable_get(VAR_DOMAINNAME));
564 fprintf(fp, "nameserver\t%s\n", cp);
567 msgDebug("Wrote out /etc/resolv.conf\n");
570 dp = variable_get(VAR_DOMAINNAME);
571 cp = variable_get(VAR_IPADDR);
572 hp = variable_get(VAR_HOSTNAME);
573 /* Tack ourselves into /etc/hosts */
574 fp = fopen("/etc/hosts", "w");
577 /* Add an entry for localhost */
578 fprintf(fp, "127.0.0.1\t\tlocalhost.%s localhost\n", dp ? dp : "my.domain");
579 /* Now the host entries, if applicable */
580 if (cp && cp[0] != '0' && hp) {
586 SAFE_STRCPY(cp2, hp);
587 *(index(cp2, '.')) = '\0';
589 fprintf(fp, "%s\t\t%s %s\n", cp, hp, cp2);
590 fprintf(fp, "%s\t\t%s.\n", cp, hp);
594 msgDebug("Wrote out /etc/hosts\n");
598 configRouter(dialogMenuItem *self)
602 ret = variable_get_value(VAR_ROUTER,
603 "Please specify the router you wish to use. Routed is\n"
604 "provided with the stock system and gated is provided\n"
605 "as an optional package which this installation system\n"
606 "will attempt to load if you select gated. Any other\n"
607 "choice of routing daemon will be assumed to be something\n"
608 "the user intends to install themselves before rebooting\n"
609 "the system. If you don't want any routing daemon, choose NO")
610 ? DITEM_SUCCESS : DITEM_FAILURE;
612 if (ret == DITEM_SUCCESS) {
613 char *cp = variable_get(VAR_ROUTER);
615 if (cp && strcmp(cp, "NO")) {
616 variable_set2(VAR_ROUTER_ENABLE, "YES");
617 if (!strcmp(cp, "gated")) {
618 if (package_add(variable_get(VAR_GATED_PKG)) != DITEM_SUCCESS) {
619 msgConfirm("Unable to load gated package. Falling back to no router.");
620 variable_unset(VAR_ROUTER);
621 variable_unset(VAR_ROUTERFLAGS);
622 variable_set2(VAR_ROUTER_ENABLE, "NO");
627 /* Now get the flags, if they chose a router */
628 ret = variable_get_value(VAR_ROUTERFLAGS,
629 "Please Specify the routing daemon flags; if you're running routed\n"
630 "then -q is the right choice for nodes and -s for gateway hosts.\n")
631 ? DITEM_SUCCESS : DITEM_FAILURE;
632 if (ret != DITEM_SUCCESS)
633 variable_unset(VAR_ROUTERFLAGS);
638 variable_set2(VAR_ROUTER_ENABLE, "NO");
639 variable_unset(VAR_ROUTERFLAGS);
640 variable_unset(VAR_ROUTER);
643 return ret | DITEM_RESTORE;
647 configPackages(dialogMenuItem *self)
649 static PkgNode top, plist;
650 static Boolean index_initted = FALSE;
655 return DITEM_FAILURE;
657 if (!mediaDevice->init(mediaDevice))
658 return DITEM_FAILURE;
660 if (!index_initted) {
661 msgNotify("Attempting to fetch packages/INDEX file from selected media.");
662 fp = mediaDevice->get(mediaDevice, "packages/INDEX", TRUE);
664 dialog_clear_norefresh();
665 msgConfirm("Unable to get packages/INDEX file from selected media.\n"
666 "This may be because the packages collection is not available at\n"
667 "on the distribution media you've chosen (most likely an FTP site\n"
668 "without the packages collection mirrored). Please verify media\n"
669 "(or path to media) and try again. If your local site does not\n"
670 "carry the packages collection, then we recommend either a CD\n"
671 "distribution or the master distribution on ftp.freebsd.org.");
672 mediaDevice->shutdown(mediaDevice);
673 return DITEM_FAILURE | DITEM_RESTORE;
675 msgNotify("Located INDEX, now reading package data from it...");
676 index_init(&top, &plist);
677 if (index_read(fp, &top)) {
678 msgConfirm("I/O or format error on packages/INDEX file.\n"
679 "Please verify media (or path to media) and try again.");
681 return DITEM_FAILURE | DITEM_RESTORE;
685 index_initted = TRUE;
688 int ret, pos, scroll;
690 /* Bring up the packages menu */
692 index_menu(&top, &plist, &pos, &scroll);
694 if (plist.kids && plist.kids->name) {
695 /* Now show the packing list menu */
697 ret = index_menu(&plist, NULL, &pos, &scroll);
698 if (ret & DITEM_LEAVE_MENU)
700 else if (DITEM_STATUS(ret) != DITEM_FAILURE) {
701 index_extract(mediaDevice, &top, &plist);
706 dialog_clear_norefresh();
707 msgConfirm("No packages were selected for extraction.");
713 PkgNodePtr tmp2 = tmp->next;
718 index_init(NULL, &plist);
719 return DITEM_SUCCESS | DITEM_RESTORE;
722 #ifdef NETCON_EXTENTIONS
723 /* Load novell client/server package */
725 configNovell(dialogMenuItem *self)
727 int ret = DITEM_SUCCESS;
729 if (!RunningAsInit) {
730 msgConfirm("This package can only be installed in multi-user mode.");
733 if (variable_get(VAR_NOVELL))
734 variable_unset(VAR_NOVELL);
736 ret = package_add(PACKAGE_NETCON);
737 if (DITEM_STATUS(ret) == DITEM_SUCCESS)
738 variable_set2(VAR_NOVELL, "YES");
740 return ret | DITEM_RESTORE;
744 /* Load pcnfsd package */
746 configPCNFSD(dialogMenuItem *self)
748 int ret = DITEM_SUCCESS;
750 if (variable_get(VAR_PCNFSD))
751 variable_unset(VAR_PCNFSD);
753 ret = package_add(variable_get(VAR_PCNFSD_PKG));
754 if (DITEM_STATUS(ret) == DITEM_SUCCESS) {
755 variable_set2(VAR_PCNFSD, "YES");
756 variable_set2("mountd_flags", "-n");
763 configNFSServer(dialogMenuItem *self)
767 /* If we're an NFS server, we need an exports file */
768 if (!file_readable("/etc/exports")) {
769 WINDOW *w = savescr();
771 if (file_readable("/etc/exports.disabled"))
772 vsystem("mv /etc/exports.disabled /etc/exports");
774 dialog_clear_norefresh();
775 msgConfirm("Operating as an NFS server means that you must first configure\n"
776 "an /etc/exports file to indicate which hosts are allowed certain\n"
777 "kinds of access to your local file systems.\n"
778 "Press [ENTER] now to invoke an editor on /etc/exports\n");
779 vsystem("echo '#The following examples export /usr to 3 machines named after ducks,' > /etc/exports");
780 vsystem("echo '#/home and all directories under it to machines named after dead rock stars' >> /etc/exports");
781 vsystem("echo '#and, finally, /a to 2 privileged machines allowed to write on it as root.' >> /etc/exports");
782 vsystem("echo '#/usr huey louie dewie' >> /etc/exports");
783 vsystem("echo '#/home -alldirs janice jimmy frank' >> /etc/exports");
784 vsystem("echo '#/a -maproot=0 bill albert' >> /etc/exports");
785 vsystem("echo '#' >> /etc/exports");
786 vsystem("echo '# You should replace these lines with your actual exported filesystems.' >> /etc/exports");
787 vsystem("echo >> /etc/exports");
788 sprintf(cmd, "%s /etc/exports", variable_get(VAR_EDITOR));
793 variable_set2(VAR_NFS_SERVER, "YES");
795 else if (variable_get(VAR_NFS_SERVER)) { /* We want to turn it off again? */
796 vsystem("mv -f /etc/exports /etc/exports.disabled");
797 variable_unset(VAR_NFS_SERVER);
799 return DITEM_SUCCESS;