install.c revision 9202
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.70.2.41 1995/06/10 07:58:37 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 * 3. All advertising materials mentioning features or use of this software
23 *    must display the following acknowledgement:
24 *	This product includes software developed by Jordan Hubbard
25 *	for the FreeBSD Project.
26 * 4. The name of Jordan Hubbard or the FreeBSD project may not be used to
27 *    endorse or promote products derived from this software without specific
28 *    prior written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * SUCH DAMAGE.
41 *
42 */
43
44#include "sysinstall.h"
45#include <sys/disklabel.h>
46#include <sys/errno.h>
47#include <sys/ioctl.h>
48#include <sys/fcntl.h>
49#include <sys/wait.h>
50#include <unistd.h>
51
52Boolean SystemWasInstalled = FALSE;
53
54static Boolean	make_filesystems(void);
55static Boolean	copy_self(void);
56static Boolean	root_extract(void);
57
58static Chunk *rootdev;
59
60static Boolean
61checkLabels(void)
62{
63    Device **devs;
64    Disk *disk;
65    Chunk *c1, *c2, *swapdev, *usrdev;
66    int i;
67
68    rootdev = swapdev = usrdev = NULL;
69    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
70    /* First verify that we have a root device */
71    for (i = 0; devs[i]; i++) {
72	if (!devs[i]->enabled)
73	    continue;
74	disk = (Disk *)devs[i]->private;
75	msgDebug("Scanning disk %s for root filesystem\n", disk->name);
76	if (!disk->chunks)
77	    msgFatal("No chunk list found for %s!", disk->name);
78	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
79	    if (c1->type == freebsd) {
80		for (c2 = c1->part; c2; c2 = c2->next) {
81		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private) {
82			if (c2->flags & CHUNK_IS_ROOT) {
83			    if (rootdev) {
84				msgConfirm("WARNING:  You have more than one root device set?!\nUsing the first one found.");
85				continue;
86			    }
87			    rootdev = c2;
88			}
89			else if (!strcmp(((PartInfo *)c2->private)->mountpoint, "/usr")) {
90			    if (usrdev) {
91				msgConfirm("WARNING:  You have more than one /usr filesystem.\nUsing the first one found.");
92				continue;
93			    }
94			    usrdev = c2;
95			}
96		    }
97		}
98	    }
99	}
100    }
101
102    /* Now check for swap devices */
103    for (i = 0; devs[i]; i++) {
104	disk = (Disk *)devs[i]->private;
105	msgDebug("Scanning disk %s for swap partitions\n", disk->name);
106	if (!disk->chunks)
107	    msgFatal("No chunk list found for %s!", disk->name);
108	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
109	    if (c1->type == freebsd) {
110		for (c2 = c1->part; c2; c2 = c2->next) {
111		    if (c2->type == part && c2->subtype == FS_SWAP) {
112			swapdev = c2;
113			break;
114		    }
115		}
116	    }
117	}
118    }
119
120    if (!rootdev) {
121	msgConfirm("No root device found - you must label a partition as /\n in the label editor.");
122	return FALSE;
123    }
124    else if (rootdev->name[strlen(rootdev->name) - 1] != 'a') {
125	msgConfirm("Invalid placement of root partition.  For now, we only support\nmounting root partitions on \"a\" partitions due to limitations\nin the FreeBSD boot block code.  Please correct this and\ntry again.");
126	return FALSE;
127    }
128    if (!swapdev) {
129	msgConfirm("No swap devices found - you must create at least one\nswap partition.");
130	return FALSE;
131    }
132    if (!usrdev)
133	msgConfirm("WARNING:  No /usr filesystem found.  This is not technically\nan error if your root filesystem is big enough (or you later\nintend to get your /usr filesystem over NFS), but it may otherwise\ncause you trouble and is not recommended procedure!");
134    return TRUE;
135}
136
137static Boolean
138installInitial(void)
139{
140    extern u_char boot1[], boot2[];
141    extern u_char mbr[], bteasy17[];
142    u_char *mbrContents;
143    Device **devs;
144    int i;
145    static Boolean alreadyDone = FALSE;
146
147    if (alreadyDone)
148	return TRUE;
149
150    if (!getenv(DISK_PARTITIONED)) {
151	msgConfirm("You need to partition your disk before you can proceed with\nthe installation.");
152	return FALSE;
153    }
154    if (!getenv(DISK_LABELLED)) {
155	msgConfirm("You need to assign disk labels before you can proceed with\nthe installation.");
156	return FALSE;
157    }
158    if (!checkLabels())
159	return FALSE;
160
161    /* Figure out what kind of MBR the user wants */
162    if (!dmenuOpenSimple(&MenuMBRType))
163	return FALSE;
164
165    switch (BootMgr) {
166    case 0:
167	mbrContents = bteasy17;
168	break;
169
170    case 1:
171	mbrContents = mbr;
172	break;
173
174    case 2:
175    default:
176	mbrContents = NULL;
177    }
178
179    /* If we refuse to proceed, bail. */
180    if (msgYesNo("Last Chance!  Are you SURE you want continue the installation?\n\nIf you're running this on an existing system, we STRONGLY\nencourage you to make proper backups before proceeding.\nWe take no responsibility for lost disk contents!"))
181	return FALSE;
182
183    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
184    for (i = 0; devs[i]; i++) {
185	Chunk *c1;
186	Disk *d = (Disk *)devs[i]->private;
187
188	if (!devs[i]->enabled)
189	    continue;
190
191	if (mbrContents) {
192	    Set_Boot_Mgr(d, mbrContents);
193	    mbrContents = NULL;
194	}
195	Set_Boot_Blocks(d, boot1, boot2);
196	msgNotify("Writing partition information to drive %s", d->name);
197	Write_Disk(d);
198
199	/* Now scan for bad blocks, if necessary */
200	for (c1 = d->chunks->part; c1; c1 = c1->next) {
201	    if (c1->flags & CHUNK_BAD144) {
202		int ret;
203
204		msgNotify("Running bad block scan on partition %s", c1->name);
205		ret = vsystem("bad144 -v /dev/r%s 1234", c1->name);
206		if (ret)
207		    msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret);
208		ret = vsystem("bad144 -v -s /dev/r%s", c1->name);
209		if (ret)
210		    msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret);
211	    }
212	}
213    }
214    if (!make_filesystems()) {
215	msgConfirm("Couldn't make filesystems properly.  Aborting.");
216	return 0;
217    }
218    if (!copy_self()) {
219	msgConfirm("Couldn't clone the boot floppy onto the root file system.\nAborting.");
220	return 0;
221    }
222    dialog_clear();
223    chroot("/mnt");
224    chdir("/");
225    variable_set2(RUNNING_ON_ROOT, "yes");
226    /* stick a helpful shell over on the 4th VTY */
227    if (OnVTY && !fork()) {
228	int i, fd;
229	extern int login_tty(int);
230
231	msgDebug("Starting an emergency holographic shell over on the 4th screen\n");
232	for (i = 0; i < 64; i++)
233	    close(i);
234	fd = open("/dev/ttyv3", O_RDWR);
235	ioctl(0, TIOCSCTTY, &fd);
236	dup2(0, 1);
237	dup2(0, 2);
238	if (login_tty(fd) == -1) {
239	    msgNotify("Can't set controlling terminal");
240	    exit(1);
241	}
242	printf("Warning: This shell is chroot()'d to /mnt\n");
243	execlp("sh", "-sh", 0);
244	exit(1);
245    }
246    alreadyDone = TRUE;
247    return TRUE;
248}
249
250/*
251 * What happens when we select "Install".  This is broken into a 3 stage installation so that
252 * the user can do a full installation but come back here again to load more distributions,
253 * perhaps from a different media type.  This would allow, for example, the user to load the
254 * majority of the system from CDROM and then use ftp to load just the DES dist.
255 */
256int
257installCommit(char *str)
258{
259    Device **devs;
260    int i;
261
262    if (!Dists) {
263	msgConfirm("You haven't told me what distributions to load yet!\nPlease select a distribution from the Distributions menu.");
264	return 0;
265    }
266    if (!mediaVerify())
267	return 0;
268
269    if (RunningAsInit && !SystemWasInstalled) {
270	if (!installInitial())
271	    return 0;
272	configFstab();
273    }
274    if (!SystemWasInstalled && !root_extract()) {
275	msgConfirm("Failed to load the ROOT distribution.  Please correct\nthis problem and try again.");
276	return 0;
277    }
278
279    /* If we're about to extract the bin dist again, reset the installed state */
280    if (Dists & DIST_BIN)
281	SystemWasInstalled = FALSE;
282
283    distExtractAll();
284
285    if (!SystemWasInstalled && access("/kernel", R_OK)) {
286	if (vsystem("ln -f /kernel.GENERIC /kernel")) {
287	    msgConfirm("Unable to link /kernel into place!");
288	    return 0;
289	}
290    }
291
292    /* Resurrect /dev after bin distribution screws it up */
293    if (!SystemWasInstalled) {
294	msgNotify("Remaking all devices.. Please wait!");
295	if (vsystem("cd /dev; sh MAKEDEV all"))
296	    msgConfirm("MAKEDEV returned non-zero status");
297
298	msgNotify("Resurrecting /dev entries for slices..");
299	devs = deviceFind(NULL, DEVICE_TYPE_DISK);
300	if (!devs)
301	    msgFatal("Couldn't get a disk device list!");
302	/* Resurrect the slices that the former clobbered */
303	for (i = 0; devs[i]; i++) {
304	    Disk *disk = (Disk *)devs[i]->private;
305	    Chunk *c1;
306
307	    if (!disk->chunks)
308		msgFatal("No chunk list found for %s!", disk->name);
309	    for (c1 = disk->chunks->part; c1; c1 = c1->next) {
310		if (c1->type == freebsd) {
311		    msgNotify("Making slice entries for %s", c1->name);
312		    if (vsystem("cd /dev; sh MAKEDEV %sh", c1->name))
313			msgConfirm("Unable to make slice entries for %s!", c1->name);
314		}
315	    }
316	}
317    }
318
319    /* XXX Do all the last ugly work-arounds here which we'll try and excise someday right?? XXX */
320    /* BOGON #1:  XFree86 extracting /usr/X11R6 with root-only perms */
321    if (file_readable("/usr/X11R6"))
322	(void)system("chmod 755 /usr/X11R6");
323
324    /* BOGON #2: We leave /etc in a bad state */
325    (void)system("chmod 755 /etc");
326
327    dialog_clear();
328    if (Dists)
329	msgConfirm("Installation completed with some errors.  You may wish\nto scroll through the debugging messages on ALT-F2 with the scroll-lock\nfeature.  Press [ENTER] to return to the installation menu.");
330    else
331	msgConfirm("Installation completed successfully, now  press [ENTER] to return\nto the main menu. If you have any network devices you have not yet\nconfigured, see the Interface configuration item on the\nConfiguration menu.");
332    SystemWasInstalled = TRUE;
333    return 0;
334}
335
336/* Go newfs and/or mount all the filesystems we've been asked to */
337static Boolean
338make_filesystems(void)
339{
340    int i;
341    Disk *disk;
342    Chunk *c1, *c2;
343    Device **devs;
344    char dname[40];
345    PartInfo *p = (PartInfo *)rootdev->private;
346    Boolean RootReadOnly;
347
348    command_clear();
349    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
350
351    /* First, create and mount the root device */
352    if (strcmp(p->mountpoint, "/"))
353	msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", rootdev->name, p->mountpoint);
354
355    if (p->newfs) {
356	int i;
357
358	sprintf(dname, "/dev/r%sa", rootdev->disk->name);
359	msgNotify("Making a new root filesystem on %s", dname);
360	i = vsystem("%s %s", p->newfs_cmd, dname);
361	if (i) {
362	    msgConfirm("Unable to make new root filesystem!  Command returned status %d", i);
363	    return FALSE;
364	}
365	RootReadOnly = FALSE;
366    }
367    else {
368	RootReadOnly = TRUE;
369	msgConfirm("Warning:  You have selected a Read-Only root device\nand may be unable to find the appropriate device entries on it\nif it is from an older pre-slice version of FreeBSD.");
370	sprintf(dname, "/dev/r%sa", rootdev->disk->name);
371	msgNotify("Checking integrity of existing %s filesystem", dname);
372	i = vsystem("fsck -y %s", dname);
373	if (i)
374	    msgConfirm("Warning: fsck returned status off %d - this partition may be\nunsafe to use.", i);
375    }
376    sprintf(dname, "/dev/%sa", rootdev->disk->name);
377    if (Mount("/mnt", dname)) {
378	msgConfirm("Unable to mount the root file system!  Giving up.");
379	return FALSE;
380    }
381
382    /* Now buzz through the rest of the partitions and mount them too */
383    for (i = 0; devs[i]; i++) {
384	if (!devs[i]->enabled)
385	    continue;
386
387	disk = (Disk *)devs[i]->private;
388	if (!disk->chunks) {
389	    msgConfirm("No chunk list found for %s!", disk->name);
390	    return FALSE;
391	}
392
393	/* Make the proper device mount points in /mnt/dev */
394	if (!(RootReadOnly && disk == rootdev->disk)) {
395	    Mkdir("/mnt/dev", NULL);
396	    MakeDevDisk(disk, "/mnt/dev");
397	}
398	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
399	    if (c1->type == freebsd) {
400		for (c2 = c1->part; c2; c2 = c2->next) {
401		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private) {
402			PartInfo *tmp = (PartInfo *)c2->private;
403
404			if (!strcmp(tmp->mountpoint, "/"))
405			    continue;
406
407			if (tmp->newfs)
408			    command_shell_add(tmp->mountpoint, "%s /mnt/dev/r%s", tmp->newfs_cmd, c2->name);
409			else
410			    command_shell_add(tmp->mountpoint, "fsck -y /mnt/dev/r%s", c2->name);
411			command_func_add(tmp->mountpoint, Mount, c2->name);
412		    }
413		    else if (c2->type == part && c2->subtype == FS_SWAP) {
414			char fname[80];
415			int i;
416
417			sprintf(fname, "/mnt/dev/%s", c2->name);
418			i = swapon(fname);
419			if (!i)
420			    msgNotify("Added %s as a swap device", fname);
421			else
422			    msgConfirm("Unable to add %s as a swap device: %s", fname, strerror(errno));
423		    }
424		}
425	    }
426	    else if (c1->type == fat && c1->private && !RootReadOnly) {
427		char name[FILENAME_MAX];
428
429		sprintf(name, "/mnt%s", ((PartInfo *)c1->private)->mountpoint);
430		Mkdir(name, NULL);
431	    }
432	}
433    }
434
435    /* Copy the boot floppy's dev files */
436    if (vsystem("find -x /dev | cpio -pdmV /mnt")) {
437	msgConfirm("Couldn't clone the /dev files!");
438	return FALSE;
439    }
440
441    command_sort();
442    command_execute();
443    return TRUE;
444}
445
446/* Copy the boot floppy contents into /stand */
447static Boolean
448copy_self(void)
449{
450    int i;
451
452    msgWeHaveOutput("Copying the boot floppy to /stand on root filesystem");
453    i = vsystem("find -x /stand | cpio -pdmV /mnt");
454    if (i) {
455	msgConfirm("Copy returned error status of %d!", i);
456	return FALSE;
457    }
458
459    /* Copy the /etc files into their rightful place */
460    if (vsystem("cd /mnt/stand; find etc | cpio -pdmV /mnt")) {
461	msgConfirm("Couldn't copy up the /etc files!");
462	return TRUE;
463    }
464    return TRUE;
465}
466
467static Boolean loop_on_root_floppy(void);
468
469static Boolean
470root_extract(void)
471{
472    int fd;
473    static Boolean alreadyExtracted = FALSE;
474
475    if (alreadyExtracted)
476	return TRUE;
477
478    if (mediaDevice) {
479	if (isDebug())
480	    msgDebug("Attempting to extract root image from %s device\n", mediaDevice->description);
481	switch(mediaDevice->type) {
482
483	case DEVICE_TYPE_FLOPPY:
484	    alreadyExtracted = loop_on_root_floppy();
485	    break;
486
487	default:
488	    if (!(*mediaDevice->init)(mediaDevice))
489		break;
490	    fd = (*mediaDevice->get)(mediaDevice, "floppies/root.flp", NULL);
491	    if (fd < 0) {
492		msgConfirm("Couldn't get root image from %s!\nWill try to get it from floppy.", mediaDevice->name);
493		(*mediaDevice->shutdown)(mediaDevice);
494	        alreadyExtracted = loop_on_root_floppy();
495	    }
496	    else {
497		msgNotify("Loading root image from %s", mediaDevice->name);
498		alreadyExtracted = mediaExtractDist("/", fd);
499		(*mediaDevice->close)(mediaDevice, fd);
500	    }
501	    break;
502	}
503    }
504    else
505	alreadyExtracted = loop_on_root_floppy();
506    return alreadyExtracted;
507}
508
509static Boolean
510loop_on_root_floppy(void)
511{
512    int fd;
513    int status = FALSE;
514
515    while (1) {
516	fd = getRootFloppy();
517	if (fd != -1) {
518	    msgNotify("Extracting root floppy..");
519	    status = mediaExtractDist("/", fd);
520	    close(fd);
521	    break;
522	}
523    }
524    return status;
525}
526