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