install.c revision 8715
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.49 1995/05/23 18:06:13 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);
56static void	do_final_setup(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 void
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;
134
135    if (!getenv(DISK_PARTITIONED)) {
136	msgConfirm("You need to partition your disk before you can proceed with\nthe installation.");
137	return;
138    }
139    if (!getenv(DISK_LABELLED)) {
140	msgConfirm("You need to assign disk labels before you can proceed with\nthe installation.");
141	return;
142    }
143    if (!checkLabels())
144	return;
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;
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    cpio_extract();
201    alreadyDone = TRUE;
202}
203
204static void
205installFinal(void)
206{
207    static Boolean alreadyDone = FALSE;
208
209    if (alreadyDone)
210	return;
211    config_fstab();
212    config_sysconfig();
213    config_resolv();
214    do_final_setup();
215    alreadyDone = TRUE;
216    SystemWasInstalled = TRUE;
217}
218
219/*
220 * What happens when we select "GO".  This is broken into a 3 stage installation so that
221 * the user can do a full installation but come back here again to load more distributions,
222 * perhaps from a different media type.  This would allow, for example, the user to load the
223 * majority of the system from CDROM and then use ftp to load just the DES dist.
224 */
225int
226installCommit(char *str)
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    installInitial();
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/%sa", rootdisk->name);
273    if (Mount("/mnt", dname)) {
274	msgConfirm("Unable to mount the root file system!  Giving up.");
275	return;
276    }
277    else {
278	extern int makedevs(void);
279
280	msgNotify("Making device files");
281	if (Mkdir("/mnt/dev", NULL) || chdir("/mnt/dev") || makedevs())
282	    msgConfirm("Failed to make some of the devices in /mnt!");
283	if (Mkdir("/mnt/stand", NULL)) {
284	    msgConfirm("Unable to make /mnt/stand directory!");
285	    return;
286	}
287	chdir("/");
288    }
289
290    /* Now buzz through the rest of the partitions and mount them too */
291    for (i = 0; devs[i]; i++) {
292	disk = (Disk *)devs[i]->private;
293	if (!disk->chunks)
294	    msgFatal("No chunk list found for %s!", disk->name);
295
296	/* Make the proper device mount points in /mnt/dev */
297	MakeDevDisk(disk, "/mnt/dev");
298
299	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
300	    if (c1->type == freebsd) {
301		for (c2 = c1->part; c2; c2 = c2->next) {
302		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private) {
303			PartInfo *tmp = (PartInfo *)c2->private;
304
305			if (!strcmp(tmp->mountpoint, "/"))
306			    continue;
307
308			if (tmp->newfs)
309			    command_shell_add(tmp->mountpoint, "%s /mnt/dev/r%s", tmp->newfs_cmd, c2->name);
310			command_func_add(tmp->mountpoint, Mount, c2->name);
311		    }
312		    else if (c2->type == part && c2->subtype == FS_SWAP) {
313			char fname[80];
314			int i;
315
316			sprintf(fname, "/mnt/dev/%s", c2->name);
317			i = swapon(fname);
318			if (!i)
319			    msgNotify("Added %s as a swap device", fname);
320			else
321			    msgConfirm("Unable to add %s as a swap device: %s", fname, strerror(errno));
322		    }
323		}
324	    }
325	}
326    }
327    command_sort();
328    command_execute();
329}
330
331/* Copy the boot floppy contents into /stand */
332static void
333copy_self(void)
334{
335    int i;
336
337    msgWeHaveOutput("Copying the boot floppy to /stand on root filesystem");
338    i = vsystem("find -x /stand | cpio -pdmv /mnt");
339    if (i)
340	msgConfirm("Copy returned error status of %d!", i);
341}
342
343static void
344cpio_extract(void)
345{
346    int i, j, zpid, cpid, pfd[2];
347    Boolean onCDROM = FALSE;
348
349#if 0
350    if (mediaDevice && mediaDevice->type == DEVICE_TYPE_CDROM) {
351	if (mediaDevice->init) {
352	    if ((*mediaDevice->init)(mediaDevice)) {
353		CpioFD = open("/cdrom/floppies/cpio.flp", O_RDONLY);
354		if (CpioFD != -1) {
355		    msgNotify("Loading CPIO floppy from CDROM");
356		    onCDROM = TRUE;
357		}
358	    }
359	}
360    }
361#endif
362 tryagain:
363    while (CpioFD == -1) {
364	msgConfirm("Please Insert CPIO floppy in floppy drive 0");
365	CpioFD = open("/dev/rfd0", O_RDONLY);
366	if (CpioFD >= 0)
367	    break;
368	msgDebug("Error on open of cpio floppy: %s (%d)\n", strerror(errno), errno);
369    }
370    j = fork();
371    if (!j) {
372	chdir("/");
373	msgWeHaveOutput("Extracting contents of CPIO floppy...");
374	pipe(pfd);
375	zpid = fork();
376	if (!zpid) {
377	    dup2(CpioFD, 0); close(CpioFD);
378	    dup2(pfd[1], 1); close(pfd[1]);
379	    if (DebugFD != -1)
380		dup2(DebugFD, 2);
381	    close(pfd[0]);
382	    i = execl("/stand/gunzip", "/stand/gunzip", 0);
383	    msgDebug("/stand/gunzip command returns %d status\n", i);
384	    exit(i);
385	}
386	cpid = fork();
387	if (!cpid) {
388	    dup2(pfd[0], 0); close(pfd[0]);
389	    close(CpioFD);
390	    close(pfd[1]);
391	    if (DebugFD != -1) {
392		dup2(DebugFD, 1);
393		dup2(DebugFD, 2);
394	    }
395	    else {
396		close(1); open("/dev/null", O_WRONLY);
397		dup2(1, 2);
398	    }
399	    i = execl("/stand/cpio", "/stand/cpio", "-iduvm", 0);
400	    msgDebug("/stand/cpio command returns %d status\n", i);
401	    exit(i);
402	}
403	close(pfd[0]);
404	close(pfd[1]);
405	close(CpioFD);
406
407	i = waitpid(zpid, &j, 0);
408	if (i < 0) {	/* Don't check status - gunzip seems to return a bogus one! */
409	    dialog_clear();
410	    msgConfirm("wait for gunzip returned status of %d!", i);
411	    exit(1);
412	}
413	i = waitpid(cpid, &j, 0);
414	if (i < 0 || WEXITSTATUS(j)) {
415	    dialog_clear();
416	    msgConfirm("cpio returned error status of %d!", WEXITSTATUS(j));
417	    exit(2);
418	}
419	exit(0);
420    }
421    else
422	i = wait(&j);
423    if (i < 0 || WEXITSTATUS(j) || access("/OK", R_OK) == -1) {
424	dialog_clear();
425	msgConfirm("CPIO floppy did not extract properly!  Please verify\nthat your media is correct and try again.");
426	close(CpioFD);
427	CpioFD = -1;
428	goto tryagain;
429    }
430    unlink("/OK");
431    if (!onCDROM)
432	msgConfirm("Please remove the CPIO floppy from the drive");
433}
434
435static void
436do_final_setup(void)
437{
438    dmenuOpenSimple(&MenuConfigure);
439}
440