install.c revision 8799
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.61 1995/05/27 10:47:32 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    FILE *fp;
224
225    if (alreadyDone)
226	return;
227    configFstab();
228    configSysconfig();
229    configResolv();
230
231    /* Tack ourselves at the end of /etc/hosts */
232    if (getenv(VAR_IPADDR)) {
233	fp = fopen("/etc/hosts", "a");
234	fprintf(fp, "%s\t\t%s\n", getenv(VAR_IPADDR), getenv(VAR_HOSTNAME));
235	fclose(fp);
236    }
237    alreadyDone = TRUE;
238    msgConfirm("Installation completed successfully.\nHit return now to go back to the main menu.");
239    SystemWasInstalled = TRUE;
240}
241
242/*
243 * What happens when we select "GO".  This is broken into a 3 stage installation so that
244 * the user can do a full installation but come back here again to load more distributions,
245 * perhaps from a different media type.  This would allow, for example, the user to load the
246 * majority of the system from CDROM and then use ftp to load just the DES dist.
247 */
248int
249installCommit(char *str)
250{
251    if (!Dists) {
252	msgConfirm("You haven't told me what distributions to load yet!\nPlease select a distribution from the Distributions menu.");
253	return 0;
254    }
255    if (!mediaVerify())
256	return 0;
257
258    if (!installInitial())
259	return 0;
260    distExtractAll();
261    installFinal();
262    return 0;
263}
264
265/* Go newfs and/or mount all the filesystems we've been asked to */
266static void
267make_filesystems(void)
268{
269    int i;
270    Disk *disk;
271    Chunk *c1, *c2;
272    Device **devs;
273    char dname[40];
274    PartInfo *p = (PartInfo *)rootdev->private;
275
276    command_clear();
277    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
278
279    /* First, create and mount the root device */
280    if (strcmp(p->mountpoint, "/"))
281	msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", rootdev->name, p->mountpoint);
282
283    if (p->newfs) {
284	int i;
285
286	sprintf(dname, "/dev/r%sa", rootdisk->name);
287	msgNotify("Making a new root filesystem on %s", dname);
288	i = vsystem("%s %s", p->newfs_cmd, dname);
289	if (i) {
290	    msgConfirm("Unable to make new root filesystem!  Command returned status %d", i);
291	    return;
292	}
293    }
294    else {
295	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.");
296	sprintf(dname, "/dev/r%sa", rootdisk->name);
297	msgNotify("Checking integrity of existing %s filesystem", dname);
298	i = vsystem("fsck -y %s", dname);
299	if (i)
300	    msgConfirm("Warning: fsck returned status off %d - this partition may be\nunsafe to use.", i);
301    }
302    sprintf(dname, "/dev/%sa", rootdisk->name);
303    if (Mount("/mnt", dname)) {
304	msgConfirm("Unable to mount the root file system!  Giving up.");
305	return;
306    }
307    else {
308	extern int makedevs(void);
309
310	msgNotify("Making device files");
311	if (Mkdir("/mnt/dev", NULL) || chdir("/mnt/dev") || makedevs())
312	    msgConfirm("Failed to make some of the devices in /mnt!");
313	if (Mkdir("/mnt/stand", NULL)) {
314	    msgConfirm("Unable to make /mnt/stand directory!");
315	    return;
316	}
317	chdir("/");
318    }
319
320    /* Now buzz through the rest of the partitions and mount them too */
321    for (i = 0; devs[i]; i++) {
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    /* copy up the etc files */
381    (void)vsystem("(cd /mnt/stand; find etc) | cpio -pdmv /mnt");
382}
383
384static void loop_on_root_floppy();
385
386static void
387root_extract(void)
388{
389    int fd;
390
391    if (OnCDROM) {
392	fd = open("/floppies/root.flp", O_RDONLY);
393	(void)mediaExtractDist("root.flp", "/", fd);
394	return;
395    }
396    if (mediaDevice) {
397	switch(mediaDevice->type) {
398
399	case DEVICE_TYPE_DOS:
400	case DEVICE_TYPE_FTP:
401	case DEVICE_TYPE_DISK:
402	case DEVICE_TYPE_NETWORK:
403	case DEVICE_TYPE_CDROM:
404	    if (mediaDevice->init)
405		if (!(*mediaDevice->init)(mediaDevice))
406		    break;
407	    fd = (*mediaDevice->get)("floppies/root.flp");
408	    if (fd != -1) {
409		msgNotify("Loading root floppy from %s", mediaDevice->name);
410		(void)mediaExtractDist("root.flp", "/", fd);
411		if (mediaDevice->close)
412		    (*mediaDevice->close)(mediaDevice, fd);
413		else
414		    close(fd);
415		if (mediaDevice->shutdown)
416		    (*mediaDevice->shutdown)(mediaDevice);
417	    } else {
418		if (mediaDevice->shutdown)
419		    (*mediaDevice->shutdown)(mediaDevice);
420	        loop_on_root_floppy();
421	    }
422	    break;
423
424	case DEVICE_TYPE_FLOPPY:
425	default:
426	    loop_on_root_floppy();
427	    break;
428	}
429    }
430    else
431	loop_on_root_floppy();
432}
433
434static void
435loop_on_root_floppy(void)
436{
437    int fd;
438
439    fd = getRootFloppy();
440    if (fd != -1)
441	mediaExtractDist("root.flp", "/", fd);
442}
443