install.c revision 87185
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 87185 2001-12-02 04:47:46Z matusita $
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	if ((fp = fopen("/boot/loader.conf", "a")) != NULL) {
759	    if (!kstat || !OnVTY)
760		fprintf(fp, "# -- sysinstall generated deltas -- #\n");
761	    if (!kstat)
762		fprintf(fp, "userconfig_script_load=\"YES\"\n");
763	    if (!OnVTY)
764		fprintf(fp, "console=\"comconsole\"\n");
765	    fclose(fp);
766	}
767#endif
768	/* BOGON #1: Resurrect /dev after bin distribution screws it up */
769	dialog_clear_norefresh();
770	msgNotify("Remaking all devices.. Please wait!");
771	if (vsystem("cd /dev; sh MAKEDEV all")) {
772	    msgConfirm("MAKEDEV returned non-zero status");
773	    return DITEM_FAILURE | DITEM_RESTORE;
774	}
775
776	dialog_clear_norefresh();
777	msgNotify("Resurrecting /dev entries for slices..");
778	devs = deviceFind(NULL, DEVICE_TYPE_DISK);
779	if (!devs)
780	    msgFatal("Couldn't get a disk device list!");
781
782	/* Resurrect the slices that the former clobbered */
783	for (i = 0; devs[i]; i++) {
784	    Disk *disk = (Disk *)devs[i]->private;
785	    Chunk *c1;
786
787	    if (!devs[i]->enabled)
788		continue;
789	    if (!disk->chunks)
790		msgFatal("No chunk list found for %s!", disk->name);
791	    for (c1 = disk->chunks->part; c1; c1 = c1->next) {
792		if (c1->type == freebsd) {
793		    dialog_clear_norefresh();
794		    msgNotify("Making slice entries for %s", c1->name);
795		    if (vsystem("cd /dev; sh MAKEDEV %sh", c1->name)) {
796			msgConfirm("Unable to make slice entries for %s!", c1->name);
797			return DITEM_FAILURE | DITEM_RESTORE;
798		    }
799		}
800	    }
801	}
802
803	/* BOGON #2: We leave /etc in a bad state */
804	chmod("/etc", 0755);
805
806	/* BOGON #3: No /var/db/mountdtab complains */
807	Mkdir("/var/db");
808	creat("/var/db/mountdtab", 0644);
809
810	/* BOGON #4: /compat created by default in root fs */
811	Mkdir("/usr/compat");
812	vsystem("ln -s usr/compat /compat");
813
814	/* BOGON #5: aliases database not build for bin */
815	vsystem("newaliases");
816
817	/* Now run all the mtree stuff to fix things up */
818        vsystem("mtree -deU -f /etc/mtree/BSD.root.dist -p /");
819        vsystem("mtree -deU -f /etc/mtree/BSD.var.dist -p /var");
820        vsystem("mtree -deU -f /etc/mtree/BSD.usr.dist -p /usr");
821
822	/* Do all the last ugly work-arounds here */
823    }
824    return DITEM_SUCCESS | DITEM_RESTORE;
825}
826
827/* Fix side-effects from the the XFree86 installation */
828int
829installFixupXFree(dialogMenuItem *self)
830{
831    /* BOGON #1:  XFree86 requires various specialized fixups */
832    if (directory_exists("/usr/X11R6")) {
833	dialog_clear_norefresh();
834	msgNotify("Fixing permissions in XFree86 tree..");
835	vsystem("chmod -R a+r /usr/X11R6");
836	vsystem("find /usr/X11R6 -type d | xargs chmod a+x");
837
838	/* Also do bogus minimal package registration so ports don't whine */
839	if (file_readable("/usr/X11R6/lib/X11/pkgreg.tar.gz")) {
840	    dialog_clear_norefresh();
841	    msgNotify("Installing package metainfo..");
842	    vsystem("tar xpzf /usr/X11R6/lib/X11/pkgreg.tar.gz -C / && rm /usr/X11R6/lib/X11/pkgreg.tar.gz");
843	}
844    }
845    return DITEM_SUCCESS | DITEM_RESTORE;
846}
847
848/* Go newfs and/or mount all the filesystems we've been asked to */
849int
850installFilesystems(dialogMenuItem *self)
851{
852    int i, mountfailed;
853    Disk *disk;
854    Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev;
855    Device **devs;
856    PartInfo *root;
857    char dname[80];
858    extern int MakeDevChunk(Chunk *c, char *n);
859    Boolean upgrade = FALSE;
860
861    /* If we've already done this, bail out */
862    if (!variable_cmp(DISK_LABELLED, "written"))
863	return DITEM_SUCCESS;
864
865    upgrade = !variable_cmp(SYSTEM_STATE, "upgrade");
866    if (!checkLabels(TRUE, &rootdev, &swapdev, &usrdev, &vardev))
867	return DITEM_FAILURE;
868
869    if (rootdev)
870	root = (PartInfo *)rootdev->private_data;
871    else
872	root = NULL;
873
874    command_clear();
875    if (swapdev && RunningAsInit) {
876	/* As the very first thing, try to get ourselves some swap space */
877	sprintf(dname, "/dev/%s", swapdev->name);
878	if (!Fake && (!MakeDevChunk(swapdev, "/dev") || !file_readable(dname))) {
879	    msgConfirm("Unable to make device node for %s in /dev!\n"
880		       "The creation of filesystems will be aborted.", dname);
881	    return DITEM_FAILURE;
882	}
883
884	if (!Fake) {
885	    if (!swapon(dname)) {
886		dialog_clear_norefresh();
887		msgNotify("Added %s as initial swap device", dname);
888	    }
889	    else {
890		msgConfirm("WARNING!  Unable to swap to %s: %s\n"
891			   "This may cause the installation to fail at some point\n"
892			   "if you don't have a lot of memory.", dname, strerror(errno));
893	    }
894	}
895    }
896
897    if (rootdev && RunningAsInit) {
898	/* Next, create and/or mount the root device */
899	sprintf(dname, "/dev/%s", rootdev->name);
900	if (!Fake && (!MakeDevChunk(rootdev, "/dev") || !file_readable(dname))) {
901	    msgConfirm("Unable to make device node for %s in /dev!\n"
902		       "The creation of filesystems will be aborted.", dname);
903	    return DITEM_FAILURE | DITEM_RESTORE;
904	}
905	if (strcmp(root->mountpoint, "/"))
906	    msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", rootdev->name, root->mountpoint);
907
908	if (root->newfs && (!upgrade || !msgNoYes("You are upgrading - are you SURE you want to newfs the root partition?"))) {
909	    int i;
910
911	    dialog_clear_norefresh();
912	    msgNotify("Making a new root filesystem on %s", dname);
913	    i = vsystem("%s %s", root->newfs_cmd, dname);
914	    if (i) {
915		msgConfirm("Unable to make new root filesystem on %s!\n"
916			   "Command returned status %d", dname, i);
917		return DITEM_FAILURE | DITEM_RESTORE;
918	    }
919	}
920	else {
921	    if (!upgrade) {
922		msgConfirm("Warning:  Using existing root partition.  It will be assumed\n"
923			   "that you have the appropriate device entries already in /dev.");
924	    }
925	    dialog_clear_norefresh();
926	    msgNotify("Checking integrity of existing %s filesystem.", dname);
927	    i = vsystem("fsck_ffs -y %s", dname);
928	    if (i)
929		msgConfirm("Warning: fsck returned status of %d for %s.\n"
930			   "This partition may be unsafe to use.", i, dname);
931	}
932	if (root->soft) {
933	    i = vsystem("tunefs -n enable %s", dname);
934	    if (i)
935		msgConfirm("Warning:  Unable to enable softupdates for root filesystem on %s", dname);
936	}
937
938	/* Switch to block device */
939	sprintf(dname, "/dev/%s", rootdev->name);
940	if (Mount("/mnt", dname)) {
941	    msgConfirm("Unable to mount the root file system on %s!  Giving up.", dname);
942	    return DITEM_FAILURE | DITEM_RESTORE;
943	}
944
945	/* Mount devfs for other partitions to mount */
946	Mkdir("/mnt/dev");
947	if (!Fake)
948	    mountfailed = mount("devfs", "/mnt/dev", 0, NULL);
949
950	if (mountfailed) {
951	    dialog_clear_norefresh();
952	    msgNotify("Copying initial device files..");
953	    /* Copy the boot floppy's dev files */
954	    if ((root->newfs || upgrade) && vsystem("find -x /dev | cpio %s -pdum /mnt", cpioVerbosity())) {
955		msgConfirm("Couldn't clone the /dev files!");
956		return DITEM_FAILURE | DITEM_RESTORE;
957	    }
958	}
959    }
960
961    /* Now buzz through the rest of the partitions and mount them too */
962    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
963    for (i = 0; devs[i]; i++) {
964	if (!devs[i]->enabled)
965	    continue;
966
967	disk = (Disk *)devs[i]->private;
968	if (!disk->chunks) {
969	    msgConfirm("No chunk list found for %s!", disk->name);
970	    return DITEM_FAILURE | DITEM_RESTORE;
971	}
972	if (mountfailed) {
973	    if (RunningAsInit && root && (root->newfs || upgrade)) {
974		Mkdir("/mnt/dev");
975		if (!Fake)
976		    MakeDevDisk(disk, "/mnt/dev");
977	    }
978	    else if (!RunningAsInit && !Fake)
979		MakeDevDisk(disk, "/dev");
980	}
981
982	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
983	    if (c1->type == freebsd) {
984		for (c2 = c1->part; c2; c2 = c2->next) {
985		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
986			PartInfo *tmp = (PartInfo *)c2->private_data;
987
988			/* Already did root */
989			if (c2 == rootdev)
990			    continue;
991
992			if (tmp->newfs && (!upgrade || !msgNoYes("You are upgrading - are you SURE you want to newfs /dev/%s?", c2->name)))
993			    command_shell_add(tmp->mountpoint, "%s %s/dev/%s", tmp->newfs_cmd, RunningAsInit ? "/mnt" : "", c2->name);
994			else
995			    command_shell_add(tmp->mountpoint, "fsck_ffs -y %s/dev/%s", RunningAsInit ? "/mnt" : "", c2->name);
996			if (tmp->soft)
997			    command_shell_add(tmp->mountpoint, "tunefs -n enable %s/dev/%s", RunningAsInit ? "/mnt" : "", c2->name);
998			command_func_add(tmp->mountpoint, Mount, c2->name);
999		    }
1000		    else if (c2->type == part && c2->subtype == FS_SWAP) {
1001			char fname[80];
1002			int i;
1003
1004			if (c2 == swapdev)
1005			    continue;
1006			sprintf(fname, "%s/dev/%s", RunningAsInit ? "/mnt" : "", c2->name);
1007			i = (Fake || swapon(fname));
1008			if (!i) {
1009			    dialog_clear_norefresh();
1010			    msgNotify("Added %s as an additional swap device", fname);
1011			}
1012			else {
1013			    msgConfirm("Unable to add %s as a swap device: %s", fname, strerror(errno));
1014			}
1015		    }
1016		}
1017	    }
1018	    else if (c1->type == fat && c1->private_data && (root->newfs || upgrade)) {
1019		char name[FILENAME_MAX];
1020
1021		sprintf(name, "%s/%s", RunningAsInit ? "/mnt" : "", ((PartInfo *)c1->private_data)->mountpoint);
1022		Mkdir(name);
1023	    }
1024	}
1025    }
1026
1027    command_sort();
1028    command_execute();
1029    if (!mountfailed && !Fake)
1030	unmount("/mnt/dev", MNT_FORCE);
1031    dialog_clear_norefresh();
1032    return DITEM_SUCCESS | DITEM_RESTORE;
1033}
1034
1035static char *
1036getRelname(void)
1037{
1038    static char buf[64];
1039    size_t sz = (sizeof buf) - 1;
1040
1041    if (sysctlbyname("kern.osrelease", buf, &sz, NULL, 0) != -1) {
1042	buf[sz] = '\0';
1043	return buf;
1044    }
1045    else
1046	return "<unknown>";
1047}
1048
1049/* Initialize various user-settable values to their defaults */
1050int
1051installVarDefaults(dialogMenuItem *self)
1052{
1053    char *cp;
1054
1055    /* Set default startup options */
1056    variable_set2(VAR_RELNAME,			getRelname(), 0);
1057    variable_set2(VAR_CPIO_VERBOSITY,		"high", 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", 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