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