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