install.c revision 8722
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.50 1995/05/24 01:27:10 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    configFstab();
212    configSysconfig();
213    configResolv();
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 (mediaDevice && mediaDevice->type == DEVICE_TYPE_CDROM) {
350	if (mediaDevice->init) {
351	    if ((*mediaDevice->init)(mediaDevice)) {
352		CpioFD = open("/cdrom/floppies/cpio.flp", O_RDONLY);
353		if (CpioFD != -1) {
354		    msgNotify("Loading CPIO floppy from CDROM");
355		    onCDROM = TRUE;
356		}
357	    }
358	}
359    }
360
361 tryagain:
362    while (CpioFD == -1) {
363	msgConfirm("Please Insert CPIO floppy in floppy drive 0");
364	CpioFD = open("/dev/rfd0", O_RDONLY);
365	if (CpioFD >= 0)
366	    break;
367	msgDebug("Error on open of cpio floppy: %s (%d)\n", strerror(errno), errno);
368    }
369    j = fork();
370    if (!j) {
371	chdir("/");
372	msgWeHaveOutput("Extracting contents of CPIO floppy...");
373	pipe(pfd);
374	zpid = fork();
375	if (!zpid) {
376	    dup2(CpioFD, 0); close(CpioFD);
377	    dup2(pfd[1], 1); close(pfd[1]);
378	    if (DebugFD != -1)
379		dup2(DebugFD, 2);
380	    close(pfd[0]);
381	    i = execl("/stand/gunzip", "/stand/gunzip", 0);
382	    msgDebug("/stand/gunzip command returns %d status\n", i);
383	    exit(i);
384	}
385	cpid = fork();
386	if (!cpid) {
387	    dup2(pfd[0], 0); close(pfd[0]);
388	    close(CpioFD);
389	    close(pfd[1]);
390	    if (DebugFD != -1) {
391		dup2(DebugFD, 1);
392		dup2(DebugFD, 2);
393	    }
394	    else {
395		close(1); open("/dev/null", O_WRONLY);
396		dup2(1, 2);
397	    }
398	    i = execl("/stand/cpio", "/stand/cpio", "-iduvm", 0);
399	    msgDebug("/stand/cpio command returns %d status\n", i);
400	    exit(i);
401	}
402	close(pfd[0]);
403	close(pfd[1]);
404	close(CpioFD);
405
406	i = waitpid(zpid, &j, 0);
407	if (i < 0) {	/* Don't check status - gunzip seems to return a bogus one! */
408	    dialog_clear();
409	    msgConfirm("wait for gunzip returned status of %d!", i);
410	    exit(1);
411	}
412	i = waitpid(cpid, &j, 0);
413	if (i < 0 || WEXITSTATUS(j)) {
414	    dialog_clear();
415	    msgConfirm("cpio returned error status of %d!", WEXITSTATUS(j));
416	    exit(2);
417	}
418	exit(0);
419    }
420    else
421	i = wait(&j);
422    if (i < 0 || WEXITSTATUS(j) || access("/OK", R_OK) == -1) {
423	dialog_clear();
424	msgConfirm("CPIO floppy did not extract properly!  Please verify\nthat your media is correct and try again.");
425	close(CpioFD);
426	CpioFD = -1;
427	goto tryagain;
428    }
429    unlink("/OK");
430    if (!onCDROM)
431	msgConfirm("Please remove the CPIO floppy from the drive");
432}
433
434static void
435do_final_setup(void)
436{
437    dmenuOpenSimple(&MenuConfigure);
438}
439