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