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