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) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 28 */ 29 30 /* 31 * System includes 32 */ 33 34 #include <assert.h> 35 #include <stdio.h> 36 #include <strings.h> 37 #include <libzfs.h> 38 #include <locale.h> 39 #include <langinfo.h> 40 #include <stdlib.h> 41 #include <wchar.h> 42 #include <sys/types.h> 43 44 #include "libbe.h" 45 46 #ifndef lint 47 #define _(x) gettext(x) 48 #else 49 #define _(x) (x) 50 #endif 51 52 #ifndef TEXT_DOMAIN 53 #define TEXT_DOMAIN "SYS_TEST" 54 #endif 55 56 #define DT_BUF_LEN (128) 57 #define NUM_COLS (6) 58 59 static int be_do_activate(int argc, char **argv); 60 static int be_do_create(int argc, char **argv); 61 static int be_do_destroy(int argc, char **argv); 62 static int be_do_list(int argc, char **argv); 63 static int be_do_mount(int argc, char **argv); 64 static int be_do_unmount(int argc, char **argv); 65 static int be_do_rename(int argc, char **argv); 66 static int be_do_rollback(int argc, char **argv); 67 static void usage(void); 68 69 /* 70 * single column name/width output format description 71 */ 72 struct col_info { 73 const char *col_name; 74 size_t width; 75 }; 76 77 /* 78 * all columns output format 79 */ 80 struct hdr_info { 81 struct col_info cols[NUM_COLS]; 82 }; 83 84 /* 85 * type of possible output formats 86 */ 87 enum be_fmt { 88 BE_FMT_DEFAULT, 89 BE_FMT_DATASET, 90 BE_FMT_SNAPSHOT, 91 BE_FMT_ALL 92 }; 93 94 /* 95 * command handler description 96 */ 97 typedef struct be_command { 98 const char *name; 99 int (*func)(int argc, char **argv); 100 } be_command_t; 101 102 /* 103 * sorted list of be commands 104 */ 105 static const be_command_t be_command_tbl[] = { 106 { "activate", be_do_activate }, 107 { "create", be_do_create }, 108 { "destroy", be_do_destroy }, 109 { "list", be_do_list }, 110 { "mount", be_do_mount }, 111 { "unmount", be_do_unmount }, 112 { "umount", be_do_unmount }, /* unmount alias */ 113 { "rename", be_do_rename }, 114 { "rollback", be_do_rollback }, 115 { NULL, NULL }, 116 }; 117 118 static void 119 usage(void) 120 { 121 (void) fprintf(stderr, _("usage:\n" 122 "\tbeadm subcommand cmd_options\n" 123 "\n" 124 "\tsubcommands:\n" 125 "\n" 126 "\tbeadm activate [-v] beName\n" 127 "\tbeadm create [-a] [-d BE_desc]\n" 128 "\t\t[-o property=value] ... [-p zpool] \n" 129 "\t\t[-e nonActiveBe | beName@snapshot] [-v] beName\n" 130 "\tbeadm create [-d BE_desc]\n" 131 "\t\t[-o property=value] ... [-p zpool] [-v] beName@snapshot\n" 132 "\tbeadm destroy [-Ffsv] beName \n" 133 "\tbeadm destroy [-Fv] beName@snapshot \n" 134 "\tbeadm list [[-a] | [-d] [-s]] [-H] [-v] [beName]\n" 135 "\tbeadm mount [-s ro|rw] [-v] beName [mountpoint]\n" 136 "\tbeadm unmount [-fv] beName | mountpoint\n" 137 "\tbeadm umount [-fv] beName | mountpoint\n" 138 "\tbeadm rename [-v] origBeName newBeName\n" 139 "\tbeadm rollback [-v] beName snapshot\n" 140 "\tbeadm rollback [-v] beName@snapshot\n")); 141 } 142 143 static int 144 run_be_cmd(const char *cmdname, int argc, char **argv) 145 { 146 const be_command_t *command; 147 148 for (command = &be_command_tbl[0]; command->name != NULL; command++) 149 if (strcmp(command->name, cmdname) == 0) 150 return (command->func(argc, argv)); 151 152 (void) fprintf(stderr, _("Invalid command: %s\n"), cmdname); 153 usage(); 154 return (1); 155 } 156 157 int 158 main(int argc, char **argv) 159 { 160 const char *cmdname; 161 162 (void) setlocale(LC_ALL, ""); 163 (void) textdomain(TEXT_DOMAIN); 164 165 if (argc < 2) { 166 usage(); 167 return (1); 168 } 169 170 cmdname = argv[1]; 171 172 /* Turn error printing off */ 173 libbe_print_errors(B_FALSE); 174 175 return (run_be_cmd(cmdname, --argc, ++argv)); 176 } 177 178 static void 179 print_hdr(struct hdr_info *hdr_info) 180 { 181 boolean_t first = B_TRUE; 182 size_t i; 183 for (i = 0; i < NUM_COLS; i++) { 184 struct col_info *col_info = &hdr_info->cols[i]; 185 const char *name = col_info->col_name; 186 size_t width = col_info->width; 187 if (name == NULL) 188 continue; 189 190 if (first) { 191 (void) printf("%-*s", width, name); 192 first = B_FALSE; 193 } else 194 (void) printf(" %-*s", width, name); 195 } 196 (void) putchar('\n'); 197 } 198 199 static void 200 init_hdr_cols(enum be_fmt be_fmt, struct hdr_info *hdr) 201 { 202 struct col_info *col = hdr->cols; 203 size_t i; 204 205 col[1].col_name = _("Active"); 206 col[2].col_name = _("Mountpoint"); 207 col[3].col_name = _("Space"); 208 col[4].col_name = _("Policy"); 209 col[5].col_name = _("Created"); 210 col[6].col_name = NULL; 211 212 switch (be_fmt) { 213 case BE_FMT_ALL: 214 col[0].col_name = _("BE/Dataset/Snapshot"); 215 break; 216 case BE_FMT_DATASET: 217 col[0].col_name = _("BE/Dataset"); 218 break; 219 case BE_FMT_SNAPSHOT: 220 col[0].col_name = _("BE/Snapshot"); 221 col[1].col_name = NULL; 222 col[2].col_name = NULL; 223 break; 224 case BE_FMT_DEFAULT: 225 default: 226 col[0].col_name = _("BE"); 227 } 228 229 for (i = 0; i < NUM_COLS; i++) { 230 const char *name = col[i].col_name; 231 col[i].width = 0; 232 233 if (name != NULL) { 234 wchar_t wname[128]; 235 size_t sz = mbstowcs(wname, name, sizeof (wname) / 236 sizeof (wchar_t)); 237 if (sz > 0) { 238 int wcsw = wcswidth(wname, sz); 239 if (wcsw > 0) 240 col[i].width = wcsw; 241 else 242 col[i].width = sz; 243 } else { 244 col[i].width = strlen(name); 245 } 246 } 247 } 248 } 249 250 static void 251 nicenum(uint64_t num, char *buf, size_t buflen) 252 { 253 uint64_t n = num; 254 int index = 0; 255 char u; 256 257 while (n >= 1024) { 258 n /= 1024; 259 index++; 260 } 261 262 u = " KMGTPE"[index]; 263 264 if (index == 0) { 265 (void) snprintf(buf, buflen, "%llu", n); 266 } else { 267 int i; 268 for (i = 2; i >= 0; i--) { 269 if (snprintf(buf, buflen, "%.*f%c", i, 270 (double)num / (1ULL << 10 * index), u) <= 5) 271 break; 272 } 273 } 274 } 275 276 static void 277 count_widths(enum be_fmt be_fmt, struct hdr_info *hdr, be_node_list_t *be_nodes) 278 { 279 size_t len[NUM_COLS]; 280 char buf[DT_BUF_LEN]; 281 int i; 282 be_node_list_t *cur_be; 283 284 for (i = 0; i < NUM_COLS; i++) 285 len[i] = hdr->cols[i].width; 286 287 for (cur_be = be_nodes; cur_be != NULL; cur_be = cur_be->be_next_node) { 288 char name[ZFS_MAXNAMELEN+1]; 289 const char *be_name = cur_be->be_node_name; 290 const char *root_ds = cur_be->be_root_ds; 291 char *pos; 292 size_t node_name_len = strlen(cur_be->be_node_name); 293 size_t root_ds_len = strlen(cur_be->be_root_ds); 294 size_t mntpt_len = 0; 295 size_t policy_len = 0; 296 size_t used_len; 297 uint64_t used = cur_be->be_space_used; 298 be_snapshot_list_t *snap = NULL; 299 300 if (cur_be->be_mntpt != NULL) 301 mntpt_len = strlen(cur_be->be_mntpt); 302 if (cur_be->be_policy_type != NULL) 303 policy_len = strlen(cur_be->be_policy_type); 304 305 (void) strlcpy(name, root_ds, sizeof (name)); 306 pos = strstr(name, be_name); 307 308 if (be_fmt == BE_FMT_DEFAULT) { 309 if (node_name_len > len[0]) 310 len[0] = node_name_len; 311 } else { 312 if (root_ds_len + 3 > len[0]) 313 len[0] = root_ds_len + 3; 314 } 315 316 if (mntpt_len > len[2]) 317 len[2] = mntpt_len; 318 if (policy_len > len[4]) 319 len[4] = policy_len; 320 321 for (snap = cur_be->be_node_snapshots; snap != NULL; 322 snap = snap->be_next_snapshot) { 323 uint64_t snap_used = snap->be_snapshot_space_used; 324 const char *snap_name = snap->be_snapshot_name; 325 (void) strcpy(pos, snap_name); 326 327 if (be_fmt == BE_FMT_DEFAULT) 328 used += snap_used; 329 else if (be_fmt & BE_FMT_SNAPSHOT) { 330 int snap_len = strlen(name) + 3; 331 if (be_fmt == BE_FMT_SNAPSHOT) 332 snap_len -= pos - name; 333 if (snap_len > len[0]) 334 len[0] = snap_len; 335 nicenum(snap_used, buf, sizeof (buf)); 336 used_len = strlen(buf); 337 if (used_len > len[3]) 338 len[3] = used_len; 339 } 340 } 341 342 if (be_fmt == BE_FMT_DEFAULT) { 343 int used_len; 344 nicenum(used, buf, sizeof (buf)); 345 used_len = strlen(buf); 346 if (used_len > len[3]) 347 len[3] = used_len; 348 } 349 350 nicenum(used, buf, sizeof (buf)); 351 } 352 353 for (i = 0; i < NUM_COLS; i++) 354 hdr->cols[i].width = len[i]; 355 } 356 357 static void 358 print_be_nodes(const char *be_name, boolean_t parsable, struct hdr_info *hdr, 359 be_node_list_t *nodes) 360 { 361 char buf[64]; 362 char datetime[DT_BUF_LEN]; 363 be_node_list_t *cur_be; 364 365 for (cur_be = nodes; cur_be != NULL; cur_be = cur_be->be_next_node) { 366 char active[3] = "-\0"; 367 int ai = 0; 368 const char *datetime_fmt = "%F %R"; 369 const char *name = cur_be->be_node_name; 370 const char *mntpt = cur_be->be_mntpt; 371 be_snapshot_list_t *snap = NULL; 372 uint64_t used = cur_be->be_space_used; 373 time_t creation = cur_be->be_node_creation; 374 struct tm *tm; 375 376 if (be_name != NULL && strcmp(be_name, name) != 0) 377 continue; 378 379 if (parsable) 380 active[0] = '\0'; 381 382 tm = localtime(&creation); 383 (void) strftime(datetime, DT_BUF_LEN, datetime_fmt, tm); 384 385 for (snap = cur_be->be_node_snapshots; snap != NULL; 386 snap = snap->be_next_snapshot) 387 used += snap->be_snapshot_space_used; 388 389 if (!cur_be->be_global_active) 390 active[ai++] = 'x'; 391 392 if (cur_be->be_active) 393 active[ai++] = 'N'; 394 if (cur_be->be_active_on_boot) { 395 if (!cur_be->be_global_active) 396 active[ai] = 'b'; 397 else 398 active[ai] = 'R'; 399 } 400 401 nicenum(used, buf, sizeof (buf)); 402 if (parsable) 403 (void) printf("%s;%s;%s;%s;%llu;%s;%ld\n", 404 name, 405 cur_be->be_uuid_str, 406 active, 407 (cur_be->be_mounted ? mntpt: ""), 408 used, 409 cur_be->be_policy_type, 410 creation); 411 else 412 (void) printf("%-*s %-*s %-*s %-*s %-*s %-*s\n", 413 hdr->cols[0].width, name, 414 hdr->cols[1].width, active, 415 hdr->cols[2].width, (cur_be->be_mounted ? mntpt: 416 "-"), 417 hdr->cols[3].width, buf, 418 hdr->cols[4].width, cur_be->be_policy_type, 419 hdr->cols[5].width, datetime); 420 } 421 } 422 423 static void 424 print_be_snapshots(be_node_list_t *be, struct hdr_info *hdr, boolean_t parsable) 425 { 426 char buf[64]; 427 char datetime[DT_BUF_LEN]; 428 be_snapshot_list_t *snap = NULL; 429 430 for (snap = be->be_node_snapshots; snap != NULL; 431 snap = snap->be_next_snapshot) { 432 char name[ZFS_MAXNAMELEN+1]; 433 const char *datetime_fmt = "%F %R"; 434 const char *be_name = be->be_node_name; 435 const char *root_ds = be->be_root_ds; 436 const char *snap_name = snap->be_snapshot_name; 437 char *pos; 438 uint64_t used = snap->be_snapshot_space_used; 439 time_t creation = snap->be_snapshot_creation; 440 struct tm *tm = localtime(&creation); 441 442 (void) strncpy(name, root_ds, sizeof (name)); 443 pos = strstr(name, be_name); 444 (void) strcpy(pos, snap_name); 445 446 (void) strftime(datetime, DT_BUF_LEN, datetime_fmt, tm); 447 nicenum(used, buf, sizeof (buf)); 448 449 if (parsable) 450 if (hdr->cols[1].width != 0) 451 (void) printf("%s;%s;%s;%s;%llu;%s;%ld\n", 452 be_name, 453 snap_name, 454 "", 455 "", 456 used, 457 be->be_policy_type, 458 creation); 459 else 460 (void) printf("%s;%s;%llu;%s;%ld\n", 461 be_name, 462 snap_name, 463 used, 464 be->be_policy_type, 465 creation); 466 else 467 if (hdr->cols[1].width != 0) 468 (void) printf(" %-*s %-*s %-*s %-*s %-*s " 469 "%-*s\n", 470 hdr->cols[0].width-3, name, 471 hdr->cols[1].width, "-", 472 hdr->cols[2].width, "-", 473 hdr->cols[3].width, buf, 474 hdr->cols[4].width, be->be_policy_type, 475 hdr->cols[5].width, datetime); 476 else 477 (void) printf(" %-*s %-*s %-*s %-*s\n", 478 hdr->cols[0].width-3, snap_name, 479 hdr->cols[3].width, buf, 480 hdr->cols[4].width, be->be_policy_type, 481 hdr->cols[5].width, datetime); 482 } 483 } 484 485 static void 486 print_fmt_nodes(const char *be_name, enum be_fmt be_fmt, boolean_t parsable, 487 struct hdr_info *hdr, be_node_list_t *nodes) 488 { 489 char buf[64]; 490 char datetime[DT_BUF_LEN]; 491 be_node_list_t *cur_be; 492 493 for (cur_be = nodes; cur_be != NULL; cur_be = cur_be->be_next_node) { 494 char active[3] = "-\0"; 495 int ai = 0; 496 const char *datetime_fmt = "%F %R"; 497 const char *name = cur_be->be_node_name; 498 const char *mntpt = cur_be->be_mntpt; 499 uint64_t used = cur_be->be_space_used; 500 time_t creation = cur_be->be_node_creation; 501 struct tm *tm; 502 503 if (be_name != NULL && strcmp(be_name, name) != 0) 504 continue; 505 506 if (!parsable) 507 (void) printf("%-s\n", name); 508 else 509 active[0] = '\0'; 510 511 tm = localtime(&creation); 512 (void) strftime(datetime, DT_BUF_LEN, datetime_fmt, tm); 513 514 if (cur_be->be_active) 515 active[ai++] = 'N'; 516 if (cur_be->be_active_on_boot) 517 active[ai] = 'R'; 518 519 nicenum(used, buf, sizeof (buf)); 520 if (be_fmt & BE_FMT_DATASET) 521 if (parsable) 522 (void) printf("%s;%s;%s;%s;%llu;%s;%ld\n", 523 cur_be->be_node_name, 524 cur_be->be_root_ds, 525 active, 526 (cur_be->be_mounted ? mntpt: ""), 527 used, 528 cur_be->be_policy_type, 529 creation); 530 else 531 (void) printf(" %-*s %-*s %-*s %-*s %-*s " 532 "%-*s\n", 533 hdr->cols[0].width-3, cur_be->be_root_ds, 534 hdr->cols[1].width, active, 535 hdr->cols[2].width, (cur_be->be_mounted ? 536 mntpt: "-"), 537 hdr->cols[3].width, buf, 538 hdr->cols[4].width, cur_be->be_policy_type, 539 hdr->cols[5].width, datetime); 540 541 if (be_fmt & BE_FMT_SNAPSHOT) 542 print_be_snapshots(cur_be, hdr, parsable); 543 } 544 } 545 546 static void 547 print_nodes(const char *be_name, boolean_t dsets, boolean_t snaps, 548 boolean_t parsable, be_node_list_t *be_nodes) 549 { 550 struct hdr_info hdr; 551 enum be_fmt be_fmt = BE_FMT_DEFAULT; 552 553 if (dsets) 554 be_fmt |= BE_FMT_DATASET; 555 if (snaps) 556 be_fmt |= BE_FMT_SNAPSHOT; 557 558 if (!parsable) { 559 init_hdr_cols(be_fmt, &hdr); 560 count_widths(be_fmt, &hdr, be_nodes); 561 print_hdr(&hdr); 562 } 563 564 if (be_fmt == BE_FMT_DEFAULT) 565 print_be_nodes(be_name, parsable, &hdr, be_nodes); 566 else 567 print_fmt_nodes(be_name, be_fmt, parsable, &hdr, be_nodes); 568 } 569 570 static boolean_t 571 confirm_destroy(const char *name) 572 { 573 boolean_t res = B_FALSE; 574 const char *yesre = nl_langinfo(YESEXPR); 575 const char *nore = nl_langinfo(NOEXPR); 576 regex_t yes_re; 577 regex_t no_re; 578 char buf[128]; 579 char *answer; 580 int cflags = REG_EXTENDED; 581 582 if (regcomp(&yes_re, yesre, cflags) != 0) { 583 /* should not happen */ 584 (void) fprintf(stderr, _("Failed to compile 'yes' regexp\n")); 585 return (res); 586 } 587 if (regcomp(&no_re, nore, cflags) != 0) { 588 /* should not happen */ 589 (void) fprintf(stderr, _("Failed to compile 'no' regexp\n")); 590 regfree(&yes_re); 591 return (res); 592 } 593 594 (void) printf(_("Are you sure you want to destroy %s?\n" 595 "This action cannot be undone (y/[n]): "), name); 596 597 answer = fgets(buf, sizeof (buf), stdin); 598 if (answer == NULL || *answer == '\0' || *answer == 10) 599 goto out; 600 601 if (regexec(&yes_re, answer, 0, NULL, 0) == 0) { 602 res = B_TRUE; 603 } else if (regexec(&no_re, answer, 0, NULL, 0) != 0) { 604 (void) fprintf(stderr, _("Invalid response. " 605 "Please enter 'y' or 'n'.\n")); 606 } 607 608 out: 609 regfree(&yes_re); 610 regfree(&no_re); 611 return (res); 612 } 613 614 static int 615 be_nvl_alloc(nvlist_t **nvlp) 616 { 617 assert(nvlp != NULL); 618 619 if (nvlist_alloc(nvlp, NV_UNIQUE_NAME, 0) != 0) { 620 (void) perror(_("nvlist_alloc failed.\n")); 621 return (1); 622 } 623 624 return (0); 625 } 626 627 static int 628 be_nvl_add_string(nvlist_t *nvl, const char *name, const char *val) 629 { 630 assert(nvl != NULL); 631 632 if (nvlist_add_string(nvl, name, val) != 0) { 633 (void) fprintf(stderr, _("nvlist_add_string failed for " 634 "%s (%s).\n"), name, val); 635 return (1); 636 } 637 638 return (0); 639 } 640 641 static int 642 be_nvl_add_nvlist(nvlist_t *nvl, const char *name, nvlist_t *val) 643 { 644 assert(nvl != NULL); 645 646 if (nvlist_add_nvlist(nvl, name, val) != 0) { 647 (void) fprintf(stderr, _("nvlist_add_nvlist failed for %s.\n"), 648 name); 649 return (1); 650 } 651 652 return (0); 653 } 654 655 static int 656 be_nvl_add_uint16(nvlist_t *nvl, const char *name, uint16_t val) 657 { 658 assert(nvl != NULL); 659 660 if (nvlist_add_uint16(nvl, name, val) != 0) { 661 (void) fprintf(stderr, _("nvlist_add_uint16 failed for " 662 "%s (%hu).\n"), name, val); 663 return (1); 664 } 665 666 return (0); 667 } 668 669 static int 670 be_do_activate(int argc, char **argv) 671 { 672 nvlist_t *be_attrs; 673 int err = 1; 674 int c; 675 char *obe_name; 676 677 while ((c = getopt(argc, argv, "v")) != -1) { 678 switch (c) { 679 case 'v': 680 libbe_print_errors(B_TRUE); 681 break; 682 default: 683 usage(); 684 return (1); 685 } 686 } 687 688 argc -= optind; 689 argv += optind; 690 691 if (argc != 1) { 692 usage(); 693 return (1); 694 } 695 696 obe_name = argv[0]; 697 698 if (be_nvl_alloc(&be_attrs) != 0) 699 return (1); 700 701 if (be_nvl_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, obe_name) != 0) 702 goto out; 703 704 err = be_activate(be_attrs); 705 706 switch (err) { 707 case BE_SUCCESS: 708 (void) printf(_("Activated successfully\n")); 709 break; 710 case BE_ERR_BE_NOENT: 711 (void) fprintf(stderr, _("%s does not exist or appear " 712 "to be a valid BE.\nPlease check that the name of " 713 "the BE provided is correct.\n"), obe_name); 714 break; 715 case BE_ERR_PERM: 716 case BE_ERR_ACCESS: 717 (void) fprintf(stderr, _("Unable to activate %s.\n"), obe_name); 718 (void) fprintf(stderr, _("You have insufficient privileges to " 719 "execute this command.\n")); 720 break; 721 case BE_ERR_ACTIVATE_CURR: 722 default: 723 (void) fprintf(stderr, _("Unable to activate %s.\n"), obe_name); 724 (void) fprintf(stderr, "%s\n", be_err_to_str(err)); 725 } 726 727 out: 728 nvlist_free(be_attrs); 729 return (err); 730 } 731 732 static int 733 be_do_create(int argc, char **argv) 734 { 735 nvlist_t *be_attrs; 736 nvlist_t *zfs_props = NULL; 737 boolean_t activate = B_FALSE; 738 boolean_t is_snap = B_FALSE; 739 int c; 740 int err = 1; 741 char *obe_name = NULL; 742 char *snap_name = NULL; 743 char *nbe_zpool = NULL; 744 char *nbe_name = NULL; 745 char *nbe_desc = NULL; 746 char *propname = NULL; 747 char *propval = NULL; 748 char *strval = NULL; 749 750 while ((c = getopt(argc, argv, "ad:e:io:p:v")) != -1) { 751 switch (c) { 752 case 'a': 753 activate = B_TRUE; 754 break; 755 case 'd': 756 nbe_desc = optarg; 757 break; 758 case 'e': 759 obe_name = optarg; 760 break; 761 case 'o': 762 if (zfs_props == NULL && be_nvl_alloc(&zfs_props) != 0) 763 return (1); 764 765 propname = optarg; 766 if ((propval = strchr(propname, '=')) == NULL) { 767 (void) fprintf(stderr, _("missing " 768 "'=' for -o option\n")); 769 goto out2; 770 } 771 *propval = '\0'; 772 propval++; 773 if (nvlist_lookup_string(zfs_props, propname, 774 &strval) == 0) { 775 (void) fprintf(stderr, _("property '%s' " 776 "specified multiple times\n"), propname); 777 goto out2; 778 779 } 780 if (be_nvl_add_string(zfs_props, propname, propval) 781 != 0) 782 goto out2; 783 784 break; 785 case 'p': 786 nbe_zpool = optarg; 787 break; 788 case 'v': 789 libbe_print_errors(B_TRUE); 790 break; 791 default: 792 usage(); 793 goto out2; 794 } 795 } 796 797 argc -= optind; 798 argv += optind; 799 800 if (argc != 1) { 801 usage(); 802 goto out2; 803 } 804 805 nbe_name = argv[0]; 806 807 if ((snap_name = strrchr(nbe_name, '@')) != NULL) { 808 if (snap_name[1] == '\0') { 809 usage(); 810 goto out2; 811 } 812 813 snap_name[0] = '\0'; 814 snap_name++; 815 is_snap = B_TRUE; 816 } 817 818 if (obe_name) { 819 if (is_snap) { 820 usage(); 821 goto out2; 822 } 823 824 /* 825 * Check if obe_name is really a snapshot name. 826 * If so, split it out. 827 */ 828 if ((snap_name = strrchr(obe_name, '@')) != NULL) { 829 if (snap_name[1] == '\0') { 830 usage(); 831 goto out2; 832 } 833 834 snap_name[0] = '\0'; 835 snap_name++; 836 } 837 } else if (is_snap) { 838 obe_name = nbe_name; 839 nbe_name = NULL; 840 } 841 842 if (be_nvl_alloc(&be_attrs) != 0) 843 goto out2; 844 845 846 if (zfs_props != NULL && be_nvl_add_nvlist(be_attrs, 847 BE_ATTR_ORIG_BE_NAME, zfs_props) != 0) 848 goto out; 849 850 if (obe_name != NULL && be_nvl_add_string(be_attrs, 851 BE_ATTR_ORIG_BE_NAME, obe_name) != 0) 852 goto out; 853 854 if (snap_name != NULL && be_nvl_add_string(be_attrs, 855 BE_ATTR_SNAP_NAME, snap_name) != 0) 856 goto out; 857 858 if (nbe_zpool != NULL && be_nvl_add_string(be_attrs, 859 BE_ATTR_NEW_BE_POOL, nbe_zpool) != 0) 860 goto out; 861 862 if (nbe_name != NULL && be_nvl_add_string(be_attrs, 863 BE_ATTR_NEW_BE_NAME, nbe_name) != 0) 864 goto out; 865 866 if (nbe_desc != NULL && be_nvl_add_string(be_attrs, 867 BE_ATTR_NEW_BE_DESC, nbe_desc) != 0) 868 goto out; 869 870 if (is_snap) 871 err = be_create_snapshot(be_attrs); 872 else 873 err = be_copy(be_attrs); 874 875 switch (err) { 876 case BE_SUCCESS: 877 if (!is_snap && !nbe_name) { 878 /* 879 * We requested an auto named BE; find out the 880 * name of the BE that was created for us and 881 * the auto snapshot created from the original BE. 882 */ 883 if (nvlist_lookup_string(be_attrs, BE_ATTR_NEW_BE_NAME, 884 &nbe_name) != 0) { 885 (void) fprintf(stderr, _("failed to get %s " 886 "attribute\n"), BE_ATTR_NEW_BE_NAME); 887 break; 888 } else 889 (void) printf(_("Auto named BE: %s\n"), 890 nbe_name); 891 892 if (nvlist_lookup_string(be_attrs, BE_ATTR_SNAP_NAME, 893 &snap_name) != 0) { 894 (void) fprintf(stderr, _("failed to get %s " 895 "attribute\n"), BE_ATTR_SNAP_NAME); 896 break; 897 } else 898 (void) printf(_("Auto named snapshot: %s\n"), 899 snap_name); 900 } 901 902 if (!is_snap && activate) { 903 char *args[] = { "activate", "", NULL }; 904 args[1] = nbe_name; 905 optind = 1; 906 907 err = be_do_activate(2, args); 908 goto out; 909 } 910 911 (void) printf(_("Created successfully\n")); 912 break; 913 case BE_ERR_BE_EXISTS: 914 (void) fprintf(stderr, _("BE %s already exists\n." 915 "Please choose a different BE name.\n"), nbe_name); 916 break; 917 case BE_ERR_SS_EXISTS: 918 (void) fprintf(stderr, _("BE %s snapshot %s already exists.\n" 919 "Please choose a different snapshot name.\n"), obe_name, 920 snap_name); 921 break; 922 case BE_ERR_PERM: 923 case BE_ERR_ACCESS: 924 if (is_snap) 925 (void) fprintf(stderr, _("Unable to create snapshot " 926 "%s.\n"), snap_name); 927 else 928 (void) fprintf(stderr, _("Unable to create %s.\n"), 929 nbe_name); 930 (void) fprintf(stderr, _("You have insufficient privileges to " 931 "execute this command.\n")); 932 break; 933 default: 934 if (is_snap) 935 (void) fprintf(stderr, _("Unable to create snapshot " 936 "%s.\n"), snap_name); 937 else 938 (void) fprintf(stderr, _("Unable to create %s.\n"), 939 nbe_name); 940 (void) fprintf(stderr, "%s\n", be_err_to_str(err)); 941 } 942 943 out: 944 nvlist_free(be_attrs); 945 out2: 946 if (zfs_props != NULL) 947 nvlist_free(zfs_props); 948 949 return (err); 950 } 951 952 static int 953 be_do_destroy(int argc, char **argv) 954 { 955 nvlist_t *be_attrs; 956 boolean_t is_snap = B_FALSE; 957 boolean_t suppress_prompt = B_FALSE; 958 int err = 1; 959 int c; 960 int destroy_flags = 0; 961 char *snap_name; 962 char *be_name; 963 964 while ((c = getopt(argc, argv, "fFsv")) != -1) { 965 switch (c) { 966 case 'f': 967 destroy_flags |= BE_DESTROY_FLAG_FORCE_UNMOUNT; 968 break; 969 case 's': 970 destroy_flags |= BE_DESTROY_FLAG_SNAPSHOTS; 971 break; 972 case 'v': 973 libbe_print_errors(B_TRUE); 974 break; 975 case 'F': 976 suppress_prompt = B_TRUE; 977 break; 978 default: 979 usage(); 980 return (1); 981 } 982 } 983 984 argc -= optind; 985 argv += optind; 986 987 if (argc != 1) { 988 usage(); 989 return (1); 990 } 991 992 be_name = argv[0]; 993 if (!suppress_prompt && !confirm_destroy(be_name)) { 994 (void) printf(_("%s has not been destroyed.\n"), be_name); 995 return (0); 996 } 997 998 if ((snap_name = strrchr(be_name, '@')) != NULL) { 999 if (snap_name[1] == '\0') { 1000 usage(); 1001 return (1); 1002 } 1003 1004 is_snap = B_TRUE; 1005 *snap_name = '\0'; 1006 snap_name++; 1007 } 1008 1009 if (be_nvl_alloc(&be_attrs) != 0) 1010 return (1); 1011 1012 1013 if (be_nvl_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, be_name) != 0) 1014 goto out; 1015 1016 if (is_snap) { 1017 if (be_nvl_add_string(be_attrs, BE_ATTR_SNAP_NAME, 1018 snap_name) != 0) 1019 goto out; 1020 1021 err = be_destroy_snapshot(be_attrs); 1022 } else { 1023 if (be_nvl_add_uint16(be_attrs, BE_ATTR_DESTROY_FLAGS, 1024 destroy_flags) != 0) 1025 goto out; 1026 1027 err = be_destroy(be_attrs); 1028 } 1029 1030 switch (err) { 1031 case BE_SUCCESS: 1032 (void) printf(_("Destroyed successfully\n")); 1033 break; 1034 case BE_ERR_MOUNTED: 1035 (void) fprintf(stderr, _("Unable to destroy %s.\n"), be_name); 1036 (void) fprintf(stderr, _("It is currently mounted and must be " 1037 "unmounted before it can be destroyed.\n" "Use 'beadm " 1038 "unmount %s' to unmount the BE before destroying\nit or " 1039 "'beadm destroy -f %s'.\n"), be_name, be_name); 1040 break; 1041 case BE_ERR_DESTROY_CURR_BE: 1042 (void) fprintf(stderr, _("%s is the currently active BE and " 1043 "cannot be destroyed.\nYou must boot from another BE in " 1044 "order to destroy %s.\n"), be_name, be_name); 1045 break; 1046 case BE_ERR_ZONES_UNMOUNT: 1047 (void) fprintf(stderr, _("Unable to destroy one of " "%s's " 1048 "zone BE's.\nUse 'beadm destroy -f %s' or " 1049 "'zfs -f destroy <dataset>'.\n"), be_name, be_name); 1050 break; 1051 case BE_ERR_SS_NOENT: 1052 (void) fprintf(stderr, _("%s does not exist or appear " 1053 "to be a valid snapshot.\nPlease check that the name of " 1054 "the snapshot provided is correct.\n"), snap_name); 1055 break; 1056 case BE_ERR_PERM: 1057 case BE_ERR_ACCESS: 1058 (void) fprintf(stderr, _("Unable to destroy %s.\n"), be_name); 1059 (void) fprintf(stderr, _("You have insufficient privileges to " 1060 "execute this command.\n")); 1061 break; 1062 case BE_ERR_SS_EXISTS: 1063 (void) fprintf(stderr, _("Unable to destroy %s: " 1064 "BE has snapshots.\nUse 'beadm destroy -s %s' or " 1065 "'zfs -r destroy <dataset>'.\n"), be_name, be_name); 1066 break; 1067 default: 1068 (void) fprintf(stderr, _("Unable to destroy %s.\n"), be_name); 1069 (void) fprintf(stderr, "%s\n", be_err_to_str(err)); 1070 } 1071 1072 out: 1073 nvlist_free(be_attrs); 1074 return (err); 1075 } 1076 1077 static int 1078 be_do_list(int argc, char **argv) 1079 { 1080 be_node_list_t *be_nodes = NULL; 1081 boolean_t all = B_FALSE; 1082 boolean_t dsets = B_FALSE; 1083 boolean_t snaps = B_FALSE; 1084 boolean_t parsable = B_FALSE; 1085 int err = 1; 1086 int c = 0; 1087 char *be_name = NULL; 1088 1089 while ((c = getopt(argc, argv, "adsvH")) != -1) { 1090 switch (c) { 1091 case 'a': 1092 all = B_TRUE; 1093 break; 1094 case 'd': 1095 dsets = B_TRUE; 1096 break; 1097 case 's': 1098 snaps = B_TRUE; 1099 break; 1100 case 'v': 1101 libbe_print_errors(B_TRUE); 1102 break; 1103 case 'H': 1104 parsable = B_TRUE; 1105 break; 1106 default: 1107 usage(); 1108 return (1); 1109 } 1110 } 1111 1112 if (all) { 1113 if (dsets) { 1114 (void) fprintf(stderr, _("Invalid options: -a and %s " 1115 "are mutually exclusive.\n"), "-d"); 1116 usage(); 1117 return (1); 1118 } 1119 if (snaps) { 1120 (void) fprintf(stderr, _("Invalid options: -a and %s " 1121 "are mutually exclusive.\n"), "-s"); 1122 usage(); 1123 return (1); 1124 } 1125 1126 dsets = B_TRUE; 1127 snaps = B_TRUE; 1128 } 1129 1130 argc -= optind; 1131 argv += optind; 1132 1133 1134 if (argc == 1) 1135 be_name = argv[0]; 1136 1137 err = be_list(be_name, &be_nodes); 1138 1139 switch (err) { 1140 case BE_SUCCESS: 1141 print_nodes(be_name, dsets, snaps, parsable, be_nodes); 1142 break; 1143 case BE_ERR_BE_NOENT: 1144 if (be_name == NULL) 1145 (void) fprintf(stderr, _("No boot environments found " 1146 "on this system.\n")); 1147 else { 1148 (void) fprintf(stderr, _("%s does not exist or appear " 1149 "to be a valid BE.\nPlease check that the name of " 1150 "the BE provided is correct.\n"), be_name); 1151 } 1152 break; 1153 default: 1154 (void) fprintf(stderr, _("Unable to display Boot " 1155 "Environment\n")); 1156 (void) fprintf(stderr, "%s\n", be_err_to_str(err)); 1157 } 1158 1159 if (be_nodes != NULL) 1160 be_free_list(be_nodes); 1161 return (err); 1162 } 1163 1164 static int 1165 be_do_mount(int argc, char **argv) 1166 { 1167 nvlist_t *be_attrs; 1168 boolean_t shared_fs = B_FALSE; 1169 int err = 1; 1170 int c; 1171 int mount_flags = 0; 1172 char *obe_name; 1173 char *mountpoint; 1174 char *tmp_mp = NULL; 1175 1176 while ((c = getopt(argc, argv, "s:v")) != -1) { 1177 switch (c) { 1178 case 's': 1179 shared_fs = B_TRUE; 1180 1181 mount_flags |= BE_MOUNT_FLAG_SHARED_FS; 1182 1183 if (strcmp(optarg, "rw") == 0) { 1184 mount_flags |= BE_MOUNT_FLAG_SHARED_RW; 1185 } else if (strcmp(optarg, "ro") != 0) { 1186 (void) fprintf(stderr, _("The -s flag " 1187 "requires an argument [ rw | ro ]\n")); 1188 usage(); 1189 return (1); 1190 } 1191 1192 break; 1193 case 'v': 1194 libbe_print_errors(B_TRUE); 1195 break; 1196 default: 1197 usage(); 1198 return (1); 1199 } 1200 } 1201 1202 argc -= optind; 1203 argv += optind; 1204 1205 if (argc < 1 || argc > 2) { 1206 usage(); 1207 return (1); 1208 } 1209 1210 obe_name = argv[0]; 1211 1212 if (argc == 2) { 1213 mountpoint = argv[1]; 1214 if (mountpoint[0] != '/') { 1215 (void) fprintf(stderr, _("Invalid mount point %s. " 1216 "Mount point must start with a /.\n"), mountpoint); 1217 return (1); 1218 } 1219 } else { 1220 const char *tmpdir = getenv("TMPDIR"); 1221 const char *tmpname = "tmp.XXXXXX"; 1222 int sz; 1223 1224 if (tmpdir == NULL) 1225 tmpdir = "/tmp"; 1226 1227 sz = asprintf(&tmp_mp, "%s/%s", tmpdir, tmpname); 1228 if (sz < 0) { 1229 (void) fprintf(stderr, _("internal error: " 1230 "out of memory\n")); 1231 return (1); 1232 } 1233 1234 mountpoint = mkdtemp(tmp_mp); 1235 } 1236 1237 if (be_nvl_alloc(&be_attrs) != 0) 1238 return (1); 1239 1240 if (be_nvl_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, obe_name) != 0) 1241 goto out; 1242 1243 if (be_nvl_add_string(be_attrs, BE_ATTR_MOUNTPOINT, mountpoint) != 0) 1244 goto out; 1245 1246 if (shared_fs && be_nvl_add_uint16(be_attrs, BE_ATTR_MOUNT_FLAGS, 1247 mount_flags) != 0) 1248 goto out; 1249 1250 err = be_mount(be_attrs); 1251 1252 switch (err) { 1253 case BE_SUCCESS: 1254 (void) printf(_("Mounted successfully on: '%s'\n"), mountpoint); 1255 break; 1256 case BE_ERR_BE_NOENT: 1257 (void) fprintf(stderr, _("%s does not exist or appear " 1258 "to be a valid BE.\nPlease check that the name of " 1259 "the BE provided is correct.\n"), obe_name); 1260 break; 1261 case BE_ERR_MOUNTED: 1262 (void) fprintf(stderr, _("%s is already mounted.\n" 1263 "Please unmount the BE before mounting it again.\n"), 1264 obe_name); 1265 break; 1266 case BE_ERR_PERM: 1267 case BE_ERR_ACCESS: 1268 (void) fprintf(stderr, _("Unable to mount %s.\n"), obe_name); 1269 (void) fprintf(stderr, _("You have insufficient privileges to " 1270 "execute this command.\n")); 1271 break; 1272 case BE_ERR_NO_MOUNTED_ZONE: 1273 (void) fprintf(stderr, _("Mounted on '%s'.\nUnable to mount " 1274 "one of %s's zone BE's.\n"), mountpoint, obe_name); 1275 break; 1276 default: 1277 (void) fprintf(stderr, _("Unable to mount %s.\n"), obe_name); 1278 (void) fprintf(stderr, "%s\n", be_err_to_str(err)); 1279 } 1280 1281 out: 1282 if (tmp_mp != NULL) 1283 free(tmp_mp); 1284 nvlist_free(be_attrs); 1285 return (err); 1286 } 1287 1288 static int 1289 be_do_unmount(int argc, char **argv) 1290 { 1291 nvlist_t *be_attrs; 1292 char *obe_name; 1293 int err = 1; 1294 int c; 1295 int unmount_flags = 0; 1296 1297 while ((c = getopt(argc, argv, "fv")) != -1) { 1298 switch (c) { 1299 case 'f': 1300 unmount_flags |= BE_UNMOUNT_FLAG_FORCE; 1301 break; 1302 case 'v': 1303 libbe_print_errors(B_TRUE); 1304 break; 1305 default: 1306 usage(); 1307 return (1); 1308 } 1309 } 1310 1311 argc -= optind; 1312 argv += optind; 1313 1314 if (argc != 1) { 1315 usage(); 1316 return (1); 1317 } 1318 1319 obe_name = argv[0]; 1320 1321 if (be_nvl_alloc(&be_attrs) != 0) 1322 return (1); 1323 1324 1325 if (be_nvl_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, obe_name) != 0) 1326 goto out; 1327 1328 if (be_nvl_add_uint16(be_attrs, BE_ATTR_UNMOUNT_FLAGS, 1329 unmount_flags) != 0) 1330 goto out; 1331 1332 err = be_unmount(be_attrs); 1333 1334 switch (err) { 1335 case BE_SUCCESS: 1336 (void) printf(_("Unmounted successfully\n")); 1337 break; 1338 case BE_ERR_BE_NOENT: 1339 (void) fprintf(stderr, _("%s does not exist or appear " 1340 "to be a valid BE.\nPlease check that the name of " 1341 "the BE provided is correct.\n"), obe_name); 1342 break; 1343 case BE_ERR_UMOUNT_CURR_BE: 1344 (void) fprintf(stderr, _("%s is the currently active BE.\n" 1345 "It cannot be unmounted unless another BE is the " 1346 "currently active BE.\n"), obe_name); 1347 break; 1348 case BE_ERR_UMOUNT_SHARED: 1349 (void) fprintf(stderr, _("%s is a shared file system and it " 1350 "cannot be unmounted.\n"), obe_name); 1351 break; 1352 case BE_ERR_PERM: 1353 case BE_ERR_ACCESS: 1354 (void) fprintf(stderr, _("Unable to unmount %s.\n"), obe_name); 1355 (void) fprintf(stderr, _("You have insufficient privileges to " 1356 "execute this command.\n")); 1357 break; 1358 default: 1359 (void) fprintf(stderr, _("Unable to unmount %s.\n"), obe_name); 1360 (void) fprintf(stderr, "%s\n", be_err_to_str(err)); 1361 } 1362 1363 out: 1364 nvlist_free(be_attrs); 1365 return (err); 1366 } 1367 1368 static int 1369 be_do_rename(int argc, char **argv) 1370 { 1371 nvlist_t *be_attrs; 1372 char *obe_name; 1373 char *nbe_name; 1374 int err = 1; 1375 int c; 1376 1377 while ((c = getopt(argc, argv, "v")) != -1) { 1378 switch (c) { 1379 case 'v': 1380 libbe_print_errors(B_TRUE); 1381 break; 1382 default: 1383 usage(); 1384 return (1); 1385 } 1386 } 1387 1388 argc -= optind; 1389 argv += optind; 1390 1391 if (argc != 2) { 1392 usage(); 1393 return (1); 1394 } 1395 1396 obe_name = argv[0]; 1397 nbe_name = argv[1]; 1398 1399 if (be_nvl_alloc(&be_attrs) != 0) 1400 return (1); 1401 1402 if (be_nvl_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, obe_name) != 0) 1403 goto out; 1404 1405 if (be_nvl_add_string(be_attrs, BE_ATTR_NEW_BE_NAME, nbe_name) != 0) 1406 goto out; 1407 1408 err = be_rename(be_attrs); 1409 1410 switch (err) { 1411 case BE_SUCCESS: 1412 (void) printf(_("Renamed successfully\n")); 1413 break; 1414 case BE_ERR_BE_NOENT: 1415 (void) fprintf(stderr, _("%s does not exist or appear " 1416 "to be a valid BE.\nPlease check that the name of " 1417 "the BE provided is correct.\n"), obe_name); 1418 break; 1419 case BE_ERR_PERM: 1420 case BE_ERR_ACCESS: 1421 (void) fprintf(stderr, _("Rename of BE %s failed.\n"), 1422 obe_name); 1423 (void) fprintf(stderr, _("You have insufficient privileges to " 1424 "execute this command.\n")); 1425 break; 1426 default: 1427 (void) fprintf(stderr, _("Rename of BE %s failed.\n"), 1428 obe_name); 1429 (void) fprintf(stderr, "%s\n", be_err_to_str(err)); 1430 } 1431 1432 out: 1433 nvlist_free(be_attrs); 1434 return (err); 1435 } 1436 1437 static int 1438 be_do_rollback(int argc, char **argv) 1439 { 1440 nvlist_t *be_attrs; 1441 char *obe_name; 1442 char *snap_name; 1443 int err = 1; 1444 int c; 1445 1446 while ((c = getopt(argc, argv, "v")) != -1) { 1447 switch (c) { 1448 case 'v': 1449 libbe_print_errors(B_TRUE); 1450 break; 1451 default: 1452 usage(); 1453 return (1); 1454 } 1455 } 1456 1457 argc -= optind; 1458 argv += optind; 1459 1460 if (argc < 1 || argc > 2) { 1461 usage(); 1462 return (1); 1463 } 1464 1465 obe_name = argv[0]; 1466 if (argc == 2) 1467 snap_name = argv[1]; 1468 else { /* argc == 1 */ 1469 if ((snap_name = strrchr(obe_name, '@')) != NULL) { 1470 if (snap_name[1] == '\0') { 1471 usage(); 1472 return (1); 1473 } 1474 1475 snap_name[0] = '\0'; 1476 snap_name++; 1477 } else { 1478 usage(); 1479 return (1); 1480 } 1481 } 1482 1483 if (be_nvl_alloc(&be_attrs) != 0) 1484 return (1); 1485 1486 if (be_nvl_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, obe_name) != 0) 1487 goto out; 1488 1489 if (be_nvl_add_string(be_attrs, BE_ATTR_SNAP_NAME, snap_name) != 0) 1490 goto out; 1491 1492 err = be_rollback(be_attrs); 1493 1494 switch (err) { 1495 case BE_SUCCESS: 1496 (void) printf(_("Rolled back successfully\n")); 1497 break; 1498 case BE_ERR_BE_NOENT: 1499 (void) fprintf(stderr, _("%s does not exist or appear " 1500 "to be a valid BE.\nPlease check that the name of " 1501 "the BE provided is correct.\n"), obe_name); 1502 break; 1503 case BE_ERR_SS_NOENT: 1504 (void) fprintf(stderr, _("%s does not exist or appear " 1505 "to be a valid snapshot.\nPlease check that the name of " 1506 "the snapshot provided is correct.\n"), snap_name); 1507 break; 1508 case BE_ERR_PERM: 1509 case BE_ERR_ACCESS: 1510 (void) fprintf(stderr, _("Rollback of BE %s snapshot %s " 1511 "failed.\n"), obe_name, snap_name); 1512 (void) fprintf(stderr, _("You have insufficient privileges to " 1513 "execute this command.\n")); 1514 break; 1515 default: 1516 (void) fprintf(stderr, _("Rollback of BE %s snapshot %s " 1517 "failed.\n"), obe_name, snap_name); 1518 (void) fprintf(stderr, "%s\n", be_err_to_str(err)); 1519 } 1520 1521 out: 1522 nvlist_free(be_attrs); 1523 return (err); 1524 }