install.c revision 46503
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.233 1999/04/28 10:51:01 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	    configXSetup(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    /* XXX Put whatever other nice configuration questions you'd like to ask the user here XXX */
632
633    /* Give user the option of one last configuration spree */
634    dialog_clear_norefresh();
635    installConfigure();
636
637    return DITEM_LEAVE_MENU | DITEM_RESTORE;
638}
639
640/* The version of commit we call from the Install Custom menu */
641int
642installCustomCommit(dialogMenuItem *self)
643{
644    int i;
645
646    dialog_clear_norefresh();
647    i = installCommit(self);
648    if (DITEM_STATUS(i) == DITEM_SUCCESS) {
649	/* Give user the option of one last configuration spree */
650	installConfigure();
651	return i;
652    }
653    else
654	msgConfirm("The commit operation completed with errors.  Not\n"
655		   "updating /etc files.");
656    return i;
657}
658
659/*
660 * What happens when we finally decide to going ahead with the installation.
661 *
662 * This is broken into multiple stages so that the user can do a full
663 * installation but come back here again to load more distributions,
664 * perhaps from a different media type.  This would allow, for
665 * example, the user to load the majority of the system from CDROM and
666 * then use ftp to load just the DES dist.
667 */
668int
669installCommit(dialogMenuItem *self)
670{
671    int i;
672    char *str;
673
674    if (!Dists)
675	distConfig(NULL);
676
677    if (!Dists)
678	if (!dmenuOpenSimple(&MenuDistributions, FALSE) && !Dists)
679	    return DITEM_FAILURE | DITEM_RESTORE;
680
681    if (!mediaVerify())
682	return DITEM_FAILURE | DITEM_RESTORE;
683
684    str = variable_get(SYSTEM_STATE);
685    if (isDebug())
686	msgDebug("installCommit: System state is `%s'\n", str);
687
688    /* Installation stuff we wouldn't do to a running system */
689    if (RunningAsInit && DITEM_STATUS((i = installInitial())) == DITEM_FAILURE)
690	return i;
691
692try_media:
693    if (!mediaDevice->init(mediaDevice)) {
694	if (!msgYesNo("Unable to initialize selected media. Would you like to\n"
695		      "adjust your media configuration and try again?")) {
696	    mediaDevice = NULL;
697	    if (!mediaVerify())
698		return DITEM_FAILURE | DITEM_RESTORE;
699	    else
700		goto try_media;
701	}
702	else
703	    return DITEM_FAILURE | DITEM_RESTORE;
704    }
705
706    /* Now go get it all */
707    i = distExtractAll(self);
708
709    /* When running as init, *now* it's safe to grab the rc.foo vars */
710    installEnvironment();
711
712    variable_set2(SYSTEM_STATE, DITEM_STATUS(i) == DITEM_FAILURE ? "error-install" : "full-install", 0);
713
714    return i | DITEM_RESTORE;
715}
716
717static void
718installConfigure(void)
719{
720    /* Final menu of last resort */
721    dialog_clear_norefresh();
722    if (!msgYesNo("Visit the general configuration menu for a chance to set\n"
723		  "any last options?")) {
724	WINDOW *w = savescr();
725
726	dmenuOpenSimple(&MenuConfigure, FALSE);
727	restorescr(w);
728    }
729    configRC_conf();
730    sync();
731}
732
733int
734installFixupBin(dialogMenuItem *self)
735{
736    Device **devs;
737    int i;
738
739    /* All of this is done only as init, just to be safe */
740    if (RunningAsInit) {
741	/* Fix up kernel first */
742	if (!file_readable("/kernel")) {
743	    if (file_readable("/kernel.GENERIC")) {
744		if (vsystem("cp -p /kernel.GENERIC /kernel")) {
745		    msgConfirm("Unable to copy /kernel into place!");
746		    return DITEM_FAILURE;
747		}
748#ifndef __alpha__
749                /* Snapshot any boot -c changes back to the new kernel */
750                if (kget("/boot/kernel.conf")) {
751		    msgConfirm("Kernel copied OK, but unable to save boot -c changes\n"
752			       "to it.  See the debug screen (ALT-F2) for details.");
753		}
754		else if (file_readable("/boot/kernel.conf")) {
755		    FILE *fp;
756
757		    if ((fp = fopen("/boot/loader.conf", "a")) != NULL) {
758			fprintf(fp, "# -- sysinstall generated deltas -- #\n");
759			fprintf(fp, "userconfig_script_load=\"YES\"\n");
760		 	fclose(fp);
761		    }
762		}
763#endif
764	    }
765	    else {
766		msgConfirm("Can't find a kernel image to link to on the root file system!\n"
767			   "You're going to have a hard time getting this system to\n"
768			   "boot from the hard disk, I'm afraid!");
769		return DITEM_FAILURE;
770	    }
771	}
772
773	/* BOGON #1: Resurrect /dev after bin distribution screws it up */
774	msgNotify("Remaking all devices.. Please wait!");
775	if (vsystem("cd /dev; sh MAKEDEV all")) {
776	    msgConfirm("MAKEDEV returned non-zero status");
777	    return DITEM_FAILURE;
778	}
779
780	msgNotify("Resurrecting /dev entries for slices..");
781	devs = deviceFind(NULL, DEVICE_TYPE_DISK);
782	if (!devs)
783	    msgFatal("Couldn't get a disk device list!");
784
785	/* Resurrect the slices that the former clobbered */
786	for (i = 0; devs[i]; i++) {
787	    Disk *disk = (Disk *)devs[i]->private;
788	    Chunk *c1;
789
790	    if (!devs[i]->enabled)
791		continue;
792	    if (!disk->chunks)
793		msgFatal("No chunk list found for %s!", disk->name);
794	    for (c1 = disk->chunks->part; c1; c1 = c1->next) {
795		if (c1->type == freebsd) {
796		    msgNotify("Making slice entries for %s", c1->name);
797		    if (vsystem("cd /dev; sh MAKEDEV %sh", c1->name)) {
798			msgConfirm("Unable to make slice entries for %s!", c1->name);
799			return DITEM_FAILURE;
800		    }
801		}
802	    }
803	}
804
805	/* BOGON #2: We leave /etc in a bad state */
806	chmod("/etc", 0755);
807
808	/* BOGON #3: No /var/db/mountdtab complains */
809	Mkdir("/var/db");
810	creat("/var/db/mountdtab", 0644);
811
812	/* BOGON #4: /compat created by default in root fs */
813	Mkdir("/usr/compat");
814	vsystem("ln -s /usr/compat /compat");
815
816	/* BOGON #5: aliases database not build for bin */
817	vsystem("newaliases");
818
819	/* Now run all the mtree stuff to fix things up */
820        vsystem("mtree -deU -f /etc/mtree/BSD.root.dist -p /");
821        vsystem("mtree -deU -f /etc/mtree/BSD.var.dist -p /var");
822        vsystem("mtree -deU -f /etc/mtree/BSD.usr.dist -p /usr");
823
824	/* Do all the last ugly work-arounds here */
825    }
826    return DITEM_SUCCESS;
827}
828
829/* Fix side-effects from the the XFree86 installation */
830int
831installFixupXFree(dialogMenuItem *self)
832{
833    /* BOGON #1:  XFree86 requires various specialized fixups */
834    if (directory_exists("/usr/X11R6")) {
835	msgNotify("Fixing permissions in XFree86 tree..");
836	vsystem("chmod -R a+r /usr/X11R6");
837	vsystem("find /usr/X11R6 -type d | xargs chmod a+x");
838
839	/* Also do bogus minimal package registration so ports don't whine */
840	if (file_readable("/usr/X11R6/lib/X11/pkgreg.tar.gz")) {
841	    msgNotify("Installing package metainfo..");
842	    vsystem("tar xpzf /usr/X11R6/lib/X11/pkgreg.tar.gz -C / && rm /usr/X11R6/lib/X11/pkgreg.tar.gz");
843	}
844    }
845    return DITEM_SUCCESS;
846}
847
848/* Go newfs and/or mount all the filesystems we've been asked to */
849int
850installFilesystems(dialogMenuItem *self)
851{
852    int i;
853    Disk *disk;
854    Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev;
855    Device **devs;
856    PartInfo *root;
857    char dname[80];
858    extern int MakeDevChunk(Chunk *c, char *n);
859    Boolean upgrade = FALSE;
860
861    /* If we've already done this, bail out */
862    if (!variable_cmp(DISK_LABELLED, "written"))
863	return DITEM_SUCCESS;
864
865    upgrade = !variable_cmp(SYSTEM_STATE, "upgrade");
866    if (!checkLabels(TRUE, &rootdev, &swapdev, &usrdev, &vardev))
867	return DITEM_FAILURE;
868
869    if (rootdev)
870	root = (PartInfo *)rootdev->private_data;
871    else
872	root = NULL;
873
874    command_clear();
875    if (swapdev && RunningAsInit) {
876	/* As the very first thing, try to get ourselves some swap space */
877	sprintf(dname, "/dev/%s", swapdev->name);
878	if (!Fake && (!MakeDevChunk(swapdev, "/dev") || !file_readable(dname))) {
879	    msgConfirm("Unable to make device node for %s in /dev!\n"
880		       "The creation of filesystems will be aborted.", dname);
881	    return DITEM_FAILURE;
882	}
883
884	if (!Fake) {
885	    if (!swapon(dname))
886		msgNotify("Added %s as initial swap device", dname);
887	    else
888		msgConfirm("WARNING!  Unable to swap to %s: %s\n"
889			   "This may cause the installation to fail at some point\n"
890			   "if you don't have a lot of memory.", dname, strerror(errno));
891	}
892    }
893
894    if (rootdev && RunningAsInit) {
895	/* Next, create and/or mount the root device */
896	sprintf(dname, "/dev/r%s", rootdev->name);
897	if (!Fake && (!MakeDevChunk(rootdev, "/dev") || !file_readable(dname))) {
898	    msgConfirm("Unable to make device node for %s in /dev!\n"
899		       "The creation of filesystems will be aborted.", dname);
900	    return DITEM_FAILURE;
901	}
902	if (strcmp(root->mountpoint, "/"))
903	    msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", rootdev->name, root->mountpoint);
904
905	if (root->newfs && (!upgrade || !msgYesNo("You are upgrading - are you SURE you want to newfs the root partition?"))) {
906	    int i;
907
908	    msgNotify("Making a new root filesystem on %s", dname);
909	    i = vsystem("%s %s", root->newfs_cmd, dname);
910	    if (i) {
911		msgConfirm("Unable to make new root filesystem on %s!\n"
912			   "Command returned status %d", dname, i);
913		return DITEM_FAILURE;
914	    }
915	}
916	else {
917	    if (!upgrade) {
918		msgConfirm("Warning:  Using existing root partition.  It will be assumed\n"
919			   "that you have the appropriate device entries already in /dev.");
920	    }
921	    msgNotify("Checking integrity of existing %s filesystem.", dname);
922	    i = vsystem("fsck -y %s", dname);
923	    if (i)
924		msgConfirm("Warning: fsck returned status of %d for %s.\n"
925			   "This partition may be unsafe to use.", i, dname);
926	}
927
928	/* Switch to block device */
929	sprintf(dname, "/dev/%s", rootdev->name);
930	if (Mount("/mnt", dname)) {
931	    msgConfirm("Unable to mount the root file system on %s!  Giving up.", dname);
932	    return DITEM_FAILURE;
933	}
934    }
935
936    /* Now buzz through the rest of the partitions and mount them too */
937    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
938    for (i = 0; devs[i]; i++) {
939	if (!devs[i]->enabled)
940	    continue;
941
942	disk = (Disk *)devs[i]->private;
943	if (!disk->chunks) {
944	    msgConfirm("No chunk list found for %s!", disk->name);
945	    return DITEM_FAILURE;
946	}
947	if (RunningAsInit && root && (root->newfs || upgrade)) {
948	    Mkdir("/mnt/dev");
949	    if (!Fake)
950		MakeDevDisk(disk, "/mnt/dev");
951	}
952	else if (!RunningAsInit && !Fake)
953	    MakeDevDisk(disk, "/dev");
954
955	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
956	    if (c1->type == freebsd) {
957		for (c2 = c1->part; c2; c2 = c2->next) {
958		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
959			PartInfo *tmp = (PartInfo *)c2->private_data;
960
961			/* Already did root */
962			if (c2 == rootdev)
963			    continue;
964
965			if (tmp->newfs && (!upgrade || !msgYesNo("You are upgrading - are you SURE you want to newfs /dev/%s?", c2->name)))
966			    command_shell_add(tmp->mountpoint, "%s %s/dev/r%s", tmp->newfs_cmd, RunningAsInit ? "/mnt" : "", c2->name);
967			else
968			    command_shell_add(tmp->mountpoint, "fsck -y %s/dev/r%s", RunningAsInit ? "/mnt" : "", c2->name);
969			command_func_add(tmp->mountpoint, Mount, c2->name);
970		    }
971		    else if (c2->type == part && c2->subtype == FS_SWAP) {
972			char fname[80];
973			int i;
974
975			if (c2 == swapdev)
976			    continue;
977			sprintf(fname, "%s/dev/%s", RunningAsInit ? "/mnt" : "", c2->name);
978			i = (Fake || swapon(fname));
979			if (!i)
980			    msgNotify("Added %s as an additional swap device", fname);
981			else
982			    msgConfirm("Unable to add %s as a swap device: %s", fname, strerror(errno));
983		    }
984		}
985	    }
986	    else if (c1->type == fat && c1->private_data && (root->newfs || upgrade)) {
987		char name[FILENAME_MAX];
988
989		sprintf(name, "%s/%s", RunningAsInit ? "/mnt" : "", ((PartInfo *)c1->private_data)->mountpoint);
990		Mkdir(name);
991	    }
992	}
993    }
994
995    if (RunningAsInit) {
996	msgNotify("Copying initial device files..");
997	/* Copy the boot floppy's dev files */
998	if ((root->newfs || upgrade) && vsystem("find -x /dev | cpio %s -pdum /mnt", cpioVerbosity())) {
999	    msgConfirm("Couldn't clone the /dev files!");
1000	    return DITEM_FAILURE;
1001	}
1002    }
1003
1004    command_sort();
1005    command_execute();
1006    return DITEM_SUCCESS;
1007}
1008
1009static char *
1010getRelname(void)
1011{
1012    static char buf[64];
1013    int sz = (sizeof buf) - 1;
1014
1015    if (sysctlbyname("kern.osrelease", buf, &sz, NULL, 0) != -1) {
1016	buf[sz] = '\0';
1017	return buf;
1018    }
1019    else
1020	return "<unknown>";
1021}
1022
1023/* Initialize various user-settable values to their defaults */
1024int
1025installVarDefaults(dialogMenuItem *self)
1026{
1027    char *cp;
1028
1029    /* Set default startup options */
1030    variable_set2(VAR_RELNAME,			getRelname(), 0);
1031    variable_set2(VAR_CPIO_VERBOSITY,		"high", 0);
1032    variable_set2(VAR_TAPE_BLOCKSIZE,		DEFAULT_TAPE_BLOCKSIZE, 0);
1033    variable_set2(VAR_INSTALL_ROOT,		"/", 0);
1034    variable_set2(VAR_INSTALL_CFG,		"install.cfg", 0);
1035    cp = getenv("EDITOR");
1036    if (!cp)
1037	cp = "/usr/bin/ee";
1038    variable_set2(VAR_EDITOR,			cp, 0);
1039    variable_set2(VAR_FTP_USER,			"ftp", 0);
1040    variable_set2(VAR_BROWSER_PACKAGE,		"@lynx", 0);
1041    variable_set2(VAR_BROWSER_BINARY,		"/usr/local/bin/lynx", 0);
1042    variable_set2(VAR_FTP_STATE,		"passive", 0);
1043    variable_set2(VAR_NFS_SECURE,		"YES", 0);
1044    variable_set2(VAR_PKG_TMPDIR,		"/usr/tmp", 0);
1045    variable_set2(VAR_MEDIA_TIMEOUT,		itoa(MEDIA_TIMEOUT), 0);
1046    if (getpid() != 1)
1047	variable_set2(SYSTEM_STATE,		"update", 0);
1048    else
1049	variable_set2(SYSTEM_STATE,		"init", 0);
1050    return DITEM_SUCCESS;
1051}
1052
1053/* Load the environment up from various system configuration files */
1054void
1055installEnvironment(void)
1056{
1057    configEnvironmentRC_conf();
1058    if (file_readable("/etc/resolv.conf"))
1059	configEnvironmentResolv("/etc/resolv.conf");
1060}
1061
1062/* Copy the boot floppy contents into /stand */
1063Boolean
1064copySelf(void)
1065{
1066    int i;
1067
1068    if (file_readable("/boot.help"))
1069	vsystem("cp /boot.help /mnt");
1070    msgWeHaveOutput("Copying the boot floppy to /stand on root filesystem");
1071    i = vsystem("find -x /stand | cpio %s -pdum /mnt", cpioVerbosity());
1072    if (i) {
1073	msgConfirm("Copy returned error status of %d!", i);
1074	return FALSE;
1075    }
1076
1077    /* Copy the /etc files into their rightful place */
1078    if (vsystem("cd /mnt/stand; find etc | cpio %s -pdum /mnt", cpioVerbosity())) {
1079	msgConfirm("Couldn't copy up the /etc files!");
1080	return TRUE;
1081    }
1082    return TRUE;
1083}
1084
1085static void
1086create_termcap(void)
1087{
1088    FILE *fp;
1089
1090    const char *caps[] = {
1091	termcap_vt100, termcap_cons25, termcap_cons25_m, termcap_cons25r,
1092	termcap_cons25r_m, termcap_cons25l1, termcap_cons25l1_m, NULL,
1093    };
1094    const char **cp;
1095
1096    if (!file_readable(TERMCAP_FILE)) {
1097	Mkdir("/usr/share/misc");
1098	fp = fopen(TERMCAP_FILE, "w");
1099	if (!fp) {
1100	    msgConfirm("Unable to initialize termcap file. Some screen-oriented\nutilities may not work.");
1101	    return;
1102	}
1103	cp = caps;
1104	while (*cp)
1105	    fprintf(fp, "%s\n", *(cp++));
1106	fclose(fp);
1107    }
1108}
1109