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