install.c revision 41558
1272343Sngie/*
2272343Sngie * The new sysinstall program.
3272343Sngie *
4272343Sngie * This is probably the last program in the `sysinstall' line - the next
5272343Sngie * generation being essentially a complete rewrite.
6272343Sngie *
7272343Sngie * $Id: install.c,v 1.219 1998/11/24 00:18:55 jkh Exp $
8272343Sngie *
9272343Sngie * Copyright (c) 1995
10272343Sngie *	Jordan Hubbard.  All rights reserved.
11272343Sngie *
12272343Sngie * Redistribution and use in source and binary forms, with or without
13272343Sngie * modification, are permitted provided that the following conditions
14272343Sngie * are met:
15272343Sngie * 1. Redistributions of source code must retain the above copyright
16272343Sngie *    notice, this list of conditions and the following disclaimer,
17272343Sngie *    verbatim and that no modifications are made prior to this
18272343Sngie *    point in the file.
19272343Sngie * 2. Redistributions in binary form must reproduce the above copyright
20272343Sngie *    notice, this list of conditions and the following disclaimer in the
21272343Sngie *    documentation and/or other materials provided with the distribution.
22272343Sngie *
23272343Sngie * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
24272343Sngie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25272343Sngie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26272343Sngie * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
27272343Sngie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28272343Sngie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29272343Sngie * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
30272343Sngie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31272343Sngie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32272343Sngie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33272343Sngie * SUCH DAMAGE.
34272343Sngie *
35272343Sngie */
36272343Sngie
37272343Sngie#include "sysinstall.h"
38272343Sngie#include "uc_main.h"
39272343Sngie#include <ctype.h>
40272343Sngie#include <sys/disklabel.h>
41272343Sngie#include <sys/errno.h>
42272343Sngie#include <sys/ioctl.h>
43272343Sngie#include <sys/fcntl.h>
44272343Sngie#include <sys/wait.h>
45272343Sngie#include <sys/param.h>
46272343Sngie#define MSDOSFS
47272343Sngie#include <sys/mount.h>
48272343Sngie#include <ufs/ufs/ufsmount.h>
49272343Sngie#include <msdosfs/msdosfsmount.h>
50272343Sngie#undef MSDOSFS
51272343Sngie#include <sys/stat.h>
52272343Sngie#include <sys/sysctl.h>
53272343Sngie#include <unistd.h>
54272343Sngie
55272343Sngiestatic void	create_termcap(void);
56272343Sngiestatic void	fixit_common(void);
57272343Sngie#ifdef SAVE_USERCONFIG
58272343Sngiestatic void	save_userconfig_to_kernel(char *);
59272343Sngie#endif
60272343Sngie
61272343Sngie#define TERMCAP_FILE	"/usr/share/misc/termcap"
62272343Sngie
63272343Sngiestatic void	installConfigure(void);
64272343Sngie
65272343SngieBoolean
66272343SngiecheckLabels(Boolean whinge, Chunk **rdev, Chunk **sdev, Chunk **udev, Chunk **vdev)
67272343Sngie{
68272343Sngie    Device **devs;
69272343Sngie    Boolean status;
70272343Sngie    Disk *disk;
71272343Sngie    Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev;
72272343Sngie    int i;
73272343Sngie
74272343Sngie    /* Don't allow whinging if noWarn is set */
75272343Sngie    if (variable_get(VAR_NO_WARN))
76272343Sngie	whinge = FALSE;
77272343Sngie
78272343Sngie    status = TRUE;
79272343Sngie    *rdev = *sdev = *udev = *vdev = rootdev = swapdev = usrdev = vardev = NULL;
80272343Sngie
81272343Sngie    /* We don't need to worry about root/usr/swap if we're already multiuser */
82272343Sngie    if (!RunningAsInit)
83272343Sngie	return status;
84272343Sngie
85272343Sngie    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
86272343Sngie    /* First verify that we have a root device */
87272343Sngie    for (i = 0; devs[i]; i++) {
88272343Sngie	if (!devs[i]->enabled)
89272343Sngie	    continue;
90272343Sngie	disk = (Disk *)devs[i]->private;
91272343Sngie	msgDebug("Scanning disk %s for root filesystem\n", disk->name);
92272343Sngie	if (!disk->chunks)
93272343Sngie	    msgFatal("No chunk list found for %s!", disk->name);
94272343Sngie	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
95272343Sngie	    if (c1->type == freebsd) {
96272343Sngie		for (c2 = c1->part; c2; c2 = c2->next) {
97272343Sngie		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
98272343Sngie			if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/")) {
99272343Sngie			    if (rootdev) {
100272343Sngie				if (whinge)
101272343Sngie				    msgConfirm("WARNING:  You have more than one root device set?!\n"
102272343Sngie					       "Using the first one found.");
103272343Sngie				continue;
104272343Sngie			    }
105272343Sngie			    else {
106272343Sngie				rootdev = c2;
107272343Sngie				if (isDebug())
108272343Sngie				    msgDebug("Found rootdev at %s!\n", rootdev->name);
109272343Sngie			    }
110272343Sngie			}
111272343Sngie			else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/usr")) {
112272343Sngie			    if (usrdev) {
113272343Sngie				if (whinge)
114272343Sngie				    msgConfirm("WARNING:  You have more than one /usr filesystem.\n"
115272343Sngie					       "Using the first one found.");
116272343Sngie				continue;
117272343Sngie			    }
118272343Sngie			    else {
119272343Sngie				usrdev = c2;
120272343Sngie				if (isDebug())
121272343Sngie				    msgDebug("Found usrdev at %s!\n", usrdev->name);
122272343Sngie			    }
123272343Sngie			}
124272343Sngie			else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/var")) {
125272343Sngie			    if (vardev) {
126272343Sngie				if (whinge)
127272343Sngie				    msgConfirm("WARNING:  You have more than one /var filesystem.\n"
128272343Sngie					       "Using the first one found.");
129272343Sngie				continue;
130272343Sngie			    }
131272343Sngie			    else {
132272343Sngie				vardev = c2;
133272343Sngie				if (isDebug())
134272343Sngie				    msgDebug("Found vardev at %s!\n", vardev->name);
135272343Sngie			    }
136272343Sngie			}
137272343Sngie		    }
138272343Sngie		}
139272343Sngie	    }
140272343Sngie	}
141272343Sngie    }
142272343Sngie
143272343Sngie    /* Now check for swap devices */
144272343Sngie    for (i = 0; devs[i]; i++) {
145272343Sngie	if (!devs[i]->enabled)
146272343Sngie	    continue;
147272343Sngie	disk = (Disk *)devs[i]->private;
148272343Sngie	msgDebug("Scanning disk %s for swap partitions\n", disk->name);
149272343Sngie	if (!disk->chunks)
150272343Sngie	    msgFatal("No chunk list found for %s!", disk->name);
151272343Sngie	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
152272343Sngie	    if (c1->type == freebsd) {
153272343Sngie		for (c2 = c1->part; c2; c2 = c2->next) {
154272343Sngie		    if (c2->type == part && c2->subtype == FS_SWAP && !swapdev) {
155272343Sngie			swapdev = c2;
156272343Sngie			if (isDebug())
157272343Sngie			    msgDebug("Found swapdev at %s!\n", swapdev->name);
158272343Sngie			break;
159272343Sngie		    }
160272343Sngie		}
161272343Sngie	    }
162272343Sngie	}
163272343Sngie    }
164272343Sngie
165272343Sngie    /* Copy our values over */
166272343Sngie    *rdev = rootdev;
167272343Sngie    *sdev = swapdev;
168272343Sngie    *udev = usrdev;
169272343Sngie    *vdev = vardev;
170272343Sngie
171272343Sngie    if (!rootdev && whinge) {
172272343Sngie	msgConfirm("No root device found - you must label a partition as /\n"
173272343Sngie		   "in the label editor.");
174272343Sngie	status = FALSE;
175272343Sngie    }
176272343Sngie    if (!swapdev && whinge) {
177272343Sngie	msgConfirm("No swap devices found - you must create at least one\n"
178272343Sngie		   "swap partition.");
179272343Sngie	status = FALSE;
180272343Sngie    }
181272343Sngie    if (!usrdev && whinge && !variable_get(VAR_NO_USR)) {
182272343Sngie	msgConfirm("WARNING:  No /usr filesystem found.  This is not technically\n"
183272343Sngie		   "an error if your root filesystem is big enough (or you later\n"
184272343Sngie		   "intend to mount your /usr filesystem over NFS), but it may otherwise\n"
185272343Sngie		   "cause you trouble if you're not exactly sure what you are doing!");
186272343Sngie    }
187272343Sngie    if (!vardev && whinge && variable_cmp(SYSTEM_STATE, "upgrade")) {
188272343Sngie	msgConfirm("WARNING:  No /var filesystem found.  This is not technically\n"
189272343Sngie		   "an error if your root filesystem is big enough (or you later\n"
190272343Sngie		   "intend to link /var to someplace else), but it may otherwise\n"
191272343Sngie		   "cause your root filesystem to fill up if you receive lots of mail\n"
192272343Sngie		   "or edit large temporary files.");
193272343Sngie    }
194272343Sngie    return status;
195272343Sngie}
196272343Sngie
197272343Sngiestatic int
198272343SngieinstallInitial(void)
199272343Sngie{
200272343Sngie    static Boolean alreadyDone = FALSE;
201272343Sngie    int status = DITEM_SUCCESS;
202272343Sngie
203272343Sngie    if (alreadyDone)
204272343Sngie	return DITEM_SUCCESS;
205272343Sngie
206272343Sngie    if (!variable_get(DISK_LABELLED)) {
207272343Sngie	msgConfirm("You need to assign disk labels before you can proceed with\n"
208272343Sngie		   "the installation.");
209272343Sngie	return DITEM_FAILURE;
210272343Sngie    }
211272343Sngie    /* If it's labelled, assume it's also partitioned */
212272343Sngie    if (!variable_get(DISK_PARTITIONED))
213272343Sngie	variable_set2(DISK_PARTITIONED, "yes");
214272343Sngie
215272343Sngie    /* If we refuse to proceed, bail. */
216272343Sngie    dialog_clear_norefresh();
217272343Sngie    if (!variable_get(VAR_NO_WARN))
218272343Sngie	if (msgYesNo(
219272343Sngie	    "Last Chance!  Are you SURE you want continue the installation?\n\n"
220272343Sngie	     "If you're running this on a disk with data you wish to save\n"
221272343Sngie	     "then WE STRONGLY ENCOURAGE YOU TO MAKE PROPER BACKUPS before\n"
222272343Sngie	     "proceeding!\n\n"
223272343Sngie	     "We can take no responsibility for lost disk contents!") != 0)
224272343Sngie	return DITEM_FAILURE | DITEM_RESTORE;
225272343Sngie
226272343Sngie    if (DITEM_STATUS(diskLabelCommit(NULL)) != DITEM_SUCCESS) {
227272343Sngie	msgConfirm("Couldn't make filesystems properly.  Aborting.");
228272343Sngie	return DITEM_FAILURE;
229272343Sngie    }
230272343Sngie
231272343Sngie    if (!copySelf()) {
232272343Sngie	msgConfirm("installInitial: Couldn't clone the boot floppy onto the\n"
233272343Sngie		   "root file system.  Aborting!");
234272343Sngie	return DITEM_FAILURE;
235272343Sngie    }
236272343Sngie
237272343Sngie    if (chroot("/mnt") == -1) {
238272343Sngie	msgConfirm("installInitial: Unable to chroot to %s - this is bad!",
239272343Sngie		   "/mnt");
240272343Sngie	return DITEM_FAILURE;
241272343Sngie    }
242272343Sngie
243272343Sngie    chdir("/");
244272343Sngie    variable_set2(RUNNING_ON_ROOT, "yes");
245272343Sngie
246272343Sngie    /* Configure various files in /etc */
247272343Sngie    if (DITEM_STATUS(configResolv(NULL)) == DITEM_FAILURE)
248272343Sngie	status = DITEM_FAILURE;
249272343Sngie    if (DITEM_STATUS(configFstab(NULL)) == DITEM_FAILURE)
250272343Sngie	status = DITEM_FAILURE;
251272343Sngie
252272343Sngie    /* stick a helpful shell over on the 4th VTY */
253272343Sngie    systemCreateHoloshell();
254272343Sngie
255272343Sngie    alreadyDone = TRUE;
256272343Sngie    return status;
257272343Sngie}
258272343Sngie
259272343Sngieint
260272343SngieinstallFixitHoloShell(dialogMenuItem *self)
261272343Sngie{
262272343Sngie    systemCreateHoloshell();
263272343Sngie    return DITEM_SUCCESS;
264272343Sngie}
265272343Sngie
266272343Sngieint
267272343SngieinstallFixitCDROM(dialogMenuItem *self)
268272343Sngie{
269272343Sngie    struct stat sb;
270272343Sngie
271272343Sngie    if (!RunningAsInit)
272272343Sngie	return DITEM_SUCCESS;
273272343Sngie
274272343Sngie    variable_set2(SYSTEM_STATE, "fixit");
275272343Sngie    (void)unlink("/mnt2");
276272343Sngie    (void)rmdir("/mnt2");
277272343Sngie
278272343Sngie    while (1) {
279272343Sngie	msgConfirm("Please insert the second FreeBSD CDROM and press return");
280272343Sngie	if (DITEM_STATUS(mediaSetCDROM(NULL)) != DITEM_SUCCESS || !mediaDevice || !mediaDevice->init(mediaDevice)) {
281272343Sngie	    /* If we can't initialize it, it's probably not a FreeBSD CDROM so punt on it */
282272343Sngie	    if (mediaDevice) {
283272343Sngie		mediaDevice->shutdown(mediaDevice);
284272343Sngie		mediaDevice = NULL;
285272343Sngie	    }
286272343Sngie	    if (msgYesNo("Unable to mount the CDROM - do you want to try again?") != 0)
287272343Sngie		return DITEM_FAILURE;
288272343Sngie	}
289272343Sngie	else
290272343Sngie	    break;
291272343Sngie    }
292272343Sngie
293272343Sngie    /* Since the fixit code expects everything to be in /mnt2, and the CDROM mounting stuff /dist, do
294272343Sngie     * a little kludge dance here..
295272343Sngie     */
296272343Sngie    if (symlink("/dist", "/mnt2")) {
297272343Sngie	msgConfirm("Unable to symlink /mnt2 to the CDROM mount point.  Please report this\n"
298272343Sngie		   "unexpected failure to freebsd-bugs@FreeBSD.org.");
299272343Sngie	return DITEM_FAILURE;
300272343Sngie    }
301272343Sngie
302272343Sngie    /*
303272343Sngie     * If /tmp points to /mnt2/tmp from a previous fixit floppy session, it's
304272343Sngie     * not very good for us if we point it to the CDROM now.  Rather make it
305272343Sngie     * a directory in the root MFS then.  Experienced admins will still be
306272343Sngie     * able to mount their disk's /tmp over this if they need.
307272343Sngie     */
308272343Sngie    if (lstat("/tmp", &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFLNK)
309272343Sngie	(void)unlink("/tmp");
310272343Sngie    Mkdir("/tmp");
311272343Sngie
312272343Sngie    /*
313272343Sngie     * Since setuid binaries ignore LD_LIBRARY_PATH, we indeed need the
314272343Sngie     * ld.so.hints file.  Fortunately, it's fairly small (~ 3 KB).
315272343Sngie     */
316272343Sngie    if (!file_readable("/var/run/ld.so.hints")) {
317272343Sngie	Mkdir("/var/run");
318272343Sngie	if (vsystem("/mnt2/sbin/ldconfig -s /mnt2/usr/lib")) {
319272343Sngie	    msgConfirm("Warning: ldconfig could not create the ld.so hints file.\n"
320272343Sngie		       "Dynamic executables from the CDROM likely won't work.");
321272343Sngie	}
322272343Sngie    }
323272343Sngie
324272343Sngie    /* Yet another iggly hardcoded pathname. */
325272343Sngie    if (!file_readable("/usr/libexec/ld.so")) {
326272343Sngie	Mkdir("/usr/libexec");
327272343Sngie	if (symlink("/mnt2/usr/libexec/ld.so", "/usr/libexec/ld.so")) {
328272343Sngie	    msgConfirm("Warning: could not create the symlink for ld.so.\n"
329272343Sngie		       "Dynamic executables from the CDROM likely won't work.");
330272343Sngie	}
331272343Sngie    }
332272343Sngie
333272343Sngie    fixit_common();
334272343Sngie
335272343Sngie    mediaDevice->shutdown(mediaDevice);
336272343Sngie    msgConfirm("Please remove the FreeBSD CDROM now.");
337272343Sngie    return DITEM_SUCCESS;
338272343Sngie}
339272343Sngie
340272343Sngieint
341272343SngieinstallFixitFloppy(dialogMenuItem *self)
342272343Sngie{
343272343Sngie    struct ufs_args args;
344272343Sngie
345272343Sngie    if (!RunningAsInit)
346272343Sngie	return DITEM_SUCCESS;
347272343Sngie
348272343Sngie    variable_set2(SYSTEM_STATE, "fixit");
349272343Sngie    Mkdir("/mnt2");
350272343Sngie
351272343Sngie    /* Try to open the floppy drive */
352272343Sngie    if (DITEM_STATUS(mediaSetFloppy(NULL)) == DITEM_FAILURE) {
353272343Sngie	msgConfirm("Unable to set media device to floppy.");
354272343Sngie	mediaClose();
355272343Sngie	return DITEM_FAILURE;
356    }
357
358    memset(&args, 0, sizeof(args));
359    args.fspec = mediaDevice->devname;
360
361    while (1) {
362	msgConfirm("Please insert a writable fixit floppy and press return");
363	mediaDevice->private = "/mnt2";
364	if (!mediaDevice->init(mediaDevice)) {
365	    if (msgYesNo("The attempt to mount the fixit floppy failed, bad floppy\n"
366			 "or unclean filesystem.  Do you want to try again?"))
367		return DITEM_FAILURE;
368	}
369	else
370	    break;
371    }
372    if (!directory_exists("/tmp"))
373	(void)symlink("/mnt2/tmp", "/tmp");
374    fixit_common();
375    mediaDevice->shutdown(mediaDevice);
376    mediaDevice = NULL;
377    msgConfirm("Please remove the fixit floppy now.");
378    return DITEM_SUCCESS;
379}
380
381/*
382 * The common code for both fixit variants.
383 */
384static void
385fixit_common(void)
386{
387    pid_t child;
388    int waitstatus;
389
390    if (!directory_exists("/var/tmp/vi.recover")) {
391	if (DITEM_STATUS(Mkdir("/var/tmp/vi.recover")) != DITEM_SUCCESS) {
392	    msgConfirm("Warning:  Was unable to create a /var/tmp/vi.recover directory.\n"
393		       "vi will kvetch and moan about it as a result but should still\n"
394		       "be essentially usable.");
395	}
396    }
397    if (!directory_exists("/bin"))
398	(void)Mkdir("/bin");
399    (void)symlink("/stand/sh", "/bin/sh");
400    /* Link the /etc/ files */
401    if (DITEM_STATUS(Mkdir("/etc")) != DITEM_SUCCESS)
402	msgConfirm("Unable to create an /etc directory!  Things are weird on this floppy..");
403    else if ((symlink("/mnt2/etc/spwd.db", "/etc/spwd.db") == -1 && errno != EEXIST) ||
404	     (symlink("/mnt2/etc/protocols", "/etc/protocols") == -1 && errno != EEXIST) ||
405	     (symlink("/mnt2/etc/services", "/etc/services") == -1 && errno != EEXIST))
406	msgConfirm("Couldn't symlink the /etc/ files!  I'm not sure I like this..");
407    if (!file_readable(TERMCAP_FILE))
408	create_termcap();
409    if (!(child = fork())) {
410	int i, fd;
411	struct termios foo;
412	extern int login_tty(int);
413
414	ioctl(0, TIOCNOTTY, NULL);
415	for (i = getdtablesize(); i >= 0; --i)
416	    close(i);
417	fd = open("/dev/ttyv3", O_RDWR);
418	ioctl(0, TIOCSCTTY, &fd);
419	dup2(0, 1);
420	dup2(0, 2);
421	DebugFD = 2;
422	if (login_tty(fd) == -1)
423	    msgDebug("fixit: I can't set the controlling terminal.\n");
424
425	signal(SIGTTOU, SIG_IGN);
426	if (tcgetattr(0, &foo) != -1) {
427	    foo.c_cc[VERASE] = '\010';
428	    if (tcsetattr(0, TCSANOW, &foo) == -1)
429		msgDebug("fixit shell: Unable to set erase character.\n");
430	}
431	else
432	    msgDebug("fixit shell: Unable to get terminal attributes!\n");
433	setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/stand:"
434	       "/mnt2/stand:/mnt2/bin:/mnt2/sbin:/mnt2/usr/bin:/mnt2/usr/sbin", 1);
435	/* use the .profile from the fixit medium */
436	setenv("HOME", "/mnt2", 1);
437	chdir("/mnt2");
438	execlp("sh", "-sh", 0);
439	msgDebug("fixit shell: Failed to execute shell!\n");
440	_exit(1);;
441    }
442    else {
443	msgNotify("Waiting for fixit shell to exit.  Go to VTY4 now by\n"
444		  "typing ALT-F4.  When you are done, type ``exit'' to exit\n"
445		  "the fixit shell and be returned here.");
446	(void)waitpid(child, &waitstatus, 0);
447    }
448    dialog_clear();
449}
450
451
452int
453installExpress(dialogMenuItem *self)
454{
455    int i;
456
457    variable_set2(SYSTEM_STATE, "express");
458    if (DITEM_STATUS((i = diskPartitionEditor(self))) == DITEM_FAILURE)
459	return i;
460
461    if (DITEM_STATUS((i = diskLabelEditor(self))) == DITEM_FAILURE)
462	return i;
463
464    dialog_clear_norefresh();
465    if (DITEM_STATUS((i = installCommit(self))) == DITEM_SUCCESS) {
466	i |= DITEM_LEAVE_MENU;
467	/* Give user the option of one last configuration spree */
468	installConfigure();
469    }
470    return i | DITEM_RESTORE;
471}
472
473/* Novice mode installation */
474int
475installNovice(dialogMenuItem *self)
476{
477    int i, tries = 0;
478    Device **devs;
479
480    variable_set2(SYSTEM_STATE, "novice");
481    dialog_clear_norefresh();
482    msgConfirm("In the next menu, you will need to set up a DOS-style (\"fdisk\") partitioning\n"
483	       "scheme for your hard disk.  If you simply wish to devote all disk space\n"
484	       "to FreeBSD (overwriting anything else that might be on the disk(s) selected)\n"
485	       "then use the (A)ll command to select the default partitioning scheme followed\n"
486	       "by a (Q)uit.  If you wish to allocate only free space to FreeBSD, move to a\n"
487	       "partition marked \"unused\" and use the (C)reate command.");
488
489nodisks:
490    if (DITEM_STATUS(diskPartitionEditor(self)) == DITEM_FAILURE)
491	return DITEM_FAILURE;
492
493    if (diskGetSelectCount(&devs) <= 0 && tries < 3) {
494	msgConfirm("You need to select some disks to operate on!  Be sure to use SPACE\n"
495		   "instead of RETURN in the disk selection menu when selecting a disk.");
496	++tries;
497	goto nodisks;
498    }
499
500    dialog_clear_norefresh();
501    msgConfirm("Next, you need to create BSD partitions inside of the fdisk partition(s)\n"
502	       "just created.  If you have a reasonable amount of disk space (200MB or more)\n"
503	       "and don't have any special requirements, simply use the (A)uto command to\n"
504	       "allocate space automatically.  If you have more specific needs or just don't\n"
505	       "care for the layout chosen by (A)uto, press F1 for more information on\n"
506	       "manual layout.");
507
508    if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE)
509	return DITEM_FAILURE;
510
511    dialog_clear_norefresh();
512    if (DITEM_STATUS((i = installCommit(self))) == DITEM_FAILURE) {
513	dialog_clear_norefresh();
514	msgConfirm("Installation completed with some errors.  You may wish to\n"
515		   "scroll through the debugging messages on VTY1 with the\n"
516		   "scroll-lock feature.  You can also chose \"No\" at the next\n"
517		   "prompt and go back into the installation menus to try and retry\n"
518		   "whichever operations have failed.");
519	return i | DITEM_RESTORE;
520
521    }
522    else {
523	dialog_clear_norefresh();
524	msgConfirm("Congratulations!  You now have FreeBSD installed on your system.\n\n"
525		   "We will now move on to the final configuration questions.\n"
526		   "For any option you do not wish to configure, simply select\n"
527		   "No.\n\n"
528		   "If you wish to re-enter this utility after the system is up, you\n"
529		   "may do so by typing: /stand/sysinstall.");
530    }
531    if (mediaDevice->type != DEVICE_TYPE_FTP && mediaDevice->type != DEVICE_TYPE_NFS) {
532	if (!msgYesNo("Would you like to configure any Ethernet or SLIP/PPP network devices?")) {
533	    Device *tmp;
534
535	    dialog_clear_norefresh();
536	    tmp = tcpDeviceSelect();
537	    dialog_clear_norefresh();
538	    if (tmp && !msgYesNo("Would you like to bring the %s interface up right now?", tmp->name))
539		if (!tmp->init(tmp))
540		    msgConfirm("Initialization of %s device failed.", tmp->name);
541	}
542    }
543
544    dialog_clear_norefresh();
545    if (!msgYesNo("Will this machine be an IP gateway (e.g. will it forward packets\n"
546		  "between interfaces)?"))
547	variable_set2("gateway_enable", "YES");
548
549    dialog_clear_norefresh();
550    if (!msgYesNo("Do you want to allow anonymous FTP connections to this machine?"))
551	configAnonFTP(self);
552
553    dialog_clear_norefresh();
554    if (!msgYesNo("Do you want to configure this machine as an NFS server?"))
555	configNFSServer(self);
556
557    dialog_clear_norefresh();
558    if (!msgYesNo("Do you want to configure this machine as an NFS client?"))
559	variable_set2("nfs_client_enable", "YES");
560
561    dialog_clear_norefresh();
562    if (!msgYesNo("Would you like to customize your system console settings?")) {
563	WINDOW *w = savescr();
564
565	dmenuOpenSimple(&MenuSyscons, FALSE);
566	restorescr(w);
567    }
568
569    dialog_clear_norefresh();
570    if (!msgYesNo("Would you like to set this machine's time zone now?")) {
571	WINDOW *w = savescr();
572
573	dialog_clear();
574	systemExecute("tzsetup");
575	restorescr(w);
576    }
577
578    dialog_clear_norefresh();
579    if (!msgYesNo("Does this system have a mouse attached to it?")) {
580	WINDOW *w = savescr();
581
582	dmenuOpenSimple(&MenuMouse, FALSE);
583	restorescr(w);
584    }
585
586    /* Now would be a good time to checkpoint the configuration data */
587    configRC_conf("/etc/rc.conf");
588    sync();
589
590    if (directory_exists("/usr/X11R6")) {
591	dialog_clear_norefresh();
592	if (!msgYesNo("Would you like to configure your X server at this time?"))
593	    configXEnvironment(self);
594    }
595
596    dialog_clear_norefresh();
597    if (!msgYesNo("The FreeBSD package collection is a collection of hundreds of ready-to-run\n"
598		  "applications, from text editors to games to WEB servers and more.  Would you\n"
599		  "like to browse the collection now?"))
600	configPackages(self);
601
602    dialog_clear_norefresh();
603    if (!msgYesNo("Would you like to add any initial user accounts to the system?\n"
604		  "Adding at least one account for yourself at this stage is suggested\n"
605		  "since working as the \"root\" user is dangerous (it is easy to do\n"
606		  "things which adversely affect the entire system)."))
607	configUsers(self);
608
609    dialog_clear_norefresh();
610    msgConfirm("Now you must set the system manager's password.\n"
611	       "This is the password you'll use to log in as \"root\".");
612    {
613	WINDOW *w = savescr();
614
615	if (!systemExecute("passwd root"))
616	    variable_set2("root_password", "YES");
617	restorescr(w);
618    }
619
620    dialog_clear_norefresh();
621    if (!msgYesNo("Would you like to register your FreeBSD system at this time?\n\n"
622		  "PLEASE, take just 5 minutes to do this.  If we're ever to get any\n"
623		  "significant base of commercial software for FreeBSD, we need to\n"
624		  "be able to provide more information about the size of our user community.\n"
625		  "This is where your registration can really help us, and you can also\n"
626		  "sign up for the new FreeBSD newsletter (its free!) at the same time.\n"))
627	configRegister(NULL);
628    else {
629	dialog_clear_norefresh();
630	msgConfirm("OK, but if you should change your mind then you always can register\n"
631		   "later by typing ``/stand/sysinstall register'' or by simply visiting our\n"
632		   "web site at http://www.freebsd.org/register.html");
633
634    }
635    /* XXX Put whatever other nice configuration questions you'd like to ask the user here XXX */
636
637    /* Give user the option of one last configuration spree */
638    dialog_clear_norefresh();
639    installConfigure();
640
641    return DITEM_LEAVE_MENU | DITEM_RESTORE;
642}
643
644/* The version of commit we call from the Install Custom menu */
645int
646installCustomCommit(dialogMenuItem *self)
647{
648    int i;
649
650    dialog_clear_norefresh();
651    i = installCommit(self);
652    if (DITEM_STATUS(i) == DITEM_SUCCESS) {
653	/* Give user the option of one last configuration spree */
654	installConfigure();
655	return i;
656    }
657    else
658	msgConfirm("The commit operation completed with errors.  Not\n"
659		   "updating /etc files.");
660    return i;
661}
662
663/*
664 * What happens when we finally decide to going ahead with the installation.
665 *
666 * This is broken into multiple stages so that the user can do a full
667 * installation but come back here again to load more distributions,
668 * perhaps from a different media type.  This would allow, for
669 * example, the user to load the majority of the system from CDROM and
670 * then use ftp to load just the DES dist.
671 */
672int
673installCommit(dialogMenuItem *self)
674{
675    int i;
676    char *str;
677
678    if (!Dists)
679	distConfig(NULL);
680
681    if (!Dists)
682	if (!dmenuOpenSimple(&MenuDistributions, FALSE) && !Dists)
683	    return DITEM_FAILURE | DITEM_RESTORE;
684
685    if (!mediaVerify())
686	return DITEM_FAILURE | DITEM_RESTORE;
687
688    str = variable_get(SYSTEM_STATE);
689    if (isDebug())
690	msgDebug("installCommit: System state is `%s'\n", str);
691
692    /* Installation stuff we wouldn't do to a running system */
693    if (RunningAsInit && DITEM_STATUS((i = installInitial())) == DITEM_FAILURE)
694	return i;
695
696try_media:
697    if (!mediaDevice->init(mediaDevice)) {
698	if (!msgYesNo("Unable to initialize selected media. Would you like to\n"
699		      "adjust your media configuration and try again?")) {
700	    mediaDevice = NULL;
701	    if (!mediaVerify())
702		return DITEM_FAILURE | DITEM_RESTORE;
703	    else
704		goto try_media;
705	}
706	else
707	    return DITEM_FAILURE | DITEM_RESTORE;
708    }
709
710    /* Now go get it all */
711    i = distExtractAll(self);
712
713    /* When running as init, *now* it's safe to grab the rc.foo vars */
714    installEnvironment();
715
716    variable_set2(SYSTEM_STATE, DITEM_STATUS(i) == DITEM_FAILURE ? "error-install" : "full-install");
717
718    return i | DITEM_RESTORE;
719}
720
721static void
722installConfigure(void)
723{
724    /* Final menu of last resort */
725    dialog_clear_norefresh();
726    if (!msgYesNo("Visit the general configuration menu for a chance to set\n"
727		  "any last options?")) {
728	WINDOW *w = savescr();
729
730	dmenuOpenSimple(&MenuConfigure, FALSE);
731	restorescr(w);
732    }
733    configRC_conf("/etc/rc.conf");
734    sync();
735}
736
737int
738installFixupBin(dialogMenuItem *self)
739{
740    Device **devs;
741    int i;
742
743    /* All of this is done only as init, just to be safe */
744    if (RunningAsInit) {
745	/* Fix up kernel first */
746	if (!file_readable("/kernel")) {
747	    if (file_readable("/kernel.GENERIC")) {
748		if (vsystem("cp -p /kernel.GENERIC /kernel")) {
749		    msgConfirm("Unable to copy /kernel into place!");
750		    return DITEM_FAILURE;
751		}
752#ifdef SAVE_USERCONFIG
753		/* Snapshot any boot -c changes back to the new kernel */
754		save_userconfig_to_kernel("/kernel");
755#endif
756	    }
757	    else {
758		msgConfirm("Can't find a kernel image to link to on the root file system!\n"
759			   "You're going to have a hard time getting this system to\n"
760			   "boot from the hard disk, I'm afraid!");
761		return DITEM_FAILURE;
762	    }
763	}
764
765	/* BOGON #1: Resurrect /dev after bin distribution screws it up */
766	msgNotify("Remaking all devices.. Please wait!");
767	if (vsystem("cd /dev; sh MAKEDEV all")) {
768	    msgConfirm("MAKEDEV returned non-zero status");
769	    return DITEM_FAILURE;
770	}
771
772	msgNotify("Resurrecting /dev entries for slices..");
773	devs = deviceFind(NULL, DEVICE_TYPE_DISK);
774	if (!devs)
775	    msgFatal("Couldn't get a disk device list!");
776
777	/* Resurrect the slices that the former clobbered */
778	for (i = 0; devs[i]; i++) {
779	    Disk *disk = (Disk *)devs[i]->private;
780	    Chunk *c1;
781
782	    if (!devs[i]->enabled)
783		continue;
784	    if (!disk->chunks)
785		msgFatal("No chunk list found for %s!", disk->name);
786	    for (c1 = disk->chunks->part; c1; c1 = c1->next) {
787		if (c1->type == freebsd) {
788		    msgNotify("Making slice entries for %s", c1->name);
789		    if (vsystem("cd /dev; sh MAKEDEV %sh", c1->name)) {
790			msgConfirm("Unable to make slice entries for %s!", c1->name);
791			return DITEM_FAILURE;
792		    }
793		}
794	    }
795	}
796
797	/* BOGON #2: We leave /etc in a bad state */
798	chmod("/etc", 0755);
799
800	/* BOGON #3: No /var/db/mountdtab complains */
801	Mkdir("/var/db");
802	creat("/var/db/mountdtab", 0644);
803
804	/* BOGON #4: /compat created by default in root fs */
805	Mkdir("/usr/compat");
806	vsystem("ln -s /usr/compat /compat");
807
808	/* BOGON #5: aliases database not build for bin */
809	vsystem("newaliases");
810
811	/* BOGON #6: deal with new boot files */
812	vsystem("touch /kernel.config");
813	vsystem("touch /boot.config");
814	if (file_readable("/stand/boot.help") && !file_readable("/boot.help"))
815	    vsystem("mv /stand/boot.help /");
816
817	/* Now run all the mtree stuff to fix things up */
818        vsystem("mtree -deU -f /etc/mtree/BSD.root.dist -p /");
819        vsystem("mtree -deU -f /etc/mtree/BSD.var.dist -p /var");
820        vsystem("mtree -deU -f /etc/mtree/BSD.usr.dist -p /usr");
821
822	/* Do all the last ugly work-arounds here */
823    }
824    return DITEM_SUCCESS;
825}
826
827/* Fix side-effects from the the XFree86 installation */
828int
829installFixupXFree(dialogMenuItem *self)
830{
831    /* BOGON #1:  XFree86 requires various specialized fixups */
832    if (directory_exists("/usr/X11R6")) {
833	msgNotify("Fixing permissions in XFree86 tree..");
834	vsystem("chmod -R a+r /usr/X11R6");
835	vsystem("find /usr/X11R6 -type d | xargs chmod a+x");
836
837	/* Also do bogus minimal package registration so ports don't whine */
838	if (file_readable("/usr/X11R6/lib/X11/pkgreg.tar.gz")) {
839	    msgNotify("Installing package metainfo..");
840	    vsystem("tar xpzf /usr/X11R6/lib/X11/pkgreg.tar.gz -C / && rm /usr/X11R6/lib/X11/pkgreg.tar.gz");
841	}
842    }
843    return DITEM_SUCCESS;
844}
845
846/* Go newfs and/or mount all the filesystems we've been asked to */
847int
848installFilesystems(dialogMenuItem *self)
849{
850    int i;
851    Disk *disk;
852    Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev;
853    Device **devs;
854    PartInfo *root;
855    char dname[80];
856    extern int MakeDevChunk(Chunk *c, char *n);
857    Boolean upgrade = FALSE;
858
859    /* If we've already done this, bail out */
860    if (!variable_cmp(DISK_LABELLED, "written"))
861	return DITEM_SUCCESS;
862
863    upgrade = !variable_cmp(SYSTEM_STATE, "upgrade");
864    if (!checkLabels(TRUE, &rootdev, &swapdev, &usrdev, &vardev))
865	return DITEM_FAILURE;
866
867    if (rootdev)
868	root = (PartInfo *)rootdev->private_data;
869    else
870	root = NULL;
871
872    command_clear();
873    if (swapdev && RunningAsInit) {
874	/* As the very first thing, try to get ourselves some swap space */
875	sprintf(dname, "/dev/%s", swapdev->name);
876	if (!Fake && (!MakeDevChunk(swapdev, "/dev") || !file_readable(dname))) {
877	    msgConfirm("Unable to make device node for %s in /dev!\n"
878		       "The creation of filesystems will be aborted.", dname);
879	    return DITEM_FAILURE;
880	}
881
882	if (!Fake) {
883	    if (!swapon(dname))
884		msgNotify("Added %s as initial swap device", dname);
885	    else
886		msgConfirm("WARNING!  Unable to swap to %s: %s\n"
887			   "This may cause the installation to fail at some point\n"
888			   "if you don't have a lot of memory.", dname, strerror(errno));
889	}
890    }
891
892    if (rootdev && RunningAsInit) {
893	/* Next, create and/or mount the root device */
894	sprintf(dname, "/dev/r%s", rootdev->name);
895	if (!Fake && (!MakeDevChunk(rootdev, "/dev") || !file_readable(dname))) {
896	    msgConfirm("Unable to make device node for %s in /dev!\n"
897		       "The creation of filesystems will be aborted.", dname);
898	    return DITEM_FAILURE;
899	}
900	if (strcmp(root->mountpoint, "/"))
901	    msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", rootdev->name, root->mountpoint);
902
903	if (root->newfs && (!upgrade || !msgYesNo("You are upgrading - are you SURE you want to newfs the root partition?"))) {
904	    int i;
905
906	    msgNotify("Making a new root filesystem on %s", dname);
907	    i = vsystem("%s %s", root->newfs_cmd, dname);
908	    if (i) {
909		msgConfirm("Unable to make new root filesystem on %s!\n"
910			   "Command returned status %d", dname, i);
911		return DITEM_FAILURE;
912	    }
913	}
914	else {
915	    if (!upgrade) {
916		msgConfirm("Warning:  Using existing root partition.  It will be assumed\n"
917			   "that you have the appropriate device entries already in /dev.");
918	    }
919	    msgNotify("Checking integrity of existing %s filesystem.", dname);
920	    i = vsystem("fsck -y %s", dname);
921	    if (i)
922		msgConfirm("Warning: fsck returned status of %d for %s.\n"
923			   "This partition may be unsafe to use.", i, dname);
924	}
925
926	/* Switch to block device */
927	sprintf(dname, "/dev/%s", rootdev->name);
928	if (Mount("/mnt", dname)) {
929	    msgConfirm("Unable to mount the root file system on %s!  Giving up.", dname);
930	    return DITEM_FAILURE;
931	}
932    }
933
934    /* Now buzz through the rest of the partitions and mount them too */
935    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
936    for (i = 0; devs[i]; i++) {
937	if (!devs[i]->enabled)
938	    continue;
939
940	disk = (Disk *)devs[i]->private;
941	if (!disk->chunks) {
942	    msgConfirm("No chunk list found for %s!", disk->name);
943	    return DITEM_FAILURE;
944	}
945	if (RunningAsInit && root && (root->newfs || upgrade)) {
946	    Mkdir("/mnt/dev");
947	    if (!Fake)
948		MakeDevDisk(disk, "/mnt/dev");
949	}
950	else if (!RunningAsInit && !Fake)
951	    MakeDevDisk(disk, "/dev");
952
953	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
954	    if (c1->type == freebsd) {
955		for (c2 = c1->part; c2; c2 = c2->next) {
956		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
957			PartInfo *tmp = (PartInfo *)c2->private_data;
958
959			/* Already did root */
960			if (c2 == rootdev)
961			    continue;
962
963			if (tmp->newfs && (!upgrade || !msgYesNo("You are upgrading - are you SURE you want to newfs /dev/%s?", c2->name)))
964			    command_shell_add(tmp->mountpoint, "%s %s/dev/r%s", tmp->newfs_cmd, RunningAsInit ? "/mnt" : "", c2->name);
965			else
966			    command_shell_add(tmp->mountpoint, "fsck -y %s/dev/r%s", RunningAsInit ? "/mnt" : "", c2->name);
967			command_func_add(tmp->mountpoint, Mount, c2->name);
968		    }
969		    else if (c2->type == part && c2->subtype == FS_SWAP) {
970			char fname[80];
971			int i;
972
973			if (c2 == swapdev)
974			    continue;
975			sprintf(fname, "%s/dev/%s", RunningAsInit ? "/mnt" : "", c2->name);
976			i = (Fake || swapon(fname));
977			if (!i)
978			    msgNotify("Added %s as an additional swap device", fname);
979			else
980			    msgConfirm("Unable to add %s as a swap device: %s", fname, strerror(errno));
981		    }
982		}
983	    }
984	    else if (c1->type == fat && c1->private_data && (root->newfs || upgrade)) {
985		char name[FILENAME_MAX];
986
987		sprintf(name, "%s/%s", RunningAsInit ? "/mnt" : "", ((PartInfo *)c1->private_data)->mountpoint);
988		Mkdir(name);
989	    }
990	}
991    }
992
993    if (RunningAsInit) {
994	msgNotify("Copying initial device files..");
995	/* Copy the boot floppy's dev files */
996	if ((root->newfs || upgrade) && vsystem("find -x /dev | cpio %s -pdum /mnt", cpioVerbosity())) {
997	    msgConfirm("Couldn't clone the /dev files!");
998	    return DITEM_FAILURE;
999	}
1000    }
1001
1002    command_sort();
1003    command_execute();
1004    return DITEM_SUCCESS;
1005}
1006
1007static char *
1008getRelname(void)
1009{
1010    static char buf[64];
1011    int sz = (sizeof buf) - 1;
1012
1013    if (sysctlbyname("kern.osrelease", buf, &sz, NULL, 0) != -1) {
1014	buf[sz] = '\0';
1015	return buf;
1016    }
1017    else
1018	return "<unknown>";
1019}
1020
1021/* Initialize various user-settable values to their defaults */
1022int
1023installVarDefaults(dialogMenuItem *self)
1024{
1025    char *cp;
1026
1027    /* Set default startup options */
1028    variable_set2(VAR_RELNAME,			getRelname());
1029    variable_set2(VAR_CPIO_VERBOSITY,		"high");
1030    variable_set2(VAR_TAPE_BLOCKSIZE,		DEFAULT_TAPE_BLOCKSIZE);
1031    variable_set2(VAR_INSTALL_ROOT,		"/");
1032    variable_set2(VAR_INSTALL_CFG,		"install.cfg");
1033    cp = getenv("EDITOR");
1034    if (!cp)
1035	cp = "/usr/bin/ee";
1036    variable_set2(VAR_EDITOR,			cp);
1037    variable_set2(VAR_FTP_USER,			"ftp");
1038    variable_set2(VAR_BROWSER_PACKAGE,		"lynx");
1039    variable_set2(VAR_BROWSER_BINARY,		"/usr/local/bin/lynx");
1040    variable_set2(VAR_FTP_STATE,		"passive");
1041    variable_set2(VAR_NFS_SECURE,		"YES");
1042    variable_set2(VAR_PKG_TMPDIR,		"/usr/tmp");
1043    variable_set2(VAR_GATED_PKG,		"gated");
1044    variable_set2(VAR_PCNFSD_PKG,		"pcnfsd");
1045    variable_set2(VAR_MEDIA_TIMEOUT,		itoa(MEDIA_TIMEOUT));
1046    if (getpid() != 1)
1047	variable_set2(SYSTEM_STATE,		"update");
1048    else
1049	variable_set2(SYSTEM_STATE,		"init");
1050    return DITEM_SUCCESS;
1051}
1052
1053/* Load the environment up from various system configuration files */
1054void
1055installEnvironment(void)
1056{
1057    if (file_readable("/etc/rc.conf"))
1058	configEnvironmentRC_conf("/etc/rc.conf");
1059    if (file_readable("/etc/resolv.conf"))
1060	configEnvironmentResolv("/etc/resolv.conf");
1061}
1062
1063/* Copy the boot floppy contents into /stand */
1064Boolean
1065copySelf(void)
1066{
1067    int i;
1068
1069    if (file_readable("/boot.help"))
1070	vsystem("cp /boot.help /mnt");
1071    msgWeHaveOutput("Copying the boot floppy to /stand on root filesystem");
1072    i = vsystem("find -x /stand | cpio %s -pdum /mnt", cpioVerbosity());
1073    if (i) {
1074	msgConfirm("Copy returned error status of %d!", i);
1075	return FALSE;
1076    }
1077
1078    /* Copy the /etc files into their rightful place */
1079    if (vsystem("cd /mnt/stand; find etc | cpio %s -pdum /mnt", cpioVerbosity())) {
1080	msgConfirm("Couldn't copy up the /etc files!");
1081	return TRUE;
1082    }
1083    return TRUE;
1084}
1085
1086static void
1087create_termcap(void)
1088{
1089    FILE *fp;
1090
1091    const char *caps[] = {
1092	termcap_vt100, termcap_cons25, termcap_cons25_m, termcap_cons25r,
1093	termcap_cons25r_m, termcap_cons25l1, termcap_cons25l1_m, NULL,
1094    };
1095    const char **cp;
1096
1097    if (!file_readable(TERMCAP_FILE)) {
1098	Mkdir("/usr/share/misc");
1099	fp = fopen(TERMCAP_FILE, "w");
1100	if (!fp) {
1101	    msgConfirm("Unable to initialize termcap file. Some screen-oriented\nutilities may not work.");
1102	    return;
1103	}
1104	cp = caps;
1105	while (*cp)
1106	    fprintf(fp, "%s\n", *(cp++));
1107	fclose(fp);
1108    }
1109}
1110
1111#ifdef SAVE_USERCONFIG
1112static void
1113save_userconfig_to_kernel(char *kern)
1114{
1115    struct kernel *core, *boot;
1116    struct list *c_isa, *b_isa, *c_dev, *b_dev;
1117    int i, d;
1118
1119    if ((core = uc_open("-incore")) == NULL) {
1120	msgDebug("save_userconf: Can't read in-core information for kernel.\n");
1121	return;
1122    }
1123
1124    if ((boot = uc_open(kern)) == NULL) {
1125	msgDebug("save_userconf: Can't read device information for kernel image %s\n", kern);
1126	return;
1127    }
1128
1129    msgNotify("Saving any boot -c changes to new kernel...");
1130    c_isa = uc_getdev(core, "-isa");
1131    b_isa = uc_getdev(boot, "-isa");
1132    if (isDebug())
1133	msgDebug("save_userconf: got %d ISA device entries from core, %d from boot.\n", c_isa->ac, b_isa->ac);
1134    for (d = 0; d < c_isa->ac; d++) {
1135	if (isDebug())
1136	    msgDebug("save_userconf: ISA device loop, c_isa->av[%d] = %s\n", d, c_isa->av[d]);
1137	if (strcmp(c_isa->av[d], "npx0")) { /* special case npx0, which mucks with its id_irq member */
1138	    c_dev = uc_getdev(core, c_isa->av[d]);
1139	    b_dev = uc_getdev(boot, b_isa->av[d]);
1140	    if (!c_dev || !b_dev) {
1141		msgDebug("save_userconf: c_dev: %x b_dev: %x\n", c_dev, b_dev);
1142		continue;
1143	    }
1144	    if (isDebug())
1145		msgDebug("save_userconf: ISA device %s: %d config parameters (core), %d (boot)\n",
1146			 c_isa->av[d], c_dev->ac, b_dev->ac);
1147	    for (i = 0; i < c_dev->ac; i++) {
1148		if (isDebug())
1149		    msgDebug("save_userconf: c_dev->av[%d] = %s, b_dev->av[%d] = %s\n", i, c_dev->av[i], i, b_dev->av[i]);
1150		if (strcmp(c_dev->av[i], b_dev->av[i])) {
1151		    if (isDebug())
1152			msgDebug("save_userconf: %s (boot) -> %s (core)\n",
1153				 c_dev->av[i], b_dev->av[i]);
1154		    isa_setdev(boot, c_dev);
1155		}
1156	    }
1157	}
1158	else {
1159	    if (isDebug())
1160		msgDebug("skipping npx0\n");
1161	}
1162    }
1163    if (isDebug())
1164	msgDebug("Closing kernels\n");
1165    uc_close(core, 0);
1166    uc_close(boot, 1);
1167}
1168#endif
1169