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