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