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