install.c revision 8727
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.51 1995/05/24 09:00:28 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 void
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;
133
134    if (!getenv(DISK_PARTITIONED)) {
135	msgConfirm("You need to partition your disk before you can proceed with\nthe installation.");
136	return;
137    }
138    if (!getenv(DISK_LABELLED)) {
139	msgConfirm("You need to assign disk labels before you can proceed with\nthe installation.");
140	return;
141    }
142    if (!checkLabels())
143	return;
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;
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}
202
203static void
204installFinal(void)
205{
206    static Boolean alreadyDone = FALSE;
207
208    if (alreadyDone)
209	return;
210    configFstab();
211    configSysconfig();
212    configResolv();
213    alreadyDone = TRUE;
214    SystemWasInstalled = TRUE;
215}
216
217/*
218 * What happens when we select "GO".  This is broken into a 3 stage installation so that
219 * the user can do a full installation but come back here again to load more distributions,
220 * perhaps from a different media type.  This would allow, for example, the user to load the
221 * majority of the system from CDROM and then use ftp to load just the DES dist.
222 */
223int
224installCommit(char *str)
225{
226    if (!Dists) {
227	msgConfirm("You haven't told me what distributions to load yet!\nPlease select a distribution from the Distributions menu.");
228	return 0;
229    }
230    if (!mediaVerify())
231	return 0;
232
233    installInitial();
234    distExtractAll();
235    installFinal();
236    return 0;
237}
238
239/* Go newfs and/or mount all the filesystems we've been asked to */
240static void
241make_filesystems(void)
242{
243    int i;
244    Disk *disk;
245    Chunk *c1, *c2;
246    Device **devs;
247    char dname[40];
248    PartInfo *p = (PartInfo *)rootdev->private;
249
250    command_clear();
251    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
252
253    /* First, create and mount the root device */
254    if (strcmp(p->mountpoint, "/"))
255	msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", rootdev->name, p->mountpoint);
256
257    if (p->newfs) {
258	int i;
259
260	sprintf(dname, "/dev/r%sa", rootdisk->name);
261	msgNotify("Making a new root filesystem on %s", dname);
262	i = vsystem("%s %s", p->newfs_cmd, dname);
263	if (i) {
264	    msgConfirm("Unable to make new root filesystem!  Command returned status %d", i);
265	    return;
266	}
267    }
268    else {
269	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.");
270	sprintf(dname, "/dev/r%sa", rootdisk->name);
271	msgNotify("Checking integrity of existing %s filesystem", dname);
272	i = vsystem("fsck -y %s", dname);
273	if (i)
274	    msgConfirm("Warning: fsck returned status off %d - this partition may be\nunsafe to use.", i);
275    }
276    sprintf(dname, "/dev/%sa", rootdisk->name);
277    if (Mount("/mnt", dname)) {
278	msgConfirm("Unable to mount the root file system!  Giving up.");
279	return;
280    }
281    else {
282	extern int makedevs(void);
283
284	msgNotify("Making device files");
285	if (Mkdir("/mnt/dev", NULL) || chdir("/mnt/dev") || makedevs())
286	    msgConfirm("Failed to make some of the devices in /mnt!");
287	if (Mkdir("/mnt/stand", NULL)) {
288	    msgConfirm("Unable to make /mnt/stand directory!");
289	    return;
290	}
291	chdir("/");
292    }
293
294    /* Now buzz through the rest of the partitions and mount them too */
295    for (i = 0; devs[i]; i++) {
296	disk = (Disk *)devs[i]->private;
297	if (!disk->chunks)
298	    msgFatal("No chunk list found for %s!", disk->name);
299
300	/* Make the proper device mount points in /mnt/dev */
301	MakeDevDisk(disk, "/mnt/dev");
302
303	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
304	    if (c1->type == freebsd) {
305		for (c2 = c1->part; c2; c2 = c2->next) {
306		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private) {
307			PartInfo *tmp = (PartInfo *)c2->private;
308
309			if (!strcmp(tmp->mountpoint, "/"))
310			    continue;
311
312			if (tmp->newfs)
313			    command_shell_add(tmp->mountpoint, "%s /mnt/dev/r%s", tmp->newfs_cmd, c2->name);
314			else
315			    command_shell_add(tmp->mountpoint, "fsck -y /mnt/dev/r%s", c2->name);
316			command_func_add(tmp->mountpoint, Mount, c2->name);
317		    }
318		    else if (c2->type == part && c2->subtype == FS_SWAP) {
319			char fname[80];
320			int i;
321
322			sprintf(fname, "/mnt/dev/%s", c2->name);
323			i = swapon(fname);
324			if (!i)
325			    msgNotify("Added %s as a swap device", fname);
326			else
327			    msgConfirm("Unable to add %s as a swap device: %s", fname, strerror(errno));
328		    }
329		}
330	    }
331	    else if (c1->type == fat) {
332		PartInfo *tmp = (PartInfo *)c1->private;
333
334		if (!tmp)
335		    continue;
336		command_func_add(tmp->mountpoint, Mount, c1->name);
337	    }
338	}
339    }
340    command_sort();
341    command_execute();
342}
343
344/* Copy the boot floppy contents into /stand */
345static void
346copy_self(void)
347{
348    int i;
349
350    msgWeHaveOutput("Copying the boot floppy to /stand on root filesystem");
351    i = vsystem("find -x /stand | cpio -pdmv /mnt");
352    if (i)
353	msgConfirm("Copy returned error status of %d!", i);
354}
355
356static Device *floppyDev;
357
358static int
359floppyChoiceHook(char *str)
360{
361    Device **devs;
362
363    /* Clip garbage off the ends */
364    string_prune(str);
365    str = string_skipwhite(str);
366    if (!*str)
367	return 0;
368    devs = deviceFind(str, DEVICE_TYPE_FLOPPY);
369    if (devs)
370	floppyDev = devs[0];
371    return devs ? 1 : 0;
372}
373
374static void
375cpio_extract(void)
376{
377    int i, j, zpid, cpid, pfd[2];
378    Boolean onCDROM = FALSE;
379
380    if (mediaDevice && mediaDevice->type == DEVICE_TYPE_CDROM) {
381	if (mediaDevice->init) {
382	    if ((*mediaDevice->init)(mediaDevice)) {
383		CpioFD = open("/cdrom/floppies/cpio.flp", O_RDONLY);
384		if (CpioFD != -1) {
385		    msgNotify("Loading CPIO floppy from CDROM");
386		    onCDROM = TRUE;
387		}
388	    }
389	}
390    }
391
392 tryagain:
393    while (CpioFD == -1) {
394	if (!floppyDev) {
395	    Device **devs;
396	    int cnt;
397
398	    devs = deviceFind(NULL, DEVICE_TYPE_FLOPPY);
399	    cnt = deviceCount(devs);
400	    if (cnt == 1)
401		floppyDev = devs[0];
402	    else if (cnt > 1) {
403		DMenu *menu;
404
405		menu = deviceCreateMenu(&MenuMediaFloppy, DEVICE_TYPE_FLOPPY, floppyChoiceHook);
406		dmenuOpenSimple(menu);
407	    }
408	    else {
409		msgConfirm("No floppy devices found!  Something is seriously wrong!");
410		return;
411	    }
412	    if (!floppyDev)
413		continue;
414	}
415	msgConfirm("Please Insert CPIO floppy in %s", floppyDev->description);
416	CpioFD = open(floppyDev->devname, O_RDONLY);
417	if (CpioFD >= 0)
418	    break;
419	msgDebug("Error on open of cpio floppy: %s (%d)\n", strerror(errno), errno);
420    }
421    j = fork();
422    if (!j) {
423	chdir("/");
424	msgWeHaveOutput("Extracting contents of CPIO floppy...");
425	pipe(pfd);
426	zpid = fork();
427	if (!zpid) {
428	    dup2(CpioFD, 0); close(CpioFD);
429	    dup2(pfd[1], 1); close(pfd[1]);
430	    if (DebugFD != -1)
431		dup2(DebugFD, 2);
432	    close(pfd[0]);
433	    i = execl("/stand/gunzip", "/stand/gunzip", 0);
434	    msgDebug("/stand/gunzip command returns %d status\n", i);
435	    exit(i);
436	}
437	cpid = fork();
438	if (!cpid) {
439	    dup2(pfd[0], 0); close(pfd[0]);
440	    close(CpioFD);
441	    close(pfd[1]);
442	    if (DebugFD != -1) {
443		dup2(DebugFD, 1);
444		dup2(DebugFD, 2);
445	    }
446	    else {
447		close(1); open("/dev/null", O_WRONLY);
448		dup2(1, 2);
449	    }
450	    i = execl("/stand/cpio", "/stand/cpio", "-iduvm", 0);
451	    msgDebug("/stand/cpio command returns %d status\n", i);
452	    exit(i);
453	}
454	close(pfd[0]);
455	close(pfd[1]);
456	close(CpioFD);
457
458	i = waitpid(zpid, &j, 0);
459	if (i < 0) {	/* Don't check status - gunzip seems to return a bogus one! */
460	    dialog_clear();
461	    msgConfirm("wait for gunzip returned status of %d!", i);
462	    exit(1);
463	}
464	i = waitpid(cpid, &j, 0);
465	if (i < 0 || WEXITSTATUS(j)) {
466	    dialog_clear();
467	    msgConfirm("cpio returned error status of %d!", WEXITSTATUS(j));
468	    exit(2);
469	}
470	exit(0);
471    }
472    else
473	i = wait(&j);
474    if (i < 0 || WEXITSTATUS(j) || access("/OK", R_OK) == -1) {
475	dialog_clear();
476	msgConfirm("CPIO floppy did not extract properly!  Please verify\nthat your media is correct and try again.");
477	close(CpioFD);
478	CpioFD = -1;
479	goto tryagain;
480    }
481    unlink("/OK");
482    if (!onCDROM)
483	msgConfirm("Please remove the CPIO floppy from the drive");
484}
485