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