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