install.c revision 8868
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.68 1995/05/29 13:37:43 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    /* If there's no kernel but there is a kernel.GENERIC, link it over */
258    if (access("/kernel", R_OK))
259	vsystem("ln -f /kernel.GENERIC /kernel");
260
261    msgConfirm("Installation completed successfully.\nHit return now to go back to the main menu.");
262    SystemWasInstalled = TRUE;
263    return 0;
264}
265
266/* Go newfs and/or mount all the filesystems we've been asked to */
267static void
268make_filesystems(void)
269{
270    int i;
271    Disk *disk;
272    Chunk *c1, *c2;
273    Device **devs;
274    char dname[40];
275    PartInfo *p = (PartInfo *)rootdev->private;
276
277    command_clear();
278    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
279
280    /* First, create and mount the root device */
281    if (strcmp(p->mountpoint, "/"))
282	msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", rootdev->name, p->mountpoint);
283
284    if (p->newfs) {
285	int i;
286
287	sprintf(dname, "/dev/r%sa", rootdev->disk->name);
288	msgNotify("Making a new root filesystem on %s", dname);
289	i = vsystem("%s %s", p->newfs_cmd, dname);
290	if (i) {
291	    msgConfirm("Unable to make new root filesystem!  Command returned status %d", i);
292	    return;
293	}
294    }
295    else {
296	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.");
297	sprintf(dname, "/dev/r%sa", rootdev->disk->name);
298	msgNotify("Checking integrity of existing %s filesystem", dname);
299	i = vsystem("fsck -y %s", dname);
300	if (i)
301	    msgConfirm("Warning: fsck returned status off %d - this partition may be\nunsafe to use.", i);
302    }
303    sprintf(dname, "/dev/%sa", rootdev->disk->name);
304    if (Mount("/mnt", dname)) {
305	msgConfirm("Unable to mount the root file system!  Giving up.");
306	return;
307    }
308    else {
309	extern int makedevs(void);
310
311	msgNotify("Making device files");
312	if (Mkdir("/mnt/dev", NULL) || chdir("/mnt/dev") || makedevs())
313	    msgConfirm("Failed to make some of the devices in /mnt!");
314	if (Mkdir("/mnt/stand", NULL)) {
315	    msgConfirm("Unable to make /mnt/stand directory!");
316	    return;
317	}
318	chdir("/");
319    }
320
321    /* Now buzz through the rest of the partitions and mount them too */
322    for (i = 0; devs[i]; i++) {
323	if (!devs[i]->enabled)
324	    continue;
325
326	disk = (Disk *)devs[i]->private;
327	if (!disk->chunks)
328	    msgFatal("No chunk list found for %s!", disk->name);
329
330	/* Make the proper device mount points in /mnt/dev */
331	MakeDevDisk(disk, "/mnt/dev");
332
333	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
334	    if (c1->type == freebsd) {
335		for (c2 = c1->part; c2; c2 = c2->next) {
336		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private) {
337			PartInfo *tmp = (PartInfo *)c2->private;
338
339			if (!strcmp(tmp->mountpoint, "/"))
340			    continue;
341
342			if (tmp->newfs)
343			    command_shell_add(tmp->mountpoint, "%s /mnt/dev/r%s", tmp->newfs_cmd, c2->name);
344			else
345			    command_shell_add(tmp->mountpoint, "fsck -y /mnt/dev/r%s", c2->name);
346			command_func_add(tmp->mountpoint, Mount, c2->name);
347		    }
348		    else if (c2->type == part && c2->subtype == FS_SWAP) {
349			char fname[80];
350			int i;
351
352			sprintf(fname, "/mnt/dev/%s", c2->name);
353			i = swapon(fname);
354			if (!i)
355			    msgNotify("Added %s as a swap device", fname);
356			else
357			    msgConfirm("Unable to add %s as a swap device: %s", fname, strerror(errno));
358		    }
359		}
360	    }
361	    else if (c1->type == fat) {
362		PartInfo *tmp = (PartInfo *)c1->private;
363
364		if (!tmp)
365		    continue;
366		command_func_add(tmp->mountpoint, Mount_DOS, c1->name);
367	    }
368	}
369    }
370    command_sort();
371    command_execute();
372}
373
374/* Copy the boot floppy contents into /stand */
375static void
376copy_self(void)
377{
378    int i;
379
380    msgWeHaveOutput("Copying the boot floppy to /stand on root filesystem");
381    i = vsystem("find -x /stand | cpio -pdmV /mnt");
382    if (i)
383	msgConfirm("Copy returned error status of %d!", i);
384
385    /* Copy the /etc files into their rightful place */
386    (void)vsystem("cd /mnt/stand; find etc | cpio -pdmv /mnt");
387}
388
389static void loop_on_root_floppy();
390
391static void
392root_extract(void)
393{
394    int fd;
395
396    if (OnCDROM) {
397	fd = open("/floppies/root.flp", O_RDONLY);
398	(void)mediaExtractDist("/", fd);
399	return;
400    }
401    if (mediaDevice) {
402	switch(mediaDevice->type) {
403
404	case DEVICE_TYPE_TAPE:
405	case DEVICE_TYPE_FLOPPY:
406	    loop_on_root_floppy();
407	    break;
408
409	default:
410	    if (mediaDevice->init)
411		if (!(*mediaDevice->init)(mediaDevice))
412		    break;
413	    fd = (*mediaDevice->get)("floppies/root.flp");
414	    if (fd != -1) {
415		msgNotify("Loading root floppy from %s", mediaDevice->name);
416		(void)mediaExtractDist("/", fd);
417		if (mediaDevice->close)
418		    (*mediaDevice->close)(mediaDevice, fd);
419		else
420		    close(fd);
421	    }
422	    else {
423		msgConfirm("Couldn't get root floppy image from %s\n, falling back to floppy.", mediaDevice->name);
424		if (mediaDevice->shutdown)
425		    (*mediaDevice->shutdown)(mediaDevice);
426	        loop_on_root_floppy();
427	    }
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    while (1) {
441	fd = getRootFloppy();
442	if (fd != -1) {
443	    mediaExtractDist("/", fd);
444	    break;
445	}
446    }
447}
448