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