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