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