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