install.c revision 42898
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.222 1999/01/20 11:56:39 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");
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");
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");
271    (void)unlink("/mnt2");
272    (void)rmdir("/mnt2");
273
274    while (1) {
275	msgConfirm("Please insert the second FreeBSD 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	    if (mediaDevice) {
279		mediaDevice->shutdown(mediaDevice);
280		mediaDevice = NULL;
281	    }
282	    if (msgYesNo("Unable to mount the CDROM - do you want to try again?") != 0)
283		return DITEM_FAILURE;
284	}
285	else
286	    break;
287    }
288
289    /* Since the fixit code expects everything to be in /mnt2, and the CDROM mounting stuff /dist, do
290     * a little kludge dance here..
291     */
292    if (symlink("/dist", "/mnt2")) {
293	msgConfirm("Unable to symlink /mnt2 to the CDROM mount point.  Please report this\n"
294		   "unexpected failure to freebsd-bugs@FreeBSD.org.");
295	return DITEM_FAILURE;
296    }
297
298    /*
299     * If /tmp points to /mnt2/tmp from a previous fixit floppy session, it's
300     * not very good for us if we point it to the CDROM now.  Rather make it
301     * a directory in the root MFS then.  Experienced admins will still be
302     * able to mount their disk's /tmp over this if they need.
303     */
304    if (lstat("/tmp", &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFLNK)
305	(void)unlink("/tmp");
306    Mkdir("/tmp");
307
308    /*
309     * Since setuid binaries ignore LD_LIBRARY_PATH, we indeed need the
310     * ld.so.hints file.  Fortunately, it's fairly small (~ 3 KB).
311     */
312    if (!file_readable("/var/run/ld.so.hints")) {
313	Mkdir("/var/run");
314	if (vsystem("/mnt2/sbin/ldconfig -s /mnt2/usr/lib")) {
315	    msgConfirm("Warning: ldconfig could not create the ld.so hints file.\n"
316		       "Dynamic executables from the CDROM likely won't work.");
317	}
318    }
319
320    /* Yet another iggly hardcoded pathname. */
321    if (!file_readable("/usr/libexec/ld.so")) {
322	Mkdir("/usr/libexec");
323	if (symlink("/mnt2/usr/libexec/ld.so", "/usr/libexec/ld.so")) {
324	    msgConfirm("Warning: could not create the symlink for ld.so.\n"
325		       "Dynamic executables from the CDROM likely won't work.");
326	}
327    }
328
329    fixit_common();
330
331    mediaDevice->shutdown(mediaDevice);
332    msgConfirm("Please remove the FreeBSD CDROM now.");
333    return DITEM_SUCCESS;
334}
335
336int
337installFixitFloppy(dialogMenuItem *self)
338{
339    struct ufs_args args;
340
341    if (!RunningAsInit)
342	return DITEM_SUCCESS;
343
344    variable_set2(SYSTEM_STATE, "fixit");
345    Mkdir("/mnt2");
346
347    /* Try to open the floppy drive */
348    if (DITEM_STATUS(mediaSetFloppy(NULL)) == DITEM_FAILURE) {
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
357    while (1) {
358	msgConfirm("Please insert a writable fixit floppy and press return");
359	mediaDevice->private = "/mnt2";
360	if (!mediaDevice->init(mediaDevice)) {
361	    if (msgYesNo("The attempt to mount the fixit floppy failed, bad floppy\n"
362			 "or unclean filesystem.  Do you want to try again?"))
363		return DITEM_FAILURE;
364	}
365	else
366	    break;
367    }
368    if (!directory_exists("/tmp"))
369	(void)symlink("/mnt2/tmp", "/tmp");
370    fixit_common();
371    mediaDevice->shutdown(mediaDevice);
372    mediaDevice = NULL;
373    msgConfirm("Please remove the fixit floppy now.");
374    return DITEM_SUCCESS;
375}
376
377/*
378 * The common code for both fixit variants.
379 */
380static void
381fixit_common(void)
382{
383    pid_t child;
384    int waitstatus;
385
386    if (!directory_exists("/var/tmp/vi.recover")) {
387	if (DITEM_STATUS(Mkdir("/var/tmp/vi.recover")) != DITEM_SUCCESS) {
388	    msgConfirm("Warning:  Was unable to create a /var/tmp/vi.recover directory.\n"
389		       "vi will kvetch and moan about it as a result but should still\n"
390		       "be essentially usable.");
391	}
392    }
393    if (!directory_exists("/bin"))
394	(void)Mkdir("/bin");
395    (void)symlink("/stand/sh", "/bin/sh");
396    /* Link the /etc/ files */
397    if (DITEM_STATUS(Mkdir("/etc")) != DITEM_SUCCESS)
398	msgConfirm("Unable to create an /etc directory!  Things are weird on this floppy..");
399    else if ((symlink("/mnt2/etc/spwd.db", "/etc/spwd.db") == -1 && errno != EEXIST) ||
400	     (symlink("/mnt2/etc/protocols", "/etc/protocols") == -1 && errno != EEXIST) ||
401	     (symlink("/mnt2/etc/services", "/etc/services") == -1 && errno != EEXIST))
402	msgConfirm("Couldn't symlink the /etc/ files!  I'm not sure I like this..");
403    if (!file_readable(TERMCAP_FILE))
404	create_termcap();
405    if (!(child = fork())) {
406	int i, fd;
407	struct termios foo;
408	extern int login_tty(int);
409
410	ioctl(0, TIOCNOTTY, NULL);
411	for (i = getdtablesize(); i >= 0; --i)
412	    close(i);
413	fd = open("/dev/ttyv3", O_RDWR);
414	ioctl(0, TIOCSCTTY, &fd);
415	dup2(0, 1);
416	dup2(0, 2);
417	DebugFD = 2;
418	if (login_tty(fd) == -1)
419	    msgDebug("fixit: I can't set the controlling terminal.\n");
420
421	signal(SIGTTOU, SIG_IGN);
422	if (tcgetattr(0, &foo) != -1) {
423	    foo.c_cc[VERASE] = '\010';
424	    if (tcsetattr(0, TCSANOW, &foo) == -1)
425		msgDebug("fixit shell: Unable to set erase character.\n");
426	}
427	else
428	    msgDebug("fixit shell: Unable to get terminal attributes!\n");
429	setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/stand:"
430	       "/mnt2/stand:/mnt2/bin:/mnt2/sbin:/mnt2/usr/bin:/mnt2/usr/sbin", 1);
431	/* use the .profile from the fixit medium */
432	setenv("HOME", "/mnt2", 1);
433	chdir("/mnt2");
434	execlp("sh", "-sh", 0);
435	msgDebug("fixit shell: Failed to execute shell!\n");
436	_exit(1);;
437    }
438    else {
439	msgNotify("Waiting for fixit shell to exit.  Go to VTY4 now by\n"
440		  "typing ALT-F4.  When you are done, type ``exit'' to exit\n"
441		  "the fixit shell and be returned here.");
442	(void)waitpid(child, &waitstatus, 0);
443    }
444    dialog_clear();
445}
446
447
448int
449installExpress(dialogMenuItem *self)
450{
451    int i;
452
453    variable_set2(SYSTEM_STATE, "express");
454#ifndef __alpha__
455    if (DITEM_STATUS((i = diskPartitionEditor(self))) == DITEM_FAILURE)
456	return i;
457#endif
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#ifndef __alpha__
480    dialog_clear_norefresh();
481    msgConfirm("In the next menu, you will need to set up a DOS-style (\"fdisk\") partitioning\n"
482	       "scheme for your hard disk.  If you simply wish to devote all disk space\n"
483	       "to FreeBSD (overwriting anything else that might be on the disk(s) selected)\n"
484	       "then use the (A)ll command to select the default partitioning scheme followed\n"
485	       "by a (Q)uit.  If you wish to allocate only free space to FreeBSD, move to a\n"
486	       "partition marked \"unused\" and use the (C)reate command.");
487
488nodisks:
489    if (DITEM_STATUS(diskPartitionEditor(self)) == DITEM_FAILURE)
490	return DITEM_FAILURE;
491
492    if (diskGetSelectCount(&devs) <= 0 && tries < 3) {
493	msgConfirm("You need to select some disks to operate on!  Be sure to use SPACE\n"
494		   "instead of RETURN in the disk selection menu when selecting a disk.");
495	++tries;
496	goto nodisks;
497    }
498#endif
499
500    dialog_clear_norefresh();
501#ifdef __alpha__
502    msgConfirm("First, you need to create BSD partitions on the disk which you are\n"
503	       "installing to.  If you have a reasonable amount of disk space (200MB or more)\n"
504	       "and don't have any special requirements, simply use the (A)uto command to\n"
505	       "allocate space automatically.  If you have more specific needs or just don't\n"
506	       "care for the layout chosen by (A)uto, press F1 for more information on\n"
507	       "manual layout.");
508#else
509    msgConfirm("First, you need to create BSD partitions inside of the fdisk partition(s)\n"
510	       "just created.  If you have a reasonable amount of disk space (200MB or more)\n"
511	       "and don't have any special requirements, simply use the (A)uto command to\n"
512	       "allocate space automatically.  If you have more specific needs or just don't\n"
513	       "care for the layout chosen by (A)uto, press F1 for more information on\n"
514	       "manual layout.");
515#endif
516
517    if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE)
518	return DITEM_FAILURE;
519
520    dialog_clear_norefresh();
521    if (DITEM_STATUS((i = installCommit(self))) == DITEM_FAILURE) {
522	dialog_clear_norefresh();
523	msgConfirm("Installation completed with some errors.  You may wish to\n"
524		   "scroll through the debugging messages on VTY1 with the\n"
525		   "scroll-lock feature.  You can also chose \"No\" at the next\n"
526		   "prompt and go back into the installation menus to try and retry\n"
527		   "whichever operations have failed.");
528	return i | DITEM_RESTORE;
529
530    }
531    else {
532	dialog_clear_norefresh();
533	msgConfirm("Congratulations!  You now have FreeBSD installed on your system.\n\n"
534		   "We will now move on to the final configuration questions.\n"
535		   "For any option you do not wish to configure, simply select\n"
536		   "No.\n\n"
537		   "If you wish to re-enter this utility after the system is up, you\n"
538		   "may do so by typing: /stand/sysinstall.");
539    }
540    if (mediaDevice->type != DEVICE_TYPE_FTP && mediaDevice->type != DEVICE_TYPE_NFS) {
541	if (!msgYesNo("Would you like to configure any Ethernet or SLIP/PPP network devices?")) {
542	    Device *tmp;
543
544	    dialog_clear_norefresh();
545	    tmp = tcpDeviceSelect();
546	    dialog_clear_norefresh();
547	    if (tmp && !msgYesNo("Would you like to bring the %s interface up right now?", tmp->name))
548		if (!tmp->init(tmp))
549		    msgConfirm("Initialization of %s device failed.", tmp->name);
550	}
551    }
552
553    dialog_clear_norefresh();
554    if (!msgYesNo("Will this machine be an IP gateway (e.g. will it forward packets\n"
555		  "between interfaces)?"))
556	variable_set2("gateway_enable", "YES");
557
558    dialog_clear_norefresh();
559    if (!msgYesNo("Do you want to allow anonymous FTP connections to this machine?"))
560	configAnonFTP(self);
561
562    dialog_clear_norefresh();
563    if (!msgYesNo("Do you want to configure this machine as an NFS server?"))
564	configNFSServer(self);
565
566    dialog_clear_norefresh();
567    if (!msgYesNo("Do you want to configure this machine as an NFS client?"))
568	variable_set2("nfs_client_enable", "YES");
569
570    dialog_clear_norefresh();
571    if (!msgYesNo("Would you like to customize your system console settings?")) {
572	WINDOW *w = savescr();
573
574	dmenuOpenSimple(&MenuSyscons, FALSE);
575	restorescr(w);
576    }
577
578    dialog_clear_norefresh();
579    if (!msgYesNo("Would you like to set this machine's time zone now?")) {
580	WINDOW *w = savescr();
581
582	dialog_clear();
583	systemExecute("tzsetup");
584	restorescr(w);
585    }
586
587    dialog_clear_norefresh();
588    if (!msgYesNo("Does this system have a mouse attached to it?")) {
589	WINDOW *w = savescr();
590
591	dmenuOpenSimple(&MenuMouse, FALSE);
592	restorescr(w);
593    }
594
595    /* Now would be a good time to checkpoint the configuration data */
596    configRC_conf("/etc/rc.conf");
597    sync();
598
599    if (directory_exists("/usr/X11R6")) {
600	dialog_clear_norefresh();
601	if (!msgYesNo("Would you like to configure your X server at this time?"))
602	    configXEnvironment(self);
603    }
604
605    dialog_clear_norefresh();
606    if (!msgYesNo("The FreeBSD package collection is a collection of hundreds of ready-to-run\n"
607		  "applications, from text editors to games to WEB servers and more.  Would you\n"
608		  "like to browse the collection now?"))
609	configPackages(self);
610
611    dialog_clear_norefresh();
612    if (!msgYesNo("Would you like to add any initial user accounts to the system?\n"
613		  "Adding at least one account for yourself at this stage is suggested\n"
614		  "since working as the \"root\" user is dangerous (it is easy to do\n"
615		  "things which adversely affect the entire system)."))
616	configUsers(self);
617
618    dialog_clear_norefresh();
619    msgConfirm("Now you must set the system manager's password.\n"
620	       "This is the password you'll use to log in as \"root\".");
621    {
622	WINDOW *w = savescr();
623
624	if (!systemExecute("passwd root"))
625	    variable_set2("root_password", "YES");
626	restorescr(w);
627    }
628
629    dialog_clear_norefresh();
630    if (!msgYesNo("Would you like to register your FreeBSD system at this time?\n\n"
631		  "PLEASE, take just 5 minutes to do this.  If we're ever to get any\n"
632		  "significant base of commercial software for FreeBSD, we need to\n"
633		  "be able to provide more information about the size of our user community.\n"
634		  "This is where your registration can really help us, and you can also\n"
635		  "sign up for the new FreeBSD newsletter (its free!) at the same time.\n"))
636	configRegister(NULL);
637    else {
638	dialog_clear_norefresh();
639	msgConfirm("OK, but if you should change your mind then you always can register\n"
640		   "later by typing ``/stand/sysinstall register'' or by simply visiting our\n"
641		   "web site at http://www.freebsd.org/register.html");
642
643    }
644    /* XXX Put whatever other nice configuration questions you'd like to ask the user here XXX */
645
646    /* Give user the option of one last configuration spree */
647    dialog_clear_norefresh();
648    installConfigure();
649
650    return DITEM_LEAVE_MENU | DITEM_RESTORE;
651}
652
653/* The version of commit we call from the Install Custom menu */
654int
655installCustomCommit(dialogMenuItem *self)
656{
657    int i;
658
659    dialog_clear_norefresh();
660    i = installCommit(self);
661    if (DITEM_STATUS(i) == DITEM_SUCCESS) {
662	/* Give user the option of one last configuration spree */
663	installConfigure();
664	return i;
665    }
666    else
667	msgConfirm("The commit operation completed with errors.  Not\n"
668		   "updating /etc files.");
669    return i;
670}
671
672/*
673 * What happens when we finally decide to going ahead with the installation.
674 *
675 * This is broken into multiple stages so that the user can do a full
676 * installation but come back here again to load more distributions,
677 * perhaps from a different media type.  This would allow, for
678 * example, the user to load the majority of the system from CDROM and
679 * then use ftp to load just the DES dist.
680 */
681int
682installCommit(dialogMenuItem *self)
683{
684    int i;
685    char *str;
686
687    if (!Dists)
688	distConfig(NULL);
689
690    if (!Dists)
691	if (!dmenuOpenSimple(&MenuDistributions, FALSE) && !Dists)
692	    return DITEM_FAILURE | DITEM_RESTORE;
693
694    if (!mediaVerify())
695	return DITEM_FAILURE | DITEM_RESTORE;
696
697    str = variable_get(SYSTEM_STATE);
698    if (isDebug())
699	msgDebug("installCommit: System state is `%s'\n", str);
700
701    /* Installation stuff we wouldn't do to a running system */
702    if (RunningAsInit && DITEM_STATUS((i = installInitial())) == DITEM_FAILURE)
703	return i;
704
705try_media:
706    if (!mediaDevice->init(mediaDevice)) {
707	if (!msgYesNo("Unable to initialize selected media. Would you like to\n"
708		      "adjust your media configuration and try again?")) {
709	    mediaDevice = NULL;
710	    if (!mediaVerify())
711		return DITEM_FAILURE | DITEM_RESTORE;
712	    else
713		goto try_media;
714	}
715	else
716	    return DITEM_FAILURE | DITEM_RESTORE;
717    }
718
719    /* Now go get it all */
720    i = distExtractAll(self);
721
722    /* When running as init, *now* it's safe to grab the rc.foo vars */
723    installEnvironment();
724
725    variable_set2(SYSTEM_STATE, DITEM_STATUS(i) == DITEM_FAILURE ? "error-install" : "full-install");
726
727    return i | DITEM_RESTORE;
728}
729
730static void
731installConfigure(void)
732{
733    /* Final menu of last resort */
734    dialog_clear_norefresh();
735    if (!msgYesNo("Visit the general configuration menu for a chance to set\n"
736		  "any last options?")) {
737	WINDOW *w = savescr();
738
739	dmenuOpenSimple(&MenuConfigure, FALSE);
740	restorescr(w);
741    }
742    configRC_conf("/etc/rc.conf");
743    sync();
744}
745
746int
747installFixupBin(dialogMenuItem *self)
748{
749    Device **devs;
750    int i;
751
752    /* All of this is done only as init, just to be safe */
753    if (RunningAsInit) {
754	/* Fix up kernel first */
755	if (!file_readable("/kernel")) {
756	    if (file_readable("/kernel.GENERIC")) {
757		if (vsystem("cp -p /kernel.GENERIC /kernel")) {
758		    msgConfirm("Unable to copy /kernel into place!");
759		    return DITEM_FAILURE;
760		}
761                /* Snapshot any boot -c changes back to the new kernel */
762                if (kget("/kernel.config")) {
763		    msgConfirm("Kernel copied OK, but unable to save boot -c changes\n"
764			       "to it.  See the debug screen (ALT-F2) for details.");
765		}
766	    }
767	    else {
768		msgConfirm("Can't find a kernel image to link to on the root file system!\n"
769			   "You're going to have a hard time getting this system to\n"
770			   "boot from the hard disk, I'm afraid!");
771		return DITEM_FAILURE;
772	    }
773	}
774
775	/* BOGON #1: Resurrect /dev after bin distribution screws it up */
776	msgNotify("Remaking all devices.. Please wait!");
777	if (vsystem("cd /dev; sh MAKEDEV all")) {
778	    msgConfirm("MAKEDEV returned non-zero status");
779	    return DITEM_FAILURE;
780	}
781
782	msgNotify("Resurrecting /dev entries for slices..");
783	devs = deviceFind(NULL, DEVICE_TYPE_DISK);
784	if (!devs)
785	    msgFatal("Couldn't get a disk device list!");
786
787	/* Resurrect the slices that the former clobbered */
788	for (i = 0; devs[i]; i++) {
789	    Disk *disk = (Disk *)devs[i]->private;
790	    Chunk *c1;
791
792	    if (!devs[i]->enabled)
793		continue;
794	    if (!disk->chunks)
795		msgFatal("No chunk list found for %s!", disk->name);
796	    for (c1 = disk->chunks->part; c1; c1 = c1->next) {
797		if (c1->type == freebsd) {
798		    msgNotify("Making slice entries for %s", c1->name);
799		    if (vsystem("cd /dev; sh MAKEDEV %sh", c1->name)) {
800			msgConfirm("Unable to make slice entries for %s!", c1->name);
801			return DITEM_FAILURE;
802		    }
803		}
804	    }
805	}
806
807	/* BOGON #2: We leave /etc in a bad state */
808	chmod("/etc", 0755);
809
810	/* BOGON #3: No /var/db/mountdtab complains */
811	Mkdir("/var/db");
812	creat("/var/db/mountdtab", 0644);
813
814	/* BOGON #4: /compat created by default in root fs */
815	Mkdir("/usr/compat");
816	vsystem("ln -s /usr/compat /compat");
817
818	/* BOGON #5: aliases database not build for bin */
819	vsystem("newaliases");
820
821	/* BOGON #6: deal with new boot files */
822	vsystem("touch /kernel.config");
823	vsystem("touch /boot.config");
824	if (file_readable("/stand/boot.help") && !file_readable("/boot.help"))
825	    vsystem("mv /stand/boot.help /");
826
827	/* Now run all the mtree stuff to fix things up */
828        vsystem("mtree -deU -f /etc/mtree/BSD.root.dist -p /");
829        vsystem("mtree -deU -f /etc/mtree/BSD.var.dist -p /var");
830        vsystem("mtree -deU -f /etc/mtree/BSD.usr.dist -p /usr");
831
832	/* Do all the last ugly work-arounds here */
833    }
834    return DITEM_SUCCESS;
835}
836
837/* Fix side-effects from the the XFree86 installation */
838int
839installFixupXFree(dialogMenuItem *self)
840{
841    /* BOGON #1:  XFree86 requires various specialized fixups */
842    if (directory_exists("/usr/X11R6")) {
843	msgNotify("Fixing permissions in XFree86 tree..");
844	vsystem("chmod -R a+r /usr/X11R6");
845	vsystem("find /usr/X11R6 -type d | xargs chmod a+x");
846
847	/* Also do bogus minimal package registration so ports don't whine */
848	if (file_readable("/usr/X11R6/lib/X11/pkgreg.tar.gz")) {
849	    msgNotify("Installing package metainfo..");
850	    vsystem("tar xpzf /usr/X11R6/lib/X11/pkgreg.tar.gz -C / && rm /usr/X11R6/lib/X11/pkgreg.tar.gz");
851	}
852    }
853    return DITEM_SUCCESS;
854}
855
856/* Go newfs and/or mount all the filesystems we've been asked to */
857int
858installFilesystems(dialogMenuItem *self)
859{
860    int i;
861    Disk *disk;
862    Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev;
863    Device **devs;
864    PartInfo *root;
865    char dname[80];
866    extern int MakeDevChunk(Chunk *c, char *n);
867    Boolean upgrade = FALSE;
868
869    /* If we've already done this, bail out */
870    if (!variable_cmp(DISK_LABELLED, "written"))
871	return DITEM_SUCCESS;
872
873    upgrade = !variable_cmp(SYSTEM_STATE, "upgrade");
874    if (!checkLabels(TRUE, &rootdev, &swapdev, &usrdev, &vardev))
875	return DITEM_FAILURE;
876
877    if (rootdev)
878	root = (PartInfo *)rootdev->private_data;
879    else
880	root = NULL;
881
882    command_clear();
883    if (swapdev && RunningAsInit) {
884	/* As the very first thing, try to get ourselves some swap space */
885	sprintf(dname, "/dev/%s", swapdev->name);
886	if (!Fake && (!MakeDevChunk(swapdev, "/dev") || !file_readable(dname))) {
887	    msgConfirm("Unable to make device node for %s in /dev!\n"
888		       "The creation of filesystems will be aborted.", dname);
889	    return DITEM_FAILURE;
890	}
891
892	if (!Fake) {
893	    if (!swapon(dname))
894		msgNotify("Added %s as initial swap device", dname);
895	    else
896		msgConfirm("WARNING!  Unable to swap to %s: %s\n"
897			   "This may cause the installation to fail at some point\n"
898			   "if you don't have a lot of memory.", dname, strerror(errno));
899	}
900    }
901
902    if (rootdev && RunningAsInit) {
903	/* Next, create and/or mount the root device */
904	sprintf(dname, "/dev/r%s", rootdev->name);
905	if (!Fake && (!MakeDevChunk(rootdev, "/dev") || !file_readable(dname))) {
906	    msgConfirm("Unable to make device node for %s in /dev!\n"
907		       "The creation of filesystems will be aborted.", dname);
908	    return DITEM_FAILURE;
909	}
910	if (strcmp(root->mountpoint, "/"))
911	    msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", rootdev->name, root->mountpoint);
912
913	if (root->newfs && (!upgrade || !msgYesNo("You are upgrading - are you SURE you want to newfs the root partition?"))) {
914	    int i;
915
916	    msgNotify("Making a new root filesystem on %s", dname);
917	    i = vsystem("%s %s", root->newfs_cmd, dname);
918	    if (i) {
919		msgConfirm("Unable to make new root filesystem on %s!\n"
920			   "Command returned status %d", dname, i);
921		return DITEM_FAILURE;
922	    }
923	}
924	else {
925	    if (!upgrade) {
926		msgConfirm("Warning:  Using existing root partition.  It will be assumed\n"
927			   "that you have the appropriate device entries already in /dev.");
928	    }
929	    msgNotify("Checking integrity of existing %s filesystem.", dname);
930	    i = vsystem("fsck -y %s", dname);
931	    if (i)
932		msgConfirm("Warning: fsck returned status of %d for %s.\n"
933			   "This partition may be unsafe to use.", i, dname);
934	}
935
936	/* Switch to block device */
937	sprintf(dname, "/dev/%s", rootdev->name);
938	if (Mount("/mnt", dname)) {
939	    msgConfirm("Unable to mount the root file system on %s!  Giving up.", dname);
940	    return DITEM_FAILURE;
941	}
942    }
943
944    /* Now buzz through the rest of the partitions and mount them too */
945    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
946    for (i = 0; devs[i]; i++) {
947	if (!devs[i]->enabled)
948	    continue;
949
950	disk = (Disk *)devs[i]->private;
951	if (!disk->chunks) {
952	    msgConfirm("No chunk list found for %s!", disk->name);
953	    return DITEM_FAILURE;
954	}
955	if (RunningAsInit && root && (root->newfs || upgrade)) {
956	    Mkdir("/mnt/dev");
957	    if (!Fake)
958		MakeDevDisk(disk, "/mnt/dev");
959	}
960	else if (!RunningAsInit && !Fake)
961	    MakeDevDisk(disk, "/dev");
962
963	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
964	    if (c1->type == freebsd) {
965		for (c2 = c1->part; c2; c2 = c2->next) {
966		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
967			PartInfo *tmp = (PartInfo *)c2->private_data;
968
969			/* Already did root */
970			if (c2 == rootdev)
971			    continue;
972
973			if (tmp->newfs && (!upgrade || !msgYesNo("You are upgrading - are you SURE you want to newfs /dev/%s?", c2->name)))
974			    command_shell_add(tmp->mountpoint, "%s %s/dev/r%s", tmp->newfs_cmd, RunningAsInit ? "/mnt" : "", c2->name);
975			else
976			    command_shell_add(tmp->mountpoint, "fsck -y %s/dev/r%s", RunningAsInit ? "/mnt" : "", c2->name);
977			command_func_add(tmp->mountpoint, Mount, c2->name);
978		    }
979		    else if (c2->type == part && c2->subtype == FS_SWAP) {
980			char fname[80];
981			int i;
982
983			if (c2 == swapdev)
984			    continue;
985			sprintf(fname, "%s/dev/%s", RunningAsInit ? "/mnt" : "", c2->name);
986			i = (Fake || swapon(fname));
987			if (!i)
988			    msgNotify("Added %s as an additional swap device", fname);
989			else
990			    msgConfirm("Unable to add %s as a swap device: %s", fname, strerror(errno));
991		    }
992		}
993	    }
994	    else if (c1->type == fat && c1->private_data && (root->newfs || upgrade)) {
995		char name[FILENAME_MAX];
996
997		sprintf(name, "%s/%s", RunningAsInit ? "/mnt" : "", ((PartInfo *)c1->private_data)->mountpoint);
998		Mkdir(name);
999	    }
1000	}
1001    }
1002
1003    if (RunningAsInit) {
1004	msgNotify("Copying initial device files..");
1005	/* Copy the boot floppy's dev files */
1006	if ((root->newfs || upgrade) && vsystem("find -x /dev | cpio %s -pdum /mnt", cpioVerbosity())) {
1007	    msgConfirm("Couldn't clone the /dev files!");
1008	    return DITEM_FAILURE;
1009	}
1010    }
1011
1012    command_sort();
1013    command_execute();
1014    return DITEM_SUCCESS;
1015}
1016
1017static char *
1018getRelname(void)
1019{
1020    static char buf[64];
1021    int sz = (sizeof buf) - 1;
1022
1023    if (sysctlbyname("kern.osrelease", buf, &sz, NULL, 0) != -1) {
1024	buf[sz] = '\0';
1025	return buf;
1026    }
1027    else
1028	return "<unknown>";
1029}
1030
1031/* Initialize various user-settable values to their defaults */
1032int
1033installVarDefaults(dialogMenuItem *self)
1034{
1035    char *cp;
1036
1037    /* Set default startup options */
1038    variable_set2(VAR_RELNAME,			getRelname());
1039    variable_set2(VAR_CPIO_VERBOSITY,		"high");
1040    variable_set2(VAR_TAPE_BLOCKSIZE,		DEFAULT_TAPE_BLOCKSIZE);
1041    variable_set2(VAR_INSTALL_ROOT,		"/");
1042    variable_set2(VAR_INSTALL_CFG,		"install.cfg");
1043    cp = getenv("EDITOR");
1044    if (!cp)
1045	cp = "/usr/bin/ee";
1046    variable_set2(VAR_EDITOR,			cp);
1047    variable_set2(VAR_FTP_USER,			"ftp");
1048    variable_set2(VAR_BROWSER_PACKAGE,		"lynx");
1049    variable_set2(VAR_BROWSER_BINARY,		"/usr/local/bin/lynx");
1050    variable_set2(VAR_FTP_STATE,		"passive");
1051    variable_set2(VAR_NFS_SECURE,		"YES");
1052    variable_set2(VAR_PKG_TMPDIR,		"/usr/tmp");
1053    variable_set2(VAR_GATED_PKG,		"gated");
1054    variable_set2(VAR_PCNFSD_PKG,		"pcnfsd");
1055    variable_set2(VAR_MEDIA_TIMEOUT,		itoa(MEDIA_TIMEOUT));
1056    if (getpid() != 1)
1057	variable_set2(SYSTEM_STATE,		"update");
1058    else
1059	variable_set2(SYSTEM_STATE,		"init");
1060    return DITEM_SUCCESS;
1061}
1062
1063/* Load the environment up from various system configuration files */
1064void
1065installEnvironment(void)
1066{
1067    if (file_readable("/etc/rc.conf"))
1068	configEnvironmentRC_conf("/etc/rc.conf");
1069    if (file_readable("/etc/resolv.conf"))
1070	configEnvironmentResolv("/etc/resolv.conf");
1071}
1072
1073/* Copy the boot floppy contents into /stand */
1074Boolean
1075copySelf(void)
1076{
1077    int i;
1078
1079    if (file_readable("/boot.help"))
1080	vsystem("cp /boot.help /mnt");
1081    msgWeHaveOutput("Copying the boot floppy to /stand on root filesystem");
1082    i = vsystem("find -x /stand | cpio %s -pdum /mnt", cpioVerbosity());
1083    if (i) {
1084	msgConfirm("Copy returned error status of %d!", i);
1085	return FALSE;
1086    }
1087
1088    /* Copy the /etc files into their rightful place */
1089    if (vsystem("cd /mnt/stand; find etc | cpio %s -pdum /mnt", cpioVerbosity())) {
1090	msgConfirm("Couldn't copy up the /etc files!");
1091	return TRUE;
1092    }
1093    return TRUE;
1094}
1095
1096static void
1097create_termcap(void)
1098{
1099    FILE *fp;
1100
1101    const char *caps[] = {
1102	termcap_vt100, termcap_cons25, termcap_cons25_m, termcap_cons25r,
1103	termcap_cons25r_m, termcap_cons25l1, termcap_cons25l1_m, NULL,
1104    };
1105    const char **cp;
1106
1107    if (!file_readable(TERMCAP_FILE)) {
1108	Mkdir("/usr/share/misc");
1109	fp = fopen(TERMCAP_FILE, "w");
1110	if (!fp) {
1111	    msgConfirm("Unable to initialize termcap file. Some screen-oriented\nutilities may not work.");
1112	    return;
1113	}
1114	cp = caps;
1115	while (*cp)
1116	    fprintf(fp, "%s\n", *(cp++));
1117	fclose(fp);
1118    }
1119}
1120