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 */ 25 26 #include <assert.h> 27 #include <libintl.h> 28 #include <libnvpair.h> 29 #include <libzfs.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <errno.h> 34 #include <sys/mnttab.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <unistd.h> 38 39 #include <libbe.h> 40 #include <libbe_priv.h> 41 42 char *mnttab = MNTTAB; 43 44 /* 45 * Private function prototypes 46 */ 47 static int set_bootfs(char *boot_rpool, char *be_root_ds); 48 static int set_canmount(be_node_list_t *, char *); 49 static int be_do_installgrub(be_transaction_data_t *); 50 static int be_get_grub_vers(be_transaction_data_t *, char **, char **); 51 static int get_ver_from_capfile(char *, char **); 52 static int be_promote_zone_ds(char *, char *); 53 static int be_promote_ds_callback(zfs_handle_t *, void *); 54 55 /* ******************************************************************** */ 56 /* Public Functions */ 57 /* ******************************************************************** */ 58 59 /* 60 * Function: be_activate 61 * Description: Calls _be_activate which activates the BE named in the 62 * attributes passed in through be_attrs. The process of 63 * activation sets the bootfs property of the root pool, resets 64 * the canmount property to noauto, and sets the default in the 65 * grub menu to the entry corresponding to the entry for the named 66 * BE. 67 * Parameters: 68 * be_attrs - pointer to nvlist_t of attributes being passed in. 69 * The follow attribute values are used by this function: 70 * 71 * BE_ATTR_ORIG_BE_NAME *required 72 * Return: 73 * BE_SUCCESS - Success 74 * be_errno_t - Failure 75 * Scope: 76 * Public 77 */ 78 int 79 be_activate(nvlist_t *be_attrs) 80 { 81 int ret = BE_SUCCESS; 82 char *be_name = NULL; 83 84 /* Initialize libzfs handle */ 85 if (!be_zfs_init()) 86 return (BE_ERR_INIT); 87 88 /* Get the BE name to activate */ 89 if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name) 90 != 0) { 91 be_print_err(gettext("be_activate: failed to " 92 "lookup BE_ATTR_ORIG_BE_NAME attribute\n")); 93 be_zfs_fini(); 94 return (BE_ERR_INVAL); 95 } 96 97 /* Validate BE name */ 98 if (!be_valid_be_name(be_name)) { 99 be_print_err(gettext("be_activate: invalid BE name %s\n"), 100 be_name); 101 be_zfs_fini(); 102 return (BE_ERR_INVAL); 103 } 104 105 ret = _be_activate(be_name); 106 107 be_zfs_fini(); 108 109 return (ret); 110 } 111 112 /* ******************************************************************** */ 113 /* Semi Private Functions */ 114 /* ******************************************************************** */ 115 116 /* 117 * Function: _be_activate 118 * Description: This does the actual work described in be_activate. 119 * Parameters: 120 * be_name - pointer to the name of BE to activate. 121 * 122 * Return: 123 * BE_SUCCESS - Success 124 * be_errnot_t - Failure 125 * Scope: 126 * Public 127 */ 128 int 129 _be_activate(char *be_name) 130 { 131 be_transaction_data_t cb = { 0 }; 132 zfs_handle_t *zhp = NULL; 133 char root_ds[MAXPATHLEN]; 134 char *cur_vers = NULL, *new_vers = NULL; 135 be_node_list_t *be_nodes = NULL; 136 uuid_t uu = {0}; 137 int entry, ret = BE_SUCCESS; 138 int zret = 0; 139 140 /* 141 * TODO: The BE needs to be validated to make sure that it is actually 142 * a bootable BE. 143 */ 144 145 if (be_name == NULL) 146 return (BE_ERR_INVAL); 147 148 /* Set obe_name to be_name in the cb structure */ 149 cb.obe_name = be_name; 150 151 /* find which zpool the be is in */ 152 if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &cb)) == 0) { 153 be_print_err(gettext("be_activate: failed to " 154 "find zpool for BE (%s)\n"), cb.obe_name); 155 return (BE_ERR_BE_NOENT); 156 } else if (zret < 0) { 157 be_print_err(gettext("be_activate: " 158 "zpool_iter failed: %s\n"), 159 libzfs_error_description(g_zfs)); 160 ret = zfs_err_to_be_err(g_zfs); 161 return (ret); 162 } 163 164 be_make_root_ds(cb.obe_zpool, cb.obe_name, root_ds, sizeof (root_ds)); 165 cb.obe_root_ds = strdup(root_ds); 166 167 if (getzoneid() == GLOBAL_ZONEID) { 168 if (be_has_grub() && (ret = be_get_grub_vers(&cb, &cur_vers, 169 &new_vers)) != BE_SUCCESS) { 170 be_print_err(gettext("be_activate: failed to get grub " 171 "versions from capability files.\n")); 172 return (ret); 173 } 174 if (cur_vers != NULL) { 175 /* 176 * We need to check to see if the version number from 177 * the BE being activated is greater than the current 178 * one. 179 */ 180 if (new_vers != NULL && 181 atof(cur_vers) < atof(new_vers)) { 182 if ((ret = be_do_installgrub(&cb)) 183 != BE_SUCCESS) { 184 free(new_vers); 185 free(cur_vers); 186 return (ret); 187 } 188 free(new_vers); 189 } 190 free(cur_vers); 191 } else if (new_vers != NULL) { 192 if ((ret = be_do_installgrub(&cb)) != BE_SUCCESS) { 193 free(new_vers); 194 return (ret); 195 } 196 free(new_vers); 197 } 198 if (!be_has_menu_entry(root_ds, cb.obe_zpool, &entry)) { 199 if ((ret = be_append_menu(cb.obe_name, cb.obe_zpool, 200 NULL, NULL, NULL)) != BE_SUCCESS) { 201 be_print_err(gettext("be_activate: Failed to " 202 "add BE (%s) to the GRUB menu\n"), 203 cb.obe_name); 204 goto done; 205 } 206 } 207 if (be_has_grub()) { 208 if ((ret = be_change_grub_default(cb.obe_name, 209 cb.obe_zpool)) != BE_SUCCESS) { 210 be_print_err(gettext("be_activate: failed to " 211 "change the default entry in menu.lst\n")); 212 goto done; 213 } 214 } 215 } 216 217 if ((ret = _be_list(cb.obe_name, &be_nodes)) != BE_SUCCESS) { 218 return (ret); 219 } 220 221 if ((ret = set_canmount(be_nodes, "noauto")) != BE_SUCCESS) { 222 be_print_err(gettext("be_activate: failed to set " 223 "canmount dataset property\n")); 224 goto done; 225 } 226 227 if ((ret = set_bootfs(be_nodes->be_rpool, root_ds)) != BE_SUCCESS) { 228 be_print_err(gettext("be_activate: failed to set " 229 "bootfs pool property for %s\n"), root_ds); 230 goto done; 231 } 232 233 if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) != NULL) { 234 /* 235 * We don't need to close the zfs handle at this 236 * point because The callback funtion 237 * be_promote_ds_callback() will close it for us. 238 */ 239 if (be_promote_ds_callback(zhp, NULL) != 0) { 240 be_print_err(gettext("be_activate: " 241 "failed to activate the " 242 "datasets for %s: %s\n"), 243 root_ds, 244 libzfs_error_description(g_zfs)); 245 ret = BE_ERR_PROMOTE; 246 goto done; 247 } 248 } else { 249 be_print_err(gettext("be_activate:: failed to open " 250 "dataset (%s): %s\n"), root_ds, 251 libzfs_error_description(g_zfs)); 252 ret = zfs_err_to_be_err(g_zfs); 253 goto done; 254 } 255 256 if (getzoneid() == GLOBAL_ZONEID && 257 be_get_uuid(cb.obe_root_ds, &uu) == BE_SUCCESS && 258 (ret = be_promote_zone_ds(cb.obe_name, cb.obe_root_ds)) 259 != BE_SUCCESS) { 260 be_print_err(gettext("be_activate: failed to promote " 261 "the active zonepath datasets for zones in BE %s\n"), 262 cb.obe_name); 263 } 264 265 done: 266 be_free_list(be_nodes); 267 return (ret); 268 } 269 270 /* 271 * Function: be_activate_current_be 272 * Description: Set the currently "active" BE to be "active on boot" 273 * Paramters: 274 * none 275 * Returns: 276 * BE_SUCCESS - Success 277 * be_errnot_t - Failure 278 * Scope: 279 * Semi-private (library wide use only) 280 */ 281 int 282 be_activate_current_be(void) 283 { 284 int ret = BE_SUCCESS; 285 be_transaction_data_t bt = { 0 }; 286 287 if ((ret = be_find_current_be(&bt)) != BE_SUCCESS) { 288 return (ret); 289 } 290 291 if ((ret = _be_activate(bt.obe_name)) != BE_SUCCESS) { 292 be_print_err(gettext("be_activate_current_be: failed to " 293 "activate %s\n"), bt.obe_name); 294 return (ret); 295 } 296 297 return (BE_SUCCESS); 298 } 299 300 /* 301 * Function: be_is_active_on_boot 302 * Description: Checks if the BE name passed in has the "active on boot" 303 * property set to B_TRUE. 304 * Paramters: 305 * be_name - the name of the BE to check 306 * Returns: 307 * B_TRUE - if active on boot. 308 * B_FALSE - if not active on boot. 309 * Scope: 310 * Semi-private (library wide use only) 311 */ 312 boolean_t 313 be_is_active_on_boot(char *be_name) 314 { 315 be_node_list_t *be_node = NULL; 316 317 if (be_name == NULL) { 318 be_print_err(gettext("be_is_active_on_boot: " 319 "be_name must not be NULL\n")); 320 return (B_FALSE); 321 } 322 323 if (_be_list(be_name, &be_node) != BE_SUCCESS) { 324 return (B_FALSE); 325 } 326 327 if (be_node == NULL) { 328 return (B_FALSE); 329 } 330 331 if (be_node->be_active_on_boot) { 332 be_free_list(be_node); 333 return (B_TRUE); 334 } else { 335 be_free_list(be_node); 336 return (B_FALSE); 337 } 338 } 339 340 /* ******************************************************************** */ 341 /* Private Functions */ 342 /* ******************************************************************** */ 343 344 /* 345 * Function: set_bootfs 346 * Description: Sets the bootfs property on the boot pool to be the 347 * root dataset of the activated BE. 348 * Parameters: 349 * boot_pool - The pool we're setting bootfs in. 350 * be_root_ds - The main dataset for the BE. 351 * Return: 352 * BE_SUCCESS - Success 353 * be_errno_t - Failure 354 * Scope: 355 * Private 356 */ 357 static int 358 set_bootfs(char *boot_rpool, char *be_root_ds) 359 { 360 zpool_handle_t *zhp; 361 int err = BE_SUCCESS; 362 363 if ((zhp = zpool_open(g_zfs, boot_rpool)) == NULL) { 364 be_print_err(gettext("set_bootfs: failed to open pool " 365 "(%s): %s\n"), boot_rpool, libzfs_error_description(g_zfs)); 366 err = zfs_err_to_be_err(g_zfs); 367 return (err); 368 } 369 370 err = zpool_set_prop(zhp, "bootfs", be_root_ds); 371 if (err) { 372 be_print_err(gettext("set_bootfs: failed to set " 373 "bootfs property for pool %s: %s\n"), boot_rpool, 374 libzfs_error_description(g_zfs)); 375 err = zfs_err_to_be_err(g_zfs); 376 zpool_close(zhp); 377 return (err); 378 } 379 380 zpool_close(zhp); 381 return (BE_SUCCESS); 382 } 383 384 /* 385 * Function: set_canmount 386 * Description: Sets the canmount property on the datasets of the 387 * activated BE. 388 * Parameters: 389 * be_nodes - The be_node_t returned from be_list 390 * value - The value of canmount we setting, on|off|noauto. 391 * Return: 392 * BE_SUCCESS - Success 393 * be_errno_t - Failure 394 * Scope: 395 * Private 396 */ 397 static int 398 set_canmount(be_node_list_t *be_nodes, char *value) 399 { 400 char ds_path[MAXPATHLEN]; 401 zfs_handle_t *zhp = NULL; 402 be_node_list_t *list = be_nodes; 403 int err = BE_SUCCESS; 404 405 while (list != NULL) { 406 be_dataset_list_t *datasets = list->be_node_datasets; 407 408 be_make_root_ds(list->be_rpool, list->be_node_name, ds_path, 409 sizeof (ds_path)); 410 411 if ((zhp = zfs_open(g_zfs, ds_path, ZFS_TYPE_DATASET)) == 412 NULL) { 413 be_print_err(gettext("set_canmount: failed to open " 414 "dataset (%s): %s\n"), ds_path, 415 libzfs_error_description(g_zfs)); 416 err = zfs_err_to_be_err(g_zfs); 417 return (err); 418 } 419 if (zfs_prop_get_int(zhp, ZFS_PROP_MOUNTED)) { 420 /* 421 * it's already mounted so we can't change the 422 * canmount property anyway. 423 */ 424 err = BE_SUCCESS; 425 } else { 426 err = zfs_prop_set(zhp, 427 zfs_prop_to_name(ZFS_PROP_CANMOUNT), value); 428 if (err) { 429 ZFS_CLOSE(zhp); 430 be_print_err(gettext("set_canmount: failed to " 431 "set dataset property (%s): %s\n"), 432 ds_path, libzfs_error_description(g_zfs)); 433 err = zfs_err_to_be_err(g_zfs); 434 return (err); 435 } 436 } 437 ZFS_CLOSE(zhp); 438 439 while (datasets != NULL) { 440 be_make_root_ds(list->be_rpool, 441 datasets->be_dataset_name, ds_path, 442 sizeof (ds_path)); 443 444 if ((zhp = zfs_open(g_zfs, ds_path, ZFS_TYPE_DATASET)) 445 == NULL) { 446 be_print_err(gettext("set_canmount: failed to " 447 "open dataset %s: %s\n"), ds_path, 448 libzfs_error_description(g_zfs)); 449 err = zfs_err_to_be_err(g_zfs); 450 return (err); 451 } 452 if (zfs_prop_get_int(zhp, ZFS_PROP_MOUNTED)) { 453 /* 454 * it's already mounted so we can't change the 455 * canmount property anyway. 456 */ 457 err = BE_SUCCESS; 458 ZFS_CLOSE(zhp); 459 break; 460 } 461 err = zfs_prop_set(zhp, 462 zfs_prop_to_name(ZFS_PROP_CANMOUNT), value); 463 if (err) { 464 ZFS_CLOSE(zhp); 465 be_print_err(gettext("set_canmount: " 466 "Failed to set property value %s " 467 "for dataset %s: %s\n"), value, ds_path, 468 libzfs_error_description(g_zfs)); 469 err = zfs_err_to_be_err(g_zfs); 470 return (err); 471 } 472 ZFS_CLOSE(zhp); 473 datasets = datasets->be_next_dataset; 474 } 475 list = list->be_next_node; 476 } 477 return (err); 478 } 479 480 /* 481 * Function: be_get_grub_vers 482 * Description: Gets the grub version number from /boot/grub/capability. If 483 * capability file doesn't exist NULL is returned. 484 * Parameters: 485 * bt - The transaction data for the BE we're getting the grub 486 * version for. 487 * cur_vers - used to return the current version of grub from 488 * the root pool. 489 * new_vers - used to return the grub version of the BE we're 490 * activating. 491 * Return: 492 * BE_SUCCESS - Success 493 * be_errno_t - Failed to find version 494 * Scope: 495 * Private 496 */ 497 static int 498 be_get_grub_vers(be_transaction_data_t *bt, char **cur_vers, char **new_vers) 499 { 500 zfs_handle_t *zhp = NULL; 501 zfs_handle_t *pool_zhp = NULL; 502 int ret = BE_SUCCESS; 503 char cap_file[MAXPATHLEN]; 504 char *temp_mntpnt = NULL; 505 char *zpool_mntpt = NULL; 506 char *ptmp_mntpnt = NULL; 507 char *orig_mntpnt = NULL; 508 boolean_t be_mounted = B_FALSE; 509 boolean_t pool_mounted = B_FALSE; 510 511 if (!be_has_grub()) { 512 be_print_err(gettext("be_get_grub_vers: Not supported on " 513 "this architecture\n")); 514 return (BE_ERR_NOTSUP); 515 } 516 517 if (bt == NULL || bt->obe_name == NULL || bt->obe_zpool == NULL || 518 bt->obe_root_ds == NULL) { 519 be_print_err(gettext("be_get_grub_vers: Invalid BE\n")); 520 return (BE_ERR_INVAL); 521 } 522 523 if ((pool_zhp = zfs_open(g_zfs, bt->obe_zpool, ZFS_TYPE_FILESYSTEM)) == 524 NULL) { 525 be_print_err(gettext("be_get_grub_vers: zfs_open failed: %s\n"), 526 libzfs_error_description(g_zfs)); 527 return (zfs_err_to_be_err(g_zfs)); 528 } 529 530 /* 531 * Check to see if the pool's dataset is mounted. If it isn't we'll 532 * attempt to mount it. 533 */ 534 if ((ret = be_mount_pool(pool_zhp, &ptmp_mntpnt, 535 &orig_mntpnt, &pool_mounted)) != BE_SUCCESS) { 536 be_print_err(gettext("be_get_grub_vers: pool dataset " 537 "(%s) could not be mounted\n"), bt->obe_zpool); 538 ZFS_CLOSE(pool_zhp); 539 return (ret); 540 } 541 542 /* 543 * Get the mountpoint for the root pool dataset. 544 */ 545 if (!zfs_is_mounted(pool_zhp, &zpool_mntpt)) { 546 be_print_err(gettext("be_get_grub_vers: pool " 547 "dataset (%s) is not mounted. Can't set the " 548 "default BE in the grub menu.\n"), bt->obe_zpool); 549 ret = BE_ERR_NO_MENU; 550 goto cleanup; 551 } 552 553 /* 554 * get the version of the most recent grub update. 555 */ 556 (void) snprintf(cap_file, sizeof (cap_file), "%s%s", 557 zpool_mntpt, BE_CAP_FILE); 558 free(zpool_mntpt); 559 zpool_mntpt = NULL; 560 561 if ((ret = get_ver_from_capfile(cap_file, cur_vers)) != BE_SUCCESS) 562 goto cleanup; 563 564 if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) == 565 NULL) { 566 be_print_err(gettext("be_get_grub_vers: failed to " 567 "open BE root dataset (%s): %s\n"), bt->obe_root_ds, 568 libzfs_error_description(g_zfs)); 569 free(cur_vers); 570 ret = zfs_err_to_be_err(g_zfs); 571 goto cleanup; 572 } 573 if (!zfs_is_mounted(zhp, &temp_mntpnt)) { 574 if ((ret = _be_mount(bt->obe_name, &temp_mntpnt, 575 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) { 576 be_print_err(gettext("be_get_grub_vers: failed to " 577 "mount BE (%s)\n"), bt->obe_name); 578 free(*cur_vers); 579 *cur_vers = NULL; 580 ZFS_CLOSE(zhp); 581 goto cleanup; 582 } 583 be_mounted = B_TRUE; 584 } 585 ZFS_CLOSE(zhp); 586 587 /* 588 * Now get the grub version for the BE being activated. 589 */ 590 (void) snprintf(cap_file, sizeof (cap_file), "%s%s", temp_mntpnt, 591 BE_CAP_FILE); 592 ret = get_ver_from_capfile(cap_file, new_vers); 593 if (ret != BE_SUCCESS) { 594 free(*cur_vers); 595 *cur_vers = NULL; 596 } 597 if (be_mounted) 598 (void) _be_unmount(bt->obe_name, 0); 599 600 cleanup: 601 if (pool_mounted) { 602 int iret = BE_SUCCESS; 603 iret = be_unmount_pool(pool_zhp, ptmp_mntpnt, orig_mntpnt); 604 if (ret == BE_SUCCESS) 605 ret = iret; 606 free(orig_mntpnt); 607 free(ptmp_mntpnt); 608 } 609 ZFS_CLOSE(pool_zhp); 610 611 free(temp_mntpnt); 612 return (ret); 613 } 614 615 /* 616 * Function: get_ver_from_capfile 617 * Description: Parses the capability file passed in looking for the VERSION 618 * line. If found the version is returned in vers, if not then 619 * NULL is returned in vers. 620 * 621 * Parameters: 622 * file - the path to the capability file we want to parse. 623 * vers - the version string that will be passed back. 624 * Return: 625 * BE_SUCCESS - Success 626 * be_errno_t - Failed to find version 627 * Scope: 628 * Private 629 */ 630 static int 631 get_ver_from_capfile(char *file, char **vers) 632 { 633 FILE *fp = NULL; 634 char line[BUFSIZ]; 635 char *last = NULL; 636 int err = BE_SUCCESS; 637 errno = 0; 638 639 if (!be_has_grub()) { 640 be_print_err(gettext("get_ver_from_capfile: Not supported " 641 "on this architecture\n")); 642 return (BE_ERR_NOTSUP); 643 } 644 645 /* 646 * Set version string to NULL; the only case this shouldn't be set 647 * to be NULL is when we've actually found a version in the capability 648 * file, which is set below. 649 */ 650 *vers = NULL; 651 652 /* 653 * If the capability file doesn't exist, we're returning success 654 * because on older releases, the capability file did not exist 655 * so this is a valid scenario. 656 */ 657 if (access(file, F_OK) == 0) { 658 if ((fp = fopen(file, "r")) == NULL) { 659 err = errno; 660 be_print_err(gettext("get_ver_from_capfile: failed to " 661 "open file %s with error %s\n"), file, 662 strerror(err)); 663 err = errno_to_be_err(err); 664 return (err); 665 } 666 667 while (fgets(line, BUFSIZ, fp)) { 668 char *tok = strtok_r(line, "=", &last); 669 670 if (tok == NULL || tok[0] == '#') { 671 continue; 672 } else if (strcmp(tok, "VERSION") == 0) { 673 *vers = strdup(last); 674 break; 675 } 676 } 677 (void) fclose(fp); 678 } 679 680 return (BE_SUCCESS); 681 } 682 683 /* 684 * Function: be_do_installgrub 685 * Description: This function runs installgrub using the grub loader files 686 * from the BE we're activating and installing them on the 687 * pool the BE lives in. 688 * 689 * Parameters: 690 * bt - The transaction data for the BE we're activating. 691 * Return: 692 * BE_SUCCESS - Success 693 * be_errno_t - Failure 694 * 695 * Scope: 696 * Private 697 */ 698 static int 699 be_do_installgrub(be_transaction_data_t *bt) 700 { 701 zpool_handle_t *zphp = NULL; 702 zfs_handle_t *zhp = NULL; 703 nvlist_t **child, *nv, *config; 704 uint_t c, children = 0; 705 char *tmp_mntpt = NULL; 706 char *pool_mntpnt = NULL; 707 char *ptmp_mntpnt = NULL; 708 char *orig_mntpnt = NULL; 709 FILE *cap_fp = NULL; 710 FILE *zpool_cap_fp = NULL; 711 char line[BUFSIZ]; 712 char cap_file[MAXPATHLEN]; 713 char zpool_cap_file[MAXPATHLEN]; 714 char stage1[MAXPATHLEN]; 715 char stage2[MAXPATHLEN]; 716 char installgrub_cmd[MAXPATHLEN]; 717 char *vname; 718 char be_run_cmd_errbuf[BUFSIZ]; 719 int ret = BE_SUCCESS; 720 int err = 0; 721 boolean_t be_mounted = B_FALSE; 722 boolean_t pool_mounted = B_FALSE; 723 724 if (!be_has_grub()) { 725 be_print_err(gettext("be_do_installgrub: Not supported " 726 "on this architecture\n")); 727 return (BE_ERR_NOTSUP); 728 } 729 730 if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) == 731 NULL) { 732 be_print_err(gettext("be_do_installgrub: failed to " 733 "open BE root dataset (%s): %s\n"), bt->obe_root_ds, 734 libzfs_error_description(g_zfs)); 735 ret = zfs_err_to_be_err(g_zfs); 736 return (ret); 737 } 738 if (!zfs_is_mounted(zhp, &tmp_mntpt)) { 739 if ((ret = _be_mount(bt->obe_name, &tmp_mntpt, 740 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) { 741 be_print_err(gettext("be_do_installgrub: failed to " 742 "mount BE (%s)\n"), bt->obe_name); 743 ZFS_CLOSE(zhp); 744 return (ret); 745 } 746 be_mounted = B_TRUE; 747 } 748 ZFS_CLOSE(zhp); 749 750 (void) snprintf(stage1, sizeof (stage1), "%s%s", tmp_mntpt, BE_STAGE_1); 751 (void) snprintf(stage2, sizeof (stage2), "%s%s", tmp_mntpt, BE_STAGE_2); 752 753 if ((zphp = zpool_open(g_zfs, bt->obe_zpool)) == NULL) { 754 be_print_err(gettext("be_do_installgrub: failed to open " 755 "pool (%s): %s\n"), bt->obe_zpool, 756 libzfs_error_description(g_zfs)); 757 ret = zfs_err_to_be_err(g_zfs); 758 if (be_mounted) 759 (void) _be_unmount(bt->obe_name, 0); 760 free(tmp_mntpt); 761 return (ret); 762 } 763 764 if ((config = zpool_get_config(zphp, NULL)) == NULL) { 765 be_print_err(gettext("be_do_installgrub: failed to get zpool " 766 "configuration information. %s\n"), 767 libzfs_error_description(g_zfs)); 768 ret = zfs_err_to_be_err(g_zfs); 769 goto done; 770 } 771 772 /* 773 * Get the vdev tree 774 */ 775 if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nv) != 0) { 776 be_print_err(gettext("be_do_installgrub: failed to get vdev " 777 "tree: %s\n"), libzfs_error_description(g_zfs)); 778 ret = zfs_err_to_be_err(g_zfs); 779 goto done; 780 } 781 782 if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child, 783 &children) != 0) { 784 be_print_err(gettext("be_do_installgrub: failed to traverse " 785 "the vdev tree: %s\n"), libzfs_error_description(g_zfs)); 786 ret = zfs_err_to_be_err(g_zfs); 787 goto done; 788 } 789 for (c = 0; c < children; c++) { 790 uint_t i, nchildren = 0; 791 nvlist_t **nvchild; 792 vname = zpool_vdev_name(g_zfs, zphp, child[c], B_FALSE); 793 if (vname == NULL) { 794 be_print_err(gettext( 795 "be_do_installgrub: " 796 "failed to get device name: %s\n"), 797 libzfs_error_description(g_zfs)); 798 ret = zfs_err_to_be_err(g_zfs); 799 goto done; 800 } 801 if (strcmp(vname, "mirror") == 0 || vname[0] != 'c') { 802 803 if (nvlist_lookup_nvlist_array(child[c], 804 ZPOOL_CONFIG_CHILDREN, &nvchild, &nchildren) != 0) { 805 be_print_err(gettext("be_do_installgrub: " 806 "failed to traverse the vdev tree: %s\n"), 807 libzfs_error_description(g_zfs)); 808 ret = zfs_err_to_be_err(g_zfs); 809 goto done; 810 } 811 812 for (i = 0; i < nchildren; i++) { 813 vname = zpool_vdev_name(g_zfs, zphp, 814 nvchild[i], B_FALSE); 815 if (vname == NULL) { 816 be_print_err(gettext( 817 "be_do_installgrub: " 818 "failed to get device name: %s\n"), 819 libzfs_error_description(g_zfs)); 820 ret = zfs_err_to_be_err(g_zfs); 821 goto done; 822 } 823 824 (void) snprintf(installgrub_cmd, 825 sizeof (installgrub_cmd), 826 "%s %s %s /dev/rdsk/%s", 827 BE_INSTALL_GRUB, stage1, stage2, vname); 828 if (be_run_cmd(installgrub_cmd, 829 be_run_cmd_errbuf, BUFSIZ, NULL, 0) != 830 BE_SUCCESS) { 831 be_print_err(gettext( 832 "be_do_installgrub: installgrub " 833 "failed for device %s.\n"), vname); 834 /* Assume localized cmd err output. */ 835 be_print_err(gettext( 836 " Command: \"%s\"\n"), 837 installgrub_cmd); 838 be_print_err("%s", be_run_cmd_errbuf); 839 free(vname); 840 ret = BE_ERR_BOOTFILE_INST; 841 goto done; 842 } 843 free(vname); 844 } 845 } else { 846 (void) snprintf(installgrub_cmd, 847 sizeof (installgrub_cmd), "%s %s %s /dev/rdsk/%s", 848 BE_INSTALL_GRUB, stage1, stage2, vname); 849 if (be_run_cmd(installgrub_cmd, be_run_cmd_errbuf, 850 BUFSIZ, NULL, 0) != BE_SUCCESS) { 851 be_print_err(gettext( 852 "be_do_installgrub: installgrub " 853 "failed for device %s.\n"), vname); 854 /* Assume localized cmd err output. */ 855 be_print_err(gettext(" Command: \"%s\"\n"), 856 installgrub_cmd); 857 be_print_err("%s", be_run_cmd_errbuf); 858 free(vname); 859 ret = BE_ERR_BOOTFILE_INST; 860 goto done; 861 } 862 free(vname); 863 } 864 } 865 866 /* 867 * Copy the grub capability file from the BE we're activating into 868 * the root pool. 869 */ 870 (void) snprintf(cap_file, sizeof (cap_file), "%s%s", tmp_mntpt, 871 BE_CAP_FILE); 872 873 if ((zhp = zfs_open(g_zfs, bt->obe_zpool, ZFS_TYPE_FILESYSTEM)) == 874 NULL) { 875 be_print_err(gettext("be_do_installgrub: zfs_open " 876 "failed: %s\n"), libzfs_error_description(g_zfs)); 877 zpool_close(zphp); 878 return (zfs_err_to_be_err(g_zfs)); 879 } 880 881 /* 882 * Check to see if the pool's dataset is mounted. If it isn't we'll 883 * attempt to mount it. 884 */ 885 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, 886 &orig_mntpnt, &pool_mounted)) != BE_SUCCESS) { 887 be_print_err(gettext("be_do_installgrub: pool dataset " 888 "(%s) could not be mounted\n"), bt->obe_zpool); 889 ZFS_CLOSE(zhp); 890 zpool_close(zphp); 891 return (ret); 892 } 893 894 /* 895 * Get the mountpoint for the root pool dataset. 896 */ 897 if (!zfs_is_mounted(zhp, &pool_mntpnt)) { 898 be_print_err(gettext("be_do_installgrub: pool " 899 "dataset (%s) is not mounted. Can't check the grub " 900 "version from the grub capability file.\n"), bt->obe_zpool); 901 ret = BE_ERR_NO_MENU; 902 goto done; 903 } 904 905 (void) snprintf(zpool_cap_file, sizeof (zpool_cap_file), "%s%s", 906 pool_mntpnt, BE_CAP_FILE); 907 908 free(pool_mntpnt); 909 pool_mntpnt = NULL; 910 911 if ((cap_fp = fopen(cap_file, "r")) == NULL) { 912 err = errno; 913 be_print_err(gettext("be_do_installgrub: failed to open grub " 914 "capability file\n")); 915 ret = errno_to_be_err(err); 916 goto done; 917 } 918 if ((zpool_cap_fp = fopen(zpool_cap_file, "w")) == NULL) { 919 err = errno; 920 be_print_err(gettext("be_do_installgrub: failed to open new " 921 "grub capability file\n")); 922 ret = errno_to_be_err(err); 923 (void) fclose(cap_fp); 924 goto done; 925 } 926 927 while (fgets(line, BUFSIZ, cap_fp)) { 928 (void) fputs(line, zpool_cap_fp); 929 } 930 931 (void) fclose(zpool_cap_fp); 932 (void) fclose(cap_fp); 933 934 done: 935 if (pool_mounted) { 936 int iret = 0; 937 iret = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt); 938 if (ret == BE_SUCCESS) 939 ret = iret; 940 free(orig_mntpnt); 941 free(ptmp_mntpnt); 942 } 943 ZFS_CLOSE(zhp); 944 if (be_mounted) 945 (void) _be_unmount(bt->obe_name, 0); 946 zpool_close(zphp); 947 free(tmp_mntpt); 948 return (ret); 949 } 950 951 /* 952 * Function: be_promote_zone_ds 953 * Description: This function finds the zones for the BE being activated 954 * and the active zonepath dataset for each zone. Then each 955 * active zonepath dataset is promoted. 956 * 957 * Parameters: 958 * be_name - the name of the global zone BE that we need to 959 * find the zones for. 960 * be_root_ds - the root dataset for be_name. 961 * Return: 962 * BE_SUCCESS - Success 963 * be_errno_t - Failure 964 * 965 * Scope: 966 * Private 967 */ 968 static int 969 be_promote_zone_ds(char *be_name, char *be_root_ds) 970 { 971 char *zone_ds = NULL; 972 char *temp_mntpt = NULL; 973 char origin[MAXPATHLEN]; 974 char zoneroot_ds[MAXPATHLEN]; 975 zfs_handle_t *zhp = NULL; 976 zfs_handle_t *z_zhp = NULL; 977 zoneList_t zone_list = NULL; 978 zoneBrandList_t *brands = NULL; 979 boolean_t be_mounted = B_FALSE; 980 int zone_index = 0; 981 int err = BE_SUCCESS; 982 983 /* 984 * Get the supported zone brands so we can pass that 985 * to z_get_nonglobal_zone_list_by_brand. Currently 986 * only the ipkg and labeled brand zones are supported 987 * 988 */ 989 if ((brands = be_get_supported_brandlist()) == NULL) { 990 be_print_err(gettext("be_promote_zone_ds: no supported " 991 "brands\n")); 992 return (BE_SUCCESS); 993 } 994 995 if ((zhp = zfs_open(g_zfs, be_root_ds, 996 ZFS_TYPE_FILESYSTEM)) == NULL) { 997 be_print_err(gettext("be_promote_zone_ds: Failed to open " 998 "dataset (%s): %s\n"), be_root_ds, 999 libzfs_error_description(g_zfs)); 1000 err = zfs_err_to_be_err(g_zfs); 1001 z_free_brand_list(brands); 1002 return (err); 1003 } 1004 1005 if (!zfs_is_mounted(zhp, &temp_mntpt)) { 1006 if ((err = _be_mount(be_name, &temp_mntpt, 1007 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) { 1008 be_print_err(gettext("be_promote_zone_ds: failed to " 1009 "mount the BE for zones procesing.\n")); 1010 ZFS_CLOSE(zhp); 1011 z_free_brand_list(brands); 1012 return (err); 1013 } 1014 be_mounted = B_TRUE; 1015 } 1016 1017 /* 1018 * Set the zone root to the temp mount point for the BE we just mounted. 1019 */ 1020 z_set_zone_root(temp_mntpt); 1021 1022 /* 1023 * Get all the zones based on the brands we're looking for. If no zones 1024 * are found that we're interested in unmount the BE and move on. 1025 */ 1026 if ((zone_list = z_get_nonglobal_zone_list_by_brand(brands)) == NULL) { 1027 if (be_mounted) 1028 (void) _be_unmount(be_name, 0); 1029 ZFS_CLOSE(zhp); 1030 z_free_brand_list(brands); 1031 free(temp_mntpt); 1032 return (BE_SUCCESS); 1033 } 1034 for (zone_index = 0; z_zlist_get_zonename(zone_list, zone_index) 1035 != NULL; zone_index++) { 1036 char *zone_path = NULL; 1037 1038 /* Skip zones that aren't at least installed */ 1039 if (z_zlist_get_current_state(zone_list, zone_index) < 1040 ZONE_STATE_INSTALLED) 1041 continue; 1042 1043 if (((zone_path = 1044 z_zlist_get_zonepath(zone_list, zone_index)) == NULL) || 1045 ((zone_ds = be_get_ds_from_dir(zone_path)) == NULL) || 1046 !be_zone_supported(zone_ds)) 1047 continue; 1048 1049 if (be_find_active_zone_root(zhp, zone_ds, 1050 zoneroot_ds, sizeof (zoneroot_ds)) != 0) { 1051 be_print_err(gettext("be_promote_zone_ds: " 1052 "Zone does not have an active root " 1053 "dataset, skipping this zone.\n")); 1054 continue; 1055 } 1056 1057 if ((z_zhp = zfs_open(g_zfs, zoneroot_ds, 1058 ZFS_TYPE_FILESYSTEM)) == NULL) { 1059 be_print_err(gettext("be_promote_zone_ds: " 1060 "Failed to open dataset " 1061 "(%s): %s\n"), zoneroot_ds, 1062 libzfs_error_description(g_zfs)); 1063 err = zfs_err_to_be_err(g_zfs); 1064 goto done; 1065 } 1066 1067 if (zfs_prop_get(z_zhp, ZFS_PROP_ORIGIN, origin, 1068 sizeof (origin), NULL, NULL, 0, B_FALSE) != 0) { 1069 ZFS_CLOSE(z_zhp); 1070 continue; 1071 } 1072 1073 /* 1074 * We don't need to close the zfs handle at this 1075 * point because the callback funtion 1076 * be_promote_ds_callback() will close it for us. 1077 */ 1078 if (be_promote_ds_callback(z_zhp, NULL) != 0) { 1079 be_print_err(gettext("be_promote_zone_ds: " 1080 "failed to activate the " 1081 "datasets for %s: %s\n"), 1082 zoneroot_ds, 1083 libzfs_error_description(g_zfs)); 1084 err = BE_ERR_PROMOTE; 1085 goto done; 1086 } 1087 } 1088 done: 1089 if (be_mounted) 1090 (void) _be_unmount(be_name, 0); 1091 ZFS_CLOSE(zhp); 1092 free(temp_mntpt); 1093 z_free_brand_list(brands); 1094 z_free_zone_list(zone_list); 1095 return (err); 1096 } 1097 1098 /* 1099 * Function: be_promote_ds_callback 1100 * Description: This function is used to promote the datasets for the BE 1101 * being activated as well as the datasets for the zones BE 1102 * being activated. 1103 * 1104 * Parameters: 1105 * zhp - the zfs handle for zone BE being activated. 1106 * data - not used. 1107 * Return: 1108 * 0 - Success 1109 * be_errno_t - Failure 1110 * 1111 * Scope: 1112 * Private 1113 */ 1114 static int 1115 /* LINTED */ 1116 be_promote_ds_callback(zfs_handle_t *zhp, void *data) 1117 { 1118 char origin[MAXPATHLEN]; 1119 char *sub_dataset = NULL; 1120 int ret = 0; 1121 1122 if (zhp != NULL) { 1123 sub_dataset = strdup(zfs_get_name(zhp)); 1124 if (sub_dataset == NULL) { 1125 ret = BE_ERR_NOMEM; 1126 goto done; 1127 } 1128 } else { 1129 be_print_err(gettext("be_promote_ds_callback: " 1130 "Invalid zfs handle passed into function\n")); 1131 ret = BE_ERR_INVAL; 1132 goto done; 1133 } 1134 1135 /* 1136 * This loop makes sure that we promote the dataset to the 1137 * top of the tree so that it is no longer a decendent of any 1138 * dataset. The ZFS close and then open is used to make sure that 1139 * the promotion is updated before we move on. 1140 */ 1141 while (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, 1142 sizeof (origin), NULL, NULL, 0, B_FALSE) == 0) { 1143 if (zfs_promote(zhp) != 0) { 1144 if (libzfs_errno(g_zfs) != EZFS_EXISTS) { 1145 be_print_err(gettext("be_promote_ds_callback: " 1146 "promote of %s failed: %s\n"), 1147 zfs_get_name(zhp), 1148 libzfs_error_description(g_zfs)); 1149 ret = zfs_err_to_be_err(g_zfs); 1150 goto done; 1151 } else { 1152 /* 1153 * If the call to zfs_promote returns the 1154 * error EZFS_EXISTS we've hit a snapshot name 1155 * collision. This means we're probably 1156 * attemping to promote a zone dataset above a 1157 * parent dataset that belongs to another zone 1158 * which this zone was cloned from. 1159 * 1160 * TODO: If this is a zone dataset at some 1161 * point we should skip this if the zone 1162 * paths for the dataset and the snapshot 1163 * don't match. 1164 */ 1165 be_print_err(gettext("be_promote_ds_callback: " 1166 "promote of %s failed due to snapshot " 1167 "name collision: %s\n"), zfs_get_name(zhp), 1168 libzfs_error_description(g_zfs)); 1169 ret = zfs_err_to_be_err(g_zfs); 1170 goto done; 1171 } 1172 } 1173 ZFS_CLOSE(zhp); 1174 if ((zhp = zfs_open(g_zfs, sub_dataset, 1175 ZFS_TYPE_FILESYSTEM)) == NULL) { 1176 be_print_err(gettext("be_promote_ds_callback: " 1177 "Failed to open dataset (%s): %s\n"), sub_dataset, 1178 libzfs_error_description(g_zfs)); 1179 ret = zfs_err_to_be_err(g_zfs); 1180 goto done; 1181 } 1182 } 1183 1184 /* Iterate down this dataset's children and promote them */ 1185 ret = zfs_iter_filesystems(zhp, be_promote_ds_callback, NULL); 1186 1187 done: 1188 free(sub_dataset); 1189 ZFS_CLOSE(zhp); 1190 return (ret); 1191 }