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