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