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