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.110 1998/03/24 09:51:54 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 (!name_set && !strncmp(lines[i], "nameserver", 10)) {
347 /* Only take the first nameserver setting - we're lame */
348 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 if (file_readable("/var/run/ld.so.hints"))
513 systemExecute("/sbin/ldconfig -m /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
515 systemExecute("/sbin/ldconfig /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
516 config = variable_get(VAR_XF86_CONFIG);
518 return DITEM_FAILURE | DITEM_RESTORE;
519 execfile = string_concat("/usr/X11R6/bin/", config);
520 if (file_executable(execfile)) {
521 dialog_clear_norefresh();
522 moused = variable_get(VAR_MOUSED);
523 while (!moused || strcmp(moused, "YES")) {
524 if (msgYesNo("The X server may access the mouse in two ways: direct access\n"
525 "or indirect access via the mouse daemon. You have not\n"
526 "configured the mouse daemon. Would you like to configure it\n"
527 "now? If you intend to let the X server access the mouse\n"
528 "directly, choose \"No\" at this time."))
530 dmenuOpenSimple(&MenuMouse, FALSE);
532 moused = variable_get(VAR_MOUSED);
534 if (moused && !strcmp(moused, "YES"))
535 msgConfirm("You have configured and been running the mouse daemon.\n"
536 "Choose \"/dev/sysmouse\" as the mouse port and \"SysMouse\" or\n"
537 "\"MouseSystems\" as the mouse protocol in the X configuration\n"
540 systemExecute(execfile);
541 return DITEM_SUCCESS | DITEM_RESTORE;
544 dialog_clear_norefresh();
545 msgConfirm("The XFree86 setup utility you chose does not appear to be installed!\n"
546 "Please install this before attempting to configure XFree86.");
547 return DITEM_FAILURE | DITEM_RESTORE;
557 cp = variable_get(VAR_NAMESERVER);
561 fp = fopen("/etc/resolv.conf", "w");
564 if (variable_get(VAR_DOMAINNAME))
565 fprintf(fp, "domain\t%s\n", variable_get(VAR_DOMAINNAME));
566 fprintf(fp, "nameserver\t%s\n", cp);
569 msgDebug("Wrote out /etc/resolv.conf\n");
572 dp = variable_get(VAR_DOMAINNAME);
573 cp = variable_get(VAR_IPADDR);
574 hp = variable_get(VAR_HOSTNAME);
575 /* Tack ourselves into /etc/hosts */
576 fp = fopen("/etc/hosts", "w");
579 /* Add an entry for localhost */
580 fprintf(fp, "127.0.0.1\t\tlocalhost.%s localhost\n", dp ? dp : "my.domain");
581 /* Now the host entries, if applicable */
582 if (cp && cp[0] != '0' && hp) {
588 SAFE_STRCPY(cp2, hp);
589 *(index(cp2, '.')) = '\0';
591 fprintf(fp, "%s\t\t%s %s\n", cp, hp, cp2);
592 fprintf(fp, "%s\t\t%s.\n", cp, hp);
596 msgDebug("Wrote out /etc/hosts\n");
600 configRouter(dialogMenuItem *self)
604 ret = variable_get_value(VAR_ROUTER,
605 "Please specify the router you wish to use. Routed is\n"
606 "provided with the stock system and gated is provided\n"
607 "as an optional package which this installation system\n"
608 "will attempt to load if you select gated. Any other\n"
609 "choice of routing daemon will be assumed to be something\n"
610 "the user intends to install themselves before rebooting\n"
611 "the system. If you don't want any routing daemon, choose NO")
612 ? DITEM_SUCCESS : DITEM_FAILURE;
614 if (ret == DITEM_SUCCESS) {
615 char *cp = variable_get(VAR_ROUTER);
617 if (cp && strcmp(cp, "NO")) {
618 variable_set2(VAR_ROUTER_ENABLE, "YES");
619 if (!strcmp(cp, "gated")) {
620 if (package_add(variable_get(VAR_GATED_PKG)) != DITEM_SUCCESS) {
621 msgConfirm("Unable to load gated package. Falling back to no router.");
622 variable_unset(VAR_ROUTER);
623 variable_unset(VAR_ROUTERFLAGS);
624 variable_set2(VAR_ROUTER_ENABLE, "NO");
629 /* Now get the flags, if they chose a router */
630 ret = variable_get_value(VAR_ROUTERFLAGS,
631 "Please Specify the routing daemon flags; if you're running routed\n"
632 "then -q is the right choice for nodes and -s for gateway hosts.\n")
633 ? DITEM_SUCCESS : DITEM_FAILURE;
634 if (ret != DITEM_SUCCESS)
635 variable_unset(VAR_ROUTERFLAGS);
640 variable_set2(VAR_ROUTER_ENABLE, "NO");
641 variable_unset(VAR_ROUTERFLAGS);
642 variable_unset(VAR_ROUTER);
645 return ret | DITEM_RESTORE;
649 configPackages(dialogMenuItem *self)
651 static PkgNode top, plist;
652 static Boolean index_initted = FALSE;
657 return DITEM_FAILURE;
659 if (!mediaDevice->init(mediaDevice))
660 return DITEM_FAILURE;
662 if (!index_initted) {
663 msgNotify("Attempting to fetch packages/INDEX file from selected media.");
664 fp = mediaDevice->get(mediaDevice, "packages/INDEX", TRUE);
666 dialog_clear_norefresh();
667 msgConfirm("Unable to get packages/INDEX file from selected media.\n"
668 "This may be because the packages collection is not available at\n"
669 "on the distribution media you've chosen (most likely an FTP site\n"
670 "without the packages collection mirrored). Please verify media\n"
671 "(or path to media) and try again. If your local site does not\n"
672 "carry the packages collection, then we recommend either a CD\n"
673 "distribution or the master distribution on ftp.freebsd.org.");
674 mediaDevice->shutdown(mediaDevice);
675 return DITEM_FAILURE | DITEM_RESTORE;
677 msgNotify("Located INDEX, now reading package data from it...");
678 index_init(&top, &plist);
679 if (index_read(fp, &top)) {
680 msgConfirm("I/O or format error on packages/INDEX file.\n"
681 "Please verify media (or path to media) and try again.");
683 return DITEM_FAILURE | DITEM_RESTORE;
687 index_initted = TRUE;
690 int ret, pos, scroll;
692 /* Bring up the packages menu */
694 index_menu(&top, &plist, &pos, &scroll);
696 if (plist.kids && plist.kids->name) {
697 /* Now show the packing list menu */
699 ret = index_menu(&plist, NULL, &pos, &scroll);
700 if (ret & DITEM_LEAVE_MENU)
702 else if (DITEM_STATUS(ret) != DITEM_FAILURE) {
703 index_extract(mediaDevice, &top, &plist);
708 dialog_clear_norefresh();
709 msgConfirm("No packages were selected for extraction.");
715 PkgNodePtr tmp2 = tmp->next;
720 index_init(NULL, &plist);
721 return DITEM_SUCCESS | DITEM_RESTORE;
724 #ifdef NETCON_EXTENTIONS
725 /* Load novell client/server package */
727 configNovell(dialogMenuItem *self)
729 int ret = DITEM_SUCCESS;
731 if (!RunningAsInit) {
732 msgConfirm("This package can only be installed in multi-user mode.");
735 if (variable_get(VAR_NOVELL))
736 variable_unset(VAR_NOVELL);
738 ret = package_add(PACKAGE_NETCON);
739 if (DITEM_STATUS(ret) == DITEM_SUCCESS)
740 variable_set2(VAR_NOVELL, "YES");
742 return ret | DITEM_RESTORE;
746 /* Load pcnfsd package */
748 configPCNFSD(dialogMenuItem *self)
750 int ret = DITEM_SUCCESS;
752 if (variable_get(VAR_PCNFSD))
753 variable_unset(VAR_PCNFSD);
755 ret = package_add(variable_get(VAR_PCNFSD_PKG));
756 if (DITEM_STATUS(ret) == DITEM_SUCCESS) {
757 variable_set2(VAR_PCNFSD, "YES");
758 variable_set2("mountd_flags", "-n");
765 configNFSServer(dialogMenuItem *self)
769 /* If we're an NFS server, we need an exports file */
770 if (!file_readable("/etc/exports")) {
771 WINDOW *w = savescr();
773 if (file_readable("/etc/exports.disabled"))
774 vsystem("mv /etc/exports.disabled /etc/exports");
776 dialog_clear_norefresh();
777 msgConfirm("Operating as an NFS server means that you must first configure\n"
778 "an /etc/exports file to indicate which hosts are allowed certain\n"
779 "kinds of access to your local file systems.\n"
780 "Press [ENTER] now to invoke an editor on /etc/exports\n");
781 vsystem("echo '#The following examples export /usr to 3 machines named after ducks,' > /etc/exports");
782 vsystem("echo '#/home and all directories under it to machines named after dead rock stars' >> /etc/exports");
783 vsystem("echo '#and, finally, /a to 2 privileged machines allowed to write on it as root.' >> /etc/exports");
784 vsystem("echo '#/usr huey louie dewie' >> /etc/exports");
785 vsystem("echo '#/home -alldirs janice jimmy frank' >> /etc/exports");
786 vsystem("echo '#/a -maproot=0 bill albert' >> /etc/exports");
787 vsystem("echo '#' >> /etc/exports");
788 vsystem("echo '# You should replace these lines with your actual exported filesystems.' >> /etc/exports");
789 vsystem("echo >> /etc/exports");
790 sprintf(cmd, "%s /etc/exports", variable_get(VAR_EDITOR));
795 variable_set2(VAR_NFS_SERVER, "YES");
797 else if (variable_get(VAR_NFS_SERVER)) { /* We want to turn it off again? */
798 vsystem("mv -f /etc/exports /etc/exports.disabled");
799 variable_unset(VAR_NFS_SERVER);
801 return DITEM_SUCCESS;