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