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