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