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