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 <sys/disklabel.h>
39#include <sys/wait.h>
40#include <sys/errno.h>
41#include <sys/ioctl.h>
42#include <sys/fcntl.h>
43#include <sys/param.h>
44#include <sys/stat.h>
45#include <unistd.h>
46#include <sys/mount.h>
47#include <libdisk.h>
48#include <time.h>
49#include <kenv.h>
50
51static Chunk *chunk_list[MAX_CHUNKS];
52static int nchunks;
53static int rootdev_is_od;
54
55/* arg to sort */
56static int
57chunk_compare(Chunk *c1, Chunk *c2)
58{
59    if (!c1 && !c2)
60	return 0;
61    else if (!c1 && c2)
62	return 1;
63    else if (c1 && !c2)
64	return -1;
65    else if (!c1->private_data && !c2->private_data)
66	return 0;
67    else if (c1->private_data && !c2->private_data)
68	return 1;
69    else if (!c1->private_data && c2->private_data)
70	return -1;
71    else
72	return strcmp(((PartInfo *)(c1->private_data))->mountpoint, ((PartInfo *)(c2->private_data))->mountpoint);
73}
74
75static void
76chunk_sort(void)
77{
78    int i, j;
79
80    for (i = 0; i < nchunks; i++) {
81	for (j = 0; j < nchunks; j++) {
82	    if (chunk_compare(chunk_list[j], chunk_list[j + 1]) > 0) {
83		Chunk *tmp = chunk_list[j];
84
85		chunk_list[j] = chunk_list[j + 1];
86		chunk_list[j + 1] = tmp;
87	    }
88	}
89    }
90}
91
92static void
93check_rootdev(Chunk **list, int n)
94{
95	int i;
96	Chunk *c;
97
98	rootdev_is_od = 0;
99	for (i = 0; i < n; i++) {
100		c = *list++;
101		if (c->type == part && (c->flags & CHUNK_IS_ROOT)
102		    && strncmp(c->disk->name, "od", 2) == 0)
103			rootdev_is_od = 1;
104	}
105}
106
107static char *
108name_of(Chunk *c1)
109{
110    return c1->name;
111}
112
113static char *
114mount_point(Chunk *c1)
115{
116    if (c1->type == part && c1->subtype == FS_SWAP)
117	return "none";
118    else if (c1->type == part || c1->type == fat || c1->type == efi)
119	return ((PartInfo *)c1->private_data)->mountpoint;
120    return "/bogus";
121}
122
123static char *
124fstype(Chunk *c1)
125{
126    if (c1->type == fat || c1->type == efi)
127	return "msdosfs";
128    else if (c1->type == part) {
129	if (c1->subtype != FS_SWAP)
130	    return "ufs";
131	else
132	    return "swap";
133    }
134    return "bogus";
135}
136
137static char *
138fstype_short(Chunk *c1)
139{
140    if (c1->type == part) {
141	if (c1->subtype != FS_SWAP) {
142	    if (rootdev_is_od == 0 && strncmp(c1->name, "od", 2) == 0)
143		return "rw,noauto";
144	    else
145		return "rw";
146	}
147	else
148	    return "sw";
149    }
150    else if (c1->type == fat) {
151	if (strncmp(c1->name, "od", 2) == 0)
152	    return "ro,noauto";
153	else
154	    return "ro";
155    }
156    else if (c1->type == efi)
157	return "rw";
158
159    return "bog";
160}
161
162static int
163seq_num(Chunk *c1)
164{
165    if (c1->type == part && c1->subtype != FS_SWAP) {
166	if (rootdev_is_od == 0 && strncmp(c1->name, "od", 2) == 0)
167	    return 0;
168	else if (c1->flags & CHUNK_IS_ROOT)
169	    return 1;
170	else
171	    return 2;
172    }
173    return 0;
174}
175
176int
177configFstab(dialogMenuItem *self)
178{
179    Device **devs;
180    Disk *disk;
181    FILE *fstab;
182    int i, cnt;
183    Chunk *c1, *c2;
184
185    if (!RunningAsInit) {
186	if (file_readable("/etc/fstab"))
187	    return DITEM_SUCCESS;
188	else {
189	    msgConfirm("Attempting to rebuild your /etc/fstab file.  Warning: If you had\n"
190		       "any CD devices in use before running %s then they may NOT\n"
191		       "be found by this run!", ProgName);
192	}
193    }
194
195    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
196    if (!devs) {
197	msgConfirm("No disks found!");
198	return DITEM_FAILURE;
199    }
200
201    /* Record all the chunks */
202    nchunks = 0;
203    for (i = 0; devs[i]; i++) {
204	if (!devs[i]->enabled)
205	    continue;
206	disk = (Disk *)devs[i]->private;
207	if (!disk->chunks)
208	    msgFatal("No chunk list found for %s!", disk->name);
209	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
210#ifdef __powerpc__
211	    if (c1->type == apple) {
212#else
213	    if (c1->type == freebsd) {
214#endif
215		for (c2 = c1->part; c2; c2 = c2->next) {
216		    if (c2->type == part && (c2->subtype == FS_SWAP || c2->private_data))
217			chunk_list[nchunks++] = c2;
218		}
219	    }
220	    else if (((c1->type == fat || c1->type == efi || c1->type == part) &&
221		    c1->private_data) || (c1->type == part && c1->subtype == FS_SWAP))
222		chunk_list[nchunks++] = c1;
223	}
224    }
225    chunk_list[nchunks] = 0;
226    chunk_sort();
227
228    fstab = fopen("/etc/fstab", "w");
229    if (!fstab) {
230	msgConfirm("Unable to create a new /etc/fstab file!  Manual intervention\n"
231		   "will be required.");
232	return DITEM_FAILURE;
233    }
234
235    check_rootdev(chunk_list, nchunks);
236
237    /* Go for the burn */
238    msgDebug("Generating /etc/fstab file\n");
239    fprintf(fstab, "# Device\t\tMountpoint\tFStype\tOptions\t\tDump\tPass#\n");
240    for (i = 0; i < nchunks; i++)
241	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]),
242		fstype(chunk_list[i]), fstype_short(chunk_list[i]), seq_num(chunk_list[i]), seq_num(chunk_list[i]));
243
244    /* Now look for the CDROMs */
245    devs = deviceFind(NULL, DEVICE_TYPE_CDROM);
246    cnt = deviceCount(devs);
247
248    /* Write out the CDROM entries */
249    for (i = 0; i < cnt; i++) {
250	char cdname[10];
251
252	sprintf(cdname, "/cdrom%s", i ? itoa(i) : "");
253	if (Mkdir(cdname))
254	    msgConfirm("Unable to make mount point for: %s", cdname);
255	else
256	    fprintf(fstab, "/dev/%s\t\t%s\t\tcd9660\tro,noauto\t0\t0\n", devs[i]->name, cdname);
257    }
258
259    fclose(fstab);
260    if (isDebug())
261	msgDebug("Wrote out /etc/fstab file\n");
262    return DITEM_SUCCESS;
263}
264
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.
270 */
271static int
272readConfig(char *config, char **lines, int max)
273{
274    FILE *fp;
275    char line[256];
276    int i, nlines;
277
278    fp = fopen(config, "r");
279    if (!fp)
280	return -1;
281
282    nlines = 0;
283    /* Read in the entire file */
284    for (i = 0; i < max; i++) {
285	if (!fgets(line, sizeof line, fp))
286	    break;
287	lines[nlines++] = strdup(line);
288    }
289    fclose(fp);
290    if (isDebug())
291	msgDebug("readConfig: Read %d lines from %s.\n", nlines, config);
292    return nlines;
293}
294
295#define MAX_LINES  2000 /* Some big number we're not likely to ever reach - I'm being really lazy here, I know */
296
297static void
298readConfigFile(char *config, int marked)
299{
300    char *lines[MAX_LINES], *cp, *cp2;
301    int i, nlines;
302
303    nlines = readConfig(config, lines, MAX_LINES);
304    if (nlines == -1)
305	return;
306
307    for (i = 0; i < nlines; i++) {
308	/* Skip the comments & non-variable settings */
309	if (lines[i][0] == '#' || !(cp = index(lines[i], '='))) {
310	    free(lines[i]);
311	    continue;
312	}
313	*cp++ = '\0';
314	/* Find quotes */
315	if ((cp2 = index(cp, '"')) || (cp2 = index(cp, '\047'))) {
316	    cp = cp2 + 1;
317	    cp2 = index(cp, *cp2);
318	}
319	/* If valid quotes, use it */
320	if (cp2) {
321	    *cp2 = '\0';
322 	    /* If we have a legit value, set it */
323	    if (strlen(cp))
324		variable_set2(lines[i], cp, marked);
325	}
326	free(lines[i]);
327    }
328}
329
330/* Load the environment from rc.conf file(s) */
331void
332configEnvironmentRC_conf(void)
333{
334    static struct {
335	char *fname;
336	int marked;
337    } configs[] = {
338	{ "/etc/defaults/rc.conf", 0 },
339	{ "/etc/rc.conf", 0 },
340	{ "/etc/rc.conf.local", 0 },
341	{ NULL, 0 },
342    };
343    int i;
344
345    for (i = 0; configs[i].fname; i++) {
346	if (file_readable(configs[i].fname))
347	    readConfigFile(configs[i].fname, configs[i].marked);
348    }
349}
350
351/* Load the environment from a resolv.conf file */
352void
353configEnvironmentResolv(char *config)
354{
355    char *lines[MAX_LINES];
356    int i, nlines;
357
358    nlines = readConfig(config, lines, MAX_LINES);
359    if (nlines == -1)
360	return;
361    for (i = 0; i < nlines; i++) {
362	Boolean name_set = variable_get(VAR_NAMESERVER) ? 1 : 0;
363
364	if (!strncmp(lines[i], "domain", 6) && !variable_get(VAR_DOMAINNAME))
365	    variable_set2(VAR_DOMAINNAME, string_skipwhite(string_prune(lines[i] + 6)), 0);
366	else if (!name_set && !strncmp(lines[i], "nameserver", 10)) {
367	    /* Only take the first nameserver setting - we're lame */
368	    variable_set2(VAR_NAMESERVER, string_skipwhite(string_prune(lines[i] + 10)), 0);
369	}
370	free(lines[i]);
371    }
372}
373
374/* Version of below for dispatch routines */
375int
376configRC(dialogMenuItem *unused)
377{
378    configRC_conf();
379    return DITEM_SUCCESS;
380}
381
382/*
383 * Write out rc.conf
384 *
385 * rc.conf is sorted if running as init and the needed utilities are
386 * present
387 *
388 * If rc.conf is sorted, all variables in rc.conf which conflict with
389 * the variables in the environment are removed from the original
390 * rc.conf
391 */
392void
393configRC_conf(void)
394{
395    char line[256];
396    FILE *rcSite, *rcOld;
397    Variable *v;
398    int write_header;
399    time_t t_loc;
400    char *cp;
401    static int did_marker = 0;
402    int do_sort;
403    int do_merge;
404    time_t tp;
405
406    configTtys();
407    write_header = !file_readable("/etc/rc.conf");
408    do_sort = RunningAsInit && file_readable("/usr/bin/sort") &&
409	file_readable("/usr/bin/uniq");
410    do_merge = do_sort && file_readable("/etc/rc.conf");
411
412    if(do_merge) {
413	rcSite = fopen("/etc/rc.conf.new", "w");
414    } else
415	rcSite = fopen("/etc/rc.conf", "a");
416    if (rcSite == NULL) {
417	msgError("Error opening new rc.conf for writing: %s (%u)", strerror(errno), errno);
418	return;
419    }
420
421    if (do_merge) {
422	/* "Copy" the old rc.conf */
423	rcOld = fopen("/etc/rc.conf", "r");
424	if(!rcOld) {
425	    msgError("Error opening rc.conf for reading: %s (%u)", strerror(errno), errno);
426	    return;
427	}
428	while(fgets(line, sizeof(line), rcOld)) {
429	    if(line[0] == '#' || variable_check2(line) != 0)
430		fprintf(rcSite, "%s", line);
431	    else {
432		if (variable_get(VAR_KEEPRCCONF) != NULL)
433		    fprintf(rcSite, "%s", line);
434		else
435		    fprintf(rcSite, "#REMOVED: %s", line);
436	    }
437	}
438	fclose(rcOld);
439    } else if (write_header) {
440	fprintf(rcSite, "# This file now contains just the overrides from /etc/defaults/rc.conf.\n");
441	fprintf(rcSite, "# Please make all changes to this file, not to /etc/defaults/rc.conf.\n\n");
442	fprintf(rcSite, "# Enable network daemons for user convenience.\n");
443	if ((t_loc = time(NULL)) != -1 && (cp = ctime(&t_loc)))
444	    fprintf(rcSite, "# Created: %s", cp);
445    }
446
447    /* Now do variable substitutions */
448    for (v = VarHead; v; v = v->next) {
449	if (v->dirty) {
450	    if (!did_marker) {
451		time(&tp);
452		fprintf(rcSite, "# -- sysinstall generated deltas -- # "
453		    "%s", ctime(&tp));
454		did_marker = 1;
455	    }
456	    fprintf(rcSite, "%s=\"%s\"\n", v->name, v->value);
457	    v->dirty = 0;
458	}
459    }
460    fclose(rcSite);
461
462    if(do_merge) {
463	if(rename("/etc/rc.conf.new", "/etc/rc.conf") != 0) {
464	    msgError("Error renaming temporary rc.conf: %s (%u)", strerror(errno), errno);
465	    return;
466	}
467    }
468
469    /* Tidy up the resulting file if it's late enough in the installation
470	for sort and uniq to be available */
471    if (do_sort) {
472	(void)vsystem("sort /etc/rc.conf | uniq > /etc/rc.conf.new && mv /etc/rc.conf.new /etc/rc.conf");
473    }
474}
475
476int
477configSaver(dialogMenuItem *self)
478{
479    variable_set((char *)self->data, 1);
480    if (!variable_get(VAR_BLANKTIME))
481	variable_set2(VAR_BLANKTIME, "300", 1);
482    return DITEM_SUCCESS;
483}
484
485int
486configSaverTimeout(dialogMenuItem *self)
487{
488    return (variable_get_value(VAR_BLANKTIME,
489	    "Enter time-out period in seconds for screen saver", 1) ?
490	DITEM_SUCCESS : DITEM_FAILURE);
491}
492
493int
494configNTP(dialogMenuItem *self)
495{
496    int status;
497
498    status = variable_get_value(VAR_NTPDATE_HOSTS,
499				"Enter the name of an NTP server", 1)
500	     ? DITEM_SUCCESS : DITEM_FAILURE;
501    if (status == DITEM_SUCCESS) {
502	static char tmp[255];
503
504	snprintf(tmp, sizeof(tmp), "ntpdate_enable=YES,ntpdate_hosts=%s",
505		 variable_get(VAR_NTPDATE_HOSTS));
506	self->data = tmp;
507	dmenuSetVariables(self);
508    }
509    return status;
510}
511
512int
513configCountry(dialogMenuItem *self)
514{
515    int choice, scroll, curr, max;
516
517    WINDOW *w = savescr();
518
519    dialog_clear_norefresh();
520    dmenuSetDefaultItem(&MenuCountry, NULL, NULL,
521	VAR_COUNTRY "=" DEFAULT_COUNTRY, &choice, &scroll, &curr, &max);
522    dmenuOpen(&MenuCountry, &choice, &scroll, &curr, &max, FALSE);
523    restorescr(w);
524    return DITEM_SUCCESS;
525}
526
527int
528configUsers(dialogMenuItem *self)
529{
530    WINDOW *w = savescr();
531
532    dialog_clear_norefresh();
533    dmenuOpenSimple(&MenuUsermgmt, FALSE);
534    restorescr(w);
535    return DITEM_SUCCESS;
536}
537
538int
539configSecurelevel(dialogMenuItem *self)
540{
541    WINDOW *w = savescr();
542
543    dialog_clear_norefresh();
544    dmenuOpenSimple(&MenuSecurelevel, FALSE);
545    restorescr(w);
546    return DITEM_SUCCESS;
547}
548
549int
550configSecurelevelDisabled(dialogMenuItem *self)
551{
552
553    variable_set2("kern_securelevel_enable", "NO", 1);
554    return DITEM_SUCCESS;
555}
556
557int
558configSecurelevelSecure(dialogMenuItem *self)
559{
560
561    variable_set2("kern_securelevel_enable", "YES", 1);
562    variable_set2("kern_securelevel", "1", 1);
563    return DITEM_SUCCESS;
564}
565
566int
567configSecurelevelHighlySecure(dialogMenuItem *self)
568{
569
570    variable_set2("kern_securelevel_enable", "YES", 1);
571    variable_set2("kern_securelevel", "2", 1);
572    return DITEM_SUCCESS;
573}
574
575int
576configSecurelevelNetworkSecure(dialogMenuItem *self)
577{
578
579    variable_set2("kern_securelevel_enable", "YES", 1);
580    variable_set2("kern_securelevel", "3", 1);
581    return DITEM_SUCCESS;
582}
583
584int
585configResolv(dialogMenuItem *ditem)
586{
587    FILE *fp;
588    char *cp, *c6p, *dp, *hp;
589
590    cp = variable_get(VAR_NAMESERVER);
591    if (!cp || !*cp)
592	goto skip;
593    Mkdir("/etc");
594    fp = fopen("/etc/resolv.conf", "w");
595    if (!fp)
596	return DITEM_FAILURE;
597    if (variable_get(VAR_DOMAINNAME))
598	fprintf(fp, "domain\t%s\n", variable_get(VAR_DOMAINNAME));
599    fprintf(fp, "nameserver\t%s\n", cp);
600    fclose(fp);
601    if (isDebug())
602	msgDebug("Wrote out /etc/resolv.conf\n");
603
604skip:
605    dp = variable_get(VAR_DOMAINNAME);
606    cp = variable_get(VAR_IPADDR);
607    c6p = variable_get(VAR_IPV6ADDR);
608    hp = variable_get(VAR_HOSTNAME);
609    /* Tack ourselves into /etc/hosts */
610    fp = fopen("/etc/hosts", "w");
611    if (!fp)
612	return DITEM_FAILURE;
613    /* Add an entry for localhost */
614    if (dp) {
615	fprintf(fp, "::1\t\t\tlocalhost localhost.%s\n", dp);
616	fprintf(fp, "127.0.0.1\t\tlocalhost localhost.%s\n", dp);
617    } else {
618	fprintf(fp, "::1\t\t\tlocalhost\n");
619	fprintf(fp, "127.0.0.1\t\tlocalhost\n");
620    }
621    /* Now the host entries, if applicable */
622    if (((cp && cp[0] != '0') || (c6p && c6p[0] != '0')) && hp) {
623	char cp2[255];
624
625	if (!index(hp, '.'))
626	    cp2[0] = '\0';
627	else {
628	    SAFE_STRCPY(cp2, hp);
629	    *(index(cp2, '.')) = '\0';
630	}
631	if (c6p && c6p[0] != '0') {
632	    fprintf(fp, "%s\t%s %s\n", c6p, hp, cp2);
633	    fprintf(fp, "%s\t%s.\n", c6p, hp);
634	}
635	if (cp && cp[0] != '0') {
636	    fprintf(fp, "%s\t\t%s %s\n", cp, hp, cp2);
637	    fprintf(fp, "%s\t\t%s.\n", cp, hp);
638	}
639    }
640    fclose(fp);
641    if (isDebug())
642	msgDebug("Wrote out /etc/hosts\n");
643    return DITEM_SUCCESS;
644}
645
646int
647configRouter(dialogMenuItem *self)
648{
649    int ret;
650
651    ret = variable_get_value(VAR_ROUTER,
652			     "Please specify the router you wish to use.  Routed is\n"
653			     "provided with the stock system and gated is provided\n"
654			     "as an optional package which this installation system\n"
655			     "will attempt to load if you select gated.  Any other\n"
656			     "choice of routing daemon will be assumed to be something\n"
657			     "the user intends to install themselves before rebooting\n"
658			     "the system.  If you don't want any routing daemon, choose NO", 1)
659      ? DITEM_SUCCESS : DITEM_FAILURE;
660
661    if (ret == DITEM_SUCCESS) {
662	char *cp = variable_get(VAR_ROUTER);
663
664	if (cp && strcmp(cp, "NO")) {
665	    variable_set2(VAR_ROUTER_ENABLE, "YES", 1);
666	    if (!strcmp(cp, "gated")) {
667		if (package_add("gated") != DITEM_SUCCESS) {
668		    msgConfirm("Unable to load gated package.  Falling back to no router.");
669		    variable_unset(VAR_ROUTER);
670		    variable_unset(VAR_ROUTERFLAGS);
671		    variable_set2(VAR_ROUTER_ENABLE, "NO", 1);
672		    cp = NULL;
673		}
674	    }
675	    if (cp) {
676		/* Now get the flags, if they chose a router */
677		ret = variable_get_value(VAR_ROUTERFLAGS,
678					 "Please Specify the routing daemon flags; if you're running routed\n"
679					 "then -q is the right choice for nodes and -s for gateway hosts.\n", 1)
680		  ? DITEM_SUCCESS : DITEM_FAILURE;
681		if (ret != DITEM_SUCCESS)
682		    variable_unset(VAR_ROUTERFLAGS);
683	    }
684	}
685	else {
686	    /* No router case */
687	    variable_set2(VAR_ROUTER_ENABLE, "NO", 1);
688	    variable_unset(VAR_ROUTERFLAGS);
689	    variable_unset(VAR_ROUTER);
690	}
691    }
692    else {
693	variable_set2(VAR_ROUTER_ENABLE, "NO", 1);
694	variable_unset(VAR_ROUTERFLAGS);
695	variable_unset(VAR_ROUTER);
696    }
697    return ret;
698}
699
700/* Shared between us and index_initialize() */
701extern PkgNode Top, Plist;
702
703int
704configPackages(dialogMenuItem *self)
705{
706    int i, restoreflag = 0;
707    PkgNodePtr tmp;
708
709    /* Did we get an INDEX? */
710    i = index_initialize("packages/INDEX");
711    if (DITEM_STATUS(i) == DITEM_FAILURE)
712	return i;
713
714    while (1) {
715	int ret, pos, scroll;
716	int current, low, high;
717
718	/* Bring up the packages menu */
719	pos = scroll = 0;
720	index_menu(&Top, &Top, &Plist, &pos, &scroll);
721
722	if (Plist.kids && Plist.kids->name) {
723	    /* Now show the packing list menu */
724	    pos = scroll = 0;
725	    ret = index_menu(&Plist, &Plist, NULL, &pos, &scroll);
726	    if (ret & DITEM_LEAVE_MENU)
727		break;
728	    else if (DITEM_STATUS(ret) != DITEM_FAILURE) {
729		dialog_clear();
730		restoreflag = 1;
731		if (have_volumes) {
732		    low = low_volume;
733		    high = high_volume;
734		} else
735		    low = high = 0;
736		for (current = low; current <= high; current++)
737		    for (tmp = Plist.kids; tmp && tmp->name; tmp = tmp->next)
738		        (void)index_extract(mediaDevice, &Top, tmp, FALSE, current);
739		break;
740	    }
741	}
742	else {
743	    msgConfirm("No packages were selected for extraction.");
744	    break;
745	}
746    }
747    tmp = Plist.kids;
748    while (tmp) {
749        PkgNodePtr tmp2 = tmp->next;
750
751        safe_free(tmp);
752        tmp = tmp2;
753    }
754    index_init(NULL, &Plist);
755    return DITEM_SUCCESS | (restoreflag ? DITEM_RESTORE : 0);
756}
757
758/* Load pcnfsd package */
759int
760configPCNFSD(dialogMenuItem *self)
761{
762    int ret;
763
764    ret = package_add("pcnfsd");
765    if (DITEM_STATUS(ret) == DITEM_SUCCESS) {
766	variable_set2(VAR_PCNFSD, "YES", 0);
767	variable_set2("mountd_flags", "-n", 1);
768    }
769    return ret;
770}
771
772int
773configInetd(dialogMenuItem *self)
774{
775    char cmd[256];
776
777    WINDOW *w = savescr();
778
779    if (msgYesNo("The Internet Super Server (inetd) allows a number of simple Internet\n"
780                 "services to be enabled, including finger, ftp, and telnetd.  Enabling\n"
781                 "these services may increase risk of security problems by increasing\n"
782                 "the exposure of your system.\n\n"
783                 "With this in mind, do you wish to enable inetd?\n")) {
784        variable_set2("inetd_enable", "NO", 1);
785    } else {
786        /* If inetd is enabled, we'll need an inetd.conf */
787        variable_set2("inetd_enable", "YES", 1);
788	if (!msgYesNo("inetd(8) relies on its configuration file, /etc/inetd.conf, to determine\n"
789                   "which of its Internet services will be available.  The default FreeBSD\n"
790                   "inetd.conf(5) leaves all services disabled by default, so they must be\n"
791                   "specifically enabled in the configuration file before they will\n"
792                   "function, even once inetd(8) is enabled.  Note that services for\n"
793		   "IPv6 must be separately enabled from IPv4 services.\n\n"
794                   "Select [Yes] now to invoke an editor on /etc/inetd.conf, or [No] to\n"
795                   "use the current settings.\n")) {
796            sprintf(cmd, "%s /etc/inetd.conf", variable_get(VAR_EDITOR));
797            dialog_clear();
798            systemExecute(cmd);
799	}
800    }
801    restorescr(w);
802    return DITEM_SUCCESS;
803}
804
805int
806configNFSServer(dialogMenuItem *self)
807{
808    char cmd[256];
809    int retval = 0;
810
811    /* If we're an NFS server, we need an exports file */
812    if (!file_readable("/etc/exports")) {
813	WINDOW *w = savescr();
814
815	if (file_readable("/etc/exports.disabled"))
816	    vsystem("mv /etc/exports.disabled /etc/exports");
817	else {
818	    dialog_clear_norefresh();
819	    msgConfirm("Operating as an NFS server means that you must first configure\n"
820		       "an /etc/exports file to indicate which hosts are allowed certain\n"
821		       "kinds of access to your local file systems.\n"
822		       "Press [ENTER] now to invoke an editor on /etc/exports\n");
823	    vsystem("echo '#The following examples export /usr to 3 machines named after ducks,' > /etc/exports");
824	    vsystem("echo '#/usr/src and /usr/obj read-only to machines named after trouble makers,' >> /etc/exports");
825	    vsystem("echo '#/home and all directories under it to machines named after dead rock stars' >> /etc/exports");
826	    vsystem("echo '#and, /a to a network of privileged machines allowed to write on it as root.' >> /etc/exports");
827	    vsystem("echo '#/usr                   huey louie dewie' >> /etc/exports");
828	    vsystem("echo '#/usr/src /usr/obj -ro  calvin hobbes' >> /etc/exports");
829	    vsystem("echo '#/home   -alldirs       janis jimi frank' >> /etc/exports");
830	    vsystem("echo '#/a      -maproot=0  -network 10.0.1.0 -mask 255.255.248.0' >> /etc/exports");
831	    vsystem("echo '#' >> /etc/exports");
832	    vsystem("echo '# You should replace these lines with your actual exported filesystems.' >> /etc/exports");
833	    vsystem("echo \"# Note that BSD's export syntax is 'host-centric' vs. Sun's 'FS-centric' one.\" >> /etc/exports");
834	    vsystem("echo >> /etc/exports");
835	    sprintf(cmd, "%s /etc/exports", variable_get(VAR_EDITOR));
836	    dialog_clear();
837	    systemExecute(cmd);
838	}
839	variable_set2(VAR_NFS_SERVER, "YES", 1);
840	retval = configRpcBind(NULL);
841	restorescr(w);
842    }
843    else if (variable_get(VAR_NFS_SERVER)) { /* We want to turn it off again? */
844	vsystem("mv -f /etc/exports /etc/exports.disabled");
845	variable_unset(VAR_NFS_SERVER);
846    }
847    return DITEM_SUCCESS | retval;
848}
849
850/*
851 * Extend the standard dmenuToggleVariable() method to also check and set
852 * the rpcbind variable if needed.
853 */
854int
855configRpcBind(dialogMenuItem *self)
856{
857    char *tmp, *tmp2;
858    int retval = 0;
859    int doupdate = 1;
860
861    if (self != NULL) {
862    	retval = dmenuToggleVariable(self);
863	tmp = strdup(self->data);
864	if ((tmp2 = index(tmp, '=')) != NULL)
865	    *tmp2 = '\0';
866	if (strcmp(variable_get(tmp), "YES") != 0)
867	    doupdate = 0;
868	free(tmp);
869    }
870
871    if (doupdate && strcmp(variable_get(VAR_RPCBIND_ENABLE), "YES") != 0) {
872	variable_set2(VAR_RPCBIND_ENABLE, "YES", 1);
873	retval |= DITEM_REDRAW;
874    }
875
876   return retval;
877}
878
879int
880configEtcTtys(dialogMenuItem *self)
881{
882    char cmd[256];
883
884    WINDOW *w = savescr();
885
886    /* Simply prompt for confirmation, then edit away. */
887    if (msgYesNo("Configuration of system TTYs requires editing the /etc/ttys file.\n"
888		 "Typical configuration activities might include enabling getty(8)\n"
889		 "on the first serial port to allow login via serial console after\n"
890		 "reboot, or to enable xdm.  The default ttys file enables normal\n"
891		 "virtual consoles, and most sites will not need to perform manual\n"
892		 "configuration.\n\n"
893		 "To load /etc/ttys in the editor, select [Yes], otherwise, [No].")) {
894    } else {
895	configTtys();
896	sprintf(cmd, "%s /etc/ttys", variable_get(VAR_EDITOR));
897	dialog_clear();
898	systemExecute(cmd);
899    }
900
901    restorescr(w);
902    return DITEM_SUCCESS;
903}
904
905#ifdef __i386__
906int
907checkLoaderACPI(void)
908{
909    char val[4];
910
911    if (kenv(KENV_GET, "loader.acpi_disabled_by_user", &val[0], 4) <= 0) {
912	return (0);
913    }
914
915    if (strtol(&val[0], NULL, 10) <= 0) {
916	return (0);
917    }
918
919    return (1);
920}
921
922int
923configLoaderACPI(int disable)
924{
925    FILE *ldconf;
926
927    ldconf = fopen("/boot/loader.conf", "a");
928    if (ldconf == NULL) {
929	msgConfirm("Unable to open /boot/loader.conf.  Please consult the\n"
930		  "FreeBSD Handbook for instructions on disabling ACPI");
931	return DITEM_FAILURE;
932    }
933
934    fprintf(ldconf, "# --- Generated by sysinstall ---\n");
935    fprintf(ldconf, "hint.acpi.0.disabled=%d\n", disable);
936    fclose(ldconf);
937
938    return DITEM_SUCCESS;
939}
940#endif
941
942int
943configMTAPostfix(dialogMenuItem *self)
944{
945    int ret;
946    FILE *perconf;
947
948    if(setenv("POSTFIX_DEFAULT_MTA", "YES", 1) != 0)
949	msgError("Error setting the enviroment variable POSTFIX_DEFAULT_MTA: %s (%u)",
950		 strerror(errno), errno);
951
952    ret = package_add("postfix-2.4");
953    unsetenv("POSTFIX_DEFAULT_MTA");
954
955    if(DITEM_STATUS(ret) == DITEM_FAILURE) {
956	msgConfirm("An error occurred while adding the postfix package\n"
957		   "Please change installation media and try again.");
958	return ret;
959    }
960
961    variable_set2(VAR_SENDMAIL_ENABLE, "YES", 1);
962    variable_set2("sendmail_flags", "-bd", 1);
963    variable_set2("sendmail_outbound_enable", "NO", 1);
964    variable_set2("sendmail_submit_enable", "NO", 1);
965    variable_set2("sendmail_msp_queue_enable", "NO", 1);
966
967    perconf = fopen("/etc/periodic.conf", "a");
968    if (perconf == NULL) {
969	msgConfirm("Unable to open /etc/periodic.conf.\n"
970		   "The daily cleanup scripts might generate errors when\n"
971		   "trying to run some sendmail only cleanup scripts.\n"
972		   "Please consult the documentation for the postfix port on how to\n"
973		   "fix this.");
974
975	/* Not really a serious problem, so we return success */
976	return DITEM_SUCCESS;
977    }
978
979    fprintf(perconf, "# --- Generated by sysinstall ---\n");
980    fprintf(perconf, "daily_clean_hoststat_enable=\"NO\"\n");
981    fprintf(perconf, "daily_status_mail_rejects_enable=\"NO\"\n");
982    fprintf(perconf, "daily_status_include_submit_mailq=\"NO\"\n");
983    fprintf(perconf, "daily_submit_queuerun=\"NO\"\n");
984    fclose(perconf);
985
986    msgConfirm("Postfix is now installed and enabled as the default MTA.\n"
987	       "Please check that the configuration works as expected.\n"
988	       "See the Postfix documentation for more information.\n"
989	       "The documentation can be found in /usr/local/share/doc/postfix/\n"
990	       "or on the Postfix website at http://www.postfix.org/.");
991
992    return DITEM_SUCCESS;
993}
994
995int
996configMTAExim(dialogMenuItem *self)
997{
998    int ret;
999    FILE *perconf, *mailerconf, *newsyslogconf;
1000
1001    ret = package_add("exim");
1002
1003    if(DITEM_STATUS(ret) == DITEM_FAILURE) {
1004	msgConfirm("An error occurred while adding the exim package\n"
1005		   "Please change installation media and try again.");
1006	return ret;
1007    }
1008
1009    variable_set2(VAR_SENDMAIL_ENABLE, "NONE", 1);
1010    variable_set2("exim_enable", "YES", 1);
1011
1012    /* Update periodic.conf */
1013    perconf = fopen("/etc/periodic.conf", "a");
1014    if (perconf == NULL) {
1015	/* Not really a serious problem, so we do not abort */
1016	msgConfirm("Unable to open /etc/periodic.conf.\n"
1017		   "The daily cleanup scripts might generate errors when\n"
1018		   "trying to run some sendmail only cleanup scripts.\n"
1019		   "Please consult the documentation for the exim port on how to\n"
1020		   "fix this.");
1021    } else {
1022	fprintf(perconf, "# --- Generated by sysinstall ---\n");
1023	fprintf(perconf, "daily_clean_hoststat_enable=\"NO\"\n");
1024	fprintf(perconf, "daily_status_include_submit_mailq=\"NO\"\n");
1025	fprintf(perconf, "daily_status_mail_rejects_enable=\"NO\"\n");
1026	fprintf(perconf, "daily_submit_queuerun=\"NO\"\n");
1027	fclose(perconf);
1028    }
1029
1030    /* Update mailer.conf */
1031    vsystem("mv -f /etc/mail/mailer.conf /etc/mail/mailer.conf.old");
1032    mailerconf = fopen("/etc/mail/mailer.conf", "w");
1033    if (mailerconf == NULL) {
1034	/* Not really a serious problem, so we do not abort */
1035	msgConfirm("Unable to open /etc/mailer.conf.\n"
1036		   "Some programs which use the sendmail wrappers may not work.\n"
1037		   "Please consult the documentation for the exim port on how\n"
1038		   "to correct this.");
1039    } else {
1040	fprintf(mailerconf, "# --- Generated by sysinstall ---\n");
1041	fprintf(mailerconf, "# Execute exim instead of sendmail\n");
1042	fprintf(mailerconf, "#\n");
1043	fprintf(mailerconf, "sendmail	/usr/local/sbin/exim\n");
1044	fprintf(mailerconf, "send-mail	/usr/local/sbin/exim\n");
1045	fprintf(mailerconf, "mailq		/usr/local/sbin/exim\n");
1046	fprintf(mailerconf, "newaliases	/usr/local/sbin/exim\n");
1047	fprintf(mailerconf, "hoststat	/usr/bin/true\n");
1048	fprintf(mailerconf, "purgestat	/usr/bin/true\n");
1049	fclose(mailerconf);
1050    }
1051
1052    /* Make newsyslog rotate exim logfiles */
1053    newsyslogconf = fopen("/etc/newsyslog.conf", "a");
1054    if (newsyslogconf == NULL) {
1055	/* Not really a serious problem, so we do not abort */
1056	msgConfirm("Unable to open /etc/newsyslog.conf.\n"
1057		   "The exim logfiles will not be rotated.\n"
1058		   "Please consult the documentation for the exim port on how to\n"
1059		   "rotate the logfiles.");
1060    } else {
1061	fprintf(newsyslogconf, "# --- Generated by sysinstall ---\n");
1062	fprintf(newsyslogconf, "/var/log/exim/mainlog	mailnull:mail	640  7	   *	@T00  ZN\n");
1063	fprintf(newsyslogconf, "/var/log/exim/rejectlog	mailnull:mail	640  7	   *	@T00  ZN\n");
1064	fclose(newsyslogconf);
1065    }
1066
1067    msgConfirm("Exim is now installed and enabled as the default MTA.\n"
1068	       "Please check that the configuration works as expected.\n"
1069	       "See the Exim documentation for more information.\n"
1070	       "The documentation can be found in /usr/local/share/doc/exim/\n"
1071	       "or on the Exim website at http://www.exim.org/.");
1072
1073    return DITEM_SUCCESS;
1074}
1075