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