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