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