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