install.c revision 115435
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: head/usr.sbin/sade/install.c 115435 2003-05-31 11:28:28Z scottl $
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 <ctype.h>
39#include <sys/disklabel.h>
40#include <sys/errno.h>
41#include <sys/ioctl.h>
42#include <sys/fcntl.h>
43#include <sys/wait.h>
44#include <sys/uio.h>
45#include <sys/param.h>
46#define MSDOSFS
47#include <sys/mount.h>
48#include <ufs/ufs/ufsmount.h>
49#include <fs/msdosfs/msdosfsmount.h>
50#undef MSDOSFS
51#include <sys/stat.h>
52#include <sys/sysctl.h>
53#include <limits.h>
54#include <unistd.h>
55#include <termios.h>
56
57/* Hack for rsaref package add, which displays interactive license.
58 * Used by package.c
59 */
60int _interactiveHack;
61int FixItMode = 0;
62
63static void	create_termcap(void);
64static void	fixit_common(void);
65
66#define TERMCAP_FILE	"/usr/share/misc/termcap"
67
68static void	installConfigure(void);
69
70Boolean
71checkLabels(Boolean whinge, Chunk **rdev, Chunk **sdev, Chunk **udev, Chunk **vdev, Chunk **tdev, Chunk **hdev)
72{
73    Device **devs;
74    Boolean status;
75    Disk *disk;
76    Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev, *tmpdev, *homedev;
77    int i;
78
79    /* Don't allow whinging if noWarn is set */
80    if (variable_get(VAR_NO_WARN))
81	whinge = FALSE;
82
83    status = TRUE;
84    if (rdev)
85	*rdev = NULL;
86    if (sdev)
87	*sdev = NULL;
88    if (udev)
89	*udev = NULL;
90    if (vdev)
91	*vdev = NULL;
92    if (tdev)
93	*tdev = NULL;
94    if (hdev)
95	*hdev = NULL;
96    rootdev = swapdev = usrdev = vardev = tmpdev = homedev = NULL;
97
98    /* We don't need to worry about root/usr/swap if we're already multiuser */
99    if (!RunningAsInit)
100	return status;
101
102    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
103    /* First verify that we have a root device */
104    for (i = 0; devs[i]; i++) {
105	if (!devs[i]->enabled)
106	    continue;
107	disk = (Disk *)devs[i]->private;
108	msgDebug("Scanning disk %s for root filesystem\n", disk->name);
109	if (!disk->chunks)
110	    msgFatal("No chunk list found for %s!", disk->name);
111	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
112	    if (c1->type == freebsd) {
113		for (c2 = c1->part; c2; c2 = c2->next) {
114		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
115			if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/")) {
116			    if (rootdev) {
117				if (whinge)
118				    msgConfirm("WARNING:  You have more than one root device set?!\n"
119					       "Using the first one found.");
120				continue;
121			    }
122			    else {
123				rootdev = c2;
124				if (isDebug())
125				    msgDebug("Found rootdev at %s!\n", rootdev->name);
126			    }
127			}
128			else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/usr")) {
129			    if (usrdev) {
130				if (whinge)
131				    msgConfirm("WARNING:  You have more than one /usr filesystem.\n"
132					       "Using the first one found.");
133				continue;
134			    }
135			    else {
136				usrdev = c2;
137				if (isDebug())
138				    msgDebug("Found usrdev at %s!\n", usrdev->name);
139			    }
140			}
141			else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/var")) {
142			    if (vardev) {
143				if (whinge)
144				    msgConfirm("WARNING:  You have more than one /var filesystem.\n"
145					       "Using the first one found.");
146				continue;
147			    }
148			    else {
149				vardev = c2;
150				if (isDebug())
151				    msgDebug("Found vardev at %s!\n", vardev->name);
152			    }
153			} else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/tmp")) {
154			    if (tmpdev) {
155				if (whinge)
156				    msgConfirm("WARNING:  You have more than one /tmp filesystem.\n"
157					       "Using the first one found.");
158				continue;
159			    }
160			    else {
161				tmpdev = c2;
162				if (isDebug())
163				    msgDebug("Found tmpdev at %s!\n", tmpdev->name);
164			    }
165			} else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/home")) {
166			    if (homedev) {
167				if (whinge)
168				    msgConfirm("WARNING:  You have more than one /home filesystem.\n"
169					       "Using the first one found.");
170				continue;
171			    }
172			    else {
173				homedev = c2;
174				if (isDebug())
175				    msgDebug("Found homedev at %s!\n", homedev->name);
176			    }
177			}
178		    }
179		}
180	    }
181	}
182    }
183
184    /* Now check for swap devices */
185    for (i = 0; devs[i]; i++) {
186	if (!devs[i]->enabled)
187	    continue;
188	disk = (Disk *)devs[i]->private;
189	msgDebug("Scanning disk %s for swap partitions\n", disk->name);
190	if (!disk->chunks)
191	    msgFatal("No chunk list found for %s!", disk->name);
192	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
193	    if (c1->type == freebsd) {
194		for (c2 = c1->part; c2; c2 = c2->next) {
195		    if (c2->type == part && c2->subtype == FS_SWAP && !swapdev) {
196			swapdev = c2;
197			if (isDebug())
198			    msgDebug("Found swapdev at %s!\n", swapdev->name);
199			break;
200		    }
201		}
202	    }
203	}
204    }
205
206    /* Copy our values over */
207    if (rdev)
208	*rdev = rootdev;
209    if (sdev)
210	*sdev = swapdev;
211    if (udev)
212	*udev = usrdev;
213    if (vdev)
214	*vdev = vardev;
215    if (tdev)
216	*tdev = tmpdev;
217    if (hdev)
218	*hdev = homedev;
219
220    if (!rootdev && whinge) {
221	msgConfirm("No root device found - you must label a partition as /\n"
222		   "in the label editor.");
223	status = FALSE;
224    }
225    if (!swapdev && whinge) {
226	msgConfirm("No swap devices found - you must create at least one\n"
227		   "swap partition.");
228	status = FALSE;
229    }
230    return status;
231}
232
233static int
234installInitial(void)
235{
236    static Boolean alreadyDone = FALSE;
237    int status = DITEM_SUCCESS;
238
239    if (alreadyDone)
240	return DITEM_SUCCESS;
241
242    if (!variable_get(DISK_LABELLED)) {
243	msgConfirm("You need to assign disk labels before you can proceed with\n"
244		   "the installation.");
245	return DITEM_FAILURE;
246    }
247    /* If it's labelled, assume it's also partitioned */
248    if (!variable_get(DISK_PARTITIONED))
249	variable_set2(DISK_PARTITIONED, "yes", 0);
250
251    /* If we refuse to proceed, bail. */
252    dialog_clear_norefresh();
253    if (!variable_get(VAR_NO_WARN)) {
254	if (msgYesNo(
255	    "Last Chance!  Are you SURE you want continue the installation?\n\n"
256	    "If you're running this on a disk with data you wish to save\n"
257	    "then WE STRONGLY ENCOURAGE YOU TO MAKE PROPER BACKUPS before\n"
258	    "proceeding!\n\n"
259	    "We can take no responsibility for lost disk contents!") != 0)
260	return DITEM_FAILURE;
261    }
262
263    if (DITEM_STATUS(diskLabelCommit(NULL)) != DITEM_SUCCESS) {
264	msgConfirm("Couldn't make filesystems properly.  Aborting.");
265	return DITEM_FAILURE;
266    }
267
268    if (!copySelf()) {
269	msgConfirm("installInitial: Couldn't clone the boot floppy onto the\n"
270		   "root file system.  Aborting!");
271	return DITEM_FAILURE;
272    }
273
274    if (!Restarting && chroot("/mnt") == -1) {
275	msgConfirm("installInitial: Unable to chroot to %s - this is bad!",
276		   "/mnt");
277	return DITEM_FAILURE;
278    }
279
280    chdir("/");
281    variable_set2(RUNNING_ON_ROOT, "yes", 0);
282
283    /* Configure various files in /etc */
284    if (DITEM_STATUS(configResolv(NULL)) == DITEM_FAILURE)
285	status = DITEM_FAILURE;
286    if (DITEM_STATUS(configFstab(NULL)) == DITEM_FAILURE)
287	status = DITEM_FAILURE;
288
289    /* stick a helpful shell over on the 4th VTY */
290    if (!variable_get(VAR_NO_HOLOSHELL))
291	systemCreateHoloshell();
292
293    alreadyDone = TRUE;
294    return status;
295}
296
297int
298installFixitHoloShell(dialogMenuItem *self)
299{
300    FixItMode = 1;
301    systemCreateHoloshell();
302    return DITEM_SUCCESS;
303    FixItMode = 0;
304}
305
306int
307installFixitCDROM(dialogMenuItem *self)
308{
309    struct stat sb;
310
311    if (!RunningAsInit)
312	return DITEM_SUCCESS;
313
314    variable_set2(SYSTEM_STATE, "fixit", 0);
315    (void)unlink("/mnt2");
316    (void)rmdir("/mnt2");
317
318    while (1) {
319	msgConfirm("Please insert a FreeBSD live filesystem CD/DVD and press return");
320	if (DITEM_STATUS(mediaSetCDROM(NULL)) != DITEM_SUCCESS
321	    || !DEVICE_INIT(mediaDevice)) {
322	    /* If we can't initialize it, it's probably not a FreeBSD CDROM so punt on it */
323	    mediaClose();
324	    if (msgYesNo("Unable to mount the disc - do you want to try again?") != 0)
325		return DITEM_FAILURE;
326	}
327	else
328	    break;
329    }
330
331    /* Since the fixit code expects everything to be in /mnt2, and the CDROM mounting stuff /dist, do
332     * a little kludge dance here..
333     */
334    if (symlink("/dist", "/mnt2")) {
335	msgConfirm("Unable to symlink /mnt2 to the disc mount point.  Please report this\n"
336		   "unexpected failure to freebsd-bugs@FreeBSD.org.");
337	return DITEM_FAILURE;
338    }
339
340    /*
341     * If /tmp points to /mnt2/tmp from a previous fixit floppy session, it's
342     * not very good for us if we point it to the CDROM now.  Rather make it
343     * a directory in the root MFS then.  Experienced admins will still be
344     * able to mount their disk's /tmp over this if they need.
345     */
346    if (lstat("/tmp", &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFLNK)
347	(void)unlink("/tmp");
348    Mkdir("/tmp");
349
350    /*
351     * Since setuid binaries ignore LD_LIBRARY_PATH, we indeed need the
352     * ld.so.hints file.  Fortunately, it's fairly small (~ 3 KB).
353     */
354    if (!file_readable("/var/run/ld.so.hints")) {
355	Mkdir("/var/run");
356	if (vsystem("/mnt2/sbin/ldconfig -s /mnt2/usr/lib")) {
357	    msgConfirm("Warning: ldconfig could not create the ld.so hints file.\n"
358		       "Dynamic executables from the disc likely won't work.");
359	}
360    }
361
362    /* Yet more iggly hardcoded pathnames. */
363    Mkdir("/usr/libexec");
364    if (!file_readable("/usr/libexec/ld.so") && file_readable("/mnt2/usr/libexec/ld.so")) {
365	if (symlink("/mnt2/usr/libexec/ld.so", "/usr/libexec/ld.so"))
366	    msgDebug("Couldn't link to ld.so - not necessarily a problem for ELF\n");
367    }
368    if (!file_readable("/usr/libexec/ld-elf.so.1")) {
369	if (symlink("/mnt2/usr/libexec/ld-elf.so.1", "/usr/libexec/ld-elf.so.1")) {
370	    msgConfirm("Warning: could not create the symlink for ld-elf.so.1\n"
371		       "Dynamic executables from the disc likely won't work.");
372	}
373    }
374    /* optional nicety */
375    if (!file_readable("/usr/bin/vi"))
376	symlink("/mnt2/usr/bin/vi", "/usr/bin/vi");
377    fixit_common();
378    mediaClose();
379    msgConfirm("Please remove the FreeBSD fixit CDROM/DVD now.");
380    return DITEM_SUCCESS;
381}
382
383int
384installFixitFloppy(dialogMenuItem *self)
385{
386    struct ufs_args args;
387    extern char *distWanted;
388
389    if (!RunningAsInit)
390	return DITEM_SUCCESS;
391
392    /* Try to open the floppy drive */
393    if (DITEM_STATUS(mediaSetFloppy(NULL)) == DITEM_FAILURE || !mediaDevice) {
394	msgConfirm("Unable to set media device to floppy.");
395	mediaClose();
396	return DITEM_FAILURE;
397    }
398
399    memset(&args, 0, sizeof(args));
400    args.fspec = mediaDevice->devname;
401    mediaDevice->private = "/mnt2";
402    distWanted = NULL;
403    Mkdir("/mnt2");
404
405    variable_set2(SYSTEM_STATE, "fixit", 0);
406
407    while (1) {
408	if (!DEVICE_INIT(mediaDevice)) {
409	    if (msgYesNo("The attempt to mount the fixit floppy failed, bad floppy\n"
410			 "or unclean filesystem.  Do you want to try again?"))
411		return DITEM_FAILURE;
412	}
413	else
414	    break;
415    }
416    if (!directory_exists("/tmp"))
417	(void)symlink("/mnt2/tmp", "/tmp");
418    fixit_common();
419    mediaClose();
420    msgConfirm("Please remove the fixit floppy now.");
421    return DITEM_SUCCESS;
422}
423
424/*
425 * The common code for both fixit variants.
426 */
427static void
428fixit_common(void)
429{
430    pid_t child;
431    int waitstatus;
432
433    if (!directory_exists("/var/tmp/vi.recover")) {
434	if (DITEM_STATUS(Mkdir("/var/tmp/vi.recover")) != DITEM_SUCCESS) {
435	    msgConfirm("Warning:  Was unable to create a /var/tmp/vi.recover directory.\n"
436		       "vi will kvetch and moan about it as a result but should still\n"
437		       "be essentially usable.");
438	}
439    }
440    if (!directory_exists("/bin"))
441	(void)Mkdir("/bin");
442    (void)symlink("/stand/sh", "/bin/sh");
443    /* Link the /etc/ files */
444    if (DITEM_STATUS(Mkdir("/etc")) != DITEM_SUCCESS)
445	msgConfirm("Unable to create an /etc directory!  Things are weird on this floppy..");
446    else if ((symlink("/mnt2/etc/spwd.db", "/etc/spwd.db") == -1 && errno != EEXIST) ||
447	     (symlink("/mnt2/etc/protocols", "/etc/protocols") == -1 && errno != EEXIST) ||
448	     (symlink("/mnt2/etc/group", "/etc/group") == -1 && errno != EEXIST) ||
449	     (symlink("/mnt2/etc/services", "/etc/services") == -1 && errno != EEXIST))
450	msgConfirm("Couldn't symlink the /etc/ files!  I'm not sure I like this..");
451    if (!file_readable(TERMCAP_FILE))
452	create_termcap();
453    if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
454	systemSuspendDialog();	/* must be before the fork() */
455    if (!(child = fork())) {
456	int i, fd;
457	struct termios foo;
458	extern int login_tty(int);
459
460	ioctl(0, TIOCNOTTY, NULL);
461	for (i = getdtablesize(); i >= 0; --i)
462	    close(i);
463
464	if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
465	    fd = open("/dev/console", O_RDWR);
466	else
467	    fd = open("/dev/ttyv3", O_RDWR);
468	ioctl(0, TIOCSCTTY, &fd);
469	dup2(0, 1);
470	dup2(0, 2);
471	DebugFD = 2;
472	if (login_tty(fd) == -1)
473	    msgDebug("fixit: I can't set the controlling terminal.\n");
474
475	signal(SIGTTOU, SIG_IGN);
476	if (tcgetattr(0, &foo) != -1) {
477	    foo.c_cc[VERASE] = '\010';
478	    if (tcsetattr(0, TCSANOW, &foo) == -1)
479		msgDebug("fixit shell: Unable to set erase character.\n");
480	}
481	else
482	    msgDebug("fixit shell: Unable to get terminal attributes!\n");
483	setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/stand:"
484	       "/mnt2/stand:/mnt2/bin:/mnt2/sbin:/mnt2/usr/bin:/mnt2/usr/sbin", 1);
485	if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0) {
486	    printf("Waiting for fixit shell to exit.\n"
487		"When you are done, type ``exit'' to exit\n"
488		"the fixit shell and be returned here.\n\n");
489	    fflush(stdout);
490	}
491
492	/* use the .profile from the fixit medium */
493	setenv("HOME", "/mnt2", 1);
494	chdir("/mnt2");
495	execlp("sh", "-sh", (char *)0);
496	msgDebug("fixit shell: Failed to execute shell!\n");
497	_exit(1);;
498    }
499    else {
500	if (strcmp(variable_get(VAR_FIXIT_TTY), "standard") == 0) {
501	    dialog_clear_norefresh();
502	    msgNotify("Waiting for fixit shell to exit.  Go to VTY4 now by\n"
503		"typing ALT-F4.  When you are done, type ``exit'' to exit\n"
504		"the fixit shell and be returned here\n.");
505	}
506	(void)waitpid(child, &waitstatus, 0);
507	if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
508	    systemResumeDialog();
509    }
510    dialog_clear();
511}
512
513
514int
515installExpress(dialogMenuItem *self)
516{
517    int i;
518
519    dialog_clear_norefresh();
520    variable_set2(SYSTEM_STATE, "express", 0);
521#ifdef WITH_SLICES
522    if (DITEM_STATUS((i = diskPartitionEditor(self))) == DITEM_FAILURE)
523	return i;
524#endif
525
526    if (DITEM_STATUS((i = diskLabelEditor(self))) == DITEM_FAILURE)
527	return i;
528
529    if (DITEM_STATUS((i = installCommit(self))) == DITEM_SUCCESS) {
530	i |= DITEM_LEAVE_MENU;
531	/* Set default security level */
532	configSecurityModerate(NULL);
533
534	/* Give user the option of one last configuration spree */
535	installConfigure();
536    }
537    return i;
538}
539
540/* Standard mode installation */
541int
542installStandard(dialogMenuItem *self)
543{
544    int i, tries = 0;
545    Device **devs;
546
547    variable_set2(SYSTEM_STATE, "standard", 0);
548    dialog_clear_norefresh();
549#ifdef WITH_SLICES
550    msgConfirm("In the next menu, you will need to set up a DOS-style (\"fdisk\") partitioning\n"
551	       "scheme for your hard disk.  If you simply wish to devote all disk space\n"
552	       "to FreeBSD (overwriting anything else that might be on the disk(s) selected)\n"
553	       "then use the (A)ll command to select the default partitioning scheme followed\n"
554	       "by a (Q)uit.  If you wish to allocate only free space to FreeBSD, move to a\n"
555	       "partition marked \"unused\" and use the (C)reate command.");
556
557nodisks:
558    if (DITEM_STATUS(diskPartitionEditor(self)) == DITEM_FAILURE)
559	return DITEM_FAILURE;
560
561    if (diskGetSelectCount(&devs) <= 0 && tries < 3) {
562	msgConfirm("You need to select some disks to operate on!  Be sure to use SPACE\n"
563		   "instead of RETURN in the disk selection menu when selecting a disk.");
564	++tries;
565	goto nodisks;
566    }
567
568    msgConfirm("Now you need to create BSD partitions inside of the fdisk partition(s)\n"
569	       "just created.  If you have a reasonable amount of disk space (200MB or more)\n"
570	       "and don't have any special requirements, simply use the (A)uto command to\n"
571	       "allocate space automatically.  If you have more specific needs or just don't\n"
572	       "care for the layout chosen by (A)uto, press F1 for more information on\n"
573	       "manual layout.");
574#else
575    msgConfirm("First you need to create BSD partitions on the disk which you are\n"
576	       "installing to.  If you have a reasonable amount of disk space (200MB or more)\n"
577	       "and don't have any special requirements, simply use the (A)uto command to\n"
578	       "allocate space automatically.  If you have more specific needs or just don't\n"
579	       "care for the layout chosen by (A)uto, press F1 for more information on\n"
580	       "manual layout.");
581#endif
582
583    if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE)
584	return DITEM_FAILURE;
585
586    if (DITEM_STATUS((i = installCommit(self))) == DITEM_FAILURE) {
587	dialog_clear();
588	msgConfirm("Installation completed with some errors.  You may wish to\n"
589		   "scroll through the debugging messages on VTY1 with the\n"
590		   "scroll-lock feature.  You can also choose \"No\" at the next\n"
591		   "prompt and go back into the installation menus to retry\n"
592		   "whichever operations have failed.");
593	return i;
594
595    }
596    else {
597	dialog_clear();
598	msgConfirm("Congratulations!  You now have FreeBSD installed on your system.\n\n"
599		   "We will now move on to the final configuration questions.\n"
600		   "For any option you do not wish to configure, simply select\n"
601		   "No.\n\n"
602		   "If you wish to re-enter this utility after the system is up, you\n"
603		   "may do so by typing: /usr/sbin/sysinstall.");
604    }
605    if (mediaDevice->type != DEVICE_TYPE_FTP && mediaDevice->type != DEVICE_TYPE_NFS) {
606	if (!msgYesNo("Would you like to configure any Ethernet or SLIP/PPP network devices?")) {
607	    Device *tmp = tcpDeviceSelect();
608
609	    if (tmp && !((DevInfo *)tmp->private)->use_dhcp && !msgYesNo("Would you like to bring the %s interface up right now?", tmp->name))
610		if (!DEVICE_INIT(tmp))
611		    msgConfirm("Initialization of %s device failed.", tmp->name);
612	}
613	dialog_clear_norefresh();
614    }
615
616    if (!msgNoYes("Do you want this machine to function as a network gateway?"))
617	variable_set2("gateway_enable", "YES", 1);
618
619    dialog_clear_norefresh();
620    if (!msgNoYes("Do you want to configure inetd and the network services that it provides?"))
621        configInetd(self);
622
623    dialog_clear_norefresh();
624    if (!msgNoYes("Do you want to have anonymous FTP access to this machine?"))
625	configAnonFTP(self);
626
627    dialog_clear_norefresh();
628    if (!msgNoYes("Do you want to configure this machine as an NFS server?"))
629	configNFSServer(self);
630
631    dialog_clear_norefresh();
632    if (!msgNoYes("Do you want to configure this machine as an NFS client?"))
633	variable_set2("nfs_client_enable", "YES", 1);
634
635    if (!msgNoYes("Do you want to select a default security profile for\n"
636	         "this host (select No for \"moderate\" security)?"))
637	configSecurityProfile(self);
638    else
639	configSecurityModerate(self);
640
641#ifdef WITH_SYSCONS
642    dialog_clear_norefresh();
643    if (!msgNoYes("Would you like to customize your system console settings?"))
644	dmenuOpenSimple(&MenuSyscons, FALSE);
645#endif
646
647    dialog_clear_norefresh();
648    if (!msgYesNo("Would you like to set this machine's time zone now?"))
649	systemExecute("tzsetup");
650
651#ifdef WITH_LINUX
652    dialog_clear_norefresh();
653    if (!msgYesNo("Would you like to enable Linux binary compatibility?"))
654	(void)configLinux(self);
655#endif
656
657#ifdef __alpha__
658    dialog_clear_norefresh();
659    if (!msgYesNo("Would you like to enable OSF/1 binary compatibility?"))
660	(void)configOSF1(self);
661#endif
662
663#ifdef WITH_MICE
664    dialog_clear_norefresh();
665    if (!msgNoYes("Does this system have a PS/2, serial, or bus mouse?"))
666	dmenuOpenSimple(&MenuMouse, FALSE);
667#endif
668
669#ifdef __i386__
670    if (checkLoaderACPI() != 0) {
671    	dialog_clear_norefresh();
672    	if (!msgNoYes("ACPI was disabled during boot.\n"
673		      "Would you like to disable it permanently?"))
674		(void)configLoaderACPI(1 /*disable*/);
675    }
676#endif
677
678    /* Now would be a good time to checkpoint the configuration data */
679    configRC_conf();
680    sync();
681
682    if (directory_exists("/usr/X11R6")) {
683	dialog_clear_norefresh();
684	if (!msgYesNo("Would you like to configure your X server at this time?"))
685	    (void)configXSetup(self);
686    }
687
688    dialog_clear_norefresh();
689    if (!msgYesNo("The FreeBSD package collection is a collection of thousands of ready-to-run\n"
690		  "applications, from text editors to games to WEB servers and more.  Would you\n"
691		  "like to browse the collection now?")) {
692	(void)configPackages(self);
693    }
694
695    if (!msgYesNo("Would you like to add any initial user accounts to the system?\n"
696		  "Adding at least one account for yourself at this stage is suggested\n"
697		  "since working as the \"root\" user is dangerous (it is easy to do\n"
698		  "things which adversely affect the entire system)."))
699	(void)configUsers(self);
700
701    msgConfirm("Now you must set the system manager's password.\n"
702	       "This is the password you'll use to log in as \"root\".");
703    if (!systemExecute("passwd root"))
704	variable_set2("root_password", "YES", 0);
705
706    /* XXX Put whatever other nice configuration questions you'd like to ask the user here XXX */
707
708    /* Give user the option of one last configuration spree */
709    dialog_clear_norefresh();
710    installConfigure();
711    return DITEM_LEAVE_MENU;
712}
713
714/* The version of commit we call from the Install Custom menu */
715int
716installCustomCommit(dialogMenuItem *self)
717{
718    int i;
719
720    i = installCommit(self);
721    if (DITEM_STATUS(i) == DITEM_SUCCESS) {
722	/* Set default security level */
723	configSecurityModerate(NULL);
724
725	/* Give user the option of one last configuration spree */
726	installConfigure();
727	return i;
728    }
729    else
730	msgConfirm("The commit operation completed with errors.  Not\n"
731		   "updating /etc files.");
732    return i;
733}
734
735/*
736 * What happens when we finally decide to going ahead with the installation.
737 *
738 * This is broken into multiple stages so that the user can do a full
739 * installation but come back here again to load more distributions,
740 * perhaps from a different media type.  This would allow, for
741 * example, the user to load the majority of the system from CDROM and
742 * then use ftp to load just the CRYPTO dist.
743 */
744int
745installCommit(dialogMenuItem *self)
746{
747    int i;
748    char *str;
749
750    dialog_clear_norefresh();
751    if (!Dists)
752	distConfig(NULL);
753
754    if (!Dists) {
755	(void)dmenuOpenSimple(&MenuDistributions, FALSE);
756	/* select reasonable defaults if necessary */
757	if (!Dists)
758	    Dists = _DIST_USER;
759    }
760
761    if (!mediaVerify())
762	return DITEM_FAILURE;
763
764    str = variable_get(SYSTEM_STATE);
765    if (isDebug())
766	msgDebug("installCommit: System state is `%s'\n", str);
767
768    /* Installation stuff we wouldn't do to a running system */
769    if (RunningAsInit && DITEM_STATUS((i = installInitial())) == DITEM_FAILURE)
770	return i;
771
772try_media:
773    if (!DEVICE_INIT(mediaDevice)) {
774	if (!msgYesNo("Unable to initialize selected media. Would you like to\n"
775		      "adjust your media configuration and try again?")) {
776	    mediaDevice = NULL;
777	    if (!mediaVerify())
778		return DITEM_FAILURE;
779	    else
780		goto try_media;
781	}
782	else
783	    return DITEM_FAILURE;
784    }
785
786    /* Now go get it all */
787    i = distExtractAll(self);
788
789    /* When running as init, *now* it's safe to grab the rc.foo vars */
790    installEnvironment();
791
792    variable_set2(SYSTEM_STATE, DITEM_STATUS(i) == DITEM_FAILURE ? "error-install" : "full-install", 0);
793
794    return i;
795}
796
797static void
798installConfigure(void)
799{
800    /* Final menu of last resort */
801    if (!msgNoYes("Visit the general configuration menu for a chance to set\n"
802		  "any last options?"))
803	dmenuOpenSimple(&MenuConfigure, FALSE);
804    configRC_conf();
805    sync();
806}
807
808int
809installFixupBase(dialogMenuItem *self)
810{
811    Device **devs;
812    char *cp;
813    int i;
814    FILE *fp;
815    int kstat = 1;
816
817    /* All of this is done only as init, just to be safe */
818    if (RunningAsInit) {
819#if defined(__i386__) || defined(__amd64__)
820	if ((fp = fopen("/boot/loader.conf", "a")) != NULL) {
821	    if (!kstat || !OnVTY)
822		fprintf(fp, "# -- sysinstall generated deltas -- #\n");
823	    if (!kstat)
824		fprintf(fp, "userconfig_script_load=\"YES\"\n");
825	    if (!OnVTY)
826		fprintf(fp, "console=\"comconsole\"\n");
827	    fclose(fp);
828	}
829#endif
830
831	/* BOGON #2: We leave /etc in a bad state */
832	chmod("/etc", 0755);
833
834	/* BOGON #3: No /var/db/mountdtab complains */
835	Mkdir("/var/db");
836	creat("/var/db/mountdtab", 0644);
837
838	/* BOGON #4: /compat created by default in root fs */
839	Mkdir("/usr/compat");
840	vsystem("ln -s usr/compat /compat");
841
842	/* BOGON #5: aliases database not build for bin */
843	vsystem("newaliases");
844
845	/* Now run all the mtree stuff to fix things up */
846        vsystem("mtree -deU -f /etc/mtree/BSD.root.dist -p /");
847        vsystem("mtree -deU -f /etc/mtree/BSD.var.dist -p /var");
848        vsystem("mtree -deU -f /etc/mtree/BSD.usr.dist -p /usr");
849
850	/* Do all the last ugly work-arounds here */
851    }
852    return DITEM_SUCCESS | DITEM_RESTORE;
853}
854
855#ifndef X_AS_PKG
856/* Fix side-effects from the the XFree86 installation */
857int
858installFixupXFree(dialogMenuItem *self)
859{
860    /* BOGON #1:  XFree86 requires various specialized fixups */
861    if (directory_exists("/usr/X11R6")) {
862	dialog_clear_norefresh();
863	msgNotify("Fixing permissions in XFree86 tree..");
864	vsystem("chmod -R a+r /usr/X11R6");
865	vsystem("find /usr/X11R6 -type d | xargs chmod a+x");
866
867	/* Also do bogus minimal package registration so ports don't whine */
868	if (file_readable("/usr/X11R6/lib/X11/pkgreg.tar.gz")) {
869	    dialog_clear_norefresh();
870	    msgNotify("Installing package metainfo..");
871	    vsystem("tar xpzf /usr/X11R6/lib/X11/pkgreg.tar.gz -C / && rm /usr/X11R6/lib/X11/pkgreg.tar.gz");
872	}
873    }
874    return DITEM_SUCCESS | DITEM_RESTORE;
875}
876#endif
877
878#define	QUEUE_YES	1
879#define	QUEUE_NO	0
880static int
881performNewfs(PartInfo *pi, char *dname, int queue)
882{
883	char buffer[LINE_MAX];
884
885	if (pi->do_newfs) {
886		switch(pi->newfs_type) {
887		case NEWFS_UFS:
888			snprintf(buffer, LINE_MAX, "%s %s %s %s %s",
889			    NEWFS_UFS_CMD,
890			    pi->newfs_data.newfs_ufs.softupdates ?  "-U" : "",
891			    pi->newfs_data.newfs_ufs.ufs1 ? "-O1" : "-O2",
892			    pi->newfs_data.newfs_ufs.user_options,
893			    dname);
894			break;
895
896		case NEWFS_MSDOS:
897			snprintf(buffer, LINE_MAX, "%s %s", NEWFS_MSDOS_CMD,
898			    dname);
899			break;
900
901		case NEWFS_CUSTOM:
902			snprintf(buffer, LINE_MAX, "%s %s",
903			    pi->newfs_data.newfs_custom.command, dname);
904			break;
905		}
906
907		if (queue == QUEUE_YES) {
908			command_shell_add(pi->mountpoint, buffer);
909			return (0);
910		} else
911			return (vsystem(buffer));
912	}
913	return (0);
914}
915
916/* Go newfs and/or mount all the filesystems we've been asked to */
917int
918installFilesystems(dialogMenuItem *self)
919{
920    int i;
921    Disk *disk;
922    Chunk *c1, *c2, *rootdev, *swapdev;
923    Device **devs;
924    PartInfo *root;
925    char dname[80];
926    Boolean upgrade = FALSE;
927#if defined(__ia64__)
928    char efi_bootdir[FILENAME_MAX];
929#endif
930
931    /* If we've already done this, bail out */
932    if (!variable_cmp(DISK_LABELLED, "written"))
933	return DITEM_SUCCESS;
934
935    upgrade = !variable_cmp(SYSTEM_STATE, "upgrade");
936    if (!checkLabels(TRUE, &rootdev, &swapdev, NULL, NULL, NULL, NULL))
937	return DITEM_FAILURE;
938
939    if (rootdev)
940	root = (PartInfo *)rootdev->private_data;
941    else
942	root = NULL;
943
944    command_clear();
945    if (swapdev && RunningAsInit) {
946	/* As the very first thing, try to get ourselves some swap space */
947	sprintf(dname, "/dev/%s", swapdev->name);
948	if (!Fake && !file_readable(dname)) {
949	    msgConfirm("Unable to find device node for %s in /dev!\n"
950		       "The creation of filesystems will be aborted.", dname);
951	    return DITEM_FAILURE;
952	}
953
954	if (!Fake) {
955	    if (!swapon(dname)) {
956		dialog_clear_norefresh();
957		msgNotify("Added %s as initial swap device", dname);
958	    }
959	    else {
960		msgConfirm("WARNING!  Unable to swap to %s: %s\n"
961			   "This may cause the installation to fail at some point\n"
962			   "if you don't have a lot of memory.", dname, strerror(errno));
963	    }
964	}
965    }
966
967    if (rootdev && RunningAsInit) {
968	/* Next, create and/or mount the root device */
969	sprintf(dname, "/dev/%s", rootdev->name);
970	if (!Fake && !file_readable(dname)) {
971	    msgConfirm("Unable to make device node for %s in /dev!\n"
972		       "The creation of filesystems will be aborted.", dname);
973	    return DITEM_FAILURE | DITEM_RESTORE;
974	}
975	if (strcmp(root->mountpoint, "/"))
976	    msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", rootdev->name, root->mountpoint);
977
978	if (root->do_newfs && (!upgrade ||
979	    !msgNoYes("You are upgrading - are you SURE you want to newfs "
980	    "the root partition?"))) {
981	    int i;
982
983	    dialog_clear_norefresh();
984	    msgNotify("Making a new root filesystem on %s", dname);
985	    i = performNewfs(root, dname, QUEUE_NO);
986	    if (i) {
987		msgConfirm("Unable to make new root filesystem on %s!\n"
988			   "Command returned status %d", dname, i);
989		return DITEM_FAILURE | DITEM_RESTORE;
990	    }
991	}
992	else {
993	    if (!upgrade) {
994		msgConfirm("Warning:  Using existing root partition.  It will be assumed\n"
995			   "that you have the appropriate device entries already in /dev.");
996	    }
997	    dialog_clear_norefresh();
998	    msgNotify("Checking integrity of existing %s filesystem.", dname);
999	    i = vsystem("fsck_ffs -y %s", dname);
1000	    if (i)
1001		msgConfirm("Warning: fsck returned status of %d for %s.\n"
1002			   "This partition may be unsafe to use.", i, dname);
1003	}
1004
1005	/*
1006	 * If soft updates was enabled in the editor but we didn't newfs,
1007	 * use tunefs to update the soft updates flag on the file system.
1008	 */
1009	if (!root->do_newfs && root->newfs_type == NEWFS_UFS &&
1010	    root->newfs_data.newfs_ufs.softupdates) {
1011		i = vsystem("tunefs -n enable %s", dname);
1012		if (i)
1013			msgConfirm("Warning: Unable to enable soft updates"
1014			    " for root file system on %s", dname);
1015	}
1016
1017	/* Switch to block device */
1018	sprintf(dname, "/dev/%s", rootdev->name);
1019	if (Mount("/mnt", dname)) {
1020	    msgConfirm("Unable to mount the root file system on %s!  Giving up.", dname);
1021	    return DITEM_FAILURE | DITEM_RESTORE;
1022	}
1023
1024	/* Mount devfs for other partitions to mount */
1025	Mkdir("/mnt/dev");
1026	if (!Fake) {
1027	    struct iovec iov[4];
1028
1029	    iov[0].iov_base = "fstype";
1030	    iov[0].iov_len = strlen(iov[0].iov_base) + 1;
1031	    iov[1].iov_base = "devfs";
1032	    iov[1].iov_len = strlen(iov[1].iov_base) + 1;
1033	    iov[2].iov_base = "fspath";
1034	    iov[2].iov_len = strlen(iov[2].iov_base) + 1;
1035	    iov[3].iov_base = "/mnt/dev";
1036	    iov[3].iov_len = strlen(iov[3].iov_base) + 1;
1037	    i = nmount(iov, 4, 0);
1038
1039	    if (i) {
1040		dialog_clear_norefresh();
1041		msgConfirm("Unable to mount DEVFS (error %d)", errno);
1042		return DITEM_FAILURE | DITEM_RESTORE;
1043	    }
1044	}
1045    }
1046
1047    /* Now buzz through the rest of the partitions and mount them too */
1048    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
1049    for (i = 0; devs[i]; i++) {
1050	if (!devs[i]->enabled)
1051	    continue;
1052
1053	disk = (Disk *)devs[i]->private;
1054	if (!disk->chunks) {
1055	    msgConfirm("No chunk list found for %s!", disk->name);
1056	    return DITEM_FAILURE | DITEM_RESTORE;
1057	}
1058	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
1059	    if (c1->type == freebsd) {
1060		for (c2 = c1->part; c2; c2 = c2->next) {
1061		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
1062			PartInfo *tmp = (PartInfo *)c2->private_data;
1063
1064			/* Already did root */
1065			if (c2 == rootdev)
1066			    continue;
1067
1068			sprintf(dname, "%s/dev/%s",
1069			    RunningAsInit ? "/mnt" : "", c2->name);
1070
1071			if (tmp->do_newfs && (!upgrade ||
1072			    !msgNoYes("You are upgrading - are you SURE you"
1073			    " want to newfs /dev/%s?", c2->name)))
1074				performNewfs(tmp, dname, QUEUE_YES);
1075			else
1076			    command_shell_add(tmp->mountpoint,
1077				"fsck_ffs -y %s/dev/%s", RunningAsInit ?
1078				"/mnt" : "", c2->name);
1079#if 0
1080			if (tmp->soft)
1081			    command_shell_add(tmp->mountpoint,
1082			    "tunefs -n enable %s/dev/%s", RunningAsInit ?
1083			    "/mnt" : "", c2->name);
1084#endif
1085			command_func_add(tmp->mountpoint, Mount, c2->name);
1086		    }
1087		    else if (c2->type == part && c2->subtype == FS_SWAP) {
1088			char fname[80];
1089			int i;
1090
1091			if (c2 == swapdev)
1092			    continue;
1093			sprintf(fname, "%s/dev/%s", RunningAsInit ? "/mnt" : "", c2->name);
1094			i = (Fake || swapon(fname));
1095			if (!i) {
1096			    dialog_clear_norefresh();
1097			    msgNotify("Added %s as an additional swap device", fname);
1098			}
1099			else {
1100			    msgConfirm("Unable to add %s as a swap device: %s", fname, strerror(errno));
1101			}
1102		    }
1103		}
1104	    }
1105	    else if (c1->type == fat && c1->private_data &&
1106		(root->do_newfs || upgrade)) {
1107		char name[FILENAME_MAX];
1108
1109		sprintf(name, "%s/%s", RunningAsInit ? "/mnt" : "", ((PartInfo *)c1->private_data)->mountpoint);
1110		Mkdir(name);
1111	    }
1112#if defined(__ia64__)
1113	    else if (c1->type == efi && c1->private_data) {
1114		char bootdir[FILENAME_MAX];
1115		PartInfo *pi = (PartInfo *)c1->private_data;
1116		char *p;
1117
1118		sprintf(dname, "%s/dev/%s", RunningAsInit ? "/mnt" : "",
1119		    c1->name);
1120
1121		if (pi->do_newfs && (!upgrade ||
1122		    !msgNoYes("You are upgrading - are you SURE you want to "
1123		    "newfs /dev/%s?", c1->name)))
1124			performNewfs(pi, dname, QUEUE_YES);
1125
1126		command_func_add(pi->mountpoint, Mount_msdosfs, c1->name);
1127
1128		/*
1129		 * Create a directory boot on the EFI filesystem and create a
1130		 * link boot on the root filesystem pointing to the one on the
1131		 * EFI filesystem. That way, we install the loader, kernel
1132		 * and modules on the EFI filesystem.
1133		 */
1134		sprintf(bootdir, "%s", RunningAsInit ? "/mnt" : "");
1135		sprintf(efi_bootdir, "%s/%s", bootdir, pi->mountpoint);
1136		strcat(bootdir, "/boot");
1137		strcat(efi_bootdir, "/boot");
1138		command_func_add(pi->mountpoint, Mkdir_command, efi_bootdir);
1139
1140		/* Make a relative link. */
1141		p = &efi_bootdir[(RunningAsInit) ? 4 : 0];
1142		while (*p == '/')
1143			p++;
1144		symlink(p, bootdir);
1145	    }
1146#endif
1147	}
1148    }
1149
1150    command_sort();
1151    command_execute();
1152    dialog_clear_norefresh();
1153    return DITEM_SUCCESS | DITEM_RESTORE;
1154}
1155
1156static char *
1157getRelname(void)
1158{
1159    static char buf[64];
1160    size_t sz = (sizeof buf) - 1;
1161
1162    if (sysctlbyname("kern.osrelease", buf, &sz, NULL, 0) != -1) {
1163	buf[sz] = '\0';
1164	return buf;
1165    }
1166    else
1167	return "<unknown>";
1168}
1169
1170/* Initialize various user-settable values to their defaults */
1171int
1172installVarDefaults(dialogMenuItem *self)
1173{
1174    char *cp;
1175
1176    /* Set default startup options */
1177    variable_set2(VAR_RELNAME,			getRelname(), 0);
1178    variable_set2(VAR_CPIO_VERBOSITY,		"high", 0);
1179    variable_set2(VAR_TAPE_BLOCKSIZE,		DEFAULT_TAPE_BLOCKSIZE, 0);
1180    variable_set2(VAR_INSTALL_ROOT,		"/", 0);
1181    variable_set2(VAR_INSTALL_CFG,		"install.cfg", 0);
1182    cp = getenv("EDITOR");
1183    if (!cp)
1184	cp = "/usr/bin/ee";
1185    variable_set2(VAR_EDITOR,			cp, 0);
1186    variable_set2(VAR_FTP_USER,			"ftp", 0);
1187    variable_set2(VAR_BROWSER_PACKAGE,		"links", 0);
1188    variable_set2(VAR_BROWSER_BINARY,		"/usr/local/bin/links", 0);
1189    variable_set2(VAR_FTP_STATE,		"passive", 0);
1190    variable_set2(VAR_NFS_SECURE,		"NO", -1);
1191    if (OnVTY)
1192	    variable_set2(VAR_FIXIT_TTY,		"standard", 0);
1193    else
1194	    variable_set2(VAR_FIXIT_TTY,		"serial", 0);
1195    variable_set2(VAR_PKG_TMPDIR,		"/var/tmp", 0);
1196    variable_set2(VAR_MEDIA_TIMEOUT,		itoa(MEDIA_TIMEOUT), 0);
1197    if (getpid() != 1)
1198	variable_set2(SYSTEM_STATE,		"update", 0);
1199    else
1200	variable_set2(SYSTEM_STATE,		"init", 0);
1201    variable_set2(VAR_NEWFS_ARGS,		"-b 16384 -f 2048", 0);
1202    variable_set2(VAR_CONSTERM,                 "NO", 0);
1203    return DITEM_SUCCESS;
1204}
1205
1206/* Load the environment up from various system configuration files */
1207void
1208installEnvironment(void)
1209{
1210    configEnvironmentRC_conf();
1211    if (file_readable("/etc/resolv.conf"))
1212	configEnvironmentResolv("/etc/resolv.conf");
1213}
1214
1215/* Copy the boot floppy contents into /stand */
1216Boolean
1217copySelf(void)
1218{
1219    int i;
1220
1221    if (file_readable("/boot.help"))
1222	vsystem("cp /boot.help /mnt");
1223    msgWeHaveOutput("Copying the boot floppy to /stand on root filesystem");
1224    i = vsystem("find -x /stand | cpio %s -pdum /mnt", cpioVerbosity());
1225    if (i) {
1226	msgConfirm("Copy returned error status of %d!", i);
1227	return FALSE;
1228    }
1229
1230    /* Copy the /etc files into their rightful place */
1231    if (vsystem("cd /mnt/stand; find etc | cpio %s -pdum /mnt", cpioVerbosity())) {
1232	msgConfirm("Couldn't copy up the /etc files!");
1233	return TRUE;
1234    }
1235    return TRUE;
1236}
1237
1238static void
1239create_termcap(void)
1240{
1241    FILE *fp;
1242
1243    const char *caps[] = {
1244	termcap_vt100, termcap_cons25, termcap_cons25_m, termcap_cons25r,
1245	termcap_cons25r_m, termcap_cons25l1, termcap_cons25l1_m,
1246	termcap_xterm, NULL,
1247    };
1248    const char **cp;
1249
1250    if (!file_readable(TERMCAP_FILE)) {
1251	Mkdir("/usr/share/misc");
1252	fp = fopen(TERMCAP_FILE, "w");
1253	if (!fp) {
1254	    msgConfirm("Unable to initialize termcap file. Some screen-oriented\nutilities may not work.");
1255	    return;
1256	}
1257	cp = caps;
1258	while (*cp)
1259	    fprintf(fp, "%s\n", *(cp++));
1260	fclose(fp);
1261    }
1262}
1263