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