install.c revision 38703
1232922Stheraven/*
2232922Stheraven * The new sysinstall program.
3232922Stheraven *
4232922Stheraven * This is probably the last program in the `sysinstall' line - the next
5232922Stheraven * generation being essentially a complete rewrite.
6232922Stheraven *
7232922Stheraven * $Id: install.c,v 1.209 1998/07/18 09:42:00 jkh Exp $
8232922Stheraven *
9232922Stheraven * Copyright (c) 1995
10232922Stheraven *	Jordan Hubbard.  All rights reserved.
11232922Stheraven *
12232922Stheraven * Redistribution and use in source and binary forms, with or without
13232922Stheraven * modification, are permitted provided that the following conditions
14232922Stheraven * are met:
15232922Stheraven * 1. Redistributions of source code must retain the above copyright
16232922Stheraven *    notice, this list of conditions and the following disclaimer,
17232922Stheraven *    verbatim and that no modifications are made prior to this
18232922Stheraven *    point in the file.
19232922Stheraven * 2. Redistributions in binary form must reproduce the above copyright
20232922Stheraven *    notice, this list of conditions and the following disclaimer in the
21232922Stheraven *    documentation and/or other materials provided with the distribution.
22232922Stheraven *
23232922Stheraven * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
24232922Stheraven * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25232922Stheraven * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26232922Stheraven * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
27227825Stheraven * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28227825Stheraven * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29227825Stheraven * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
30227825Stheraven * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31227825Stheraven * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32233233Stheraven * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33233233Stheraven * SUCH DAMAGE.
34233233Stheraven *
35233233Stheraven */
36227825Stheraven
37233233Stheraven#include "sysinstall.h"
38233233Stheraven#include "uc_main.h"
39233233Stheraven#include <ctype.h>
40233233Stheraven#include <sys/disklabel.h>
41233233Stheraven#include <sys/errno.h>
42233233Stheraven#include <sys/ioctl.h>
43233233Stheraven#include <sys/fcntl.h>
44233233Stheraven#include <sys/wait.h>
45233233Stheraven#include <sys/param.h>
46233233Stheraven#define MSDOSFS
47233233Stheraven#include <sys/mount.h>
48233233Stheraven#include <ufs/ufs/ufsmount.h>
49233233Stheraven#include <msdosfs/msdosfsmount.h>
50233233Stheraven#undef MSDOSFS
51233233Stheraven#include <sys/stat.h>
52233233Stheraven#include <unistd.h>
53233233Stheraven
54233233Stheravenstatic void	create_termcap(void);
55233233Stheravenstatic void	fixit_common(void);
56233233Stheraven#ifdef SAVE_USERCONFIG
57233233Stheravenstatic void	save_userconfig_to_kernel(char *);
58233233Stheraven#endif
59233233Stheraven
60233233Stheraven#define TERMCAP_FILE	"/usr/share/misc/termcap"
61233233Stheraven
62233233Stheravenstatic void	installConfigure(void);
63233233Stheraven
64233233StheravenBoolean
65233233StheravencheckLabels(Boolean whinge, Chunk **rdev, Chunk **sdev, Chunk **udev, Chunk **vdev)
66233233Stheraven{
67233233Stheraven    Device **devs;
68233233Stheraven    Boolean status;
69233233Stheraven    Disk *disk;
70233233Stheraven    Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev;
71233233Stheraven    int i;
72233233Stheraven
73245302Stheraven    /* Don't allow whinging if noWarn is set */
74245302Stheraven    if (variable_get(VAR_NO_WARN))
75245302Stheraven	whinge = FALSE;
76245302Stheraven
77245302Stheraven    status = TRUE;
78245302Stheraven    *rdev = *sdev = *udev = *vdev = rootdev = swapdev = usrdev = vardev = NULL;
79245302Stheraven
80245302Stheraven    /* We don't need to worry about root/usr/swap if we're already multiuser */
81233233Stheraven    if (!RunningAsInit)
82233233Stheraven	return status;
83233233Stheraven
84233233Stheraven    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
85233233Stheraven    /* First verify that we have a root device */
86233233Stheraven    for (i = 0; devs[i]; i++) {
87233233Stheraven	if (!devs[i]->enabled)
88233233Stheraven	    continue;
89233233Stheraven	disk = (Disk *)devs[i]->private;
90233233Stheraven	msgDebug("Scanning disk %s for root filesystem\n", disk->name);
91233233Stheraven	if (!disk->chunks)
92233233Stheraven	    msgFatal("No chunk list found for %s!", disk->name);
93233233Stheraven	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
94233233Stheraven	    if (c1->type == freebsd) {
95233233Stheraven		for (c2 = c1->part; c2; c2 = c2->next) {
96233233Stheraven		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
97233233Stheraven			if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/")) {
98233233Stheraven			    if (rootdev) {
99233233Stheraven				if (whinge)
100233233Stheraven				    msgConfirm("WARNING:  You have more than one root device set?!\n"
101233233Stheraven					       "Using the first one found.");
102233233Stheraven				continue;
103233233Stheraven			    }
104233233Stheraven			    else {
105233233Stheraven				rootdev = c2;
106233233Stheraven				if (isDebug())
107233233Stheraven				    msgDebug("Found rootdev at %s!\n", rootdev->name);
108233233Stheraven			    }
109233233Stheraven			}
110233233Stheraven			else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/usr")) {
111233233Stheraven			    if (usrdev) {
112233233Stheraven				if (whinge)
113227825Stheraven				    msgConfirm("WARNING:  You have more than one /usr filesystem.\n"
114227825Stheraven					       "Using the first one found.");
115227825Stheraven				continue;
116227825Stheraven			    }
117227825Stheraven			    else {
118227825Stheraven				usrdev = c2;
119227825Stheraven				if (isDebug())
120227825Stheraven				    msgDebug("Found usrdev at %s!\n", usrdev->name);
121227825Stheraven			    }
122227825Stheraven			}
123227825Stheraven			else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/var")) {
124227825Stheraven			    if (vardev) {
125227825Stheraven				if (whinge)
126227825Stheraven				    msgConfirm("WARNING:  You have more than one /var filesystem.\n"
127227825Stheraven					       "Using the first one found.");
128227825Stheraven				continue;
129227825Stheraven			    }
130227825Stheraven			    else {
131227825Stheraven				vardev = c2;
132233233Stheraven				if (isDebug())
133227825Stheraven				    msgDebug("Found vardev at %s!\n", vardev->name);
134227825Stheraven			    }
135227825Stheraven			}
136227825Stheraven		    }
137227825Stheraven		}
138227825Stheraven	    }
139227825Stheraven	}
140227825Stheraven    }
141227825Stheraven
142227825Stheraven    /* Now check for swap devices */
143227825Stheraven    for (i = 0; devs[i]; i++) {
144227825Stheraven	if (!devs[i]->enabled)
145227825Stheraven	    continue;
146227825Stheraven	disk = (Disk *)devs[i]->private;
147227825Stheraven	msgDebug("Scanning disk %s for swap partitions\n", disk->name);
148227825Stheraven	if (!disk->chunks)
149227825Stheraven	    msgFatal("No chunk list found for %s!", disk->name);
150227825Stheraven	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
151233233Stheraven	    if (c1->type == freebsd) {
152233233Stheraven		for (c2 = c1->part; c2; c2 = c2->next) {
153233233Stheraven		    if (c2->type == part && c2->subtype == FS_SWAP && !swapdev) {
154233233Stheraven			swapdev = c2;
155233233Stheraven			if (isDebug())
156233233Stheraven			    msgDebug("Found swapdev at %s!\n", swapdev->name);
157227825Stheraven			break;
158227825Stheraven		    }
159227825Stheraven		}
160227825Stheraven	    }
161227825Stheraven	}
162227825Stheraven    }
163227825Stheraven
164227825Stheraven    /* Copy our values over */
165227825Stheraven    *rdev = rootdev;
166227825Stheraven    *sdev = swapdev;
167233233Stheraven    *udev = usrdev;
168233233Stheraven    *vdev = vardev;
169233233Stheraven
170227825Stheraven    if (!rootdev && whinge) {
171227825Stheraven	msgConfirm("No root device found - you must label a partition as /\n"
172227825Stheraven		   "in the label editor.");
173227825Stheraven	status = FALSE;
174227825Stheraven    }
175227825Stheraven    if (!swapdev && whinge) {
176227825Stheraven	msgConfirm("No swap devices found - you must create at least one\n"
177227825Stheraven		   "swap partition.");
178227825Stheraven	status = FALSE;
179227825Stheraven    }
180227825Stheraven    if (!usrdev && whinge && !variable_get(VAR_NO_USR)) {
181227825Stheraven	msgConfirm("WARNING:  No /usr filesystem found.  This is not technically\n"
182227825Stheraven		   "an error if your root filesystem is big enough (or you later\n"
183227825Stheraven		   "intend to mount your /usr filesystem over NFS), but it may otherwise\n"
184227825Stheraven		   "cause you trouble if you're not exactly sure what you are doing!");
185227825Stheraven    }
186227825Stheraven    if (!vardev && whinge && variable_cmp(SYSTEM_STATE, "upgrade")) {
187227825Stheraven	msgConfirm("WARNING:  No /var filesystem found.  This is not technically\n"
188227825Stheraven		   "an error if your root filesystem is big enough (or you later\n"
189227825Stheraven		   "intend to link /var to someplace else), but it may otherwise\n"
190227825Stheraven		   "cause your root filesystem to fill up if you receive lots of mail\n"
191227825Stheraven		   "or edit large temporary files.");
192227825Stheraven    }
193227825Stheraven    return status;
194227825Stheraven}
195227825Stheraven
196227825Stheravenstatic int
197227825StheraveninstallInitial(void)
198227825Stheraven{
199227825Stheraven    static Boolean alreadyDone = FALSE;
200227825Stheraven
201227825Stheraven    if (alreadyDone)
202227825Stheraven	return DITEM_SUCCESS;
203227825Stheraven
204227825Stheraven    if (!variable_get(DISK_LABELLED)) {
205227825Stheraven	msgConfirm("You need to assign disk labels before you can proceed with\n"
206227825Stheraven		   "the installation.");
207227825Stheraven	return DITEM_FAILURE;
208227825Stheraven    }
209227825Stheraven    /* If it's labelled, assume it's also partitioned */
210227825Stheraven    if (!variable_get(DISK_PARTITIONED))
211227825Stheraven	variable_set2(DISK_PARTITIONED, "yes");
212227825Stheraven
213227825Stheraven    /* If we refuse to proceed, bail. */
214227825Stheraven    dialog_clear_norefresh();
215227825Stheraven    if (!variable_get(VAR_NO_WARN))
216227825Stheraven	if (msgYesNo(
217227825Stheraven	    "Last Chance!  Are you SURE you want continue the installation?\n\n"
218227825Stheraven	     "If you're running this on a disk with data you wish to save\n"
219227825Stheraven	     "then WE STRONGLY ENCOURAGE YOU TO MAKE PROPER BACKUPS before\n"
220227825Stheraven	     "proceeding!\n\n"
221227825Stheraven	     "We can take no responsibility for lost disk contents!") != 0)
222227825Stheraven	return DITEM_FAILURE | DITEM_RESTORE;
223227825Stheraven
224227825Stheraven    if (DITEM_STATUS(diskLabelCommit(NULL)) != DITEM_SUCCESS) {
225227825Stheraven	msgConfirm("Couldn't make filesystems properly.  Aborting.");
226227825Stheraven	return DITEM_FAILURE;
227227825Stheraven    }
228227825Stheraven
229227825Stheraven    if (!copySelf()) {
230227825Stheraven	msgConfirm("installInitial: Couldn't clone the boot floppy onto the\n"
231227825Stheraven		   "root file system.  Aborting!");
232227825Stheraven	return DITEM_FAILURE;
233227825Stheraven    }
234227825Stheraven
235227825Stheraven    if (chroot("/mnt") == -1) {
236227825Stheraven	msgConfirm("installInitial: Unable to chroot to %s - this is bad!",
237227825Stheraven		   "/mnt");
238227825Stheraven	return DITEM_FAILURE;
239227825Stheraven    }
240227825Stheraven
241227825Stheraven    chdir("/");
242227825Stheraven    variable_set2(RUNNING_ON_ROOT, "yes");
243227825Stheraven    configResolv();
244227825Stheraven
245227825Stheraven    /* stick a helpful shell over on the 4th VTY */
246227825Stheraven    systemCreateHoloshell();
247227825Stheraven
248227825Stheraven    alreadyDone = TRUE;
249227825Stheraven    return DITEM_SUCCESS;
250233233Stheraven}
251233233Stheraven
252233233Stheravenint
253227825StheraveninstallFixitHoloShell(dialogMenuItem *self)
254227825Stheraven{
255227825Stheraven    systemCreateHoloshell();
256227825Stheraven    return DITEM_SUCCESS;
257227825Stheraven}
258227825Stheraven
259227825Stheravenint
260227825StheraveninstallFixitCDROM(dialogMenuItem *self)
261227825Stheraven{
262227825Stheraven    struct stat sb;
263227825Stheraven
264227825Stheraven    if (!RunningAsInit)
265227825Stheraven	return DITEM_SUCCESS;
266227825Stheraven
267227825Stheraven    variable_set2(SYSTEM_STATE, "fixit");
268227825Stheraven    (void)unlink("/mnt2");
269227825Stheraven    (void)rmdir("/mnt2");
270227825Stheraven
271227825Stheraven    while (1) {
272227825Stheraven	msgConfirm("Please insert the second FreeBSD CDROM and press return");
273227825Stheraven	if (DITEM_STATUS(mediaSetCDROM(NULL)) != DITEM_SUCCESS || !mediaDevice || !mediaDevice->init(mediaDevice)) {
274227825Stheraven	    /* If we can't initialize it, it's probably not a FreeBSD CDROM so punt on it */
275227825Stheraven	    if (mediaDevice) {
276227825Stheraven		mediaDevice->shutdown(mediaDevice);
277227825Stheraven		mediaDevice = NULL;
278227825Stheraven	    }
279227825Stheraven	    if (msgYesNo("Unable to mount the CDROM - do you want to try again?") != 0)
280227825Stheraven		return DITEM_FAILURE;
281227825Stheraven	}
282227825Stheraven	else
283227825Stheraven	    break;
284227825Stheraven    }
285227825Stheraven
286227825Stheraven    /* Since the fixit code expects everything to be in /mnt2, and the CDROM mounting stuff /dist, do
287233233Stheraven     * a little kludge dance here..
288233233Stheraven     */
289233233Stheraven    if (symlink("/dist", "/mnt2")) {
290227825Stheraven	msgConfirm("Unable to symlink /mnt2 to the CDROM mount point.  Please report this\n"
291227825Stheraven		   "unexpected failure to freebsd-bugs@FreeBSD.org.");
292227825Stheraven	return DITEM_FAILURE;
293227825Stheraven    }
294227825Stheraven
295227825Stheraven    /*
296227825Stheraven     * If /tmp points to /mnt2/tmp from a previous fixit floppy session, it's
297227825Stheraven     * not very good for us if we point it to the CDROM now.  Rather make it
298245302Stheraven     * a directory in the root MFS then.  Experienced admins will still be
299227825Stheraven     * able to mount their disk's /tmp over this if they need.
300227825Stheraven     */
301227825Stheraven    if (lstat("/tmp", &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFLNK)
302227825Stheraven	(void)unlink("/tmp");
303227825Stheraven    Mkdir("/tmp");
304227825Stheraven
305227825Stheraven    /*
306227825Stheraven     * Since setuid binaries ignore LD_LIBRARY_PATH, we indeed need the
307227825Stheraven     * ld.so.hints file.  Fortunately, it's fairly small (~ 3 KB).
308227825Stheraven     */
309227825Stheraven    if (!file_readable("/var/run/ld.so.hints")) {
310227825Stheraven	Mkdir("/var/run");
311227825Stheraven	if (vsystem("/mnt2/sbin/ldconfig -s /mnt2/usr/lib")) {
312227825Stheraven	    msgConfirm("Warning: ldconfig could not create the ld.so hints file.\n"
313227825Stheraven		       "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	if (mount("ufs", "/mnt2", 0, (caddr_t)&args) != -1)
357	    break;
358	msgConfirm("An attempt to mount the fixit floppy failed, maybe the filesystem\n"
359		   "is unclean.  Trying a forcible mount as a last resort...");
360	if (mount("ufs", "/mnt2", MNT_FORCE, (caddr_t)&args) != -1)
361	    break;
362	if (msgYesNo("Unable to mount the fixit floppy - do you want to try again?") != 0)
363	    return DITEM_FAILURE;
364    }
365
366    if (!directory_exists("/tmp"))
367	(void)symlink("/mnt2/tmp", "/tmp");
368
369    fixit_common();
370
371    unmount("/mnt2", MNT_FORCE);
372    msgConfirm("Please remove the fixit floppy now.");
373    return DITEM_SUCCESS;
374}
375
376/*
377 * The common code for both fixit variants.
378 */
379static void
380fixit_common(void)
381{
382    pid_t child;
383    int waitstatus;
384
385    if (!directory_exists("/var/tmp/vi.recover")) {
386	if (DITEM_STATUS(Mkdir("/var/tmp/vi.recover")) != DITEM_SUCCESS) {
387	    msgConfirm("Warning:  Was unable to create a /var/tmp/vi.recover directory.\n"
388		       "vi will kvetch and moan about it as a result but should still\n"
389		       "be essentially usable.");
390	}
391    }
392    if (!directory_exists("/bin"))
393	(void)Mkdir("/bin");
394    (void)symlink("/stand/sh", "/bin/sh");
395    /* Link the /etc/ files */
396    if (DITEM_STATUS(Mkdir("/etc")) != DITEM_SUCCESS)
397	msgConfirm("Unable to create an /etc directory!  Things are weird on this floppy..");
398    else if ((symlink("/mnt2/etc/spwd.db", "/etc/spwd.db") == -1 && errno != EEXIST) ||
399	     (symlink("/mnt2/etc/protocols", "/etc/protocols") == -1 && errno != EEXIST) ||
400	     (symlink("/mnt2/etc/services", "/etc/services") == -1 && errno != EEXIST))
401	msgConfirm("Couldn't symlink the /etc/ files!  I'm not sure I like this..");
402    if (!file_readable(TERMCAP_FILE))
403	create_termcap();
404    if (!(child = fork())) {
405	int i, fd;
406	struct termios foo;
407	extern int login_tty(int);
408
409	ioctl(0, TIOCNOTTY, NULL);
410	for (i = getdtablesize(); i >= 0; --i)
411	    close(i);
412	fd = open("/dev/ttyv3", O_RDWR);
413	ioctl(0, TIOCSCTTY, &fd);
414	dup2(0, 1);
415	dup2(0, 2);
416	DebugFD = 2;
417	if (login_tty(fd) == -1)
418	    msgDebug("fixit: I can't set the controlling terminal.\n");
419
420	signal(SIGTTOU, SIG_IGN);
421	if (tcgetattr(0, &foo) != -1) {
422	    foo.c_cc[VERASE] = '\010';
423	    if (tcsetattr(0, TCSANOW, &foo) == -1)
424		msgDebug("fixit shell: Unable to set erase character.\n");
425	}
426	else
427	    msgDebug("fixit shell: Unable to get terminal attributes!\n");
428	setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/stand:"
429	       "/mnt2/stand:/mnt2/bin:/mnt2/sbin:/mnt2/usr/bin:/mnt2/usr/sbin", 1);
430	/* use the .profile from the fixit medium */
431	setenv("HOME", "/mnt2", 1);
432	chdir("/mnt2");
433	execlp("sh", "-sh", 0);
434	msgDebug("fixit shell: Failed to execute shell!\n");
435	_exit(1);;
436    }
437    else {
438	msgNotify("Waiting for fixit shell to exit.  Go to VTY4 now by\n"
439		  "typing ALT-F4.  When you are done, type ``exit'' to exit\n"
440		  "the fixit shell and be returned here.");
441	(void)waitpid(child, &waitstatus, 0);
442    }
443    dialog_clear();
444}
445
446
447int
448installExpress(dialogMenuItem *self)
449{
450    int i;
451
452    variable_set2(SYSTEM_STATE, "express");
453    if (DITEM_STATUS((i = diskPartitionEditor(self))) == DITEM_FAILURE)
454	return i;
455
456    if (DITEM_STATUS((i = diskLabelEditor(self))) == DITEM_FAILURE)
457	return i;
458
459    dialog_clear_norefresh();
460    if (DITEM_STATUS((i = installCommit(self))) == DITEM_SUCCESS) {
461	i |= DITEM_LEAVE_MENU;
462	/* Give user the option of one last configuration spree */
463	installConfigure();
464    }
465    return i | DITEM_RESTORE;
466}
467
468/* Novice mode installation */
469int
470installNovice(dialogMenuItem *self)
471{
472    int i, tries = 0;
473    Device **devs;
474
475    variable_set2(SYSTEM_STATE, "novice");
476    dialog_clear_norefresh();
477    msgConfirm("In the next menu, you will need to set up a DOS-style (\"fdisk\") partitioning\n"
478	       "scheme for your hard disk.  If you simply wish to devote all disk space\n"
479	       "to FreeBSD (overwriting anything else that might be on the disk(s) selected)\n"
480	       "then use the (A)ll command to select the default partitioning scheme followed\n"
481	       "by a (Q)uit.  If you wish to allocate only free space to FreeBSD, move to a\n"
482	       "partition marked \"unused\" and use the (C)reate command.");
483
484nodisks:
485    if (DITEM_STATUS(diskPartitionEditor(self)) == DITEM_FAILURE)
486	return DITEM_FAILURE;
487
488    if (diskGetSelectCount(&devs) <= 0 && tries < 3) {
489	msgConfirm("You need to select some disks to operate on!  Be sure to use SPACE\n"
490		   "instead of RETURN in the disk selection menu when selecting a disk.");
491	++tries;
492	goto nodisks;
493    }
494
495    dialog_clear_norefresh();
496    msgConfirm("Next, you need to create BSD partitions inside of the fdisk partition(s)\n"
497	       "just created.  If you have a reasonable amount of disk space (200MB or more)\n"
498	       "and don't have any special requirements, simply use the (A)uto command to\n"
499	       "allocate space automatically.  If you have more specific needs or just don't\n"
500	       "care for the layout chosen by (A)uto, press F1 for more information on\n"
501	       "manual layout.");
502
503    if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE)
504	return DITEM_FAILURE;
505
506    dialog_clear_norefresh();
507    if (DITEM_STATUS((i = installCommit(self))) == DITEM_FAILURE) {
508	dialog_clear_norefresh();
509	msgConfirm("Installation completed with some errors.  You may wish to\n"
510		   "scroll through the debugging messages on VTY1 with the\n"
511		   "scroll-lock feature.  You can also chose \"No\" at the next\n"
512		   "prompt and go back into the installation menus to try and retry\n"
513		   "whichever operations have failed.");
514	return i | DITEM_RESTORE;
515
516    }
517    else {
518	dialog_clear_norefresh();
519	msgConfirm("Congratulations!  You now have FreeBSD installed on your system.\n\n"
520		   "We will now move on to the final configuration questions.\n"
521		   "For any option you do not wish to configure, simply select\n"
522		   "No.\n\n"
523		   "If you wish to re-enter this utility after the system is up, you\n"
524		   "may do so by typing: /stand/sysinstall.");
525    }
526    if (mediaDevice->type != DEVICE_TYPE_FTP && mediaDevice->type != DEVICE_TYPE_NFS) {
527	if (!msgYesNo("Would you like to configure any Ethernet or SLIP/PPP network devices?")) {
528	    Device *tmp;
529
530	    dialog_clear_norefresh();
531	    tmp = tcpDeviceSelect();
532	    dialog_clear_norefresh();
533	    if (tmp && !msgYesNo("Would you like to bring the %s interface up right now?", tmp->name))
534		if (!tmp->init(tmp))
535		    msgConfirm("Initialization of %s device failed.", tmp->name);
536	}
537    }
538
539    dialog_clear_norefresh();
540    if (!msgYesNo("Will this machine be an IP gateway (e.g. will it forward packets\n"
541		  "between interfaces)?"))
542	variable_set2("gateway_enable", "YES");
543
544    dialog_clear_norefresh();
545    if (!msgYesNo("Do you want to allow anonymous FTP connections to this machine?"))
546	configAnonFTP(self);
547
548    dialog_clear_norefresh();
549    if (!msgYesNo("Do you want to configure this machine as an NFS server?"))
550	configNFSServer(self);
551
552    dialog_clear_norefresh();
553    if (!msgYesNo("Do you want to configure this machine as an NFS client?"))
554	variable_set2("nfs_client_enable", "YES");
555
556    dialog_clear_norefresh();
557    if (!msgYesNo("Would you like to customize your system console settings?")) {
558	WINDOW *w = savescr();
559
560	dmenuOpenSimple(&MenuSyscons, FALSE);
561	restorescr(w);
562    }
563
564    dialog_clear_norefresh();
565    if (!msgYesNo("Would you like to set this machine's time zone now?")) {
566	WINDOW *w = savescr();
567
568	dialog_clear();
569	systemExecute("tzsetup");
570	restorescr(w);
571    }
572
573    dialog_clear_norefresh();
574    if (!msgYesNo("Does this system have a mouse attached to it?")) {
575	WINDOW *w = savescr();
576
577	dmenuOpenSimple(&MenuMouse, FALSE);
578	restorescr(w);
579    }
580
581    /* Now would be a good time to checkpoint the configuration data */
582    configRC_conf("/etc/rc.conf");
583    sync();
584
585    if (directory_exists("/usr/X11R6")) {
586	dialog_clear_norefresh();
587	if (!msgYesNo("Would you like to configure your X server at this time?"))
588	    configXEnvironment(self);
589    }
590
591    dialog_clear_norefresh();
592    if (!msgYesNo("The FreeBSD package collection is a collection of hundreds of ready-to-run\n"
593		  "applications, from text editors to games to WEB servers and more.  Would you\n"
594		  "like to browse the collection now?"))
595	configPackages(self);
596
597    dialog_clear_norefresh();
598    if (!msgYesNo("Would you like to add any initial user accounts to the system?\n"
599		  "Adding at least one account for yourself at this stage is suggested\n"
600		  "since working as the \"root\" user is dangerous (it is easy to do\n"
601		  "things which adversely affect the entire system)."))
602	configUsers(self);
603
604    dialog_clear_norefresh();
605    msgConfirm("Now you must set the system manager's password.\n"
606	       "This is the password you'll use to log in as \"root\".");
607    {
608	WINDOW *w = savescr();
609
610	if (!systemExecute("passwd root"))
611	    variable_set2("root_password", "YES");
612	restorescr(w);
613    }
614
615    dialog_clear_norefresh();
616    if (!msgYesNo("Would you like to register your FreeBSD system at this time?\n\n"
617		  "PLEASE, take just 5 minutes to do this.  If we're ever to get any\n"
618		  "significant base of commercial software for FreeBSD, we need to\n"
619		  "be able to provide more information about the size of our user community.\n"
620		  "This is where your registration can really help us, and you can also\n"
621		  "sign up for the new FreeBSD newsletter (its free!) at the same time.\n"))
622	configRegister(NULL);
623    else {
624	dialog_clear_norefresh();
625	msgConfirm("OK, but if you should change your mind then you always can register\n"
626		   "later by typing ``/stand/sysinstall register'' or by simply visiting our\n"
627		   "web site at http://www.freebsd.org/register.html");
628
629    }
630    /* XXX Put whatever other nice configuration questions you'd like to ask the user here XXX */
631
632    /* Give user the option of one last configuration spree */
633    dialog_clear_norefresh();
634    installConfigure();
635
636    return DITEM_LEAVE_MENU | DITEM_RESTORE;
637}
638
639/* The version of commit we call from the Install Custom menu */
640int
641installCustomCommit(dialogMenuItem *self)
642{
643    int i;
644
645    dialog_clear_norefresh();
646    i = installCommit(self);
647    if (DITEM_STATUS(i) == DITEM_SUCCESS) {
648	/* Give user the option of one last configuration spree */
649	installConfigure();
650	return i;
651    }
652    else
653	msgConfirm("The commit operation completed with errors.  Not\n"
654		   "updating /etc files.");
655    return i;
656}
657
658/*
659 * What happens when we finally decide to going ahead with the installation.
660 *
661 * This is broken into multiple stages so that the user can do a full
662 * installation but come back here again to load more distributions,
663 * perhaps from a different media type.  This would allow, for
664 * example, the user to load the majority of the system from CDROM and
665 * then use ftp to load just the DES dist.
666 */
667int
668installCommit(dialogMenuItem *self)
669{
670    int i;
671    char *str;
672    Boolean need_bin;
673
674    if (!Dists)
675	distConfig(NULL);
676
677    if (!Dists)
678	if (!dmenuOpenSimple(&MenuDistributions, FALSE) && !Dists)
679	    return DITEM_FAILURE | DITEM_RESTORE;
680
681    if (!mediaVerify())
682	return DITEM_FAILURE | DITEM_RESTORE;
683
684    str = variable_get(SYSTEM_STATE);
685    if (isDebug())
686	msgDebug("installCommit: System state is `%s'\n", str);
687
688    if (RunningAsInit) {
689	/* Do things we wouldn't do to a multi-user system */
690	if (DITEM_STATUS((i = installInitial())) == DITEM_FAILURE)
691	    return i;
692	if (DITEM_STATUS((i = configFstab())) == DITEM_FAILURE)
693	    return i;
694    }
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    need_bin = Dists & DIST_BIN;
711    i = distExtractAll(self);
712    if (DITEM_STATUS(i) == DITEM_SUCCESS) {
713	if (need_bin && !(Dists & DIST_BIN))
714	    i = installFixup(self);
715    }
716    /* When running as init, *now* it's safe to grab the rc.foo vars */
717    installEnvironment();
718
719    variable_set2(SYSTEM_STATE, DITEM_STATUS(i) == DITEM_FAILURE ? "error-install" : "full-install");
720
721    return i | DITEM_RESTORE;
722}
723
724static void
725installConfigure(void)
726{
727    /* Final menu of last resort */
728    dialog_clear_norefresh();
729    if (!msgYesNo("Visit the general configuration menu for a chance to set\n"
730		  "any last options?")) {
731	WINDOW *w = savescr();
732
733	dmenuOpenSimple(&MenuConfigure, FALSE);
734	restorescr(w);
735    }
736    configRC_conf("/etc/rc.conf");
737    sync();
738}
739
740int
741installFixup(dialogMenuItem *self)
742{
743    Device **devs;
744    int i;
745
746    if (!file_readable("/kernel")) {
747	if (file_readable("/kernel.GENERIC")) {
748#ifdef SAVE_USERCONFIG
749	    /* Snapshot any boot -c changes back to the GENERIC kernel */
750	    if (!variable_cmp(VAR_RELNAME, RELEASE_NAME))
751		save_userconfig_to_kernel("/kernel.GENERIC");
752#endif
753	    if (vsystem("cp -p /kernel.GENERIC /kernel")) {
754		msgConfirm("Unable to link /kernel into place!");
755		return DITEM_FAILURE;
756	    }
757	}
758	else {
759	    msgConfirm("Can't find a kernel image to link to on the root file system!\n"
760		       "You're going to have a hard time getting this system to\n"
761		       "boot from the hard disk, I'm afraid!");
762	    return DITEM_FAILURE;
763	}
764    }
765
766    /* Resurrect /dev after bin distribution screws it up */
767    if (RunningAsInit) {
768	msgNotify("Remaking all devices.. Please wait!");
769	if (vsystem("cd /dev; sh MAKEDEV all")) {
770	    msgConfirm("MAKEDEV returned non-zero status");
771	    return DITEM_FAILURE;
772	}
773
774	msgNotify("Resurrecting /dev entries for slices..");
775	devs = deviceFind(NULL, DEVICE_TYPE_DISK);
776	if (!devs)
777	    msgFatal("Couldn't get a disk device list!");
778
779	/* Resurrect the slices that the former clobbered */
780	for (i = 0; devs[i]; i++) {
781	    Disk *disk = (Disk *)devs[i]->private;
782	    Chunk *c1;
783
784	    if (!devs[i]->enabled)
785		continue;
786	    if (!disk->chunks)
787		msgFatal("No chunk list found for %s!", disk->name);
788	    for (c1 = disk->chunks->part; c1; c1 = c1->next) {
789		if (c1->type == freebsd) {
790		    msgNotify("Making slice entries for %s", c1->name);
791		    if (vsystem("cd /dev; sh MAKEDEV %sh", c1->name)) {
792			msgConfirm("Unable to make slice entries for %s!", c1->name);
793			return DITEM_FAILURE;
794		    }
795		}
796	    }
797	}
798	/* XXX Do all the last ugly work-arounds here which we'll try and excise someday right?? XXX */
799
800	msgNotify("Fixing permissions..");
801	/* BOGON #1:  XFree86 extracting /usr/X11R6 with root-only perms */
802	if (directory_exists("/usr/X11R6")) {
803	    vsystem("chmod -R a+r /usr/X11R6");
804	    vsystem("find /usr/X11R6 -type d | xargs chmod a+x");
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