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