install.c revision 45971
1178479Sjb/*
2178479Sjb * The new sysinstall program.
3178479Sjb *
4178479Sjb * This is probably the last program in the `sysinstall' line - the next
5178479Sjb * generation being essentially a complete rewrite.
6178479Sjb *
7178479Sjb * $Id: install.c,v 1.230 1999/03/11 09:38:06 jkh Exp $
8178479Sjb *
9178479Sjb * Copyright (c) 1995
10178479Sjb *	Jordan Hubbard.  All rights reserved.
11178479Sjb *
12178479Sjb * Redistribution and use in source and binary forms, with or without
13178479Sjb * modification, are permitted provided that the following conditions
14178479Sjb * are met:
15178479Sjb * 1. Redistributions of source code must retain the above copyright
16178479Sjb *    notice, this list of conditions and the following disclaimer,
17178479Sjb *    verbatim and that no modifications are made prior to this
18178479Sjb *    point in the file.
19178479Sjb * 2. Redistributions in binary form must reproduce the above copyright
20178479Sjb *    notice, this list of conditions and the following disclaimer in the
21178479Sjb *    documentation and/or other materials provided with the distribution.
22178479Sjb *
23178479Sjb * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
24178479Sjb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25178479Sjb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26178479Sjb * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
27178479Sjb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28178479Sjb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29178479Sjb * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
30178479Sjb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31178479Sjb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32178479Sjb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33178479Sjb * SUCH DAMAGE.
34178479Sjb *
35178479Sjb */
36178479Sjb
37178479Sjb#include "sysinstall.h"
38178479Sjb#include <ctype.h>
39178479Sjb#include <sys/disklabel.h>
40178479Sjb#include <sys/errno.h>
41178479Sjb#include <sys/ioctl.h>
42178479Sjb#include <sys/fcntl.h>
43178479Sjb#include <sys/wait.h>
44178479Sjb#include <sys/param.h>
45178479Sjb#define MSDOSFS
46178479Sjb#include <sys/mount.h>
47178479Sjb#include <ufs/ufs/ufsmount.h>
48178479Sjb#include <msdosfs/msdosfsmount.h>
49178479Sjb#undef MSDOSFS
50178479Sjb#include <sys/stat.h>
51178479Sjb#include <sys/sysctl.h>
52178479Sjb#include <unistd.h>
53178479Sjb
54178479Sjbstatic void	create_termcap(void);
55178479Sjbstatic void	fixit_common(void);
56178479Sjb
57178479Sjb#define TERMCAP_FILE	"/usr/share/misc/termcap"
58178572Sjb
59178479Sjbstatic void	installConfigure(void);
60178479Sjb
61178479SjbBoolean
62178479SjbcheckLabels(Boolean whinge, Chunk **rdev, Chunk **sdev, Chunk **udev, Chunk **vdev)
63178479Sjb{
64178479Sjb    Device **devs;
65178479Sjb    Boolean status;
66178479Sjb    Disk *disk;
67178479Sjb    Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev;
68178479Sjb    int i;
69178479Sjb
70178479Sjb    /* Don't allow whinging if noWarn is set */
71178479Sjb    if (variable_get(VAR_NO_WARN))
72178479Sjb	whinge = FALSE;
73178479Sjb
74178479Sjb    status = TRUE;
75178479Sjb    *rdev = *sdev = *udev = *vdev = rootdev = swapdev = usrdev = vardev = NULL;
76178479Sjb
77178479Sjb    /* We don't need to worry about root/usr/swap if we're already multiuser */
78178479Sjb    if (!RunningAsInit)
79178479Sjb	return status;
80178479Sjb
81178479Sjb    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
82178479Sjb    /* First verify that we have a root device */
83178479Sjb    for (i = 0; devs[i]; i++) {
84178479Sjb	if (!devs[i]->enabled)
85178479Sjb	    continue;
86178479Sjb	disk = (Disk *)devs[i]->private;
87178479Sjb	msgDebug("Scanning disk %s for root filesystem\n", disk->name);
88178572Sjb	if (!disk->chunks)
89178479Sjb	    msgFatal("No chunk list found for %s!", disk->name);
90178572Sjb	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
91178572Sjb	    if (c1->type == freebsd) {
92178572Sjb		for (c2 = c1->part; c2; c2 = c2->next) {
93178572Sjb		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
94178479Sjb			if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/")) {
95178479Sjb			    if (rootdev) {
96178479Sjb				if (whinge)
97178479Sjb				    msgConfirm("WARNING:  You have more than one root device set?!\n"
98178479Sjb					       "Using the first one found.");
99178479Sjb				continue;
100178479Sjb			    }
101178479Sjb			    else {
102178479Sjb				rootdev = c2;
103178479Sjb				if (isDebug())
104178572Sjb				    msgDebug("Found rootdev at %s!\n", rootdev->name);
105178479Sjb			    }
106178479Sjb			}
107178572Sjb			else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/usr")) {
108178572Sjb			    if (usrdev) {
109178572Sjb				if (whinge)
110178572Sjb				    msgConfirm("WARNING:  You have more than one /usr filesystem.\n"
111178479Sjb					       "Using the first one found.");
112178479Sjb				continue;
113178479Sjb			    }
114178479Sjb			    else {
115178479Sjb				usrdev = c2;
116178479Sjb				if (isDebug())
117178479Sjb				    msgDebug("Found usrdev at %s!\n", usrdev->name);
118178479Sjb			    }
119178479Sjb			}
120178479Sjb			else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/var")) {
121178479Sjb			    if (vardev) {
122178572Sjb				if (whinge)
123178479Sjb				    msgConfirm("WARNING:  You have more than one /var filesystem.\n"
124178479Sjb					       "Using the first one found.");
125178479Sjb				continue;
126178479Sjb			    }
127178572Sjb			    else {
128178479Sjb				vardev = c2;
129178479Sjb				if (isDebug())
130178479Sjb				    msgDebug("Found vardev at %s!\n", vardev->name);
131178479Sjb			    }
132178479Sjb			}
133178479Sjb		    }
134178479Sjb		}
135178479Sjb	    }
136178479Sjb	}
137178479Sjb    }
138178479Sjb
139178479Sjb    /* Now check for swap devices */
140178479Sjb    for (i = 0; devs[i]; i++) {
141178479Sjb	if (!devs[i]->enabled)
142178479Sjb	    continue;
143178479Sjb	disk = (Disk *)devs[i]->private;
144178479Sjb	msgDebug("Scanning disk %s for swap partitions\n", disk->name);
145178572Sjb	if (!disk->chunks)
146178479Sjb	    msgFatal("No chunk list found for %s!", disk->name);
147178479Sjb	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
148178479Sjb	    if (c1->type == freebsd) {
149178479Sjb		for (c2 = c1->part; c2; c2 = c2->next) {
150178479Sjb		    if (c2->type == part && c2->subtype == FS_SWAP && !swapdev) {
151178479Sjb			swapdev = c2;
152178479Sjb			if (isDebug())
153178479Sjb			    msgDebug("Found swapdev at %s!\n", swapdev->name);
154178479Sjb			break;
155178479Sjb		    }
156178572Sjb		}
157178479Sjb	    }
158178572Sjb	}
159178572Sjb    }
160178479Sjb
161178479Sjb    /* Copy our values over */
162178479Sjb    *rdev = rootdev;
163178479Sjb    *sdev = swapdev;
164178479Sjb    *udev = usrdev;
165178479Sjb    *vdev = vardev;
166178479Sjb
167178479Sjb    if (!rootdev && whinge) {
168178572Sjb	msgConfirm("No root device found - you must label a partition as /\n"
169178479Sjb		   "in the label editor.");
170178572Sjb	status = FALSE;
171178572Sjb    }
172178479Sjb    if (!swapdev && whinge) {
173178479Sjb	msgConfirm("No swap devices found - you must create at least one\n"
174178479Sjb		   "swap partition.");
175178479Sjb	status = FALSE;
176178479Sjb    }
177178479Sjb    if (!usrdev && whinge && !variable_get(VAR_NO_USR)) {
178178479Sjb	msgConfirm("WARNING:  No /usr filesystem found.  This is not technically\n"
179178479Sjb		   "an error if your root filesystem is big enough (or you later\n"
180178479Sjb		   "intend to mount your /usr filesystem over NFS), but it may otherwise\n"
181178479Sjb		   "cause you trouble if you're not exactly sure what you are doing!");
182178479Sjb    }
183178479Sjb    if (!vardev && whinge && variable_cmp(SYSTEM_STATE, "upgrade")) {
184178572Sjb	msgConfirm("WARNING:  No /var filesystem found.  This is not technically\n"
185178479Sjb		   "an error if your root filesystem is big enough (or you later\n"
186178572Sjb		   "intend to link /var to someplace else), but it may otherwise\n"
187178572Sjb		   "cause your root filesystem to fill up if you receive lots of mail\n"
188178572Sjb		   "or edit large temporary files.");
189178572Sjb    }
190178479Sjb    return status;
191178479Sjb}
192178479Sjb
193178479Sjbstatic int
194178479SjbinstallInitial(void)
195178572Sjb{
196178572Sjb    static Boolean alreadyDone = FALSE;
197178479Sjb    int status = DITEM_SUCCESS;
198178479Sjb
199178479Sjb    if (alreadyDone)
200178479Sjb	return DITEM_SUCCESS;
201178479Sjb
202178479Sjb    if (!variable_get(DISK_LABELLED)) {
203178479Sjb	msgConfirm("You need to assign disk labels before you can proceed with\n"
204178479Sjb		   "the installation.");
205178479Sjb	return DITEM_FAILURE;
206178479Sjb    }
207    /* If it's labelled, assume it's also partitioned */
208    if (!variable_get(DISK_PARTITIONED))
209	variable_set2(DISK_PARTITIONED, "yes", 0);
210
211    /* If we refuse to proceed, bail. */
212    dialog_clear_norefresh();
213    if (!variable_get(VAR_NO_WARN))
214	if (msgYesNo(
215	    "Last Chance!  Are you SURE you want continue the installation?\n\n"
216	     "If you're running this on a disk with data you wish to save\n"
217	     "then WE STRONGLY ENCOURAGE YOU TO MAKE PROPER BACKUPS before\n"
218	     "proceeding!\n\n"
219	     "We can take no responsibility for lost disk contents!") != 0)
220	return DITEM_FAILURE | DITEM_RESTORE;
221
222    if (DITEM_STATUS(diskLabelCommit(NULL)) != DITEM_SUCCESS) {
223	msgConfirm("Couldn't make filesystems properly.  Aborting.");
224	return DITEM_FAILURE;
225    }
226
227    if (!copySelf()) {
228	msgConfirm("installInitial: Couldn't clone the boot floppy onto the\n"
229		   "root file system.  Aborting!");
230	return DITEM_FAILURE;
231    }
232
233    if (chroot("/mnt") == -1) {
234	msgConfirm("installInitial: Unable to chroot to %s - this is bad!",
235		   "/mnt");
236	return DITEM_FAILURE;
237    }
238
239    chdir("/");
240    variable_set2(RUNNING_ON_ROOT, "yes", 0);
241
242    /* Configure various files in /etc */
243    if (DITEM_STATUS(configResolv(NULL)) == DITEM_FAILURE)
244	status = DITEM_FAILURE;
245    if (DITEM_STATUS(configFstab(NULL)) == DITEM_FAILURE)
246	status = DITEM_FAILURE;
247
248    /* stick a helpful shell over on the 4th VTY */
249    systemCreateHoloshell();
250
251    alreadyDone = TRUE;
252    return status;
253}
254
255int
256installFixitHoloShell(dialogMenuItem *self)
257{
258    systemCreateHoloshell();
259    return DITEM_SUCCESS;
260}
261
262int
263installFixitCDROM(dialogMenuItem *self)
264{
265    struct stat sb;
266
267    if (!RunningAsInit)
268	return DITEM_SUCCESS;
269
270    variable_set2(SYSTEM_STATE, "fixit", 0);
271    (void)unlink("/mnt2");
272    (void)rmdir("/mnt2");
273
274    while (1) {
275	msgConfirm("Please insert a FreeBSD live filesystem CDROM and press return");
276	if (DITEM_STATUS(mediaSetCDROM(NULL)) != DITEM_SUCCESS || !mediaDevice || !mediaDevice->init(mediaDevice)) {
277	    /* If we can't initialize it, it's probably not a FreeBSD CDROM so punt on it */
278	    mediaClose();
279	    if (msgYesNo("Unable to mount the CDROM - do you want to try again?") != 0)
280		return DITEM_FAILURE;
281	}
282	else
283	    break;
284    }
285
286    /* Since the fixit code expects everything to be in /mnt2, and the CDROM mounting stuff /dist, do
287     * a little kludge dance here..
288     */
289    if (symlink("/dist", "/mnt2")) {
290	msgConfirm("Unable to symlink /mnt2 to the CDROM mount point.  Please report this\n"
291		   "unexpected failure to freebsd-bugs@FreeBSD.org.");
292	return DITEM_FAILURE;
293    }
294
295    /*
296     * If /tmp points to /mnt2/tmp from a previous fixit floppy session, it's
297     * not very good for us if we point it to the CDROM now.  Rather make it
298     * a directory in the root MFS then.  Experienced admins will still be
299     * able to mount their disk's /tmp over this if they need.
300     */
301    if (lstat("/tmp", &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFLNK)
302	(void)unlink("/tmp");
303    Mkdir("/tmp");
304
305    /*
306     * Since setuid binaries ignore LD_LIBRARY_PATH, we indeed need the
307     * ld.so.hints file.  Fortunately, it's fairly small (~ 3 KB).
308     */
309    if (!file_readable("/var/run/ld.so.hints")) {
310	Mkdir("/var/run");
311	if (vsystem("/mnt2/sbin/ldconfig -s /mnt2/usr/lib")) {
312	    msgConfirm("Warning: ldconfig could not create the ld.so hints file.\n"
313		       "Dynamic executables from the CDROM likely won't work.");
314	}
315    }
316
317    /* Yet more iggly hardcoded pathnames. */
318    Mkdir("/usr/libexec");
319    if (!file_readable("/usr/libexec/ld.so")) {
320	if (symlink("/mnt2/usr/libexec/ld.so", "/usr/libexec/ld.so"))
321	    msgDebug("Couldn't link to ld.so - not necessarily a problem for ELF\n");
322    }
323    if (!file_readable("/usr/libexec/ld-elf.so.1")) {
324	if (symlink("/mnt2/usr/libexec/ld-elf.so.1", "/usr/libexec/ld-elf.so.1")) {
325	    msgConfirm("Warning: could not create the symlink for ld-elf.so.1\n"
326		       "Dynamic executables from the CDROM likely won't work.");
327	}
328    }
329    /* optional nicety */
330    if (!file_readable("/usr/bin/vi"))
331	symlink("/mnt2/usr/bin/vi", "/usr/bin/vi");
332    fixit_common();
333    mediaClose();
334    msgConfirm("Please remove the FreeBSD fixit CDROM now.");
335    return DITEM_SUCCESS;
336}
337
338int
339installFixitFloppy(dialogMenuItem *self)
340{
341    struct ufs_args args;
342    extern char *distWanted;
343
344    if (!RunningAsInit)
345	return DITEM_SUCCESS;
346
347    /* Try to open the floppy drive */
348    if (DITEM_STATUS(mediaSetFloppy(NULL)) == DITEM_FAILURE || !mediaDevice) {
349	msgConfirm("Unable to set media device to floppy.");
350	mediaClose();
351	return DITEM_FAILURE;
352    }
353
354    memset(&args, 0, sizeof(args));
355    args.fspec = mediaDevice->devname;
356    mediaDevice->private = "/mnt2";
357    distWanted = NULL;
358    Mkdir("/mnt2");
359
360    variable_set2(SYSTEM_STATE, "fixit", 0);
361
362    while (1) {
363	if (!mediaDevice->init(mediaDevice)) {
364	    if (msgYesNo("The attempt to mount the fixit floppy failed, bad floppy\n"
365			 "or unclean filesystem.  Do you want to try again?"))
366		return DITEM_FAILURE;
367	}
368	else
369	    break;
370    }
371    if (!directory_exists("/tmp"))
372	(void)symlink("/mnt2/tmp", "/tmp");
373    fixit_common();
374    mediaClose();
375    msgConfirm("Please remove the fixit floppy now.");
376    return DITEM_SUCCESS;
377}
378
379/*
380 * The common code for both fixit variants.
381 */
382static void
383fixit_common(void)
384{
385    pid_t child;
386    int waitstatus;
387
388    if (!directory_exists("/var/tmp/vi.recover")) {
389	if (DITEM_STATUS(Mkdir("/var/tmp/vi.recover")) != DITEM_SUCCESS) {
390	    msgConfirm("Warning:  Was unable to create a /var/tmp/vi.recover directory.\n"
391		       "vi will kvetch and moan about it as a result but should still\n"
392		       "be essentially usable.");
393	}
394    }
395    if (!directory_exists("/bin"))
396	(void)Mkdir("/bin");
397    (void)symlink("/stand/sh", "/bin/sh");
398    /* Link the /etc/ files */
399    if (DITEM_STATUS(Mkdir("/etc")) != DITEM_SUCCESS)
400	msgConfirm("Unable to create an /etc directory!  Things are weird on this floppy..");
401    else if ((symlink("/mnt2/etc/spwd.db", "/etc/spwd.db") == -1 && errno != EEXIST) ||
402	     (symlink("/mnt2/etc/protocols", "/etc/protocols") == -1 && errno != EEXIST) ||
403	     (symlink("/mnt2/etc/services", "/etc/services") == -1 && errno != EEXIST))
404	msgConfirm("Couldn't symlink the /etc/ files!  I'm not sure I like this..");
405    if (!file_readable(TERMCAP_FILE))
406	create_termcap();
407    if (!(child = fork())) {
408	int i, fd;
409	struct termios foo;
410	extern int login_tty(int);
411
412	ioctl(0, TIOCNOTTY, NULL);
413	for (i = getdtablesize(); i >= 0; --i)
414	    close(i);
415	fd = open("/dev/ttyv3", O_RDWR);
416	ioctl(0, TIOCSCTTY, &fd);
417	dup2(0, 1);
418	dup2(0, 2);
419	DebugFD = 2;
420	if (login_tty(fd) == -1)
421	    msgDebug("fixit: I can't set the controlling terminal.\n");
422
423	signal(SIGTTOU, SIG_IGN);
424	if (tcgetattr(0, &foo) != -1) {
425	    foo.c_cc[VERASE] = '\010';
426	    if (tcsetattr(0, TCSANOW, &foo) == -1)
427		msgDebug("fixit shell: Unable to set erase character.\n");
428	}
429	else
430	    msgDebug("fixit shell: Unable to get terminal attributes!\n");
431	setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/stand:"
432	       "/mnt2/stand:/mnt2/bin:/mnt2/sbin:/mnt2/usr/bin:/mnt2/usr/sbin", 1);
433	/* use the .profile from the fixit medium */
434	setenv("HOME", "/mnt2", 1);
435	chdir("/mnt2");
436	execlp("sh", "-sh", 0);
437	msgDebug("fixit shell: Failed to execute shell!\n");
438	_exit(1);;
439    }
440    else {
441	msgNotify("Waiting for fixit shell to exit.  Go to VTY4 now by\n"
442		  "typing ALT-F4.  When you are done, type ``exit'' to exit\n"
443		  "the fixit shell and be returned here.");
444	(void)waitpid(child, &waitstatus, 0);
445    }
446    dialog_clear();
447}
448
449
450int
451installExpress(dialogMenuItem *self)
452{
453    int i;
454
455    variable_set2(SYSTEM_STATE, "express", 0);
456#ifndef __alpha__
457    if (DITEM_STATUS((i = diskPartitionEditor(self))) == DITEM_FAILURE)
458	return i;
459#endif
460
461    if (DITEM_STATUS((i = diskLabelEditor(self))) == DITEM_FAILURE)
462	return i;
463
464    dialog_clear_norefresh();
465    if (DITEM_STATUS((i = installCommit(self))) == DITEM_SUCCESS) {
466	i |= DITEM_LEAVE_MENU;
467	/* Give user the option of one last configuration spree */
468	installConfigure();
469    }
470    return i | DITEM_RESTORE;
471}
472
473/* Novice mode installation */
474int
475installNovice(dialogMenuItem *self)
476{
477    int i, tries = 0;
478    Device **devs;
479
480    variable_set2(SYSTEM_STATE, "novice", 0);
481#ifndef __alpha__
482    dialog_clear_norefresh();
483    msgConfirm("In the next menu, you will need to set up a DOS-style (\"fdisk\") partitioning\n"
484	       "scheme for your hard disk.  If you simply wish to devote all disk space\n"
485	       "to FreeBSD (overwriting anything else that might be on the disk(s) selected)\n"
486	       "then use the (A)ll command to select the default partitioning scheme followed\n"
487	       "by a (Q)uit.  If you wish to allocate only free space to FreeBSD, move to a\n"
488	       "partition marked \"unused\" and use the (C)reate command.");
489
490nodisks:
491    if (DITEM_STATUS(diskPartitionEditor(self)) == DITEM_FAILURE)
492	return DITEM_FAILURE;
493
494    if (diskGetSelectCount(&devs) <= 0 && tries < 3) {
495	msgConfirm("You need to select some disks to operate on!  Be sure to use SPACE\n"
496		   "instead of RETURN in the disk selection menu when selecting a disk.");
497	++tries;
498	goto nodisks;
499    }
500#endif
501
502    dialog_clear_norefresh();
503#ifdef __alpha__
504    msgConfirm("First, you need to create BSD partitions on the disk which you are\n"
505	       "installing to.  If you have a reasonable amount of disk space (200MB or more)\n"
506	       "and don't have any special requirements, simply use the (A)uto command to\n"
507	       "allocate space automatically.  If you have more specific needs or just don't\n"
508	       "care for the layout chosen by (A)uto, press F1 for more information on\n"
509	       "manual layout.");
510#else
511    msgConfirm("First, you need to create BSD partitions inside of the fdisk partition(s)\n"
512	       "just created.  If you have a reasonable amount of disk space (200MB or more)\n"
513	       "and don't have any special requirements, simply use the (A)uto command to\n"
514	       "allocate space automatically.  If you have more specific needs or just don't\n"
515	       "care for the layout chosen by (A)uto, press F1 for more information on\n"
516	       "manual layout.");
517#endif
518
519    if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE)
520	return DITEM_FAILURE;
521
522    dialog_clear_norefresh();
523    if (DITEM_STATUS((i = installCommit(self))) == DITEM_FAILURE) {
524	dialog_clear_norefresh();
525	msgConfirm("Installation completed with some errors.  You may wish to\n"
526		   "scroll through the debugging messages on VTY1 with the\n"
527		   "scroll-lock feature.  You can also chose \"No\" at the next\n"
528		   "prompt and go back into the installation menus to try and retry\n"
529		   "whichever operations have failed.");
530	return i | DITEM_RESTORE;
531
532    }
533    else {
534	dialog_clear_norefresh();
535	msgConfirm("Congratulations!  You now have FreeBSD installed on your system.\n\n"
536		   "We will now move on to the final configuration questions.\n"
537		   "For any option you do not wish to configure, simply select\n"
538		   "No.\n\n"
539		   "If you wish to re-enter this utility after the system is up, you\n"
540		   "may do so by typing: /stand/sysinstall.");
541    }
542    if (mediaDevice->type != DEVICE_TYPE_FTP && mediaDevice->type != DEVICE_TYPE_NFS) {
543	if (!msgYesNo("Would you like to configure any Ethernet or SLIP/PPP network devices?")) {
544	    Device *tmp;
545
546	    dialog_clear_norefresh();
547	    tmp = tcpDeviceSelect();
548	    dialog_clear_norefresh();
549	    if (tmp && !msgYesNo("Would you like to bring the %s interface up right now?", tmp->name))
550		if (!tmp->init(tmp))
551		    msgConfirm("Initialization of %s device failed.", tmp->name);
552	}
553    }
554
555    dialog_clear_norefresh();
556    if (!msgYesNo("Will this machine be an IP gateway (e.g. will it forward packets\n"
557		  "between interfaces)?"))
558	variable_set2("gateway_enable", "YES", 1);
559
560    dialog_clear_norefresh();
561    if (!msgYesNo("Do you want to allow anonymous FTP connections to this machine?"))
562	configAnonFTP(self);
563
564    dialog_clear_norefresh();
565    if (!msgYesNo("Do you want to configure this machine as an NFS server?"))
566	configNFSServer(self);
567
568    dialog_clear_norefresh();
569    if (!msgYesNo("Do you want to configure this machine as an NFS client?"))
570	variable_set2("nfs_client_enable", "YES", 1);
571
572    dialog_clear_norefresh();
573    if (!msgYesNo("Would you like to customize your system console settings?")) {
574	WINDOW *w = savescr();
575
576	dmenuOpenSimple(&MenuSyscons, FALSE);
577	restorescr(w);
578    }
579
580    dialog_clear_norefresh();
581    if (!msgYesNo("Would you like to set this machine's time zone now?")) {
582	WINDOW *w = savescr();
583
584	dialog_clear();
585	systemExecute("tzsetup");
586	restorescr(w);
587    }
588
589    dialog_clear_norefresh();
590    if (!msgYesNo("Does this system have a mouse attached to it?")) {
591	WINDOW *w = savescr();
592
593	dmenuOpenSimple(&MenuMouse, FALSE);
594	restorescr(w);
595    }
596
597    /* Now would be a good time to checkpoint the configuration data */
598    configRC_conf();
599    sync();
600
601    if (directory_exists("/usr/X11R6")) {
602	dialog_clear_norefresh();
603	if (!msgYesNo("Would you like to configure your X server at this time?"))
604	    configXEnvironment(self);
605    }
606
607    dialog_clear_norefresh();
608    if (!msgYesNo("The FreeBSD package collection is a collection of hundreds of ready-to-run\n"
609		  "applications, from text editors to games to WEB servers and more.  Would you\n"
610		  "like to browse the collection now?"))
611	configPackages(self);
612
613    dialog_clear_norefresh();
614    if (!msgYesNo("Would you like to add any initial user accounts to the system?\n"
615		  "Adding at least one account for yourself at this stage is suggested\n"
616		  "since working as the \"root\" user is dangerous (it is easy to do\n"
617		  "things which adversely affect the entire system)."))
618	configUsers(self);
619
620    dialog_clear_norefresh();
621    msgConfirm("Now you must set the system manager's password.\n"
622	       "This is the password you'll use to log in as \"root\".");
623    {
624	WINDOW *w = savescr();
625
626	if (!systemExecute("passwd root"))
627	    variable_set2("root_password", "YES", 0);
628	restorescr(w);
629    }
630
631    /* XXX Put whatever other nice configuration questions you'd like to ask the user here XXX */
632
633    /* Give user the option of one last configuration spree */
634    dialog_clear_norefresh();
635    installConfigure();
636
637    return DITEM_LEAVE_MENU | DITEM_RESTORE;
638}
639
640/* The version of commit we call from the Install Custom menu */
641int
642installCustomCommit(dialogMenuItem *self)
643{
644    int i;
645
646    dialog_clear_norefresh();
647    i = installCommit(self);
648    if (DITEM_STATUS(i) == DITEM_SUCCESS) {
649	/* Give user the option of one last configuration spree */
650	installConfigure();
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
674    if (!Dists)
675	distConfig(NULL);
676
677    if (!Dists)
678	if (!dmenuOpenSimple(&MenuDistributions, FALSE) && !Dists)
679	    return DITEM_FAILURE | DITEM_RESTORE;
680
681    if (!mediaVerify())
682	return DITEM_FAILURE | DITEM_RESTORE;
683
684    str = variable_get(SYSTEM_STATE);
685    if (isDebug())
686	msgDebug("installCommit: System state is `%s'\n", str);
687
688    /* Installation stuff we wouldn't do to a running system */
689    if (RunningAsInit && DITEM_STATUS((i = installInitial())) == DITEM_FAILURE)
690	return i;
691
692try_media:
693    if (!mediaDevice->init(mediaDevice)) {
694	if (!msgYesNo("Unable to initialize selected media. Would you like to\n"
695		      "adjust your media configuration and try again?")) {
696	    mediaDevice = NULL;
697	    if (!mediaVerify())
698		return DITEM_FAILURE | DITEM_RESTORE;
699	    else
700		goto try_media;
701	}
702	else
703	    return DITEM_FAILURE | DITEM_RESTORE;
704    }
705
706    /* Now go get it all */
707    i = distExtractAll(self);
708
709    /* When running as init, *now* it's safe to grab the rc.foo vars */
710    installEnvironment();
711
712    variable_set2(SYSTEM_STATE, DITEM_STATUS(i) == DITEM_FAILURE ? "error-install" : "full-install", 0);
713
714    return i | DITEM_RESTORE;
715}
716
717static void
718installConfigure(void)
719{
720    /* Final menu of last resort */
721    dialog_clear_norefresh();
722    if (!msgYesNo("Visit the general configuration menu for a chance to set\n"
723		  "any last options?")) {
724	WINDOW *w = savescr();
725
726	dmenuOpenSimple(&MenuConfigure, FALSE);
727	restorescr(w);
728    }
729    configRC_conf();
730    sync();
731}
732
733int
734installFixupBin(dialogMenuItem *self)
735{
736    Device **devs;
737    int i;
738
739    /* All of this is done only as init, just to be safe */
740    if (RunningAsInit) {
741	/* Fix up kernel first */
742	if (!file_readable("/kernel")) {
743	    if (file_readable("/kernel.GENERIC")) {
744		if (vsystem("cp -p /kernel.GENERIC /kernel")) {
745		    msgConfirm("Unable to copy /kernel into place!");
746		    return DITEM_FAILURE;
747		}
748#ifndef __alpha__
749                /* Snapshot any boot -c changes back to the new kernel */
750                if (kget("/boot/kernel.conf")) {
751		    msgConfirm("Kernel copied OK, but unable to save boot -c changes\n"
752			       "to it.  See the debug screen (ALT-F2) for details.");
753		}
754		else {
755		    if (!file_readable("/boot/loader.rc")) {
756			FILE *fp;
757
758			if ((fp = fopen("/boot/loader.rc", "w")) != NULL) {
759			     fprintf(fp, "load /kernel\n");
760			     fprintf(fp, "load -t userconfig_script /boot/kernel.conf\n");
761			     fprintf(fp, "autoboot 5\n");
762			     fclose(fp);
763			}
764		    }
765		    else {
766			msgConfirm("You already have a /boot/loader.rc file so I won't touch it.\n"
767				   "You will need to add a: load -t userconfig_script /boot/kernel.conf\n"
768				   "line to your /boot/loader.rc before your saved kernel changes\n"
769				   "(if any) can go into effect.");
770		    }
771		}
772#endif
773	    }
774	    else {
775		msgConfirm("Can't find a kernel image to link to on the root file system!\n"
776			   "You're going to have a hard time getting this system to\n"
777			   "boot from the hard disk, I'm afraid!");
778		return DITEM_FAILURE;
779	    }
780	}
781
782	/* BOGON #1: Resurrect /dev after bin distribution screws it up */
783	msgNotify("Remaking all devices.. Please wait!");
784	if (vsystem("cd /dev; sh MAKEDEV all")) {
785	    msgConfirm("MAKEDEV returned non-zero status");
786	    return DITEM_FAILURE;
787	}
788
789	msgNotify("Resurrecting /dev entries for slices..");
790	devs = deviceFind(NULL, DEVICE_TYPE_DISK);
791	if (!devs)
792	    msgFatal("Couldn't get a disk device list!");
793
794	/* Resurrect the slices that the former clobbered */
795	for (i = 0; devs[i]; i++) {
796	    Disk *disk = (Disk *)devs[i]->private;
797	    Chunk *c1;
798
799	    if (!devs[i]->enabled)
800		continue;
801	    if (!disk->chunks)
802		msgFatal("No chunk list found for %s!", disk->name);
803	    for (c1 = disk->chunks->part; c1; c1 = c1->next) {
804		if (c1->type == freebsd) {
805		    msgNotify("Making slice entries for %s", c1->name);
806		    if (vsystem("cd /dev; sh MAKEDEV %sh", c1->name)) {
807			msgConfirm("Unable to make slice entries for %s!", c1->name);
808			return DITEM_FAILURE;
809		    }
810		}
811	    }
812	}
813
814	/* BOGON #2: We leave /etc in a bad state */
815	chmod("/etc", 0755);
816
817	/* BOGON #3: No /var/db/mountdtab complains */
818	Mkdir("/var/db");
819	creat("/var/db/mountdtab", 0644);
820
821	/* BOGON #4: /compat created by default in root fs */
822	Mkdir("/usr/compat");
823	vsystem("ln -s /usr/compat /compat");
824
825	/* BOGON #5: aliases database not build for bin */
826	vsystem("newaliases");
827
828	/* Now run all the mtree stuff to fix things up */
829        vsystem("mtree -deU -f /etc/mtree/BSD.root.dist -p /");
830        vsystem("mtree -deU -f /etc/mtree/BSD.var.dist -p /var");
831        vsystem("mtree -deU -f /etc/mtree/BSD.usr.dist -p /usr");
832
833	/* Do all the last ugly work-arounds here */
834    }
835    return DITEM_SUCCESS;
836}
837
838/* Fix side-effects from the the XFree86 installation */
839int
840installFixupXFree(dialogMenuItem *self)
841{
842    /* BOGON #1:  XFree86 requires various specialized fixups */
843    if (directory_exists("/usr/X11R6")) {
844	msgNotify("Fixing permissions in XFree86 tree..");
845	vsystem("chmod -R a+r /usr/X11R6");
846	vsystem("find /usr/X11R6 -type d | xargs chmod a+x");
847
848	/* Also do bogus minimal package registration so ports don't whine */
849	if (file_readable("/usr/X11R6/lib/X11/pkgreg.tar.gz")) {
850	    msgNotify("Installing package metainfo..");
851	    vsystem("tar xpzf /usr/X11R6/lib/X11/pkgreg.tar.gz -C / && rm /usr/X11R6/lib/X11/pkgreg.tar.gz");
852	}
853    }
854    return DITEM_SUCCESS;
855}
856
857/* Go newfs and/or mount all the filesystems we've been asked to */
858int
859installFilesystems(dialogMenuItem *self)
860{
861    int i;
862    Disk *disk;
863    Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev;
864    Device **devs;
865    PartInfo *root;
866    char dname[80];
867    extern int MakeDevChunk(Chunk *c, char *n);
868    Boolean upgrade = FALSE;
869
870    /* If we've already done this, bail out */
871    if (!variable_cmp(DISK_LABELLED, "written"))
872	return DITEM_SUCCESS;
873
874    upgrade = !variable_cmp(SYSTEM_STATE, "upgrade");
875    if (!checkLabels(TRUE, &rootdev, &swapdev, &usrdev, &vardev))
876	return DITEM_FAILURE;
877
878    if (rootdev)
879	root = (PartInfo *)rootdev->private_data;
880    else
881	root = NULL;
882
883    command_clear();
884    if (swapdev && RunningAsInit) {
885	/* As the very first thing, try to get ourselves some swap space */
886	sprintf(dname, "/dev/%s", swapdev->name);
887	if (!Fake && (!MakeDevChunk(swapdev, "/dev") || !file_readable(dname))) {
888	    msgConfirm("Unable to make device node for %s in /dev!\n"
889		       "The creation of filesystems will be aborted.", dname);
890	    return DITEM_FAILURE;
891	}
892
893	if (!Fake) {
894	    if (!swapon(dname))
895		msgNotify("Added %s as initial swap device", dname);
896	    else
897		msgConfirm("WARNING!  Unable to swap to %s: %s\n"
898			   "This may cause the installation to fail at some point\n"
899			   "if you don't have a lot of memory.", dname, strerror(errno));
900	}
901    }
902
903    if (rootdev && RunningAsInit) {
904	/* Next, create and/or mount the root device */
905	sprintf(dname, "/dev/r%s", rootdev->name);
906	if (!Fake && (!MakeDevChunk(rootdev, "/dev") || !file_readable(dname))) {
907	    msgConfirm("Unable to make device node for %s in /dev!\n"
908		       "The creation of filesystems will be aborted.", dname);
909	    return DITEM_FAILURE;
910	}
911	if (strcmp(root->mountpoint, "/"))
912	    msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", rootdev->name, root->mountpoint);
913
914	if (root->newfs && (!upgrade || !msgYesNo("You are upgrading - are you SURE you want to newfs the root partition?"))) {
915	    int i;
916
917	    msgNotify("Making a new root filesystem on %s", dname);
918	    i = vsystem("%s %s", root->newfs_cmd, dname);
919	    if (i) {
920		msgConfirm("Unable to make new root filesystem on %s!\n"
921			   "Command returned status %d", dname, i);
922		return DITEM_FAILURE;
923	    }
924	}
925	else {
926	    if (!upgrade) {
927		msgConfirm("Warning:  Using existing root partition.  It will be assumed\n"
928			   "that you have the appropriate device entries already in /dev.");
929	    }
930	    msgNotify("Checking integrity of existing %s filesystem.", dname);
931	    i = vsystem("fsck -y %s", dname);
932	    if (i)
933		msgConfirm("Warning: fsck returned status of %d for %s.\n"
934			   "This partition may be unsafe to use.", i, dname);
935	}
936
937	/* Switch to block device */
938	sprintf(dname, "/dev/%s", rootdev->name);
939	if (Mount("/mnt", dname)) {
940	    msgConfirm("Unable to mount the root file system on %s!  Giving up.", dname);
941	    return DITEM_FAILURE;
942	}
943    }
944
945    /* Now buzz through the rest of the partitions and mount them too */
946    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
947    for (i = 0; devs[i]; i++) {
948	if (!devs[i]->enabled)
949	    continue;
950
951	disk = (Disk *)devs[i]->private;
952	if (!disk->chunks) {
953	    msgConfirm("No chunk list found for %s!", disk->name);
954	    return DITEM_FAILURE;
955	}
956	if (RunningAsInit && root && (root->newfs || upgrade)) {
957	    Mkdir("/mnt/dev");
958	    if (!Fake)
959		MakeDevDisk(disk, "/mnt/dev");
960	}
961	else if (!RunningAsInit && !Fake)
962	    MakeDevDisk(disk, "/dev");
963
964	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
965	    if (c1->type == freebsd) {
966		for (c2 = c1->part; c2; c2 = c2->next) {
967		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
968			PartInfo *tmp = (PartInfo *)c2->private_data;
969
970			/* Already did root */
971			if (c2 == rootdev)
972			    continue;
973
974			if (tmp->newfs && (!upgrade || !msgYesNo("You are upgrading - are you SURE you want to newfs /dev/%s?", c2->name)))
975			    command_shell_add(tmp->mountpoint, "%s %s/dev/r%s", tmp->newfs_cmd, RunningAsInit ? "/mnt" : "", c2->name);
976			else
977			    command_shell_add(tmp->mountpoint, "fsck -y %s/dev/r%s", RunningAsInit ? "/mnt" : "", c2->name);
978			command_func_add(tmp->mountpoint, Mount, c2->name);
979		    }
980		    else if (c2->type == part && c2->subtype == FS_SWAP) {
981			char fname[80];
982			int i;
983
984			if (c2 == swapdev)
985			    continue;
986			sprintf(fname, "%s/dev/%s", RunningAsInit ? "/mnt" : "", c2->name);
987			i = (Fake || swapon(fname));
988			if (!i)
989			    msgNotify("Added %s as an additional swap device", fname);
990			else
991			    msgConfirm("Unable to add %s as a swap device: %s", fname, strerror(errno));
992		    }
993		}
994	    }
995	    else if (c1->type == fat && c1->private_data && (root->newfs || upgrade)) {
996		char name[FILENAME_MAX];
997
998		sprintf(name, "%s/%s", RunningAsInit ? "/mnt" : "", ((PartInfo *)c1->private_data)->mountpoint);
999		Mkdir(name);
1000	    }
1001	}
1002    }
1003
1004    if (RunningAsInit) {
1005	msgNotify("Copying initial device files..");
1006	/* Copy the boot floppy's dev files */
1007	if ((root->newfs || upgrade) && vsystem("find -x /dev | cpio %s -pdum /mnt", cpioVerbosity())) {
1008	    msgConfirm("Couldn't clone the /dev files!");
1009	    return DITEM_FAILURE;
1010	}
1011    }
1012
1013    command_sort();
1014    command_execute();
1015    return DITEM_SUCCESS;
1016}
1017
1018static char *
1019getRelname(void)
1020{
1021    static char buf[64];
1022    int sz = (sizeof buf) - 1;
1023
1024    if (sysctlbyname("kern.osrelease", buf, &sz, NULL, 0) != -1) {
1025	buf[sz] = '\0';
1026	return buf;
1027    }
1028    else
1029	return "<unknown>";
1030}
1031
1032/* Initialize various user-settable values to their defaults */
1033int
1034installVarDefaults(dialogMenuItem *self)
1035{
1036    char *cp;
1037
1038    /* Set default startup options */
1039    variable_set2(VAR_RELNAME,			getRelname(), 0);
1040    variable_set2(VAR_CPIO_VERBOSITY,		"high", 0);
1041    variable_set2(VAR_TAPE_BLOCKSIZE,		DEFAULT_TAPE_BLOCKSIZE, 0);
1042    variable_set2(VAR_INSTALL_ROOT,		"/", 0);
1043    variable_set2(VAR_INSTALL_CFG,		"install.cfg", 0);
1044    cp = getenv("EDITOR");
1045    if (!cp)
1046	cp = "/usr/bin/ee";
1047    variable_set2(VAR_EDITOR,			cp, 0);
1048    variable_set2(VAR_FTP_USER,			"ftp", 0);
1049    variable_set2(VAR_BROWSER_PACKAGE,		"lynx", 0);
1050    variable_set2(VAR_BROWSER_BINARY,		"/usr/local/bin/lynx", 0);
1051    variable_set2(VAR_FTP_STATE,		"passive", 0);
1052    variable_set2(VAR_NFS_SECURE,		"YES", 0);
1053    variable_set2(VAR_PKG_TMPDIR,		"/usr/tmp", 0);
1054    variable_set2(VAR_GATED_PKG,		"gated", 0);
1055    variable_set2(VAR_PCNFSD_PKG,		"pcnfsd", 0);
1056    variable_set2(VAR_MEDIA_TIMEOUT,		itoa(MEDIA_TIMEOUT), 0);
1057    if (getpid() != 1)
1058	variable_set2(SYSTEM_STATE,		"update", 0);
1059    else
1060	variable_set2(SYSTEM_STATE,		"init", 0);
1061    return DITEM_SUCCESS;
1062}
1063
1064/* Load the environment up from various system configuration files */
1065void
1066installEnvironment(void)
1067{
1068    configEnvironmentRC_conf();
1069    if (file_readable("/etc/resolv.conf"))
1070	configEnvironmentResolv("/etc/resolv.conf");
1071}
1072
1073/* Copy the boot floppy contents into /stand */
1074Boolean
1075copySelf(void)
1076{
1077    int i;
1078
1079    if (file_readable("/boot.help"))
1080	vsystem("cp /boot.help /mnt");
1081    msgWeHaveOutput("Copying the boot floppy to /stand on root filesystem");
1082    i = vsystem("find -x /stand | cpio %s -pdum /mnt", cpioVerbosity());
1083    if (i) {
1084	msgConfirm("Copy returned error status of %d!", i);
1085	return FALSE;
1086    }
1087
1088    /* Copy the /etc files into their rightful place */
1089    if (vsystem("cd /mnt/stand; find etc | cpio %s -pdum /mnt", cpioVerbosity())) {
1090	msgConfirm("Couldn't copy up the /etc files!");
1091	return TRUE;
1092    }
1093    return TRUE;
1094}
1095
1096static void
1097create_termcap(void)
1098{
1099    FILE *fp;
1100
1101    const char *caps[] = {
1102	termcap_vt100, termcap_cons25, termcap_cons25_m, termcap_cons25r,
1103	termcap_cons25r_m, termcap_cons25l1, termcap_cons25l1_m, NULL,
1104    };
1105    const char **cp;
1106
1107    if (!file_readable(TERMCAP_FILE)) {
1108	Mkdir("/usr/share/misc");
1109	fp = fopen(TERMCAP_FILE, "w");
1110	if (!fp) {
1111	    msgConfirm("Unable to initialize termcap file. Some screen-oriented\nutilities may not work.");
1112	    return;
1113	}
1114	cp = caps;
1115	while (*cp)
1116	    fprintf(fp, "%s\n", *(cp++));
1117	fclose(fp);
1118    }
1119}
1120