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