1 /* $NetBSD: installboot.c,v 1.36 2011/11/03 20:46:41 martin Exp $ */ 2 3 /*- 4 * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Luke Mewburn of Wasabi Systems. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #if HAVE_NBTOOL_CONFIG_H 33 #include "nbtool_config.h" 34 #endif 35 36 #include <sys/cdefs.h> 37 #if !defined(__lint) 38 __RCSID("$NetBSD: installboot.c,v 1.36 2011/11/03 20:46:41 martin Exp $"); 39 #endif /* !__lint */ 40 41 #include <sys/ioctl.h> 42 #include <sys/utsname.h> 43 #include <sys/ttycom.h> 44 45 #include <assert.h> 46 #include <err.h> 47 #include <fcntl.h> 48 #include <limits.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <stddef.h> 52 #include <string.h> 53 #include <unistd.h> 54 55 #include "installboot.h" 56 57 static void getmachine(ib_params *, const char *, const char *); 58 static void getfstype(ib_params *, const char *, const char *); 59 static void parseoptions(ib_params *, const char *); 60 __dead static void usage(void); 61 static void options_usage(void); 62 static void machine_usage(void); 63 static void fstype_usage(void); 64 65 static ib_params installboot_params; 66 67 #define OFFSET(field) offsetof(ib_params, field) 68 const struct option { 69 const char *name; /* Name of option */ 70 ib_flags flag; /* Corresponding IB_xxx flag */ 71 enum { /* Type of option value... */ 72 OPT_BOOL, /* no value */ 73 OPT_INT, /* numeric value */ 74 OPT_WORD, /* space/tab/, terminated */ 75 OPT_STRING /* null terminated */ 76 } type; 77 int offset; /* of field in ib_params */ 78 } options[] = { 79 { "alphasum", IB_ALPHASUM, OPT_BOOL, 0 }, 80 { "append", IB_APPEND, OPT_BOOL, 0 }, 81 { "command", IB_COMMAND, OPT_STRING, OFFSET(command) }, 82 { "console", IB_CONSOLE, OPT_WORD, OFFSET(console) }, 83 { "ioaddr", IB_CONSADDR, OPT_INT, OFFSET(consaddr) }, 84 { "keymap", IB_KEYMAP, OPT_WORD, OFFSET(keymap) }, 85 { "password", IB_PASSWORD, OPT_WORD, OFFSET(password) }, 86 { "resetvideo", IB_RESETVIDEO, OPT_BOOL, 0 }, 87 { "speed", IB_CONSPEED, OPT_INT, OFFSET(conspeed) }, 88 { "sunsum", IB_SUNSUM, OPT_BOOL, 0 }, 89 { "timeout", IB_TIMEOUT, OPT_INT, OFFSET(timeout) }, 90 { "modules", IB_MODULES, OPT_BOOL, 0 }, 91 { "bootconf", IB_BOOTCONF, OPT_BOOL, 0 }, 92 { .name = NULL }, 93 }; 94 #undef OFFSET 95 #define OPTION(params, type, opt) (*(type *)((char *)(params) + (opt)->offset)) 96 97 #define DFL_SECSIZE 512 /* Don't use DEV_BSIZE. It's host's value. */ 98 99 int 100 main(int argc, char *argv[]) 101 { 102 struct utsname utsname; 103 ib_params *params; 104 unsigned long lval; 105 int ch, rv, mode; 106 char *p; 107 const char *op; 108 ib_flags unsupported_flags; 109 110 /* XXX Temp stuff for MINIX until fdisk is ported */ 111 if ((4 <= argc && argc <= 6) && isoption(argv[1], "-master")) { 112 install_master(argv[2], argv[3], argv + 4); 113 exit(0); 114 } 115 116 setprogname(argv[0]); 117 params = &installboot_params; 118 memset(params, 0, sizeof(*params)); 119 params->fsfd = -1; 120 params->s1fd = -1; 121 if ((p = getenv("MACHINE")) != NULL) 122 getmachine(params, p, "$MACHINE"); 123 124 while ((ch = getopt(argc, argv, "b:B:cefm:no:t:v")) != -1) { 125 switch (ch) { 126 127 case 'b': 128 case 'B': 129 if (*optarg == '\0') 130 goto badblock; 131 lval = strtoul(optarg, &p, 0); 132 if (lval > UINT32_MAX || *p != '\0') { 133 badblock: 134 errx(1, "Invalid block number `%s'", optarg); 135 } 136 if (ch == 'b') { 137 params->s1start = (uint32_t)lval; 138 params->flags |= IB_STAGE1START; 139 } else { 140 params->s2start = (uint32_t)lval; 141 params->flags |= IB_STAGE2START; 142 } 143 break; 144 145 case 'c': 146 params->flags |= IB_CLEAR; 147 break; 148 149 case 'e': 150 params->flags |= IB_EDIT; 151 break; 152 153 case 'f': 154 params->flags |= IB_FORCE; 155 break; 156 157 case 'm': 158 getmachine(params, optarg, "-m"); 159 break; 160 161 case 'n': 162 params->flags |= IB_NOWRITE; 163 break; 164 165 case 'o': 166 parseoptions(params, optarg); 167 break; 168 169 case 't': 170 getfstype(params, optarg, "-t"); 171 break; 172 173 case 'v': 174 params->flags |= IB_VERBOSE; 175 break; 176 177 case '?': 178 default: 179 usage(); 180 /* NOTREACHED */ 181 182 } 183 } 184 argc -= optind; 185 argv += optind; 186 187 if (params->flags & IB_CLEAR && params->flags & IB_EDIT) 188 usage(); 189 if (argc < 1 || argc + 2 * !!(params->flags & (IB_CLEAR | IB_EDIT)) > 3) 190 usage(); 191 192 /* set missing defaults */ 193 if (params->machine == NULL) { 194 if (uname(&utsname) == -1) 195 err(1, "Determine uname"); 196 getmachine(params, utsname.machine, "uname()"); 197 } 198 199 /* Check that options are supported by this system */ 200 unsupported_flags = params->flags & ~params->machine->valid_flags; 201 unsupported_flags &= ~(IB_VERBOSE | IB_NOWRITE | IB_CLEAR | IB_EDIT 202 | IB_FORCE); 203 if (unsupported_flags != 0) { 204 int ndx; 205 for (ndx = 0; options[ndx].name != NULL; ndx++) { 206 if (unsupported_flags & options[ndx].flag) { 207 unsupported_flags &= ~options[ndx].flag; 208 warnx("`-o %s' is not supported for %s", 209 options[ndx].name, params->machine->name); 210 } 211 } 212 if (unsupported_flags & IB_STAGE1START) 213 warnx("`-b bno' is not supported for %s", 214 params->machine->name); 215 if (unsupported_flags & IB_STAGE2START) 216 warnx("`-B bno' is not supported for %s", 217 params->machine->name); 218 unsupported_flags &= ~(IB_STAGE1START | IB_STAGE2START); 219 if (unsupported_flags != 0) 220 warnx("Unknown unsupported flag %#x (coding error!)", 221 unsupported_flags); 222 exit(1); 223 } 224 /* and some illegal combinations */ 225 if (params->flags & IB_STAGE1START && params->flags & IB_APPEND) { 226 warnx("Can't use `-b bno' with `-o append'"); 227 exit(1); 228 } 229 if (params->flags & IB_CLEAR && 230 params->flags & (IB_STAGE1START | IB_STAGE2START | IB_APPEND)) { 231 warnx("Can't use `-b bno', `-B bno' or `-o append' with `-c'"); 232 exit(1); 233 } 234 235 if (argc >= 3) { 236 params->stage2 = argv[2]; 237 } 238 239 params->filesystem = argv[0]; 240 if (params->flags & IB_NOWRITE) { 241 op = "only"; 242 mode = O_RDONLY; 243 } else { 244 op = "write"; 245 mode = O_RDWR; 246 } 247 248 if (minixfs3_is_minix_partition(params)) { 249 /* Old setups has just 1 sector for bootblock, 250 * but bootxx_minixfs is ~8Kb, so we require new setups 251 * to have 32 sectors before the first subpartition. 252 * This prevents from overwriting FS on old setups. 253 */ 254 if (!minixfs3_has_bootblock_space(params)) { 255 err(1, "No space for bootxx, you should have 32 sectors" 256 " before the first subpartition on %s", 257 params->filesystem); 258 } 259 } 260 261 /* XXX should be specified via option */ 262 params->sectorsize = DFL_SECSIZE; 263 if ((params->fsfd = open(params->filesystem, mode, 0600)) == -1) 264 err(1, "Opening file system `%s' read-%s", 265 params->filesystem, op); 266 if (fstat(params->fsfd, ¶ms->fsstat) == -1) 267 err(1, "Examining file system `%s'", params->filesystem); 268 if (params->fstype != NULL) { 269 if (! params->fstype->match(params)) 270 errx(1, "File system `%s' is not of type %s", 271 params->filesystem, params->fstype->name); 272 } else { 273 if (params->stage2 != NULL) { 274 params->fstype = &fstypes[0]; 275 while (params->fstype->name != NULL && 276 !params->fstype->match(params)) 277 params->fstype++; 278 if (params->fstype->name == NULL) 279 errx(1, "File system `%s' is of an unknown type", 280 params->filesystem); 281 } 282 } 283 284 if (argc >= 2) { 285 if ((params->s1fd = open(argv[1], O_RDONLY, 0600)) == -1) 286 err(1, "Opening primary bootstrap `%s'", argv[1]); 287 if (fstat(params->s1fd, ¶ms->s1stat) == -1) 288 err(1, "Examining primary bootstrap `%s'", argv[1]); 289 if (!S_ISREG(params->s1stat.st_mode)) 290 errx(1, "`%s' must be a regular file", argv[1]); 291 params->stage1 = argv[1]; 292 } 293 assert(params->machine != NULL); 294 295 if (params->flags & IB_VERBOSE) { 296 printf("File system: %s\n", params->filesystem); 297 if (params->fstype) 298 printf("File system type: %s (blocksize %u, " 299 "needswap %d)\n", 300 params->fstype->name, params->fstype->blocksize, 301 params->fstype->needswap); 302 if (!(params->flags & IB_EDIT)) 303 printf("Primary bootstrap: %s\n", 304 (params->flags & IB_CLEAR) ? "(to be cleared)" 305 : params->stage1 ? params->stage1 : "(none)" ); 306 if (params->stage2 != NULL) 307 printf("Secondary bootstrap: %s\n", params->stage2); 308 } 309 310 if (params->flags & IB_EDIT) { 311 op = "Edit"; 312 rv = params->machine->editboot(params); 313 } else if (params->flags & IB_CLEAR) { 314 op = "Clear"; 315 rv = params->machine->clearboot(params); 316 } else { 317 if (argc < 2) 318 errx(EXIT_FAILURE, "Please specify the primary " 319 "bootstrap file"); 320 op = "Set"; 321 rv = params->machine->setboot(params); 322 } 323 if (rv == 0) 324 errx(1, "%s bootstrap operation failed", op); 325 326 if (S_ISREG(params->fsstat.st_mode)) { 327 if (fsync(params->fsfd) == -1) 328 err(1, "Synchronising file system `%s'", 329 params->filesystem); 330 } else { 331 /* Sync filesystems (to clean in-memory superblock?) */ 332 sync(); 333 } 334 if (close(params->fsfd) == -1) 335 err(1, "Closing file system `%s'", params->filesystem); 336 if (argc == 2) 337 if (close(params->s1fd) == -1) 338 err(1, "Closing primary bootstrap `%s'", 339 params->stage1); 340 341 exit(0); 342 /* NOTREACHED */ 343 } 344 345 static void 346 parseoptions(ib_params *params, const char *option) 347 { 348 char *cp; 349 const struct option *opt; 350 int len; 351 unsigned long val; 352 353 assert(params != NULL); 354 assert(option != NULL); 355 356 for (;; option += len) { 357 option += strspn(option, ", \t"); 358 if (*option == 0) 359 return; 360 len = strcspn(option, "=,"); 361 for (opt = options; opt->name != NULL; opt++) { 362 if (memcmp(option, opt->name, len) == 0 363 && opt->name[len] == 0) 364 break; 365 } 366 if (opt->name == NULL) { 367 len = strcspn(option, ","); 368 warnx("Unknown option `-o %.*s'", len, option); 369 break; 370 } 371 params->flags |= opt->flag; 372 if (opt->type == OPT_BOOL) { 373 if (option[len] != '=') 374 continue; 375 warnx("Option `%s' must not have a value", opt->name); 376 break; 377 } 378 if (option[len] != '=') { 379 warnx("Option `%s' must have a value", opt->name); 380 break; 381 } 382 option += len + 1; 383 len = strcspn(option, ","); 384 switch (opt->type) { 385 case OPT_STRING: 386 len = strlen(option); 387 /* FALLTHROUGH */ 388 case OPT_WORD: 389 cp = strdup(option); 390 if (cp == NULL) 391 err(1, "strdup"); 392 cp[len] = 0; 393 OPTION(params, char *, opt) = cp; 394 continue; 395 case OPT_INT: 396 val = strtoul(option, &cp, 0); 397 if (cp > option + len || (*cp != 0 && *cp != ',')) 398 break; 399 if (val > INT_MAX) 400 break; 401 OPTION(params, int, opt) = (int)val; 402 continue; 403 default: 404 errx(1, "Internal error: option `%s' has invalid type %d", 405 opt->name, opt->type); 406 } 407 warnx("Invalid option value `%s=%.*s'", opt->name, len, option); 408 break; 409 } 410 options_usage(); 411 exit(1); 412 } 413 414 static void 415 options_usage(void) 416 { 417 int ndx; 418 const char *pfx; 419 420 warnx("Valid options are:"); 421 pfx = "\t"; 422 for (ndx = 0; options[ndx].name != 0; ndx++) { 423 fprintf(stderr, "%s%s", pfx, options[ndx].name); 424 switch (options[ndx].type) { 425 case OPT_INT: 426 fprintf(stderr, "=number"); 427 break; 428 case OPT_WORD: 429 fprintf(stderr, "=word"); 430 break; 431 case OPT_STRING: 432 fprintf(stderr, "=string"); 433 break; 434 default: 435 break; 436 } 437 if ((ndx % 5) == 4) 438 pfx = ",\n\t"; 439 else 440 pfx = ", "; 441 } 442 fprintf(stderr, "\n"); 443 } 444 445 int 446 no_setboot(ib_params *params) 447 { 448 449 assert(params != NULL); 450 451 warnx("%s: bootstrap installation is not supported", 452 params->machine->name); 453 return (0); 454 } 455 456 int 457 no_clearboot(ib_params *params) 458 { 459 460 assert(params != NULL); 461 462 warnx("%s: bootstrap removal is not supported", 463 params->machine->name); 464 return (0); 465 } 466 467 int 468 no_editboot(ib_params *params) 469 { 470 471 assert(params != NULL); 472 473 warnx("%s: bootstrap editing is not supported", 474 params->machine->name); 475 return (0); 476 } 477 478 479 static void 480 getmachine(ib_params *param, const char *mach, const char *provider) 481 { 482 int i; 483 484 assert(param != NULL); 485 assert(mach != NULL); 486 assert(provider != NULL); 487 488 for (i = 0; machines[i] != NULL; i++) { 489 if (machines[i]->name == NULL) 490 continue; 491 if (strcmp(machines[i]->name, mach) == 0) { 492 param->machine = machines[i]; 493 return; 494 } 495 } 496 warnx("Invalid machine `%s' from %s", mach, provider); 497 machine_usage(); 498 exit(1); 499 } 500 501 static void 502 machine_usage(void) 503 { 504 const char *prefix; 505 int i; 506 int col, len; 507 const char *name; 508 int wincol=80; 509 #ifdef TIOCGWINSZ 510 struct winsize win; 511 512 if (ioctl(fileno(stderr), TIOCGWINSZ, &win) == 0 && win.ws_col > 0) 513 wincol = win.ws_col; 514 #endif 515 516 warnx("Supported machines are:"); 517 prefix="\t"; 518 col = 8 + 3; 519 for (i = 0; machines[i] != NULL; i++) { 520 name = machines[i]->name; 521 if (name == NULL) 522 continue; 523 len = strlen(name); 524 if (col + len > wincol) { 525 prefix=",\n\t"; 526 col = -2 + 8 + 3; 527 } 528 col += fprintf(stderr, "%s%s", prefix, name); 529 prefix=", "; 530 } 531 fputs("\n", stderr); 532 } 533 534 static void 535 getfstype(ib_params *param, const char *fstype, const char *provider) 536 { 537 int i; 538 539 assert(param != NULL); 540 assert(fstype != NULL); 541 assert(provider != NULL); 542 543 for (i = 0; fstypes[i].name != NULL; i++) { 544 if (strcmp(fstypes[i].name, fstype) == 0) { 545 param->fstype = &fstypes[i]; 546 return; 547 } 548 } 549 warnx("Invalid file system type `%s' from %s", fstype, provider); 550 fstype_usage(); 551 exit(1); 552 } 553 554 static void 555 fstype_usage(void) 556 { 557 #ifndef NO_STAGE2 558 const char *prefix; 559 int i; 560 561 warnx("Supported file system types are:"); 562 #define FSTYPES_PER_LINE 9 563 prefix="\t"; 564 for (i = 0; fstypes[i].name != NULL; i++) { 565 if (i && (i % FSTYPES_PER_LINE) == 0) 566 prefix=",\n\t"; 567 fprintf(stderr, "%s%s", prefix, fstypes[i].name); 568 prefix=", "; 569 } 570 fputs("\n", stderr); 571 #endif 572 } 573 574 static void 575 usage(void) 576 { 577 const char *prog; 578 579 prog = getprogname(); 580 fprintf(stderr, 581 "usage: %s [-fnv] [-B s2bno] [-b s1bno] [-m machine] [-o options]\n" 582 "\t\t [-t fstype] filesystem primary [secondary]\n" 583 "usage: %s -c [-fnv] [-m machine] [-o options] [-t fstype] filesystem\n" 584 "usage: %s -e [-fnv] [-m machine] [-o options] bootstrap\n", 585 prog, prog, prog); 586 machine_usage(); 587 fstype_usage(); 588 options_usage(); 589 exit(1); 590 } 591