install.c revision 21010
1/*
2 * The new sysinstall program.
3 *
4 * This is probably the last program in the `sysinstall' line - the next
5 * generation being essentially a complete rewrite.
6 *
7 * $Id: install.c,v 1.146 1996/12/26 03:32:50 jkh Exp $
8 *
9 * Copyright (c) 1995
10 *	Jordan Hubbard.  All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer,
17 *    verbatim and that no modifications are made prior to this
18 *    point in the file.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 *    notice, this list of conditions and the following disclaimer in the
21 *    documentation and/or other materials provided with the distribution.
22 *
23 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 */
36
37#include "sysinstall.h"
38#include "uc_main.h"
39#include <ctype.h>
40#include <sys/disklabel.h>
41#include <sys/errno.h>
42#include <sys/ioctl.h>
43#include <sys/fcntl.h>
44#include <sys/wait.h>
45#include <sys/param.h>
46#define MSDOSFS
47#include <sys/mount.h>
48#undef MSDOSFS
49#include <sys/stat.h>
50#include <unistd.h>
51
52static void	create_termcap(void);
53static void	fixit_common(void);
54#ifdef SAVE_USERCONFIG
55static void	save_userconfig_to_kernel(char *);
56#endif
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    status = TRUE;
72    *rdev = *sdev = *udev = *vdev = rootdev = swapdev = usrdev = vardev = NULL;
73
74    /* We don't need to worry about root/usr/swap if we're already multiuser */
75    if (!RunningAsInit)
76	return status;
77
78    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
79    /* First verify that we have a root device */
80    for (i = 0; devs[i]; i++) {
81	if (!devs[i]->enabled)
82	    continue;
83	disk = (Disk *)devs[i]->private;
84	msgDebug("Scanning disk %s for root filesystem\n", disk->name);
85	if (!disk->chunks)
86	    msgFatal("No chunk list found for %s!", disk->name);
87	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
88	    if (c1->type == freebsd) {
89		for (c2 = c1->part; c2; c2 = c2->next) {
90		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
91			if (c2->flags & CHUNK_IS_ROOT) {
92			    if (rootdev) {
93				if (whinge)
94				    msgConfirm("WARNING:  You have more than one root device set?!\n"
95					       "Using the first one found.");
96				continue;
97			    }
98			    else {
99				rootdev = c2;
100				if (isDebug())
101				    msgDebug("Found rootdev at %s!\n", rootdev->name);
102			    }
103			}
104			else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/usr")) {
105			    if (usrdev) {
106				if (whinge)
107				    msgConfirm("WARNING:  You have more than one /usr filesystem.\n"
108					       "Using the first one found.");
109				continue;
110			    }
111			    else {
112				usrdev = c2;
113				if (isDebug())
114				    msgDebug("Found usrdev at %s!\n", usrdev->name);
115			    }
116			}
117			else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/var")) {
118			    if (vardev) {
119				if (whinge)
120				    msgConfirm("WARNING:  You have more than one /var filesystem.\n"
121					       "Using the first one found.");
122				continue;
123			    }
124			    else {
125				vardev = c2;
126				if (isDebug())
127				    msgDebug("Found vardev at %s!\n", vardev->name);
128			    }
129			}
130		    }
131		}
132	    }
133	}
134    }
135
136    /* Now check for swap devices */
137    for (i = 0; devs[i]; i++) {
138	if (!devs[i]->enabled)
139	    continue;
140	disk = (Disk *)devs[i]->private;
141	msgDebug("Scanning disk %s for swap partitions\n", disk->name);
142	if (!disk->chunks)
143	    msgFatal("No chunk list found for %s!", disk->name);
144	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
145	    if (c1->type == freebsd) {
146		for (c2 = c1->part; c2; c2 = c2->next) {
147		    if (c2->type == part && c2->subtype == FS_SWAP && !swapdev) {
148			swapdev = c2;
149			if (isDebug())
150			    msgDebug("Found swapdev at %s!\n", swapdev->name);
151			break;
152		    }
153		}
154	    }
155	}
156    }
157
158    /* Copy our values over */
159    *rdev = rootdev;
160    *sdev = swapdev;
161    *udev = usrdev;
162    *vdev = vardev;
163
164    if (!rootdev && whinge) {
165	msgConfirm("No root device found - you must label a partition as /\n"
166		   "in the label editor.");
167	status = FALSE;
168    }
169    if (!swapdev && whinge) {
170	msgConfirm("No swap devices found - you must create at least one\n"
171		   "swap partition.");
172	status = FALSE;
173    }
174    if (!usrdev && whinge) {
175	msgConfirm("WARNING:  No /usr filesystem found.  This is not technically\n"
176		   "an error if your root filesystem is big enough (or you later\n"
177		   "intend to mount your /usr filesystem over NFS), but it may otherwise\n"
178		   "cause you trouble if you're not exactly sure what you are doing!");
179    }
180    if (!vardev && whinge) {
181	msgConfirm("WARNING:  No /var filesystem found.  This is not technically\n"
182		   "an error if your root filesystem is big enough (or you later\n"
183		   "intend to link /var to someplace else), but it may otherwise\n"
184		   "cause your root filesystem to fill up if you receive lots of mail\n"
185		   "or edit large temporary files.");
186    }
187    return status;
188}
189
190static int
191installInitial(void)
192{
193    static Boolean alreadyDone = FALSE;
194
195    if (alreadyDone)
196	return DITEM_SUCCESS;
197
198    if (!variable_get(DISK_LABELLED)) {
199	msgConfirm("You need to assign disk labels before you can proceed with\n"
200		   "the installation.");
201	return DITEM_FAILURE;
202    }
203    /* If it's labelled, assume it's also partitioned */
204    if (!variable_get(DISK_PARTITIONED))
205	variable_set2(DISK_PARTITIONED, "yes");
206
207    /* If we refuse to proceed, bail. */
208    dialog_clear_norefresh();
209    if (msgYesNo("Last Chance!  Are you SURE you want continue the installation?\n\n"
210		 "If you're running this on a disk with data you wish to save\n"
211		 "then WE STRONGLY ENCOURAGE YOU TO MAKE PROPER BACKUPS before\n"
212		 "proceeding!\n\n"
213		 "We can take no responsibility for lost disk contents!") != 0)
214	return DITEM_FAILURE | DITEM_RESTORE;
215
216    if (DITEM_STATUS(diskLabelCommit(NULL)) != DITEM_SUCCESS) {
217	msgConfirm("Couldn't make filesystems properly.  Aborting.");
218	return DITEM_FAILURE;
219    }
220    else if (isDebug())
221	msgDebug("installInitial: Scribbled successfully on the disk(s)\n");
222
223    if (!copySelf()) {
224	msgConfirm("Couldn't clone the boot floppy onto the root file system.\n"
225		   "Aborting.");
226	return DITEM_FAILURE;
227    }
228
229    if (chroot("/mnt") == -1) {
230	msgConfirm("Unable to chroot to /mnt - this is bad!");
231	return DITEM_FAILURE;
232    }
233
234    chdir("/");
235    variable_set2(RUNNING_ON_ROOT, "yes");
236    configResolv();
237
238    /* stick a helpful shell over on the 4th VTY */
239    systemCreateHoloshell();
240
241    alreadyDone = TRUE;
242    return DITEM_SUCCESS;
243}
244
245int
246installFixitHoloShell(dialogMenuItem *self)
247{
248    systemCreateHoloshell();
249    return DITEM_SUCCESS;
250}
251
252
253int
254installFixitCDROM(dialogMenuItem *self)
255{
256    struct stat sb;
257
258    variable_set2(SYSTEM_STATE, "fixit");
259    (void)unlink("/mnt2");
260    (void)rmdir("/mnt2");
261
262    while (1) {
263	msgConfirm("Please insert the second CD-ROM and press return");
264	if (DITEM_STATUS(mediaSetCDROM(NULL)) != DITEM_SUCCESS || !mediaDevice->init(mediaDevice)) {
265	    /* If we can't initialize it, it's probably not a FreeBSD CDROM so punt on it */
266	    mediaDevice = NULL;
267	    if (msgYesNo("Unable to mount the CD-ROM - do you want to try again?") != 0)
268		return DITEM_FAILURE;
269	}
270    }
271
272    /* Since the fixit code expects everything to be in /mnt2, and the CDROM mounting stuff /cdrom, do
273     * a little kludge dance here..
274     */
275    if (symlink("/cdrom", "/mnt2")) {
276	msgConfirm("Unable to symlink /mnt2 to the CDROM mount point.  Please report this\n"
277		   "unexpected failure to bugs@freebsd.org.");
278	return DITEM_FAILURE;
279    }
280
281    /*
282     * If /tmp points to /mnt2/tmp from a previous fixit floppy session, it's
283     * not very good for us if we point it to the CD-ROM now.  Rather make it
284     * a directory in the root MFS then.  Experienced admins will still be
285     * able to mount their disk's /tmp over this if they need.
286     */
287    if (lstat("/tmp", &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFLNK)
288	(void)unlink("/tmp");
289    Mkdir("/tmp");
290
291    /*
292     * Since setuid binaries ignore LD_LIBRARY_PATH, we indeed need the
293     * ld.so.hints file.  Fortunately, it's fairly small (~ 3 KB).
294     */
295    if (!file_readable("/var/run/ld.so.hints")) {
296	Mkdir("/var/run");
297	if (vsystem("/mnt2/sbin/ldconfig -s /mnt2/usr/lib")) {
298	    msgConfirm("Warning: ldconfig could not create the ld.so hints file.\n"
299		       "Dynamic executables from the CD-ROM likely won't work.");
300	}
301    }
302
303    /* Yet another iggly hardcoded pathname. */
304    if (!file_readable("/usr/libexec/ld.so")) {
305	Mkdir("/usr/libexec");
306	if (symlink("/mnt2/usr/libexec/ld.so", "/usr/libexec/ld.so")) {
307	    msgConfirm("Warning: could not create the symlink for ld.so.\n"
308		       "Dynamic executables from the CD-ROM likely won't work.");
309	}
310    }
311
312    fixit_common();
313
314    msgConfirm("Please remove the CD-ROM now.");
315    return DITEM_SUCCESS;
316}
317
318int
319installFixitFloppy(dialogMenuItem *self)
320{
321    struct ufs_args args;
322
323    variable_set2(SYSTEM_STATE, "fixit");
324    memset(&args, 0, sizeof(args));
325    args.fspec = "/dev/fd0";
326    Mkdir("/mnt2");
327
328    while (1) {
329	msgConfirm("Please insert a writable fixit floppy and press return");
330	if (mount(MOUNT_UFS, "/mnt2", 0, (caddr_t)&args) != -1)
331	    break;
332	msgConfirm("An attempt to mount the fixit floppy failed, maybe the filesystem\n"
333		   "is unclean.  Trying a forcible mount as a last resort...");
334	if (mount(MOUNT_UFS, "/mnt2", MNT_FORCE, (caddr_t)&args) != -1)
335	    break;
336	if (msgYesNo("Unable to mount the fixit floppy - do you want to try again?") != 0)
337	    return DITEM_FAILURE;
338    }
339
340    if (!directory_exists("/tmp"))
341	(void)symlink("/mnt2/tmp", "/tmp");
342
343    fixit_common();
344
345    msgConfirm("Please remove the fixit floppy now.");
346    return DITEM_SUCCESS;
347}
348
349/*
350 * The common code for both fixit variants.
351 */
352static void
353fixit_common(void)
354{
355    pid_t child;
356    int waitstatus;
357
358    if (!directory_exists("/var/tmp/vi.recover")) {
359	if (DITEM_STATUS(Mkdir("/var/tmp/vi.recover")) != DITEM_SUCCESS) {
360	    msgConfirm("Warning:  Was unable to create a /var/tmp/vi.recover directory.\n"
361		       "vi will kvetch and moan about it as a result but should still\n"
362		       "be essentially usable.");
363	}
364    }
365    if (!directory_exists("/bin"))
366	(void)Mkdir("/bin");
367    (void)symlink("/stand/sh", "/bin/sh");
368    /* Link the /etc/ files */
369    if (DITEM_STATUS(Mkdir("/etc")) != DITEM_SUCCESS)
370	msgConfirm("Unable to create an /etc directory!  Things are weird on this floppy..");
371    else if ((symlink("/mnt2/etc/spwd.db", "/etc/spwd.db") == -1 && errno != EEXIST) ||
372	     (symlink("/mnt2/etc/protocols", "/etc/protocols") == -1 && errno != EEXIST) ||
373	     (symlink("/mnt2/etc/services", "/etc/services") == -1 && errno != EEXIST))
374	msgConfirm("Couldn't symlink the /etc/ files!  I'm not sure I like this..");
375    if (!file_readable(TERMCAP_FILE))
376	create_termcap();
377    if (!(child = fork())) {
378	int i, fd;
379	struct termios foo;
380	extern int login_tty(int);
381
382	ioctl(0, TIOCNOTTY, NULL);
383	for (i = getdtablesize(); i >= 0; --i)
384	    close(i);
385	fd = open("/dev/ttyv3", O_RDWR);
386	ioctl(0, TIOCSCTTY, &fd);
387	dup2(0, 1);
388	dup2(0, 2);
389	DebugFD = 2;
390	if (login_tty(fd) == -1)
391	    msgDebug("fixit: I can't set the controlling terminal.\n");
392
393	signal(SIGTTOU, SIG_IGN);
394	if (tcgetattr(0, &foo) != -1) {
395	    foo.c_cc[VERASE] = '\010';
396	    if (tcsetattr(0, TCSANOW, &foo) == -1)
397		msgDebug("fixit shell: Unable to set erase character.\n");
398	}
399	else
400	    msgDebug("fixit shell: Unable to get terminal attributes!\n");
401	setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/stand:"
402	       "/mnt2/stand:/mnt2/bin:/mnt2/sbin:/mnt2/usr/bin:/mnt2/usr/sbin", 1);
403	/* use the .profile from the fixit medium */
404	setenv("HOME", "/mnt2", 1);
405	chdir("/mnt2");
406	execlp("sh", "-sh", 0);
407	msgDebug("fixit shell: Failed to execute shell!\n");
408	_exit(1);;
409    }
410    else {
411	msgNotify("Waiting for fixit shell to exit.  Go to VTY4 now by\n"
412		  "typing ALT-F4.  When you are done, type ``exit'' to exit\n"
413		  "the fixit shell and be returned here.");
414	(void)waitpid(child, &waitstatus, 0);
415    }
416    unmount("/mnt2", MNT_FORCE);
417    dialog_clear();
418}
419
420
421int
422installExpress(dialogMenuItem *self)
423{
424    int i;
425
426    variable_set2(SYSTEM_STATE, "express");
427    if (DITEM_STATUS((i = diskPartitionEditor(self))) == DITEM_FAILURE)
428	return i;
429
430    if (DITEM_STATUS((i = diskLabelEditor(self))) == DITEM_FAILURE)
431	return i;
432
433    if (!Dists) {
434	dialog_clear_norefresh();
435	if (!dmenuOpenSimple(&MenuDistributions, FALSE) && !Dists)
436	    return DITEM_FAILURE | DITEM_RECREATE;
437    }
438
439    if (!mediaDevice) {
440	dialog_clear_norefresh();
441	if (!dmenuOpenSimple(&MenuMedia, FALSE) || !mediaDevice)
442	    return DITEM_FAILURE | DITEM_RECREATE;
443    }
444
445    if (DITEM_STATUS((i = installCommit(self))) == DITEM_SUCCESS) {
446	i |= DITEM_LEAVE_MENU;
447	/* Give user the option of one last configuration spree */
448	installConfigure();
449
450	/* Now write out any changes .. */
451	configSysconfig("/etc/sysconfig");
452    }
453    return i | DITEM_RECREATE;
454}
455
456/* Novice mode installation */
457int
458installNovice(dialogMenuItem *self)
459{
460    int i;
461
462    variable_set2(SYSTEM_STATE, "novice");
463    dialog_clear_norefresh();
464    msgConfirm("In the next menu, you will need to set up a DOS-style (\"fdisk\") partitioning\n"
465	       "scheme for your hard disk.  If you simply wish to devote all disk space\n"
466	       "to FreeBSD (overwritting anything else that might be on the disk(s) selected)\n"
467	       "then use the (A)ll command to select the default partitioning scheme followed\n"
468	       "by a (Q)uit.  If you wish to allocate only free space to FreeBSD, move to a\n"
469	       "partition marked \"unused\" and use the (C)reate command.");
470
471    if (DITEM_STATUS(diskPartitionEditor(self)) == DITEM_FAILURE)
472	return DITEM_FAILURE;
473
474    dialog_clear_norefresh();
475    msgConfirm("Next, you need to create BSD partitions inside of the fdisk partition(s)\n"
476	       "just created.  If you have a reasonable amount of disk space (200MB or more)\n"
477	       "and don't have any special requirements, simply use the (A)uto command to\n"
478	       "allocate space automatically.  If you have more specific needs or just don't\n"
479	       "care for the layout chosen by (A)uto, press F1 for more information on\n"
480	       "manual layout.");
481
482    if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE)
483	return DITEM_FAILURE;
484
485    while (1) {
486	dialog_clear_norefresh();
487	if (!dmenuOpenSimple(&MenuDistributions, FALSE) && !Dists)
488	    return DITEM_FAILURE | DITEM_RECREATE;
489
490	if (Dists || !msgYesNo("No distributions selected.  Are you sure you wish to continue?"))
491	    break;
492    }
493
494    if (!mediaDevice && !dmenuOpenSimple(&MenuMedia, FALSE))
495	return DITEM_FAILURE | DITEM_RECREATE;
496
497    if (DITEM_STATUS((i = installCommit(self))) == DITEM_FAILURE) {
498	dialog_clear_norefresh();
499	msgConfirm("Installation completed with some errors.  You may wish to\n"
500		   "scroll through the debugging messages on VTY1 with the\n"
501		   "scroll-lock feature.  You can also chose \"No\" at the next\n"
502		   "prompt and go back into the installation menus to try and retry\n"
503		   "whichever operations have failed.");
504	return i | DITEM_RECREATE;
505
506    }
507    else {
508	dialog_clear_norefresh();
509	msgConfirm("Congratulations!  You now have FreeBSD installed on your system.\n\n"
510		   "We will now move on to the final configuration questions.\n"
511		   "For any option you do not wish to configure, simply select\n"
512		   "No.\n\n"
513		   "If you wish to re-enter this utility after the system is up, you\n"
514		   "may do so by typing: /stand/sysinstall.");
515    }
516    if (mediaDevice->type != DEVICE_TYPE_FTP && mediaDevice->type != DEVICE_TYPE_NFS) {
517	if (!msgYesNo("Would you like to configure any SLIP/PPP or network interface devices?")) {
518	    Device *save = mediaDevice;
519
520	    /* This will also set the media device, which we don't want */
521	    tcpDeviceSelect();
522	    /* so we restore our saved value below */
523	    mediaDevice = save;
524	    dialog_clear_norefresh();
525	}
526    }
527
528    dialog_clear_norefresh();
529    if (!msgYesNo("Would you like to configure Samba for connecting NETBUI clients to this\n"
530		  "machine?  Windows 95, Windows NT and Windows for Workgroups\n"
531		  "machines can use NETBUI transport for disk and printer sharing."))
532	configSamba(self);
533
534    dialog_clear_norefresh();
535    if (!msgYesNo("Will this machine be an IP gateway (e.g. will it forward packets\n"
536		  "between interfaces)?"))
537	variable_set2("gateway", "YES");
538
539    dialog_clear_norefresh();
540    if (!msgYesNo("Do you want to allow anonymous FTP connections to this machine?"))
541	configAnonFTP(self);
542
543    dialog_clear_norefresh();
544    if (!msgYesNo("Do you want to configure this machine as an NFS server?"))
545	configNFSServer(self);
546
547    dialog_clear_norefresh();
548    if (!msgYesNo("Do you want to configure this machine as an NFS client?"))
549	variable_set2("nfs_client", "YES");
550
551    dialog_clear_norefresh();
552    if (!msgYesNo("Do you want to configure this machine as a WEB server?"))
553	configApache(self);
554
555    dialog_clear_norefresh();
556    if (!msgYesNo("Would you like to customize your system console settings?")) {
557	WINDOW *w = savescr();
558
559	dmenuOpenSimple(&MenuSyscons, FALSE);
560	restorescr(w);
561    }
562
563    dialog_clear_norefresh();
564    if (!msgYesNo("Would you like to set this machine's time zone now?")) {
565	WINDOW *w = savescr();
566
567	dialog_clear();
568	systemExecute("rm -f /etc/wall_cmos_clock /etc/localtime; tzsetup");
569	restorescr(w);
570    }
571
572    dialog_clear_norefresh();
573    if (!msgYesNo("Does this system have a mouse attached to it?")) {
574	WINDOW *w = savescr();
575
576	dmenuOpenSimple(&MenuMouse, FALSE);
577	restorescr(w);
578    }
579
580    if (directory_exists("/usr/X11R6")) {
581	dialog_clear_norefresh();
582	if (!msgYesNo("Would you like to configure your X server at this time?"))
583	    configXFree86(self);
584    }
585
586    dialog_clear_norefresh();
587    if (!msgYesNo("The FreeBSD package collection is a collection of over 700 ready-to-run\n"
588		  "applications, from text editors to games to WEB servers.  Would you like\n"
589		  "to browse the collection now?"))
590	configPackages(self);
591
592    /* XXX Put whatever other nice configuration questions you'd like to ask the user here XXX */
593
594    /* Give user the option of one last configuration spree */
595    installConfigure();
596
597    /* Now write out any changes .. */
598    configSysconfig("/etc/sysconfig");
599
600    return DITEM_LEAVE_MENU | DITEM_RECREATE;
601}
602
603/* The version of commit we call from the Install Custom menu */
604int
605installCustomCommit(dialogMenuItem *self)
606{
607    int i;
608
609    i = installCommit(self);
610    if (DITEM_STATUS(i) == DITEM_SUCCESS) {
611	/* Give user the option of one last configuration spree */
612	installConfigure();
613
614	/* Now write out any changes .. */
615	configSysconfig("/etc/sysconfig");
616	return i;
617    }
618    else
619	msgConfirm("The commit operation completed with errors.  Not\n"
620		   "updating /etc files.");
621    return i;
622}
623
624/*
625 * What happens when we finally decide to going ahead with the installation.
626 *
627 * This is broken into multiple stages so that the user can do a full
628 * installation but come back here again to load more distributions,
629 * perhaps from a different media type.  This would allow, for
630 * example, the user to load the majority of the system from CDROM and
631 * then use ftp to load just the DES dist.
632 */
633int
634installCommit(dialogMenuItem *self)
635{
636    int i;
637    char *str;
638    Boolean need_bin = FALSE;
639
640    if (!mediaVerify())
641	return DITEM_FAILURE;
642
643    str = variable_get(SYSTEM_STATE);
644    if (isDebug())
645	msgDebug("installCommit: System state is `%s'\n", str);
646
647    if (RunningAsInit) {
648	/* Do things we wouldn't do to a multi-user system */
649	if (DITEM_STATUS((i = installInitial())) == DITEM_FAILURE)
650	    return i;
651	if (DITEM_STATUS((i = configFstab())) == DITEM_FAILURE)
652	    return i;
653    }
654
655    if (Dists & DIST_BIN)
656	need_bin = TRUE;
657    i = distExtractAll(self);
658    if (DITEM_STATUS(i) != DITEM_FAILURE || !need_bin || !(Dists & DIST_BIN))
659	i = installFixup(self);
660
661    variable_set2(SYSTEM_STATE, DITEM_STATUS(i) == DITEM_FAILURE ? "error-install" : "full-install");
662    return i | DITEM_RECREATE;
663}
664
665static void
666installConfigure(void)
667{
668    /* Final menu of last resort */
669    dialog_clear_norefresh();
670    if (!msgYesNo("Visit the general configuration menu for a chance to set\n"
671		  "any last options?")) {
672	WINDOW *w = savescr();
673
674	dmenuOpenSimple(&MenuConfigure, FALSE);
675	restorescr(w);
676    }
677}
678
679int
680installFixup(dialogMenuItem *self)
681{
682    Device **devs;
683    int i;
684
685    if (!file_readable("/kernel")) {
686	if (file_readable("/kernel.GENERIC")) {
687#ifdef SAVE_USERCONFIG
688	    /* Snapshot any boot -c changes back to the GENERIC kernel */
689	    if (!strcmp(variable_get(VAR_RELNAME), RELEASE_NAME))
690		save_userconfig_to_kernel("/kernel.GENERIC");
691#endif
692	    if (vsystem("cp -p /kernel.GENERIC /kernel")) {
693		msgConfirm("Unable to link /kernel into place!");
694		return DITEM_FAILURE;
695	    }
696	}
697	else {
698	    msgConfirm("Can't find a kernel image to link to on the root file system!\n"
699		       "You're going to have a hard time getting this system to\n"
700		       "boot from the hard disk, I'm afraid!");
701	    return DITEM_FAILURE;
702	}
703    }
704
705    /* Resurrect /dev after bin distribution screws it up */
706    if (RunningAsInit) {
707	msgNotify("Remaking all devices.. Please wait!");
708	if (vsystem("cd /dev; sh MAKEDEV all")) {
709	    msgConfirm("MAKEDEV returned non-zero status");
710	    return DITEM_FAILURE;
711	}
712
713	msgNotify("Resurrecting /dev entries for slices..");
714	devs = deviceFind(NULL, DEVICE_TYPE_DISK);
715	if (!devs)
716	    msgFatal("Couldn't get a disk device list!");
717
718	/* Resurrect the slices that the former clobbered */
719	for (i = 0; devs[i]; i++) {
720	    Disk *disk = (Disk *)devs[i]->private;
721	    Chunk *c1;
722
723	    if (!devs[i]->enabled)
724		continue;
725	    if (!disk->chunks)
726		msgFatal("No chunk list found for %s!", disk->name);
727	    for (c1 = disk->chunks->part; c1; c1 = c1->next) {
728		if (c1->type == freebsd) {
729		    msgNotify("Making slice entries for %s", c1->name);
730		    if (vsystem("cd /dev; sh MAKEDEV %sh", c1->name)) {
731			msgConfirm("Unable to make slice entries for %s!", c1->name);
732			return DITEM_FAILURE;
733		    }
734		}
735	    }
736	}
737	/* XXX Do all the last ugly work-arounds here which we'll try and excise someday right?? XXX */
738
739	msgNotify("Fixing permissions..");
740	/* BOGON #1:  XFree86 extracting /usr/X11R6 with root-only perms */
741	if (directory_exists("/usr/X11R6")) {
742	    vsystem("chmod -R a+r /usr/X11R6");
743	    vsystem("find /usr/X11R6 -type d | xargs chmod a+x");
744	}
745	/* BOGON #2: We leave /etc in a bad state */
746	chmod("/etc", 0755);
747
748	/* BOGON #3: No /var/db/mountdtab complains */
749	Mkdir("/var/db");
750	creat("/var/db/mountdtab", 0644);
751
752	/* Now run all the mtree stuff to fix things up */
753        vsystem("mtree -deU -f /etc/mtree/BSD.root.dist -p /");
754        vsystem("mtree -deU -f /etc/mtree/BSD.var.dist -p /var");
755        vsystem("mtree -deU -f /etc/mtree/BSD.usr.dist -p /usr");
756    }
757    return DITEM_SUCCESS;
758}
759
760/* Go newfs and/or mount all the filesystems we've been asked to */
761int
762installFilesystems(dialogMenuItem *self)
763{
764    int i;
765    Disk *disk;
766    Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev;
767    Device **devs;
768    PartInfo *root;
769    char dname[80], *str;
770    extern int MakeDevChunk(Chunk *c, char *n);
771    Boolean upgrade = FALSE;
772
773    /* If we've already done this, bail out */
774    if ((str = variable_get(DISK_LABELLED)) && !strcmp(str, "written"))
775	return DITEM_SUCCESS;
776
777    str = variable_get(SYSTEM_STATE);
778
779    if (!checkLabels(TRUE, &rootdev, &swapdev, &usrdev, &vardev))
780	return DITEM_FAILURE;
781
782    if (rootdev)
783	root = (PartInfo *)rootdev->private_data;
784    else
785	root = NULL;
786
787    command_clear();
788    upgrade = str && !strcmp(str, "upgrade");
789
790    if (swapdev) {
791	/* As the very first thing, try to get ourselves some swap space */
792	sprintf(dname, "/dev/%s", swapdev->name);
793	if (!Fake && (!MakeDevChunk(swapdev, "/dev") || !file_readable(dname))) {
794	    msgConfirm("Unable to make device node for %s in /dev!\n"
795		       "The creation of filesystems will be aborted.", dname);
796	    return DITEM_FAILURE;
797	}
798
799	if (!Fake) {
800	    if (!swapon(dname))
801		msgNotify("Added %s as initial swap device", dname);
802	    else
803		msgConfirm("WARNING!  Unable to swap to %s: %s\n"
804			   "This may cause the installation to fail at some point\n"
805			   "if you don't have a lot of memory.", dname, strerror(errno));
806	}
807    }
808
809    if (rootdev) {
810	/* Next, create and/or mount the root device */
811	sprintf(dname, "/dev/r%sa", rootdev->disk->name);
812	if (!Fake && (!MakeDevChunk(rootdev, "/dev") || !file_readable(dname))) {
813	    msgConfirm("Unable to make device node for %s in /dev!\n"
814		       "The creation of filesystems will be aborted.", dname);
815	    return DITEM_FAILURE;
816	}
817	if (strcmp(root->mountpoint, "/"))
818	    msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", rootdev->name, root->mountpoint);
819
820	if (root->newfs) {
821	    int i;
822
823	    msgNotify("Making a new root filesystem on %s", dname);
824	    i = vsystem("%s %s", root->newfs_cmd, dname);
825	    if (i) {
826		msgConfirm("Unable to make new root filesystem on %s!\n"
827			   "Command returned status %d", dname, i);
828		return DITEM_FAILURE;
829	    }
830	}
831	else {
832	    if (!upgrade) {
833		msgConfirm("Warning:  Using existing root partition.  It will be assumed\n"
834			   "that you have the appropriate device entries already in /dev.");
835	    }
836	    msgNotify("Checking integrity of existing %s filesystem.", dname);
837	    i = vsystem("fsck -y %s", dname);
838	    if (i)
839		msgConfirm("Warning: fsck returned status of %d for %s.\n"
840			   "This partition may be unsafe to use.", i, dname);
841	}
842
843	/* Switch to block device */
844	sprintf(dname, "/dev/%sa", rootdev->disk->name);
845	if (Mount("/mnt", dname)) {
846	    msgConfirm("Unable to mount the root file system on %s!  Giving up.", dname);
847	    return DITEM_FAILURE;
848	}
849    }
850
851    /* Now buzz through the rest of the partitions and mount them too */
852    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
853    for (i = 0; devs[i]; i++) {
854	if (!devs[i]->enabled)
855	    continue;
856
857	disk = (Disk *)devs[i]->private;
858	if (!disk->chunks) {
859	    msgConfirm("No chunk list found for %s!", disk->name);
860	    return DITEM_FAILURE;
861	}
862	if (root && (root->newfs || upgrade)) {
863	    Mkdir("/mnt/dev");
864	    if (!Fake)
865		MakeDevDisk(disk, "/mnt/dev");
866	}
867
868	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
869	    if (c1->type == freebsd) {
870		for (c2 = c1->part; c2; c2 = c2->next) {
871		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
872			PartInfo *tmp = (PartInfo *)c2->private_data;
873
874			/* Already did root */
875			if (c2 == rootdev)
876			    continue;
877
878			if (tmp->newfs)
879			    command_shell_add(tmp->mountpoint, "%s /mnt/dev/r%s", tmp->newfs_cmd, c2->name);
880			else
881			    command_shell_add(tmp->mountpoint, "fsck -y /mnt/dev/r%s", c2->name);
882			command_func_add(tmp->mountpoint, Mount, c2->name);
883		    }
884		    else if (c2->type == part && c2->subtype == FS_SWAP) {
885			char fname[80];
886			int i;
887
888			if (c2 == swapdev)
889			    continue;
890			sprintf(fname, "/mnt/dev/%s", c2->name);
891			i = (Fake || swapon(fname));
892			if (!i)
893			    msgNotify("Added %s as an additional swap device", fname);
894			else
895			    msgConfirm("Unable to add %s as a swap device: %s", fname, strerror(errno));
896		    }
897		}
898	    }
899	    else if (c1->type == fat && c1->private_data && (root->newfs || upgrade)) {
900		char name[FILENAME_MAX];
901
902		sprintf(name, "/mnt%s", ((PartInfo *)c1->private_data)->mountpoint);
903		Mkdir(name);
904	    }
905	}
906    }
907
908    msgNotify("Copying initial device files..");
909    /* Copy the boot floppy's dev files */
910    if ((root->newfs || upgrade) && vsystem("find -x /dev | cpio %s -pdum /mnt", cpioVerbosity())) {
911	msgConfirm("Couldn't clone the /dev files!");
912	return DITEM_FAILURE;
913    }
914
915    command_sort();
916    command_execute();
917    return DITEM_SUCCESS;
918}
919
920/* Initialize various user-settable values to their defaults */
921int
922installVarDefaults(dialogMenuItem *self)
923{
924    char *cp;
925
926    /* Set default startup options */
927    variable_set2(VAR_ROUTER,			"NO");
928    variable_set2(VAR_RELNAME,			RELEASE_NAME);
929    variable_set2(VAR_CPIO_VERBOSITY,		"high");
930    variable_set2(VAR_TAPE_BLOCKSIZE,		DEFAULT_TAPE_BLOCKSIZE);
931    variable_set2(VAR_INSTALL_ROOT,		"/");
932    cp = getenv("EDITOR");
933    if (!cp)
934	cp = "/usr/bin/ee";
935    variable_set2(VAR_EDITOR,			cp);
936    variable_set2(VAR_FTP_USER,			"ftp");
937    variable_set2(VAR_BROWSER_PACKAGE,		PACKAGE_LYNX);
938    variable_set2(VAR_BROWSER_BINARY,		"/usr/local/bin/lynx");
939    variable_set2(VAR_FTP_STATE,		"passive");
940    variable_set2(VAR_PKG_TMPDIR,		"/usr/tmp");
941    if (getpid() != 1)
942	variable_set2(SYSTEM_STATE,		"update");
943    else
944	variable_set2(SYSTEM_STATE,		"init");
945    return DITEM_SUCCESS;
946}
947
948/* Copy the boot floppy contents into /stand */
949Boolean
950copySelf(void)
951{
952    int i;
953
954    msgWeHaveOutput("Copying the boot floppy to /stand on root filesystem");
955    i = vsystem("find -x /stand | cpio %s -pdum /mnt", cpioVerbosity());
956    if (i) {
957	msgConfirm("Copy returned error status of %d!", i);
958	return FALSE;
959    }
960
961    /* Copy the /etc files into their rightful place */
962    if (vsystem("cd /mnt/stand; find etc | cpio %s -pdum /mnt", cpioVerbosity())) {
963	msgConfirm("Couldn't copy up the /etc files!");
964	return TRUE;
965    }
966    return TRUE;
967}
968
969static void
970create_termcap(void)
971{
972    FILE *fp;
973
974    const char *caps[] = {
975	termcap_vt100, termcap_cons25, termcap_cons25_m, termcap_cons25r,
976	termcap_cons25r_m, termcap_cons25l1, termcap_cons25l1_m, NULL,
977    };
978    const char **cp;
979
980    if (!file_readable(TERMCAP_FILE)) {
981	Mkdir("/usr/share/misc");
982	fp = fopen(TERMCAP_FILE, "w");
983	if (!fp) {
984	    msgConfirm("Unable to initialize termcap file. Some screen-oriented\nutilities may not work.");
985	    return;
986	}
987	cp = caps;
988	while (*cp)
989	    fprintf(fp, "%s\n", *(cp++));
990	fclose(fp);
991    }
992}
993
994#ifdef SAVE_USERCONFIG
995static void
996save_userconfig_to_kernel(char *kern)
997{
998    struct kernel *core, *boot;
999    struct list *c_isa, *b_isa, *c_dev, *b_dev;
1000    int i, d;
1001
1002    if ((core = uc_open("-incore")) == NULL) {
1003	msgDebug("save_userconf: Can't read in-core information for kernel.\n");
1004	return;
1005    }
1006
1007    if ((boot = uc_open(kern)) == NULL) {
1008	msgDebug("save_userconf: Can't read device information for kernel image %s\n", kern);
1009	return;
1010    }
1011
1012    msgNotify("Saving any boot -c changes to new kernel...");
1013    c_isa = uc_getdev(core, "-isa");
1014    b_isa = uc_getdev(boot, "-isa");
1015    if (isDebug())
1016	msgDebug("save_userconf: got %d ISA device entries from core, %d from boot.\n", c_isa->ac, b_isa->ac);
1017    for (d = 0; d < c_isa->ac; d++) {
1018	if (isDebug())
1019	    msgDebug("save_userconf: ISA device loop, c_isa->av[%d] = %s\n", d, c_isa->av[d]);
1020	if (strcmp(c_isa->av[d], "npx0")) { /* special case npx0, which mucks with its id_irq member */
1021	    c_dev = uc_getdev(core, c_isa->av[d]);
1022	    b_dev = uc_getdev(boot, b_isa->av[d]);
1023	    if (!c_dev || !b_dev) {
1024		msgDebug("save_userconf: c_dev: %x b_dev: %x\n", c_dev, b_dev);
1025		continue;
1026	    }
1027	    if (isDebug())
1028		msgDebug("save_userconf: ISA device %s: %d config parameters (core), %d (boot)\n",
1029			 c_isa->av[d], c_dev->ac, b_dev->ac);
1030	    for (i = 0; i < c_dev->ac; i++) {
1031		if (isDebug())
1032		    msgDebug("save_userconf: c_dev->av[%d] = %s, b_dev->av[%d] = %s\n", i, c_dev->av[i], i, b_dev->av[i]);
1033		if (strcmp(c_dev->av[i], b_dev->av[i])) {
1034		    if (isDebug())
1035			msgDebug("save_userconf: %s (boot) -> %s (core)\n",
1036				 c_dev->av[i], b_dev->av[i]);
1037		    isa_setdev(boot, c_dev);
1038		}
1039	    }
1040	}
1041	else {
1042	    if (isDebug())
1043		msgDebug("skipping npx0\n");
1044	}
1045    }
1046    if (isDebug())
1047	msgDebug("Closing kernels\n");
1048    uc_close(core, 0);
1049    uc_close(boot, 1);
1050}
1051#endif
1052