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