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 /* 23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 25 * Copyright 2016 Toomas Soome <tsoome@me.com> 26 * Copyright (c) 2015 by Delphix. All rights reserved. 27 * Copyright 2022 OmniOS Community Edition (OmniOSce) Association. 28 * Copyright (c) 2018, Joyent, Inc. 29 */ 30 31 32 /* 33 * System includes 34 */ 35 #include <assert.h> 36 #include <errno.h> 37 #include <libgen.h> 38 #include <libintl.h> 39 #include <libnvpair.h> 40 #include <libzfs.h> 41 #include <libgen.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <sys/stat.h> 46 #include <sys/types.h> 47 #include <sys/vfstab.h> 48 #include <sys/param.h> 49 #include <sys/systeminfo.h> 50 #include <ctype.h> 51 #include <time.h> 52 #include <unistd.h> 53 #include <fcntl.h> 54 #include <deflt.h> 55 #include <wait.h> 56 #include <libdevinfo.h> 57 #include <libgen.h> 58 59 #include <libbe.h> 60 #include <libbe_priv.h> 61 #include <boot_utils.h> 62 #include <ficl.h> 63 #include <ficlplatform/emu.h> 64 65 /* Private function prototypes */ 66 static int update_dataset(char *, int, char *, char *, char *); 67 static int _update_vfstab(char *, char *, char *, char *, be_fs_list_data_t *); 68 static int be_open_menu(char *, char *, FILE **, char *, boolean_t); 69 static int be_create_menu(char *, char *, FILE **, char *); 70 static char *be_get_auto_name(char *, char *, boolean_t); 71 72 /* 73 * Global error printing 74 */ 75 boolean_t do_print = B_FALSE; 76 77 /* 78 * Private datatypes 79 */ 80 typedef struct zone_be_name_cb_data { 81 char *base_be_name; 82 int num; 83 } zone_be_name_cb_data_t; 84 85 /* ******************************************************************** */ 86 /* Public Functions */ 87 /* ******************************************************************** */ 88 89 /* 90 * Callback for ficl to suppress all output from ficl, as we do not 91 * want to confuse user with messages from ficl, and we are only 92 * checking results from function calls. 93 */ 94 /*ARGSUSED*/ 95 static void 96 ficlSuppressTextOutput(ficlCallback *cb, char *text) 97 { 98 /* This function is intentionally doing nothing. */ 99 } 100 101 /* 102 * Function: be_get_boot_args 103 * Description: Returns the fast boot argument string for enumerated BE. 104 * Parameters: 105 * fbarg - pointer to argument string. 106 * entry - index of BE. 107 * Returns: 108 * fast boot argument string. 109 * Scope: 110 * Public 111 */ 112 int 113 be_get_boot_args(char **fbarg, int entry) 114 { 115 be_node_list_t *node, *be_nodes = NULL; 116 be_transaction_data_t bt = {0}; 117 char *mountpoint = NULL; 118 boolean_t be_mounted = B_FALSE; 119 int ret = BE_SUCCESS; 120 int index; 121 ficlVm *vm; 122 123 *fbarg = NULL; 124 if (!be_zfs_init()) 125 return (BE_ERR_INIT); 126 127 /* 128 * need pool name, menu.lst has entries from our pool only 129 */ 130 ret = be_find_current_be(&bt); 131 if (ret != BE_SUCCESS) { 132 be_zfs_fini(); 133 return (ret); 134 } 135 136 /* 137 * be_get_boot_args() is for loader, fail with grub will trigger 138 * normal boot. 139 */ 140 if (be_has_grub()) { 141 ret = BE_ERR_INIT; 142 goto done; 143 } 144 145 ret = _be_list(NULL, &be_nodes, BE_LIST_DEFAULT); 146 if (ret != BE_SUCCESS) 147 goto done; 148 149 /* 150 * iterate through be_nodes, 151 * if entry == -1, stop if be_active_on_boot, 152 * else stop if index == entry. 153 */ 154 index = 0; 155 for (node = be_nodes; node != NULL; node = node->be_next_node) { 156 if (strcmp(node->be_rpool, bt.obe_zpool) != 0) 157 continue; 158 if (entry == BE_ENTRY_DEFAULT && 159 node->be_active_on_boot == B_TRUE) 160 break; 161 if (index == entry) 162 break; 163 index++; 164 } 165 if (node == NULL) { 166 be_free_list(be_nodes); 167 ret = BE_ERR_NOENT; 168 goto done; 169 } 170 171 /* try to mount inactive be */ 172 if (node->be_active == B_FALSE) { 173 ret = _be_mount(node->be_node_name, &mountpoint, 174 BE_MOUNT_FLAG_NO_ZONES); 175 if (ret != BE_SUCCESS && ret != BE_ERR_MOUNTED) { 176 be_free_list(be_nodes); 177 goto done; 178 } else 179 be_mounted = B_TRUE; 180 } 181 182 vm = bf_init("", ficlSuppressTextOutput); 183 if (vm != NULL) { 184 /* 185 * zfs MAXNAMELEN is 256, so we need to pick buf large enough 186 * to contain such names. 187 */ 188 char buf[MAXNAMELEN * 2]; 189 char *kernel_options = NULL; 190 char *kernel = NULL; 191 char *tmp; 192 zpool_handle_t *zph; 193 194 /* 195 * just try to interpret following words. on error 196 * we will be missing kernelname, and will get out. 197 */ 198 (void) snprintf(buf, sizeof (buf), "set currdev=zfs:%s:", 199 node->be_root_ds); 200 ret = ficlVmEvaluate(vm, buf); 201 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 202 be_print_err(gettext("be_get_boot_args: error " 203 "interpreting boot config: %d\n"), ret); 204 bf_fini(); 205 ret = BE_ERR_NO_MENU; 206 goto cleanup; 207 } 208 (void) snprintf(buf, sizeof (buf), 209 "include /boot/forth/loader.4th"); 210 ret = ficlVmEvaluate(vm, buf); 211 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 212 be_print_err(gettext("be_get_boot_args: error " 213 "interpreting boot config: %d\n"), ret); 214 bf_fini(); 215 ret = BE_ERR_NO_MENU; 216 goto cleanup; 217 } 218 (void) snprintf(buf, sizeof (buf), "start"); 219 ret = ficlVmEvaluate(vm, buf); 220 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 221 be_print_err(gettext("be_get_boot_args: error " 222 "interpreting boot config: %d\n"), ret); 223 bf_fini(); 224 ret = BE_ERR_NO_MENU; 225 goto cleanup; 226 } 227 (void) snprintf(buf, sizeof (buf), "boot"); 228 ret = ficlVmEvaluate(vm, buf); 229 bf_fini(); 230 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 231 be_print_err(gettext("be_get_boot_args: error " 232 "interpreting boot config: %d\n"), ret); 233 ret = BE_ERR_NO_MENU; 234 goto cleanup; 235 } 236 237 kernel_options = getenv("boot-args"); 238 kernel = getenv("kernelname"); 239 240 if (kernel == NULL) { 241 be_print_err(gettext("be_get_boot_args: no kernel\n")); 242 ret = BE_ERR_NOENT; 243 goto cleanup; 244 } 245 246 if ((zph = zpool_open(g_zfs, node->be_rpool)) == NULL) { 247 be_print_err(gettext("be_get_boot_args: failed to " 248 "open root pool (%s): %s\n"), node->be_rpool, 249 libzfs_error_description(g_zfs)); 250 ret = zfs_err_to_be_err(g_zfs); 251 goto cleanup; 252 } 253 ret = zpool_get_physpath(zph, buf, sizeof (buf)); 254 zpool_close(zph); 255 if (ret != 0) { 256 be_print_err(gettext("be_get_boot_args: failed to " 257 "get physpath\n")); 258 goto cleanup; 259 } 260 261 /* zpool_get_physpath() can return space separated list */ 262 tmp = buf; 263 tmp = strsep(&tmp, " "); 264 265 if (kernel_options == NULL || *kernel_options == '\0') 266 (void) asprintf(fbarg, "/ %s " 267 "-B zfs-bootfs=%s,bootpath=\"%s\"\n", kernel, 268 node->be_root_ds, tmp); 269 else 270 (void) asprintf(fbarg, "/ %s %s " 271 "-B zfs-bootfs=%s,bootpath=\"%s\"\n", kernel, 272 kernel_options, node->be_root_ds, tmp); 273 274 if (*fbarg == NULL) 275 ret = BE_ERR_NOMEM; 276 else 277 ret = 0; 278 } else 279 ret = BE_ERR_NOMEM; 280 cleanup: 281 if (be_mounted == B_TRUE) 282 (void) _be_unmount(node->be_node_name, BE_UNMOUNT_FLAG_FORCE); 283 be_free_list(be_nodes); 284 done: 285 free(mountpoint); 286 free(bt.obe_name); 287 free(bt.obe_root_ds); 288 free(bt.obe_zpool); 289 free(bt.obe_snap_name); 290 free(bt.obe_altroot); 291 be_zfs_fini(); 292 return (ret); 293 } 294 295 /* 296 * Function: be_max_avail 297 * Description: Returns the available size for the zfs dataset passed in. 298 * Parameters: 299 * dataset - The dataset we want to get the available space for. 300 * ret - The available size will be returned in this. 301 * Returns: 302 * The error returned by the zfs get property function. 303 * Scope: 304 * Public 305 */ 306 int 307 be_max_avail(char *dataset, uint64_t *ret) 308 { 309 zfs_handle_t *zhp; 310 int err = 0; 311 312 /* Initialize libzfs handle */ 313 if (!be_zfs_init()) 314 return (BE_ERR_INIT); 315 316 zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET); 317 if (zhp == NULL) { 318 /* 319 * The zfs_open failed return an error 320 */ 321 err = zfs_err_to_be_err(g_zfs); 322 } else { 323 err = be_maxsize_avail(zhp, ret); 324 } 325 ZFS_CLOSE(zhp); 326 be_zfs_fini(); 327 return (err); 328 } 329 330 /* 331 * Function: libbe_print_errors 332 * Description: Turns on/off error output for the library. 333 * Parameter: 334 * set_do_print - Boolean that turns library error 335 * printing on or off. 336 * Returns: 337 * None 338 * Scope: 339 * Public; 340 */ 341 void 342 libbe_print_errors(boolean_t set_do_print) 343 { 344 do_print = set_do_print; 345 } 346 347 /* ******************************************************************** */ 348 /* Semi-Private Functions */ 349 /* ******************************************************************** */ 350 351 /* 352 * Function: be_zfs_init 353 * Description: Initializes the libary global libzfs handle. 354 * Parameters: 355 * None 356 * Returns: 357 * B_TRUE - Success 358 * B_FALSE - Failure 359 * Scope: 360 * Semi-private (library wide use only) 361 */ 362 boolean_t 363 be_zfs_init(void) 364 { 365 be_zfs_fini(); 366 367 if ((g_zfs = libzfs_init()) == NULL) { 368 be_print_err(gettext("be_zfs_init: failed to initialize ZFS " 369 "library\n")); 370 return (B_FALSE); 371 } 372 373 return (B_TRUE); 374 } 375 376 /* 377 * Function: be_zfs_fini 378 * Description: Closes the library global libzfs handle if it currently open. 379 * Parameter: 380 * None 381 * Returns: 382 * None 383 * Scope: 384 * Semi-private (library wide use only) 385 */ 386 void 387 be_zfs_fini(void) 388 { 389 if (g_zfs) 390 libzfs_fini(g_zfs); 391 392 g_zfs = NULL; 393 } 394 395 /* 396 * Function: be_get_defaults 397 * Description: Open defaults and gets be default paramets 398 * Parameters: 399 * defaults - be defaults struct 400 * Returns: 401 * None 402 * Scope: 403 * Semi-private (library wide use only) 404 */ 405 void 406 be_get_defaults(struct be_defaults *defaults) 407 { 408 void *defp; 409 410 defaults->be_deflt_grub = B_FALSE; 411 defaults->be_deflt_rpool_container = B_FALSE; 412 defaults->be_deflt_bename_starts_with[0] = '\0'; 413 414 if ((defp = defopen_r(BE_DEFAULTS)) != NULL) { 415 const char *res = defread_r(BE_DFLT_BENAME_STARTS, defp); 416 if (res != NULL && res[0] != '\0') { 417 (void) strlcpy(defaults->be_deflt_bename_starts_with, 418 res, ZFS_MAX_DATASET_NAME_LEN); 419 defaults->be_deflt_rpool_container = B_TRUE; 420 } 421 if (be_is_isa("i386")) { 422 res = defread_r(BE_DFLT_BE_HAS_GRUB, defp); 423 if (res != NULL && res[0] != '\0') { 424 if (strcasecmp(res, "true") == 0) 425 defaults->be_deflt_grub = B_TRUE; 426 } 427 } 428 defclose_r(defp); 429 } 430 } 431 432 /* 433 * Function: be_make_root_ds 434 * Description: Generate string for BE's root dataset given the pool 435 * it lives in and the BE name. 436 * Parameters: 437 * zpool - pointer zpool name. 438 * be_name - pointer to BE name. 439 * be_root_ds - pointer to buffer to return BE root dataset in. 440 * be_root_ds_size - size of be_root_ds 441 * Returns: 442 * BE_SUCCESS - Success 443 * be_errno_t - Failure 444 * Scope: 445 * Semi-private (library wide use only) 446 */ 447 int 448 be_make_root_ds(const char *zpool, const char *be_name, char *be_root_ds, 449 int be_root_ds_size) 450 { 451 struct be_defaults be_defaults; 452 be_get_defaults(&be_defaults); 453 454 assert(zpool != NULL); 455 456 if (getzoneid() == GLOBAL_ZONEID) { 457 if (be_defaults.be_deflt_rpool_container) { 458 (void) snprintf(be_root_ds, be_root_ds_size, 459 "%s/%s", zpool, be_name); 460 } else { 461 (void) snprintf(be_root_ds, be_root_ds_size, 462 "%s/%s/%s", zpool, BE_CONTAINER_DS_NAME, be_name); 463 } 464 } else { 465 /* 466 * In a non-global zone we can use the path from the mounted 467 * root dataset to generate the BE's root dataset string. 468 */ 469 char *root_ds = be_get_ds_from_dir("/"); 470 471 if (root_ds == NULL) { 472 be_print_err(gettext("be_make_root_ds: zone root " 473 "dataset is not mounted\n")); 474 return (BE_ERR_NOTMOUNTED); 475 } 476 if (strncmp(root_ds, zpool, strlen(zpool)) != 0 || 477 root_ds[strlen(zpool)] != '/') { 478 /* 479 * This pool is not the one that contains the zone 480 * root. 481 */ 482 return (BE_ERR_ACCESS); 483 } 484 485 (void) snprintf(be_root_ds, be_root_ds_size, "%s/%s", 486 dirname(root_ds), be_name); 487 } 488 489 return (BE_SUCCESS); 490 } 491 492 /* 493 * Function: be_make_container_ds 494 * Description: Generate string for the BE container dataset given a pool name. 495 * Parameters: 496 * zpool - pointer zpool name. 497 * container_ds - pointer to buffer to return BE container 498 * dataset in. 499 * container_ds_size - size of container_ds 500 * Returns: 501 * BE_SUCCESS - Success 502 * be_errno_t - Failure 503 * Scope: 504 * Semi-private (library wide use only) 505 */ 506 int 507 be_make_container_ds(const char *zpool, char *container_ds, 508 int container_ds_size) 509 { 510 struct be_defaults be_defaults; 511 be_get_defaults(&be_defaults); 512 513 if (getzoneid() == GLOBAL_ZONEID) { 514 if (be_defaults.be_deflt_rpool_container) { 515 (void) snprintf(container_ds, container_ds_size, 516 "%s", zpool); 517 } else { 518 (void) snprintf(container_ds, container_ds_size, 519 "%s/%s", zpool, BE_CONTAINER_DS_NAME); 520 } 521 } else { 522 char *root_ds = be_get_ds_from_dir("/"); 523 524 if (root_ds == NULL) { 525 be_print_err(gettext("be_make_container_ds: zone root " 526 "dataset is not mounted\n")); 527 return (BE_ERR_NOTMOUNTED); 528 } 529 if (strncmp(root_ds, zpool, strlen(zpool)) != 0 || 530 root_ds[strlen(zpool)] != '/') { 531 /* 532 * This pool is not the one that contains the zone 533 * root. 534 */ 535 return (BE_ERR_ACCESS); 536 } 537 (void) strlcpy(container_ds, dirname(root_ds), 538 container_ds_size); 539 } 540 541 return (BE_SUCCESS); 542 } 543 544 /* 545 * Function: be_make_root_container_ds 546 * Description: Generate string for the BE root container dataset given a pool 547 * name. 548 * Parameters: 549 * zpool - pointer zpool name. 550 * container_ds - pointer to buffer in which to return result 551 * container_ds_size - size of container_ds 552 * Returns: 553 * BE_SUCCESS - Success 554 * be_errno_t - Failure 555 * Scope: 556 * Semi-private (library wide use only) 557 */ 558 int 559 be_make_root_container_ds(const char *zpool, char *container_ds, 560 int container_ds_size) 561 { 562 char *root; 563 int ret; 564 565 if ((ret = be_make_container_ds(zpool, container_ds, 566 container_ds_size)) != BE_SUCCESS) { 567 return (ret); 568 } 569 570 /* If the container DS ends with /ROOT, remove it. */ 571 572 if ((root = strrchr(container_ds, '/')) != NULL && 573 strcmp(root + 1, BE_CONTAINER_DS_NAME) == 0) { 574 *root = '\0'; 575 } 576 577 return (BE_SUCCESS); 578 } 579 580 /* 581 * Function: be_make_name_from_ds 582 * Description: This function takes a dataset name and strips off the 583 * BE container dataset portion from the beginning. The 584 * returned name is allocated in heap storage, so the caller 585 * is responsible for freeing it. 586 * Parameters: 587 * dataset - dataset to get name from. 588 * rc_loc - dataset underwhich the root container dataset lives. 589 * Returns: 590 * name of dataset relative to BE container dataset. 591 * NULL if dataset is not under a BE root dataset. 592 * Scope: 593 * Semi-primate (library wide use only) 594 */ 595 char * 596 be_make_name_from_ds(const char *dataset, char *rc_loc) 597 { 598 char ds[ZFS_MAX_DATASET_NAME_LEN]; 599 char *tok = NULL; 600 char *name = NULL; 601 struct be_defaults be_defaults; 602 int rlen = strlen(rc_loc); 603 604 be_get_defaults(&be_defaults); 605 606 /* 607 * First token is the location of where the root container dataset 608 * lives; it must match rc_loc. 609 */ 610 if (strncmp(dataset, rc_loc, rlen) == 0 && dataset[rlen] == '/') 611 (void) strlcpy(ds, dataset + rlen + 1, sizeof (ds)); 612 else 613 return (NULL); 614 615 if (be_defaults.be_deflt_rpool_container) { 616 if ((name = strdup(ds)) == NULL) { 617 be_print_err(gettext("be_make_name_from_ds: " 618 "memory allocation failed\n")); 619 return (NULL); 620 } 621 } else { 622 /* Second token must be BE container dataset name */ 623 if ((tok = strtok(ds, "/")) == NULL || 624 strcmp(tok, BE_CONTAINER_DS_NAME) != 0) 625 return (NULL); 626 627 /* Return the remaining token if one exists */ 628 if ((tok = strtok(NULL, "")) == NULL) 629 return (NULL); 630 631 if ((name = strdup(tok)) == NULL) { 632 be_print_err(gettext("be_make_name_from_ds: " 633 "memory allocation failed\n")); 634 return (NULL); 635 } 636 } 637 638 return (name); 639 } 640 641 /* 642 * Function: be_maxsize_avail 643 * Description: Returns the available size for the zfs handle passed in. 644 * Parameters: 645 * zhp - A pointer to the open zfs handle. 646 * ret - The available size will be returned in this. 647 * Returns: 648 * The error returned by the zfs get property function. 649 * Scope: 650 * Semi-private (library wide use only) 651 */ 652 int 653 be_maxsize_avail(zfs_handle_t *zhp, uint64_t *ret) 654 { 655 return ((*ret = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE))); 656 } 657 658 /* 659 * Function: be_append_menu 660 * Description: Appends an entry for a BE into the menu.lst. 661 * Parameters: 662 * be_name - pointer to name of BE to add boot menu entry for. 663 * be_root_pool - pointer to name of pool BE lives in. 664 * boot_pool - Used if the pool containing the grub menu is 665 * different than the one contaiing the BE. This 666 * will normally be NULL. 667 * be_orig_root_ds - The root dataset for the BE. This is 668 * used to check to see if an entry already exists 669 * for this BE. 670 * description - pointer to description of BE to be added in 671 * the title line for this BEs entry. 672 * Returns: 673 * BE_SUCCESS - Success 674 * be_errno_t - Failure 675 * Scope: 676 * Semi-private (library wide use only) 677 */ 678 int 679 be_append_menu(char *be_name, char *be_root_pool, char *boot_pool, 680 char *be_orig_root_ds, char *description) 681 { 682 zfs_handle_t *zhp = NULL; 683 char menu_file[MAXPATHLEN]; 684 char be_root_ds[MAXPATHLEN]; 685 char line[BUFSIZ]; 686 char temp_line[BUFSIZ]; 687 char title[MAXPATHLEN]; 688 char *entries[BUFSIZ]; 689 char *tmp_entries[BUFSIZ]; 690 char *pool_mntpnt = NULL; 691 char *ptmp_mntpnt = NULL; 692 char *orig_mntpnt = NULL; 693 boolean_t found_be = B_FALSE; 694 boolean_t found_orig_be = B_FALSE; 695 boolean_t found_title = B_FALSE; 696 boolean_t pool_mounted = B_FALSE; 697 boolean_t collect_lines = B_FALSE; 698 FILE *menu_fp = NULL; 699 int err = 0, ret = BE_SUCCESS; 700 int i, num_tmp_lines = 0, num_lines = 0; 701 702 if (be_name == NULL || be_root_pool == NULL) 703 return (BE_ERR_INVAL); 704 705 if (boot_pool == NULL) 706 boot_pool = be_root_pool; 707 708 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) { 709 be_print_err(gettext("be_append_menu: failed to open " 710 "pool dataset for %s: %s\n"), be_root_pool, 711 libzfs_error_description(g_zfs)); 712 return (zfs_err_to_be_err(g_zfs)); 713 } 714 715 /* 716 * Check to see if the pool's dataset is mounted. If it isn't we'll 717 * attempt to mount it. 718 */ 719 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt, 720 &pool_mounted)) != BE_SUCCESS) { 721 be_print_err(gettext("be_append_menu: pool dataset " 722 "(%s) could not be mounted\n"), be_root_pool); 723 ZFS_CLOSE(zhp); 724 return (ret); 725 } 726 727 /* 728 * Get the mountpoint for the root pool dataset. 729 */ 730 if (!zfs_is_mounted(zhp, &pool_mntpnt)) { 731 be_print_err(gettext("be_append_menu: pool " 732 "dataset (%s) is not mounted. Can't set " 733 "the default BE in the grub menu.\n"), be_root_pool); 734 ret = BE_ERR_NO_MENU; 735 goto cleanup; 736 } 737 738 /* 739 * Check to see if this system supports grub 740 */ 741 if (be_has_grub()) { 742 (void) snprintf(menu_file, sizeof (menu_file), 743 "%s%s", pool_mntpnt, BE_GRUB_MENU); 744 } else { 745 (void) snprintf(menu_file, sizeof (menu_file), 746 "%s%s", pool_mntpnt, BE_SPARC_MENU); 747 } 748 749 if ((ret = be_make_root_ds(be_root_pool, be_name, be_root_ds, 750 sizeof (be_root_ds))) != BE_SUCCESS) { 751 be_print_err(gettext("%s: failed to get BE container dataset " 752 "for %s/%s\n"), __func__, be_root_pool, be_name); 753 goto cleanup; 754 } 755 756 /* 757 * Iterate through menu first to make sure the BE doesn't already 758 * have an entry in the menu. 759 * 760 * Additionally while iterating through the menu, if we have an 761 * original root dataset for a BE we're cloning from, we need to keep 762 * track of that BE's menu entry. We will then use the lines from 763 * that entry to create the entry for the new BE. 764 */ 765 if ((ret = be_open_menu(be_root_pool, menu_file, 766 &menu_fp, "r", B_TRUE)) != BE_SUCCESS) { 767 goto cleanup; 768 } else if (menu_fp == NULL) { 769 ret = BE_ERR_NO_MENU; 770 goto cleanup; 771 } 772 773 free(pool_mntpnt); 774 pool_mntpnt = NULL; 775 776 while (fgets(line, BUFSIZ, menu_fp)) { 777 char *tok = NULL; 778 779 (void) strlcpy(temp_line, line, BUFSIZ); 780 tok = strtok(line, BE_WHITE_SPACE); 781 782 if (tok == NULL || tok[0] == '#') { 783 continue; 784 } else if (strcmp(tok, "title") == 0) { 785 collect_lines = B_FALSE; 786 if ((tok = strtok(NULL, "\n")) == NULL) 787 (void) strlcpy(title, "", sizeof (title)); 788 else 789 (void) strlcpy(title, tok, sizeof (title)); 790 found_title = B_TRUE; 791 792 if (num_tmp_lines != 0) { 793 for (i = 0; i < num_tmp_lines; i++) { 794 free(tmp_entries[i]); 795 tmp_entries[i] = NULL; 796 } 797 num_tmp_lines = 0; 798 } 799 } else if (strcmp(tok, "bootfs") == 0) { 800 char *bootfs = strtok(NULL, BE_WHITE_SPACE); 801 found_title = B_FALSE; 802 if (bootfs == NULL) 803 continue; 804 805 if (strcmp(bootfs, be_root_ds) == 0) { 806 found_be = B_TRUE; 807 break; 808 } 809 810 if (be_orig_root_ds != NULL && 811 strcmp(bootfs, be_orig_root_ds) == 0 && 812 !found_orig_be) { 813 char str[BUFSIZ]; 814 found_orig_be = B_TRUE; 815 num_lines = 0; 816 /* 817 * Store the new title line 818 */ 819 (void) snprintf(str, BUFSIZ, "title %s\n", 820 description ? description : be_name); 821 entries[num_lines] = strdup(str); 822 num_lines++; 823 /* 824 * If there are any lines between the title 825 * and the bootfs line store these. Also 826 * free the temporary lines. 827 */ 828 for (i = 0; i < num_tmp_lines; i++) { 829 entries[num_lines] = tmp_entries[i]; 830 tmp_entries[i] = NULL; 831 num_lines++; 832 } 833 num_tmp_lines = 0; 834 /* 835 * Store the new bootfs line. 836 */ 837 (void) snprintf(str, BUFSIZ, "bootfs %s\n", 838 be_root_ds); 839 entries[num_lines] = strdup(str); 840 num_lines++; 841 collect_lines = B_TRUE; 842 } 843 } else if (found_orig_be && collect_lines) { 844 /* 845 * get the rest of the lines for the original BE and 846 * store them. 847 */ 848 if (strstr(line, BE_GRUB_COMMENT) != NULL || 849 strstr(line, "BOOTADM") != NULL) 850 continue; 851 if (strcmp(tok, "splashimage") == 0) { 852 entries[num_lines] = 853 strdup("splashimage " 854 "/boot/splashimage.xpm\n"); 855 } else { 856 entries[num_lines] = strdup(temp_line); 857 } 858 num_lines++; 859 } else if (found_title && !found_orig_be) { 860 tmp_entries[num_tmp_lines] = strdup(temp_line); 861 num_tmp_lines++; 862 } 863 } 864 865 (void) fclose(menu_fp); 866 867 if (found_be) { 868 /* 869 * If an entry for this BE was already in the menu, then if 870 * that entry's title matches what we would have put in 871 * return success. Otherwise return failure. 872 */ 873 char *new_title = description ? description : be_name; 874 875 if (strcmp(title, new_title) == 0) { 876 ret = BE_SUCCESS; 877 goto cleanup; 878 } else { 879 if (be_remove_menu(be_name, be_root_pool, 880 boot_pool) != BE_SUCCESS) { 881 be_print_err(gettext("be_append_menu: " 882 "Failed to remove existing unusable " 883 "entry '%s' in boot menu.\n"), be_name); 884 ret = BE_ERR_BE_EXISTS; 885 goto cleanup; 886 } 887 } 888 } 889 890 /* Append BE entry to the end of the file */ 891 menu_fp = fopen(menu_file, "a+"); 892 err = errno; 893 if (menu_fp == NULL) { 894 be_print_err(gettext("be_append_menu: failed " 895 "to open menu.lst file %s\n"), menu_file); 896 ret = errno_to_be_err(err); 897 goto cleanup; 898 } 899 900 if (found_orig_be) { 901 /* 902 * write out all the stored lines 903 */ 904 for (i = 0; i < num_lines; i++) { 905 (void) fprintf(menu_fp, "%s", entries[i]); 906 free(entries[i]); 907 } 908 num_lines = 0; 909 910 /* 911 * Check to see if this system supports grub 912 */ 913 if (be_has_grub()) 914 (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT); 915 ret = BE_SUCCESS; 916 } else { 917 (void) fprintf(menu_fp, "title %s\n", 918 description ? description : be_name); 919 (void) fprintf(menu_fp, "bootfs %s\n", be_root_ds); 920 921 /* 922 * Check to see if this system supports grub 923 */ 924 if (be_has_grub()) { 925 (void) fprintf(menu_fp, "kernel$ " 926 "/platform/i86pc/kernel/$ISADIR/unix -B " 927 "$ZFS-BOOTFS\n"); 928 (void) fprintf(menu_fp, "module$ " 929 "/platform/i86pc/$ISADIR/boot_archive\n"); 930 (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT); 931 } 932 ret = BE_SUCCESS; 933 } 934 (void) fclose(menu_fp); 935 cleanup: 936 if (pool_mounted) { 937 int err = BE_SUCCESS; 938 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt); 939 if (ret == BE_SUCCESS) 940 ret = err; 941 free(orig_mntpnt); 942 free(ptmp_mntpnt); 943 } 944 ZFS_CLOSE(zhp); 945 if (num_tmp_lines > 0) { 946 for (i = 0; i < num_tmp_lines; i++) { 947 free(tmp_entries[i]); 948 tmp_entries[i] = NULL; 949 } 950 } 951 if (num_lines > 0) { 952 for (i = 0; i < num_lines; i++) { 953 free(entries[i]); 954 entries[i] = NULL; 955 } 956 } 957 return (ret); 958 } 959 960 /* 961 * Function: be_remove_menu 962 * Description: Removes a BE's entry from a menu.lst file. 963 * Parameters: 964 * be_name - the name of BE whose entry is to be removed from 965 * the menu.lst file. 966 * be_root_pool - the pool that be_name lives in. 967 * boot_pool - the pool where the BE is, if different than 968 * the pool containing the boot menu. If this is 969 * NULL it will be set to be_root_pool. 970 * Returns: 971 * BE_SUCCESS - Success 972 * be_errno_t - Failure 973 * Scope: 974 * Semi-private (library wide use only) 975 */ 976 int 977 be_remove_menu(char *be_name, char *be_root_pool, char *boot_pool) 978 { 979 zfs_handle_t *zhp = NULL; 980 char be_root_ds[MAXPATHLEN]; 981 char **buffer = NULL; 982 char menu_buf[BUFSIZ]; 983 char menu[MAXPATHLEN]; 984 char *pool_mntpnt = NULL; 985 char *ptmp_mntpnt = NULL; 986 char *orig_mntpnt = NULL; 987 char *tmp_menu = NULL; 988 FILE *menu_fp = NULL; 989 FILE *tmp_menu_fp = NULL; 990 struct stat sb; 991 int ret = BE_SUCCESS; 992 int i; 993 int fd; 994 int err = 0; 995 int nlines = 0; 996 int default_entry = 0; 997 int entry_cnt = 0; 998 int entry_del = 0; 999 int num_entry_del = 0; 1000 int tmp_menu_len = 0; 1001 boolean_t write = B_TRUE; 1002 boolean_t do_buffer = B_FALSE; 1003 boolean_t pool_mounted = B_FALSE; 1004 1005 if (boot_pool == NULL) 1006 boot_pool = be_root_pool; 1007 1008 /* Get name of BE's root dataset */ 1009 if ((ret = be_make_root_ds(be_root_pool, be_name, be_root_ds, 1010 sizeof (be_root_ds))) != BE_SUCCESS) { 1011 be_print_err(gettext("%s: failed to get BE container dataset " 1012 "for %s/%s\n"), __func__, be_root_pool, be_name); 1013 return (ret); 1014 } 1015 1016 /* Get handle to pool dataset */ 1017 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) { 1018 be_print_err(gettext("be_remove_menu: " 1019 "failed to open pool dataset for %s: %s"), 1020 be_root_pool, libzfs_error_description(g_zfs)); 1021 return (zfs_err_to_be_err(g_zfs)); 1022 } 1023 1024 /* 1025 * Check to see if the pool's dataset is mounted. If it isn't we'll 1026 * attempt to mount it. 1027 */ 1028 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt, 1029 &pool_mounted)) != BE_SUCCESS) { 1030 be_print_err(gettext("be_remove_menu: pool dataset " 1031 "(%s) could not be mounted\n"), be_root_pool); 1032 ZFS_CLOSE(zhp); 1033 return (ret); 1034 } 1035 1036 /* 1037 * Get the mountpoint for the root pool dataset. 1038 */ 1039 if (!zfs_is_mounted(zhp, &pool_mntpnt)) { 1040 be_print_err(gettext("be_remove_menu: pool " 1041 "dataset (%s) is not mounted. Can't set " 1042 "the default BE in the grub menu.\n"), be_root_pool); 1043 ret = BE_ERR_NO_MENU; 1044 goto cleanup; 1045 } 1046 1047 /* Get path to boot menu */ 1048 (void) strlcpy(menu, pool_mntpnt, sizeof (menu)); 1049 1050 /* 1051 * Check to see if this system supports grub 1052 */ 1053 if (be_has_grub()) 1054 (void) strlcat(menu, BE_GRUB_MENU, sizeof (menu)); 1055 else 1056 (void) strlcat(menu, BE_SPARC_MENU, sizeof (menu)); 1057 1058 /* Get handle to boot menu file */ 1059 if ((ret = be_open_menu(be_root_pool, menu, &menu_fp, "r", 1060 B_TRUE)) != BE_SUCCESS) { 1061 goto cleanup; 1062 } else if (menu_fp == NULL) { 1063 ret = BE_ERR_NO_MENU; 1064 goto cleanup; 1065 } 1066 1067 free(pool_mntpnt); 1068 pool_mntpnt = NULL; 1069 1070 /* Grab the stats of the original menu file */ 1071 if (stat(menu, &sb) != 0) { 1072 err = errno; 1073 be_print_err(gettext("be_remove_menu: " 1074 "failed to stat file %s: %s\n"), menu, strerror(err)); 1075 ret = errno_to_be_err(err); 1076 goto cleanup; 1077 } 1078 1079 /* Create a tmp file for the modified menu.lst */ 1080 tmp_menu_len = strlen(menu) + 7; 1081 if ((tmp_menu = (char *)malloc(tmp_menu_len)) == NULL) { 1082 be_print_err(gettext("be_remove_menu: malloc failed\n")); 1083 ret = BE_ERR_NOMEM; 1084 goto cleanup; 1085 } 1086 (void) memset(tmp_menu, 0, tmp_menu_len); 1087 (void) strlcpy(tmp_menu, menu, tmp_menu_len); 1088 (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len); 1089 if ((fd = mkstemp(tmp_menu)) == -1) { 1090 err = errno; 1091 be_print_err(gettext("be_remove_menu: mkstemp failed\n")); 1092 ret = errno_to_be_err(err); 1093 free(tmp_menu); 1094 tmp_menu = NULL; 1095 goto cleanup; 1096 } 1097 if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) { 1098 err = errno; 1099 be_print_err(gettext("be_remove_menu: " 1100 "could not open tmp file for write: %s\n"), strerror(err)); 1101 (void) close(fd); 1102 ret = errno_to_be_err(err); 1103 goto cleanup; 1104 } 1105 1106 while (fgets(menu_buf, BUFSIZ, menu_fp)) { 1107 char tline [BUFSIZ]; 1108 char *tok = NULL; 1109 1110 (void) strlcpy(tline, menu_buf, sizeof (tline)); 1111 1112 /* Tokenize line */ 1113 tok = strtok(tline, BE_WHITE_SPACE); 1114 1115 if (tok == NULL || tok[0] == '#') { 1116 /* Found empty line or comment line */ 1117 if (do_buffer) { 1118 /* Buffer this line */ 1119 if ((buffer = (char **)realloc(buffer, 1120 sizeof (char *)*(nlines + 1))) == NULL) { 1121 ret = BE_ERR_NOMEM; 1122 goto cleanup; 1123 } 1124 if ((buffer[nlines++] = strdup(menu_buf)) 1125 == NULL) { 1126 ret = BE_ERR_NOMEM; 1127 goto cleanup; 1128 } 1129 1130 } else if (write || strncmp(menu_buf, BE_GRUB_COMMENT, 1131 strlen(BE_GRUB_COMMENT)) != 0) { 1132 /* Write this line out */ 1133 (void) fputs(menu_buf, tmp_menu_fp); 1134 } 1135 } else if (strcmp(tok, "default") == 0) { 1136 /* 1137 * Record what 'default' is set to because we might 1138 * need to adjust this upon deleting an entry. 1139 */ 1140 tok = strtok(NULL, BE_WHITE_SPACE); 1141 1142 if (tok != NULL) { 1143 default_entry = atoi(tok); 1144 } 1145 1146 (void) fputs(menu_buf, tmp_menu_fp); 1147 } else if (strcmp(tok, "title") == 0) { 1148 /* 1149 * If we've reached a 'title' line and do_buffer is 1150 * is true, that means we've just buffered an entire 1151 * entry without finding a 'bootfs' directive. We 1152 * need to write that entry out and keep searching. 1153 */ 1154 if (do_buffer) { 1155 for (i = 0; i < nlines; i++) { 1156 (void) fputs(buffer[i], tmp_menu_fp); 1157 free(buffer[i]); 1158 } 1159 free(buffer); 1160 buffer = NULL; 1161 nlines = 0; 1162 } 1163 1164 /* 1165 * Turn writing off and buffering on, and increment 1166 * our entry counter. 1167 */ 1168 write = B_FALSE; 1169 do_buffer = B_TRUE; 1170 entry_cnt++; 1171 1172 /* Buffer this 'title' line */ 1173 if ((buffer = (char **)realloc(buffer, 1174 sizeof (char *)*(nlines + 1))) == NULL) { 1175 ret = BE_ERR_NOMEM; 1176 goto cleanup; 1177 } 1178 if ((buffer[nlines++] = strdup(menu_buf)) == NULL) { 1179 ret = BE_ERR_NOMEM; 1180 goto cleanup; 1181 } 1182 1183 } else if (strcmp(tok, "bootfs") == 0) { 1184 char *bootfs = NULL; 1185 1186 /* 1187 * Found a 'bootfs' line. See if it matches the 1188 * BE we're looking for. 1189 */ 1190 if ((bootfs = strtok(NULL, BE_WHITE_SPACE)) == NULL || 1191 strcmp(bootfs, be_root_ds) != 0) { 1192 /* 1193 * Either there's nothing after the 'bootfs' 1194 * or this is not the BE we're looking for, 1195 * write out the line(s) we've buffered since 1196 * finding the title. 1197 */ 1198 for (i = 0; i < nlines; i++) { 1199 (void) fputs(buffer[i], tmp_menu_fp); 1200 free(buffer[i]); 1201 } 1202 free(buffer); 1203 buffer = NULL; 1204 nlines = 0; 1205 1206 /* 1207 * Turn writing back on, and turn off buffering 1208 * since this isn't the entry we're looking 1209 * for. 1210 */ 1211 write = B_TRUE; 1212 do_buffer = B_FALSE; 1213 1214 /* Write this 'bootfs' line out. */ 1215 (void) fputs(menu_buf, tmp_menu_fp); 1216 } else { 1217 /* 1218 * Found the entry we're looking for. 1219 * Record its entry number, increment the 1220 * number of entries we've deleted, and turn 1221 * writing off. Also, throw away the lines 1222 * we've buffered for this entry so far, we 1223 * don't need them. 1224 */ 1225 entry_del = entry_cnt - 1; 1226 num_entry_del++; 1227 write = B_FALSE; 1228 do_buffer = B_FALSE; 1229 1230 for (i = 0; i < nlines; i++) { 1231 free(buffer[i]); 1232 } 1233 free(buffer); 1234 buffer = NULL; 1235 nlines = 0; 1236 } 1237 } else { 1238 if (do_buffer) { 1239 /* Buffer this line */ 1240 if ((buffer = (char **)realloc(buffer, 1241 sizeof (char *)*(nlines + 1))) == NULL) { 1242 ret = BE_ERR_NOMEM; 1243 goto cleanup; 1244 } 1245 if ((buffer[nlines++] = strdup(menu_buf)) 1246 == NULL) { 1247 ret = BE_ERR_NOMEM; 1248 goto cleanup; 1249 } 1250 } else if (write) { 1251 /* Write this line out */ 1252 (void) fputs(menu_buf, tmp_menu_fp); 1253 } 1254 } 1255 } 1256 1257 (void) fclose(menu_fp); 1258 menu_fp = NULL; 1259 (void) fclose(tmp_menu_fp); 1260 tmp_menu_fp = NULL; 1261 1262 /* Copy the modified menu.lst into place */ 1263 if (rename(tmp_menu, menu) != 0) { 1264 err = errno; 1265 be_print_err(gettext("be_remove_menu: " 1266 "failed to rename file %s to %s: %s\n"), 1267 tmp_menu, menu, strerror(err)); 1268 ret = errno_to_be_err(err); 1269 goto cleanup; 1270 } 1271 free(tmp_menu); 1272 tmp_menu = NULL; 1273 1274 /* 1275 * If we've removed an entry, see if we need to 1276 * adjust the default value in the menu.lst. If the 1277 * entry we've deleted comes before the default entry 1278 * we need to adjust the default value accordingly. 1279 * 1280 * be_has_grub is used here to check to see if this system 1281 * supports grub. 1282 */ 1283 if (be_has_grub() && num_entry_del > 0) { 1284 if (entry_del <= default_entry) { 1285 default_entry = default_entry - num_entry_del; 1286 if (default_entry < 0) 1287 default_entry = 0; 1288 1289 /* 1290 * Adjust the default value by rewriting the 1291 * menu.lst file. This may be overkill, but to 1292 * preserve the location of the 'default' entry 1293 * in the file, we need to do this. 1294 */ 1295 1296 /* Get handle to boot menu file */ 1297 if ((menu_fp = fopen(menu, "r")) == NULL) { 1298 err = errno; 1299 be_print_err(gettext("be_remove_menu: " 1300 "failed to open menu.lst (%s): %s\n"), 1301 menu, strerror(err)); 1302 ret = errno_to_be_err(err); 1303 goto cleanup; 1304 } 1305 1306 /* Create a tmp file for the modified menu.lst */ 1307 tmp_menu_len = strlen(menu) + 7; 1308 if ((tmp_menu = (char *)malloc(tmp_menu_len)) 1309 == NULL) { 1310 be_print_err(gettext("be_remove_menu: " 1311 "malloc failed\n")); 1312 ret = BE_ERR_NOMEM; 1313 goto cleanup; 1314 } 1315 (void) memset(tmp_menu, 0, tmp_menu_len); 1316 (void) strlcpy(tmp_menu, menu, tmp_menu_len); 1317 (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len); 1318 if ((fd = mkstemp(tmp_menu)) == -1) { 1319 err = errno; 1320 be_print_err(gettext("be_remove_menu: " 1321 "mkstemp failed: %s\n"), strerror(err)); 1322 ret = errno_to_be_err(err); 1323 free(tmp_menu); 1324 tmp_menu = NULL; 1325 goto cleanup; 1326 } 1327 if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) { 1328 err = errno; 1329 be_print_err(gettext("be_remove_menu: " 1330 "could not open tmp file for write: %s\n"), 1331 strerror(err)); 1332 (void) close(fd); 1333 ret = errno_to_be_err(err); 1334 goto cleanup; 1335 } 1336 1337 while (fgets(menu_buf, BUFSIZ, menu_fp)) { 1338 char tline [BUFSIZ]; 1339 char *tok = NULL; 1340 1341 (void) strlcpy(tline, menu_buf, sizeof (tline)); 1342 1343 /* Tokenize line */ 1344 tok = strtok(tline, BE_WHITE_SPACE); 1345 1346 if (tok == NULL) { 1347 /* Found empty line, write it out */ 1348 (void) fputs(menu_buf, tmp_menu_fp); 1349 } else if (strcmp(tok, "default") == 0) { 1350 /* Found the default line, adjust it */ 1351 (void) snprintf(tline, sizeof (tline), 1352 "default %d\n", default_entry); 1353 1354 (void) fputs(tline, tmp_menu_fp); 1355 } else { 1356 /* Pass through all other lines */ 1357 (void) fputs(menu_buf, tmp_menu_fp); 1358 } 1359 } 1360 1361 (void) fclose(menu_fp); 1362 menu_fp = NULL; 1363 (void) fclose(tmp_menu_fp); 1364 tmp_menu_fp = NULL; 1365 1366 /* Copy the modified menu.lst into place */ 1367 if (rename(tmp_menu, menu) != 0) { 1368 err = errno; 1369 be_print_err(gettext("be_remove_menu: " 1370 "failed to rename file %s to %s: %s\n"), 1371 tmp_menu, menu, strerror(err)); 1372 ret = errno_to_be_err(err); 1373 goto cleanup; 1374 } 1375 1376 free(tmp_menu); 1377 tmp_menu = NULL; 1378 } 1379 } 1380 1381 /* Set the perms and ownership of the updated file */ 1382 if (chmod(menu, sb.st_mode) != 0) { 1383 err = errno; 1384 be_print_err(gettext("be_remove_menu: " 1385 "failed to chmod %s: %s\n"), menu, strerror(err)); 1386 ret = errno_to_be_err(err); 1387 goto cleanup; 1388 } 1389 if (chown(menu, sb.st_uid, sb.st_gid) != 0) { 1390 err = errno; 1391 be_print_err(gettext("be_remove_menu: " 1392 "failed to chown %s: %s\n"), menu, strerror(err)); 1393 ret = errno_to_be_err(err); 1394 goto cleanup; 1395 } 1396 1397 cleanup: 1398 if (pool_mounted) { 1399 int err = BE_SUCCESS; 1400 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt); 1401 if (ret == BE_SUCCESS) 1402 ret = err; 1403 free(orig_mntpnt); 1404 free(ptmp_mntpnt); 1405 } 1406 ZFS_CLOSE(zhp); 1407 1408 free(buffer); 1409 if (menu_fp != NULL) 1410 (void) fclose(menu_fp); 1411 if (tmp_menu_fp != NULL) 1412 (void) fclose(tmp_menu_fp); 1413 if (tmp_menu != NULL) { 1414 (void) unlink(tmp_menu); 1415 free(tmp_menu); 1416 } 1417 1418 return (ret); 1419 } 1420 1421 /* 1422 * Function: be_default_grub_bootfs 1423 * Description: This function returns the dataset in the default entry of 1424 * the grub menu. If no default entry is found with a valid bootfs 1425 * entry NULL is returned. 1426 * Parameters: 1427 * be_root_pool - This is the name of the root pool where the 1428 * grub menu can be found. 1429 * def_bootfs - This is used to pass back the bootfs string. On 1430 * error NULL is returned here. 1431 * Returns: 1432 * Success - BE_SUCCESS is returned. 1433 * Failure - a be_errno_t is returned. 1434 * Scope: 1435 * Semi-private (library wide use only) 1436 */ 1437 int 1438 be_default_grub_bootfs(const char *be_root_pool, char **def_bootfs) 1439 { 1440 zfs_handle_t *zhp = NULL; 1441 char grub_file[MAXPATHLEN]; 1442 FILE *menu_fp; 1443 char line[BUFSIZ]; 1444 char *pool_mntpnt = NULL; 1445 char *ptmp_mntpnt = NULL; 1446 char *orig_mntpnt = NULL; 1447 int default_entry = 0, entries = 0; 1448 int found_default = 0; 1449 int ret = BE_SUCCESS; 1450 boolean_t pool_mounted = B_FALSE; 1451 1452 errno = 0; 1453 1454 /* 1455 * Check to see if this system supports grub 1456 */ 1457 if (!be_has_grub()) { 1458 be_print_err(gettext("be_default_grub_bootfs: operation " 1459 "not supported on this architecture\n")); 1460 return (BE_ERR_NOTSUP); 1461 } 1462 1463 *def_bootfs = NULL; 1464 1465 /* Get handle to pool dataset */ 1466 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) { 1467 be_print_err(gettext("be_default_grub_bootfs: " 1468 "failed to open pool dataset for %s: %s"), 1469 be_root_pool, libzfs_error_description(g_zfs)); 1470 return (zfs_err_to_be_err(g_zfs)); 1471 } 1472 1473 /* 1474 * Check to see if the pool's dataset is mounted. If it isn't we'll 1475 * attempt to mount it. 1476 */ 1477 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt, 1478 &pool_mounted)) != BE_SUCCESS) { 1479 be_print_err(gettext("be_default_grub_bootfs: pool dataset " 1480 "(%s) could not be mounted\n"), be_root_pool); 1481 ZFS_CLOSE(zhp); 1482 return (ret); 1483 } 1484 1485 /* 1486 * Get the mountpoint for the root pool dataset. 1487 */ 1488 if (!zfs_is_mounted(zhp, &pool_mntpnt)) { 1489 be_print_err(gettext("be_default_grub_bootfs: failed " 1490 "to get mount point for the root pool. Can't set " 1491 "the default BE in the grub menu.\n")); 1492 ret = BE_ERR_NO_MENU; 1493 goto cleanup; 1494 } 1495 1496 (void) snprintf(grub_file, MAXPATHLEN, "%s%s", 1497 pool_mntpnt, BE_GRUB_MENU); 1498 1499 if ((ret = be_open_menu((char *)be_root_pool, grub_file, 1500 &menu_fp, "r", B_FALSE)) != BE_SUCCESS) { 1501 goto cleanup; 1502 } else if (menu_fp == NULL) { 1503 ret = BE_ERR_NO_MENU; 1504 goto cleanup; 1505 } 1506 1507 free(pool_mntpnt); 1508 pool_mntpnt = NULL; 1509 1510 while (fgets(line, BUFSIZ, menu_fp)) { 1511 char *tok = strtok(line, BE_WHITE_SPACE); 1512 1513 if (tok != NULL && tok[0] != '#') { 1514 if (!found_default) { 1515 if (strcmp(tok, "default") == 0) { 1516 tok = strtok(NULL, BE_WHITE_SPACE); 1517 if (tok != NULL) { 1518 default_entry = atoi(tok); 1519 rewind(menu_fp); 1520 found_default = 1; 1521 } 1522 } 1523 continue; 1524 } 1525 if (strcmp(tok, "title") == 0) { 1526 entries++; 1527 } else if (default_entry == entries - 1) { 1528 if (strcmp(tok, "bootfs") == 0) { 1529 tok = strtok(NULL, BE_WHITE_SPACE); 1530 (void) fclose(menu_fp); 1531 1532 if (tok == NULL) { 1533 ret = BE_SUCCESS; 1534 goto cleanup; 1535 } 1536 1537 if ((*def_bootfs = strdup(tok)) != 1538 NULL) { 1539 ret = BE_SUCCESS; 1540 goto cleanup; 1541 } 1542 be_print_err(gettext( 1543 "be_default_grub_bootfs: " 1544 "memory allocation failed\n")); 1545 ret = BE_ERR_NOMEM; 1546 goto cleanup; 1547 } 1548 } else if (default_entry < entries - 1) { 1549 /* 1550 * no bootfs entry for the default entry. 1551 */ 1552 break; 1553 } 1554 } 1555 } 1556 (void) fclose(menu_fp); 1557 1558 cleanup: 1559 if (pool_mounted) { 1560 int err = BE_SUCCESS; 1561 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt); 1562 if (ret == BE_SUCCESS) 1563 ret = err; 1564 free(orig_mntpnt); 1565 free(ptmp_mntpnt); 1566 } 1567 ZFS_CLOSE(zhp); 1568 return (ret); 1569 } 1570 1571 /* 1572 * Function: be_change_grub_default 1573 * Description: This function takes two parameters. These are the name of 1574 * the BE we want to have as the default booted in the grub 1575 * menu and the root pool where the path to the grub menu exists. 1576 * The code takes this and finds the BE's entry in the grub menu 1577 * and changes the default entry to point to that entry in the 1578 * list. 1579 * Parameters: 1580 * be_name - This is the name of the BE wanted as the default 1581 * for the next boot. 1582 * be_root_pool - This is the name of the root pool where the 1583 * grub menu can be found. 1584 * Returns: 1585 * BE_SUCCESS - Success 1586 * be_errno_t - Failure 1587 * Scope: 1588 * Semi-private (library wide use only) 1589 */ 1590 int 1591 be_change_grub_default(char *be_name, char *be_root_pool) 1592 { 1593 zfs_handle_t *zhp = NULL; 1594 char grub_file[MAXPATHLEN]; 1595 char *temp_grub = NULL; 1596 char *pool_mntpnt = NULL; 1597 char *ptmp_mntpnt = NULL; 1598 char *orig_mntpnt = NULL; 1599 char line[BUFSIZ]; 1600 char temp_line[BUFSIZ]; 1601 char be_root_ds[MAXPATHLEN]; 1602 FILE *grub_fp = NULL; 1603 FILE *temp_fp = NULL; 1604 struct stat sb; 1605 int temp_grub_len = 0; 1606 int fd, entries = 0; 1607 int err = 0; 1608 int ret = BE_SUCCESS; 1609 boolean_t found_default = B_FALSE; 1610 boolean_t pool_mounted = B_FALSE; 1611 1612 errno = 0; 1613 1614 /* 1615 * Check to see if this system supports grub 1616 */ 1617 if (!be_has_grub()) { 1618 be_print_err(gettext("be_change_grub_default: operation " 1619 "not supported on this architecture\n")); 1620 return (BE_ERR_NOTSUP); 1621 } 1622 1623 /* Generate string for BE's root dataset */ 1624 if ((ret = be_make_root_ds(be_root_pool, be_name, be_root_ds, 1625 sizeof (be_root_ds))) != BE_SUCCESS) { 1626 be_print_err(gettext("%s: failed to get BE container dataset " 1627 "for %s/%s\n"), __func__, be_root_pool, be_name); 1628 return (ret); 1629 } 1630 1631 /* Get handle to pool dataset */ 1632 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) { 1633 be_print_err(gettext("be_change_grub_default: " 1634 "failed to open pool dataset for %s: %s"), 1635 be_root_pool, libzfs_error_description(g_zfs)); 1636 return (zfs_err_to_be_err(g_zfs)); 1637 } 1638 1639 /* 1640 * Check to see if the pool's dataset is mounted. If it isn't we'll 1641 * attempt to mount it. 1642 */ 1643 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt, 1644 &pool_mounted)) != BE_SUCCESS) { 1645 be_print_err(gettext("be_change_grub_default: pool dataset " 1646 "(%s) could not be mounted\n"), be_root_pool); 1647 ZFS_CLOSE(zhp); 1648 return (ret); 1649 } 1650 1651 /* 1652 * Get the mountpoint for the root pool dataset. 1653 */ 1654 if (!zfs_is_mounted(zhp, &pool_mntpnt)) { 1655 be_print_err(gettext("be_change_grub_default: pool " 1656 "dataset (%s) is not mounted. Can't set " 1657 "the default BE in the grub menu.\n"), be_root_pool); 1658 ret = BE_ERR_NO_MENU; 1659 goto cleanup; 1660 } 1661 1662 (void) snprintf(grub_file, MAXPATHLEN, "%s%s", 1663 pool_mntpnt, BE_GRUB_MENU); 1664 1665 if ((ret = be_open_menu(be_root_pool, grub_file, 1666 &grub_fp, "r+", B_TRUE)) != BE_SUCCESS) { 1667 goto cleanup; 1668 } else if (grub_fp == NULL) { 1669 ret = BE_ERR_NO_MENU; 1670 goto cleanup; 1671 } 1672 1673 free(pool_mntpnt); 1674 pool_mntpnt = NULL; 1675 1676 /* Grab the stats of the original menu file */ 1677 if (stat(grub_file, &sb) != 0) { 1678 err = errno; 1679 be_print_err(gettext("be_change_grub_default: " 1680 "failed to stat file %s: %s\n"), grub_file, strerror(err)); 1681 ret = errno_to_be_err(err); 1682 goto cleanup; 1683 } 1684 1685 /* Create a tmp file for the modified menu.lst */ 1686 temp_grub_len = strlen(grub_file) + 7; 1687 if ((temp_grub = (char *)malloc(temp_grub_len)) == NULL) { 1688 be_print_err(gettext("be_change_grub_default: " 1689 "malloc failed\n")); 1690 ret = BE_ERR_NOMEM; 1691 goto cleanup; 1692 } 1693 (void) memset(temp_grub, 0, temp_grub_len); 1694 (void) strlcpy(temp_grub, grub_file, temp_grub_len); 1695 (void) strlcat(temp_grub, "XXXXXX", temp_grub_len); 1696 if ((fd = mkstemp(temp_grub)) == -1) { 1697 err = errno; 1698 be_print_err(gettext("be_change_grub_default: " 1699 "mkstemp failed: %s\n"), strerror(err)); 1700 ret = errno_to_be_err(err); 1701 free(temp_grub); 1702 temp_grub = NULL; 1703 goto cleanup; 1704 } 1705 if ((temp_fp = fdopen(fd, "w")) == NULL) { 1706 err = errno; 1707 be_print_err(gettext("be_change_grub_default: " 1708 "failed to open %s file: %s\n"), 1709 temp_grub, strerror(err)); 1710 (void) close(fd); 1711 ret = errno_to_be_err(err); 1712 goto cleanup; 1713 } 1714 1715 while (fgets(line, BUFSIZ, grub_fp)) { 1716 char *tok = strtok(line, BE_WHITE_SPACE); 1717 1718 if (tok == NULL || tok[0] == '#') { 1719 continue; 1720 } else if (strcmp(tok, "title") == 0) { 1721 entries++; 1722 continue; 1723 } else if (strcmp(tok, "bootfs") == 0) { 1724 char *bootfs = strtok(NULL, BE_WHITE_SPACE); 1725 if (bootfs == NULL) 1726 continue; 1727 1728 if (strcmp(bootfs, be_root_ds) == 0) { 1729 found_default = B_TRUE; 1730 break; 1731 } 1732 } 1733 } 1734 1735 if (!found_default) { 1736 be_print_err(gettext("be_change_grub_default: failed " 1737 "to find entry for %s in the grub menu\n"), 1738 be_name); 1739 ret = BE_ERR_BE_NOENT; 1740 goto cleanup; 1741 } 1742 1743 rewind(grub_fp); 1744 1745 while (fgets(line, BUFSIZ, grub_fp)) { 1746 char *tok = NULL; 1747 1748 (void) strncpy(temp_line, line, BUFSIZ); 1749 1750 if ((tok = strtok(temp_line, BE_WHITE_SPACE)) != NULL && 1751 strcmp(tok, "default") == 0) { 1752 (void) snprintf(temp_line, BUFSIZ, "default %d\n", 1753 entries - 1 >= 0 ? entries - 1 : 0); 1754 (void) fputs(temp_line, temp_fp); 1755 } else { 1756 (void) fputs(line, temp_fp); 1757 } 1758 } 1759 1760 (void) fclose(grub_fp); 1761 grub_fp = NULL; 1762 (void) fclose(temp_fp); 1763 temp_fp = NULL; 1764 1765 if (rename(temp_grub, grub_file) != 0) { 1766 err = errno; 1767 be_print_err(gettext("be_change_grub_default: " 1768 "failed to rename file %s to %s: %s\n"), 1769 temp_grub, grub_file, strerror(err)); 1770 ret = errno_to_be_err(err); 1771 goto cleanup; 1772 } 1773 free(temp_grub); 1774 temp_grub = NULL; 1775 1776 /* Set the perms and ownership of the updated file */ 1777 if (chmod(grub_file, sb.st_mode) != 0) { 1778 err = errno; 1779 be_print_err(gettext("be_change_grub_default: " 1780 "failed to chmod %s: %s\n"), grub_file, strerror(err)); 1781 ret = errno_to_be_err(err); 1782 goto cleanup; 1783 } 1784 if (chown(grub_file, sb.st_uid, sb.st_gid) != 0) { 1785 err = errno; 1786 be_print_err(gettext("be_change_grub_default: " 1787 "failed to chown %s: %s\n"), grub_file, strerror(err)); 1788 ret = errno_to_be_err(err); 1789 } 1790 1791 cleanup: 1792 if (pool_mounted) { 1793 int err = BE_SUCCESS; 1794 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt); 1795 if (ret == BE_SUCCESS) 1796 ret = err; 1797 free(orig_mntpnt); 1798 free(ptmp_mntpnt); 1799 } 1800 ZFS_CLOSE(zhp); 1801 if (grub_fp != NULL) 1802 (void) fclose(grub_fp); 1803 if (temp_fp != NULL) 1804 (void) fclose(temp_fp); 1805 if (temp_grub != NULL) { 1806 (void) unlink(temp_grub); 1807 free(temp_grub); 1808 } 1809 1810 return (ret); 1811 } 1812 1813 /* 1814 * Function: be_update_menu 1815 * Description: This function is used by be_rename to change the BE name in 1816 * an existing entry in the grub menu to the new name of the BE. 1817 * Parameters: 1818 * be_orig_name - the original name of the BE 1819 * be_new_name - the new name the BE is being renameed to. 1820 * be_root_pool - The pool which contains the grub menu 1821 * boot_pool - the pool where the BE is, if different than 1822 * the pool containing the boot menu. If this is 1823 * NULL it will be set to be_root_pool. 1824 * Returns: 1825 * BE_SUCCESS - Success 1826 * be_errno_t - Failure 1827 * Scope: 1828 * Semi-private (library wide use only) 1829 */ 1830 int 1831 be_update_menu(char *be_orig_name, char *be_new_name, char *be_root_pool, 1832 char *boot_pool) 1833 { 1834 zfs_handle_t *zhp = NULL; 1835 char menu_file[MAXPATHLEN]; 1836 char be_root_ds[MAXPATHLEN]; 1837 char be_new_root_ds[MAXPATHLEN]; 1838 char line[BUFSIZ]; 1839 char *pool_mntpnt = NULL; 1840 char *ptmp_mntpnt = NULL; 1841 char *orig_mntpnt = NULL; 1842 char *temp_menu = NULL; 1843 FILE *menu_fp = NULL; 1844 FILE *new_fp = NULL; 1845 struct stat sb; 1846 int temp_menu_len = 0; 1847 int tmp_fd; 1848 int ret = BE_SUCCESS; 1849 int err = 0; 1850 boolean_t pool_mounted = B_FALSE; 1851 1852 errno = 0; 1853 1854 if (boot_pool == NULL) 1855 boot_pool = be_root_pool; 1856 1857 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) { 1858 be_print_err(gettext("be_update_menu: failed to open " 1859 "pool dataset for %s: %s\n"), be_root_pool, 1860 libzfs_error_description(g_zfs)); 1861 return (zfs_err_to_be_err(g_zfs)); 1862 } 1863 1864 /* 1865 * Check to see if the pool's dataset is mounted. If it isn't we'll 1866 * attempt to mount it. 1867 */ 1868 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt, 1869 &pool_mounted)) != BE_SUCCESS) { 1870 be_print_err(gettext("be_update_menu: pool dataset " 1871 "(%s) could not be mounted\n"), be_root_pool); 1872 ZFS_CLOSE(zhp); 1873 return (ret); 1874 } 1875 1876 /* 1877 * Get the mountpoint for the root pool dataset. 1878 */ 1879 if (!zfs_is_mounted(zhp, &pool_mntpnt)) { 1880 be_print_err(gettext("be_update_menu: failed " 1881 "to get mount point for the root pool. Can't set " 1882 "the default BE in the grub menu.\n")); 1883 ret = BE_ERR_NO_MENU; 1884 goto cleanup; 1885 } 1886 1887 /* 1888 * Check to see if this system supports grub 1889 */ 1890 if (be_has_grub()) { 1891 (void) snprintf(menu_file, sizeof (menu_file), 1892 "%s%s", pool_mntpnt, BE_GRUB_MENU); 1893 } else { 1894 (void) snprintf(menu_file, sizeof (menu_file), 1895 "%s%s", pool_mntpnt, BE_SPARC_MENU); 1896 } 1897 1898 if ((ret = be_make_root_ds(be_root_pool, be_orig_name, be_root_ds, 1899 sizeof (be_root_ds))) != BE_SUCCESS) { 1900 be_print_err(gettext("%s: failed to get BE container dataset " 1901 "for %s/%s\n"), __func__, be_root_pool, be_orig_name); 1902 goto cleanup; 1903 } 1904 if ((ret = be_make_root_ds(be_root_pool, be_new_name, be_new_root_ds, 1905 sizeof (be_new_root_ds))) != BE_SUCCESS) { 1906 be_print_err(gettext("%s: failed to get BE container dataset " 1907 "for %s/%s\n"), __func__, be_root_pool, be_new_name); 1908 goto cleanup; 1909 } 1910 1911 if ((ret = be_open_menu(be_root_pool, menu_file, 1912 &menu_fp, "r", B_TRUE)) != BE_SUCCESS) { 1913 goto cleanup; 1914 } else if (menu_fp == NULL) { 1915 ret = BE_ERR_NO_MENU; 1916 goto cleanup; 1917 } 1918 1919 free(pool_mntpnt); 1920 pool_mntpnt = NULL; 1921 1922 /* Grab the stat of the original menu file */ 1923 if (stat(menu_file, &sb) != 0) { 1924 err = errno; 1925 be_print_err(gettext("be_update_menu: " 1926 "failed to stat file %s: %s\n"), menu_file, strerror(err)); 1927 (void) fclose(menu_fp); 1928 ret = errno_to_be_err(err); 1929 goto cleanup; 1930 } 1931 1932 /* Create tmp file for modified menu.lst */ 1933 temp_menu_len = strlen(menu_file) + 7; 1934 if ((temp_menu = (char *)malloc(temp_menu_len)) 1935 == NULL) { 1936 be_print_err(gettext("be_update_menu: " 1937 "malloc failed\n")); 1938 (void) fclose(menu_fp); 1939 ret = BE_ERR_NOMEM; 1940 goto cleanup; 1941 } 1942 (void) memset(temp_menu, 0, temp_menu_len); 1943 (void) strlcpy(temp_menu, menu_file, temp_menu_len); 1944 (void) strlcat(temp_menu, "XXXXXX", temp_menu_len); 1945 if ((tmp_fd = mkstemp(temp_menu)) == -1) { 1946 err = errno; 1947 be_print_err(gettext("be_update_menu: " 1948 "mkstemp failed: %s\n"), strerror(err)); 1949 (void) fclose(menu_fp); 1950 free(temp_menu); 1951 ret = errno_to_be_err(err); 1952 goto cleanup; 1953 } 1954 if ((new_fp = fdopen(tmp_fd, "w")) == NULL) { 1955 err = errno; 1956 be_print_err(gettext("be_update_menu: " 1957 "fdopen failed: %s\n"), strerror(err)); 1958 (void) close(tmp_fd); 1959 (void) fclose(menu_fp); 1960 free(temp_menu); 1961 ret = errno_to_be_err(err); 1962 goto cleanup; 1963 } 1964 1965 while (fgets(line, BUFSIZ, menu_fp)) { 1966 char tline[BUFSIZ]; 1967 char new_line[BUFSIZ]; 1968 char *c = NULL; 1969 1970 (void) strlcpy(tline, line, sizeof (tline)); 1971 1972 /* Tokenize line */ 1973 c = strtok(tline, BE_WHITE_SPACE); 1974 1975 if (c == NULL) { 1976 /* Found empty line, write it out. */ 1977 (void) fputs(line, new_fp); 1978 } else if (c[0] == '#') { 1979 /* Found a comment line, write it out. */ 1980 (void) fputs(line, new_fp); 1981 } else if (strcmp(c, "title") == 0) { 1982 char *name = NULL; 1983 char *desc = NULL; 1984 1985 /* 1986 * Found a 'title' line, parse out BE name or 1987 * the description. 1988 */ 1989 name = strtok(NULL, BE_WHITE_SPACE); 1990 1991 if (name == NULL) { 1992 /* 1993 * Nothing after 'title', just push 1994 * this line through 1995 */ 1996 (void) fputs(line, new_fp); 1997 } else { 1998 /* 1999 * Grab the remainder of the title which 2000 * could be a multi worded description 2001 */ 2002 desc = strtok(NULL, "\n"); 2003 2004 if (strcmp(name, be_orig_name) == 0) { 2005 /* 2006 * The first token of the title is 2007 * the old BE name, replace it with 2008 * the new one, and write it out 2009 * along with the remainder of 2010 * description if there is one. 2011 */ 2012 if (desc) { 2013 (void) snprintf(new_line, 2014 sizeof (new_line), 2015 "title %s %s\n", 2016 be_new_name, desc); 2017 } else { 2018 (void) snprintf(new_line, 2019 sizeof (new_line), 2020 "title %s\n", be_new_name); 2021 } 2022 2023 (void) fputs(new_line, new_fp); 2024 } else { 2025 (void) fputs(line, new_fp); 2026 } 2027 } 2028 } else if (strcmp(c, "bootfs") == 0) { 2029 /* 2030 * Found a 'bootfs' line, parse out the BE root 2031 * dataset value. 2032 */ 2033 char *root_ds = strtok(NULL, BE_WHITE_SPACE); 2034 2035 if (root_ds == NULL) { 2036 /* 2037 * Nothing after 'bootfs', just push 2038 * this line through 2039 */ 2040 (void) fputs(line, new_fp); 2041 } else { 2042 /* 2043 * If this bootfs is the one we're renaming, 2044 * write out the new root dataset value 2045 */ 2046 if (strcmp(root_ds, be_root_ds) == 0) { 2047 (void) snprintf(new_line, 2048 sizeof (new_line), "bootfs %s\n", 2049 be_new_root_ds); 2050 2051 (void) fputs(new_line, new_fp); 2052 } else { 2053 (void) fputs(line, new_fp); 2054 } 2055 } 2056 } else { 2057 /* 2058 * Found some other line we don't care 2059 * about, write it out. 2060 */ 2061 (void) fputs(line, new_fp); 2062 } 2063 } 2064 2065 (void) fclose(menu_fp); 2066 (void) fclose(new_fp); 2067 (void) close(tmp_fd); 2068 2069 if (rename(temp_menu, menu_file) != 0) { 2070 err = errno; 2071 be_print_err(gettext("be_update_menu: " 2072 "failed to rename file %s to %s: %s\n"), 2073 temp_menu, menu_file, strerror(err)); 2074 ret = errno_to_be_err(err); 2075 } 2076 free(temp_menu); 2077 2078 /* Set the perms and ownership of the updated file */ 2079 if (chmod(menu_file, sb.st_mode) != 0) { 2080 err = errno; 2081 be_print_err(gettext("be_update_menu: " 2082 "failed to chmod %s: %s\n"), menu_file, strerror(err)); 2083 ret = errno_to_be_err(err); 2084 goto cleanup; 2085 } 2086 if (chown(menu_file, sb.st_uid, sb.st_gid) != 0) { 2087 err = errno; 2088 be_print_err(gettext("be_update_menu: " 2089 "failed to chown %s: %s\n"), menu_file, strerror(err)); 2090 ret = errno_to_be_err(err); 2091 } 2092 2093 cleanup: 2094 if (pool_mounted) { 2095 int err = BE_SUCCESS; 2096 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt); 2097 if (ret == BE_SUCCESS) 2098 ret = err; 2099 free(orig_mntpnt); 2100 free(ptmp_mntpnt); 2101 } 2102 ZFS_CLOSE(zhp); 2103 return (ret); 2104 } 2105 2106 /* 2107 * Function: be_has_menu_entry 2108 * Description: Checks to see if the BEs root dataset has an entry in the grub 2109 * menu. 2110 * Parameters: 2111 * be_dataset - The root dataset of the BE 2112 * be_root_pool - The pool which contains the boot menu 2113 * entry - A pointer the the entry number of the BE if found. 2114 * Returns: 2115 * B_TRUE - Success 2116 * B_FALSE - Failure 2117 * Scope: 2118 * Semi-private (library wide use only) 2119 */ 2120 boolean_t 2121 be_has_menu_entry(char *be_dataset, char *be_root_pool, int *entry) 2122 { 2123 zfs_handle_t *zhp = NULL; 2124 char menu_file[MAXPATHLEN]; 2125 FILE *menu_fp = NULL; 2126 char line[BUFSIZ]; 2127 char *last; 2128 char *rpool_mntpnt = NULL; 2129 char *ptmp_mntpnt = NULL; 2130 char *orig_mntpnt = NULL; 2131 int ent_num = 0; 2132 boolean_t ret = 0; 2133 boolean_t pool_mounted = B_FALSE; 2134 2135 2136 /* 2137 * Check to see if this system supports grub 2138 */ 2139 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) { 2140 be_print_err(gettext("be_has_menu_entry: failed to open " 2141 "pool dataset for %s: %s\n"), be_root_pool, 2142 libzfs_error_description(g_zfs)); 2143 return (B_FALSE); 2144 } 2145 2146 /* 2147 * Check to see if the pool's dataset is mounted. If it isn't we'll 2148 * attempt to mount it. 2149 */ 2150 if (be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt, 2151 &pool_mounted) != 0) { 2152 be_print_err(gettext("be_has_menu_entry: pool dataset " 2153 "(%s) could not be mounted\n"), be_root_pool); 2154 ZFS_CLOSE(zhp); 2155 return (B_FALSE); 2156 } 2157 2158 /* 2159 * Get the mountpoint for the root pool dataset. 2160 */ 2161 if (!zfs_is_mounted(zhp, &rpool_mntpnt)) { 2162 be_print_err(gettext("be_has_menu_entry: pool " 2163 "dataset (%s) is not mounted. Can't set " 2164 "the default BE in the grub menu.\n"), be_root_pool); 2165 ret = B_FALSE; 2166 goto cleanup; 2167 } 2168 2169 if (be_has_grub()) { 2170 (void) snprintf(menu_file, MAXPATHLEN, "/%s%s", 2171 rpool_mntpnt, BE_GRUB_MENU); 2172 } else { 2173 (void) snprintf(menu_file, MAXPATHLEN, "/%s%s", 2174 rpool_mntpnt, BE_SPARC_MENU); 2175 } 2176 2177 if (be_open_menu(be_root_pool, menu_file, &menu_fp, "r", 2178 B_FALSE) != 0) { 2179 ret = B_FALSE; 2180 goto cleanup; 2181 } else if (menu_fp == NULL) { 2182 ret = B_FALSE; 2183 goto cleanup; 2184 } 2185 2186 free(rpool_mntpnt); 2187 rpool_mntpnt = NULL; 2188 2189 while (fgets(line, BUFSIZ, menu_fp)) { 2190 char *tok = strtok_r(line, BE_WHITE_SPACE, &last); 2191 2192 if (tok != NULL && tok[0] != '#') { 2193 if (strcmp(tok, "bootfs") == 0) { 2194 tok = strtok_r(last, BE_WHITE_SPACE, &last); 2195 if (tok != NULL && strcmp(tok, 2196 be_dataset) == 0) { 2197 /* 2198 * The entry number needs to be 2199 * decremented here because the title 2200 * will always be the first line for 2201 * an entry. Because of this we'll 2202 * always be off by one entry when we 2203 * check for bootfs. 2204 */ 2205 *entry = ent_num - 1; 2206 ret = B_TRUE; 2207 goto cleanup; 2208 } 2209 } else if (strcmp(tok, "title") == 0) 2210 ent_num++; 2211 } 2212 } 2213 2214 cleanup: 2215 if (pool_mounted) { 2216 (void) be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt); 2217 free(orig_mntpnt); 2218 free(ptmp_mntpnt); 2219 } 2220 ZFS_CLOSE(zhp); 2221 (void) fclose(menu_fp); 2222 return (ret); 2223 } 2224 2225 /* 2226 * Function: be_update_vfstab 2227 * Description: This function digs into a BE's vfstab and updates all 2228 * entries with file systems listed in be_fs_list_data_t. 2229 * The entry's root container dataset and be_name will be 2230 * updated with the parameters passed in. 2231 * Parameters: 2232 * be_name - name of BE to update 2233 * old_rc_loc - dataset under which the root container dataset 2234 * of the old BE resides in. 2235 * new_rc_loc - dataset under which the root container dataset 2236 * of the new BE resides in. 2237 * fld - be_fs_list_data_t pointer providing the list of 2238 * file systems to look for in vfstab. 2239 * mountpoint - directory of where BE is currently mounted. 2240 * If NULL, then BE is not currently mounted. 2241 * Returns: 2242 * BE_SUCCESS - Success 2243 * be_errno_t - Failure 2244 * Scope: 2245 * Semi-private (library wide use only) 2246 */ 2247 int 2248 be_update_vfstab(char *be_name, char *old_rc_loc, char *new_rc_loc, 2249 be_fs_list_data_t *fld, char *mountpoint) 2250 { 2251 char *tmp_mountpoint = NULL; 2252 char alt_vfstab[MAXPATHLEN]; 2253 int ret = BE_SUCCESS, err = BE_SUCCESS; 2254 2255 if (fld == NULL || fld->fs_list == NULL || fld->fs_num == 0) 2256 return (BE_SUCCESS); 2257 2258 /* If BE not already mounted, mount the BE */ 2259 if (mountpoint == NULL) { 2260 if ((ret = _be_mount(be_name, &tmp_mountpoint, 2261 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) { 2262 be_print_err(gettext("be_update_vfstab: " 2263 "failed to mount BE (%s)\n"), be_name); 2264 return (ret); 2265 } 2266 } else { 2267 tmp_mountpoint = mountpoint; 2268 } 2269 2270 /* Get string for vfstab in the mounted BE. */ 2271 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab", 2272 tmp_mountpoint); 2273 2274 /* Update the vfstab */ 2275 ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc, 2276 fld); 2277 2278 /* Unmount BE if we mounted it */ 2279 if (mountpoint == NULL) { 2280 if ((err = _be_unmount(be_name, 0)) == BE_SUCCESS) { 2281 /* Remove temporary mountpoint */ 2282 (void) rmdir(tmp_mountpoint); 2283 } else { 2284 be_print_err(gettext("be_update_vfstab: " 2285 "failed to unmount BE %s mounted at %s\n"), 2286 be_name, tmp_mountpoint); 2287 if (ret == BE_SUCCESS) 2288 ret = err; 2289 } 2290 2291 free(tmp_mountpoint); 2292 } 2293 2294 return (ret); 2295 } 2296 2297 /* 2298 * Function: be_update_zone_vfstab 2299 * Description: This function digs into a zone BE's vfstab and updates all 2300 * entries with file systems listed in be_fs_list_data_t. 2301 * The entry's root container dataset and be_name will be 2302 * updated with the parameters passed in. 2303 * Parameters: 2304 * zhp - zfs_handle_t pointer to zone root dataset. 2305 * be_name - name of zone BE to update 2306 * old_rc_loc - dataset under which the root container dataset 2307 * of the old zone BE resides in. 2308 * new_rc_loc - dataset under which the root container dataset 2309 * of the new zone BE resides in. 2310 * fld - be_fs_list_data_t pointer providing the list of 2311 * file systems to look for in vfstab. 2312 * Returns: 2313 * BE_SUCCESS - Success 2314 * be_errno_t - Failure 2315 * Scope: 2316 * Semi-private (library wide use only) 2317 */ 2318 int 2319 be_update_zone_vfstab(zfs_handle_t *zhp, char *be_name, char *old_rc_loc, 2320 char *new_rc_loc, be_fs_list_data_t *fld) 2321 { 2322 be_mount_data_t md = { 0 }; 2323 be_unmount_data_t ud = { 0 }; 2324 char alt_vfstab[MAXPATHLEN]; 2325 boolean_t mounted_here = B_FALSE; 2326 int ret = BE_SUCCESS; 2327 2328 /* 2329 * If zone root not already mounted, mount it at a 2330 * temporary location. 2331 */ 2332 if (!zfs_is_mounted(zhp, &md.altroot)) { 2333 /* Generate temporary mountpoint to mount zone root */ 2334 if ((ret = be_make_tmp_mountpoint(&md.altroot)) != BE_SUCCESS) { 2335 be_print_err(gettext("be_update_zone_vfstab: " 2336 "failed to make temporary mountpoint to " 2337 "mount zone root\n")); 2338 return (ret); 2339 } 2340 2341 if (be_mount_zone_root(zhp, &md) != BE_SUCCESS) { 2342 be_print_err(gettext("be_update_zone_vfstab: " 2343 "failed to mount zone root %s\n"), 2344 zfs_get_name(zhp)); 2345 free(md.altroot); 2346 return (BE_ERR_MOUNT_ZONEROOT); 2347 } 2348 mounted_here = B_TRUE; 2349 } 2350 2351 /* Get string from vfstab in the mounted zone BE */ 2352 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab", 2353 md.altroot); 2354 2355 /* Update the vfstab */ 2356 ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc, 2357 fld); 2358 2359 /* Unmount zone root if we mounted it */ 2360 if (mounted_here) { 2361 ud.force = B_TRUE; 2362 2363 if (be_unmount_zone_root(zhp, &ud) == BE_SUCCESS) { 2364 /* Remove the temporary mountpoint */ 2365 (void) rmdir(md.altroot); 2366 } else { 2367 be_print_err(gettext("be_update_zone_vfstab: " 2368 "failed to unmount zone root %s from %s\n"), 2369 zfs_get_name(zhp), md.altroot); 2370 if (ret == 0) 2371 ret = BE_ERR_UMOUNT_ZONEROOT; 2372 } 2373 } 2374 2375 free(md.altroot); 2376 return (ret); 2377 } 2378 2379 /* 2380 * Function: be_auto_snap_name 2381 * Description: Generate an auto snapshot name constructed based on the 2382 * current date and time. The auto snapshot name is of the form: 2383 * 2384 * <date>-<time> 2385 * 2386 * where <date> is in ISO standard format, so the resultant name 2387 * is of the form: 2388 * 2389 * %Y-%m-%d-%H:%M:%S 2390 * 2391 * Parameters: 2392 * None 2393 * Returns: 2394 * Success - pointer to auto generated snapshot name. The name 2395 * is allocated in heap storage so the caller is 2396 * responsible for free'ing the name. 2397 * Failure - NULL 2398 * Scope: 2399 * Semi-private (library wide use only) 2400 */ 2401 char * 2402 be_auto_snap_name(void) 2403 { 2404 time_t utc_tm = 0; 2405 struct tm *gmt_tm = NULL; 2406 char gmt_time_str[64]; 2407 char *auto_snap_name = NULL; 2408 2409 if (time(&utc_tm) == -1) { 2410 be_print_err(gettext("be_auto_snap_name: time() failed\n")); 2411 return (NULL); 2412 } 2413 2414 if ((gmt_tm = gmtime(&utc_tm)) == NULL) { 2415 be_print_err(gettext("be_auto_snap_name: gmtime() failed\n")); 2416 return (NULL); 2417 } 2418 2419 (void) strftime(gmt_time_str, sizeof (gmt_time_str), "%F-%T", gmt_tm); 2420 2421 if ((auto_snap_name = strdup(gmt_time_str)) == NULL) { 2422 be_print_err(gettext("be_auto_snap_name: " 2423 "memory allocation failed\n")); 2424 return (NULL); 2425 } 2426 2427 return (auto_snap_name); 2428 } 2429 2430 /* 2431 * Function: be_auto_be_name 2432 * Description: Generate an auto BE name constructed based on the BE name 2433 * of the original BE being cloned. 2434 * Parameters: 2435 * obe_name - name of the original BE being cloned. 2436 * Returns: 2437 * Success - pointer to auto generated BE name. The name 2438 * is allocated in heap storage so the caller is 2439 * responsible for free'ing the name. 2440 * Failure - NULL 2441 * Scope: 2442 * Semi-private (library wide use only) 2443 */ 2444 char * 2445 be_auto_be_name(char *obe_name) 2446 { 2447 return (be_get_auto_name(obe_name, NULL, B_FALSE)); 2448 } 2449 2450 /* 2451 * Function: be_auto_zone_be_name 2452 * Description: Generate an auto BE name for a zone constructed based on 2453 * the BE name of the original zone BE being cloned. 2454 * Parameters: 2455 * container_ds - container dataset for the zone. 2456 * zbe_name - name of the original zone BE being cloned. 2457 * Returns: 2458 * Success - pointer to auto generated BE name. The name 2459 * is allocated in heap storage so the caller is 2460 * responsible for free'ing the name. 2461 * Failure - NULL 2462 * Scope: 2463 * Semi-private (library wide use only) 2464 */ 2465 char * 2466 be_auto_zone_be_name(char *container_ds, char *zbe_name) 2467 { 2468 return (be_get_auto_name(zbe_name, container_ds, B_TRUE)); 2469 } 2470 2471 /* 2472 * Function: be_valid_be_name 2473 * Description: Validates a BE name. 2474 * Parameters: 2475 * be_name - name of BE to validate 2476 * Returns: 2477 * B_TRUE - be_name is valid 2478 * B_FALSE - be_name is invalid 2479 * Scope: 2480 * Semi-private (library wide use only) 2481 */ 2482 2483 boolean_t 2484 be_valid_be_name(const char *be_name) 2485 { 2486 const char *c = NULL; 2487 struct be_defaults be_defaults; 2488 2489 if (be_name == NULL) 2490 return (B_FALSE); 2491 2492 be_get_defaults(&be_defaults); 2493 2494 /* 2495 * A BE name must not be a multi-level dataset name. We also check 2496 * that it does not contain the ' ' and '%' characters. The ' ' is 2497 * a valid character for datasets, however we don't allow that in a 2498 * BE name. The '%' is invalid, but zfs_name_valid() allows it for 2499 * internal reasons, so we explicitly check for it here. 2500 */ 2501 c = be_name; 2502 while (*c != '\0' && *c != '/' && *c != ' ' && *c != '%') 2503 c++; 2504 2505 if (*c != '\0') 2506 return (B_FALSE); 2507 2508 /* 2509 * The BE name must comply with a zfs dataset filesystem. We also 2510 * verify its length to be < BE_NAME_MAX_LEN. 2511 */ 2512 if (!zfs_name_valid(be_name, ZFS_TYPE_FILESYSTEM) || 2513 strlen(be_name) > BE_NAME_MAX_LEN) 2514 return (B_FALSE); 2515 2516 if (be_defaults.be_deflt_bename_starts_with[0] != '\0' && 2517 strstr(be_name, be_defaults.be_deflt_bename_starts_with) == NULL) { 2518 return (B_FALSE); 2519 } 2520 2521 return (B_TRUE); 2522 } 2523 2524 /* 2525 * Function: be_valid_auto_snap_name 2526 * Description: This function checks that a snapshot name is a valid auto 2527 * generated snapshot name. A valid auto generated snapshot 2528 * name is of the form: 2529 * 2530 * %Y-%m-%d-%H:%M:%S 2531 * 2532 * An older form of the auto generated snapshot name also 2533 * included the snapshot's BE cleanup policy and a reserved 2534 * field. Those names will also be verified by this function. 2535 * 2536 * Examples of valid auto snapshot names are: 2537 * 2538 * 2008-03-31-18:41:30 2539 * 2008-03-31-22:17:24 2540 * <policy>:-:2008:04-05-09:12:55 2541 * <policy>:-:2008:04-06-15:34:12 2542 * 2543 * Parameters: 2544 * name - name of the snapshot to be validated. 2545 * Returns: 2546 * B_TRUE - the name is a valid auto snapshot name. 2547 * B_FALSE - the name is not a valid auto snapshot name. 2548 * Scope: 2549 * Semi-private (library wide use only) 2550 */ 2551 boolean_t 2552 be_valid_auto_snap_name(char *name) 2553 { 2554 struct tm gmt_tm; 2555 2556 char *policy = NULL; 2557 char *reserved = NULL; 2558 char *date = NULL; 2559 char *c = NULL; 2560 2561 /* Validate the snapshot name by converting it into utc time */ 2562 if (strptime(name, "%Y-%m-%d-%T", &gmt_tm) != NULL && 2563 (mktime(&gmt_tm) != -1)) { 2564 return (B_TRUE); 2565 } 2566 2567 /* 2568 * Validate the snapshot name against the older form of an 2569 * auto generated snapshot name. 2570 */ 2571 policy = strdup(name); 2572 2573 /* 2574 * Get the first field from the snapshot name, 2575 * which is the BE policy 2576 */ 2577 c = strchr(policy, ':'); 2578 if (c == NULL) { 2579 free(policy); 2580 return (B_FALSE); 2581 } 2582 c[0] = '\0'; 2583 2584 /* Validate the policy name */ 2585 if (!valid_be_policy(policy)) { 2586 free(policy); 2587 return (B_FALSE); 2588 } 2589 2590 /* Get the next field, which is the reserved field. */ 2591 if (c[1] == '\0') { 2592 free(policy); 2593 return (B_FALSE); 2594 } 2595 reserved = c+1; 2596 c = strchr(reserved, ':'); 2597 if (c == NULL) { 2598 free(policy); 2599 return (B_FALSE); 2600 } 2601 c[0] = '\0'; 2602 2603 /* Validate the reserved field */ 2604 if (strcmp(reserved, "-") != 0) { 2605 free(policy); 2606 return (B_FALSE); 2607 } 2608 2609 /* The remaining string should be the date field */ 2610 if (c[1] == '\0') { 2611 free(policy); 2612 return (B_FALSE); 2613 } 2614 date = c+1; 2615 2616 /* Validate the date string by converting it into utc time */ 2617 if (strptime(date, "%Y-%m-%d-%T", &gmt_tm) == NULL || 2618 (mktime(&gmt_tm) == -1)) { 2619 be_print_err(gettext("be_valid_auto_snap_name: " 2620 "invalid auto snapshot name\n")); 2621 free(policy); 2622 return (B_FALSE); 2623 } 2624 2625 free(policy); 2626 return (B_TRUE); 2627 } 2628 2629 /* 2630 * Function: be_default_policy 2631 * Description: Temporary hardcoded policy support. This function returns 2632 * the default policy type to be used to create a BE or a BE 2633 * snapshot. 2634 * Parameters: 2635 * None 2636 * Returns: 2637 * Name of default BE policy. 2638 * Scope: 2639 * Semi-private (library wide use only) 2640 */ 2641 char * 2642 be_default_policy(void) 2643 { 2644 return (BE_PLCY_STATIC); 2645 } 2646 2647 /* 2648 * Function: valid_be_policy 2649 * Description: Temporary hardcoded policy support. This function valids 2650 * whether a policy is a valid known policy or not. 2651 * Paramters: 2652 * policy - name of policy to validate. 2653 * Returns: 2654 * B_TRUE - policy is a valid. 2655 * B_FALSE - policy is invalid. 2656 * Scope: 2657 * Semi-private (library wide use only) 2658 */ 2659 boolean_t 2660 valid_be_policy(char *policy) 2661 { 2662 if (policy == NULL) 2663 return (B_FALSE); 2664 2665 if (strcmp(policy, BE_PLCY_STATIC) == 0 || 2666 strcmp(policy, BE_PLCY_VOLATILE) == 0) { 2667 return (B_TRUE); 2668 } 2669 2670 return (B_FALSE); 2671 } 2672 2673 /* 2674 * Function: be_print_err 2675 * Description: This function prints out error messages if do_print is 2676 * set to B_TRUE or if the BE_PRINT_ERR environment variable 2677 * is set to true. 2678 * Paramters: 2679 * prnt_str - the string we wish to print and any arguments 2680 * for the format of that string. 2681 * Returns: 2682 * void 2683 * Scope: 2684 * Semi-private (library wide use only) 2685 */ 2686 void 2687 be_print_err(char *prnt_str, ...) 2688 { 2689 va_list ap; 2690 char buf[BUFSIZ]; 2691 char *env_buf; 2692 static boolean_t env_checked = B_FALSE; 2693 2694 if (!env_checked) { 2695 if ((env_buf = getenv("BE_PRINT_ERR")) != NULL) { 2696 if (strcasecmp(env_buf, "true") == 0) { 2697 do_print = B_TRUE; 2698 } 2699 } 2700 env_checked = B_TRUE; 2701 } 2702 2703 if (do_print) { 2704 va_start(ap, prnt_str); 2705 /* LINTED variable format specifier */ 2706 (void) vsnprintf(buf, BUFSIZ, prnt_str, ap); 2707 (void) fputs(buf, stderr); 2708 va_end(ap); 2709 } 2710 } 2711 2712 /* 2713 * Function: be_find_current_be 2714 * Description: Find the currently "active" BE. Fill in the 2715 * passed in be_transaction_data_t reference with the 2716 * active BE's data. 2717 * Paramters: 2718 * none 2719 * Returns: 2720 * BE_SUCCESS - Success 2721 * be_errnot_t - Failure 2722 * Scope: 2723 * Semi-private (library wide use only) 2724 * Notes: 2725 * The caller is responsible for initializing the libzfs handle 2726 * and freeing the memory used by the active be_name. 2727 */ 2728 int 2729 be_find_current_be(be_transaction_data_t *bt) 2730 { 2731 int zret; 2732 2733 if ((zret = zpool_iter(g_zfs, be_zpool_find_current_be_callback, 2734 bt)) == 0) { 2735 be_print_err(gettext("be_find_current_be: failed to " 2736 "find current BE name\n")); 2737 return (BE_ERR_BE_NOENT); 2738 } else if (zret < 0) { 2739 be_print_err(gettext("be_find_current_be: " 2740 "zpool_iter failed: %s\n"), 2741 libzfs_error_description(g_zfs)); 2742 return (zfs_err_to_be_err(g_zfs)); 2743 } 2744 2745 return (BE_SUCCESS); 2746 } 2747 2748 /* 2749 * Function: be_zpool_find_current_be_callback 2750 * Description: Callback function used to iterate through all existing pools 2751 * to find the BE that is the currently booted BE. 2752 * Parameters: 2753 * zlp - zpool_handle_t pointer to the current pool being 2754 * looked at. 2755 * data - be_transaction_data_t pointer. 2756 * Upon successfully finding the current BE, the 2757 * obe_zpool member of this parameter is set to the 2758 * pool it is found in. 2759 * Return: 2760 * 1 - Found current BE in this pool. 2761 * 0 - Did not find current BE in this pool. 2762 * Scope: 2763 * Semi-private (library wide use only) 2764 */ 2765 int 2766 be_zpool_find_current_be_callback(zpool_handle_t *zlp, void *data) 2767 { 2768 be_transaction_data_t *bt = data; 2769 zfs_handle_t *zhp = NULL; 2770 const char *zpool = zpool_get_name(zlp); 2771 char be_container_ds[MAXPATHLEN]; 2772 2773 /* 2774 * Generate string for BE container dataset 2775 */ 2776 if (be_make_container_ds(zpool, be_container_ds, 2777 sizeof (be_container_ds)) != BE_SUCCESS) { 2778 zpool_close(zlp); 2779 return (0); 2780 } 2781 2782 /* 2783 * Check if a BE container dataset exists in this pool. 2784 */ 2785 if (!zfs_dataset_exists(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) { 2786 zpool_close(zlp); 2787 return (0); 2788 } 2789 2790 /* 2791 * Get handle to this zpool's BE container dataset. 2792 */ 2793 if ((zhp = zfs_open(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) == 2794 NULL) { 2795 be_print_err(gettext("be_zpool_find_current_be_callback: " 2796 "failed to open BE container dataset (%s)\n"), 2797 be_container_ds); 2798 zpool_close(zlp); 2799 return (0); 2800 } 2801 2802 /* 2803 * Iterate through all potential BEs in this zpool 2804 */ 2805 if (zfs_iter_filesystems(zhp, be_zfs_find_current_be_callback, bt)) { 2806 /* 2807 * Found current BE dataset; set obe_zpool 2808 */ 2809 if ((bt->obe_zpool = strdup(zpool)) == NULL) { 2810 be_print_err(gettext( 2811 "be_zpool_find_current_be_callback: " 2812 "memory allocation failed\n")); 2813 ZFS_CLOSE(zhp); 2814 zpool_close(zlp); 2815 return (0); 2816 } 2817 2818 ZFS_CLOSE(zhp); 2819 zpool_close(zlp); 2820 return (1); 2821 } 2822 2823 ZFS_CLOSE(zhp); 2824 zpool_close(zlp); 2825 2826 return (0); 2827 } 2828 2829 /* 2830 * Function: be_zfs_find_current_be_callback 2831 * Description: Callback function used to iterate through all BEs in a 2832 * pool to find the BE that is the currently booted BE. 2833 * Parameters: 2834 * zhp - zfs_handle_t pointer to current filesystem being checked. 2835 * data - be_transaction-data_t pointer 2836 * Upon successfully finding the current BE, the 2837 * obe_name and obe_root_ds members of this parameter 2838 * are set to the BE name and BE's root dataset 2839 * respectively. 2840 * Return: 2841 * 1 - Found current BE. 2842 * 0 - Did not find current BE. 2843 * Scope: 2844 * Semi-private (library wide use only) 2845 */ 2846 int 2847 be_zfs_find_current_be_callback(zfs_handle_t *zhp, void *data) 2848 { 2849 be_transaction_data_t *bt = data; 2850 char *mp = NULL; 2851 2852 /* 2853 * Check if dataset is mounted, and if so where. 2854 */ 2855 if (zfs_is_mounted(zhp, &mp)) { 2856 /* 2857 * If mounted at root, set obe_root_ds and obe_name 2858 */ 2859 if (mp != NULL && strcmp(mp, "/") == 0) { 2860 free(mp); 2861 2862 if ((bt->obe_root_ds = strdup(zfs_get_name(zhp))) 2863 == NULL) { 2864 be_print_err(gettext( 2865 "be_zfs_find_current_be_callback: " 2866 "memory allocation failed\n")); 2867 ZFS_CLOSE(zhp); 2868 return (0); 2869 } 2870 2871 if ((bt->obe_name = strdup(basename(bt->obe_root_ds))) 2872 == NULL) { 2873 be_print_err(gettext( 2874 "be_zfs_find_current_be_callback: " 2875 "memory allocation failed\n")); 2876 ZFS_CLOSE(zhp); 2877 return (0); 2878 } 2879 2880 ZFS_CLOSE(zhp); 2881 return (1); 2882 } 2883 2884 free(mp); 2885 } 2886 ZFS_CLOSE(zhp); 2887 2888 return (0); 2889 } 2890 2891 /* 2892 * Function: be_check_be_roots_callback 2893 * Description: This function checks whether or not the dataset name passed 2894 * is hierachically located under the BE root container dataset 2895 * for this pool. 2896 * Parameters: 2897 * zlp - zpool_handle_t pointer to current pool being processed. 2898 * data - name of dataset to check 2899 * Returns: 2900 * 0 - dataset is not in this pool's BE root container dataset 2901 * 1 - dataset is in this pool's BE root container dataset 2902 * Scope: 2903 * Semi-private (library wide use only) 2904 */ 2905 int 2906 be_check_be_roots_callback(zpool_handle_t *zlp, void *data) 2907 { 2908 const char *zpool = zpool_get_name(zlp); 2909 char *ds = data; 2910 char be_container_ds[MAXPATHLEN]; 2911 2912 /* Generate string for this pool's BE root container dataset */ 2913 if (be_make_container_ds(zpool, be_container_ds, 2914 sizeof (be_container_ds)) != BE_SUCCESS) { 2915 return (0); 2916 } 2917 2918 /* 2919 * If dataset lives under the BE root container dataset 2920 * of this pool, return failure. 2921 */ 2922 if (strncmp(be_container_ds, ds, strlen(be_container_ds)) == 0 && 2923 ds[strlen(be_container_ds)] == '/') { 2924 zpool_close(zlp); 2925 return (1); 2926 } 2927 2928 zpool_close(zlp); 2929 return (0); 2930 } 2931 2932 /* 2933 * Function: zfs_err_to_be_err 2934 * Description: This function takes the error stored in the libzfs handle 2935 * and maps it to an be_errno_t. If there are no matching 2936 * be_errno_t's then BE_ERR_ZFS is returned. 2937 * Paramters: 2938 * zfsh - The libzfs handle containing the error we're looking up. 2939 * Returns: 2940 * be_errno_t 2941 * Scope: 2942 * Semi-private (library wide use only) 2943 */ 2944 int 2945 zfs_err_to_be_err(libzfs_handle_t *zfsh) 2946 { 2947 int err = libzfs_errno(zfsh); 2948 2949 switch (err) { 2950 case 0: 2951 return (BE_SUCCESS); 2952 case EZFS_PERM: 2953 return (BE_ERR_PERM); 2954 case EZFS_INTR: 2955 return (BE_ERR_INTR); 2956 case EZFS_NOENT: 2957 return (BE_ERR_NOENT); 2958 case EZFS_NOSPC: 2959 return (BE_ERR_NOSPC); 2960 case EZFS_MOUNTFAILED: 2961 return (BE_ERR_MOUNT); 2962 case EZFS_UMOUNTFAILED: 2963 return (BE_ERR_UMOUNT); 2964 case EZFS_EXISTS: 2965 return (BE_ERR_BE_EXISTS); 2966 case EZFS_BUSY: 2967 return (BE_ERR_DEV_BUSY); 2968 case EZFS_POOLREADONLY: 2969 return (BE_ERR_ROFS); 2970 case EZFS_NAMETOOLONG: 2971 return (BE_ERR_NAMETOOLONG); 2972 case EZFS_NODEVICE: 2973 return (BE_ERR_NODEV); 2974 case EZFS_POOL_INVALARG: 2975 return (BE_ERR_INVAL); 2976 case EZFS_PROPTYPE: 2977 return (BE_ERR_INVALPROP); 2978 case EZFS_BADTYPE: 2979 return (BE_ERR_DSTYPE); 2980 case EZFS_PROPNONINHERIT: 2981 return (BE_ERR_NONINHERIT); 2982 case EZFS_PROPREADONLY: 2983 return (BE_ERR_READONLYPROP); 2984 case EZFS_RESILVERING: 2985 case EZFS_POOLUNAVAIL: 2986 return (BE_ERR_UNAVAIL); 2987 case EZFS_DSREADONLY: 2988 return (BE_ERR_READONLYDS); 2989 default: 2990 return (BE_ERR_ZFS); 2991 } 2992 } 2993 2994 /* 2995 * Function: errno_to_be_err 2996 * Description: This function takes an errno and maps it to an be_errno_t. 2997 * If there are no matching be_errno_t's then BE_ERR_UNKNOWN is 2998 * returned. 2999 * Paramters: 3000 * err - The errno we're compairing against. 3001 * Returns: 3002 * be_errno_t 3003 * Scope: 3004 * Semi-private (library wide use only) 3005 */ 3006 int 3007 errno_to_be_err(int err) 3008 { 3009 switch (err) { 3010 case EPERM: 3011 return (BE_ERR_PERM); 3012 case EACCES: 3013 return (BE_ERR_ACCESS); 3014 case ECANCELED: 3015 return (BE_ERR_CANCELED); 3016 case EINTR: 3017 return (BE_ERR_INTR); 3018 case ENOENT: 3019 return (BE_ERR_NOENT); 3020 case ENOSPC: 3021 case EDQUOT: 3022 return (BE_ERR_NOSPC); 3023 case EEXIST: 3024 return (BE_ERR_BE_EXISTS); 3025 case EBUSY: 3026 return (BE_ERR_BUSY); 3027 case EROFS: 3028 return (BE_ERR_ROFS); 3029 case ENAMETOOLONG: 3030 return (BE_ERR_NAMETOOLONG); 3031 case ENXIO: 3032 return (BE_ERR_NXIO); 3033 case EINVAL: 3034 return (BE_ERR_INVAL); 3035 case EFAULT: 3036 return (BE_ERR_FAULT); 3037 default: 3038 return (BE_ERR_UNKNOWN); 3039 } 3040 } 3041 3042 /* 3043 * Function: be_err_to_str 3044 * Description: This function takes a be_errno_t and maps it to a message. 3045 * If there are no matching be_errno_t's then NULL is returned. 3046 * Paramters: 3047 * be_errno_t - The be_errno_t we're mapping. 3048 * Returns: 3049 * string or NULL if the error code is not known. 3050 * Scope: 3051 * Semi-private (library wide use only) 3052 */ 3053 char * 3054 be_err_to_str(int err) 3055 { 3056 switch (err) { 3057 case BE_ERR_ACCESS: 3058 return (gettext("Permission denied.")); 3059 case BE_ERR_ACTIVATE_CURR: 3060 return (gettext("Activation of current BE failed.")); 3061 case BE_ERR_AUTONAME: 3062 return (gettext("Auto naming failed.")); 3063 case BE_ERR_BE_NOENT: 3064 return (gettext("No such BE.")); 3065 case BE_ERR_BUSY: 3066 return (gettext("Mount busy.")); 3067 case BE_ERR_DEV_BUSY: 3068 return (gettext("Device busy.")); 3069 case BE_ERR_CANCELED: 3070 return (gettext("Operation canceled.")); 3071 case BE_ERR_CLONE: 3072 return (gettext("BE clone failed.")); 3073 case BE_ERR_COPY: 3074 return (gettext("BE copy failed.")); 3075 case BE_ERR_CREATDS: 3076 return (gettext("Dataset creation failed.")); 3077 case BE_ERR_CURR_BE_NOT_FOUND: 3078 return (gettext("Can't find current BE.")); 3079 case BE_ERR_DESTROY: 3080 return (gettext("Failed to destroy BE or snapshot.")); 3081 case BE_ERR_DESTROY_CURR_BE: 3082 return (gettext("Cannot destroy current BE.")); 3083 case BE_ERR_DEMOTE: 3084 return (gettext("BE demotion failed.")); 3085 case BE_ERR_DSTYPE: 3086 return (gettext("Invalid dataset type.")); 3087 case BE_ERR_BE_EXISTS: 3088 return (gettext("BE exists.")); 3089 case BE_ERR_INIT: 3090 return (gettext("be_zfs_init failed.")); 3091 case BE_ERR_INTR: 3092 return (gettext("Interupted system call.")); 3093 case BE_ERR_INVAL: 3094 return (gettext("Invalid argument.")); 3095 case BE_ERR_INVALPROP: 3096 return (gettext("Invalid property for dataset.")); 3097 case BE_ERR_INVALMOUNTPOINT: 3098 return (gettext("Unexpected mountpoint.")); 3099 case BE_ERR_MOUNT: 3100 return (gettext("Mount failed.")); 3101 case BE_ERR_MOUNTED: 3102 return (gettext("Already mounted.")); 3103 case BE_ERR_NAMETOOLONG: 3104 return (gettext("name > BUFSIZ.")); 3105 case BE_ERR_NOENT: 3106 return (gettext("Doesn't exist.")); 3107 case BE_ERR_POOL_NOENT: 3108 return (gettext("No such pool.")); 3109 case BE_ERR_NODEV: 3110 return (gettext("No such device.")); 3111 case BE_ERR_NOTMOUNTED: 3112 return (gettext("File system not mounted.")); 3113 case BE_ERR_NOMEM: 3114 return (gettext("Not enough memory.")); 3115 case BE_ERR_NONINHERIT: 3116 return (gettext( 3117 "Property is not inheritable for the BE dataset.")); 3118 case BE_ERR_NXIO: 3119 return (gettext("No such device or address.")); 3120 case BE_ERR_NOSPC: 3121 return (gettext("No space on device.")); 3122 case BE_ERR_NOTSUP: 3123 return (gettext("Operation not supported.")); 3124 case BE_ERR_OPEN: 3125 return (gettext("Open failed.")); 3126 case BE_ERR_PERM: 3127 return (gettext("Not owner.")); 3128 case BE_ERR_UNAVAIL: 3129 return (gettext("The BE is currently unavailable.")); 3130 case BE_ERR_PROMOTE: 3131 return (gettext("BE promotion failed.")); 3132 case BE_ERR_ROFS: 3133 return (gettext("Read only file system.")); 3134 case BE_ERR_READONLYDS: 3135 return (gettext("Read only dataset.")); 3136 case BE_ERR_READONLYPROP: 3137 return (gettext("Read only property.")); 3138 case BE_ERR_RENAME_ACTIVE: 3139 return (gettext("Renaming the active BE is not supported.")); 3140 case BE_ERR_SS_EXISTS: 3141 return (gettext("Snapshot exists.")); 3142 case BE_ERR_SS_NOENT: 3143 return (gettext("No such snapshot.")); 3144 case BE_ERR_UMOUNT: 3145 return (gettext("Unmount failed.")); 3146 case BE_ERR_UMOUNT_CURR_BE: 3147 return (gettext("Can't unmount the current BE.")); 3148 case BE_ERR_UMOUNT_SHARED: 3149 return (gettext("Unmount of a shared File System failed.")); 3150 case BE_ERR_FAULT: 3151 return (gettext("Bad address.")); 3152 case BE_ERR_UNKNOWN: 3153 return (gettext("Unknown error.")); 3154 case BE_ERR_ZFS: 3155 return (gettext("ZFS returned an error.")); 3156 case BE_ERR_GEN_UUID: 3157 return (gettext("Failed to generate uuid.")); 3158 case BE_ERR_PARSE_UUID: 3159 return (gettext("Failed to parse uuid.")); 3160 case BE_ERR_NO_UUID: 3161 return (gettext("No uuid")); 3162 case BE_ERR_ZONE_NO_PARENTBE: 3163 return (gettext("No parent uuid")); 3164 case BE_ERR_ZONE_MULTIPLE_ACTIVE: 3165 return (gettext("Multiple active zone roots")); 3166 case BE_ERR_ZONE_NO_ACTIVE_ROOT: 3167 return (gettext("No active zone root")); 3168 case BE_ERR_ZONE_ROOT_NOT_LEGACY: 3169 return (gettext("Zone root not legacy")); 3170 case BE_ERR_MOUNT_ZONEROOT: 3171 return (gettext("Failed to mount a zone root.")); 3172 case BE_ERR_UMOUNT_ZONEROOT: 3173 return (gettext("Failed to unmount a zone root.")); 3174 case BE_ERR_NO_MOUNTED_ZONE: 3175 return (gettext("Zone is not mounted")); 3176 case BE_ERR_ZONES_UNMOUNT: 3177 return (gettext("Unable to unmount a zone BE.")); 3178 case BE_ERR_NO_MENU: 3179 return (gettext("Missing boot menu file.")); 3180 case BE_ERR_BAD_MENU_PATH: 3181 return (gettext("Invalid path for menu.lst file")); 3182 case BE_ERR_ZONE_SS_EXISTS: 3183 return (gettext("Zone snapshot exists.")); 3184 case BE_ERR_BOOTFILE_INST: 3185 return (gettext("Error installing boot files.")); 3186 case BE_ERR_EXTCMD: 3187 return (gettext("Error running an external command.")); 3188 default: 3189 return (NULL); 3190 } 3191 } 3192 3193 /* 3194 * Function: be_has_grub 3195 * Description: Boolean function indicating whether the current system 3196 * uses grub. 3197 * Return: B_FALSE - the system does not have grub 3198 * B_TRUE - the system does have grub. 3199 * Scope: 3200 * Semi-private (library wide use only) 3201 */ 3202 boolean_t 3203 be_has_grub(void) 3204 { 3205 static struct be_defaults be_defaults; 3206 static boolean_t be_deflts_set = B_FALSE; 3207 3208 /* Cache the defaults, because be_has_grub is used often. */ 3209 if (be_deflts_set == B_FALSE) { 3210 be_get_defaults(&be_defaults); 3211 be_deflts_set = B_TRUE; 3212 } 3213 3214 return (be_defaults.be_deflt_grub); 3215 } 3216 3217 /* 3218 * Function: be_is_isa 3219 * Description: Boolean function indicating whether the instruction set 3220 * architecture of the executing system matches the name provided. 3221 * The string must match a system defined architecture (e.g. 3222 * "i386", "sparc") and is case sensitive. 3223 * Parameters: name - string representing the name of instruction set 3224 * architecture being tested 3225 * Returns: B_FALSE - the system instruction set architecture is different 3226 * from the one specified 3227 * B_TRUE - the system instruction set architecture is the same 3228 * as the one specified 3229 * Scope: 3230 * Semi-private (library wide use only) 3231 */ 3232 boolean_t 3233 be_is_isa(char *name) 3234 { 3235 return ((strcmp((char *)be_get_default_isa(), name) == 0)); 3236 } 3237 3238 /* 3239 * Function: be_get_default_isa 3240 * Description: 3241 * Returns the default instruction set architecture of the 3242 * machine it is executed on. (eg. sparc, i386, ...) 3243 * NOTE: SYS_INST environment variable may override default 3244 * return value 3245 * Parameters: 3246 * none 3247 * Returns: 3248 * NULL - the architecture returned by sysinfo() was too 3249 * long for local variables 3250 * char * - pointer to a string containing the default 3251 * implementation 3252 * Scope: 3253 * Semi-private (library wide use only) 3254 */ 3255 char * 3256 be_get_default_isa(void) 3257 { 3258 int i; 3259 char *envp; 3260 static char default_inst[ARCH_LENGTH] = ""; 3261 3262 if (default_inst[0] == '\0') { 3263 if ((envp = getenv("SYS_INST")) != NULL) { 3264 if ((int)strlen(envp) >= ARCH_LENGTH) 3265 return (NULL); 3266 else 3267 (void) strcpy(default_inst, envp); 3268 } else { 3269 i = sysinfo(SI_ARCHITECTURE, default_inst, ARCH_LENGTH); 3270 if (i < 0 || i > ARCH_LENGTH) 3271 return (NULL); 3272 } 3273 } 3274 return (default_inst); 3275 } 3276 3277 /* 3278 * Function: be_get_platform 3279 * Description: 3280 * Returns the platfom name 3281 * Parameters: 3282 * none 3283 * Returns: 3284 * NULL - the platform name returned by sysinfo() was too 3285 * long for local variables 3286 * char * - pointer to a string containing the platform name 3287 * Scope: 3288 * Semi-private (library wide use only) 3289 */ 3290 char * 3291 be_get_platform(void) 3292 { 3293 int i; 3294 static char default_inst[ARCH_LENGTH] = ""; 3295 3296 if (default_inst[0] == '\0') { 3297 i = sysinfo(SI_PLATFORM, default_inst, ARCH_LENGTH); 3298 if (i < 0 || i > ARCH_LENGTH) 3299 return (NULL); 3300 } 3301 return (default_inst); 3302 } 3303 3304 /* 3305 * Function: be_run_cmd 3306 * Description: 3307 * Runs a command in a separate subprocess. Splits out stdout from stderr 3308 * and sends each to its own buffer. Buffers must be pre-allocated and 3309 * passed in as arguments. Buffer sizes are also passed in as arguments. 3310 * 3311 * Notes / caveats: 3312 * - Command being run is assumed to not have any stdout or stderr 3313 * redirection. 3314 * - Commands which emit total stderr output of greater than PIPE_BUF 3315 * bytes can hang. For such commands, a different implementation 3316 * which uses poll(2) must be used. 3317 * - stdout_buf can be NULL. In this case, stdout_bufsize is ignored, and 3318 * the stream which would have gone to it is sent to the bit 3319 * bucket. 3320 * - stderr_buf cannot be NULL. 3321 * - Only subprocess errors are appended to the stderr_buf. Errors 3322 * running the command are reported through be_print_err(). 3323 * - Data which would overflow its respective buffer is sent to the bit 3324 * bucket. 3325 * 3326 * Parameters: 3327 * command: command to run. Assumed not to have embedded stdout 3328 * or stderr redirection. May have stdin redirection, 3329 * however. 3330 * stderr_buf: buffer returning subprocess stderr data. Errors 3331 * reported by this function are reported through 3332 * be_print_err(). 3333 * stderr_bufsize: size of stderr_buf 3334 * stdout_buf: buffer returning subprocess stdout data. 3335 * stdout_bufsize: size of stdout_buf 3336 * Returns: 3337 * BE_SUCCESS - The command ran successfully without returning 3338 * errors. 3339 * BE_ERR_EXTCMD 3340 * - The command could not be run. 3341 * - The command terminated with error status. 3342 * - There were errors extracting or returning subprocess 3343 * data. 3344 * BE_ERR_NOMEM - The command exceeds the command buffer size. 3345 * BE_ERR_INVAL - An invalid argument was specified. 3346 * Scope: 3347 * Semi-private (library wide use only) 3348 */ 3349 int 3350 be_run_cmd(char *command, char *stderr_buf, int stderr_bufsize, 3351 char *stdout_buf, int stdout_bufsize) 3352 { 3353 char *temp_filename = strdup(tmpnam(NULL)); 3354 FILE *stdout_str = NULL; 3355 FILE *stderr_str = NULL; 3356 char cmdline[BUFSIZ]; 3357 char oneline[BUFSIZ]; 3358 int exit_status; 3359 int rval = BE_SUCCESS; 3360 3361 if ((command == NULL) || (stderr_buf == NULL) || 3362 (stderr_bufsize <= 0) || (stdout_bufsize < 0) || 3363 ((stdout_buf != NULL) ^ (stdout_bufsize != 0))) { 3364 return (BE_ERR_INVAL); 3365 } 3366 3367 /* Set up command so popen returns stderr, not stdout */ 3368 if (snprintf(cmdline, BUFSIZ, "%s 2> %s", command, 3369 temp_filename) >= BUFSIZ) { 3370 rval = BE_ERR_NOMEM; 3371 goto cleanup; 3372 } 3373 3374 /* Set up the fifo that will make stderr available. */ 3375 if (mkfifo(temp_filename, 0600) != 0) { 3376 (void) be_print_err(gettext("be_run_cmd: mkfifo: %s\n"), 3377 strerror(errno)); 3378 rval = BE_ERR_EXTCMD; 3379 goto cleanup; 3380 } 3381 3382 if ((stdout_str = popen(cmdline, "r")) == NULL) { 3383 (void) be_print_err(gettext("be_run_cmd: popen: %s\n"), 3384 strerror(errno)); 3385 rval = BE_ERR_EXTCMD; 3386 goto cleanup; 3387 } 3388 3389 if ((stderr_str = fopen(temp_filename, "r")) == NULL) { 3390 (void) be_print_err(gettext("be_run_cmd: fopen: %s\n"), 3391 strerror(errno)); 3392 (void) pclose(stdout_str); 3393 rval = BE_ERR_EXTCMD; 3394 goto cleanup; 3395 } 3396 3397 /* Read stdout first, as it usually outputs more than stderr. */ 3398 oneline[BUFSIZ-1] = '\0'; 3399 while (fgets(oneline, BUFSIZ-1, stdout_str) != NULL) { 3400 if (stdout_str != NULL) { 3401 (void) strlcat(stdout_buf, oneline, stdout_bufsize); 3402 } 3403 } 3404 3405 while (fgets(oneline, BUFSIZ-1, stderr_str) != NULL) { 3406 (void) strlcat(stderr_buf, oneline, stderr_bufsize); 3407 } 3408 3409 /* Close pipe, get exit status. */ 3410 if ((exit_status = pclose(stdout_str)) == -1) { 3411 (void) be_print_err(gettext("be_run_cmd: pclose: %s\n"), 3412 strerror(errno)); 3413 rval = BE_ERR_EXTCMD; 3414 } else if (WIFEXITED(exit_status)) { 3415 exit_status = (int)((char)WEXITSTATUS(exit_status)); 3416 /* 3417 * error code BC_NOUPDT means more recent version 3418 * is installed 3419 */ 3420 if (exit_status != BC_SUCCESS && exit_status != BC_NOUPDT) { 3421 (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: " 3422 "command terminated with error status: %d\n"), 3423 exit_status); 3424 (void) strlcat(stderr_buf, oneline, stderr_bufsize); 3425 rval = BE_ERR_EXTCMD; 3426 } 3427 } else { 3428 (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: command " 3429 "terminated on signal: %s\n"), 3430 strsignal(WTERMSIG(exit_status))); 3431 (void) strlcat(stderr_buf, oneline, stderr_bufsize); 3432 rval = BE_ERR_EXTCMD; 3433 } 3434 3435 cleanup: 3436 (void) unlink(temp_filename); 3437 (void) free(temp_filename); 3438 3439 return (rval); 3440 } 3441 3442 /* ******************************************************************** */ 3443 /* Private Functions */ 3444 /* ******************************************************************** */ 3445 3446 /* 3447 * Function: update_dataset 3448 * Description: This function takes a dataset name and replaces the zpool 3449 * and be_name components of the dataset with the new be_name 3450 * zpool passed in. 3451 * Parameters: 3452 * dataset - name of dataset 3453 * dataset_len - lenth of buffer in which dataset is passed in. 3454 * be_name - name of new BE name to update to. 3455 * old_rc_loc - dataset under which the root container dataset 3456 * for the old BE lives. 3457 * new_rc_loc - dataset under which the root container dataset 3458 * for the new BE lives. 3459 * Returns: 3460 * BE_SUCCESS - Success 3461 * be_errno_t - Failure 3462 * Scope: 3463 * Private 3464 */ 3465 static int 3466 update_dataset(char *dataset, int dataset_len, char *be_name, 3467 char *old_rc_loc, char *new_rc_loc) 3468 { 3469 char *ds = NULL; 3470 char *sub_ds = NULL; 3471 int ret; 3472 3473 /* Tear off the BE container dataset */ 3474 if ((ds = be_make_name_from_ds(dataset, old_rc_loc)) == NULL) { 3475 return (BE_ERR_INVAL); 3476 } 3477 3478 /* Get dataset name relative to BE root, if there is one */ 3479 sub_ds = strchr(ds, '/'); 3480 3481 /* Generate the BE root dataset name */ 3482 if ((ret = be_make_root_ds(new_rc_loc, be_name, dataset, 3483 dataset_len)) != BE_SUCCESS) { 3484 return (ret); 3485 } 3486 3487 /* If a subordinate dataset name was found, append it */ 3488 if (sub_ds != NULL) 3489 (void) strlcat(dataset, sub_ds, dataset_len); 3490 3491 free(ds); 3492 return (BE_SUCCESS); 3493 } 3494 3495 /* 3496 * Function: _update_vfstab 3497 * Description: This function updates a vfstab file to reflect the new 3498 * root container dataset location and be_name for all 3499 * entries listed in the be_fs_list_data_t structure passed in. 3500 * Parameters: 3501 * vfstab - vfstab file to modify 3502 * be_name - name of BE to update. 3503 * old_rc_loc - dataset under which the root container dataset 3504 * of the old BE resides in. 3505 * new_rc_loc - dataset under which the root container dataset 3506 * of the new BE resides in. 3507 * fld - be_fs_list_data_t pointer providing the list of 3508 * file systems to look for in vfstab. 3509 * Returns: 3510 * BE_SUCCESS - Success 3511 * be_errno_t - Failure 3512 * Scope: 3513 * Private 3514 */ 3515 static int 3516 _update_vfstab(char *vfstab, char *be_name, char *old_rc_loc, 3517 char *new_rc_loc, be_fs_list_data_t *fld) 3518 { 3519 struct vfstab vp; 3520 char *tmp_vfstab = NULL; 3521 char comments_buf[BUFSIZ]; 3522 FILE *comments = NULL; 3523 FILE *vfs_ents = NULL; 3524 FILE *tfile = NULL; 3525 struct stat sb; 3526 char dev[MAXPATHLEN]; 3527 char *c; 3528 int fd; 3529 int ret = BE_SUCCESS, err = 0; 3530 int i; 3531 int tmp_vfstab_len = 0; 3532 3533 errno = 0; 3534 3535 /* 3536 * Open vfstab for reading twice. First is for comments, 3537 * second is for actual entries. 3538 */ 3539 if ((comments = fopen(vfstab, "r")) == NULL || 3540 (vfs_ents = fopen(vfstab, "r")) == NULL) { 3541 err = errno; 3542 be_print_err(gettext("_update_vfstab: " 3543 "failed to open vfstab (%s): %s\n"), vfstab, 3544 strerror(err)); 3545 ret = errno_to_be_err(err); 3546 goto cleanup; 3547 } 3548 3549 /* Grab the stats of the original vfstab file */ 3550 if (stat(vfstab, &sb) != 0) { 3551 err = errno; 3552 be_print_err(gettext("_update_vfstab: " 3553 "failed to stat file %s: %s\n"), vfstab, 3554 strerror(err)); 3555 ret = errno_to_be_err(err); 3556 goto cleanup; 3557 } 3558 3559 /* Create tmp file for modified vfstab */ 3560 if ((tmp_vfstab = (char *)malloc(strlen(vfstab) + 7)) 3561 == NULL) { 3562 be_print_err(gettext("_update_vfstab: " 3563 "malloc failed\n")); 3564 ret = BE_ERR_NOMEM; 3565 goto cleanup; 3566 } 3567 tmp_vfstab_len = strlen(vfstab) + 7; 3568 (void) memset(tmp_vfstab, 0, tmp_vfstab_len); 3569 (void) strlcpy(tmp_vfstab, vfstab, tmp_vfstab_len); 3570 (void) strlcat(tmp_vfstab, "XXXXXX", tmp_vfstab_len); 3571 if ((fd = mkstemp(tmp_vfstab)) == -1) { 3572 err = errno; 3573 be_print_err(gettext("_update_vfstab: " 3574 "mkstemp failed: %s\n"), strerror(err)); 3575 ret = errno_to_be_err(err); 3576 goto cleanup; 3577 } 3578 if ((tfile = fdopen(fd, "w")) == NULL) { 3579 err = errno; 3580 be_print_err(gettext("_update_vfstab: " 3581 "could not open file for write\n")); 3582 (void) close(fd); 3583 ret = errno_to_be_err(err); 3584 goto cleanup; 3585 } 3586 3587 while (fgets(comments_buf, BUFSIZ, comments)) { 3588 for (c = comments_buf; *c != '\0' && isspace(*c); c++) 3589 ; 3590 if (*c == '\0') { 3591 continue; 3592 } else if (*c == '#') { 3593 /* 3594 * If line is a comment line, just put 3595 * it through to the tmp vfstab. 3596 */ 3597 (void) fputs(comments_buf, tfile); 3598 } else { 3599 /* 3600 * Else line is a vfstab entry, grab it 3601 * into a vfstab struct. 3602 */ 3603 if (getvfsent(vfs_ents, &vp) != 0) { 3604 err = errno; 3605 be_print_err(gettext("_update_vfstab: " 3606 "getvfsent failed: %s\n"), strerror(err)); 3607 ret = errno_to_be_err(err); 3608 goto cleanup; 3609 } 3610 3611 if (vp.vfs_special == NULL || vp.vfs_mountp == NULL) { 3612 (void) putvfsent(tfile, &vp); 3613 continue; 3614 } 3615 3616 /* 3617 * If the entry is one of the entries in the list 3618 * of file systems to update, modify it's device 3619 * field to be correct for this BE. 3620 */ 3621 for (i = 0; i < fld->fs_num; i++) { 3622 if (strcmp(vp.vfs_special, fld->fs_list[i]) 3623 == 0) { 3624 /* 3625 * Found entry that needs an update. 3626 * Replace the root container dataset 3627 * location and be_name in the 3628 * entry's device. 3629 */ 3630 (void) strlcpy(dev, vp.vfs_special, 3631 sizeof (dev)); 3632 3633 if ((ret = update_dataset(dev, 3634 sizeof (dev), be_name, old_rc_loc, 3635 new_rc_loc)) != 0) { 3636 be_print_err( 3637 gettext("_update_vfstab: " 3638 "Failed to update device " 3639 "field for vfstab entry " 3640 "%s\n"), fld->fs_list[i]); 3641 goto cleanup; 3642 } 3643 3644 vp.vfs_special = dev; 3645 break; 3646 } 3647 } 3648 3649 /* Put entry through to tmp vfstab */ 3650 (void) putvfsent(tfile, &vp); 3651 } 3652 } 3653 3654 (void) fclose(comments); 3655 comments = NULL; 3656 (void) fclose(vfs_ents); 3657 vfs_ents = NULL; 3658 (void) fclose(tfile); 3659 tfile = NULL; 3660 3661 /* Copy tmp vfstab into place */ 3662 if (rename(tmp_vfstab, vfstab) != 0) { 3663 err = errno; 3664 be_print_err(gettext("_update_vfstab: " 3665 "failed to rename file %s to %s: %s\n"), tmp_vfstab, 3666 vfstab, strerror(err)); 3667 ret = errno_to_be_err(err); 3668 goto cleanup; 3669 } 3670 3671 /* Set the perms and ownership of the updated file */ 3672 if (chmod(vfstab, sb.st_mode) != 0) { 3673 err = errno; 3674 be_print_err(gettext("_update_vfstab: " 3675 "failed to chmod %s: %s\n"), vfstab, strerror(err)); 3676 ret = errno_to_be_err(err); 3677 goto cleanup; 3678 } 3679 if (chown(vfstab, sb.st_uid, sb.st_gid) != 0) { 3680 err = errno; 3681 be_print_err(gettext("_update_vfstab: " 3682 "failed to chown %s: %s\n"), vfstab, strerror(err)); 3683 ret = errno_to_be_err(err); 3684 goto cleanup; 3685 } 3686 3687 cleanup: 3688 if (comments != NULL) 3689 (void) fclose(comments); 3690 if (vfs_ents != NULL) 3691 (void) fclose(vfs_ents); 3692 (void) unlink(tmp_vfstab); 3693 (void) free(tmp_vfstab); 3694 if (tfile != NULL) 3695 (void) fclose(tfile); 3696 3697 return (ret); 3698 } 3699 3700 3701 /* 3702 * Function: be_get_auto_name 3703 * Description: Generate an auto name constructed based on the BE name 3704 * of the original BE or zone BE being cloned. 3705 * Parameters: 3706 * obe_name - name of the original BE or zone BE being cloned. 3707 * container_ds - container dataset for the zone. 3708 * Note: if zone_be is false this should be 3709 * NULL. 3710 * zone_be - flag that indicates if we are operating on a zone BE. 3711 * Returns: 3712 * Success - pointer to auto generated BE name. The name 3713 * is allocated in heap storage so the caller is 3714 * responsible for free'ing the name. 3715 * Failure - NULL 3716 * Scope: 3717 * Private 3718 */ 3719 static char * 3720 be_get_auto_name(char *obe_name, char *be_container_ds, boolean_t zone_be) 3721 { 3722 be_node_list_t *be_nodes = NULL; 3723 be_node_list_t *cur_be = NULL; 3724 char auto_be_name[MAXPATHLEN]; 3725 char base_be_name[MAXPATHLEN]; 3726 char cur_be_name[MAXPATHLEN]; 3727 char *num_str = NULL; 3728 char *c = NULL; 3729 int num = 0; 3730 int cur_num = 0; 3731 3732 errno = 0; 3733 3734 /* 3735 * Check if obe_name is already in an auto BE name format. 3736 * If it is, then strip off the increment number to get the 3737 * base name. 3738 */ 3739 (void) strlcpy(base_be_name, obe_name, sizeof (base_be_name)); 3740 3741 if ((num_str = strrchr(base_be_name, BE_AUTO_NAME_DELIM)) 3742 != NULL) { 3743 /* Make sure remaining string is all digits */ 3744 c = num_str + 1; 3745 while (c[0] != '\0' && isdigit(c[0])) 3746 c++; 3747 /* 3748 * If we're now at the end of the string strip off the 3749 * increment number. 3750 */ 3751 if (c[0] == '\0') 3752 num_str[0] = '\0'; 3753 } 3754 3755 if (zone_be) { 3756 if (be_container_ds == NULL) 3757 return (NULL); 3758 if (be_get_zone_be_list(obe_name, be_container_ds, 3759 &be_nodes) != BE_SUCCESS) { 3760 be_print_err(gettext("be_get_auto_name: " 3761 "be_get_zone_be_list failed\n")); 3762 return (NULL); 3763 } 3764 } else if (_be_list(NULL, &be_nodes, BE_LIST_DEFAULT) != BE_SUCCESS) { 3765 be_print_err(gettext("be_get_auto_name: be_list failed\n")); 3766 return (NULL); 3767 } 3768 3769 for (cur_be = be_nodes; cur_be != NULL; cur_be = cur_be->be_next_node) { 3770 (void) strlcpy(cur_be_name, cur_be->be_node_name, 3771 sizeof (cur_be_name)); 3772 3773 /* If cur_be_name doesn't match at least base be name, skip. */ 3774 if (strncmp(cur_be_name, base_be_name, strlen(base_be_name)) 3775 != 0) 3776 continue; 3777 3778 /* Get the string following the base be name */ 3779 num_str = cur_be_name + strlen(base_be_name); 3780 3781 /* 3782 * If nothing follows the base be name, this cur_be_name 3783 * is the BE named with the base be name, skip. 3784 */ 3785 if (num_str == NULL || num_str[0] == '\0') 3786 continue; 3787 3788 /* 3789 * Remove the name delimiter. If its not there, 3790 * cur_be_name isn't part of this BE name stream, skip. 3791 */ 3792 if (num_str[0] == BE_AUTO_NAME_DELIM) 3793 num_str++; 3794 else 3795 continue; 3796 3797 /* Make sure remaining string is all digits */ 3798 c = num_str; 3799 while (c[0] != '\0' && isdigit(c[0])) 3800 c++; 3801 if (c[0] != '\0') 3802 continue; 3803 3804 /* Convert the number string to an int */ 3805 cur_num = atoi(num_str); 3806 3807 /* 3808 * If failed to convert the string, skip it. If its too 3809 * long to be converted to an int, we wouldn't auto generate 3810 * this number anyway so there couldn't be a conflict. 3811 * We treat it as a manually created BE name. 3812 */ 3813 if (cur_num == 0 && errno == EINVAL) 3814 continue; 3815 3816 /* 3817 * Compare current number to current max number, 3818 * take higher of the two. 3819 */ 3820 if (cur_num > num) 3821 num = cur_num; 3822 } 3823 3824 /* 3825 * Store off a copy of 'num' incase we need it later. If incrementing 3826 * 'num' causes it to roll over, this means 'num' is the largest 3827 * positive int possible; we'll need it later in the loop to determine 3828 * if we've exhausted all possible increment numbers. We store it in 3829 * 'cur_num'. 3830 */ 3831 cur_num = num; 3832 3833 /* Increment 'num' to get new auto BE name number */ 3834 if (++num <= 0) { 3835 int ret = 0; 3836 3837 /* 3838 * Since incrementing 'num' caused it to rollover, start 3839 * over at 0 and find the first available number. 3840 */ 3841 for (num = 0; num < cur_num; num++) { 3842 3843 (void) snprintf(cur_be_name, sizeof (cur_be_name), 3844 "%s%c%d", base_be_name, BE_AUTO_NAME_DELIM, num); 3845 3846 ret = zpool_iter(g_zfs, be_exists_callback, 3847 cur_be_name); 3848 3849 if (ret == 0) { 3850 /* 3851 * BE name doesn't exist, break out 3852 * to use 'num'. 3853 */ 3854 break; 3855 } else if (ret == 1) { 3856 /* BE name exists, continue looking */ 3857 continue; 3858 } else { 3859 be_print_err(gettext("be_get_auto_name: " 3860 "zpool_iter failed: %s\n"), 3861 libzfs_error_description(g_zfs)); 3862 be_free_list(be_nodes); 3863 return (NULL); 3864 } 3865 } 3866 3867 /* 3868 * If 'num' equals 'cur_num', we've exhausted all possible 3869 * auto BE names for this base BE name. 3870 */ 3871 if (num == cur_num) { 3872 be_print_err(gettext("be_get_auto_name: " 3873 "No more available auto BE names for base " 3874 "BE name %s\n"), base_be_name); 3875 be_free_list(be_nodes); 3876 return (NULL); 3877 } 3878 } 3879 3880 be_free_list(be_nodes); 3881 3882 /* 3883 * Generate string for auto BE name. 3884 */ 3885 (void) snprintf(auto_be_name, sizeof (auto_be_name), "%s%c%d", 3886 base_be_name, BE_AUTO_NAME_DELIM, num); 3887 3888 if ((c = strdup(auto_be_name)) == NULL) { 3889 be_print_err(gettext("be_get_auto_name: " 3890 "memory allocation failed\n")); 3891 return (NULL); 3892 } 3893 3894 return (c); 3895 } 3896 3897 /* 3898 * Function: be_get_console_prop 3899 * Description: Determine console device. 3900 * Returns: 3901 * Success - pointer to console setting. 3902 * Failure - NULL 3903 * Scope: 3904 * Private 3905 */ 3906 static char * 3907 be_get_console_prop(void) 3908 { 3909 di_node_t dn; 3910 char *console = NULL; 3911 3912 if ((dn = di_init("/", DINFOPROP)) == DI_NODE_NIL) { 3913 be_print_err(gettext("be_get_console_prop: " 3914 "di_init() failed\n")); 3915 return (NULL); 3916 } 3917 3918 if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn, 3919 "console", &console) != -1) { 3920 di_fini(dn); 3921 return (console); 3922 } 3923 3924 if (console == NULL) { 3925 if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn, 3926 "output-device", &console) != -1) { 3927 di_fini(dn); 3928 if (strncmp(console, "screen", strlen("screen")) == 0) 3929 console = BE_DEFAULT_CONSOLE; 3930 } 3931 } 3932 3933 /* 3934 * Default console to text 3935 */ 3936 if (console == NULL) { 3937 console = BE_DEFAULT_CONSOLE; 3938 } 3939 3940 return (console); 3941 } 3942 3943 /* 3944 * Function: be_create_menu 3945 * Description: 3946 * This function is used if no menu.lst file exists. In 3947 * this case a new file is created and if needed default 3948 * lines are added to the file. 3949 * Parameters: 3950 * pool - The name of the pool the menu.lst file is on 3951 * menu_file - The name of the file we're creating. 3952 * menu_fp - A pointer to the file pointer of the file we 3953 * created. This is also used to pass back the file 3954 * pointer to the newly created file. 3955 * mode - the original mode used for the failed attempt to 3956 * non-existent file. 3957 * Returns: 3958 * BE_SUCCESS - Success 3959 * be_errno_t - Failure 3960 * Scope: 3961 * Private 3962 */ 3963 static int 3964 be_create_menu( 3965 char *pool, 3966 char *menu_file, 3967 FILE **menu_fp, 3968 char *mode) 3969 { 3970 be_node_list_t *be_nodes = NULL; 3971 char *menu_path = NULL; 3972 char *be_rpool = NULL; 3973 char *be_name = NULL; 3974 char *console = NULL; 3975 errno = 0; 3976 3977 if (menu_file == NULL || menu_fp == NULL || mode == NULL) 3978 return (BE_ERR_INVAL); 3979 3980 menu_path = strdup(menu_file); 3981 if (menu_path == NULL) 3982 return (BE_ERR_NOMEM); 3983 3984 (void) dirname(menu_path); 3985 if (*menu_path == '.') { 3986 free(menu_path); 3987 return (BE_ERR_BAD_MENU_PATH); 3988 } 3989 if (mkdirp(menu_path, 3990 S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1 && 3991 errno != EEXIST) { 3992 be_print_err(gettext("be_create_menu: Failed to create the %s " 3993 "directory: %s\n"), menu_path, strerror(errno)); 3994 free(menu_path); 3995 return (errno_to_be_err(errno)); 3996 } 3997 free(menu_path); 3998 3999 /* 4000 * Check to see if this system supports grub 4001 */ 4002 if (be_has_grub()) { 4003 /* 4004 * The grub menu is missing so we need to create it 4005 * and fill in the first few lines. 4006 */ 4007 FILE *temp_fp = fopen(menu_file, "a+"); 4008 if (temp_fp == NULL) { 4009 *menu_fp = NULL; 4010 return (errno_to_be_err(errno)); 4011 } 4012 4013 if ((console = be_get_console_prop()) != NULL) { 4014 4015 /* 4016 * If console is redirected to serial line, 4017 * GRUB splash screen will not be enabled. 4018 */ 4019 if (strncmp(console, "text", strlen("text")) == 0 || 4020 strncmp(console, "graphics", 4021 strlen("graphics")) == 0) { 4022 4023 (void) fprintf(temp_fp, "%s\n", BE_GRUB_SPLASH); 4024 (void) fprintf(temp_fp, "%s\n", 4025 BE_GRUB_FOREGROUND); 4026 (void) fprintf(temp_fp, "%s\n", 4027 BE_GRUB_BACKGROUND); 4028 (void) fprintf(temp_fp, "%s\n", 4029 BE_GRUB_DEFAULT); 4030 } else { 4031 be_print_err(gettext("be_create_menu: " 4032 "console on serial line, " 4033 "GRUB splash image will be disabled\n")); 4034 } 4035 } 4036 4037 (void) fprintf(temp_fp, "timeout 30\n"); 4038 (void) fclose(temp_fp); 4039 4040 } else { 4041 /* 4042 * The menu file doesn't exist so we need to create a 4043 * blank file. 4044 */ 4045 FILE *temp_fp = fopen(menu_file, "w+"); 4046 if (temp_fp == NULL) { 4047 *menu_fp = NULL; 4048 return (errno_to_be_err(errno)); 4049 } 4050 (void) fclose(temp_fp); 4051 } 4052 4053 /* 4054 * Now we need to add all the BE's back into the the file. 4055 */ 4056 if (_be_list(NULL, &be_nodes, BE_LIST_DEFAULT) == BE_SUCCESS) { 4057 while (be_nodes != NULL) { 4058 if (strcmp(pool, be_nodes->be_rpool) == 0) { 4059 (void) be_append_menu(be_nodes->be_node_name, 4060 be_nodes->be_rpool, NULL, NULL, NULL); 4061 } 4062 if (be_nodes->be_active_on_boot) { 4063 be_rpool = strdup(be_nodes->be_rpool); 4064 be_name = strdup(be_nodes->be_node_name); 4065 } 4066 4067 be_nodes = be_nodes->be_next_node; 4068 } 4069 } 4070 be_free_list(be_nodes); 4071 4072 /* 4073 * Check to see if this system supports grub 4074 */ 4075 if (be_has_grub()) { 4076 int err = be_change_grub_default(be_name, be_rpool); 4077 if (err != BE_SUCCESS) 4078 return (err); 4079 } 4080 *menu_fp = fopen(menu_file, mode); 4081 if (*menu_fp == NULL) 4082 return (errno_to_be_err(errno)); 4083 4084 return (BE_SUCCESS); 4085 } 4086 4087 /* 4088 * Function: be_open_menu 4089 * Description: 4090 * This function is used it open the menu.lst file. If this 4091 * file does not exist be_create_menu is called to create it 4092 * and the open file pointer is returned. If the file does 4093 * exist it is simply opened using the mode passed in. 4094 * Parameters: 4095 * pool - The name of the pool the menu.lst file is on 4096 * menu_file - The name of the file we're opening. 4097 * menu_fp - A pointer to the file pointer of the file we're 4098 * opening. This is also used to pass back the file 4099 * pointer. 4100 * mode - the original mode to be used for opening the menu.lst 4101 * file. 4102 * create_menu - If this is true and the menu.lst file does not 4103 * exist we will attempt to re-create it. However 4104 * if it's false the error returned from the fopen 4105 * will be returned. 4106 * Returns: 4107 * BE_SUCCESS - Success 4108 * be_errno_t - Failure 4109 * Scope: 4110 * Private 4111 */ 4112 static int 4113 be_open_menu( 4114 char *pool, 4115 char *menu_file, 4116 FILE **menu_fp, 4117 char *mode, 4118 boolean_t create_menu) 4119 { 4120 int err = 0; 4121 boolean_t set_print = B_FALSE; 4122 4123 *menu_fp = fopen(menu_file, mode); 4124 err = errno; 4125 if (*menu_fp == NULL) { 4126 if (err == ENOENT && create_menu) { 4127 be_print_err(gettext("be_open_menu: menu.lst " 4128 "file %s does not exist,\n"), menu_file); 4129 if (!do_print) { 4130 set_print = B_TRUE; 4131 do_print = B_TRUE; 4132 } 4133 be_print_err(gettext("WARNING: menu.lst " 4134 "file %s does not exist,\n generating " 4135 "a new menu.lst file\n"), menu_file); 4136 if (set_print) 4137 do_print = B_FALSE; 4138 err = 0; 4139 if ((err = be_create_menu(pool, menu_file, 4140 menu_fp, mode)) == ENOENT) 4141 return (BE_ERR_NO_MENU); 4142 else if (err != BE_SUCCESS) 4143 return (err); 4144 else if (*menu_fp == NULL) 4145 return (BE_ERR_NO_MENU); 4146 } else { 4147 be_print_err(gettext("be_open_menu: failed " 4148 "to open menu.lst file %s\n"), menu_file); 4149 if (err == ENOENT) 4150 return (BE_ERR_NO_MENU); 4151 else 4152 return (errno_to_be_err(err)); 4153 } 4154 } 4155 return (BE_SUCCESS); 4156 } 4157