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