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.106 1998/03/09 08:39:46 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 */
324 variable_set2(lines[i], cp);
330 /* Load the environment from a resolv.conf file */
332 configEnvironmentResolv(char *config)
334 char *lines[MAX_LINES];
337 nlines = readConfig(config, lines, MAX_LINES);
340 for (i = 0; i < nlines; i++) {
341 Boolean name_set = FALSE;
343 if (!strncmp(lines[i], "domain", 6))
344 variable_set2(VAR_DOMAINNAME, string_skipwhite(string_prune(lines[i] + 6)));
345 else if (!strncmp(lines[i], "nameserver", 10) && !name_set) {
346 /* Only take the first nameserver setting - we're lame */
347 variable_set2(VAR_NAMESERVER, string_skipwhite(string_prune(lines[i] + 10)));
354 /* Version of below for dispatch routines */
356 configRC(dialogMenuItem *unused)
358 configRC_conf("/etc/rc.conf");
359 return DITEM_SUCCESS;
363 * This sucks in /etc/rc.conf, substitutes anything needing substitution, then
364 * writes it all back out. It's pretty gross and needs re-writing at some point.
367 configRC_conf(char *config)
370 char *lines[MAX_LINES], *cp;
374 nlines = readConfig(config, lines, MAX_LINES);
378 /* Now do variable substitutions */
379 for (v = VarHead; v; v = v->next) {
380 for (i = 0; i < nlines; i++) {
381 /* Skip the comments & non-variable settings */
382 if (lines[i][0] == '#' || !(cp = index(lines[i], '=')))
384 len = strlen(v->name);
385 if (!strncmp(lines[i], v->name, cp - lines[i]) && (cp - lines[i]) == len) {
386 char *cp2, *comment = NULL;
388 /* If trailing comment, try and preserve it */
389 if ((index(lines[i], '#')) != NULL) {
391 if ((cp2 = index(cp, '"')) || (cp2 = index(cp, '\047')))
392 cp2 = index(cp2 + 1, *cp2);
393 if (cp2 && strlen(cp2 + 1)) {
394 comment = alloca(strlen(cp2));
395 strcpy(comment, cp2 + 1);
399 lines[i] = (char *)malloc(strlen(v->name) + strlen(v->value) + (comment ? strlen(comment) : 0) + 10);
401 sprintf(lines[i], "%s=\"%s\"%s", v->name, v->value, comment);
403 sprintf(lines[i], "%s=\"%s\"\n", v->name, v->value);
408 /* Now write it all back out again */
410 msgDebug("Writing configuration changes to %s file..", config);
412 fp = fdopen(DebugFD, "w");
414 (void)vsystem("cp %s %s.previous", config, config);
415 fp = fopen(config, "w");
417 for (i = 0; i < nlines; i++) {
418 fprintf(fp, lines[i]);
419 /* Stand by for bogus special case handling - we try to dump the interface specs here */
420 if (!strncmp(lines[i], VAR_INTERFACES, strlen(VAR_INTERFACES))) {
424 devp = deviceFind(NULL, DEVICE_TYPE_NETWORK);
425 cnt = deviceCount(devp);
426 for (j = 0; j < cnt; j++) {
427 char iname[255], toadd[512];
430 if (!strncmp(devp[j]->name, "ppp", 3) || !strncmp(devp[j]->name, "tun", 3))
433 snprintf(iname, 255, "%s%s", VAR_IFCONFIG, devp[j]->name);
434 if ((cp = variable_get(iname))) {
435 snprintf(toadd, sizeof toadd, "%s=\"%s\"\n", iname, cp);
436 for (k = 0; k < nlines; k++) {
437 if (!strcmp(lines[k], toadd)) {
453 configSaver(dialogMenuItem *self)
455 variable_set((char *)self->data);
456 if (!variable_get(VAR_BLANKTIME))
457 variable_set2(VAR_BLANKTIME, "300");
458 return DITEM_SUCCESS;
462 configSaverTimeout(dialogMenuItem *self)
464 return (variable_get_value(VAR_BLANKTIME,
465 "Enter time-out period in seconds for screen saver") ?
466 DITEM_SUCCESS : DITEM_FAILURE) | DITEM_RESTORE;
470 configRegister(dialogMenuItem *self)
472 return DITEM_STATUS(registerOpenDialog()) | DITEM_RESTORE;
476 configNTP(dialogMenuItem *self)
480 status = variable_get_value(VAR_NTPDATE_FLAGS,
481 "Enter the name of an NTP server")
482 ? DITEM_SUCCESS : DITEM_FAILURE;
483 if (status == DITEM_SUCCESS) {
484 static char tmp[255];
486 snprintf(tmp, sizeof(tmp), "ntpdate_enable=YES,ntpdate_flags=%s",
487 variable_get(VAR_NTPDATE_FLAGS));
489 dmenuSetVariables(self);
491 return status | DITEM_RESTORE;
495 configUsers(dialogMenuItem *self)
497 dialog_clear_norefresh();
498 dmenuOpenSimple(&MenuUsermgmt, FALSE);
500 return DITEM_SUCCESS | DITEM_RESTORE;
504 configXEnvironment(dialogMenuItem *self)
506 char *config, *execfile;
509 dialog_clear_norefresh();
510 if (!dmenuOpenSimple(&MenuXF86Config, FALSE))
511 return DITEM_FAILURE | DITEM_RESTORE;
512 systemExecute("/sbin/ldconfig /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
513 config = variable_get(VAR_XF86_CONFIG);
515 return DITEM_FAILURE | DITEM_RESTORE;
516 execfile = string_concat("/usr/X11R6/bin/", config);
517 if (file_executable(execfile)) {
518 dialog_clear_norefresh();
519 moused = variable_get(VAR_MOUSED);
520 while (!moused || strcmp(moused, "YES")) {
521 if (msgYesNo("The X server may access the mouse in two ways: direct access\n"
522 "or indirect access via the mouse daemon. You have not\n"
523 "configured the mouse daemon. Would you like to configure it\n"
524 "now? If you intend to let the X server access the mouse\n"
525 "directly, choose \"No\" at this time."))
527 dmenuOpenSimple(&MenuMouse, FALSE);
529 moused = variable_get(VAR_MOUSED);
531 if (moused && !strcmp(moused, "YES"))
532 msgConfirm("You have configured and been running the mouse daemon.\n"
533 "Choose \"/dev/sysmouse\" as the mouse port and \"SysMouse\" or\n"
534 "\"MouseSystems\" as the mouse protocol in the X configuration\n"
537 systemExecute(execfile);
538 return DITEM_SUCCESS | DITEM_RESTORE;
541 dialog_clear_norefresh();
542 msgConfirm("XFree86 does not appear to be installed! Please install\n"
543 "The XFree86 distribution before attempting to configure it.");
544 return DITEM_FAILURE | DITEM_RESTORE;
554 cp = variable_get(VAR_NAMESERVER);
558 fp = fopen("/etc/resolv.conf", "w");
561 if (variable_get(VAR_DOMAINNAME))
562 fprintf(fp, "domain\t%s\n", variable_get(VAR_DOMAINNAME));
563 fprintf(fp, "nameserver\t%s\n", cp);
566 msgDebug("Wrote out /etc/resolv.conf\n");
569 dp = variable_get(VAR_DOMAINNAME);
570 cp = variable_get(VAR_IPADDR);
571 hp = variable_get(VAR_HOSTNAME);
572 /* Tack ourselves into /etc/hosts */
573 fp = fopen("/etc/hosts", "w");
576 /* Add an entry for localhost */
577 fprintf(fp, "127.0.0.1\t\tlocalhost.%s localhost\n", dp ? dp : "my.domain");
578 /* Now the host entries, if applicable */
579 if (cp && cp[0] != '0' && hp) {
585 SAFE_STRCPY(cp2, hp);
586 *(index(cp2, '.')) = '\0';
588 fprintf(fp, "%s\t\t%s %s\n", cp, hp, cp2);
589 fprintf(fp, "%s\t\t%s.\n", cp, hp);
593 msgDebug("Wrote out /etc/hosts\n");
597 configRouter(dialogMenuItem *self)
601 ret = variable_get_value(VAR_ROUTER,
602 "Please specify the router you wish to use. Routed is\n"
603 "provided with the stock system and gated is provided\n"
604 "as an optional package which this installation system\n"
605 "will attempt to load if you select gated. Any other\n"
606 "choice of routing daemon will be assumed to be something\n"
607 "the user intends to install themselves before rebooting\n"
608 "the system. If you don't want any routing daemon, choose NO")
609 ? DITEM_SUCCESS : DITEM_FAILURE;
611 if (ret == DITEM_SUCCESS) {
612 char *cp = variable_get(VAR_ROUTER);
614 if (cp && strcmp(cp, "NO")) {
615 variable_set2(VAR_ROUTER_ENABLE, "YES");
616 if (!strcmp(cp, "gated")) {
617 if (package_add(variable_get(VAR_GATED_PKG)) != DITEM_SUCCESS) {
618 msgConfirm("Unable to load gated package. Falling back to no router.");
619 variable_unset(VAR_ROUTER);
620 variable_unset(VAR_ROUTERFLAGS);
621 variable_set2(VAR_ROUTER_ENABLE, "NO");
626 /* Now get the flags, if they chose a router */
627 ret = variable_get_value(VAR_ROUTERFLAGS,
628 "Please Specify the routing daemon flags; if you're running routed\n"
629 "then -q is the right choice for nodes and -s for gateway hosts.\n")
630 ? DITEM_SUCCESS : DITEM_FAILURE;
631 if (ret != DITEM_SUCCESS)
632 variable_unset(VAR_ROUTERFLAGS);
637 variable_set2(VAR_ROUTER_ENABLE, "NO");
638 variable_unset(VAR_ROUTERFLAGS);
639 variable_unset(VAR_ROUTER);
642 return ret | DITEM_RESTORE;
646 configPackages(dialogMenuItem *self)
648 static PkgNode top, plist;
649 static Boolean index_initted = FALSE;
654 return DITEM_FAILURE;
656 if (!mediaDevice->init(mediaDevice))
657 return DITEM_FAILURE;
659 if (!index_initted) {
660 msgNotify("Attempting to fetch packages/INDEX file from selected media.");
661 fp = mediaDevice->get(mediaDevice, "packages/INDEX", TRUE);
663 dialog_clear_norefresh();
664 msgConfirm("Unable to get packages/INDEX file from selected media.\n"
665 "This may be because the packages collection is not available at\n"
666 "on the distribution media you've chosen (most likely an FTP site\n"
667 "without the packages collection mirrored). Please verify media\n"
668 "(or path to media) and try again. If your local site does not\n"
669 "carry the packages collection, then we recommend either a CD\n"
670 "distribution or the master distribution on ftp.freebsd.org.");
671 mediaDevice->shutdown(mediaDevice);
672 return DITEM_FAILURE | DITEM_RESTORE;
674 msgNotify("Located INDEX, now reading package data from it...");
675 index_init(&top, &plist);
676 if (index_read(fp, &top)) {
677 msgConfirm("I/O or format error on packages/INDEX file.\n"
678 "Please verify media (or path to media) and try again.");
680 return DITEM_FAILURE | DITEM_RESTORE;
684 index_initted = TRUE;
687 int ret, pos, scroll;
689 /* Bring up the packages menu */
691 index_menu(&top, &plist, &pos, &scroll);
693 if (plist.kids && plist.kids->name) {
694 /* Now show the packing list menu */
696 ret = index_menu(&plist, NULL, &pos, &scroll);
697 if (ret & DITEM_LEAVE_MENU)
699 else if (DITEM_STATUS(ret) != DITEM_FAILURE) {
700 index_extract(mediaDevice, &top, &plist);
705 dialog_clear_norefresh();
706 msgConfirm("No packages were selected for extraction.");
712 PkgNodePtr tmp2 = tmp->next;
717 index_init(NULL, &plist);
718 return DITEM_SUCCESS | DITEM_RESTORE;
721 #ifdef NETCON_EXTENTIONS
722 /* Load novell client/server package */
724 configNovell(dialogMenuItem *self)
726 int ret = DITEM_SUCCESS;
728 if (!RunningAsInit) {
729 msgConfirm("This package can only be installed in multi-user mode.");
732 if (variable_get(VAR_NOVELL))
733 variable_unset(VAR_NOVELL);
735 ret = package_add(PACKAGE_NETCON);
736 if (DITEM_STATUS(ret) == DITEM_SUCCESS)
737 variable_set2(VAR_NOVELL, "YES");
739 return ret | DITEM_RESTORE;
743 /* Load pcnfsd package */
745 configPCNFSD(dialogMenuItem *self)
747 int ret = DITEM_SUCCESS;
749 if (variable_get(VAR_PCNFSD))
750 variable_unset(VAR_PCNFSD);
752 ret = package_add(variable_get(VAR_PCNFSD_PKG));
753 if (DITEM_STATUS(ret) == DITEM_SUCCESS) {
754 variable_set2(VAR_PCNFSD, "YES");
755 variable_set2("mountd_flags", "-n");
762 configNFSServer(dialogMenuItem *self)
766 /* If we're an NFS server, we need an exports file */
767 if (!file_readable("/etc/exports")) {
768 WINDOW *w = savescr();
770 if (file_readable("/etc/exports.disabled"))
771 vsystem("mv /etc/exports.disabled /etc/exports");
773 dialog_clear_norefresh();
774 msgConfirm("Operating as an NFS server means that you must first configure\n"
775 "an /etc/exports file to indicate which hosts are allowed certain\n"
776 "kinds of access to your local file systems.\n"
777 "Press [ENTER] now to invoke an editor on /etc/exports\n");
778 vsystem("echo '#The following examples export /usr to 3 machines named after ducks,' > /etc/exports");
779 vsystem("echo '#/home and all directories under it to machines named after dead rock stars' >> /etc/exports");
780 vsystem("echo '#and, finally, /a to 2 privileged machines allowed to write on it as root.' >> /etc/exports");
781 vsystem("echo '#/usr huey louie dewie' >> /etc/exports");
782 vsystem("echo '#/home -alldirs janice jimmy frank' >> /etc/exports");
783 vsystem("echo '#/a -maproot=0 bill albert' >> /etc/exports");
784 vsystem("echo '#' >> /etc/exports");
785 vsystem("echo '# You should replace these lines with your actual exported filesystems.' >> /etc/exports");
786 vsystem("echo >> /etc/exports");
787 sprintf(cmd, "%s /etc/exports", variable_get(VAR_EDITOR));
792 variable_set2(VAR_NFS_SERVER, "YES");
794 else if (variable_get(VAR_NFS_SERVER)) { /* We want to turn it off again? */
795 vsystem("mv -f /etc/exports /etc/exports.disabled");
796 variable_unset(VAR_NFS_SERVER);
798 return DITEM_SUCCESS;