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