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 if (!parsable) { 499 hdr = hdrs + be_fmt; 500 init_hdr_cols(be_fmt, hdr); 501 count_widths(be_fmt, hdr, nodes); 502 print_hdr(hdr); 503 } 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 default: 1050 (void) fprintf(stderr, _("Unable to destroy %s.\n"), be_name); 1051 (void) fprintf(stderr, "%s\n", be_err_to_str(err)); 1052 } 1053 1054 out: 1055 nvlist_free(be_attrs); 1056 return (err); 1057 } 1058 1059 static int 1060 be_do_list(int argc, char **argv) 1061 { 1062 be_node_list_t *be_nodes = NULL; 1063 boolean_t all = B_FALSE; 1064 boolean_t dsets = B_FALSE; 1065 boolean_t snaps = B_FALSE; 1066 boolean_t parsable = B_FALSE; 1067 int err = 1; 1068 int c = 0; 1069 char *be_name = NULL; 1070 1071 while ((c = getopt(argc, argv, "nadsH")) != -1) { 1072 switch (c) { 1073 case 'a': 1074 all = B_TRUE; 1075 break; 1076 case 'd': 1077 dsets = B_TRUE; 1078 break; 1079 case 's': 1080 snaps = B_TRUE; 1081 break; 1082 case 'H': 1083 parsable = B_TRUE; 1084 break; 1085 default: 1086 usage(); 1087 return (1); 1088 } 1089 } 1090 1091 if (all) { 1092 if (dsets) { 1093 (void) fprintf(stderr, _("Invalid options: -a and %s " 1094 "are mutually exclusive.\n"), "-d"); 1095 usage(); 1096 return (1); 1097 } 1098 if (snaps) { 1099 (void) fprintf(stderr, _("Invalid options: -a and %s " 1100 "are mutually exclusive.\n"), "-s"); 1101 usage(); 1102 return (1); 1103 } 1104 1105 dsets = B_TRUE; 1106 snaps = B_TRUE; 1107 } 1108 1109 argc -= optind; 1110 argv += optind; 1111 1112 1113 if (argc == 1) 1114 be_name = argv[0]; 1115 1116 err = be_list(be_name, &be_nodes); 1117 1118 switch (err) { 1119 case BE_SUCCESS: 1120 print_nodes(be_name, dsets, snaps, parsable, be_nodes); 1121 break; 1122 case BE_ERR_BE_NOENT: 1123 if (be_name == NULL) 1124 (void) fprintf(stderr, _("No boot environments found " 1125 "on this system.\n")); 1126 else { 1127 (void) fprintf(stderr, _("%s does not exist or appear " 1128 "to be a valid BE.\nPlease check that the name of " 1129 "the BE provided is correct.\n"), be_name); 1130 } 1131 break; 1132 default: 1133 (void) fprintf(stderr, _("Unable to display Boot " 1134 "Environment\n")); 1135 (void) fprintf(stderr, "%s\n", be_err_to_str(err)); 1136 } 1137 1138 if (be_nodes != NULL) 1139 be_free_list(be_nodes); 1140 return (err); 1141 } 1142 1143 static int 1144 be_do_mount(int argc, char **argv) 1145 { 1146 nvlist_t *be_attrs; 1147 boolean_t shared_fs = B_FALSE; 1148 int err = 1; 1149 int c; 1150 int mount_flags = 0; 1151 char *obe_name; 1152 char *mountpoint; 1153 char *tmp_mp = NULL; 1154 1155 while ((c = getopt(argc, argv, "s:")) != -1) { 1156 switch (c) { 1157 case 's': 1158 shared_fs = B_TRUE; 1159 1160 mount_flags |= BE_MOUNT_FLAG_SHARED_FS; 1161 1162 if (strcmp(optarg, "rw") == 0) { 1163 mount_flags |= BE_MOUNT_FLAG_SHARED_RW; 1164 } else if (strcmp(optarg, "ro") != 0) { 1165 (void) fprintf(stderr, _("The -s flag " 1166 "requires an argument [ rw | ro ]\n")); 1167 usage(); 1168 return (1); 1169 } 1170 1171 break; 1172 default: 1173 usage(); 1174 return (1); 1175 } 1176 } 1177 1178 argc -= optind; 1179 argv += optind; 1180 1181 if (argc < 1 || argc > 2) { 1182 usage(); 1183 return (1); 1184 } 1185 1186 obe_name = argv[0]; 1187 1188 if (argc == 2) { 1189 mountpoint = argv[1]; 1190 if (mountpoint[0] != '/') { 1191 (void) fprintf(stderr, _("Invalid mount point %s. " 1192 "Mount point must start with a /.\n"), mountpoint); 1193 return (1); 1194 } 1195 } else { 1196 const char *tmpdir = getenv("TMPDIR"); 1197 const char *tmpname = "tmp.XXXXXX"; 1198 int sz; 1199 1200 if (tmpdir == NULL) 1201 tmpdir = "/tmp"; 1202 1203 sz = asprintf(&tmp_mp, "%s/%s", tmpdir, tmpname); 1204 if (sz < 0) { 1205 (void) fprintf(stderr, _("internal error: " 1206 "out of memory\n")); 1207 return (1); 1208 } 1209 1210 mountpoint = mkdtemp(tmp_mp); 1211 } 1212 1213 if (be_nvl_alloc(&be_attrs) != 0) 1214 return (1); 1215 1216 if (be_nvl_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, obe_name) != 0) 1217 goto out; 1218 1219 if (be_nvl_add_string(be_attrs, BE_ATTR_MOUNTPOINT, mountpoint) != 0) 1220 goto out; 1221 1222 if (shared_fs && be_nvl_add_uint16(be_attrs, BE_ATTR_MOUNT_FLAGS, 1223 mount_flags) != 0) 1224 goto out; 1225 1226 err = be_mount(be_attrs); 1227 1228 switch (err) { 1229 case BE_SUCCESS: 1230 (void) printf(_("Mounted successfully on: '%s'\n"), mountpoint); 1231 break; 1232 case BE_ERR_BE_NOENT: 1233 err = 1; 1234 (void) fprintf(stderr, _("%s does not exist or appear " 1235 "to be a valid BE.\nPlease check that the name of " 1236 "the BE provided is correct.\n"), obe_name); 1237 break; 1238 case BE_ERR_MOUNTED: 1239 (void) fprintf(stderr, _("%s is already mounted.\n" 1240 "Please unmount the BE before mounting it again.\n"), 1241 obe_name); 1242 break; 1243 case BE_ERR_PERM: 1244 case BE_ERR_ACCESS: 1245 err = 1; 1246 (void) fprintf(stderr, _("Unable to mount %s.\n"), obe_name); 1247 (void) fprintf(stderr, _("You have insufficient privileges to " 1248 "execute this command.\n")); 1249 break; 1250 default: 1251 err = 1; 1252 (void) fprintf(stderr, _("Unable to mount %s.\n"), obe_name); 1253 (void) fprintf(stderr, "%s\n", be_err_to_str(err)); 1254 } 1255 1256 out: 1257 if (tmp_mp != NULL) 1258 free(tmp_mp); 1259 nvlist_free(be_attrs); 1260 return (err); 1261 } 1262 1263 static int 1264 be_do_unmount(int argc, char **argv) 1265 { 1266 nvlist_t *be_attrs; 1267 char *obe_name; 1268 int err = 1; 1269 int c; 1270 int unmount_flags = 0; 1271 1272 while ((c = getopt(argc, argv, "f")) != -1) { 1273 switch (c) { 1274 case 'f': 1275 unmount_flags |= BE_UNMOUNT_FLAG_FORCE; 1276 break; 1277 default: 1278 usage(); 1279 return (1); 1280 } 1281 } 1282 1283 argc -= optind; 1284 argv += optind; 1285 1286 if (argc != 1) { 1287 usage(); 1288 return (1); 1289 } 1290 1291 obe_name = argv[0]; 1292 1293 if (be_nvl_alloc(&be_attrs) != 0) 1294 return (1); 1295 1296 1297 if (be_nvl_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, obe_name) != 0) 1298 goto out; 1299 1300 if (be_nvl_add_uint16(be_attrs, BE_ATTR_UNMOUNT_FLAGS, 1301 unmount_flags) != 0) 1302 goto out; 1303 1304 err = be_unmount(be_attrs); 1305 1306 switch (err) { 1307 case BE_SUCCESS: 1308 (void) printf(_("Unmounted successfully\n")); 1309 break; 1310 case BE_ERR_BE_NOENT: 1311 (void) fprintf(stderr, _("%s does not exist or appear " 1312 "to be a valid BE.\nPlease check that the name of " 1313 "the BE provided is correct.\n"), obe_name); 1314 break; 1315 case BE_ERR_UMOUNT_CURR_BE: 1316 (void) fprintf(stderr, _("%s is the currently active BE.\n" 1317 "It cannot be unmounted unless another BE is the " 1318 "currently active BE.\n"), obe_name); 1319 break; 1320 case BE_ERR_UMOUNT_SHARED: 1321 (void) fprintf(stderr, _("%s is a shared file system and it " 1322 "cannot be unmounted.\n"), obe_name); 1323 break; 1324 case BE_ERR_PERM: 1325 case BE_ERR_ACCESS: 1326 (void) fprintf(stderr, _("Unable to unmount %s.\n"), obe_name); 1327 (void) fprintf(stderr, _("You have insufficient privileges to " 1328 "execute this command.\n")); 1329 break; 1330 default: 1331 (void) fprintf(stderr, _("Unable to unmount %s.\n"), obe_name); 1332 (void) fprintf(stderr, "%s\n", be_err_to_str(err)); 1333 } 1334 1335 out: 1336 nvlist_free(be_attrs); 1337 return (err); 1338 } 1339 1340 static int 1341 be_do_rename(int argc, char **argv) 1342 { 1343 nvlist_t *be_attrs; 1344 char *obe_name; 1345 char *nbe_name; 1346 int err = 1; 1347 1348 argc -= optind; 1349 argv += optind; 1350 1351 if (argc != 2) { 1352 usage(); 1353 return (1); 1354 } 1355 1356 obe_name = argv[0]; 1357 nbe_name = argv[1]; 1358 1359 if (be_nvl_alloc(&be_attrs) != 0) 1360 return (1); 1361 1362 if (be_nvl_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, obe_name) != 0) 1363 goto out; 1364 1365 if (be_nvl_add_string(be_attrs, BE_ATTR_NEW_BE_NAME, nbe_name) != 0) 1366 goto out; 1367 1368 err = be_rename(be_attrs); 1369 1370 switch (err) { 1371 case BE_SUCCESS: 1372 (void) printf(_("Renamed successfully\n")); 1373 break; 1374 case BE_ERR_BE_NOENT: 1375 (void) fprintf(stderr, _("%s does not exist or appear " 1376 "to be a valid BE.\nPlease check that the name of " 1377 "the BE provided is correct.\n"), obe_name); 1378 break; 1379 case BE_ERR_PERM: 1380 case BE_ERR_ACCESS: 1381 (void) fprintf(stderr, _("Rename of BE %s failed.\n"), 1382 obe_name); 1383 (void) fprintf(stderr, _("You have insufficient privileges to " 1384 "execute this command.\n")); 1385 break; 1386 default: 1387 (void) fprintf(stderr, _("Rename of BE %s failed.\n"), 1388 obe_name); 1389 (void) fprintf(stderr, "%s\n", be_err_to_str(err)); 1390 } 1391 1392 out: 1393 nvlist_free(be_attrs); 1394 return (err); 1395 } 1396 1397 static int 1398 be_do_rollback(int argc, char **argv) 1399 { 1400 nvlist_t *be_attrs; 1401 char *obe_name; 1402 char *snap_name; 1403 int err = 1; 1404 1405 argc -= optind; 1406 argv += optind; 1407 1408 if (argc < 1 || argc > 2) { 1409 usage(); 1410 return (1); 1411 } 1412 1413 obe_name = argv[0]; 1414 if (argc == 2) 1415 snap_name = argv[1]; 1416 else { /* argc == 1 */ 1417 if ((snap_name = strrchr(obe_name, '@')) != NULL) { 1418 if (snap_name[1] == '\0') { 1419 usage(); 1420 return (1); 1421 } 1422 1423 snap_name[0] = '\0'; 1424 snap_name++; 1425 } else { 1426 usage(); 1427 return (1); 1428 } 1429 } 1430 1431 if (be_nvl_alloc(&be_attrs) != 0) 1432 return (1); 1433 1434 if (be_nvl_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, obe_name) != 0) 1435 goto out; 1436 1437 if (be_nvl_add_string(be_attrs, BE_ATTR_SNAP_NAME, snap_name) != 0) 1438 goto out; 1439 1440 err = be_rollback(be_attrs); 1441 1442 switch (err) { 1443 case BE_SUCCESS: 1444 (void) printf(_("Rolled back successfully\n")); 1445 break; 1446 case BE_ERR_BE_NOENT: 1447 (void) fprintf(stderr, _("%s does not exist or appear " 1448 "to be a valid BE.\nPlease check that the name of " 1449 "the BE provided is correct.\n"), obe_name); 1450 break; 1451 case BE_ERR_SS_NOENT: 1452 (void) fprintf(stderr, _("%s does not exist or appear " 1453 "to be a valid snapshot.\nPlease check that the name of " 1454 "the snapshot provided is correct.\n"), snap_name); 1455 break; 1456 case BE_ERR_PERM: 1457 case BE_ERR_ACCESS: 1458 (void) fprintf(stderr, _("Rollback of BE %s snapshot %s " 1459 "failed.\n"), obe_name, snap_name); 1460 (void) fprintf(stderr, _("You have insufficient privileges to " 1461 "execute this command.\n")); 1462 break; 1463 default: 1464 (void) fprintf(stderr, _("Rollback of BE %s snapshot %s " 1465 "failed.\n"), obe_name, snap_name); 1466 (void) fprintf(stderr, "%s\n", be_err_to_str(err)); 1467 } 1468 1469 out: 1470 nvlist_free(be_attrs); 1471 return (err); 1472 } 1473