install.c revision 141430
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 141430 2005-02-07 04:25:34Z obrien $
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    dialog_clear_norefresh();
700    if (!msgYesNo("The FreeBSD package collection is a collection of thousands of ready-to-run\n"
701		  "applications, from text editors to games to WEB servers and more.  Would you\n"
702		  "like to browse the collection now?")) {
703	(void)configPackages(self);
704    }
705
706    if (!msgYesNo("Would you like to add any initial user accounts to the system?\n"
707		  "Adding at least one account for yourself at this stage is suggested\n"
708		  "since working as the \"root\" user is dangerous (it is easy to do\n"
709		  "things which adversely affect the entire system)."))
710	(void)configUsers(self);
711
712    msgConfirm("Now you must set the system manager's password.\n"
713	       "This is the password you'll use to log in as \"root\".");
714    if (!systemExecute("passwd root"))
715	variable_set2("root_password", "YES", 0);
716
717    /* XXX Put whatever other nice configuration questions you'd like to ask the user here XXX */
718
719    /* Give user the option of one last configuration spree */
720    dialog_clear_norefresh();
721    installConfigure();
722    return DITEM_LEAVE_MENU;
723}
724
725/* The version of commit we call from the Install Custom menu */
726int
727installCustomCommit(dialogMenuItem *self)
728{
729    int i;
730
731    i = installCommit(self);
732    if (DITEM_STATUS(i) == DITEM_SUCCESS) {
733	/* Give user the option of one last configuration spree */
734	installConfigure();
735	return i;
736    }
737    else
738	msgConfirm("The commit operation completed with errors.  Not\n"
739		   "updating /etc files.");
740    return i;
741}
742
743/*
744 * What happens when we finally decide to going ahead with the installation.
745 *
746 * This is broken into multiple stages so that the user can do a full
747 * installation but come back here again to load more distributions,
748 * perhaps from a different media type.  This would allow, for
749 * example, the user to load the majority of the system from CDROM and
750 * then use ftp to load a different dist.
751 */
752int
753installCommit(dialogMenuItem *self)
754{
755    int i;
756    char *str;
757
758    dialog_clear_norefresh();
759    if (!Dists)
760	distConfig(NULL);
761
762    if (!Dists) {
763	(void)dmenuOpenSimple(&MenuDistributions, FALSE);
764	/* select reasonable defaults if necessary */
765	if (!Dists)
766	    Dists = _DIST_USER;
767    }
768
769    if (!mediaVerify())
770	return DITEM_FAILURE;
771
772    str = variable_get(SYSTEM_STATE);
773    if (isDebug())
774	msgDebug("installCommit: System state is `%s'\n", str);
775
776    /* Installation stuff we wouldn't do to a running system */
777    if (RunningAsInit && DITEM_STATUS((i = installInitial())) == DITEM_FAILURE)
778	return i;
779
780try_media:
781    if (!DEVICE_INIT(mediaDevice)) {
782	if (!msgYesNo("Unable to initialize selected media. Would you like to\n"
783		      "adjust your media configuration and try again?")) {
784	    mediaDevice = NULL;
785	    if (!mediaVerify())
786		return DITEM_FAILURE;
787	    else
788		goto try_media;
789	}
790	else
791	    return DITEM_FAILURE;
792    }
793
794    /* Now go get it all */
795    i = distExtractAll(self);
796
797    /* When running as init, *now* it's safe to grab the rc.foo vars */
798    installEnvironment();
799
800    variable_set2(SYSTEM_STATE, DITEM_STATUS(i) == DITEM_FAILURE ? "error-install" : "full-install", 0);
801
802    return i;
803}
804
805static void
806installConfigure(void)
807{
808    /* Final menu of last resort */
809    if (!msgNoYes("Visit the general configuration menu for a chance to set\n"
810		  "any last options?"))
811	dmenuOpenSimple(&MenuConfigure, FALSE);
812    configRC_conf();
813    sync();
814}
815
816int
817installFixupBase(dialogMenuItem *self)
818{
819    FILE *fp;
820#ifdef __ia64__
821    const char *efi_mntpt;
822#endif
823
824    /* All of this is done only as init, just to be safe */
825    if (RunningAsInit) {
826#if defined(__i386__) || defined(__amd64__)
827	if ((fp = fopen("/boot/loader.conf", "a")) != NULL) {
828	    if (!OnVTY) {
829		fprintf(fp, "# -- sysinstall generated deltas -- #\n");
830		fprintf(fp, "console=\"comconsole\"\n");
831	    }
832	    fclose(fp);
833	}
834#endif
835
836	/* BOGON #2: We leave /etc in a bad state */
837	chmod("/etc", 0755);
838
839	/* BOGON #3: No /var/db/mountdtab complains */
840	Mkdir("/var/db");
841	creat("/var/db/mountdtab", 0644);
842
843	/* BOGON #4: /compat created by default in root fs */
844	Mkdir("/usr/compat");
845	vsystem("ln -s usr/compat /compat");
846
847	/* BOGON #5: aliases database not build for bin */
848	vsystem("newaliases");
849
850	/* BOGON #6: Remove /stand (finally) */
851	vsystem("rm -rf /stand");
852
853	/* Now run all the mtree stuff to fix things up */
854        vsystem("mtree -deU -f /etc/mtree/BSD.root.dist -p /");
855        vsystem("mtree -deU -f /etc/mtree/BSD.var.dist -p /var");
856        vsystem("mtree -deU -f /etc/mtree/BSD.usr.dist -p /usr");
857
858#ifdef __ia64__
859	/* Move /boot to the the EFI partition and make /boot a link to it. */
860	efi_mntpt = (EfiChunk != NULL) ? ((PartInfo *)EfiChunk->private_data)->mountpoint : NULL;
861	if (efi_mntpt != NULL) {
862		vsystem("if [ ! -L /boot ]; then mv /boot %s; fi", efi_mntpt);
863		vsystem("if [ ! -e /boot ]; then ln -sf %s/boot /boot; fi",
864		    efi_mntpt + 1);	/* Skip leading '/' */
865		/* Make sure the kernel knows which partition is the root file system. */
866		vsystem("echo 'vfs.root.mountfrom=\"ufs:/dev/%s\"' >> /boot/loader.conf", RootChunk->name);
867	}
868#endif
869
870	/* Do all the last ugly work-arounds here */
871    }
872    return DITEM_SUCCESS | DITEM_RESTORE;
873}
874
875#define	QUEUE_YES	1
876#define	QUEUE_NO	0
877static int
878performNewfs(PartInfo *pi, char *dname, int queue)
879{
880	char buffer[LINE_MAX];
881
882	if (pi->do_newfs) {
883		switch(pi->newfs_type) {
884		case NEWFS_UFS:
885			snprintf(buffer, LINE_MAX, "%s %s %s %s %s",
886			    NEWFS_UFS_CMD,
887			    pi->newfs_data.newfs_ufs.softupdates ?  "-U" : "",
888			    pi->newfs_data.newfs_ufs.ufs1 ? "-O1" : "-O2",
889			    pi->newfs_data.newfs_ufs.user_options,
890			    dname);
891			break;
892
893		case NEWFS_MSDOS:
894			snprintf(buffer, LINE_MAX, "%s %s", NEWFS_MSDOS_CMD,
895			    dname);
896			break;
897
898		case NEWFS_CUSTOM:
899			snprintf(buffer, LINE_MAX, "%s %s",
900			    pi->newfs_data.newfs_custom.command, dname);
901			break;
902		}
903
904		if (queue == QUEUE_YES) {
905			command_shell_add(pi->mountpoint, buffer);
906			return (0);
907		} else
908			return (vsystem(buffer));
909	}
910	return (0);
911}
912
913/* Go newfs and/or mount all the filesystems we've been asked to */
914int
915installFilesystems(dialogMenuItem *self)
916{
917    int i;
918    Disk *disk;
919    Chunk *c1, *c2;
920    Device **devs;
921    PartInfo *root;
922    char dname[80];
923    Boolean upgrade = FALSE;
924
925    /* If we've already done this, bail out */
926    if (!variable_cmp(DISK_LABELLED, "written"))
927	return DITEM_SUCCESS;
928
929    upgrade = !variable_cmp(SYSTEM_STATE, "upgrade");
930    if (!checkLabels(TRUE))
931	return DITEM_FAILURE;
932
933    root = (RootChunk != NULL) ? (PartInfo *)RootChunk->private_data : NULL;
934
935    command_clear();
936    if (SwapChunk && RunningAsInit) {
937	/* As the very first thing, try to get ourselves some swap space */
938	sprintf(dname, "/dev/%s", SwapChunk->name);
939	if (!Fake && !file_readable(dname)) {
940	    msgConfirm("Unable to find device node for %s in /dev!\n"
941		       "The creation of filesystems will be aborted.", dname);
942	    return DITEM_FAILURE;
943	}
944
945	if (!Fake) {
946	    if (!swapon(dname)) {
947		dialog_clear_norefresh();
948		msgNotify("Added %s as initial swap device", dname);
949	    }
950	    else {
951		msgConfirm("WARNING!  Unable to swap to %s: %s\n"
952			   "This may cause the installation to fail at some point\n"
953			   "if you don't have a lot of memory.", dname, strerror(errno));
954	    }
955	}
956    }
957
958    if (RootChunk && RunningAsInit) {
959	/* Next, create and/or mount the root device */
960	sprintf(dname, "/dev/%s", RootChunk->name);
961	if (!Fake && !file_readable(dname)) {
962	    msgConfirm("Unable to make device node for %s in /dev!\n"
963		       "The creation of filesystems will be aborted.", dname);
964	    return DITEM_FAILURE | DITEM_RESTORE;
965	}
966	if (strcmp(root->mountpoint, "/"))
967	    msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", RootChunk->name, root->mountpoint);
968
969	if (root->do_newfs && (!upgrade ||
970	    !msgNoYes("You are upgrading - are you SURE you want to newfs "
971	    "the root partition?"))) {
972	    int i;
973
974	    dialog_clear_norefresh();
975	    msgNotify("Making a new root filesystem on %s", dname);
976	    i = performNewfs(root, dname, QUEUE_NO);
977	    if (i) {
978		msgConfirm("Unable to make new root filesystem on %s!\n"
979			   "Command returned status %d", dname, i);
980		return DITEM_FAILURE | DITEM_RESTORE;
981	    }
982	}
983	else {
984	    if (!upgrade) {
985		msgConfirm("Warning:  Using existing root partition.  It will be assumed\n"
986			   "that you have the appropriate device entries already in /dev.");
987	    }
988	    dialog_clear_norefresh();
989	    msgNotify("Checking integrity of existing %s filesystem.", dname);
990	    i = vsystem("fsck_ffs -y %s", dname);
991	    if (i)
992		msgConfirm("Warning: fsck returned status of %d for %s.\n"
993			   "This partition may be unsafe to use.", i, dname);
994	}
995
996	/*
997	 * If soft updates was enabled in the editor but we didn't newfs,
998	 * use tunefs to update the soft updates flag on the file system.
999	 */
1000	if (!root->do_newfs && root->newfs_type == NEWFS_UFS &&
1001	    root->newfs_data.newfs_ufs.softupdates) {
1002		i = vsystem("tunefs -n enable %s", dname);
1003		if (i)
1004			msgConfirm("Warning: Unable to enable soft updates"
1005			    " for root file system on %s", dname);
1006	}
1007
1008	/* Switch to block device */
1009	sprintf(dname, "/dev/%s", RootChunk->name);
1010	if (Mount("/mnt", dname)) {
1011	    msgConfirm("Unable to mount the root file system on %s!  Giving up.", dname);
1012	    return DITEM_FAILURE | DITEM_RESTORE;
1013	}
1014
1015	/* Mount devfs for other partitions to mount */
1016	Mkdir("/mnt/dev");
1017	if (!Fake) {
1018	    struct iovec iov[4];
1019
1020	    iov[0].iov_base = "fstype";
1021	    iov[0].iov_len = strlen(iov[0].iov_base) + 1;
1022	    iov[1].iov_base = "devfs";
1023	    iov[1].iov_len = strlen(iov[1].iov_base) + 1;
1024	    iov[2].iov_base = "fspath";
1025	    iov[2].iov_len = strlen(iov[2].iov_base) + 1;
1026	    iov[3].iov_base = "/mnt/dev";
1027	    iov[3].iov_len = strlen(iov[3].iov_base) + 1;
1028	    i = nmount(iov, 4, 0);
1029
1030	    if (i) {
1031		dialog_clear_norefresh();
1032		msgConfirm("Unable to mount DEVFS (error %d)", errno);
1033		return DITEM_FAILURE | DITEM_RESTORE;
1034	    }
1035	}
1036    }
1037
1038    /* Now buzz through the rest of the partitions and mount them too */
1039    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
1040    for (i = 0; devs[i]; i++) {
1041	if (!devs[i]->enabled)
1042	    continue;
1043
1044	disk = (Disk *)devs[i]->private;
1045	if (!disk->chunks) {
1046	    msgConfirm("No chunk list found for %s!", disk->name);
1047	    return DITEM_FAILURE | DITEM_RESTORE;
1048	}
1049	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
1050#ifdef __ia64__
1051	if (c1->type == part) {
1052		c2 = c1;
1053		{
1054#elif defined(__powerpc__)
1055	    if (c1->type == apple) {
1056		for (c2 = c1->part; c2; c2 = c2->next) {
1057#else
1058	    if (c1->type == freebsd) {
1059		for (c2 = c1->part; c2; c2 = c2->next) {
1060#endif
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 == RootChunk)
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 == SwapChunk)
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#endif
1129	}
1130    }
1131
1132    command_sort();
1133    command_execute();
1134    dialog_clear_norefresh();
1135    return DITEM_SUCCESS | DITEM_RESTORE;
1136}
1137
1138static char *
1139getRelname(void)
1140{
1141    static char buf[64];
1142    size_t sz = (sizeof buf) - 1;
1143
1144    if (sysctlbyname("kern.osrelease", buf, &sz, NULL, 0) != -1) {
1145	buf[sz] = '\0';
1146	return buf;
1147    }
1148    else
1149	return "<unknown>";
1150}
1151
1152/* Initialize various user-settable values to their defaults */
1153int
1154installVarDefaults(dialogMenuItem *self)
1155{
1156    char *cp;
1157
1158    /* Set default startup options */
1159    variable_set2(VAR_RELNAME,			getRelname(), 0);
1160    variable_set2(VAR_CPIO_VERBOSITY,		"high", 0);
1161    variable_set2(VAR_TAPE_BLOCKSIZE,		DEFAULT_TAPE_BLOCKSIZE, 0);
1162    variable_set2(VAR_INSTALL_ROOT,		"/", 0);
1163    variable_set2(VAR_INSTALL_CFG,		"install.cfg", 0);
1164    variable_set2(VAR_SKIP_PCCARD,		"NO", 0);
1165    cp = getenv("EDITOR");
1166    if (!cp)
1167	cp = "/usr/bin/ee";
1168    variable_set2(VAR_EDITOR,			cp, 0);
1169    variable_set2(VAR_FTP_USER,			"ftp", 0);
1170    variable_set2(VAR_BROWSER_PACKAGE,		"links", 0);
1171    variable_set2(VAR_BROWSER_BINARY,		"/usr/local/bin/links", 0);
1172    variable_set2(VAR_FTP_STATE,		"passive", 0);
1173    variable_set2(VAR_NFS_SECURE,		"NO", -1);
1174    variable_set2(VAR_NFS_TCP,   		"NO", -1);
1175    variable_set2(VAR_NFS_V3,   		"YES", -1);
1176    if (OnVTY)
1177	    variable_set2(VAR_FIXIT_TTY,		"standard", 0);
1178    else
1179	    variable_set2(VAR_FIXIT_TTY,		"serial", 0);
1180    variable_set2(VAR_PKG_TMPDIR,		"/var/tmp", 0);
1181    variable_set2(VAR_MEDIA_TIMEOUT,		itoa(MEDIA_TIMEOUT), 0);
1182    if (getpid() != 1)
1183	variable_set2(SYSTEM_STATE,		"update", 0);
1184    else
1185	variable_set2(SYSTEM_STATE,		"init", 0);
1186    variable_set2(VAR_NEWFS_ARGS,		"-b 16384 -f 2048", 0);
1187    variable_set2(VAR_CONSTERM,                 "NO", 0);
1188    return DITEM_SUCCESS;
1189}
1190
1191/* Load the environment up from various system configuration files */
1192void
1193installEnvironment(void)
1194{
1195    configEnvironmentRC_conf();
1196    if (file_readable("/etc/resolv.conf"))
1197	configEnvironmentResolv("/etc/resolv.conf");
1198}
1199
1200/* Copy the boot floppy contents into /stand */
1201Boolean
1202copySelf(void)
1203{
1204    int i;
1205
1206    if (file_readable("/boot.help"))
1207	vsystem("cp /boot.help /mnt");
1208    msgWeHaveOutput("Copying the boot floppy to /stand on root filesystem");
1209    i = vsystem("find -x /stand | cpio %s -pdum /mnt", cpioVerbosity());
1210    if (i) {
1211	msgConfirm("Copy returned error status of %d!", i);
1212	return FALSE;
1213    }
1214
1215    /* Copy the /etc files into their rightful place */
1216    if (vsystem("cd /mnt/stand; find etc | cpio %s -pdum /mnt", cpioVerbosity())) {
1217	msgConfirm("Couldn't copy up the /etc files!");
1218	return TRUE;
1219    }
1220    return TRUE;
1221}
1222
1223static void
1224create_termcap(void)
1225{
1226    FILE *fp;
1227
1228    const char *caps[] = {
1229	termcap_vt100, termcap_cons25, termcap_cons25_m, termcap_cons25r,
1230	termcap_cons25r_m, termcap_cons25l1, termcap_cons25l1_m,
1231	termcap_xterm, NULL,
1232    };
1233    const char **cp;
1234
1235    if (!file_readable(TERMCAP_FILE)) {
1236	Mkdir("/usr/share/misc");
1237	fp = fopen(TERMCAP_FILE, "w");
1238	if (!fp) {
1239	    msgConfirm("Unable to initialize termcap file. Some screen-oriented\nutilities may not work.");
1240	    return;
1241	}
1242	cp = caps;
1243	while (*cp)
1244	    fprintf(fp, "%s\n", *(cp++));
1245	fclose(fp);
1246    }
1247}
1248