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