1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright 2012 Milan Jurik. All rights reserved. 24 */ 25 26 /* 27 * Copyright 2015 Nexenta Systems, Inc. All rights reserved. 28 * Copyright 2016 Toomas Soome <tsoome@me.com> 29 */ 30 31 /* 32 * Loader menu management. 33 */ 34 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <wchar.h> 39 #include <errno.h> 40 #include <limits.h> 41 #include <alloca.h> 42 #include <unistd.h> 43 #include <sys/types.h> 44 #include <sys/stat.h> 45 #include <sys/queue.h> 46 #include <libbe.h> 47 #include <ficl.h> 48 #include <ficlplatform/emu.h> 49 #include <ofmt.h> 50 51 #include "bootadm.h" 52 53 extern int bam_rootlen; 54 extern int bam_alt_root; 55 extern char *rootbuf; 56 extern char *bam_root; 57 58 #define BOOT_DIR "/boot" 59 #define CONF_DIR BOOT_DIR "/conf.d" 60 #define MENU BOOT_DIR "/menu.lst" 61 #define TRANSIENT BOOT_DIR "/transient.conf" 62 #define XEN_CONFIG CONF_DIR "/xen" 63 64 typedef struct menu_entry { 65 int me_idx; 66 boolean_t me_active; 67 char *me_title; 68 char *me_type; 69 char *me_bootfs; 70 STAILQ_ENTRY(menu_entry) me_next; 71 } menu_entry_t; 72 STAILQ_HEAD(menu_lst, menu_entry); 73 74 static error_t set_option(struct menu_lst *, char *, char *); 75 static error_t list_entry(struct menu_lst *, char *, char *); 76 static error_t update_entry(struct menu_lst *, char *, char *); 77 static error_t update_temp(struct menu_lst *, char *, char *); 78 static error_t list_setting(struct menu_lst *menu, char *, char *); 79 static error_t disable_hyper(struct menu_lst *, char *, char *); 80 static error_t enable_hyper(struct menu_lst *, char *, char *); 81 82 /* Menu related sub commands */ 83 static subcmd_defn_t menu_subcmds[] = { 84 "set_option", OPT_ABSENT, set_option, 0, /* PUB */ 85 "list_entry", OPT_OPTIONAL, list_entry, 1, /* PUB */ 86 "update_entry", OPT_REQ, update_entry, 0, /* menu */ 87 "update_temp", OPT_OPTIONAL, update_temp, 0, /* reboot */ 88 "list_setting", OPT_OPTIONAL, list_setting, 1, /* menu */ 89 "disable_hypervisor", OPT_ABSENT, disable_hyper, 0, /* menu */ 90 "enable_hypervisor", OPT_ABSENT, enable_hyper, 0, /* menu */ 91 NULL, 0, NULL, 0 /* must be last */ 92 }; 93 94 #define NUM_COLS (5) 95 96 static boolean_t 97 print_menu_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize) 98 { 99 menu_entry_t *entry = ofarg->ofmt_cbarg; 100 101 switch (ofarg->ofmt_id) { 102 case 0: 103 (void) snprintf(buf, bufsize, "%d", entry->me_idx); 104 break; 105 case 1: 106 (void) snprintf(buf, bufsize, "%s", entry->me_title); 107 break; 108 case 2: 109 (void) snprintf(buf, bufsize, "%s", entry->me_bootfs); 110 break; 111 case 3: 112 (void) snprintf(buf, bufsize, "%s", entry->me_type); 113 break; 114 case 4: 115 if (entry->me_active == B_TRUE) 116 (void) snprintf(buf, bufsize, " *"); 117 else 118 (void) snprintf(buf, bufsize, " -"); 119 break; 120 default: 121 return (B_FALSE); 122 } 123 return (B_TRUE); 124 } 125 126 static void 127 init_hdr_cols(ofmt_field_t *hdr) 128 { 129 uint_t i; 130 131 for (i = 0; i < NUM_COLS; i++) { 132 char *name = NULL; 133 134 switch (i) { 135 case 0: 136 name = _("INDEX"); 137 break; 138 case 1: 139 name = _("NAME"); 140 break; 141 case 2: 142 name = _("DEVICE"); 143 break; 144 case 3: 145 name = _("TYPE"); 146 break; 147 case 4: 148 name = _("DEFAULT"); 149 break; 150 } 151 152 hdr[i].of_name = name; 153 hdr[i].of_id = i; 154 hdr[i].of_cb = print_menu_cb; 155 156 if (name != NULL) { 157 wchar_t wname[128]; 158 size_t sz = mbstowcs(wname, name, sizeof (wname) / 159 sizeof (wchar_t)); 160 if (sz > 0) { 161 int wcsw = wcswidth(wname, sz); 162 if (wcsw > 0) 163 hdr[i].of_width = wcsw; 164 else 165 hdr[i].of_width = sz; 166 } else { 167 hdr[i].of_width = strlen(name); 168 } 169 } 170 } 171 } 172 173 static void 174 menu_update_widths(ofmt_field_t *hdr, struct menu_lst *menu) 175 { 176 size_t len[NUM_COLS]; 177 menu_entry_t *entry; 178 int i; 179 180 for (i = 0; i < NUM_COLS; i++) 181 len[i] = hdr[i].of_width + 1; 182 183 STAILQ_FOREACH(entry, menu, me_next) { 184 size_t entry_len; 185 186 entry_len = strlen(entry->me_title) + 1; 187 if (entry_len > len[1]) 188 len[1] = entry_len; 189 190 entry_len = strlen(entry->me_bootfs) + 1; 191 if (entry_len > len[2]) 192 len[2] = entry_len; 193 194 entry_len = strlen(entry->me_type) + 1; 195 if (entry_len > len[3]) 196 len[3] = entry_len; 197 } 198 199 for (i = 0; i < NUM_COLS; i++) 200 hdr[i].of_width = len[i]; 201 } 202 203 static ofmt_field_t * 204 init_menu_template(struct menu_lst *menu) 205 { 206 ofmt_field_t *temp; 207 208 if ((temp = calloc(NUM_COLS + 1, sizeof (ofmt_field_t))) == NULL) 209 return (temp); 210 211 init_hdr_cols(temp); 212 menu_update_widths(temp, menu); 213 return (temp); 214 } 215 216 static void 217 print_nodes(boolean_t parsable, struct menu_lst *menu) 218 { 219 ofmt_status_t oferr; 220 ofmt_handle_t ofmt; 221 uint_t ofmtflags = 0; 222 ofmt_field_t *menu_template; 223 menu_entry_t *entry; 224 225 if (parsable == B_TRUE) 226 ofmtflags = OFMT_PARSABLE; 227 228 menu_template = init_menu_template(menu); 229 oferr = ofmt_open(NULL, menu_template, ofmtflags, 0, &ofmt); 230 231 if (oferr != OFMT_SUCCESS) { 232 char buf[OFMT_BUFSIZE]; 233 234 (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf)); 235 (void) printf("bootadm: %s\n", buf); 236 free(menu_template); 237 return; 238 } 239 240 STAILQ_FOREACH(entry, menu, me_next) 241 ofmt_print(ofmt, entry); 242 243 ofmt_close(ofmt); 244 free(menu_template); 245 } 246 247 /* 248 * Get the be_active_on_boot for bootfs. 249 */ 250 static boolean_t 251 menu_active_on_boot(be_node_list_t *be_nodes, const char *bootfs) 252 { 253 be_node_list_t *be_node; 254 boolean_t rv = B_FALSE; 255 256 for (be_node = be_nodes; be_node != NULL; 257 be_node = be_node->be_next_node) { 258 if (strcmp(be_node->be_root_ds, bootfs) == 0) { 259 rv = be_node->be_active_on_boot; 260 break; 261 } 262 } 263 264 return (rv); 265 } 266 267 error_t 268 menu_read(struct menu_lst *menu, char *menu_path) 269 { 270 FILE *fp; 271 be_node_list_t *be_nodes; 272 menu_entry_t *mp; 273 char buf[PATH_MAX]; 274 char *title; 275 char *bootfs; 276 char *type; 277 char *key, *value; 278 int i = 0; 279 int ret = BAM_SUCCESS; 280 281 fp = fopen(menu_path, "r"); 282 if (fp == NULL) 283 return (BAM_ERROR); 284 285 if (be_list(NULL, &be_nodes) != BE_SUCCESS) 286 be_nodes = NULL; 287 288 /* 289 * menu.lst entry is on two lines, one for title, one for bootfs 290 * so we process both lines in succession. 291 */ 292 title = NULL; 293 type = NULL; 294 bootfs = NULL; 295 do { 296 if (fgets(buf, PATH_MAX, fp) == NULL) { 297 if (!feof(fp)) 298 ret = BAM_ERROR; 299 goto done; 300 } 301 key = strtok(buf, " \n"); 302 if (strcmp(key, "title") != 0) { 303 ret = BAM_ERROR; 304 goto done; 305 } 306 value = strtok(NULL, " \n"); 307 if ((title = strdup(value)) == NULL) { 308 ret = BAM_ERROR; 309 goto done; 310 } 311 312 if (fgets(buf, PATH_MAX, fp) == NULL) { 313 ret = BAM_ERROR; 314 goto done; 315 } 316 317 key = strtok(buf, " \n"); 318 if ((type = strdup(key)) == NULL) { 319 ret = BAM_ERROR; 320 goto done; 321 } 322 value = strtok(NULL, " \n"); 323 if ((bootfs = strdup(value)) == NULL) { 324 ret = BAM_ERROR; 325 goto done; 326 } 327 if ((mp = malloc(sizeof (menu_entry_t))) == NULL) { 328 ret = BAM_ERROR; 329 goto done; 330 } 331 mp->me_idx = i++; 332 mp->me_title = title; 333 mp->me_type = type; 334 mp->me_bootfs = bootfs; 335 mp->me_active = menu_active_on_boot(be_nodes, bootfs); 336 STAILQ_INSERT_TAIL(menu, mp, me_next); 337 338 title = NULL; 339 type = NULL; 340 bootfs = NULL; 341 } while (feof(fp) == 0); 342 343 done: 344 free(title); 345 free(type); 346 free(bootfs); 347 (void) fclose(fp); 348 be_free_list(be_nodes); 349 return (ret); 350 } 351 352 void 353 menu_free(struct menu_lst *menu) 354 { 355 menu_entry_t *entry; 356 STAILQ_FOREACH(entry, menu, me_next) { 357 STAILQ_REMOVE_HEAD(menu, me_next); 358 free(entry->me_title); 359 free(entry->me_type); 360 free(entry->me_bootfs); 361 free(entry); 362 } 363 } 364 365 error_t 366 bam_loader_menu(char *subcmd, char *opt, int largc, char *largv[]) 367 { 368 error_t ret; 369 char menu_path[PATH_MAX]; 370 char clean_menu_root[PATH_MAX]; 371 char menu_root[PATH_MAX]; 372 struct stat sb; 373 error_t (*f)(struct menu_lst *, char *, char *); 374 char *special; 375 char *pool = NULL; 376 zfs_mnted_t zmnted; 377 char *zmntpt; 378 char *osdev; 379 char *osroot; 380 const char *fcn = "bam_loader_menu()"; 381 struct menu_lst menu = {0}; 382 383 STAILQ_INIT(&menu); 384 385 /* 386 * Check arguments 387 */ 388 ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f); 389 if (ret == BAM_ERROR) { 390 return (BAM_ERROR); 391 } 392 393 assert(bam_root); 394 395 (void) strlcpy(menu_root, bam_root, sizeof (menu_root)); 396 osdev = osroot = NULL; 397 398 if (strcmp(subcmd, "update_entry") == 0) { 399 assert(opt); 400 401 osdev = strtok(opt, ","); 402 assert(osdev); 403 osroot = strtok(NULL, ","); 404 if (osroot) { 405 /* fixup bam_root so that it points at osroot */ 406 if (realpath(osroot, rootbuf) == NULL) { 407 bam_error(_("cannot resolve path %s: %s\n"), 408 osroot, strerror(errno)); 409 return (BAM_ERROR); 410 } 411 bam_alt_root = 1; 412 bam_root = rootbuf; 413 bam_rootlen = strlen(rootbuf); 414 } 415 } 416 417 if (stat(menu_root, &sb) == -1) { 418 bam_error(_("cannot find menu\n")); 419 return (BAM_ERROR); 420 } 421 422 if (!is_zfs(menu_root)) { 423 bam_error(_("only ZFS root is supported\n")); 424 return (BAM_ERROR); 425 } 426 427 assert(strcmp(menu_root, bam_root) == 0); 428 special = get_special(menu_root); 429 INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL); 430 if (special == NULL) { 431 bam_error(_("cant find special file for mount-point %s\n"), 432 menu_root); 433 return (BAM_ERROR); 434 } 435 pool = strtok(special, "/"); 436 INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL); 437 if (pool == NULL) { 438 free(special); 439 bam_error(_("cant find pool for mount-point %s\n"), menu_root); 440 return (BAM_ERROR); 441 } 442 BAM_DPRINTF(("%s: derived pool=%s from special\n", fcn, pool)); 443 444 zmntpt = mount_top_dataset(pool, &zmnted); 445 INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL); 446 if (zmntpt == NULL) { 447 bam_error(_("cannot mount pool dataset for pool: %s\n"), pool); 448 free(special); 449 return (BAM_ERROR); 450 } 451 BAM_DPRINTF(("%s: top dataset mountpoint=%s\n", fcn, zmntpt)); 452 453 (void) strlcpy(menu_root, zmntpt, sizeof (menu_root)); 454 BAM_DPRINTF(("%s: zfs menu_root=%s\n", fcn, menu_root)); 455 456 elide_trailing_slash(menu_root, clean_menu_root, 457 sizeof (clean_menu_root)); 458 459 BAM_DPRINTF(("%s: cleaned menu root is <%s>\n", fcn, clean_menu_root)); 460 461 (void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path)); 462 (void) strlcat(menu_path, MENU, sizeof (menu_path)); 463 464 BAM_DPRINTF(("%s: menu path is: %s\n", fcn, menu_path)); 465 466 /* 467 * update_entry is special case, its used by installer 468 * and needs to create menu.lst file for loader 469 */ 470 if (menu_read(&menu, menu_path) == BAM_ERROR && 471 strcmp(subcmd, "update_entry") != 0) { 472 bam_error(_("cannot find menu file: %s\n"), menu_path); 473 if (special != NULL) 474 free(special); 475 return (BAM_ERROR); 476 } 477 478 /* 479 * If listing the menu, display the menu location 480 */ 481 if (strcmp(subcmd, "list_entry") == 0) 482 bam_print(_("the location for the active menu is: %s\n"), 483 menu_path); 484 485 /* 486 * We already checked the following case in 487 * check_subcmd_and_suboptions() above. Complete the 488 * final step now. 489 */ 490 if (strcmp(subcmd, "set_option") == 0) { 491 assert(largc == 1 && largv[0] && largv[1] == NULL); 492 opt = largv[0]; 493 } else if ((strcmp(subcmd, "enable_hypervisor") != 0) && 494 (strcmp(subcmd, "list_setting") != 0)) { 495 assert(largc == 0 && largv == NULL); 496 } 497 498 /* 499 * Once the sub-cmd handler has run 500 * only the line field is guaranteed to have valid values 501 */ 502 if (strcmp(subcmd, "update_entry") == 0) { 503 ret = f(&menu, menu_root, osdev); 504 } else if (strcmp(subcmd, "upgrade") == 0) { 505 ret = f(&menu, bam_root, menu_root); 506 } else if (strcmp(subcmd, "list_entry") == 0) { 507 ret = f(&menu, menu_path, opt); 508 } else if (strcmp(subcmd, "list_setting") == 0) { 509 ret = f(&menu, ((largc > 0) ? largv[0] : ""), 510 ((largc > 1) ? largv[1] : "")); 511 } else if (strcmp(subcmd, "disable_hypervisor") == 0) { 512 if (is_sparc()) { 513 bam_error(_("%s operation unsupported on SPARC " 514 "machines\n"), subcmd); 515 ret = BAM_ERROR; 516 } else { 517 ret = f(&menu, bam_root, NULL); 518 } 519 } else if (strcmp(subcmd, "enable_hypervisor") == 0) { 520 if (is_sparc()) { 521 bam_error(_("%s operation unsupported on SPARC " 522 "machines\n"), subcmd); 523 ret = BAM_ERROR; 524 } else { 525 char *extra_args = NULL; 526 527 /* 528 * Compress all arguments passed in the largv[] array 529 * into one string that can then be appended to the 530 * end of the kernel$ string the routine to enable the 531 * hypervisor will build. 532 * 533 * This allows the caller to supply arbitrary unparsed 534 * arguments, such as dom0 memory settings or APIC 535 * options. 536 * 537 * This concatenation will be done without ANY syntax 538 * checking whatsoever, so it's the responsibility of 539 * the caller to make sure the arguments are valid and 540 * do not duplicate arguments the conversion routines 541 * may create. 542 */ 543 if (largc > 0) { 544 int extra_len, i; 545 546 for (extra_len = 0, i = 0; i < largc; i++) 547 extra_len += strlen(largv[i]); 548 549 /* 550 * Allocate space for argument strings, 551 * intervening spaces and terminating NULL. 552 */ 553 extra_args = alloca(extra_len + largc); 554 555 (void) strcpy(extra_args, largv[0]); 556 557 for (i = 1; i < largc; i++) { 558 (void) strcat(extra_args, " "); 559 (void) strcat(extra_args, largv[i]); 560 } 561 } 562 563 ret = f(&menu, bam_root, extra_args); 564 } 565 } else 566 ret = f(&menu, NULL, opt); 567 568 if (ret == BAM_WRITE) { 569 BAM_DPRINTF(("%s: writing menu to clean-menu-root: <%s>\n", 570 fcn, clean_menu_root)); 571 /* ret = menu_write(clean_menu_root, menu); */ 572 } 573 574 INJECT_ERROR1("POOL_SET", pool = "/pooldata"); 575 assert((is_zfs(menu_root)) ^ (pool == NULL)); 576 if (pool) { 577 (void) umount_top_dataset(pool, zmnted, zmntpt); 578 free(special); 579 } 580 581 menu_free(&menu); 582 return (ret); 583 } 584 585 /* 586 * To suppress output from ficl. We do not want to see messages 587 * from interpreting loader config. 588 */ 589 590 /*ARGSUSED*/ 591 static void 592 ficlTextOutSilent(ficlCallback *cb, char *text) 593 { 594 } 595 596 /*ARGSUSED*/ 597 static error_t 598 set_option(struct menu_lst *menu, char *dummy, char *opt) 599 { 600 char path[PATH_MAX]; 601 char *val; 602 char *rest; 603 int optval; 604 menu_entry_t *entry; 605 nvlist_t *be_attrs; 606 FILE *fp; 607 int rv, ret = BAM_SUCCESS; 608 609 assert(menu); 610 assert(opt); 611 assert(dummy == NULL); 612 613 val = strchr(opt, '='); 614 if (val != NULL) { 615 *val++ = '\0'; 616 } 617 618 if (strcmp(opt, "default") == 0) { 619 errno = 0; 620 optval = strtol(val, &rest, 10); 621 if (errno != 0 || *rest != '\0') { 622 bam_error(_("invalid boot entry number: %s\n"), val); 623 return (BAM_ERROR); 624 } 625 STAILQ_FOREACH(entry, menu, me_next) { 626 if (entry->me_idx == optval) 627 break; 628 } 629 if (entry == NULL) { 630 bam_error(_("invalid boot entry number: %s\n"), val); 631 return (BAM_ERROR); 632 } 633 if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) { 634 bam_error(_("out of memory\n")); 635 return (BAM_ERROR); 636 } 637 if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, 638 entry->me_title) != 0) { 639 bam_error(_("out of memory\n")); 640 nvlist_free(be_attrs); 641 return (BAM_ERROR); 642 } 643 ret = be_activate(be_attrs); 644 nvlist_free(be_attrs); 645 if (ret != 0) 646 ret = BAM_ERROR; 647 return (ret); 648 } else if (strcmp(opt, "timeout") == 0) { 649 errno = 0; 650 optval = strtol(val, &rest, 10); 651 if (errno != 0 || *rest != '\0') { 652 bam_error(_("invalid timeout: %s\n"), val); 653 return (BAM_ERROR); 654 } 655 656 (void) snprintf(path, PATH_MAX, "%s" CONF_DIR "/timeout", 657 bam_root); 658 659 fp = fopen(path, "w"); 660 if (fp == NULL) { 661 bam_error(_("failed to open file: %s: %s\n"), 662 path, strerror(errno)); 663 return (BAM_ERROR); 664 } 665 /* 666 * timeout=-1 is to disable auto boot in illumos, but 667 * loader needs "NO" to disable auto boot. 668 */ 669 if (optval == -1) 670 rv = fprintf(fp, "autoboot_delay=\"NO\"\n"); 671 else 672 rv = fprintf(fp, "autoboot_delay=\"%d\"\n", optval); 673 674 if (rv < 0) { 675 bam_error(_("write to file failed: %s: %s\n"), 676 path, strerror(errno)); 677 (void) fclose(fp); 678 ret = BAM_ERROR; 679 } else 680 rv = fclose(fp); 681 682 if (rv < 0) { 683 bam_error(_("failed to close file: %s: %s\n"), 684 path, strerror(errno)); 685 ret = BAM_ERROR; 686 } 687 if (ret == BAM_ERROR) 688 (void) unlink(path); 689 690 return (BAM_SUCCESS); 691 } 692 693 bam_error(_("invalid option: %s\n"), opt); 694 return (BAM_ERROR); 695 } 696 697 static int 698 bam_mount_be(menu_entry_t *entry, char **dir) 699 { 700 nvlist_t *be_attrs = NULL; 701 const char *tmpdir = getenv("TMPDIR"); 702 const char *tmpname = "bam.XXXXXX"; 703 be_node_list_t *be_node, *be_nodes = NULL; 704 int ret; 705 706 *dir = NULL; 707 if (tmpdir == NULL) 708 tmpdir = "/tmp"; 709 710 ret = asprintf(dir, "%s/%s", tmpdir, tmpname); 711 if (ret < 0) { 712 return (BE_ERR_NOMEM); 713 } 714 *dir = mkdtemp(*dir); 715 716 if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) { 717 ret = BE_ERR_NOMEM; 718 goto out; 719 } 720 721 ret = be_list(NULL, &be_nodes); 722 if (ret != BE_SUCCESS) { 723 goto out; 724 } 725 726 for (be_node = be_nodes; be_node; 727 be_node = be_node->be_next_node) 728 if (strcmp(be_node->be_root_ds, entry->me_bootfs) == 0) 729 break; 730 731 if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, 732 be_node->be_node_name) != 0) { 733 ret = BE_ERR_NOMEM; 734 goto out; 735 } 736 737 if (nvlist_add_string(be_attrs, BE_ATTR_MOUNTPOINT, *dir) != 0) { 738 ret = BE_ERR_NOMEM; 739 goto out; 740 } 741 742 ret = be_mount(be_attrs); 743 if (ret == BE_ERR_MOUNTED) { 744 /* 745 * if BE is mounted, dir does not point to correct directory 746 */ 747 (void) rmdir(*dir); 748 free(*dir); 749 *dir = NULL; 750 } 751 out: 752 if (be_nodes != NULL) 753 be_free_list(be_nodes); 754 nvlist_free(be_attrs); 755 return (ret); 756 } 757 758 static int 759 bam_umount_be(char *dir) 760 { 761 nvlist_t *be_attrs; 762 int ret; 763 764 if (dir == NULL) /* nothing to do */ 765 return (BE_SUCCESS); 766 767 if (nvlist_alloc(&be_attrs, NV_UNIQUE_NAME, 0) != 0) 768 return (BE_ERR_NOMEM); 769 770 if (nvlist_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, dir) != 0) { 771 ret = BE_ERR_NOMEM; 772 goto out; 773 } 774 775 ret = be_unmount(be_attrs); 776 out: 777 nvlist_free(be_attrs); 778 return (ret); 779 } 780 781 /* 782 * display details of menu entry or single property 783 */ 784 static error_t 785 list_menu_entry(menu_entry_t *entry, char *setting) 786 { 787 int ret = BAM_SUCCESS; 788 char *ptr, *dir; 789 char buf[MAX_INPUT]; 790 ficlVm *vm; 791 int mounted; 792 793 if (strcmp(entry->me_type, "bootfs") != 0 || 794 strchr(entry->me_bootfs, ':') != NULL) { 795 (void) printf("\nTitle: %s\n", entry->me_title); 796 (void) printf("Type: %s\n", entry->me_type); 797 (void) printf("Device: %s\n", entry->me_bootfs); 798 return (ret); 799 } 800 801 mounted = bam_mount_be(entry, &dir); 802 if (mounted != BE_SUCCESS && mounted != BE_ERR_MOUNTED) { 803 if (dir != NULL) { 804 (void) rmdir(dir); 805 free(dir); 806 } 807 bam_error(_("%s is not mounted\n"), entry->me_title); 808 return (BAM_ERROR); 809 } 810 811 vm = bf_init("", ficlTextOutSilent); 812 if (vm == NULL) { 813 bam_error(_("error setting up forth interpreter\n")); 814 ret = BAM_ERROR; 815 goto done; 816 } 817 818 /* should only get FICL_VM_STATUS_OUT_OF_TEXT */ 819 (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", 820 entry->me_bootfs); 821 ret = ficlVmEvaluate(vm, buf); 822 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 823 bam_error(_("error interpreting boot config\n")); 824 ret = BAM_ERROR; 825 goto done; 826 } 827 (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th"); 828 ret = ficlVmEvaluate(vm, buf); 829 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 830 bam_error(_("error interpreting boot config\n")); 831 ret = BAM_ERROR; 832 goto done; 833 } 834 (void) snprintf(buf, MAX_INPUT, "start"); 835 ret = ficlVmEvaluate(vm, buf); 836 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 837 bam_error(_("error interpreting boot config\n")); 838 ret = BAM_ERROR; 839 goto done; 840 } 841 (void) snprintf(buf, MAX_INPUT, "boot"); 842 ret = ficlVmEvaluate(vm, buf); 843 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 844 bam_error(_("error interpreting boot config\n")); 845 ret = BAM_ERROR; 846 goto done; 847 } 848 849 ret = BAM_SUCCESS; 850 if (*setting == '\0') 851 (void) printf("\nTitle: %s\n", entry->me_title); 852 else if (strcasecmp(setting, "title") == 0) { 853 (void) printf("%s\n", entry->me_title); 854 goto done; 855 } 856 857 ptr = getenv("autoboot_delay"); 858 if (ptr != NULL) { 859 char *timeout = "-1"; 860 861 if (strcasecmp(ptr, "NO") != 0) 862 timeout = ptr; 863 864 if (*setting == '\0') 865 (void) printf("Timeout: %s\n", timeout); 866 else if (strcasecmp(setting, "timeout") == 0) { 867 (void) printf("%s\n", timeout); 868 goto done; 869 } 870 871 } 872 ptr = getenv("console"); 873 if (ptr != NULL) { 874 if (*setting == '\0') 875 (void) printf("Console: %s\n", ptr); 876 else if (strcasecmp(setting, "console") == 0) { 877 (void) printf("%s\n", ptr); 878 goto done; 879 } 880 } 881 882 if (*setting == '\0') 883 (void) printf("Bootfs: %s\n", entry->me_bootfs); 884 else if (strcasecmp(setting, "bootfs") == 0) { 885 (void) printf("%s\n", entry->me_bootfs); 886 goto done; 887 } 888 889 ptr = getenv("xen_kernel"); 890 if (ptr != NULL) { 891 if (*setting == '\0') { 892 (void) printf("Xen kernel: %s\n", ptr); 893 } else if (strcasecmp(setting, "xen_kernel") == 0) { 894 (void) printf("%s\n", ptr); 895 goto done; 896 } 897 898 if (*setting == '\0') { 899 (void) printf("Xen args: \"%s\"\n", 900 getenv("xen_cmdline")); 901 } else if (strcasecmp(setting, "xen_cmdline") == 0) { 902 (void) printf("%s\n", getenv("xen_cmdline")); 903 goto done; 904 } 905 906 if (*setting == '\0') { 907 (void) printf("Kernel: %s\n", 908 getenv("bootfile")); 909 } if (strcasecmp(setting, "kernel") == 0) { 910 (void) printf("%s\n", getenv("bootfile")); 911 goto done; 912 } 913 } else { 914 ptr = getenv("kernelname"); 915 if (ptr != NULL) { 916 if (*setting == '\0') { 917 (void) printf("Kernel: %s\n", ptr); 918 } else if (strcasecmp(setting, "kernel") == 0) { 919 (void) printf("%s\n", ptr); 920 goto done; 921 } 922 } 923 } 924 925 ptr = getenv("boot-args"); 926 if (ptr != NULL) { 927 if (*setting == '\0') { 928 (void) printf("Boot-args: \"%s\"\n", ptr); 929 } else if (strcasecmp(setting, "boot-args") == 0) { 930 (void) printf("%s\n", ptr); 931 goto done; 932 } 933 } 934 935 if (*setting == '\0' || strcasecmp(setting, "modules") == 0) { 936 (void) printf("\nModules:\n"); 937 ficlVmSetTextOut(vm, ficlCallbackDefaultTextOut); 938 (void) snprintf(buf, MAX_INPUT, "show-module-options"); 939 ret = ficlVmEvaluate(vm, buf); 940 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 941 bam_error(_("error interpreting boot config\n")); 942 ret = BAM_ERROR; 943 goto done; 944 } 945 ret = BAM_SUCCESS; 946 goto done; 947 } 948 949 /* if we got here with setting string, its unknown property */ 950 if (*setting != '\0') { 951 bam_error(_("unknown property: %s\n"), setting); 952 ret = BAM_ERROR; 953 } else 954 ret = BAM_SUCCESS; 955 done: 956 bf_fini(); 957 if (mounted != BE_ERR_MOUNTED) { 958 (void) bam_umount_be(dir); 959 } 960 961 if (dir != NULL) { 962 (void) rmdir(dir); 963 free(dir); 964 } 965 966 return (ret); 967 } 968 969 /*ARGSUSED*/ 970 static error_t 971 list_entry(struct menu_lst *menu, char *menu_root, char *opt) 972 { 973 error_t ret = BAM_SUCCESS; 974 menu_entry_t *entry; 975 char *ptr, *title = NULL; 976 int i, e = -1; 977 978 if (opt == NULL) { 979 print_nodes(B_FALSE, menu); 980 return (ret); 981 } 982 983 if ((ptr = strchr(opt, '=')) == NULL) { 984 bam_error(_("invalid option: %s\n"), opt); 985 return (BAM_ERROR); 986 } 987 988 i = ptr - opt; 989 if (strncmp(opt, "entry", i) == 0) { 990 e = atoi(ptr+1); 991 } else if (strncmp(opt, "title", i) == 0) { 992 title = ptr+1; 993 } else { 994 bam_error(_("invalid option: %s\n"), opt); 995 return (BAM_ERROR); 996 } 997 998 STAILQ_FOREACH(entry, menu, me_next) { 999 if (title != NULL) { 1000 if (strcmp(title, entry->me_title) == 0) 1001 break; 1002 } else if (entry->me_idx == e) 1003 break; 1004 } 1005 1006 if (entry == NULL) { 1007 bam_error(_("no matching entry found\n")); 1008 return (BAM_ERROR); 1009 } 1010 1011 return (list_menu_entry(entry, "")); 1012 } 1013 1014 /* 1015 * For now this is just stub entry to support grub interface, the 1016 * known consumer is installer ict.py code, calling as: 1017 * bootadm update-menu -R /a -Z -o rdisk 1018 * Later this can be converted to do something useful. 1019 */ 1020 /*ARGSUSED*/ 1021 static error_t 1022 update_entry(struct menu_lst *menu, char *menu_root, char *osdev) 1023 { 1024 char path[PATH_MAX]; 1025 char *pool = menu_root + 1; 1026 be_node_list_t *be_nodes, *be_node; 1027 int rv; 1028 FILE *fp; 1029 1030 (void) snprintf(path, PATH_MAX, "%s%s", menu_root, MENU); 1031 rv = be_list(NULL, &be_nodes); 1032 1033 if (rv != BE_SUCCESS) 1034 return (BAM_ERROR); 1035 1036 fp = fopen(path, "w"); 1037 if (fp == NULL) { 1038 be_free_list(be_nodes); 1039 return (BAM_ERROR); 1040 } 1041 1042 for (be_node = be_nodes; be_node; be_node = be_node->be_next_node) { 1043 if (strcmp(be_node->be_rpool, pool) == 0) { 1044 (void) fprintf(fp, "title %s\n", be_node->be_node_name); 1045 (void) fprintf(fp, "bootfs %s\n", be_node->be_root_ds); 1046 } 1047 } 1048 1049 be_free_list(be_nodes); 1050 (void) fclose(fp); 1051 return (BAM_SUCCESS); 1052 } 1053 1054 /*ARGSUSED*/ 1055 static error_t 1056 update_temp(struct menu_lst *menu, char *dummy, char *opt) 1057 { 1058 error_t ret = BAM_ERROR; 1059 char path[PATH_MAX]; 1060 char buf[MAX_INPUT]; 1061 struct mnttab mpref = { 0 }; 1062 struct mnttab mp = { 0 }; 1063 ficlVm *vm; 1064 char *env, *o; 1065 FILE *fp; 1066 1067 (void) snprintf(path, PATH_MAX, "%s" TRANSIENT, bam_root); 1068 /* 1069 * if opt == NULL, remove transient config 1070 */ 1071 if (opt == NULL) { 1072 (void) unlink(path); 1073 return (BAM_SUCCESS); 1074 } 1075 1076 fp = fopen(MNTTAB, "r"); 1077 if (fp == NULL) 1078 return (BAM_ERROR); 1079 1080 mpref.mnt_mountp = "/"; 1081 if (getmntany(fp, &mp, &mpref) != 0) { 1082 (void) fclose(fp); 1083 return (BAM_ERROR); 1084 } 1085 (void) fclose(fp); 1086 1087 vm = bf_init("", ficlTextOutSilent); 1088 if (vm == NULL) { 1089 bam_error(_("Error setting up forth interpreter\n")); 1090 return (ret); 1091 } 1092 1093 /* 1094 * need to check current boot config, so fire up the ficl 1095 * if its xen setup, we add option to boot-args list, not replacing it. 1096 */ 1097 (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", mp.mnt_special); 1098 ret = ficlVmEvaluate(vm, buf); 1099 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 1100 bam_error(_("Error interpreting boot config\n")); 1101 bf_fini(); 1102 return (BAM_ERROR); 1103 } 1104 (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th"); 1105 ret = ficlVmEvaluate(vm, buf); 1106 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 1107 bam_error(_("Error interpreting boot config\n")); 1108 bf_fini(); 1109 return (BAM_ERROR); 1110 } 1111 (void) snprintf(buf, MAX_INPUT, "start"); 1112 ret = ficlVmEvaluate(vm, buf); 1113 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 1114 bam_error(_("Error interpreting boot config\n")); 1115 bf_fini(); 1116 return (BAM_ERROR); 1117 } 1118 (void) snprintf(buf, MAX_INPUT, "boot"); 1119 ret = ficlVmEvaluate(vm, buf); 1120 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 1121 bam_error(_("Error interpreting boot config\n")); 1122 bf_fini(); 1123 return (BAM_ERROR); 1124 } 1125 bf_fini(); 1126 1127 if (opt[0] == '-') { 1128 env = getenv("xen_kernel"); 1129 fp = fopen(path, "w"); 1130 if (fp == NULL) 1131 return (BAM_ERROR); 1132 1133 if (env != NULL) { 1134 env = getenv("boot-args"); 1135 (void) fprintf(fp, "boot-args=\"%s %s\"\n", env, opt); 1136 } else 1137 (void) fprintf(fp, "boot-args=\"%s\"\n", opt); 1138 (void) fclose(fp); 1139 return (BAM_SUCCESS); 1140 } 1141 1142 /* 1143 * it should be the case with "kernel args" 1144 * so, we split the opt at first space 1145 * and store bootfile= and boot-args= 1146 */ 1147 env = getenv("xen_kernel"); 1148 1149 o = strchr(opt, ' '); 1150 if (o == NULL) { 1151 fp = fopen(path, "w"); 1152 if (fp == NULL) 1153 return (BAM_ERROR); 1154 (void) fprintf(fp, "bootfile=\"%s;unix\"\n", opt); 1155 (void) fclose(fp); 1156 return (BAM_SUCCESS); 1157 } 1158 *o++ = '\0'; 1159 fp = fopen(path, "w"); 1160 if (fp == NULL) 1161 return (BAM_ERROR); 1162 (void) fprintf(fp, "bootfile=\"%s;unix\"\n", opt); 1163 1164 if (env != NULL) { 1165 env = getenv("boot-args"); 1166 (void) fprintf(fp, "boot-args=\"%s %s\"\n", env, opt); 1167 } else 1168 (void) fprintf(fp, "boot-args=\"%s\"\n", o); 1169 1170 (void) fflush(fp); 1171 (void) fclose(fp); 1172 return (ret); 1173 } 1174 1175 static error_t 1176 list_setting(struct menu_lst *menu, char *which, char *setting) 1177 { 1178 int entry = -1; 1179 menu_entry_t *m; 1180 be_node_list_t *be_nodes, *be_node = NULL; 1181 int ret; 1182 1183 assert(which); 1184 assert(setting); 1185 1186 /* 1187 * which can be: 1188 * "" - list default entry 1189 * number - use for entry number 1190 * property name 1191 */ 1192 if (*which != '\0') { 1193 if (isdigit(*which)) { 1194 char *rest; 1195 errno = 0; 1196 entry = strtol(which, &rest, 10); 1197 if (errno != 0 || *rest != '\0') { 1198 bam_error(_("invalid boot entry number: %s\n"), 1199 which); 1200 return (BAM_ERROR); 1201 } 1202 } else 1203 setting = which; 1204 } 1205 1206 /* find default entry */ 1207 if (entry == -1) { 1208 ret = be_list(NULL, &be_nodes); 1209 if (ret != BE_SUCCESS) { 1210 bam_error(_("No BE's found\n")); 1211 return (BAM_ERROR); 1212 } 1213 STAILQ_FOREACH(m, menu, me_next) { 1214 entry++; 1215 for (be_node = be_nodes; be_node; 1216 be_node = be_node->be_next_node) { 1217 if (strcmp(be_node->be_root_ds, 1218 m->me_bootfs) == 0) 1219 break; 1220 } 1221 if (be_node != NULL && 1222 be_node->be_active_on_boot == B_TRUE) 1223 break; /* found active node */ 1224 } 1225 be_free_list(be_nodes); 1226 if (be_node == NULL) { 1227 bam_error(_("None of BE nodes is marked active\n")); 1228 return (BAM_ERROR); 1229 } 1230 } else { 1231 STAILQ_FOREACH(m, menu, me_next) 1232 if (m->me_idx == entry) 1233 break; 1234 1235 if (m == NULL) { 1236 bam_error(_("no matching entry found\n")); 1237 return (BAM_ERROR); 1238 } 1239 } 1240 1241 return (list_menu_entry(m, setting)); 1242 } 1243 1244 /*ARGSUSED*/ 1245 static error_t 1246 disable_hyper(struct menu_lst *menu, char *osroot, char *opt) 1247 { 1248 char path[PATH_MAX]; 1249 1250 (void) snprintf(path, PATH_MAX, "%s" XEN_CONFIG, bam_root); 1251 (void) unlink(path); 1252 return (BAM_SUCCESS); 1253 } 1254 1255 /*ARGSUSED*/ 1256 static error_t 1257 enable_hyper(struct menu_lst *menu, char *osroot, char *opt) 1258 { 1259 ficlVm *vm; 1260 char path[PATH_MAX]; 1261 char buf[MAX_INPUT]; 1262 char *env; 1263 FILE *fp; 1264 struct mnttab mpref = { 0 }; 1265 struct mnttab mp = { 0 }; 1266 int ret; 1267 1268 fp = fopen(MNTTAB, "r"); 1269 if (fp == NULL) 1270 return (BAM_ERROR); 1271 1272 mpref.mnt_mountp = "/"; 1273 if (getmntany(fp, &mp, &mpref) != 0) { 1274 (void) fclose(fp); 1275 return (BAM_ERROR); 1276 } 1277 (void) fclose(fp); 1278 1279 vm = bf_init("", ficlTextOutSilent); 1280 if (vm == NULL) { 1281 bam_error(_("Error setting up forth interpreter\n")); 1282 return (BAM_ERROR); 1283 } 1284 1285 /* 1286 * need to check current boot config, so fire up the ficl 1287 * if its xen setup, we add option to boot-args list, not replacing it. 1288 */ 1289 (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", mp.mnt_special); 1290 ret = ficlVmEvaluate(vm, buf); 1291 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 1292 bam_error(_("Error interpreting boot config\n")); 1293 bf_fini(); 1294 return (BAM_ERROR); 1295 } 1296 (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th"); 1297 ret = ficlVmEvaluate(vm, buf); 1298 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 1299 bam_error(_("Error interpreting boot config\n")); 1300 bf_fini(); 1301 return (BAM_ERROR); 1302 } 1303 (void) snprintf(buf, MAX_INPUT, "start"); 1304 ret = ficlVmEvaluate(vm, buf); 1305 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 1306 bam_error(_("Error interpreting boot config\n")); 1307 bf_fini(); 1308 return (BAM_ERROR); 1309 } 1310 (void) snprintf(buf, MAX_INPUT, "boot"); 1311 ret = ficlVmEvaluate(vm, buf); 1312 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 1313 bam_error(_("Error interpreting boot config\n")); 1314 bf_fini(); 1315 return (BAM_ERROR); 1316 } 1317 bf_fini(); 1318 1319 (void) mkdir(CONF_DIR, 0755); 1320 (void) snprintf(path, PATH_MAX, "%s" XEN_CONFIG, bam_root); 1321 fp = fopen(path, "w"); 1322 if (fp == NULL) { 1323 return (BAM_ERROR); /* error, cant write config */ 1324 } 1325 1326 errno = 0; 1327 /* 1328 * on write error, remove file to ensure we have bootable config. 1329 * note we dont mind if config exists, it will get updated 1330 */ 1331 (void) fprintf(fp, "xen_kernel=\"/boot/${ISADIR}/xen\"\n"); 1332 if (errno != 0) 1333 goto error; 1334 1335 /* 1336 * really simple and stupid console conversion. 1337 * it really has to be gone, it belongs to milestone/xvm properties. 1338 */ 1339 env = getenv("console"); 1340 if (env != NULL) { 1341 if (strcmp(env, "ttya") == 0) 1342 (void) fprintf(fp, "xen_cmdline=\"console=com1 %s\"\n", 1343 opt); 1344 else if (strcmp(env, "ttyb") == 0) 1345 (void) fprintf(fp, "xen_cmdline=\"console=com2 %s\"\n", 1346 opt); 1347 else 1348 (void) fprintf(fp, "xen_cmdline=\"console=vga %s\"\n", 1349 opt); 1350 } else 1351 (void) fprintf(fp, "xen_cmdline=\"%s\"\n", opt); 1352 if (errno != 0) 1353 goto error; 1354 1355 (void) fprintf(fp, 1356 "bootfile=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n"); 1357 if (errno != 0) 1358 goto error; 1359 1360 (void) fprintf(fp, 1361 "boot-args=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n"); 1362 if (errno != 0) 1363 goto error; 1364 1365 (void) fclose(fp); 1366 if (errno != 0) { 1367 (void) unlink(path); 1368 return (BAM_ERROR); 1369 } 1370 return (BAM_SUCCESS); 1371 error: 1372 (void) fclose(fp); 1373 (void) unlink(path); 1374 return (BAM_ERROR); 1375 }