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