install.c revision 9202
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.70.2.41 1995/06/10 07:58:37 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/ioctl.h> 48#include <sys/fcntl.h> 49#include <sys/wait.h> 50#include <unistd.h> 51 52Boolean SystemWasInstalled = FALSE; 53 54static Boolean make_filesystems(void); 55static Boolean copy_self(void); 56static Boolean root_extract(void); 57 58static Chunk *rootdev; 59 60static Boolean 61checkLabels(void) 62{ 63 Device **devs; 64 Disk *disk; 65 Chunk *c1, *c2, *swapdev, *usrdev; 66 int i; 67 68 rootdev = swapdev = usrdev = NULL; 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 && c2->private) { 82 if (c2->flags & CHUNK_IS_ROOT) { 83 if (rootdev) { 84 msgConfirm("WARNING: You have more than one root device set?!\nUsing the first one found."); 85 continue; 86 } 87 rootdev = c2; 88 } 89 else if (!strcmp(((PartInfo *)c2->private)->mountpoint, "/usr")) { 90 if (usrdev) { 91 msgConfirm("WARNING: You have more than one /usr filesystem.\nUsing the first one found."); 92 continue; 93 } 94 usrdev = c2; 95 } 96 } 97 } 98 } 99 } 100 } 101 102 /* Now check for swap devices */ 103 for (i = 0; devs[i]; i++) { 104 disk = (Disk *)devs[i]->private; 105 msgDebug("Scanning disk %s for swap partitions\n", disk->name); 106 if (!disk->chunks) 107 msgFatal("No chunk list found for %s!", disk->name); 108 for (c1 = disk->chunks->part; c1; c1 = c1->next) { 109 if (c1->type == freebsd) { 110 for (c2 = c1->part; c2; c2 = c2->next) { 111 if (c2->type == part && c2->subtype == FS_SWAP) { 112 swapdev = c2; 113 break; 114 } 115 } 116 } 117 } 118 } 119 120 if (!rootdev) { 121 msgConfirm("No root device found - you must label a partition as /\n in the label editor."); 122 return FALSE; 123 } 124 else if (rootdev->name[strlen(rootdev->name) - 1] != 'a') { 125 msgConfirm("Invalid placement of root partition. For now, we only support\nmounting root partitions on \"a\" partitions due to limitations\nin the FreeBSD boot block code. Please correct this and\ntry again."); 126 return FALSE; 127 } 128 if (!swapdev) { 129 msgConfirm("No swap devices found - you must create at least one\nswap partition."); 130 return FALSE; 131 } 132 if (!usrdev) 133 msgConfirm("WARNING: No /usr filesystem found. This is not technically\nan error if your root filesystem is big enough (or you later\nintend to get your /usr filesystem over NFS), but it may otherwise\ncause you trouble and is not recommended procedure!"); 134 return TRUE; 135} 136 137static Boolean 138installInitial(void) 139{ 140 extern u_char boot1[], boot2[]; 141 extern u_char mbr[], bteasy17[]; 142 u_char *mbrContents; 143 Device **devs; 144 int i; 145 static Boolean alreadyDone = FALSE; 146 147 if (alreadyDone) 148 return TRUE; 149 150 if (!getenv(DISK_PARTITIONED)) { 151 msgConfirm("You need to partition your disk before you can proceed with\nthe installation."); 152 return FALSE; 153 } 154 if (!getenv(DISK_LABELLED)) { 155 msgConfirm("You need to assign disk labels before you can proceed with\nthe installation."); 156 return FALSE; 157 } 158 if (!checkLabels()) 159 return FALSE; 160 161 /* Figure out what kind of MBR the user wants */ 162 if (!dmenuOpenSimple(&MenuMBRType)) 163 return FALSE; 164 165 switch (BootMgr) { 166 case 0: 167 mbrContents = bteasy17; 168 break; 169 170 case 1: 171 mbrContents = mbr; 172 break; 173 174 case 2: 175 default: 176 mbrContents = NULL; 177 } 178 179 /* If we refuse to proceed, bail. */ 180 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!")) 181 return FALSE; 182 183 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 184 for (i = 0; devs[i]; i++) { 185 Chunk *c1; 186 Disk *d = (Disk *)devs[i]->private; 187 188 if (!devs[i]->enabled) 189 continue; 190 191 if (mbrContents) { 192 Set_Boot_Mgr(d, mbrContents); 193 mbrContents = NULL; 194 } 195 Set_Boot_Blocks(d, boot1, boot2); 196 msgNotify("Writing partition information to drive %s", d->name); 197 Write_Disk(d); 198 199 /* Now scan for bad blocks, if necessary */ 200 for (c1 = d->chunks->part; c1; c1 = c1->next) { 201 if (c1->flags & CHUNK_BAD144) { 202 int ret; 203 204 msgNotify("Running bad block scan on partition %s", c1->name); 205 ret = vsystem("bad144 -v /dev/r%s 1234", c1->name); 206 if (ret) 207 msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret); 208 ret = vsystem("bad144 -v -s /dev/r%s", c1->name); 209 if (ret) 210 msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret); 211 } 212 } 213 } 214 if (!make_filesystems()) { 215 msgConfirm("Couldn't make filesystems properly. Aborting."); 216 return 0; 217 } 218 if (!copy_self()) { 219 msgConfirm("Couldn't clone the boot floppy onto the root file system.\nAborting."); 220 return 0; 221 } 222 dialog_clear(); 223 chroot("/mnt"); 224 chdir("/"); 225 variable_set2(RUNNING_ON_ROOT, "yes"); 226 /* stick a helpful shell over on the 4th VTY */ 227 if (OnVTY && !fork()) { 228 int i, fd; 229 extern int login_tty(int); 230 231 msgDebug("Starting an emergency holographic shell over on the 4th screen\n"); 232 for (i = 0; i < 64; i++) 233 close(i); 234 fd = open("/dev/ttyv3", O_RDWR); 235 ioctl(0, TIOCSCTTY, &fd); 236 dup2(0, 1); 237 dup2(0, 2); 238 if (login_tty(fd) == -1) { 239 msgNotify("Can't set controlling terminal"); 240 exit(1); 241 } 242 printf("Warning: This shell is chroot()'d to /mnt\n"); 243 execlp("sh", "-sh", 0); 244 exit(1); 245 } 246 alreadyDone = TRUE; 247 return TRUE; 248} 249 250/* 251 * What happens when we select "Install". This is broken into a 3 stage installation so that 252 * the user can do a full installation but come back here again to load more distributions, 253 * perhaps from a different media type. This would allow, for example, the user to load the 254 * majority of the system from CDROM and then use ftp to load just the DES dist. 255 */ 256int 257installCommit(char *str) 258{ 259 Device **devs; 260 int i; 261 262 if (!Dists) { 263 msgConfirm("You haven't told me what distributions to load yet!\nPlease select a distribution from the Distributions menu."); 264 return 0; 265 } 266 if (!mediaVerify()) 267 return 0; 268 269 if (RunningAsInit && !SystemWasInstalled) { 270 if (!installInitial()) 271 return 0; 272 configFstab(); 273 } 274 if (!SystemWasInstalled && !root_extract()) { 275 msgConfirm("Failed to load the ROOT distribution. Please correct\nthis problem and try again."); 276 return 0; 277 } 278 279 /* If we're about to extract the bin dist again, reset the installed state */ 280 if (Dists & DIST_BIN) 281 SystemWasInstalled = FALSE; 282 283 distExtractAll(); 284 285 if (!SystemWasInstalled && access("/kernel", R_OK)) { 286 if (vsystem("ln -f /kernel.GENERIC /kernel")) { 287 msgConfirm("Unable to link /kernel into place!"); 288 return 0; 289 } 290 } 291 292 /* Resurrect /dev after bin distribution screws it up */ 293 if (!SystemWasInstalled) { 294 msgNotify("Remaking all devices.. Please wait!"); 295 if (vsystem("cd /dev; sh MAKEDEV all")) 296 msgConfirm("MAKEDEV returned non-zero status"); 297 298 msgNotify("Resurrecting /dev entries for slices.."); 299 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 300 if (!devs) 301 msgFatal("Couldn't get a disk device list!"); 302 /* Resurrect the slices that the former clobbered */ 303 for (i = 0; devs[i]; i++) { 304 Disk *disk = (Disk *)devs[i]->private; 305 Chunk *c1; 306 307 if (!disk->chunks) 308 msgFatal("No chunk list found for %s!", disk->name); 309 for (c1 = disk->chunks->part; c1; c1 = c1->next) { 310 if (c1->type == freebsd) { 311 msgNotify("Making slice entries for %s", c1->name); 312 if (vsystem("cd /dev; sh MAKEDEV %sh", c1->name)) 313 msgConfirm("Unable to make slice entries for %s!", c1->name); 314 } 315 } 316 } 317 } 318 319 /* XXX Do all the last ugly work-arounds here which we'll try and excise someday right?? XXX */ 320 /* BOGON #1: XFree86 extracting /usr/X11R6 with root-only perms */ 321 if (file_readable("/usr/X11R6")) 322 (void)system("chmod 755 /usr/X11R6"); 323 324 /* BOGON #2: We leave /etc in a bad state */ 325 (void)system("chmod 755 /etc"); 326 327 dialog_clear(); 328 if (Dists) 329 msgConfirm("Installation completed with some errors. You may wish\nto scroll through the debugging messages on ALT-F2 with the scroll-lock\nfeature. Press [ENTER] to return to the installation menu."); 330 else 331 msgConfirm("Installation completed successfully, now press [ENTER] to return\nto the main menu. If you have any network devices you have not yet\nconfigured, see the Interface configuration item on the\nConfiguration menu."); 332 SystemWasInstalled = TRUE; 333 return 0; 334} 335 336/* Go newfs and/or mount all the filesystems we've been asked to */ 337static Boolean 338make_filesystems(void) 339{ 340 int i; 341 Disk *disk; 342 Chunk *c1, *c2; 343 Device **devs; 344 char dname[40]; 345 PartInfo *p = (PartInfo *)rootdev->private; 346 Boolean RootReadOnly; 347 348 command_clear(); 349 devs = deviceFind(NULL, DEVICE_TYPE_DISK); 350 351 /* First, create and mount the root device */ 352 if (strcmp(p->mountpoint, "/")) 353 msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", rootdev->name, p->mountpoint); 354 355 if (p->newfs) { 356 int i; 357 358 sprintf(dname, "/dev/r%sa", rootdev->disk->name); 359 msgNotify("Making a new root filesystem on %s", dname); 360 i = vsystem("%s %s", p->newfs_cmd, dname); 361 if (i) { 362 msgConfirm("Unable to make new root filesystem! Command returned status %d", i); 363 return FALSE; 364 } 365 RootReadOnly = FALSE; 366 } 367 else { 368 RootReadOnly = TRUE; 369 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."); 370 sprintf(dname, "/dev/r%sa", rootdev->disk->name); 371 msgNotify("Checking integrity of existing %s filesystem", dname); 372 i = vsystem("fsck -y %s", dname); 373 if (i) 374 msgConfirm("Warning: fsck returned status off %d - this partition may be\nunsafe to use.", i); 375 } 376 sprintf(dname, "/dev/%sa", rootdev->disk->name); 377 if (Mount("/mnt", dname)) { 378 msgConfirm("Unable to mount the root file system! Giving up."); 379 return FALSE; 380 } 381 382 /* Now buzz through the rest of the partitions and mount them too */ 383 for (i = 0; devs[i]; i++) { 384 if (!devs[i]->enabled) 385 continue; 386 387 disk = (Disk *)devs[i]->private; 388 if (!disk->chunks) { 389 msgConfirm("No chunk list found for %s!", disk->name); 390 return FALSE; 391 } 392 393 /* Make the proper device mount points in /mnt/dev */ 394 if (!(RootReadOnly && disk == rootdev->disk)) { 395 Mkdir("/mnt/dev", NULL); 396 MakeDevDisk(disk, "/mnt/dev"); 397 } 398 for (c1 = disk->chunks->part; c1; c1 = c1->next) { 399 if (c1->type == freebsd) { 400 for (c2 = c1->part; c2; c2 = c2->next) { 401 if (c2->type == part && c2->subtype != FS_SWAP && c2->private) { 402 PartInfo *tmp = (PartInfo *)c2->private; 403 404 if (!strcmp(tmp->mountpoint, "/")) 405 continue; 406 407 if (tmp->newfs) 408 command_shell_add(tmp->mountpoint, "%s /mnt/dev/r%s", tmp->newfs_cmd, c2->name); 409 else 410 command_shell_add(tmp->mountpoint, "fsck -y /mnt/dev/r%s", c2->name); 411 command_func_add(tmp->mountpoint, Mount, c2->name); 412 } 413 else if (c2->type == part && c2->subtype == FS_SWAP) { 414 char fname[80]; 415 int i; 416 417 sprintf(fname, "/mnt/dev/%s", c2->name); 418 i = swapon(fname); 419 if (!i) 420 msgNotify("Added %s as a swap device", fname); 421 else 422 msgConfirm("Unable to add %s as a swap device: %s", fname, strerror(errno)); 423 } 424 } 425 } 426 else if (c1->type == fat && c1->private && !RootReadOnly) { 427 char name[FILENAME_MAX]; 428 429 sprintf(name, "/mnt%s", ((PartInfo *)c1->private)->mountpoint); 430 Mkdir(name, NULL); 431 } 432 } 433 } 434 435 /* Copy the boot floppy's dev files */ 436 if (vsystem("find -x /dev | cpio -pdmV /mnt")) { 437 msgConfirm("Couldn't clone the /dev files!"); 438 return FALSE; 439 } 440 441 command_sort(); 442 command_execute(); 443 return TRUE; 444} 445 446/* Copy the boot floppy contents into /stand */ 447static Boolean 448copy_self(void) 449{ 450 int i; 451 452 msgWeHaveOutput("Copying the boot floppy to /stand on root filesystem"); 453 i = vsystem("find -x /stand | cpio -pdmV /mnt"); 454 if (i) { 455 msgConfirm("Copy returned error status of %d!", i); 456 return FALSE; 457 } 458 459 /* Copy the /etc files into their rightful place */ 460 if (vsystem("cd /mnt/stand; find etc | cpio -pdmV /mnt")) { 461 msgConfirm("Couldn't copy up the /etc files!"); 462 return TRUE; 463 } 464 return TRUE; 465} 466 467static Boolean loop_on_root_floppy(void); 468 469static Boolean 470root_extract(void) 471{ 472 int fd; 473 static Boolean alreadyExtracted = FALSE; 474 475 if (alreadyExtracted) 476 return TRUE; 477 478 if (mediaDevice) { 479 if (isDebug()) 480 msgDebug("Attempting to extract root image from %s device\n", mediaDevice->description); 481 switch(mediaDevice->type) { 482 483 case DEVICE_TYPE_FLOPPY: 484 alreadyExtracted = loop_on_root_floppy(); 485 break; 486 487 default: 488 if (!(*mediaDevice->init)(mediaDevice)) 489 break; 490 fd = (*mediaDevice->get)(mediaDevice, "floppies/root.flp", NULL); 491 if (fd < 0) { 492 msgConfirm("Couldn't get root image from %s!\nWill try to get it from floppy.", mediaDevice->name); 493 (*mediaDevice->shutdown)(mediaDevice); 494 alreadyExtracted = loop_on_root_floppy(); 495 } 496 else { 497 msgNotify("Loading root image from %s", mediaDevice->name); 498 alreadyExtracted = mediaExtractDist("/", fd); 499 (*mediaDevice->close)(mediaDevice, fd); 500 } 501 break; 502 } 503 } 504 else 505 alreadyExtracted = loop_on_root_floppy(); 506 return alreadyExtracted; 507} 508 509static Boolean 510loop_on_root_floppy(void) 511{ 512 int fd; 513 int status = FALSE; 514 515 while (1) { 516 fd = getRootFloppy(); 517 if (fd != -1) { 518 msgNotify("Extracting root floppy.."); 519 status = mediaExtractDist("/", fd); 520 close(fd); 521 break; 522 } 523 } 524 return status; 525} 526