install.c revision 8739
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.52 1995/05/24 17:49:16 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	msgConfirm("Please Insert CPIO floppy in %s", floppyDev->description);
418	CpioFD = open(floppyDev->devname, O_RDONLY);
419	if (CpioFD >= 0)
420	    break;
421	msgDebug("Error on open of cpio floppy: %s (%d)\n", strerror(errno), errno);
422    }
423    j = fork();
424    if (!j) {
425	chdir("/");
426	msgWeHaveOutput("Extracting contents of CPIO floppy...");
427	pipe(pfd);
428	zpid = fork();
429	if (!zpid) {
430	    dup2(CpioFD, 0); close(CpioFD);
431	    dup2(pfd[1], 1); close(pfd[1]);
432	    if (DebugFD != -1)
433		dup2(DebugFD, 2);
434	    close(pfd[0]);
435	    i = execl("/stand/gunzip", "/stand/gunzip", 0);
436	    msgDebug("/stand/gunzip command returns %d status\n", i);
437	    exit(i);
438	}
439	cpid = fork();
440	if (!cpid) {
441	    dup2(pfd[0], 0); close(pfd[0]);
442	    close(CpioFD);
443	    close(pfd[1]);
444	    if (DebugFD != -1) {
445		dup2(DebugFD, 1);
446		dup2(DebugFD, 2);
447	    }
448	    else {
449		close(1); open("/dev/null", O_WRONLY);
450		dup2(1, 2);
451	    }
452	    i = execl("/stand/cpio", "/stand/cpio", "-iduvm", 0);
453	    msgDebug("/stand/cpio command returns %d status\n", i);
454	    exit(i);
455	}
456	close(pfd[0]);
457	close(pfd[1]);
458	close(CpioFD);
459
460	i = waitpid(zpid, &j, 0);
461	if (i < 0) {	/* Don't check status - gunzip seems to return a bogus one! */
462	    dialog_clear();
463	    msgConfirm("wait for gunzip returned status of %d!", i);
464	    exit(1);
465	}
466	i = waitpid(cpid, &j, 0);
467	if (i < 0 || WEXITSTATUS(j)) {
468	    dialog_clear();
469	    msgConfirm("cpio returned error status of %d!", WEXITSTATUS(j));
470	    exit(2);
471	}
472	exit(0);
473    }
474    else
475	i = wait(&j);
476    if (i < 0 || WEXITSTATUS(j) || access("/OK", R_OK) == -1) {
477	dialog_clear();
478	msgConfirm("CPIO floppy did not extract properly!  Please verify\nthat your media is correct and try again.");
479	close(CpioFD);
480	CpioFD = -1;
481	goto tryagain;
482    }
483    unlink("/OK");
484    if (!onCDROM)
485	msgConfirm("Please remove the CPIO floppy from the drive");
486}
487