install.c revision 8742
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.53 1995/05/25 01:22:15 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/fcntl.h>
48#include <sys/wait.h>
49#include <unistd.h>
50
51Boolean SystemWasInstalled;
52
53static void	make_filesystems(void);
54static void	copy_self(void);
55static void	cpio_extract(void);
56
57static Disk *rootdisk;
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 &&
81			c2->private && c2->flags & CHUNK_IS_ROOT) {
82			rootdisk = disk;
83			rootdev = c2;
84			break;
85		    }
86		}
87	    }
88	}
89    }
90
91    /* Now check for swap devices */
92    for (i = 0; devs[i]; i++) {
93	disk = (Disk *)devs[i]->private;
94	msgDebug("Scanning disk %s for swap partitions\n", disk->name);
95	if (!disk->chunks)
96	    msgFatal("No chunk list found for %s!", disk->name);
97	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
98	    if (c1->type == freebsd) {
99		for (c2 = c1->part; c2; c2 = c2->next) {
100		    if (c2->type == part && c2->subtype == FS_SWAP) {
101			swapdev = c2;
102			break;
103		    }
104		}
105	    }
106	}
107    }
108
109    if (!rootdev) {
110	msgConfirm("No root device found - you must label a partition as /\n in the label editor.");
111	return FALSE;
112    }
113    if (!swapdev) {
114	msgConfirm("No swap devices found - you must create at least one\nswap partition.");
115	return FALSE;
116    }
117    return TRUE;
118}
119
120static Boolean
121installInitial(void)
122{
123    extern u_char boot1[], boot2[];
124    extern u_char mbr[], bteasy17[];
125    u_char *mbrContents;
126    Device **devs;
127    int i;
128    static Boolean alreadyDone = FALSE;
129    char *cp;
130
131    if (alreadyDone)
132	return TRUE;
133
134    if (!getenv(DISK_PARTITIONED)) {
135	msgConfirm("You need to partition your disk before you can proceed with\nthe installation.");
136	return FALSE;
137    }
138    if (!getenv(DISK_LABELLED)) {
139	msgConfirm("You need to assign disk labels before you can proceed with\nthe installation.");
140	return FALSE;
141    }
142    if (!checkLabels())
143	return FALSE;
144
145    /* Figure out what kind of MBR the user wants */
146    dmenuOpenSimple(&MenuMBRType);
147    mbrContents = NULL;
148    cp = getenv("bootManager");
149    if (cp) {
150	if (!strcmp(cp, "bteasy"))
151	    mbrContents = bteasy17;
152	else if (!strcmp(cp, "mbr"))
153	    mbrContents = mbr;
154    }
155
156    /* If we refuse to proceed, bail. */
157    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!"))
158	return FALSE;
159
160    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
161    for (i = 0; devs[i]; i++) {
162	Chunk *c1;
163	Disk *d = (Disk *)devs[i]->private;
164
165	if (!devs[i]->enabled)
166	    continue;
167
168	if (mbrContents) {
169	    Set_Boot_Mgr(d, mbrContents);
170	    mbrContents = NULL;
171	}
172	Set_Boot_Blocks(d, boot1, boot2);
173	msgNotify("Writing partition information to drive %s", d->name);
174	Write_Disk(d);
175
176	/* Now scan for bad blocks, if necessary */
177	for (c1 = d->chunks->part; c1; c1 = c1->next) {
178	    if (c1->flags & CHUNK_BAD144) {
179		int ret;
180
181		msgNotify("Running bad block scan on partition %s", c1->name);
182		ret = vsystem("bad144 -v /dev/r%s 1234", c1->name);
183		if (ret)
184		    msgConfirm("Bad144 init on %s returned status of %d!",
185			c1->name, ret);
186		ret = vsystem("bad144 -v -s /dev/r%s", c1->name);
187		if (ret)
188		    msgConfirm("Bad144 scan on %s returned status of %d!",
189			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    cpio_extract();
200    alreadyDone = TRUE;
201    return TRUE;
202}
203
204static void
205installFinal(void)
206{
207    static Boolean alreadyDone = FALSE;
208
209    if (alreadyDone)
210	return;
211    configFstab();
212    configSysconfig();
213    configResolv();
214    alreadyDone = TRUE;
215    SystemWasInstalled = TRUE;
216}
217
218/*
219 * What happens when we select "GO".  This is broken into a 3 stage installation so that
220 * the user can do a full installation but come back here again to load more distributions,
221 * perhaps from a different media type.  This would allow, for example, the user to load the
222 * majority of the system from CDROM and then use ftp to load just the DES dist.
223 */
224int
225installCommit(char *str)
226{
227    if (!Dists) {
228	msgConfirm("You haven't told me what distributions to load yet!\nPlease select a distribution from the Distributions menu.");
229	return 0;
230    }
231    if (!mediaVerify())
232	return 0;
233
234    if (!installInitial())
235	return 0;
236    distExtractAll();
237    installFinal();
238    return 0;
239}
240
241/* Go newfs and/or mount all the filesystems we've been asked to */
242static void
243make_filesystems(void)
244{
245    int i;
246    Disk *disk;
247    Chunk *c1, *c2;
248    Device **devs;
249    char dname[40];
250    PartInfo *p = (PartInfo *)rootdev->private;
251
252    command_clear();
253    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
254
255    /* First, create and mount the root device */
256    if (strcmp(p->mountpoint, "/"))
257	msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", rootdev->name, p->mountpoint);
258
259    if (p->newfs) {
260	int i;
261
262	sprintf(dname, "/dev/r%sa", rootdisk->name);
263	msgNotify("Making a new root filesystem on %s", dname);
264	i = vsystem("%s %s", p->newfs_cmd, dname);
265	if (i) {
266	    msgConfirm("Unable to make new root filesystem!  Command returned status %d", i);
267	    return;
268	}
269    }
270    else {
271	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.");
272	sprintf(dname, "/dev/r%sa", rootdisk->name);
273	msgNotify("Checking integrity of existing %s filesystem", dname);
274	i = vsystem("fsck -y %s", dname);
275	if (i)
276	    msgConfirm("Warning: fsck returned status off %d - this partition may be\nunsafe to use.", i);
277    }
278    sprintf(dname, "/dev/%sa", rootdisk->name);
279    if (Mount("/mnt", dname)) {
280	msgConfirm("Unable to mount the root file system!  Giving up.");
281	return;
282    }
283    else {
284	extern int makedevs(void);
285
286	msgNotify("Making device files");
287	if (Mkdir("/mnt/dev", NULL) || chdir("/mnt/dev") || makedevs())
288	    msgConfirm("Failed to make some of the devices in /mnt!");
289	if (Mkdir("/mnt/stand", NULL)) {
290	    msgConfirm("Unable to make /mnt/stand directory!");
291	    return;
292	}
293	chdir("/");
294    }
295
296    /* Now buzz through the rest of the partitions and mount them too */
297    for (i = 0; devs[i]; i++) {
298	disk = (Disk *)devs[i]->private;
299	if (!disk->chunks)
300	    msgFatal("No chunk list found for %s!", disk->name);
301
302	/* Make the proper device mount points in /mnt/dev */
303	MakeDevDisk(disk, "/mnt/dev");
304
305	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
306	    if (c1->type == freebsd) {
307		for (c2 = c1->part; c2; c2 = c2->next) {
308		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private) {
309			PartInfo *tmp = (PartInfo *)c2->private;
310
311			if (!strcmp(tmp->mountpoint, "/"))
312			    continue;
313
314			if (tmp->newfs)
315			    command_shell_add(tmp->mountpoint, "%s /mnt/dev/r%s", tmp->newfs_cmd, c2->name);
316			else
317			    command_shell_add(tmp->mountpoint, "fsck -y /mnt/dev/r%s", c2->name);
318			command_func_add(tmp->mountpoint, Mount, c2->name);
319		    }
320		    else if (c2->type == part && c2->subtype == FS_SWAP) {
321			char fname[80];
322			int i;
323
324			sprintf(fname, "/mnt/dev/%s", c2->name);
325			i = swapon(fname);
326			if (!i)
327			    msgNotify("Added %s as a swap device", fname);
328			else
329			    msgConfirm("Unable to add %s as a swap device: %s", fname, strerror(errno));
330		    }
331		}
332	    }
333	    else if (c1->type == fat) {
334		PartInfo *tmp = (PartInfo *)c1->private;
335
336		if (!tmp)
337		    continue;
338		command_func_add(tmp->mountpoint, Mount_DOS, c1->name);
339	    }
340	}
341    }
342    command_sort();
343    command_execute();
344}
345
346/* Copy the boot floppy contents into /stand */
347static void
348copy_self(void)
349{
350    int i;
351
352    msgWeHaveOutput("Copying the boot floppy to /stand on root filesystem");
353    i = vsystem("find -x /stand | cpio -pdmv /mnt");
354    if (i)
355	msgConfirm("Copy returned error status of %d!", i);
356}
357
358static Device *floppyDev;
359
360static int
361floppyChoiceHook(char *str)
362{
363    Device **devs;
364
365    /* Clip garbage off the ends */
366    string_prune(str);
367    str = string_skipwhite(str);
368    if (!*str)
369	return 0;
370    devs = deviceFind(str, DEVICE_TYPE_FLOPPY);
371    if (devs)
372	floppyDev = devs[0];
373    return devs ? 1 : 0;
374}
375
376static void
377cpio_extract(void)
378{
379    int i, j, zpid, cpid, pfd[2];
380    Boolean onCDROM = FALSE;
381
382    if (mediaDevice && mediaDevice->type == DEVICE_TYPE_CDROM) {
383	if (mediaDevice->init) {
384	    if ((*mediaDevice->init)(mediaDevice)) {
385		CpioFD = open("/cdrom/floppies/cpio.flp", O_RDONLY);
386		if (CpioFD != -1) {
387		    msgNotify("Loading CPIO floppy from CDROM");
388		    onCDROM = TRUE;
389		}
390	    }
391	}
392    }
393
394 tryagain:
395    while (CpioFD == -1) {
396	if (!floppyDev) {
397	    Device **devs;
398	    int cnt;
399
400	    devs = deviceFind(NULL, DEVICE_TYPE_FLOPPY);
401	    cnt = deviceCount(devs);
402	    if (cnt == 1)
403		floppyDev = devs[0];
404	    else if (cnt > 1) {
405		DMenu *menu;
406
407		menu = deviceCreateMenu(&MenuMediaFloppy, DEVICE_TYPE_FLOPPY, floppyChoiceHook);
408		dmenuOpenSimple(menu);
409	    }
410	    else {
411		msgConfirm("No floppy devices found!  Something is seriously wrong!");
412		return;
413	    }
414	    if (!floppyDev)
415		continue;
416	}
417	dialog_clear();
418	msgConfirm("Please Insert CPIO floppy in %s", floppyDev->description);
419	CpioFD = open(floppyDev->devname, O_RDONLY);
420	if (CpioFD >= 0)
421	    break;
422	msgDebug("Error on open of cpio floppy: %s (%d)\n", strerror(errno), errno);
423    }
424    j = fork();
425    if (!j) {
426	chdir("/");
427	msgWeHaveOutput("Extracting contents of CPIO floppy...");
428	pipe(pfd);
429	zpid = fork();
430	if (!zpid) {
431	    dup2(CpioFD, 0); close(CpioFD);
432	    dup2(pfd[1], 1); close(pfd[1]);
433	    if (DebugFD != -1)
434		dup2(DebugFD, 2);
435	    close(pfd[0]);
436	    i = execl("/stand/gunzip", "/stand/gunzip", 0);
437	    msgDebug("/stand/gunzip command returns %d status\n", i);
438	    exit(i);
439	}
440	cpid = fork();
441	if (!cpid) {
442	    dup2(pfd[0], 0); close(pfd[0]);
443	    close(CpioFD);
444	    close(pfd[1]);
445	    if (DebugFD != -1) {
446		dup2(DebugFD, 1);
447		dup2(DebugFD, 2);
448	    }
449	    else {
450		close(1); open("/dev/null", O_WRONLY);
451		dup2(1, 2);
452	    }
453	    i = execl("/stand/cpio", "/stand/cpio", "-iduvm", 0);
454	    msgDebug("/stand/cpio command returns %d status\n", i);
455	    exit(i);
456	}
457	close(pfd[0]);
458	close(pfd[1]);
459	close(CpioFD);
460
461	i = waitpid(zpid, &j, 0);
462	if (i < 0) {	/* Don't check status - gunzip seems to return a bogus one! */
463	    dialog_clear();
464	    msgConfirm("wait for gunzip returned status of %d!", i);
465	    exit(1);
466	}
467	i = waitpid(cpid, &j, 0);
468	if (i < 0 || WEXITSTATUS(j)) {
469	    dialog_clear();
470	    msgConfirm("cpio returned error status of %d!", WEXITSTATUS(j));
471	    exit(2);
472	}
473	exit(0);
474    }
475    else
476	i = wait(&j);
477    if (i < 0 || WEXITSTATUS(j) || access("/OK", R_OK) == -1) {
478	dialog_clear();
479	msgConfirm("CPIO floppy did not extract properly!  Please verify\nthat your media is correct and try again.");
480	close(CpioFD);
481	CpioFD = -1;
482	dialog_clear();
483	goto tryagain;
484    }
485    unlink("/OK");
486    if (!onCDROM)
487	msgConfirm("Please remove the CPIO floppy from the drive");
488}
489