install.c revision 8825
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.65 1995/05/28 23:12:05 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;
53
54static void	make_filesystems(void);
55static void	copy_self(void);
56static void	root_extract(void);
57
58static Chunk *rootdev;
59
60static Boolean
61checkLabels(void)
62{
63    Device **devs;
64    Disk *disk;
65    Chunk *c1, *c2, *swapdev = NULL;
66    int i;
67
68    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
69    /* First verify that we have a root device */
70    for (i = 0; devs[i]; i++) {
71	if (!devs[i]->enabled)
72	    continue;
73	disk = (Disk *)devs[i]->private;
74	msgDebug("Scanning disk %s for root filesystem\n", disk->name);
75	if (!disk->chunks)
76	    msgFatal("No chunk list found for %s!", disk->name);
77	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
78	    if (c1->type == freebsd) {
79		for (c2 = c1->part; c2; c2 = c2->next) {
80		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private && c2->flags & CHUNK_IS_ROOT) {
81			rootdev = c2;
82			break;
83		    }
84		}
85	    }
86	}
87    }
88
89    /* Now check for swap devices */
90    for (i = 0; devs[i]; i++) {
91	disk = (Disk *)devs[i]->private;
92	msgDebug("Scanning disk %s for swap partitions\n", disk->name);
93	if (!disk->chunks)
94	    msgFatal("No chunk list found for %s!", disk->name);
95	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
96	    if (c1->type == freebsd) {
97		for (c2 = c1->part; c2; c2 = c2->next) {
98		    if (c2->type == part && c2->subtype == FS_SWAP) {
99			swapdev = c2;
100			break;
101		    }
102		}
103	    }
104	}
105    }
106
107    if (!rootdev) {
108	msgConfirm("No root device found - you must label a partition as /\n in the label editor.");
109	return FALSE;
110    }
111    else if (rootdev->name[strlen(rootdev->name) - 1] != 'a') {
112	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.");
113	return FALSE;
114    }
115    if (!swapdev) {
116	msgConfirm("No swap devices found - you must create at least one\nswap partition.");
117	return FALSE;
118    }
119    return TRUE;
120}
121
122static Boolean
123installInitial(void)
124{
125    extern u_char boot1[], boot2[];
126    extern u_char mbr[], bteasy17[];
127    u_char *mbrContents;
128    Device **devs;
129    int i;
130    static Boolean alreadyDone = FALSE;
131    char *cp;
132
133    if (alreadyDone)
134	return TRUE;
135
136    if (!getenv(DISK_PARTITIONED)) {
137	msgConfirm("You need to partition your disk before you can proceed with\nthe installation.");
138	return FALSE;
139    }
140    if (!getenv(DISK_LABELLED)) {
141	msgConfirm("You need to assign disk labels before you can proceed with\nthe installation.");
142	return FALSE;
143    }
144    if (!checkLabels())
145	return FALSE;
146
147    /* Figure out what kind of MBR the user wants */
148    dmenuOpenSimple(&MenuMBRType);
149    mbrContents = NULL;
150    cp = getenv("bootManager");
151    if (cp) {
152	if (!strcmp(cp, "bteasy"))
153	    mbrContents = bteasy17;
154	else if (!strcmp(cp, "mbr"))
155	    mbrContents = mbr;
156    }
157
158    /* If we refuse to proceed, bail. */
159    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!"))
160	return FALSE;
161
162    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
163    for (i = 0; devs[i]; i++) {
164	Chunk *c1;
165	Disk *d = (Disk *)devs[i]->private;
166
167	if (!devs[i]->enabled)
168	    continue;
169
170	if (mbrContents) {
171	    Set_Boot_Mgr(d, mbrContents);
172	    mbrContents = NULL;
173	}
174	Set_Boot_Blocks(d, boot1, boot2);
175	msgNotify("Writing partition information to drive %s", d->name);
176	Write_Disk(d);
177
178	/* Now scan for bad blocks, if necessary */
179	for (c1 = d->chunks->part; c1; c1 = c1->next) {
180	    if (c1->flags & CHUNK_BAD144) {
181		int ret;
182
183		msgNotify("Running bad block scan on partition %s", c1->name);
184		ret = vsystem("bad144 -v /dev/r%s 1234", c1->name);
185		if (ret)
186		    msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret);
187		ret = vsystem("bad144 -v -s /dev/r%s", c1->name);
188		if (ret)
189		    msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret);
190	    }
191	}
192    }
193    make_filesystems();
194    copy_self();
195    dialog_clear();
196    chroot("/mnt");
197    chdir("/");
198    variable_set2(RUNNING_ON_ROOT, "yes");
199    /* stick a helpful shell over on the 4th VTY */
200    msgDebug("Sticking a potentially helpful shell over on the 4th screen\n");
201    if (!fork()) {
202	int i, fd;
203	extern int login_tty(int);
204
205	for (i = 0; i < 64; i++)
206	    close(i);
207	fd = open("/dev/ttyv3", O_RDWR);
208	ioctl(0, TIOCSCTTY, &fd);
209	dup2(0, 1);
210	dup2(0, 2);
211	if (login_tty(fd)==-1) {
212	    msgConfirm("Can't set controlling terminal");
213	    exit(1);
214	}
215	execlp("sh", "-sh", 0);
216	exit(1);
217    }
218    root_extract();
219    alreadyDone = TRUE;
220    return TRUE;
221}
222
223/*
224 * What happens when we select "Install".  This is broken into a 3 stage installation so that
225 * the user can do a full installation but come back here again to load more distributions,
226 * perhaps from a different media type.  This would allow, for example, the user to load the
227 * majority of the system from CDROM and then use ftp to load just the DES dist.
228 */
229int
230installCommit(char *str)
231{
232    FILE *fp;
233    static Boolean hostsModified = FALSE;
234
235    if (!Dists) {
236	msgConfirm("You haven't told me what distributions to load yet!\nPlease select a distribution from the Distributions menu.");
237	return 0;
238    }
239    if (!mediaVerify())
240	return 0;
241
242    if (RunningAsInit) {
243	if (!installInitial())
244	    return 0;
245	configFstab();
246	configResolv();
247    }
248    distExtractAll();
249
250    /* Tack ourselves at the end of /etc/hosts */
251    if (RunningAsInit && getenv(VAR_IPADDR) && !hostsModified) {
252	fp = fopen("/etc/hosts", "a");
253	fprintf(fp, "%s\t\t%s\n", getenv(VAR_IPADDR), getenv(VAR_HOSTNAME));
254	fclose(fp);
255	hostsModified = TRUE;
256    }
257    msgConfirm("Installation completed successfully.\nHit return now to go back to the main menu.");
258    SystemWasInstalled = TRUE;
259    return 0;
260}
261
262/* Go newfs and/or mount all the filesystems we've been asked to */
263static void
264make_filesystems(void)
265{
266    int i;
267    Disk *disk;
268    Chunk *c1, *c2;
269    Device **devs;
270    char dname[40];
271    PartInfo *p = (PartInfo *)rootdev->private;
272
273    command_clear();
274    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
275
276    /* First, create and mount the root device */
277    if (strcmp(p->mountpoint, "/"))
278	msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", rootdev->name, p->mountpoint);
279
280    if (p->newfs) {
281	int i;
282
283	sprintf(dname, "/dev/r%sa", rootdev->disk->name);
284	msgNotify("Making a new root filesystem on %s", dname);
285	i = vsystem("%s %s", p->newfs_cmd, dname);
286	if (i) {
287	    msgConfirm("Unable to make new root filesystem!  Command returned status %d", i);
288	    return;
289	}
290    }
291    else {
292	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.");
293	sprintf(dname, "/dev/r%sa", rootdev->disk->name);
294	msgNotify("Checking integrity of existing %s filesystem", dname);
295	i = vsystem("fsck -y %s", dname);
296	if (i)
297	    msgConfirm("Warning: fsck returned status off %d - this partition may be\nunsafe to use.", i);
298    }
299    sprintf(dname, "/dev/%sa", rootdev->disk->name);
300    if (Mount("/mnt", dname)) {
301	msgConfirm("Unable to mount the root file system!  Giving up.");
302	return;
303    }
304    else {
305	extern int makedevs(void);
306
307	msgNotify("Making device files");
308	if (Mkdir("/mnt/dev", NULL) || chdir("/mnt/dev") || makedevs())
309	    msgConfirm("Failed to make some of the devices in /mnt!");
310	if (Mkdir("/mnt/stand", NULL)) {
311	    msgConfirm("Unable to make /mnt/stand directory!");
312	    return;
313	}
314	chdir("/");
315    }
316
317    /* Now buzz through the rest of the partitions and mount them too */
318    for (i = 0; devs[i]; i++) {
319	if (!devs[i]->enabled)
320	    continue;
321
322	disk = (Disk *)devs[i]->private;
323	if (!disk->chunks)
324	    msgFatal("No chunk list found for %s!", disk->name);
325
326	/* Make the proper device mount points in /mnt/dev */
327	MakeDevDisk(disk, "/mnt/dev");
328
329	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
330	    if (c1->type == freebsd) {
331		for (c2 = c1->part; c2; c2 = c2->next) {
332		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private) {
333			PartInfo *tmp = (PartInfo *)c2->private;
334
335			if (!strcmp(tmp->mountpoint, "/"))
336			    continue;
337
338			if (tmp->newfs)
339			    command_shell_add(tmp->mountpoint, "%s /mnt/dev/r%s", tmp->newfs_cmd, c2->name);
340			else
341			    command_shell_add(tmp->mountpoint, "fsck -y /mnt/dev/r%s", c2->name);
342			command_func_add(tmp->mountpoint, Mount, c2->name);
343		    }
344		    else if (c2->type == part && c2->subtype == FS_SWAP) {
345			char fname[80];
346			int i;
347
348			sprintf(fname, "/mnt/dev/%s", c2->name);
349			i = swapon(fname);
350			if (!i)
351			    msgNotify("Added %s as a swap device", fname);
352			else
353			    msgConfirm("Unable to add %s as a swap device: %s", fname, strerror(errno));
354		    }
355		}
356	    }
357	    else if (c1->type == fat) {
358		PartInfo *tmp = (PartInfo *)c1->private;
359
360		if (!tmp)
361		    continue;
362		command_func_add(tmp->mountpoint, Mount_DOS, c1->name);
363	    }
364	}
365    }
366    command_sort();
367    command_execute();
368}
369
370/* Copy the boot floppy contents into /stand */
371static void
372copy_self(void)
373{
374    int i;
375
376    msgWeHaveOutput("Copying the boot floppy to /stand on root filesystem");
377    i = vsystem("find -x /stand | cpio -pdmV /mnt");
378    if (i)
379	msgConfirm("Copy returned error status of %d!", i);
380
381    /* Copy the /etc files into their rightful place */
382    (void)vsystem("(cd /stand; find etc) | cpio -pdmv /mnt");
383}
384
385static void loop_on_root_floppy();
386
387static void
388root_extract(void)
389{
390    int fd;
391
392    if (OnCDROM) {
393	fd = open("/floppies/root.flp", O_RDONLY);
394	(void)mediaExtractDist("root.flp", "/", fd);
395	return;
396    }
397    if (mediaDevice) {
398	switch(mediaDevice->type) {
399
400	case DEVICE_TYPE_DOS:
401	case DEVICE_TYPE_FTP:
402	case DEVICE_TYPE_DISK:
403	case DEVICE_TYPE_NETWORK:
404	case DEVICE_TYPE_CDROM:
405	    if (mediaDevice->init)
406		if (!(*mediaDevice->init)(mediaDevice))
407		    break;
408	    fd = (*mediaDevice->get)("floppies/root.flp");
409	    if (fd != -1) {
410		msgNotify("Loading root floppy from %s", mediaDevice->name);
411		(void)mediaExtractDist("root.flp", "/", fd);
412		if (mediaDevice->close)
413		    (*mediaDevice->close)(mediaDevice, fd);
414		else
415		    close(fd);
416		if (mediaDevice->shutdown)
417		    (*mediaDevice->shutdown)(mediaDevice);
418	    } else {
419		if (mediaDevice->shutdown)
420		    (*mediaDevice->shutdown)(mediaDevice);
421	        loop_on_root_floppy();
422	    }
423	    break;
424
425	case DEVICE_TYPE_FLOPPY:
426	default:
427	    loop_on_root_floppy();
428	    break;
429	}
430    }
431    else
432	loop_on_root_floppy();
433}
434
435static void
436loop_on_root_floppy(void)
437{
438    int fd;
439
440    fd = getRootFloppy();
441    if (fd != -1)
442	mediaExtractDist("root.flp", "/", fd);
443}
444