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