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.108 1998/03/10 17:24:03 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 if (file_readable("/var/run/ld.so.hints"))
514 systemExecute("/sbin/ldconfig -m /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
516 systemExecute("/sbin/ldconfig /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
517 config = variable_get(VAR_XF86_CONFIG);
519 return DITEM_FAILURE | DITEM_RESTORE;
520 execfile = string_concat("/usr/X11R6/bin/", config);
521 if (file_executable(execfile)) {
522 dialog_clear_norefresh();
523 moused = variable_get(VAR_MOUSED);
524 while (!moused || strcmp(moused, "YES")) {
525 if (msgYesNo("The X server may access the mouse in two ways: direct access\n"
526 "or indirect access via the mouse daemon. You have not\n"
527 "configured the mouse daemon. Would you like to configure it\n"
528 "now? If you intend to let the X server access the mouse\n"
529 "directly, choose \"No\" at this time."))
531 dmenuOpenSimple(&MenuMouse, FALSE);
533 moused = variable_get(VAR_MOUSED);
535 if (moused && !strcmp(moused, "YES"))
536 msgConfirm("You have configured and been running the mouse daemon.\n"
537 "Choose \"/dev/sysmouse\" as the mouse port and \"SysMouse\" or\n"
538 "\"MouseSystems\" as the mouse protocol in the X configuration\n"
541 systemExecute(execfile);
542 return DITEM_SUCCESS | DITEM_RESTORE;
545 dialog_clear_norefresh();
546 msgConfirm("XFree86 does not appear to be installed! Please install\n"
547 "The XFree86 distribution before attempting to configure it.");
548 return DITEM_FAILURE | DITEM_RESTORE;
558 cp = variable_get(VAR_NAMESERVER);
562 fp = fopen("/etc/resolv.conf", "w");
565 if (variable_get(VAR_DOMAINNAME))
566 fprintf(fp, "domain\t%s\n", variable_get(VAR_DOMAINNAME));
567 fprintf(fp, "nameserver\t%s\n", cp);
570 msgDebug("Wrote out /etc/resolv.conf\n");
573 dp = variable_get(VAR_DOMAINNAME);
574 cp = variable_get(VAR_IPADDR);
575 hp = variable_get(VAR_HOSTNAME);
576 /* Tack ourselves into /etc/hosts */
577 fp = fopen("/etc/hosts", "w");
580 /* Add an entry for localhost */
581 fprintf(fp, "127.0.0.1\t\tlocalhost.%s localhost\n", dp ? dp : "my.domain");
582 /* Now the host entries, if applicable */
583 if (cp && cp[0] != '0' && hp) {
589 SAFE_STRCPY(cp2, hp);
590 *(index(cp2, '.')) = '\0';
592 fprintf(fp, "%s\t\t%s %s\n", cp, hp, cp2);
593 fprintf(fp, "%s\t\t%s.\n", cp, hp);
597 msgDebug("Wrote out /etc/hosts\n");
601 configRouter(dialogMenuItem *self)
605 ret = variable_get_value(VAR_ROUTER,
606 "Please specify the router you wish to use. Routed is\n"
607 "provided with the stock system and gated is provided\n"
608 "as an optional package which this installation system\n"
609 "will attempt to load if you select gated. Any other\n"
610 "choice of routing daemon will be assumed to be something\n"
611 "the user intends to install themselves before rebooting\n"
612 "the system. If you don't want any routing daemon, choose NO")
613 ? DITEM_SUCCESS : DITEM_FAILURE;
615 if (ret == DITEM_SUCCESS) {
616 char *cp = variable_get(VAR_ROUTER);
618 if (cp && strcmp(cp, "NO")) {
619 variable_set2(VAR_ROUTER_ENABLE, "YES");
620 if (!strcmp(cp, "gated")) {
621 if (package_add(variable_get(VAR_GATED_PKG)) != DITEM_SUCCESS) {
622 msgConfirm("Unable to load gated package. Falling back to no router.");
623 variable_unset(VAR_ROUTER);
624 variable_unset(VAR_ROUTERFLAGS);
625 variable_set2(VAR_ROUTER_ENABLE, "NO");
630 /* Now get the flags, if they chose a router */
631 ret = variable_get_value(VAR_ROUTERFLAGS,
632 "Please Specify the routing daemon flags; if you're running routed\n"
633 "then -q is the right choice for nodes and -s for gateway hosts.\n")
634 ? DITEM_SUCCESS : DITEM_FAILURE;
635 if (ret != DITEM_SUCCESS)
636 variable_unset(VAR_ROUTERFLAGS);
641 variable_set2(VAR_ROUTER_ENABLE, "NO");
642 variable_unset(VAR_ROUTERFLAGS);
643 variable_unset(VAR_ROUTER);
646 return ret | DITEM_RESTORE;
650 configPackages(dialogMenuItem *self)
652 static PkgNode top, plist;
653 static Boolean index_initted = FALSE;
658 return DITEM_FAILURE;
660 if (!mediaDevice->init(mediaDevice))
661 return DITEM_FAILURE;
663 if (!index_initted) {
664 msgNotify("Attempting to fetch packages/INDEX file from selected media.");
665 fp = mediaDevice->get(mediaDevice, "packages/INDEX", TRUE);
667 dialog_clear_norefresh();
668 msgConfirm("Unable to get packages/INDEX file from selected media.\n"
669 "This may be because the packages collection is not available at\n"
670 "on the distribution media you've chosen (most likely an FTP site\n"
671 "without the packages collection mirrored). Please verify media\n"
672 "(or path to media) and try again. If your local site does not\n"
673 "carry the packages collection, then we recommend either a CD\n"
674 "distribution or the master distribution on ftp.freebsd.org.");
675 mediaDevice->shutdown(mediaDevice);
676 return DITEM_FAILURE | DITEM_RESTORE;
678 msgNotify("Located INDEX, now reading package data from it...");
679 index_init(&top, &plist);
680 if (index_read(fp, &top)) {
681 msgConfirm("I/O or format error on packages/INDEX file.\n"
682 "Please verify media (or path to media) and try again.");
684 return DITEM_FAILURE | DITEM_RESTORE;
688 index_initted = TRUE;
691 int ret, pos, scroll;
693 /* Bring up the packages menu */
695 index_menu(&top, &plist, &pos, &scroll);
697 if (plist.kids && plist.kids->name) {
698 /* Now show the packing list menu */
700 ret = index_menu(&plist, NULL, &pos, &scroll);
701 if (ret & DITEM_LEAVE_MENU)
703 else if (DITEM_STATUS(ret) != DITEM_FAILURE) {
704 index_extract(mediaDevice, &top, &plist);
709 dialog_clear_norefresh();
710 msgConfirm("No packages were selected for extraction.");
716 PkgNodePtr tmp2 = tmp->next;
721 index_init(NULL, &plist);
722 return DITEM_SUCCESS | DITEM_RESTORE;
725 #ifdef NETCON_EXTENTIONS
726 /* Load novell client/server package */
728 configNovell(dialogMenuItem *self)
730 int ret = DITEM_SUCCESS;
732 if (!RunningAsInit) {
733 msgConfirm("This package can only be installed in multi-user mode.");
736 if (variable_get(VAR_NOVELL))
737 variable_unset(VAR_NOVELL);
739 ret = package_add(PACKAGE_NETCON);
740 if (DITEM_STATUS(ret) == DITEM_SUCCESS)
741 variable_set2(VAR_NOVELL, "YES");
743 return ret | DITEM_RESTORE;
747 /* Load pcnfsd package */
749 configPCNFSD(dialogMenuItem *self)
751 int ret = DITEM_SUCCESS;
753 if (variable_get(VAR_PCNFSD))
754 variable_unset(VAR_PCNFSD);
756 ret = package_add(variable_get(VAR_PCNFSD_PKG));
757 if (DITEM_STATUS(ret) == DITEM_SUCCESS) {
758 variable_set2(VAR_PCNFSD, "YES");
759 variable_set2("mountd_flags", "-n");
766 configNFSServer(dialogMenuItem *self)
770 /* If we're an NFS server, we need an exports file */
771 if (!file_readable("/etc/exports")) {
772 WINDOW *w = savescr();
774 if (file_readable("/etc/exports.disabled"))
775 vsystem("mv /etc/exports.disabled /etc/exports");
777 dialog_clear_norefresh();
778 msgConfirm("Operating as an NFS server means that you must first configure\n"
779 "an /etc/exports file to indicate which hosts are allowed certain\n"
780 "kinds of access to your local file systems.\n"
781 "Press [ENTER] now to invoke an editor on /etc/exports\n");
782 vsystem("echo '#The following examples export /usr to 3 machines named after ducks,' > /etc/exports");
783 vsystem("echo '#/home and all directories under it to machines named after dead rock stars' >> /etc/exports");
784 vsystem("echo '#and, finally, /a to 2 privileged machines allowed to write on it as root.' >> /etc/exports");
785 vsystem("echo '#/usr huey louie dewie' >> /etc/exports");
786 vsystem("echo '#/home -alldirs janice jimmy frank' >> /etc/exports");
787 vsystem("echo '#/a -maproot=0 bill albert' >> /etc/exports");
788 vsystem("echo '#' >> /etc/exports");
789 vsystem("echo '# You should replace these lines with your actual exported filesystems.' >> /etc/exports");
790 vsystem("echo >> /etc/exports");
791 sprintf(cmd, "%s /etc/exports", variable_get(VAR_EDITOR));
796 variable_set2(VAR_NFS_SERVER, "YES");
798 else if (variable_get(VAR_NFS_SERVER)) { /* We want to turn it off again? */
799 vsystem("mv -f /etc/exports /etc/exports.disabled");
800 variable_unset(VAR_NFS_SERVER);
802 return DITEM_SUCCESS;