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