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