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 2018 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 if (strcmp(entry->me_type, "bootfs") != 0 || 797 strchr(entry->me_bootfs, ':') != NULL) { 798 (void) printf("\nTitle: %s\n", entry->me_title); 799 (void) printf("Type: %s\n", entry->me_type); 800 (void) printf("Device: %s\n", entry->me_bootfs); 801 return (ret); 802 } 803 804 mounted = bam_mount_be(entry, &dir); 805 if (mounted != BE_SUCCESS && mounted != BE_ERR_MOUNTED) { 806 if (dir != NULL) { 807 (void) rmdir(dir); 808 free(dir); 809 } 810 bam_error(_("%s is not mounted\n"), entry->me_title); 811 return (BAM_ERROR); 812 } 813 814 vm = bf_init("", ficlTextOutSilent); 815 if (vm == NULL) { 816 bam_error(_("error setting up forth interpreter\n")); 817 ret = BAM_ERROR; 818 goto done; 819 } 820 821 /* should only get FICL_VM_STATUS_OUT_OF_TEXT */ 822 (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", 823 entry->me_bootfs); 824 ret = ficlVmEvaluate(vm, buf); 825 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 826 bam_error(_("error interpreting boot config\n")); 827 ret = BAM_ERROR; 828 goto done; 829 } 830 (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th"); 831 ret = ficlVmEvaluate(vm, buf); 832 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 833 bam_error(_("error interpreting boot config\n")); 834 ret = BAM_ERROR; 835 goto done; 836 } 837 (void) snprintf(buf, MAX_INPUT, "start"); 838 ret = ficlVmEvaluate(vm, buf); 839 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 840 bam_error(_("error interpreting boot config\n")); 841 ret = BAM_ERROR; 842 goto done; 843 } 844 (void) snprintf(buf, MAX_INPUT, "boot"); 845 ret = ficlVmEvaluate(vm, buf); 846 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 847 bam_error(_("error interpreting boot config\n")); 848 ret = BAM_ERROR; 849 goto done; 850 } 851 852 ret = BAM_SUCCESS; 853 if (*setting == '\0') 854 (void) printf("\nTitle: %s\n", entry->me_title); 855 else if (strcasecmp(setting, "title") == 0) { 856 (void) printf("%s\n", entry->me_title); 857 goto done; 858 } 859 860 ptr = getenv("autoboot_delay"); 861 if (ptr != NULL) { 862 char *timeout = "-1"; 863 864 if (strcasecmp(ptr, "NO") != 0) 865 timeout = ptr; 866 867 if (*setting == '\0') 868 (void) printf("Timeout: %s\n", timeout); 869 else if (strcasecmp(setting, "timeout") == 0) { 870 (void) printf("%s\n", timeout); 871 goto done; 872 } 873 874 } 875 ptr = getenv("console"); 876 if (ptr != NULL) { 877 if (*setting == '\0') 878 (void) printf("Console: %s\n", ptr); 879 else if (strcasecmp(setting, "console") == 0) { 880 (void) printf("%s\n", ptr); 881 goto done; 882 } 883 } 884 885 if (*setting == '\0') 886 (void) printf("Bootfs: %s\n", entry->me_bootfs); 887 else if (strcasecmp(setting, "bootfs") == 0) { 888 (void) printf("%s\n", entry->me_bootfs); 889 goto done; 890 } 891 892 ptr = getenv("xen_kernel"); 893 if (ptr != NULL) { 894 if (*setting == '\0') { 895 (void) printf("Xen kernel: %s\n", ptr); 896 } else if (strcasecmp(setting, "xen_kernel") == 0) { 897 (void) printf("%s\n", ptr); 898 goto done; 899 } 900 901 if (*setting == '\0') { 902 (void) printf("Xen args: \"%s\"\n", 903 getenv("xen_cmdline")); 904 } else if (strcasecmp(setting, "xen_cmdline") == 0) { 905 (void) printf("%s\n", getenv("xen_cmdline")); 906 goto done; 907 } 908 909 if (*setting == '\0') { 910 (void) printf("Kernel: %s\n", 911 getenv("bootfile")); 912 } else if (strcasecmp(setting, "kernel") == 0) { 913 (void) printf("%s\n", getenv("bootfile")); 914 goto done; 915 } 916 } else { 917 ptr = getenv("kernelname"); 918 if (ptr != NULL) { 919 if (*setting == '\0') { 920 (void) printf("Kernel: %s\n", ptr); 921 } else if (strcasecmp(setting, "kernel") == 0) { 922 (void) printf("%s\n", ptr); 923 goto done; 924 } 925 } 926 } 927 928 ptr = getenv("boot-args"); 929 if (ptr != NULL) { 930 if (*setting == '\0') { 931 (void) printf("Boot-args: \"%s\"\n", ptr); 932 } else if (strcasecmp(setting, "boot-args") == 0) { 933 (void) printf("%s\n", ptr); 934 goto done; 935 } 936 } 937 938 if (*setting == '\0' || strcasecmp(setting, "modules") == 0) { 939 (void) printf("\nModules:\n"); 940 ficlVmSetTextOut(vm, ficlCallbackDefaultTextOut); 941 (void) snprintf(buf, MAX_INPUT, "show-module-options"); 942 ret = ficlVmEvaluate(vm, buf); 943 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 944 bam_error(_("error interpreting boot config\n")); 945 ret = BAM_ERROR; 946 goto done; 947 } 948 ret = BAM_SUCCESS; 949 goto done; 950 } 951 952 /* if we got here with setting string, its unknown property */ 953 if (*setting != '\0') { 954 bam_error(_("unknown property: %s\n"), setting); 955 ret = BAM_ERROR; 956 } else 957 ret = BAM_SUCCESS; 958 done: 959 bf_fini(); 960 if (mounted != BE_ERR_MOUNTED) { 961 (void) bam_umount_be(dir); 962 } 963 964 if (dir != NULL) { 965 (void) rmdir(dir); 966 free(dir); 967 } 968 969 return (ret); 970 } 971 972 /*ARGSUSED*/ 973 static error_t 974 list_entry(struct menu_lst *menu, char *menu_root, char *opt) 975 { 976 error_t ret = BAM_SUCCESS; 977 menu_entry_t *entry; 978 char *ptr, *title = NULL; 979 int i, e = -1; 980 981 if (opt == NULL) { 982 print_nodes(B_FALSE, menu); 983 return (ret); 984 } 985 986 if ((ptr = strchr(opt, '=')) == NULL) { 987 bam_error(_("invalid option: %s\n"), opt); 988 return (BAM_ERROR); 989 } 990 991 i = ptr - opt; 992 if (strncmp(opt, "entry", i) == 0) { 993 e = atoi(ptr+1); 994 } else if (strncmp(opt, "title", i) == 0) { 995 title = ptr+1; 996 } else { 997 bam_error(_("invalid option: %s\n"), opt); 998 return (BAM_ERROR); 999 } 1000 1001 STAILQ_FOREACH(entry, menu, me_next) { 1002 if (title != NULL) { 1003 if (strcmp(title, entry->me_title) == 0) 1004 break; 1005 } else if (entry->me_idx == e) 1006 break; 1007 } 1008 1009 if (entry == NULL) { 1010 bam_error(_("no matching entry found\n")); 1011 return (BAM_ERROR); 1012 } 1013 1014 return (list_menu_entry(entry, "")); 1015 } 1016 1017 /* 1018 * For now this is just stub entry to support grub interface, the 1019 * known consumer is installer ict.py code, calling as: 1020 * bootadm update-menu -R /a -Z -o rdisk 1021 * Later this can be converted to do something useful. 1022 */ 1023 /*ARGSUSED*/ 1024 static error_t 1025 update_entry(struct menu_lst *menu, char *menu_root, char *osdev) 1026 { 1027 char path[PATH_MAX]; 1028 char *pool = menu_root + 1; 1029 be_node_list_t *be_nodes, *be_node; 1030 int rv; 1031 FILE *fp; 1032 1033 (void) snprintf(path, PATH_MAX, "%s%s", menu_root, MENU); 1034 rv = be_list(NULL, &be_nodes, BE_LIST_DEFAULT); 1035 1036 if (rv != BE_SUCCESS) 1037 return (BAM_ERROR); 1038 1039 fp = fopen(path, "w"); 1040 if (fp == NULL) { 1041 be_free_list(be_nodes); 1042 return (BAM_ERROR); 1043 } 1044 1045 for (be_node = be_nodes; be_node; be_node = be_node->be_next_node) { 1046 if (strcmp(be_node->be_rpool, pool) == 0) { 1047 (void) fprintf(fp, "title %s\n", be_node->be_node_name); 1048 (void) fprintf(fp, "bootfs %s\n", be_node->be_root_ds); 1049 } 1050 } 1051 1052 be_free_list(be_nodes); 1053 (void) fclose(fp); 1054 return (BAM_SUCCESS); 1055 } 1056 1057 /*ARGSUSED*/ 1058 static error_t 1059 update_temp(struct menu_lst *menu, char *dummy, char *opt) 1060 { 1061 error_t ret = BAM_ERROR; 1062 char path[PATH_MAX]; 1063 char buf[MAX_INPUT]; 1064 struct mnttab mpref = { 0 }; 1065 struct mnttab mp = { 0 }; 1066 ficlVm *vm; 1067 char *env, *o; 1068 FILE *fp; 1069 1070 (void) snprintf(path, PATH_MAX, "%s" TRANSIENT, bam_root); 1071 /* 1072 * if opt == NULL, remove transient config 1073 */ 1074 if (opt == NULL) { 1075 (void) unlink(path); 1076 return (BAM_SUCCESS); 1077 } 1078 1079 fp = fopen(MNTTAB, "r"); 1080 if (fp == NULL) 1081 return (BAM_ERROR); 1082 1083 mpref.mnt_mountp = "/"; 1084 if (getmntany(fp, &mp, &mpref) != 0) { 1085 (void) fclose(fp); 1086 return (BAM_ERROR); 1087 } 1088 (void) fclose(fp); 1089 1090 vm = bf_init("", ficlTextOutSilent); 1091 if (vm == NULL) { 1092 bam_error(_("Error setting up forth interpreter\n")); 1093 return (ret); 1094 } 1095 1096 /* 1097 * need to check current boot config, so fire up the ficl 1098 * if its xen setup, we add option to boot-args list, not replacing it. 1099 */ 1100 (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", mp.mnt_special); 1101 ret = ficlVmEvaluate(vm, buf); 1102 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 1103 bam_error(_("Error interpreting boot config\n")); 1104 bf_fini(); 1105 return (BAM_ERROR); 1106 } 1107 (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th"); 1108 ret = ficlVmEvaluate(vm, buf); 1109 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 1110 bam_error(_("Error interpreting boot config\n")); 1111 bf_fini(); 1112 return (BAM_ERROR); 1113 } 1114 (void) snprintf(buf, MAX_INPUT, "start"); 1115 ret = ficlVmEvaluate(vm, buf); 1116 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 1117 bam_error(_("Error interpreting boot config\n")); 1118 bf_fini(); 1119 return (BAM_ERROR); 1120 } 1121 (void) snprintf(buf, MAX_INPUT, "boot"); 1122 ret = ficlVmEvaluate(vm, buf); 1123 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 1124 bam_error(_("Error interpreting boot config\n")); 1125 bf_fini(); 1126 return (BAM_ERROR); 1127 } 1128 bf_fini(); 1129 1130 if (opt[0] == '-') { 1131 env = getenv("xen_kernel"); 1132 fp = fopen(path, "w"); 1133 if (fp == NULL) 1134 return (BAM_ERROR); 1135 1136 if (env != NULL) { 1137 env = getenv("boot-args"); 1138 (void) fprintf(fp, "boot-args=\"%s %s\"\n", env, opt); 1139 } else 1140 (void) fprintf(fp, "boot-args=\"%s\"\n", opt); 1141 (void) fclose(fp); 1142 return (BAM_SUCCESS); 1143 } 1144 1145 /* 1146 * it should be the case with "kernel args" 1147 * so, we split the opt at first space 1148 * and store bootfile= and boot-args= 1149 */ 1150 env = getenv("xen_kernel"); 1151 1152 o = strchr(opt, ' '); 1153 if (o == NULL) { 1154 fp = fopen(path, "w"); 1155 if (fp == NULL) 1156 return (BAM_ERROR); 1157 (void) fprintf(fp, "bootfile=\"%s;unix\"\n", opt); 1158 (void) fclose(fp); 1159 return (BAM_SUCCESS); 1160 } 1161 *o++ = '\0'; 1162 fp = fopen(path, "w"); 1163 if (fp == NULL) 1164 return (BAM_ERROR); 1165 (void) fprintf(fp, "bootfile=\"%s;unix\"\n", opt); 1166 1167 if (env != NULL) { 1168 env = getenv("boot-args"); 1169 (void) fprintf(fp, "boot-args=\"%s %s\"\n", env, opt); 1170 } else 1171 (void) fprintf(fp, "boot-args=\"%s\"\n", o); 1172 1173 (void) fflush(fp); 1174 (void) fclose(fp); 1175 return (ret); 1176 } 1177 1178 static error_t 1179 list_setting(struct menu_lst *menu, char *which, char *setting) 1180 { 1181 int entry = -1; 1182 menu_entry_t *m; 1183 be_node_list_t *be_nodes, *be_node = NULL; 1184 int ret; 1185 1186 assert(which); 1187 assert(setting); 1188 1189 /* 1190 * which can be: 1191 * "" - list default entry 1192 * number - use for entry number 1193 * property name 1194 */ 1195 if (*which != '\0') { 1196 if (isdigit(*which)) { 1197 char *rest; 1198 errno = 0; 1199 entry = strtol(which, &rest, 10); 1200 if (errno != 0 || *rest != '\0') { 1201 bam_error(_("invalid boot entry number: %s\n"), 1202 which); 1203 return (BAM_ERROR); 1204 } 1205 } else 1206 setting = which; 1207 } 1208 1209 /* find default entry */ 1210 if (entry == -1) { 1211 ret = be_list(NULL, &be_nodes, BE_LIST_DEFAULT); 1212 if (ret != BE_SUCCESS) { 1213 bam_error(_("No BE's found\n")); 1214 return (BAM_ERROR); 1215 } 1216 STAILQ_FOREACH(m, menu, me_next) { 1217 entry++; 1218 for (be_node = be_nodes; be_node; 1219 be_node = be_node->be_next_node) { 1220 if (strcmp(be_node->be_root_ds, 1221 m->me_bootfs) == 0) 1222 break; 1223 } 1224 if (be_node != NULL && 1225 be_node->be_active_on_boot == B_TRUE) 1226 break; /* found active node */ 1227 } 1228 be_free_list(be_nodes); 1229 if (be_node == NULL) { 1230 bam_error(_("None of BE nodes is marked active\n")); 1231 return (BAM_ERROR); 1232 } 1233 } else { 1234 STAILQ_FOREACH(m, menu, me_next) 1235 if (m->me_idx == entry) 1236 break; 1237 1238 if (m == NULL) { 1239 bam_error(_("no matching entry found\n")); 1240 return (BAM_ERROR); 1241 } 1242 } 1243 1244 return (list_menu_entry(m, setting)); 1245 } 1246 1247 /*ARGSUSED*/ 1248 static error_t 1249 disable_hyper(struct menu_lst *menu, char *osroot, char *opt) 1250 { 1251 char path[PATH_MAX]; 1252 1253 (void) snprintf(path, PATH_MAX, "%s" XEN_CONFIG, bam_root); 1254 (void) unlink(path); 1255 return (BAM_SUCCESS); 1256 } 1257 1258 /*ARGSUSED*/ 1259 static error_t 1260 enable_hyper(struct menu_lst *menu, char *osroot, char *opt) 1261 { 1262 ficlVm *vm; 1263 char path[PATH_MAX]; 1264 char buf[MAX_INPUT]; 1265 char *env; 1266 FILE *fp; 1267 struct mnttab mpref = { 0 }; 1268 struct mnttab mp = { 0 }; 1269 int ret; 1270 1271 fp = fopen(MNTTAB, "r"); 1272 if (fp == NULL) 1273 return (BAM_ERROR); 1274 1275 mpref.mnt_mountp = "/"; 1276 if (getmntany(fp, &mp, &mpref) != 0) { 1277 (void) fclose(fp); 1278 return (BAM_ERROR); 1279 } 1280 (void) fclose(fp); 1281 1282 vm = bf_init("", ficlTextOutSilent); 1283 if (vm == NULL) { 1284 bam_error(_("Error setting up forth interpreter\n")); 1285 return (BAM_ERROR); 1286 } 1287 1288 /* 1289 * need to check current boot config, so fire up the ficl 1290 * if its xen setup, we add option to boot-args list, not replacing it. 1291 */ 1292 (void) snprintf(buf, MAX_INPUT, "set currdev=zfs:%s:", mp.mnt_special); 1293 ret = ficlVmEvaluate(vm, buf); 1294 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 1295 bam_error(_("Error interpreting boot config\n")); 1296 bf_fini(); 1297 return (BAM_ERROR); 1298 } 1299 (void) snprintf(buf, MAX_INPUT, "include /boot/forth/loader.4th"); 1300 ret = ficlVmEvaluate(vm, buf); 1301 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 1302 bam_error(_("Error interpreting boot config\n")); 1303 bf_fini(); 1304 return (BAM_ERROR); 1305 } 1306 (void) snprintf(buf, MAX_INPUT, "start"); 1307 ret = ficlVmEvaluate(vm, buf); 1308 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 1309 bam_error(_("Error interpreting boot config\n")); 1310 bf_fini(); 1311 return (BAM_ERROR); 1312 } 1313 (void) snprintf(buf, MAX_INPUT, "boot"); 1314 ret = ficlVmEvaluate(vm, buf); 1315 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 1316 bam_error(_("Error interpreting boot config\n")); 1317 bf_fini(); 1318 return (BAM_ERROR); 1319 } 1320 bf_fini(); 1321 1322 (void) mkdir(CONF_DIR, 0755); 1323 (void) snprintf(path, PATH_MAX, "%s" XEN_CONFIG, bam_root); 1324 fp = fopen(path, "w"); 1325 if (fp == NULL) { 1326 return (BAM_ERROR); /* error, cant write config */ 1327 } 1328 1329 errno = 0; 1330 /* 1331 * on write error, remove file to ensure we have bootable config. 1332 * note we dont mind if config exists, it will get updated 1333 */ 1334 (void) fprintf(fp, "xen_kernel=\"/boot/${ISADIR}/xen\"\n"); 1335 if (errno != 0) 1336 goto error; 1337 1338 /* 1339 * really simple and stupid console conversion. 1340 * it really has to be gone, it belongs to milestone/xvm properties. 1341 */ 1342 env = getenv("console"); 1343 if (env != NULL) { 1344 if (strcmp(env, "ttya") == 0) 1345 (void) fprintf(fp, "xen_cmdline=\"console=com1 %s\"\n", 1346 opt); 1347 else if (strcmp(env, "ttyb") == 0) 1348 (void) fprintf(fp, "xen_cmdline=\"console=com2 %s\"\n", 1349 opt); 1350 else 1351 (void) fprintf(fp, "xen_cmdline=\"console=vga %s\"\n", 1352 opt); 1353 } else 1354 (void) fprintf(fp, "xen_cmdline=\"%s\"\n", opt); 1355 if (errno != 0) 1356 goto error; 1357 1358 (void) fprintf(fp, 1359 "bootfile=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n"); 1360 if (errno != 0) 1361 goto error; 1362 1363 (void) fprintf(fp, 1364 "boot-args=\"/platform/i86xpv/kernel/${ISADIR}/unix\"\n"); 1365 if (errno != 0) 1366 goto error; 1367 1368 (void) fclose(fp); 1369 if (errno != 0) { 1370 (void) unlink(path); 1371 return (BAM_ERROR); 1372 } 1373 return (BAM_SUCCESS); 1374 error: 1375 (void) fclose(fp); 1376 (void) unlink(path); 1377 return (BAM_ERROR); 1378 } 1379