xref: /illumos-gate/usr/src/lib/libbe/common/be_utils.c (revision f22cbd2db87ae3945ed6a9166f8b9d61b65c6ab9)
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) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
25  * Copyright 2016 Toomas Soome <tsoome@me.com>
26  * Copyright (c) 2015 by Delphix. All rights reserved.
27  */
28 
29 
30 /*
31  * System includes
32  */
33 #include <assert.h>
34 #include <errno.h>
35 #include <libgen.h>
36 #include <libintl.h>
37 #include <libnvpair.h>
38 #include <libzfs.h>
39 #include <libgen.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <sys/stat.h>
44 #include <sys/types.h>
45 #include <sys/vfstab.h>
46 #include <sys/param.h>
47 #include <sys/systeminfo.h>
48 #include <ctype.h>
49 #include <time.h>
50 #include <unistd.h>
51 #include <fcntl.h>
52 #include <deflt.h>
53 #include <wait.h>
54 #include <libdevinfo.h>
55 #include <libgen.h>
56 
57 #include <libbe.h>
58 #include <libbe_priv.h>
59 #include <boot_utils.h>
60 #include <ficl.h>
61 #include <ficlplatform/emu.h>
62 
63 /* Private function prototypes */
64 static int update_dataset(char *, int, char *, char *, char *);
65 static int _update_vfstab(char *, char *, char *, char *, be_fs_list_data_t *);
66 static int be_open_menu(char *, char *, FILE **, char *, boolean_t);
67 static int be_create_menu(char *, char *, FILE **, char *);
68 static char *be_get_auto_name(char *, char *, boolean_t);
69 
70 /*
71  * Global error printing
72  */
73 boolean_t do_print = B_FALSE;
74 
75 /*
76  * Private datatypes
77  */
78 typedef struct zone_be_name_cb_data {
79 	char *base_be_name;
80 	int num;
81 } zone_be_name_cb_data_t;
82 
83 /* ********************************************************************	*/
84 /*			Public Functions				*/
85 /* ******************************************************************** */
86 
87 /*
88  * Callback for ficl to suppress all output from ficl, as we do not
89  * want to confuse user with messages from ficl, and we are only
90  * checking results from function calls.
91  */
92 /*ARGSUSED*/
93 static void
94 ficlSuppressTextOutput(ficlCallback *cb, char *text)
95 {
96 	/* This function is intentionally doing nothing. */
97 }
98 
99 /*
100  * Function:	be_get_boot_args
101  * Description:	Returns the fast boot argument string for enumerated BE.
102  * Parameters:
103  *		fbarg - pointer to argument string.
104  *		entry - index of BE.
105  * Returns:
106  *		fast boot argument string.
107  * Scope:
108  *		Public
109  */
110 int
111 be_get_boot_args(char **fbarg, int entry)
112 {
113 	be_node_list_t *node, *be_nodes = NULL;
114 	be_transaction_data_t bt = {0};
115 	char *mountpoint = NULL;
116 	boolean_t be_mounted = B_FALSE;
117 	int ret = BE_SUCCESS;
118 	int index;
119 	ficlVm *vm;
120 
121 	*fbarg = NULL;
122 	if (!be_zfs_init())
123 		return (BE_ERR_INIT);
124 
125 	/*
126 	 * need pool name, menu.lst has entries from our pool only
127 	 */
128 	ret = be_find_current_be(&bt);
129 	if (ret != BE_SUCCESS) {
130 		be_zfs_fini();
131 		return (ret);
132 	}
133 
134 	/*
135 	 * be_get_boot_args() is for loader, fail with grub will trigger
136 	 * normal boot.
137 	 */
138 	if (be_has_grub()) {
139 		ret = BE_ERR_INIT;
140 		goto done;
141 	}
142 
143 	ret = _be_list(NULL, &be_nodes);
144 	if (ret != BE_SUCCESS)
145 		goto done;
146 
147 	/*
148 	 * iterate through be_nodes,
149 	 * if entry == -1, stop if be_active_on_boot,
150 	 * else stop if index == entry.
151 	 */
152 	index = 0;
153 	for (node = be_nodes; node != NULL; node = node->be_next_node) {
154 		if (strcmp(node->be_rpool, bt.obe_zpool) != 0)
155 			continue;
156 		if (entry == BE_ENTRY_DEFAULT &&
157 		    node->be_active_on_boot == B_TRUE)
158 			break;
159 		if (index == entry)
160 			break;
161 		index++;
162 	}
163 	if (node == NULL) {
164 		be_free_list(be_nodes);
165 		ret = BE_ERR_NOENT;
166 		goto done;
167 	}
168 
169 	/* try to mount inactive be */
170 	if (node->be_active == B_FALSE) {
171 		ret = _be_mount(node->be_node_name, &mountpoint,
172 		    BE_MOUNT_FLAG_NO_ZONES);
173 		if (ret != BE_SUCCESS && ret != BE_ERR_MOUNTED) {
174 			be_free_list(be_nodes);
175 			goto done;
176 		} else
177 			be_mounted = B_TRUE;
178 	}
179 
180 	vm = bf_init("", ficlSuppressTextOutput);
181 	if (vm != NULL) {
182 		/*
183 		 * zfs MAXNAMELEN is 256, so we need to pick buf large enough
184 		 * to contain such names.
185 		 */
186 		char buf[MAXNAMELEN * 2];
187 		char *kernel_options = NULL;
188 		char *kernel = NULL;
189 		char *tmp;
190 		zpool_handle_t *zph;
191 
192 		/*
193 		 * just try to interpret following words. on error
194 		 * we will be missing kernelname, and will get out.
195 		 */
196 		(void) snprintf(buf, sizeof (buf), "set currdev=zfs:%s:",
197 		    node->be_root_ds);
198 		ret = ficlVmEvaluate(vm, buf);
199 		if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
200 			be_print_err(gettext("be_get_boot_args: error "
201 			    "interpreting boot config: %d\n"), ret);
202 			bf_fini();
203 			ret = BE_ERR_NO_MENU;
204 			goto cleanup;
205 		}
206 		(void) snprintf(buf, sizeof (buf),
207 		    "include /boot/forth/loader.4th");
208 		ret = ficlVmEvaluate(vm, buf);
209 		if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
210 			be_print_err(gettext("be_get_boot_args: error "
211 			    "interpreting boot config: %d\n"), ret);
212 			bf_fini();
213 			ret = BE_ERR_NO_MENU;
214 			goto cleanup;
215 		}
216 		(void) snprintf(buf, sizeof (buf), "start");
217 		ret = ficlVmEvaluate(vm, buf);
218 		if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
219 			be_print_err(gettext("be_get_boot_args: error "
220 			    "interpreting boot config: %d\n"), ret);
221 			bf_fini();
222 			ret = BE_ERR_NO_MENU;
223 			goto cleanup;
224 		}
225 		(void) snprintf(buf, sizeof (buf), "boot");
226 		ret = ficlVmEvaluate(vm, buf);
227 		bf_fini();
228 		if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
229 			be_print_err(gettext("be_get_boot_args: error "
230 			    "interpreting boot config: %d\n"), ret);
231 			ret = BE_ERR_NO_MENU;
232 			goto cleanup;
233 		}
234 
235 		kernel_options = getenv("boot-args");
236 		kernel = getenv("kernelname");
237 
238 		if (kernel == NULL) {
239 			be_print_err(gettext("be_get_boot_args: no kernel\n"));
240 			ret = BE_ERR_NOENT;
241 			goto cleanup;
242 		}
243 
244 		if ((zph = zpool_open(g_zfs, node->be_rpool)) == NULL) {
245 			be_print_err(gettext("be_get_boot_args: failed to "
246 			    "open root pool (%s): %s\n"), node->be_rpool,
247 			    libzfs_error_description(g_zfs));
248 			ret = zfs_err_to_be_err(g_zfs);
249 			goto cleanup;
250 		}
251 		ret = zpool_get_physpath(zph, buf, sizeof (buf));
252 		zpool_close(zph);
253 		if (ret != 0) {
254 			be_print_err(gettext("be_get_boot_args: failed to "
255 			    "get physpath\n"));
256 			goto cleanup;
257 		}
258 
259 		/* zpool_get_physpath() can return space separated list */
260 		tmp = buf;
261 		tmp = strsep(&tmp, " ");
262 
263 		if (kernel_options == NULL || *kernel_options == '\0')
264 			(void) asprintf(fbarg, "/ %s "
265 			    "-B zfs-bootfs=%s,bootpath=\"%s\"\n", kernel,
266 			    node->be_root_ds, tmp);
267 		else
268 			(void) asprintf(fbarg, "/ %s %s "
269 			    "-B zfs-bootfs=%s,bootpath=\"%s\"\n", kernel,
270 			    kernel_options, node->be_root_ds, tmp);
271 
272 		if (fbarg == NULL)
273 			ret = BE_ERR_NOMEM;
274 		else
275 			ret = 0;
276 	} else
277 		ret = BE_ERR_NOMEM;
278 cleanup:
279 	if (be_mounted == B_TRUE)
280 		(void) _be_unmount(node->be_node_name, BE_UNMOUNT_FLAG_FORCE);
281 	be_free_list(be_nodes);
282 done:
283 	free(mountpoint);
284 	free(bt.obe_name);
285 	free(bt.obe_root_ds);
286 	free(bt.obe_zpool);
287 	free(bt.obe_snap_name);
288 	free(bt.obe_altroot);
289 	be_zfs_fini();
290 	return (ret);
291 }
292 
293 /*
294  * Function:	be_max_avail
295  * Description:	Returns the available size for the zfs dataset passed in.
296  * Parameters:
297  *		dataset - The dataset we want to get the available space for.
298  *		ret - The available size will be returned in this.
299  * Returns:
300  *		The error returned by the zfs get property function.
301  * Scope:
302  *		Public
303  */
304 int
305 be_max_avail(char *dataset, uint64_t *ret)
306 {
307 	zfs_handle_t *zhp;
308 	int err = 0;
309 
310 	/* Initialize libzfs handle */
311 	if (!be_zfs_init())
312 		return (BE_ERR_INIT);
313 
314 	zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET);
315 	if (zhp == NULL) {
316 		/*
317 		 * The zfs_open failed return an error
318 		 */
319 		err = zfs_err_to_be_err(g_zfs);
320 	} else {
321 		err = be_maxsize_avail(zhp, ret);
322 	}
323 	ZFS_CLOSE(zhp);
324 	be_zfs_fini();
325 	return (err);
326 }
327 
328 /*
329  * Function:	libbe_print_errors
330  * Description:	Turns on/off error output for the library.
331  * Parameter:
332  *		set_do_print - Boolean that turns library error
333  *			       printing on or off.
334  * Returns:
335  *		None
336  * Scope:
337  *		Public;
338  */
339 void
340 libbe_print_errors(boolean_t set_do_print)
341 {
342 	do_print = set_do_print;
343 }
344 
345 /* ********************************************************************	*/
346 /*			Semi-Private Functions				*/
347 /* ******************************************************************** */
348 
349 /*
350  * Function:	be_zfs_init
351  * Description:	Initializes the libary global libzfs handle.
352  * Parameters:
353  *		None
354  * Returns:
355  *		B_TRUE - Success
356  *		B_FALSE - Failure
357  * Scope:
358  *		Semi-private (library wide use only)
359  */
360 boolean_t
361 be_zfs_init(void)
362 {
363 	be_zfs_fini();
364 
365 	if ((g_zfs = libzfs_init()) == NULL) {
366 		be_print_err(gettext("be_zfs_init: failed to initialize ZFS "
367 		    "library\n"));
368 		return (B_FALSE);
369 	}
370 
371 	return (B_TRUE);
372 }
373 
374 /*
375  * Function:	be_zfs_fini
376  * Description:	Closes the library global libzfs handle if it currently open.
377  * Parameter:
378  *		None
379  * Returns:
380  *		None
381  * Scope:
382  *		Semi-private (library wide use only)
383  */
384 void
385 be_zfs_fini(void)
386 {
387 	if (g_zfs)
388 		libzfs_fini(g_zfs);
389 
390 	g_zfs = NULL;
391 }
392 
393 /*
394  * Function:	be_get_defaults
395  * Description:	Open defaults and gets be default paramets
396  * Parameters:
397  *		defaults - be defaults struct
398  * Returns:
399  *		None
400  * Scope:
401  *		Semi-private (library wide use only)
402  */
403 void
404 be_get_defaults(struct be_defaults *defaults)
405 {
406 	void	*defp;
407 
408 	defaults->be_deflt_grub = B_FALSE;
409 	defaults->be_deflt_rpool_container = B_FALSE;
410 	defaults->be_deflt_bename_starts_with[0] = '\0';
411 
412 	if ((defp = defopen_r(BE_DEFAULTS)) != NULL) {
413 		const char *res = defread_r(BE_DFLT_BENAME_STARTS, defp);
414 		if (res != NULL && res[0] != '\0') {
415 			(void) strlcpy(defaults->be_deflt_bename_starts_with,
416 			    res, ZFS_MAX_DATASET_NAME_LEN);
417 			defaults->be_deflt_rpool_container = B_TRUE;
418 		}
419 		if (be_is_isa("i386")) {
420 			res = defread_r(BE_DFLT_BE_HAS_GRUB, defp);
421 			if (res != NULL && res[0] != '\0') {
422 				if (strcasecmp(res, "true") == 0)
423 					defaults->be_deflt_grub = B_TRUE;
424 			}
425 		}
426 		defclose_r(defp);
427 	}
428 }
429 
430 /*
431  * Function:	be_make_root_ds
432  * Description:	Generate string for BE's root dataset given the pool
433  *		it lives in and the BE name.
434  * Parameters:
435  *		zpool - pointer zpool name.
436  *		be_name - pointer to BE name.
437  *		be_root_ds - pointer to buffer to return BE root dataset in.
438  *		be_root_ds_size - size of be_root_ds
439  * Returns:
440  *		None
441  * Scope:
442  *		Semi-private (library wide use only)
443  */
444 void
445 be_make_root_ds(const char *zpool, const char *be_name, char *be_root_ds,
446     int be_root_ds_size)
447 {
448 	struct be_defaults be_defaults;
449 	be_get_defaults(&be_defaults);
450 	char	*root_ds = NULL;
451 
452 	if (getzoneid() == GLOBAL_ZONEID) {
453 		if (be_defaults.be_deflt_rpool_container) {
454 			(void) snprintf(be_root_ds, be_root_ds_size,
455 			    "%s/%s", zpool, be_name);
456 		} else {
457 			(void) snprintf(be_root_ds, be_root_ds_size,
458 			    "%s/%s/%s", zpool, BE_CONTAINER_DS_NAME, be_name);
459 		}
460 	} else {
461 		/*
462 		 * In non-global zone we can use path from mounted root dataset
463 		 * to generate BE's root dataset string.
464 		 */
465 		if ((root_ds = be_get_ds_from_dir("/")) != NULL) {
466 			(void) snprintf(be_root_ds, be_root_ds_size, "%s/%s",
467 			    dirname(root_ds), be_name);
468 		} else {
469 			be_print_err(gettext("be_make_root_ds: zone root "
470 			    "dataset is not mounted\n"));
471 			return;
472 		}
473 	}
474 }
475 
476 /*
477  * Function:	be_make_container_ds
478  * Description:	Generate string for the BE container dataset given a pool name.
479  * Parameters:
480  *		zpool - pointer zpool name.
481  *		container_ds - pointer to buffer to return BE container
482  *			dataset in.
483  *		container_ds_size - size of container_ds
484  * Returns:
485  *		None
486  * Scope:
487  *		Semi-private (library wide use only)
488  */
489 void
490 be_make_container_ds(const char *zpool,  char *container_ds,
491     int container_ds_size)
492 {
493 	struct be_defaults be_defaults;
494 	be_get_defaults(&be_defaults);
495 	char	*root_ds = NULL;
496 
497 	if (getzoneid() == GLOBAL_ZONEID) {
498 		if (be_defaults.be_deflt_rpool_container) {
499 			(void) snprintf(container_ds, container_ds_size,
500 			    "%s", zpool);
501 		} else {
502 			(void) snprintf(container_ds, container_ds_size,
503 			    "%s/%s", zpool, BE_CONTAINER_DS_NAME);
504 		}
505 	} else {
506 		if ((root_ds = be_get_ds_from_dir("/")) != NULL) {
507 			(void) strlcpy(container_ds, dirname(root_ds),
508 			    container_ds_size);
509 		} else {
510 			be_print_err(gettext("be_make_container_ds: zone root "
511 			    "dataset is not mounted\n"));
512 			return;
513 		}
514 	}
515 }
516 
517 /*
518  * Function:	be_make_name_from_ds
519  * Description:	This function takes a dataset name and strips off the
520  *		BE container dataset portion from the beginning.  The
521  *		returned name is allocated in heap storage, so the caller
522  *		is responsible for freeing it.
523  * Parameters:
524  *		dataset - dataset to get name from.
525  *		rc_loc - dataset underwhich the root container dataset lives.
526  * Returns:
527  *		name of dataset relative to BE container dataset.
528  *		NULL if dataset is not under a BE root dataset.
529  * Scope:
530  *		Semi-primate (library wide use only)
531  */
532 char *
533 be_make_name_from_ds(const char *dataset, char *rc_loc)
534 {
535 	char	ds[ZFS_MAX_DATASET_NAME_LEN];
536 	char	*tok = NULL;
537 	char	*name = NULL;
538 	struct be_defaults be_defaults;
539 	int	rlen = strlen(rc_loc);
540 
541 	be_get_defaults(&be_defaults);
542 
543 	/*
544 	 * First token is the location of where the root container dataset
545 	 * lives; it must match rc_loc.
546 	 */
547 	if (strncmp(dataset, rc_loc, rlen) == 0 && dataset[rlen] == '/')
548 		(void) strlcpy(ds, dataset + rlen + 1, sizeof (ds));
549 	else
550 		return (NULL);
551 
552 	if (be_defaults.be_deflt_rpool_container) {
553 		if ((name = strdup(ds)) == NULL) {
554 			be_print_err(gettext("be_make_name_from_ds: "
555 			    "memory allocation failed\n"));
556 			return (NULL);
557 		}
558 	} else {
559 		/* Second token must be BE container dataset name */
560 		if ((tok = strtok(ds, "/")) == NULL ||
561 		    strcmp(tok, BE_CONTAINER_DS_NAME) != 0)
562 			return (NULL);
563 
564 		/* Return the remaining token if one exists */
565 		if ((tok = strtok(NULL, "")) == NULL)
566 			return (NULL);
567 
568 		if ((name = strdup(tok)) == NULL) {
569 			be_print_err(gettext("be_make_name_from_ds: "
570 			    "memory allocation failed\n"));
571 			return (NULL);
572 		}
573 	}
574 
575 	return (name);
576 }
577 
578 /*
579  * Function:	be_maxsize_avail
580  * Description:	Returns the available size for the zfs handle passed in.
581  * Parameters:
582  *		zhp - A pointer to the open zfs handle.
583  *		ret - The available size will be returned in this.
584  * Returns:
585  *		The error returned by the zfs get property function.
586  * Scope:
587  *		Semi-private (library wide use only)
588  */
589 int
590 be_maxsize_avail(zfs_handle_t *zhp, uint64_t *ret)
591 {
592 	return ((*ret = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE)));
593 }
594 
595 /*
596  * Function:	be_append_menu
597  * Description:	Appends an entry for a BE into the menu.lst.
598  * Parameters:
599  *		be_name - pointer to name of BE to add boot menu entry for.
600  *		be_root_pool - pointer to name of pool BE lives in.
601  *		boot_pool - Used if the pool containing the grub menu is
602  *			    different than the one contaiing the BE. This
603  *			    will normally be NULL.
604  *		be_orig_root_ds - The root dataset for the BE. This is
605  *			used to check to see if an entry already exists
606  *			for this BE.
607  *		description - pointer to description of BE to be added in
608  *			the title line for this BEs entry.
609  * Returns:
610  *		BE_SUCCESS - Success
611  *		be_errno_t - Failure
612  * Scope:
613  *		Semi-private (library wide use only)
614  */
615 int
616 be_append_menu(char *be_name, char *be_root_pool, char *boot_pool,
617     char *be_orig_root_ds, char *description)
618 {
619 	zfs_handle_t *zhp = NULL;
620 	char menu_file[MAXPATHLEN];
621 	char be_root_ds[MAXPATHLEN];
622 	char line[BUFSIZ];
623 	char temp_line[BUFSIZ];
624 	char title[MAXPATHLEN];
625 	char *entries[BUFSIZ];
626 	char *tmp_entries[BUFSIZ];
627 	char *pool_mntpnt = NULL;
628 	char *ptmp_mntpnt = NULL;
629 	char *orig_mntpnt = NULL;
630 	boolean_t found_be = B_FALSE;
631 	boolean_t found_orig_be = B_FALSE;
632 	boolean_t found_title = B_FALSE;
633 	boolean_t pool_mounted = B_FALSE;
634 	boolean_t collect_lines = B_FALSE;
635 	FILE *menu_fp = NULL;
636 	int err = 0, ret = BE_SUCCESS;
637 	int i, num_tmp_lines = 0, num_lines = 0;
638 
639 	if (be_name == NULL || be_root_pool == NULL)
640 		return (BE_ERR_INVAL);
641 
642 	if (boot_pool == NULL)
643 		boot_pool = be_root_pool;
644 
645 	if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
646 		be_print_err(gettext("be_append_menu: failed to open "
647 		    "pool dataset for %s: %s\n"), be_root_pool,
648 		    libzfs_error_description(g_zfs));
649 		return (zfs_err_to_be_err(g_zfs));
650 	}
651 
652 	/*
653 	 * Check to see if the pool's dataset is mounted. If it isn't we'll
654 	 * attempt to mount it.
655 	 */
656 	if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
657 	    &pool_mounted)) != BE_SUCCESS) {
658 		be_print_err(gettext("be_append_menu: pool dataset "
659 		    "(%s) could not be mounted\n"), be_root_pool);
660 		ZFS_CLOSE(zhp);
661 		return (ret);
662 	}
663 
664 	/*
665 	 * Get the mountpoint for the root pool dataset.
666 	 */
667 	if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
668 		be_print_err(gettext("be_append_menu: pool "
669 		    "dataset (%s) is not mounted. Can't set "
670 		    "the default BE in the grub menu.\n"), be_root_pool);
671 		ret = BE_ERR_NO_MENU;
672 		goto cleanup;
673 	}
674 
675 	/*
676 	 * Check to see if this system supports grub
677 	 */
678 	if (be_has_grub()) {
679 		(void) snprintf(menu_file, sizeof (menu_file),
680 		    "%s%s", pool_mntpnt, BE_GRUB_MENU);
681 	} else {
682 		(void) snprintf(menu_file, sizeof (menu_file),
683 		    "%s%s", pool_mntpnt, BE_SPARC_MENU);
684 	}
685 
686 	be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
687 
688 	/*
689 	 * Iterate through menu first to make sure the BE doesn't already
690 	 * have an entry in the menu.
691 	 *
692 	 * Additionally while iterating through the menu, if we have an
693 	 * original root dataset for a BE we're cloning from, we need to keep
694 	 * track of that BE's menu entry. We will then use the lines from
695 	 * that entry to create the entry for the new BE.
696 	 */
697 	if ((ret = be_open_menu(be_root_pool, menu_file,
698 	    &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
699 		goto cleanup;
700 	} else if (menu_fp == NULL) {
701 		ret = BE_ERR_NO_MENU;
702 		goto cleanup;
703 	}
704 
705 	free(pool_mntpnt);
706 	pool_mntpnt = NULL;
707 
708 	while (fgets(line, BUFSIZ, menu_fp)) {
709 		char *tok = NULL;
710 
711 		(void) strlcpy(temp_line, line, BUFSIZ);
712 		tok = strtok(line, BE_WHITE_SPACE);
713 
714 		if (tok == NULL || tok[0] == '#') {
715 			continue;
716 		} else if (strcmp(tok, "title") == 0) {
717 			collect_lines = B_FALSE;
718 			if ((tok = strtok(NULL, "\n")) == NULL)
719 				(void) strlcpy(title, "", sizeof (title));
720 			else
721 				(void) strlcpy(title, tok, sizeof (title));
722 			found_title = B_TRUE;
723 
724 			if (num_tmp_lines != 0) {
725 				for (i = 0; i < num_tmp_lines; i++) {
726 					free(tmp_entries[i]);
727 					tmp_entries[i] = NULL;
728 				}
729 				num_tmp_lines = 0;
730 			}
731 		} else if (strcmp(tok, "bootfs") == 0) {
732 			char *bootfs = strtok(NULL, BE_WHITE_SPACE);
733 			found_title = B_FALSE;
734 			if (bootfs == NULL)
735 				continue;
736 
737 			if (strcmp(bootfs, be_root_ds) == 0) {
738 				found_be = B_TRUE;
739 				break;
740 			}
741 
742 			if (be_orig_root_ds != NULL &&
743 			    strcmp(bootfs, be_orig_root_ds) == 0 &&
744 			    !found_orig_be) {
745 				char str[BUFSIZ];
746 				found_orig_be = B_TRUE;
747 				num_lines = 0;
748 				/*
749 				 * Store the new title line
750 				 */
751 				(void) snprintf(str, BUFSIZ, "title %s\n",
752 				    description ? description : be_name);
753 				entries[num_lines] = strdup(str);
754 				num_lines++;
755 				/*
756 				 * If there are any lines between the title
757 				 * and the bootfs line store these. Also
758 				 * free the temporary lines.
759 				 */
760 				for (i = 0; i < num_tmp_lines; i++) {
761 					entries[num_lines] = tmp_entries[i];
762 					tmp_entries[i] = NULL;
763 					num_lines++;
764 				}
765 				num_tmp_lines = 0;
766 				/*
767 				 * Store the new bootfs line.
768 				 */
769 				(void) snprintf(str, BUFSIZ, "bootfs %s\n",
770 				    be_root_ds);
771 				entries[num_lines] = strdup(str);
772 				num_lines++;
773 				collect_lines = B_TRUE;
774 			}
775 		} else if (found_orig_be && collect_lines) {
776 			/*
777 			 * get the rest of the lines for the original BE and
778 			 * store them.
779 			 */
780 			if (strstr(line, BE_GRUB_COMMENT) != NULL ||
781 			    strstr(line, "BOOTADM") != NULL)
782 				continue;
783 			if (strcmp(tok, "splashimage") == 0) {
784 				entries[num_lines] =
785 				    strdup("splashimage "
786 				    "/boot/splashimage.xpm\n");
787 			} else {
788 				entries[num_lines] = strdup(temp_line);
789 			}
790 			num_lines++;
791 		} else if (found_title && !found_orig_be) {
792 			tmp_entries[num_tmp_lines] = strdup(temp_line);
793 			num_tmp_lines++;
794 		}
795 	}
796 
797 	(void) fclose(menu_fp);
798 
799 	if (found_be) {
800 		/*
801 		 * If an entry for this BE was already in the menu, then if
802 		 * that entry's title matches what we would have put in
803 		 * return success.  Otherwise return failure.
804 		 */
805 		char *new_title = description ? description : be_name;
806 
807 		if (strcmp(title, new_title) == 0) {
808 			ret = BE_SUCCESS;
809 			goto cleanup;
810 		} else {
811 			if (be_remove_menu(be_name, be_root_pool,
812 			    boot_pool) != BE_SUCCESS) {
813 				be_print_err(gettext("be_append_menu: "
814 				    "Failed to remove existing unusable "
815 				    "entry '%s' in boot menu.\n"), be_name);
816 				ret = BE_ERR_BE_EXISTS;
817 				goto cleanup;
818 			}
819 		}
820 	}
821 
822 	/* Append BE entry to the end of the file */
823 	menu_fp = fopen(menu_file, "a+");
824 	err = errno;
825 	if (menu_fp == NULL) {
826 		be_print_err(gettext("be_append_menu: failed "
827 		    "to open menu.lst file %s\n"), menu_file);
828 		ret = errno_to_be_err(err);
829 		goto cleanup;
830 	}
831 
832 	if (found_orig_be) {
833 		/*
834 		 * write out all the stored lines
835 		 */
836 		for (i = 0; i < num_lines; i++) {
837 			(void) fprintf(menu_fp, "%s", entries[i]);
838 			free(entries[i]);
839 		}
840 		num_lines = 0;
841 
842 		/*
843 		 * Check to see if this system supports grub
844 		 */
845 		if (be_has_grub())
846 			(void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
847 		ret = BE_SUCCESS;
848 	} else {
849 		(void) fprintf(menu_fp, "title %s\n",
850 		    description ? description : be_name);
851 		(void) fprintf(menu_fp, "bootfs %s\n", be_root_ds);
852 
853 		/*
854 		 * Check to see if this system supports grub
855 		 */
856 		if (be_has_grub()) {
857 			(void) fprintf(menu_fp, "kernel$ "
858 			    "/platform/i86pc/kernel/$ISADIR/unix -B "
859 			    "$ZFS-BOOTFS\n");
860 			(void) fprintf(menu_fp, "module$ "
861 			    "/platform/i86pc/$ISADIR/boot_archive\n");
862 			(void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
863 		}
864 		ret = BE_SUCCESS;
865 	}
866 	(void) fclose(menu_fp);
867 cleanup:
868 	if (pool_mounted) {
869 		int err = BE_SUCCESS;
870 		err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
871 		if (ret == BE_SUCCESS)
872 			ret = err;
873 		free(orig_mntpnt);
874 		free(ptmp_mntpnt);
875 	}
876 	ZFS_CLOSE(zhp);
877 	if (num_tmp_lines > 0) {
878 		for (i = 0; i < num_tmp_lines; i++) {
879 			free(tmp_entries[i]);
880 			tmp_entries[i] = NULL;
881 		}
882 	}
883 	if (num_lines > 0) {
884 		for (i = 0; i < num_lines; i++) {
885 			free(entries[i]);
886 			entries[i] = NULL;
887 		}
888 	}
889 	return (ret);
890 }
891 
892 /*
893  * Function:	be_remove_menu
894  * Description:	Removes a BE's entry from a menu.lst file.
895  * Parameters:
896  *		be_name - the name of BE whose entry is to be removed from
897  *			the menu.lst file.
898  *		be_root_pool - the pool that be_name lives in.
899  *		boot_pool - the pool where the BE is, if different than
900  *			the pool containing the boot menu.  If this is
901  *			NULL it will be set to be_root_pool.
902  * Returns:
903  *		BE_SUCCESS - Success
904  *		be_errno_t - Failure
905  * Scope:
906  *		Semi-private (library wide use only)
907  */
908 int
909 be_remove_menu(char *be_name, char *be_root_pool, char *boot_pool)
910 {
911 	zfs_handle_t	*zhp = NULL;
912 	char		be_root_ds[MAXPATHLEN];
913 	char		**buffer = NULL;
914 	char		menu_buf[BUFSIZ];
915 	char		menu[MAXPATHLEN];
916 	char		*pool_mntpnt = NULL;
917 	char		*ptmp_mntpnt = NULL;
918 	char		*orig_mntpnt = NULL;
919 	char		*tmp_menu = NULL;
920 	FILE		*menu_fp = NULL;
921 	FILE		*tmp_menu_fp = NULL;
922 	struct stat	sb;
923 	int		ret = BE_SUCCESS;
924 	int		i;
925 	int		fd;
926 	int		err = 0;
927 	int		nlines = 0;
928 	int		default_entry = 0;
929 	int		entry_cnt = 0;
930 	int		entry_del = 0;
931 	int		num_entry_del = 0;
932 	int		tmp_menu_len = 0;
933 	boolean_t	write = B_TRUE;
934 	boolean_t	do_buffer = B_FALSE;
935 	boolean_t	pool_mounted = B_FALSE;
936 
937 	if (boot_pool == NULL)
938 		boot_pool = be_root_pool;
939 
940 	/* Get name of BE's root dataset */
941 	be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
942 
943 	/* Get handle to pool dataset */
944 	if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
945 		be_print_err(gettext("be_remove_menu: "
946 		    "failed to open pool dataset for %s: %s"),
947 		    be_root_pool, libzfs_error_description(g_zfs));
948 		return (zfs_err_to_be_err(g_zfs));
949 	}
950 
951 	/*
952 	 * Check to see if the pool's dataset is mounted. If it isn't we'll
953 	 * attempt to mount it.
954 	 */
955 	if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
956 	    &pool_mounted)) != BE_SUCCESS) {
957 		be_print_err(gettext("be_remove_menu: pool dataset "
958 		    "(%s) could not be mounted\n"), be_root_pool);
959 		ZFS_CLOSE(zhp);
960 		return (ret);
961 	}
962 
963 	/*
964 	 * Get the mountpoint for the root pool dataset.
965 	 */
966 	if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
967 		be_print_err(gettext("be_remove_menu: pool "
968 		    "dataset (%s) is not mounted. Can't set "
969 		    "the default BE in the grub menu.\n"), be_root_pool);
970 		ret = BE_ERR_NO_MENU;
971 		goto cleanup;
972 	}
973 
974 	/* Get path to boot menu */
975 	(void) strlcpy(menu, pool_mntpnt, sizeof (menu));
976 
977 	/*
978 	 * Check to see if this system supports grub
979 	 */
980 	if (be_has_grub())
981 		(void) strlcat(menu, BE_GRUB_MENU, sizeof (menu));
982 	else
983 		(void) strlcat(menu, BE_SPARC_MENU, sizeof (menu));
984 
985 	/* Get handle to boot menu file */
986 	if ((ret = be_open_menu(be_root_pool, menu, &menu_fp, "r",
987 	    B_TRUE)) != BE_SUCCESS) {
988 		goto cleanup;
989 	} else if (menu_fp == NULL) {
990 		ret = BE_ERR_NO_MENU;
991 		goto cleanup;
992 	}
993 
994 	free(pool_mntpnt);
995 	pool_mntpnt = NULL;
996 
997 	/* Grab the stats of the original menu file */
998 	if (stat(menu, &sb) != 0) {
999 		err = errno;
1000 		be_print_err(gettext("be_remove_menu: "
1001 		    "failed to stat file %s: %s\n"), menu, strerror(err));
1002 		ret = errno_to_be_err(err);
1003 		goto cleanup;
1004 	}
1005 
1006 	/* Create a tmp file for the modified menu.lst */
1007 	tmp_menu_len = strlen(menu) + 7;
1008 	if ((tmp_menu = (char *)malloc(tmp_menu_len)) == NULL) {
1009 		be_print_err(gettext("be_remove_menu: malloc failed\n"));
1010 		ret = BE_ERR_NOMEM;
1011 		goto cleanup;
1012 	}
1013 	(void) memset(tmp_menu, 0, tmp_menu_len);
1014 	(void) strlcpy(tmp_menu, menu, tmp_menu_len);
1015 	(void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
1016 	if ((fd = mkstemp(tmp_menu)) == -1) {
1017 		err = errno;
1018 		be_print_err(gettext("be_remove_menu: mkstemp failed\n"));
1019 		ret = errno_to_be_err(err);
1020 		free(tmp_menu);
1021 		tmp_menu = NULL;
1022 		goto cleanup;
1023 	}
1024 	if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
1025 		err = errno;
1026 		be_print_err(gettext("be_remove_menu: "
1027 		    "could not open tmp file for write: %s\n"), strerror(err));
1028 		(void) close(fd);
1029 		ret = errno_to_be_err(err);
1030 		goto cleanup;
1031 	}
1032 
1033 	while (fgets(menu_buf, BUFSIZ, menu_fp)) {
1034 		char tline [BUFSIZ];
1035 		char *tok = NULL;
1036 
1037 		(void) strlcpy(tline, menu_buf, sizeof (tline));
1038 
1039 		/* Tokenize line */
1040 		tok = strtok(tline, BE_WHITE_SPACE);
1041 
1042 		if (tok == NULL || tok[0] == '#') {
1043 			/* Found empty line or comment line */
1044 			if (do_buffer) {
1045 				/* Buffer this line */
1046 				if ((buffer = (char **)realloc(buffer,
1047 				    sizeof (char *)*(nlines + 1))) == NULL) {
1048 					ret = BE_ERR_NOMEM;
1049 					goto cleanup;
1050 				}
1051 				if ((buffer[nlines++] = strdup(menu_buf))
1052 				    == NULL) {
1053 					ret = BE_ERR_NOMEM;
1054 					goto cleanup;
1055 				}
1056 
1057 			} else if (write || strncmp(menu_buf, BE_GRUB_COMMENT,
1058 			    strlen(BE_GRUB_COMMENT)) != 0) {
1059 				/* Write this line out */
1060 				(void) fputs(menu_buf, tmp_menu_fp);
1061 			}
1062 		} else if (strcmp(tok, "default") == 0) {
1063 			/*
1064 			 * Record what 'default' is set to because we might
1065 			 * need to adjust this upon deleting an entry.
1066 			 */
1067 			tok = strtok(NULL, BE_WHITE_SPACE);
1068 
1069 			if (tok != NULL) {
1070 				default_entry = atoi(tok);
1071 			}
1072 
1073 			(void) fputs(menu_buf, tmp_menu_fp);
1074 		} else if (strcmp(tok, "title") == 0) {
1075 			/*
1076 			 * If we've reached a 'title' line and do_buffer is
1077 			 * is true, that means we've just buffered an entire
1078 			 * entry without finding a 'bootfs' directive.  We
1079 			 * need to write that entry out and keep searching.
1080 			 */
1081 			if (do_buffer) {
1082 				for (i = 0; i < nlines; i++) {
1083 					(void) fputs(buffer[i], tmp_menu_fp);
1084 					free(buffer[i]);
1085 				}
1086 				free(buffer);
1087 				buffer = NULL;
1088 				nlines = 0;
1089 			}
1090 
1091 			/*
1092 			 * Turn writing off and buffering on, and increment
1093 			 * our entry counter.
1094 			 */
1095 			write = B_FALSE;
1096 			do_buffer = B_TRUE;
1097 			entry_cnt++;
1098 
1099 			/* Buffer this 'title' line */
1100 			if ((buffer = (char **)realloc(buffer,
1101 			    sizeof (char *)*(nlines + 1))) == NULL) {
1102 				ret = BE_ERR_NOMEM;
1103 				goto cleanup;
1104 			}
1105 			if ((buffer[nlines++] = strdup(menu_buf)) == NULL) {
1106 				ret = BE_ERR_NOMEM;
1107 				goto cleanup;
1108 			}
1109 
1110 		} else if (strcmp(tok, "bootfs") == 0) {
1111 			char *bootfs = NULL;
1112 
1113 			/*
1114 			 * Found a 'bootfs' line.  See if it matches the
1115 			 * BE we're looking for.
1116 			 */
1117 			if ((bootfs = strtok(NULL, BE_WHITE_SPACE)) == NULL ||
1118 			    strcmp(bootfs, be_root_ds) != 0) {
1119 				/*
1120 				 * Either there's nothing after the 'bootfs'
1121 				 * or this is not the BE we're looking for,
1122 				 * write out the line(s) we've buffered since
1123 				 * finding the title.
1124 				 */
1125 				for (i = 0; i < nlines; i++) {
1126 					(void) fputs(buffer[i], tmp_menu_fp);
1127 					free(buffer[i]);
1128 				}
1129 				free(buffer);
1130 				buffer = NULL;
1131 				nlines = 0;
1132 
1133 				/*
1134 				 * Turn writing back on, and turn off buffering
1135 				 * since this isn't the entry we're looking
1136 				 * for.
1137 				 */
1138 				write = B_TRUE;
1139 				do_buffer = B_FALSE;
1140 
1141 				/* Write this 'bootfs' line out. */
1142 				(void) fputs(menu_buf, tmp_menu_fp);
1143 			} else {
1144 				/*
1145 				 * Found the entry we're looking for.
1146 				 * Record its entry number, increment the
1147 				 * number of entries we've deleted, and turn
1148 				 * writing off.  Also, throw away the lines
1149 				 * we've buffered for this entry so far, we
1150 				 * don't need them.
1151 				 */
1152 				entry_del = entry_cnt - 1;
1153 				num_entry_del++;
1154 				write = B_FALSE;
1155 				do_buffer = B_FALSE;
1156 
1157 				for (i = 0; i < nlines; i++) {
1158 					free(buffer[i]);
1159 				}
1160 				free(buffer);
1161 				buffer = NULL;
1162 				nlines = 0;
1163 			}
1164 		} else {
1165 			if (do_buffer) {
1166 				/* Buffer this line */
1167 				if ((buffer = (char **)realloc(buffer,
1168 				    sizeof (char *)*(nlines + 1))) == NULL) {
1169 					ret = BE_ERR_NOMEM;
1170 					goto cleanup;
1171 				}
1172 				if ((buffer[nlines++] = strdup(menu_buf))
1173 				    == NULL) {
1174 					ret = BE_ERR_NOMEM;
1175 					goto cleanup;
1176 				}
1177 			} else if (write) {
1178 				/* Write this line out */
1179 				(void) fputs(menu_buf, tmp_menu_fp);
1180 			}
1181 		}
1182 	}
1183 
1184 	(void) fclose(menu_fp);
1185 	menu_fp = NULL;
1186 	(void) fclose(tmp_menu_fp);
1187 	tmp_menu_fp = NULL;
1188 
1189 	/* Copy the modified menu.lst into place */
1190 	if (rename(tmp_menu, menu) != 0) {
1191 		err = errno;
1192 		be_print_err(gettext("be_remove_menu: "
1193 		    "failed to rename file %s to %s: %s\n"),
1194 		    tmp_menu, menu, strerror(err));
1195 		ret = errno_to_be_err(err);
1196 		goto cleanup;
1197 	}
1198 	free(tmp_menu);
1199 	tmp_menu = NULL;
1200 
1201 	/*
1202 	 * If we've removed an entry, see if we need to
1203 	 * adjust the default value in the menu.lst.  If the
1204 	 * entry we've deleted comes before the default entry
1205 	 * we need to adjust the default value accordingly.
1206 	 *
1207 	 * be_has_grub is used here to check to see if this system
1208 	 * supports grub.
1209 	 */
1210 	if (be_has_grub() && num_entry_del > 0) {
1211 		if (entry_del <= default_entry) {
1212 			default_entry = default_entry - num_entry_del;
1213 			if (default_entry < 0)
1214 				default_entry = 0;
1215 
1216 			/*
1217 			 * Adjust the default value by rewriting the
1218 			 * menu.lst file.  This may be overkill, but to
1219 			 * preserve the location of the 'default' entry
1220 			 * in the file, we need to do this.
1221 			 */
1222 
1223 			/* Get handle to boot menu file */
1224 			if ((menu_fp = fopen(menu, "r")) == NULL) {
1225 				err = errno;
1226 				be_print_err(gettext("be_remove_menu: "
1227 				    "failed to open menu.lst (%s): %s\n"),
1228 				    menu, strerror(err));
1229 				ret = errno_to_be_err(err);
1230 				goto cleanup;
1231 			}
1232 
1233 			/* Create a tmp file for the modified menu.lst */
1234 			tmp_menu_len = strlen(menu) + 7;
1235 			if ((tmp_menu = (char *)malloc(tmp_menu_len))
1236 			    == NULL) {
1237 				be_print_err(gettext("be_remove_menu: "
1238 				    "malloc failed\n"));
1239 				ret = BE_ERR_NOMEM;
1240 				goto cleanup;
1241 			}
1242 			(void) memset(tmp_menu, 0, tmp_menu_len);
1243 			(void) strlcpy(tmp_menu, menu, tmp_menu_len);
1244 			(void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
1245 			if ((fd = mkstemp(tmp_menu)) == -1) {
1246 				err = errno;
1247 				be_print_err(gettext("be_remove_menu: "
1248 				    "mkstemp failed: %s\n"), strerror(err));
1249 				ret = errno_to_be_err(err);
1250 				free(tmp_menu);
1251 				tmp_menu = NULL;
1252 				goto cleanup;
1253 			}
1254 			if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
1255 				err = errno;
1256 				be_print_err(gettext("be_remove_menu: "
1257 				    "could not open tmp file for write: %s\n"),
1258 				    strerror(err));
1259 				(void) close(fd);
1260 				ret = errno_to_be_err(err);
1261 				goto cleanup;
1262 			}
1263 
1264 			while (fgets(menu_buf, BUFSIZ, menu_fp)) {
1265 				char tline [BUFSIZ];
1266 				char *tok = NULL;
1267 
1268 				(void) strlcpy(tline, menu_buf, sizeof (tline));
1269 
1270 				/* Tokenize line */
1271 				tok = strtok(tline, BE_WHITE_SPACE);
1272 
1273 				if (tok == NULL) {
1274 					/* Found empty line, write it out */
1275 					(void) fputs(menu_buf, tmp_menu_fp);
1276 				} else if (strcmp(tok, "default") == 0) {
1277 					/* Found the default line, adjust it */
1278 					(void) snprintf(tline, sizeof (tline),
1279 					    "default %d\n", default_entry);
1280 
1281 					(void) fputs(tline, tmp_menu_fp);
1282 				} else {
1283 					/* Pass through all other lines */
1284 					(void) fputs(menu_buf, tmp_menu_fp);
1285 				}
1286 			}
1287 
1288 			(void) fclose(menu_fp);
1289 			menu_fp = NULL;
1290 			(void) fclose(tmp_menu_fp);
1291 			tmp_menu_fp = NULL;
1292 
1293 			/* Copy the modified menu.lst into place */
1294 			if (rename(tmp_menu, menu) != 0) {
1295 				err = errno;
1296 				be_print_err(gettext("be_remove_menu: "
1297 				    "failed to rename file %s to %s: %s\n"),
1298 				    tmp_menu, menu, strerror(err));
1299 				ret = errno_to_be_err(err);
1300 				goto cleanup;
1301 			}
1302 
1303 			free(tmp_menu);
1304 			tmp_menu = NULL;
1305 		}
1306 	}
1307 
1308 	/* Set the perms and ownership of the updated file */
1309 	if (chmod(menu, sb.st_mode) != 0) {
1310 		err = errno;
1311 		be_print_err(gettext("be_remove_menu: "
1312 		    "failed to chmod %s: %s\n"), menu, strerror(err));
1313 		ret = errno_to_be_err(err);
1314 		goto cleanup;
1315 	}
1316 	if (chown(menu, sb.st_uid, sb.st_gid) != 0) {
1317 		err = errno;
1318 		be_print_err(gettext("be_remove_menu: "
1319 		    "failed to chown %s: %s\n"), menu, strerror(err));
1320 		ret = errno_to_be_err(err);
1321 		goto cleanup;
1322 	}
1323 
1324 cleanup:
1325 	if (pool_mounted) {
1326 		int err = BE_SUCCESS;
1327 		err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1328 		if (ret == BE_SUCCESS)
1329 			ret = err;
1330 		free(orig_mntpnt);
1331 		free(ptmp_mntpnt);
1332 	}
1333 	ZFS_CLOSE(zhp);
1334 
1335 	free(buffer);
1336 	if (menu_fp != NULL)
1337 		(void) fclose(menu_fp);
1338 	if (tmp_menu_fp != NULL)
1339 		(void) fclose(tmp_menu_fp);
1340 	if (tmp_menu != NULL) {
1341 		(void) unlink(tmp_menu);
1342 		free(tmp_menu);
1343 	}
1344 
1345 	return (ret);
1346 }
1347 
1348 /*
1349  * Function:	be_default_grub_bootfs
1350  * Description:	This function returns the dataset in the default entry of
1351  *		the grub menu. If no default entry is found with a valid bootfs
1352  *		entry NULL is returned.
1353  * Parameters:
1354  *		be_root_pool - This is the name of the root pool where the
1355  *			       grub menu can be found.
1356  *              def_bootfs - This is used to pass back the bootfs string. On
1357  *				error NULL is returned here.
1358  * Returns:
1359  *		Success - BE_SUCCESS is returned.
1360  *		Failure - a be_errno_t is returned.
1361  * Scope:
1362  *		Semi-private (library wide use only)
1363  */
1364 int
1365 be_default_grub_bootfs(const char *be_root_pool, char **def_bootfs)
1366 {
1367 	zfs_handle_t	*zhp = NULL;
1368 	char		grub_file[MAXPATHLEN];
1369 	FILE		*menu_fp;
1370 	char		line[BUFSIZ];
1371 	char		*pool_mntpnt = NULL;
1372 	char		*ptmp_mntpnt = NULL;
1373 	char		*orig_mntpnt = NULL;
1374 	int		default_entry = 0, entries = 0;
1375 	int		found_default = 0;
1376 	int		ret = BE_SUCCESS;
1377 	boolean_t	pool_mounted = B_FALSE;
1378 
1379 	errno = 0;
1380 
1381 	/*
1382 	 * Check to see if this system supports grub
1383 	 */
1384 	if (!be_has_grub()) {
1385 		be_print_err(gettext("be_default_grub_bootfs: operation "
1386 		    "not supported on this architecture\n"));
1387 		return (BE_ERR_NOTSUP);
1388 	}
1389 
1390 	*def_bootfs = NULL;
1391 
1392 	/* Get handle to pool dataset */
1393 	if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1394 		be_print_err(gettext("be_default_grub_bootfs: "
1395 		    "failed to open pool dataset for %s: %s"),
1396 		    be_root_pool, libzfs_error_description(g_zfs));
1397 		return (zfs_err_to_be_err(g_zfs));
1398 	}
1399 
1400 	/*
1401 	 * Check to see if the pool's dataset is mounted. If it isn't we'll
1402 	 * attempt to mount it.
1403 	 */
1404 	if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1405 	    &pool_mounted)) != BE_SUCCESS) {
1406 		be_print_err(gettext("be_default_grub_bootfs: pool dataset "
1407 		    "(%s) could not be mounted\n"), be_root_pool);
1408 		ZFS_CLOSE(zhp);
1409 		return (ret);
1410 	}
1411 
1412 	/*
1413 	 * Get the mountpoint for the root pool dataset.
1414 	 */
1415 	if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1416 		be_print_err(gettext("be_default_grub_bootfs: failed "
1417 		    "to get mount point for the root pool. Can't set "
1418 		    "the default BE in the grub menu.\n"));
1419 		ret = BE_ERR_NO_MENU;
1420 		goto cleanup;
1421 	}
1422 
1423 	(void) snprintf(grub_file, MAXPATHLEN, "%s%s",
1424 	    pool_mntpnt, BE_GRUB_MENU);
1425 
1426 	if ((ret = be_open_menu((char *)be_root_pool, grub_file,
1427 	    &menu_fp, "r", B_FALSE)) != BE_SUCCESS) {
1428 		goto cleanup;
1429 	} else if (menu_fp == NULL) {
1430 		ret = BE_ERR_NO_MENU;
1431 		goto cleanup;
1432 	}
1433 
1434 	free(pool_mntpnt);
1435 	pool_mntpnt = NULL;
1436 
1437 	while (fgets(line, BUFSIZ, menu_fp)) {
1438 		char *tok = strtok(line, BE_WHITE_SPACE);
1439 
1440 		if (tok != NULL && tok[0] != '#') {
1441 			if (!found_default) {
1442 				if (strcmp(tok, "default") == 0) {
1443 					tok = strtok(NULL, BE_WHITE_SPACE);
1444 					if (tok != NULL) {
1445 						default_entry = atoi(tok);
1446 						rewind(menu_fp);
1447 						found_default = 1;
1448 					}
1449 				}
1450 				continue;
1451 			}
1452 			if (strcmp(tok, "title") == 0) {
1453 				entries++;
1454 			} else if (default_entry == entries - 1) {
1455 				if (strcmp(tok, "bootfs") == 0) {
1456 					tok = strtok(NULL, BE_WHITE_SPACE);
1457 					(void) fclose(menu_fp);
1458 
1459 					if (tok == NULL) {
1460 						ret = BE_SUCCESS;
1461 						goto cleanup;
1462 					}
1463 
1464 					if ((*def_bootfs = strdup(tok)) !=
1465 					    NULL) {
1466 						ret = BE_SUCCESS;
1467 						goto cleanup;
1468 					}
1469 					be_print_err(gettext(
1470 					    "be_default_grub_bootfs: "
1471 					    "memory allocation failed\n"));
1472 					ret = BE_ERR_NOMEM;
1473 					goto cleanup;
1474 				}
1475 			} else if (default_entry < entries - 1) {
1476 				/*
1477 				 * no bootfs entry for the default entry.
1478 				 */
1479 				break;
1480 			}
1481 		}
1482 	}
1483 	(void) fclose(menu_fp);
1484 
1485 cleanup:
1486 	if (pool_mounted) {
1487 		int err = BE_SUCCESS;
1488 		err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1489 		if (ret == BE_SUCCESS)
1490 			ret = err;
1491 		free(orig_mntpnt);
1492 		free(ptmp_mntpnt);
1493 	}
1494 	ZFS_CLOSE(zhp);
1495 	return (ret);
1496 }
1497 
1498 /*
1499  * Function:	be_change_grub_default
1500  * Description:	This function takes two parameters. These are the name of
1501  *		the BE we want to have as the default booted in the grub
1502  *		menu and the root pool where the path to the grub menu exists.
1503  *		The code takes this and finds the BE's entry in the grub menu
1504  *		and changes the default entry to point to that entry in the
1505  *		list.
1506  * Parameters:
1507  *		be_name - This is the name of the BE wanted as the default
1508  *			for the next boot.
1509  *		be_root_pool - This is the name of the root pool where the
1510  *			grub menu can be found.
1511  * Returns:
1512  *		BE_SUCCESS - Success
1513  *		be_errno_t - Failure
1514  * Scope:
1515  *		Semi-private (library wide use only)
1516  */
1517 int
1518 be_change_grub_default(char *be_name, char *be_root_pool)
1519 {
1520 	zfs_handle_t	*zhp = NULL;
1521 	char	grub_file[MAXPATHLEN];
1522 	char	*temp_grub = NULL;
1523 	char	*pool_mntpnt = NULL;
1524 	char	*ptmp_mntpnt = NULL;
1525 	char	*orig_mntpnt = NULL;
1526 	char	line[BUFSIZ];
1527 	char	temp_line[BUFSIZ];
1528 	char	be_root_ds[MAXPATHLEN];
1529 	FILE	*grub_fp = NULL;
1530 	FILE	*temp_fp = NULL;
1531 	struct stat	sb;
1532 	int	temp_grub_len = 0;
1533 	int	fd, entries = 0;
1534 	int	err = 0;
1535 	int	ret = BE_SUCCESS;
1536 	boolean_t	found_default = B_FALSE;
1537 	boolean_t	pool_mounted = B_FALSE;
1538 
1539 	errno = 0;
1540 
1541 	/*
1542 	 * Check to see if this system supports grub
1543 	 */
1544 	if (!be_has_grub()) {
1545 		be_print_err(gettext("be_change_grub_default: operation "
1546 		    "not supported on this architecture\n"));
1547 		return (BE_ERR_NOTSUP);
1548 	}
1549 
1550 	/* Generate string for BE's root dataset */
1551 	be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
1552 
1553 	/* Get handle to pool dataset */
1554 	if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1555 		be_print_err(gettext("be_change_grub_default: "
1556 		    "failed to open pool dataset for %s: %s"),
1557 		    be_root_pool, libzfs_error_description(g_zfs));
1558 		return (zfs_err_to_be_err(g_zfs));
1559 	}
1560 
1561 	/*
1562 	 * Check to see if the pool's dataset is mounted. If it isn't we'll
1563 	 * attempt to mount it.
1564 	 */
1565 	if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1566 	    &pool_mounted)) != BE_SUCCESS) {
1567 		be_print_err(gettext("be_change_grub_default: pool dataset "
1568 		    "(%s) could not be mounted\n"), be_root_pool);
1569 		ZFS_CLOSE(zhp);
1570 		return (ret);
1571 	}
1572 
1573 	/*
1574 	 * Get the mountpoint for the root pool dataset.
1575 	 */
1576 	if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1577 		be_print_err(gettext("be_change_grub_default: pool "
1578 		    "dataset (%s) is not mounted. Can't set "
1579 		    "the default BE in the grub menu.\n"), be_root_pool);
1580 		ret = BE_ERR_NO_MENU;
1581 		goto cleanup;
1582 	}
1583 
1584 	(void) snprintf(grub_file, MAXPATHLEN, "%s%s",
1585 	    pool_mntpnt, BE_GRUB_MENU);
1586 
1587 	if ((ret = be_open_menu(be_root_pool, grub_file,
1588 	    &grub_fp, "r+", B_TRUE)) != BE_SUCCESS) {
1589 		goto cleanup;
1590 	} else if (grub_fp == NULL) {
1591 		ret = BE_ERR_NO_MENU;
1592 		goto cleanup;
1593 	}
1594 
1595 	free(pool_mntpnt);
1596 	pool_mntpnt = NULL;
1597 
1598 	/* Grab the stats of the original menu file */
1599 	if (stat(grub_file, &sb) != 0) {
1600 		err = errno;
1601 		be_print_err(gettext("be_change_grub_default: "
1602 		    "failed to stat file %s: %s\n"), grub_file, strerror(err));
1603 		ret = errno_to_be_err(err);
1604 		goto cleanup;
1605 	}
1606 
1607 	/* Create a tmp file for the modified menu.lst */
1608 	temp_grub_len = strlen(grub_file) + 7;
1609 	if ((temp_grub = (char *)malloc(temp_grub_len)) == NULL) {
1610 		be_print_err(gettext("be_change_grub_default: "
1611 		    "malloc failed\n"));
1612 		ret = BE_ERR_NOMEM;
1613 		goto cleanup;
1614 	}
1615 	(void) memset(temp_grub, 0, temp_grub_len);
1616 	(void) strlcpy(temp_grub, grub_file, temp_grub_len);
1617 	(void) strlcat(temp_grub, "XXXXXX", temp_grub_len);
1618 	if ((fd = mkstemp(temp_grub)) == -1) {
1619 		err = errno;
1620 		be_print_err(gettext("be_change_grub_default: "
1621 		    "mkstemp failed: %s\n"), strerror(err));
1622 		ret = errno_to_be_err(err);
1623 		free(temp_grub);
1624 		temp_grub = NULL;
1625 		goto cleanup;
1626 	}
1627 	if ((temp_fp = fdopen(fd, "w")) == NULL) {
1628 		err = errno;
1629 		be_print_err(gettext("be_change_grub_default: "
1630 		    "failed to open %s file: %s\n"),
1631 		    temp_grub, strerror(err));
1632 		(void) close(fd);
1633 		ret = errno_to_be_err(err);
1634 		goto cleanup;
1635 	}
1636 
1637 	while (fgets(line, BUFSIZ, grub_fp)) {
1638 		char *tok = strtok(line, BE_WHITE_SPACE);
1639 
1640 		if (tok == NULL || tok[0] == '#') {
1641 			continue;
1642 		} else if (strcmp(tok, "title") == 0) {
1643 			entries++;
1644 			continue;
1645 		} else if (strcmp(tok, "bootfs") == 0) {
1646 			char *bootfs = strtok(NULL, BE_WHITE_SPACE);
1647 			if (bootfs == NULL)
1648 				continue;
1649 
1650 			if (strcmp(bootfs, be_root_ds) == 0) {
1651 				found_default = B_TRUE;
1652 				break;
1653 			}
1654 		}
1655 	}
1656 
1657 	if (!found_default) {
1658 		be_print_err(gettext("be_change_grub_default: failed "
1659 		    "to find entry for %s in the grub menu\n"),
1660 		    be_name);
1661 		ret = BE_ERR_BE_NOENT;
1662 		goto cleanup;
1663 	}
1664 
1665 	rewind(grub_fp);
1666 
1667 	while (fgets(line, BUFSIZ, grub_fp)) {
1668 		char *tok = NULL;
1669 
1670 		(void) strncpy(temp_line, line, BUFSIZ);
1671 
1672 		if ((tok = strtok(temp_line, BE_WHITE_SPACE)) != NULL &&
1673 		    strcmp(tok, "default") == 0) {
1674 			(void) snprintf(temp_line, BUFSIZ, "default %d\n",
1675 			    entries - 1 >= 0 ? entries - 1 : 0);
1676 			(void) fputs(temp_line, temp_fp);
1677 		} else {
1678 			(void) fputs(line, temp_fp);
1679 		}
1680 	}
1681 
1682 	(void) fclose(grub_fp);
1683 	grub_fp = NULL;
1684 	(void) fclose(temp_fp);
1685 	temp_fp = NULL;
1686 
1687 	if (rename(temp_grub, grub_file) != 0) {
1688 		err = errno;
1689 		be_print_err(gettext("be_change_grub_default: "
1690 		    "failed to rename file %s to %s: %s\n"),
1691 		    temp_grub, grub_file, strerror(err));
1692 		ret = errno_to_be_err(err);
1693 		goto cleanup;
1694 	}
1695 	free(temp_grub);
1696 	temp_grub = NULL;
1697 
1698 	/* Set the perms and ownership of the updated file */
1699 	if (chmod(grub_file, sb.st_mode) != 0) {
1700 		err = errno;
1701 		be_print_err(gettext("be_change_grub_default: "
1702 		    "failed to chmod %s: %s\n"), grub_file, strerror(err));
1703 		ret = errno_to_be_err(err);
1704 		goto cleanup;
1705 	}
1706 	if (chown(grub_file, sb.st_uid, sb.st_gid) != 0) {
1707 		err = errno;
1708 		be_print_err(gettext("be_change_grub_default: "
1709 		    "failed to chown %s: %s\n"), grub_file, strerror(err));
1710 		ret = errno_to_be_err(err);
1711 	}
1712 
1713 cleanup:
1714 	if (pool_mounted) {
1715 		int err = BE_SUCCESS;
1716 		err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1717 		if (ret == BE_SUCCESS)
1718 			ret = err;
1719 		free(orig_mntpnt);
1720 		free(ptmp_mntpnt);
1721 	}
1722 	ZFS_CLOSE(zhp);
1723 	if (grub_fp != NULL)
1724 		(void) fclose(grub_fp);
1725 	if (temp_fp != NULL)
1726 		(void) fclose(temp_fp);
1727 	if (temp_grub != NULL) {
1728 		(void) unlink(temp_grub);
1729 		free(temp_grub);
1730 	}
1731 
1732 	return (ret);
1733 }
1734 
1735 /*
1736  * Function:	be_update_menu
1737  * Description:	This function is used by be_rename to change the BE name in
1738  *		an existing entry in the grub menu to the new name of the BE.
1739  * Parameters:
1740  *		be_orig_name - the original name of the BE
1741  *		be_new_name - the new name the BE is being renameed to.
1742  *		be_root_pool - The pool which contains the grub menu
1743  *		boot_pool - the pool where the BE is, if different than
1744  *			the pool containing the boot menu.  If this is
1745  *			NULL it will be set to be_root_pool.
1746  * Returns:
1747  *		BE_SUCCESS - Success
1748  *		be_errno_t - Failure
1749  * Scope:
1750  *		Semi-private (library wide use only)
1751  */
1752 int
1753 be_update_menu(char *be_orig_name, char *be_new_name, char *be_root_pool,
1754     char *boot_pool)
1755 {
1756 	zfs_handle_t *zhp = NULL;
1757 	char menu_file[MAXPATHLEN];
1758 	char be_root_ds[MAXPATHLEN];
1759 	char be_new_root_ds[MAXPATHLEN];
1760 	char line[BUFSIZ];
1761 	char *pool_mntpnt = NULL;
1762 	char *ptmp_mntpnt = NULL;
1763 	char *orig_mntpnt = NULL;
1764 	char *temp_menu = NULL;
1765 	FILE *menu_fp = NULL;
1766 	FILE *new_fp = NULL;
1767 	struct stat sb;
1768 	int temp_menu_len = 0;
1769 	int tmp_fd;
1770 	int ret = BE_SUCCESS;
1771 	int err = 0;
1772 	boolean_t pool_mounted = B_FALSE;
1773 
1774 	errno = 0;
1775 
1776 	if (boot_pool == NULL)
1777 		boot_pool = be_root_pool;
1778 
1779 	if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1780 		be_print_err(gettext("be_update_menu: failed to open "
1781 		    "pool dataset for %s: %s\n"), be_root_pool,
1782 		    libzfs_error_description(g_zfs));
1783 		return (zfs_err_to_be_err(g_zfs));
1784 	}
1785 
1786 	/*
1787 	 * Check to see if the pool's dataset is mounted. If it isn't we'll
1788 	 * attempt to mount it.
1789 	 */
1790 	if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1791 	    &pool_mounted)) != BE_SUCCESS) {
1792 		be_print_err(gettext("be_update_menu: pool dataset "
1793 		    "(%s) could not be mounted\n"), be_root_pool);
1794 		ZFS_CLOSE(zhp);
1795 		return (ret);
1796 	}
1797 
1798 	/*
1799 	 * Get the mountpoint for the root pool dataset.
1800 	 */
1801 	if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1802 		be_print_err(gettext("be_update_menu: failed "
1803 		    "to get mount point for the root pool. Can't set "
1804 		    "the default BE in the grub menu.\n"));
1805 		ret = BE_ERR_NO_MENU;
1806 		goto cleanup;
1807 	}
1808 
1809 	/*
1810 	 * Check to see if this system supports grub
1811 	 */
1812 	if (be_has_grub()) {
1813 		(void) snprintf(menu_file, sizeof (menu_file),
1814 		    "%s%s", pool_mntpnt, BE_GRUB_MENU);
1815 	} else {
1816 		(void) snprintf(menu_file, sizeof (menu_file),
1817 		    "%s%s", pool_mntpnt, BE_SPARC_MENU);
1818 	}
1819 
1820 	be_make_root_ds(be_root_pool, be_orig_name, be_root_ds,
1821 	    sizeof (be_root_ds));
1822 	be_make_root_ds(be_root_pool, be_new_name, be_new_root_ds,
1823 	    sizeof (be_new_root_ds));
1824 
1825 	if ((ret = be_open_menu(be_root_pool, menu_file,
1826 	    &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
1827 		goto cleanup;
1828 	} else if (menu_fp == NULL) {
1829 		ret = BE_ERR_NO_MENU;
1830 		goto cleanup;
1831 	}
1832 
1833 	free(pool_mntpnt);
1834 	pool_mntpnt = NULL;
1835 
1836 	/* Grab the stat of the original menu file */
1837 	if (stat(menu_file, &sb) != 0) {
1838 		err = errno;
1839 		be_print_err(gettext("be_update_menu: "
1840 		    "failed to stat file %s: %s\n"), menu_file, strerror(err));
1841 		(void) fclose(menu_fp);
1842 		ret = errno_to_be_err(err);
1843 		goto cleanup;
1844 	}
1845 
1846 	/* Create tmp file for modified menu.lst */
1847 	temp_menu_len = strlen(menu_file) + 7;
1848 	if ((temp_menu = (char *)malloc(temp_menu_len))
1849 	    == NULL) {
1850 		be_print_err(gettext("be_update_menu: "
1851 		    "malloc failed\n"));
1852 		(void) fclose(menu_fp);
1853 		ret = BE_ERR_NOMEM;
1854 		goto cleanup;
1855 	}
1856 	(void) memset(temp_menu, 0, temp_menu_len);
1857 	(void) strlcpy(temp_menu, menu_file, temp_menu_len);
1858 	(void) strlcat(temp_menu, "XXXXXX", temp_menu_len);
1859 	if ((tmp_fd = mkstemp(temp_menu)) == -1) {
1860 		err = errno;
1861 		be_print_err(gettext("be_update_menu: "
1862 		    "mkstemp failed: %s\n"), strerror(err));
1863 		(void) fclose(menu_fp);
1864 		free(temp_menu);
1865 		ret = errno_to_be_err(err);
1866 		goto cleanup;
1867 	}
1868 	if ((new_fp = fdopen(tmp_fd, "w")) == NULL) {
1869 		err = errno;
1870 		be_print_err(gettext("be_update_menu: "
1871 		    "fdopen failed: %s\n"), strerror(err));
1872 		(void) close(tmp_fd);
1873 		(void) fclose(menu_fp);
1874 		free(temp_menu);
1875 		ret = errno_to_be_err(err);
1876 		goto cleanup;
1877 	}
1878 
1879 	while (fgets(line, BUFSIZ, menu_fp)) {
1880 		char tline[BUFSIZ];
1881 		char new_line[BUFSIZ];
1882 		char *c = NULL;
1883 
1884 		(void) strlcpy(tline, line, sizeof (tline));
1885 
1886 		/* Tokenize line */
1887 		c = strtok(tline, BE_WHITE_SPACE);
1888 
1889 		if (c == NULL) {
1890 			/* Found empty line, write it out. */
1891 			(void) fputs(line, new_fp);
1892 		} else if (c[0] == '#') {
1893 			/* Found a comment line, write it out. */
1894 			(void) fputs(line, new_fp);
1895 		} else if (strcmp(c, "title") == 0) {
1896 			char *name = NULL;
1897 			char *desc = NULL;
1898 
1899 			/*
1900 			 * Found a 'title' line, parse out BE name or
1901 			 * the description.
1902 			 */
1903 			name = strtok(NULL, BE_WHITE_SPACE);
1904 
1905 			if (name == NULL) {
1906 				/*
1907 				 * Nothing after 'title', just push
1908 				 * this line through
1909 				 */
1910 				(void) fputs(line, new_fp);
1911 			} else {
1912 				/*
1913 				 * Grab the remainder of the title which
1914 				 * could be a multi worded description
1915 				 */
1916 				desc = strtok(NULL, "\n");
1917 
1918 				if (strcmp(name, be_orig_name) == 0) {
1919 					/*
1920 					 * The first token of the title is
1921 					 * the old BE name, replace it with
1922 					 * the new one, and write it out
1923 					 * along with the remainder of
1924 					 * description if there is one.
1925 					 */
1926 					if (desc) {
1927 						(void) snprintf(new_line,
1928 						    sizeof (new_line),
1929 						    "title %s %s\n",
1930 						    be_new_name, desc);
1931 					} else {
1932 						(void) snprintf(new_line,
1933 						    sizeof (new_line),
1934 						    "title %s\n", be_new_name);
1935 					}
1936 
1937 					(void) fputs(new_line, new_fp);
1938 				} else {
1939 					(void) fputs(line, new_fp);
1940 				}
1941 			}
1942 		} else if (strcmp(c, "bootfs") == 0) {
1943 			/*
1944 			 * Found a 'bootfs' line, parse out the BE root
1945 			 * dataset value.
1946 			 */
1947 			char *root_ds = strtok(NULL, BE_WHITE_SPACE);
1948 
1949 			if (root_ds == NULL) {
1950 				/*
1951 				 * Nothing after 'bootfs', just push
1952 				 * this line through
1953 				 */
1954 				(void) fputs(line, new_fp);
1955 			} else {
1956 				/*
1957 				 * If this bootfs is the one we're renaming,
1958 				 * write out the new root dataset value
1959 				 */
1960 				if (strcmp(root_ds, be_root_ds) == 0) {
1961 					(void) snprintf(new_line,
1962 					    sizeof (new_line), "bootfs %s\n",
1963 					    be_new_root_ds);
1964 
1965 					(void) fputs(new_line, new_fp);
1966 				} else {
1967 					(void) fputs(line, new_fp);
1968 				}
1969 			}
1970 		} else {
1971 			/*
1972 			 * Found some other line we don't care
1973 			 * about, write it out.
1974 			 */
1975 			(void) fputs(line, new_fp);
1976 		}
1977 	}
1978 
1979 	(void) fclose(menu_fp);
1980 	(void) fclose(new_fp);
1981 	(void) close(tmp_fd);
1982 
1983 	if (rename(temp_menu, menu_file) != 0) {
1984 		err = errno;
1985 		be_print_err(gettext("be_update_menu: "
1986 		    "failed to rename file %s to %s: %s\n"),
1987 		    temp_menu, menu_file, strerror(err));
1988 		ret = errno_to_be_err(err);
1989 	}
1990 	free(temp_menu);
1991 
1992 	/* Set the perms and ownership of the updated file */
1993 	if (chmod(menu_file, sb.st_mode) != 0) {
1994 		err = errno;
1995 		be_print_err(gettext("be_update_menu: "
1996 		    "failed to chmod %s: %s\n"), menu_file, strerror(err));
1997 		ret = errno_to_be_err(err);
1998 		goto cleanup;
1999 	}
2000 	if (chown(menu_file, sb.st_uid, sb.st_gid) != 0) {
2001 		err = errno;
2002 		be_print_err(gettext("be_update_menu: "
2003 		    "failed to chown %s: %s\n"), menu_file, strerror(err));
2004 		ret = errno_to_be_err(err);
2005 	}
2006 
2007 cleanup:
2008 	if (pool_mounted) {
2009 		int err = BE_SUCCESS;
2010 		err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
2011 		if (ret == BE_SUCCESS)
2012 			ret = err;
2013 		free(orig_mntpnt);
2014 		free(ptmp_mntpnt);
2015 	}
2016 	ZFS_CLOSE(zhp);
2017 	return (ret);
2018 }
2019 
2020 /*
2021  * Function:	be_has_menu_entry
2022  * Description:	Checks to see if the BEs root dataset has an entry in the grub
2023  *		menu.
2024  * Parameters:
2025  *		be_dataset - The root dataset of the BE
2026  *		be_root_pool - The pool which contains the boot menu
2027  *		entry - A pointer the the entry number of the BE if found.
2028  * Returns:
2029  *		B_TRUE - Success
2030  *		B_FALSE - Failure
2031  * Scope:
2032  *		Semi-private (library wide use only)
2033  */
2034 boolean_t
2035 be_has_menu_entry(char *be_dataset, char *be_root_pool, int *entry)
2036 {
2037 	zfs_handle_t *zhp = NULL;
2038 	char		menu_file[MAXPATHLEN];
2039 	FILE		*menu_fp;
2040 	char		line[BUFSIZ];
2041 	char		*last;
2042 	char		*rpool_mntpnt = NULL;
2043 	char		*ptmp_mntpnt = NULL;
2044 	char		*orig_mntpnt = NULL;
2045 	int		ent_num = 0;
2046 	boolean_t	ret = 0;
2047 	boolean_t	pool_mounted = B_FALSE;
2048 
2049 
2050 	/*
2051 	 * Check to see if this system supports grub
2052 	 */
2053 	if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
2054 		be_print_err(gettext("be_has_menu_entry: failed to open "
2055 		    "pool dataset for %s: %s\n"), be_root_pool,
2056 		    libzfs_error_description(g_zfs));
2057 		return (B_FALSE);
2058 	}
2059 
2060 	/*
2061 	 * Check to see if the pool's dataset is mounted. If it isn't we'll
2062 	 * attempt to mount it.
2063 	 */
2064 	if (be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
2065 	    &pool_mounted) != 0) {
2066 		be_print_err(gettext("be_has_menu_entry: pool dataset "
2067 		    "(%s) could not be mounted\n"), be_root_pool);
2068 		ZFS_CLOSE(zhp);
2069 		return (B_FALSE);
2070 	}
2071 
2072 	/*
2073 	 * Get the mountpoint for the root pool dataset.
2074 	 */
2075 	if (!zfs_is_mounted(zhp, &rpool_mntpnt)) {
2076 		be_print_err(gettext("be_has_menu_entry: pool "
2077 		    "dataset (%s) is not mounted. Can't set "
2078 		    "the default BE in the grub menu.\n"), be_root_pool);
2079 		ret = B_FALSE;
2080 		goto cleanup;
2081 	}
2082 
2083 	if (be_has_grub()) {
2084 		(void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
2085 		    rpool_mntpnt, BE_GRUB_MENU);
2086 	} else {
2087 		(void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
2088 		    rpool_mntpnt, BE_SPARC_MENU);
2089 	}
2090 
2091 	if (be_open_menu(be_root_pool, menu_file, &menu_fp, "r",
2092 	    B_FALSE) != 0) {
2093 		ret = B_FALSE;
2094 		goto cleanup;
2095 	} else if (menu_fp == NULL) {
2096 		ret = B_FALSE;
2097 		goto cleanup;
2098 	}
2099 
2100 	free(rpool_mntpnt);
2101 	rpool_mntpnt = NULL;
2102 
2103 	while (fgets(line, BUFSIZ, menu_fp)) {
2104 		char *tok = strtok_r(line, BE_WHITE_SPACE, &last);
2105 
2106 		if (tok != NULL && tok[0] != '#') {
2107 			if (strcmp(tok, "bootfs") == 0) {
2108 				tok = strtok_r(last, BE_WHITE_SPACE, &last);
2109 				if (tok != NULL && strcmp(tok,
2110 				    be_dataset) == 0) {
2111 					(void) fclose(menu_fp);
2112 					/*
2113 					 * The entry number needs to be
2114 					 * decremented here because the title
2115 					 * will always be the first line for
2116 					 * an entry. Because of this we'll
2117 					 * always be off by one entry when we
2118 					 * check for bootfs.
2119 					 */
2120 					*entry = ent_num - 1;
2121 					ret = B_TRUE;
2122 					goto cleanup;
2123 				}
2124 			} else if (strcmp(tok, "title") == 0)
2125 				ent_num++;
2126 		}
2127 	}
2128 
2129 cleanup:
2130 	if (pool_mounted) {
2131 		(void) be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
2132 		free(orig_mntpnt);
2133 		free(ptmp_mntpnt);
2134 	}
2135 	ZFS_CLOSE(zhp);
2136 	(void) fclose(menu_fp);
2137 	return (ret);
2138 }
2139 
2140 /*
2141  * Function:	be_update_vfstab
2142  * Description:	This function digs into a BE's vfstab and updates all
2143  *		entries with file systems listed in be_fs_list_data_t.
2144  *		The entry's root container dataset and be_name will be
2145  *		updated with the parameters passed in.
2146  * Parameters:
2147  *		be_name - name of BE to update
2148  *		old_rc_loc - dataset under which the root container dataset
2149  *			of the old BE resides in.
2150  *		new_rc_loc - dataset under which the root container dataset
2151  *			of the new BE resides in.
2152  *		fld - be_fs_list_data_t pointer providing the list of
2153  *			file systems to look for in vfstab.
2154  *		mountpoint - directory of where BE is currently mounted.
2155  *			If NULL, then BE is not currently mounted.
2156  * Returns:
2157  *		BE_SUCCESS - Success
2158  *		be_errno_t - Failure
2159  * Scope:
2160  *		Semi-private (library wide use only)
2161  */
2162 int
2163 be_update_vfstab(char *be_name, char *old_rc_loc, char *new_rc_loc,
2164     be_fs_list_data_t *fld, char *mountpoint)
2165 {
2166 	char		*tmp_mountpoint = NULL;
2167 	char		alt_vfstab[MAXPATHLEN];
2168 	int		ret = BE_SUCCESS, err = BE_SUCCESS;
2169 
2170 	if (fld == NULL || fld->fs_list == NULL || fld->fs_num == 0)
2171 		return (BE_SUCCESS);
2172 
2173 	/* If BE not already mounted, mount the BE */
2174 	if (mountpoint == NULL) {
2175 		if ((ret = _be_mount(be_name, &tmp_mountpoint,
2176 		    BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
2177 			be_print_err(gettext("be_update_vfstab: "
2178 			    "failed to mount BE (%s)\n"), be_name);
2179 			return (ret);
2180 		}
2181 	} else {
2182 		tmp_mountpoint = mountpoint;
2183 	}
2184 
2185 	/* Get string for vfstab in the mounted BE. */
2186 	(void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
2187 	    tmp_mountpoint);
2188 
2189 	/* Update the vfstab */
2190 	ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
2191 	    fld);
2192 
2193 	/* Unmount BE if we mounted it */
2194 	if (mountpoint == NULL) {
2195 		if ((err = _be_unmount(be_name, 0)) == BE_SUCCESS) {
2196 			/* Remove temporary mountpoint */
2197 			(void) rmdir(tmp_mountpoint);
2198 		} else {
2199 			be_print_err(gettext("be_update_vfstab: "
2200 			    "failed to unmount BE %s mounted at %s\n"),
2201 			    be_name, tmp_mountpoint);
2202 			if (ret == BE_SUCCESS)
2203 				ret = err;
2204 		}
2205 
2206 		free(tmp_mountpoint);
2207 	}
2208 
2209 	return (ret);
2210 }
2211 
2212 /*
2213  * Function:	be_update_zone_vfstab
2214  * Description:	This function digs into a zone BE's vfstab and updates all
2215  *		entries with file systems listed in be_fs_list_data_t.
2216  *		The entry's root container dataset and be_name will be
2217  *		updated with the parameters passed in.
2218  * Parameters:
2219  *		zhp - zfs_handle_t pointer to zone root dataset.
2220  *		be_name - name of zone BE to update
2221  *		old_rc_loc - dataset under which the root container dataset
2222  *			of the old zone BE resides in.
2223  *		new_rc_loc - dataset under which the root container dataset
2224  *			of the new zone BE resides in.
2225  *		fld - be_fs_list_data_t pointer providing the list of
2226  *			file systems to look for in vfstab.
2227  * Returns:
2228  *		BE_SUCCESS - Success
2229  *		be_errno_t - Failure
2230  * Scope:
2231  *		Semi-private (library wide use only)
2232  */
2233 int
2234 be_update_zone_vfstab(zfs_handle_t *zhp, char *be_name, char *old_rc_loc,
2235     char *new_rc_loc, be_fs_list_data_t *fld)
2236 {
2237 	be_mount_data_t		md = { 0 };
2238 	be_unmount_data_t	ud = { 0 };
2239 	char			alt_vfstab[MAXPATHLEN];
2240 	boolean_t		mounted_here = B_FALSE;
2241 	int			ret = BE_SUCCESS;
2242 
2243 	/*
2244 	 * If zone root not already mounted, mount it at a
2245 	 * temporary location.
2246 	 */
2247 	if (!zfs_is_mounted(zhp, &md.altroot)) {
2248 		/* Generate temporary mountpoint to mount zone root */
2249 		if ((ret = be_make_tmp_mountpoint(&md.altroot)) != BE_SUCCESS) {
2250 			be_print_err(gettext("be_update_zone_vfstab: "
2251 			    "failed to make temporary mountpoint to "
2252 			    "mount zone root\n"));
2253 			return (ret);
2254 		}
2255 
2256 		if (be_mount_zone_root(zhp, &md) != BE_SUCCESS) {
2257 			be_print_err(gettext("be_update_zone_vfstab: "
2258 			    "failed to mount zone root %s\n"),
2259 			    zfs_get_name(zhp));
2260 			free(md.altroot);
2261 			return (BE_ERR_MOUNT_ZONEROOT);
2262 		}
2263 		mounted_here = B_TRUE;
2264 	}
2265 
2266 	/* Get string from vfstab in the mounted zone BE */
2267 	(void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
2268 	    md.altroot);
2269 
2270 	/* Update the vfstab */
2271 	ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
2272 	    fld);
2273 
2274 	/* Unmount zone root if we mounted it */
2275 	if (mounted_here) {
2276 		ud.force = B_TRUE;
2277 
2278 		if (be_unmount_zone_root(zhp, &ud) == BE_SUCCESS) {
2279 			/* Remove the temporary mountpoint */
2280 			(void) rmdir(md.altroot);
2281 		} else {
2282 			be_print_err(gettext("be_update_zone_vfstab: "
2283 			    "failed to unmount zone root %s from %s\n"),
2284 			    zfs_get_name(zhp), md.altroot);
2285 			if (ret == 0)
2286 				ret = BE_ERR_UMOUNT_ZONEROOT;
2287 		}
2288 	}
2289 
2290 	free(md.altroot);
2291 	return (ret);
2292 }
2293 
2294 /*
2295  * Function:	be_auto_snap_name
2296  * Description:	Generate an auto snapshot name constructed based on the
2297  *		current date and time.  The auto snapshot name is of the form:
2298  *
2299  *			<date>-<time>
2300  *
2301  *		where <date> is in ISO standard format, so the resultant name
2302  *		is of the form:
2303  *
2304  *			%Y-%m-%d-%H:%M:%S
2305  *
2306  * Parameters:
2307  *		None
2308  * Returns:
2309  *		Success - pointer to auto generated snapshot name.  The name
2310  *			is allocated in heap storage so the caller is
2311  *			responsible for free'ing the name.
2312  *		Failure - NULL
2313  * Scope:
2314  *		Semi-private (library wide use only)
2315  */
2316 char *
2317 be_auto_snap_name(void)
2318 {
2319 	time_t		utc_tm = NULL;
2320 	struct tm	*gmt_tm = NULL;
2321 	char		gmt_time_str[64];
2322 	char		*auto_snap_name = NULL;
2323 
2324 	if (time(&utc_tm) == -1) {
2325 		be_print_err(gettext("be_auto_snap_name: time() failed\n"));
2326 		return (NULL);
2327 	}
2328 
2329 	if ((gmt_tm = gmtime(&utc_tm)) == NULL) {
2330 		be_print_err(gettext("be_auto_snap_name: gmtime() failed\n"));
2331 		return (NULL);
2332 	}
2333 
2334 	(void) strftime(gmt_time_str, sizeof (gmt_time_str), "%F-%T", gmt_tm);
2335 
2336 	if ((auto_snap_name = strdup(gmt_time_str)) == NULL) {
2337 		be_print_err(gettext("be_auto_snap_name: "
2338 		    "memory allocation failed\n"));
2339 		return (NULL);
2340 	}
2341 
2342 	return (auto_snap_name);
2343 }
2344 
2345 /*
2346  * Function:	be_auto_be_name
2347  * Description:	Generate an auto BE name constructed based on the BE name
2348  *		of the original BE being cloned.
2349  * Parameters:
2350  *		obe_name - name of the original BE being cloned.
2351  * Returns:
2352  *		Success - pointer to auto generated BE name.  The name
2353  *			is allocated in heap storage so the caller is
2354  *			responsible for free'ing the name.
2355  *		Failure - NULL
2356  * Scope:
2357  *		Semi-private (library wide use only)
2358  */
2359 char *
2360 be_auto_be_name(char *obe_name)
2361 {
2362 	return (be_get_auto_name(obe_name, NULL, B_FALSE));
2363 }
2364 
2365 /*
2366  * Function:	be_auto_zone_be_name
2367  * Description:	Generate an auto BE name for a zone constructed based on
2368  *              the BE name of the original zone BE being cloned.
2369  * Parameters:
2370  *              container_ds - container dataset for the zone.
2371  *		zbe_name - name of the original zone BE being cloned.
2372  * Returns:
2373  *		Success - pointer to auto generated BE name.  The name
2374  *			is allocated in heap storage so the caller is
2375  *			responsible for free'ing the name.
2376  *		Failure - NULL
2377  * Scope:
2378  *		Semi-private (library wide use only)
2379  */
2380 char *
2381 be_auto_zone_be_name(char *container_ds, char *zbe_name)
2382 {
2383 	return (be_get_auto_name(zbe_name, container_ds, B_TRUE));
2384 }
2385 
2386 /*
2387  * Function:	be_valid_be_name
2388  * Description:	Validates a BE name.
2389  * Parameters:
2390  *		be_name - name of BE to validate
2391  * Returns:
2392  *		B_TRUE - be_name is valid
2393  *		B_FALSE - be_name is invalid
2394  * Scope:
2395  *		Semi-private (library wide use only)
2396  */
2397 
2398 boolean_t
2399 be_valid_be_name(const char *be_name)
2400 {
2401 	const char	*c = NULL;
2402 	struct be_defaults be_defaults;
2403 
2404 	if (be_name == NULL)
2405 		return (B_FALSE);
2406 
2407 	be_get_defaults(&be_defaults);
2408 
2409 	/*
2410 	 * A BE name must not be a multi-level dataset name.  We also check
2411 	 * that it does not contain the ' ' and '%' characters.  The ' ' is
2412 	 * a valid character for datasets, however we don't allow that in a
2413 	 * BE name.  The '%' is invalid, but zfs_name_valid() allows it for
2414 	 * internal reasons, so we explicitly check for it here.
2415 	 */
2416 	c = be_name;
2417 	while (*c != '\0' && *c != '/' && *c != ' ' && *c != '%')
2418 		c++;
2419 
2420 	if (*c != '\0')
2421 		return (B_FALSE);
2422 
2423 	/*
2424 	 * The BE name must comply with a zfs dataset filesystem. We also
2425 	 * verify its length to be < BE_NAME_MAX_LEN.
2426 	 */
2427 	if (!zfs_name_valid(be_name, ZFS_TYPE_FILESYSTEM) ||
2428 	    strlen(be_name) > BE_NAME_MAX_LEN)
2429 		return (B_FALSE);
2430 
2431 	if (be_defaults.be_deflt_bename_starts_with[0] != '\0' &&
2432 	    strstr(be_name, be_defaults.be_deflt_bename_starts_with) == NULL) {
2433 		return (B_FALSE);
2434 	}
2435 
2436 	return (B_TRUE);
2437 }
2438 
2439 /*
2440  * Function:	be_valid_auto_snap_name
2441  * Description:	This function checks that a snapshot name is a valid auto
2442  *		generated snapshot name.  A valid auto generated snapshot
2443  *		name is of the form:
2444  *
2445  *			%Y-%m-%d-%H:%M:%S
2446  *
2447  *		An older form of the auto generated snapshot name also
2448  *		included the snapshot's BE cleanup policy and a reserved
2449  *		field.  Those names will also be verified by this function.
2450  *
2451  *		Examples of valid auto snapshot names are:
2452  *
2453  *			2008-03-31-18:41:30
2454  *			2008-03-31-22:17:24
2455  *			<policy>:-:2008:04-05-09:12:55
2456  *			<policy>:-:2008:04-06-15:34:12
2457  *
2458  * Parameters:
2459  *		name - name of the snapshot to be validated.
2460  * Returns:
2461  *		B_TRUE - the name is a valid auto snapshot name.
2462  *		B_FALSE - the name is not a valid auto snapshot name.
2463  * Scope:
2464  *		Semi-private (library wide use only)
2465  */
2466 boolean_t
2467 be_valid_auto_snap_name(char *name)
2468 {
2469 	struct tm gmt_tm;
2470 
2471 	char *policy = NULL;
2472 	char *reserved = NULL;
2473 	char *date = NULL;
2474 	char *c = NULL;
2475 
2476 	/* Validate the snapshot name by converting it into utc time */
2477 	if (strptime(name, "%Y-%m-%d-%T", &gmt_tm) != NULL &&
2478 	    (mktime(&gmt_tm) != -1)) {
2479 		return (B_TRUE);
2480 	}
2481 
2482 	/*
2483 	 * Validate the snapshot name against the older form of an
2484 	 * auto generated snapshot name.
2485 	 */
2486 	policy = strdup(name);
2487 
2488 	/*
2489 	 * Get the first field from the snapshot name,
2490 	 * which is the BE policy
2491 	 */
2492 	c = strchr(policy, ':');
2493 	if (c == NULL) {
2494 		free(policy);
2495 		return (B_FALSE);
2496 	}
2497 	c[0] = '\0';
2498 
2499 	/* Validate the policy name */
2500 	if (!valid_be_policy(policy)) {
2501 		free(policy);
2502 		return (B_FALSE);
2503 	}
2504 
2505 	/* Get the next field, which is the reserved field. */
2506 	if (c[1] == NULL || c[1] == '\0') {
2507 		free(policy);
2508 		return (B_FALSE);
2509 	}
2510 	reserved = c+1;
2511 	c = strchr(reserved, ':');
2512 	if (c == NULL) {
2513 		free(policy);
2514 		return (B_FALSE);
2515 	}
2516 	c[0] = '\0';
2517 
2518 	/* Validate the reserved field */
2519 	if (strcmp(reserved, "-") != 0) {
2520 		free(policy);
2521 		return (B_FALSE);
2522 	}
2523 
2524 	/* The remaining string should be the date field */
2525 	if (c[1] == NULL || c[1] == '\0') {
2526 		free(policy);
2527 		return (B_FALSE);
2528 	}
2529 	date = c+1;
2530 
2531 	/* Validate the date string by converting it into utc time */
2532 	if (strptime(date, "%Y-%m-%d-%T", &gmt_tm) == NULL ||
2533 	    (mktime(&gmt_tm) == -1)) {
2534 		be_print_err(gettext("be_valid_auto_snap_name: "
2535 		    "invalid auto snapshot name\n"));
2536 		free(policy);
2537 		return (B_FALSE);
2538 	}
2539 
2540 	free(policy);
2541 	return (B_TRUE);
2542 }
2543 
2544 /*
2545  * Function:	be_default_policy
2546  * Description:	Temporary hardcoded policy support.  This function returns
2547  *		the default policy type to be used to create a BE or a BE
2548  *		snapshot.
2549  * Parameters:
2550  *		None
2551  * Returns:
2552  *		Name of default BE policy.
2553  * Scope:
2554  *		Semi-private (library wide use only)
2555  */
2556 char *
2557 be_default_policy(void)
2558 {
2559 	return (BE_PLCY_STATIC);
2560 }
2561 
2562 /*
2563  * Function:	valid_be_policy
2564  * Description:	Temporary hardcoded policy support.  This function valids
2565  *		whether a policy is a valid known policy or not.
2566  * Paramters:
2567  *		policy - name of policy to validate.
2568  * Returns:
2569  *		B_TRUE - policy is a valid.
2570  *		B_FALSE - policy is invalid.
2571  * Scope:
2572  *		Semi-private (library wide use only)
2573  */
2574 boolean_t
2575 valid_be_policy(char *policy)
2576 {
2577 	if (policy == NULL)
2578 		return (B_FALSE);
2579 
2580 	if (strcmp(policy, BE_PLCY_STATIC) == 0 ||
2581 	    strcmp(policy, BE_PLCY_VOLATILE) == 0) {
2582 		return (B_TRUE);
2583 	}
2584 
2585 	return (B_FALSE);
2586 }
2587 
2588 /*
2589  * Function:	be_print_err
2590  * Description:	This function prints out error messages if do_print is
2591  *		set to B_TRUE or if the BE_PRINT_ERR environment variable
2592  *		is set to true.
2593  * Paramters:
2594  *		prnt_str - the string we wish to print and any arguments
2595  *		for the format of that string.
2596  * Returns:
2597  *		void
2598  * Scope:
2599  *		Semi-private (library wide use only)
2600  */
2601 void
2602 be_print_err(char *prnt_str, ...)
2603 {
2604 	va_list ap;
2605 	char buf[BUFSIZ];
2606 	char *env_buf;
2607 	static boolean_t env_checked = B_FALSE;
2608 
2609 	if (!env_checked) {
2610 		if ((env_buf = getenv("BE_PRINT_ERR")) != NULL) {
2611 			if (strcasecmp(env_buf, "true") == 0) {
2612 				do_print = B_TRUE;
2613 			}
2614 		}
2615 		env_checked = B_TRUE;
2616 	}
2617 
2618 	if (do_print) {
2619 		va_start(ap, prnt_str);
2620 		/* LINTED variable format specifier */
2621 		(void) vsnprintf(buf, BUFSIZ, prnt_str, ap);
2622 		(void) fputs(buf, stderr);
2623 		va_end(ap);
2624 	}
2625 }
2626 
2627 /*
2628  * Function:	be_find_current_be
2629  * Description:	Find the currently "active" BE. Fill in the
2630  * 		passed in be_transaction_data_t reference with the
2631  *		active BE's data.
2632  * Paramters:
2633  *		none
2634  * Returns:
2635  *		BE_SUCCESS - Success
2636  *		be_errnot_t - Failure
2637  * Scope:
2638  *		Semi-private (library wide use only)
2639  * Notes:
2640  *		The caller is responsible for initializing the libzfs handle
2641  *		and freeing the memory used by the active be_name.
2642  */
2643 int
2644 be_find_current_be(be_transaction_data_t *bt)
2645 {
2646 	int	zret;
2647 
2648 	if ((zret = zpool_iter(g_zfs, be_zpool_find_current_be_callback,
2649 	    bt)) == 0) {
2650 		be_print_err(gettext("be_find_current_be: failed to "
2651 		    "find current BE name\n"));
2652 		return (BE_ERR_BE_NOENT);
2653 	} else if (zret < 0) {
2654 		be_print_err(gettext("be_find_current_be: "
2655 		    "zpool_iter failed: %s\n"),
2656 		    libzfs_error_description(g_zfs));
2657 		return (zfs_err_to_be_err(g_zfs));
2658 	}
2659 
2660 	return (BE_SUCCESS);
2661 }
2662 
2663 /*
2664  * Function:	be_zpool_find_current_be_callback
2665  * Description: Callback function used to iterate through all existing pools
2666  *		to find the BE that is the currently booted BE.
2667  * Parameters:
2668  *		zlp - zpool_handle_t pointer to the current pool being
2669  *			looked at.
2670  *		data - be_transaction_data_t pointer.
2671  *			Upon successfully finding the current BE, the
2672  *			obe_zpool member of this parameter is set to the
2673  *			pool it is found in.
2674  * Return:
2675  *		1 - Found current BE in this pool.
2676  *		0 - Did not find current BE in this pool.
2677  * Scope:
2678  *		Semi-private (library wide use only)
2679  */
2680 int
2681 be_zpool_find_current_be_callback(zpool_handle_t *zlp, void *data)
2682 {
2683 	be_transaction_data_t	*bt = data;
2684 	zfs_handle_t		*zhp = NULL;
2685 	const char		*zpool =  zpool_get_name(zlp);
2686 	char			be_container_ds[MAXPATHLEN];
2687 	char			*zpath = NULL;
2688 
2689 	/*
2690 	 * Generate string for BE container dataset
2691 	 */
2692 	if (getzoneid() != GLOBAL_ZONEID) {
2693 		if ((zpath = be_get_ds_from_dir("/")) != NULL) {
2694 			(void) strlcpy(be_container_ds, dirname(zpath),
2695 			    sizeof (be_container_ds));
2696 		} else {
2697 			be_print_err(gettext(
2698 			    "be_zpool_find_current_be_callback: "
2699 			    "zone root dataset is not mounted\n"));
2700 			return (0);
2701 		}
2702 	} else {
2703 		be_make_container_ds(zpool, be_container_ds,
2704 		    sizeof (be_container_ds));
2705 	}
2706 
2707 	/*
2708 	 * Check if a BE container dataset exists in this pool.
2709 	 */
2710 	if (!zfs_dataset_exists(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) {
2711 		zpool_close(zlp);
2712 		return (0);
2713 	}
2714 
2715 	/*
2716 	 * Get handle to this zpool's BE container dataset.
2717 	 */
2718 	if ((zhp = zfs_open(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) ==
2719 	    NULL) {
2720 		be_print_err(gettext("be_zpool_find_current_be_callback: "
2721 		    "failed to open BE container dataset (%s)\n"),
2722 		    be_container_ds);
2723 		zpool_close(zlp);
2724 		return (0);
2725 	}
2726 
2727 	/*
2728 	 * Iterate through all potential BEs in this zpool
2729 	 */
2730 	if (zfs_iter_filesystems(zhp, be_zfs_find_current_be_callback, bt)) {
2731 		/*
2732 		 * Found current BE dataset; set obe_zpool
2733 		 */
2734 		if ((bt->obe_zpool = strdup(zpool)) == NULL) {
2735 			be_print_err(gettext(
2736 			    "be_zpool_find_current_be_callback: "
2737 			    "memory allocation failed\n"));
2738 			ZFS_CLOSE(zhp);
2739 			zpool_close(zlp);
2740 			return (0);
2741 		}
2742 
2743 		ZFS_CLOSE(zhp);
2744 		zpool_close(zlp);
2745 		return (1);
2746 	}
2747 
2748 	ZFS_CLOSE(zhp);
2749 	zpool_close(zlp);
2750 
2751 	return (0);
2752 }
2753 
2754 /*
2755  * Function:	be_zfs_find_current_be_callback
2756  * Description:	Callback function used to iterate through all BEs in a
2757  *		pool to find the BE that is the currently booted BE.
2758  * Parameters:
2759  *		zhp - zfs_handle_t pointer to current filesystem being checked.
2760  *		data - be_transaction-data_t pointer
2761  *			Upon successfully finding the current BE, the
2762  *			obe_name and obe_root_ds members of this parameter
2763  *			are set to the BE name and BE's root dataset
2764  *			respectively.
2765  * Return:
2766  *		1 - Found current BE.
2767  *		0 - Did not find current BE.
2768  * Scope:
2769  *		Semi-private (library wide use only)
2770  */
2771 int
2772 be_zfs_find_current_be_callback(zfs_handle_t *zhp, void *data)
2773 {
2774 	be_transaction_data_t	*bt = data;
2775 	char			*mp = NULL;
2776 
2777 	/*
2778 	 * Check if dataset is mounted, and if so where.
2779 	 */
2780 	if (zfs_is_mounted(zhp, &mp)) {
2781 		/*
2782 		 * If mounted at root, set obe_root_ds and obe_name
2783 		 */
2784 		if (mp != NULL && strcmp(mp, "/") == 0) {
2785 			free(mp);
2786 
2787 			if ((bt->obe_root_ds = strdup(zfs_get_name(zhp)))
2788 			    == NULL) {
2789 				be_print_err(gettext(
2790 				    "be_zfs_find_current_be_callback: "
2791 				    "memory allocation failed\n"));
2792 				ZFS_CLOSE(zhp);
2793 				return (0);
2794 			}
2795 
2796 			if ((bt->obe_name = strdup(basename(bt->obe_root_ds)))
2797 			    == NULL) {
2798 				be_print_err(gettext(
2799 				    "be_zfs_find_current_be_callback: "
2800 				    "memory allocation failed\n"));
2801 				ZFS_CLOSE(zhp);
2802 				return (0);
2803 			}
2804 
2805 			ZFS_CLOSE(zhp);
2806 			return (1);
2807 		}
2808 
2809 		free(mp);
2810 	}
2811 	ZFS_CLOSE(zhp);
2812 
2813 	return (0);
2814 }
2815 
2816 /*
2817  * Function:	be_check_be_roots_callback
2818  * Description:	This function checks whether or not the dataset name passed
2819  *		is hierachically located under the BE root container dataset
2820  *		for this pool.
2821  * Parameters:
2822  *		zlp - zpool_handle_t pointer to current pool being processed.
2823  *		data - name of dataset to check
2824  * Returns:
2825  *		0 - dataset is not in this pool's BE root container dataset
2826  *		1 - dataset is in this pool's BE root container dataset
2827  * Scope:
2828  *		Semi-private (library wide use only)
2829  */
2830 int
2831 be_check_be_roots_callback(zpool_handle_t *zlp, void *data)
2832 {
2833 	const char	*zpool = zpool_get_name(zlp);
2834 	char		*ds = data;
2835 	char		be_container_ds[MAXPATHLEN];
2836 
2837 	/* Generate string for this pool's BE root container dataset */
2838 	be_make_container_ds(zpool, be_container_ds, sizeof (be_container_ds));
2839 
2840 	/*
2841 	 * If dataset lives under the BE root container dataset
2842 	 * of this pool, return failure.
2843 	 */
2844 	if (strncmp(be_container_ds, ds, strlen(be_container_ds)) == 0 &&
2845 	    ds[strlen(be_container_ds)] == '/') {
2846 		zpool_close(zlp);
2847 		return (1);
2848 	}
2849 
2850 	zpool_close(zlp);
2851 	return (0);
2852 }
2853 
2854 /*
2855  * Function:	zfs_err_to_be_err
2856  * Description:	This function takes the error stored in the libzfs handle
2857  *		and maps it to an be_errno_t. If there are no matching
2858  *		be_errno_t's then BE_ERR_ZFS is returned.
2859  * Paramters:
2860  *		zfsh - The libzfs handle containing the error we're looking up.
2861  * Returns:
2862  *		be_errno_t
2863  * Scope:
2864  *		Semi-private (library wide use only)
2865  */
2866 int
2867 zfs_err_to_be_err(libzfs_handle_t *zfsh)
2868 {
2869 	int err = libzfs_errno(zfsh);
2870 
2871 	switch (err) {
2872 	case 0:
2873 		return (BE_SUCCESS);
2874 	case EZFS_PERM:
2875 		return (BE_ERR_PERM);
2876 	case EZFS_INTR:
2877 		return (BE_ERR_INTR);
2878 	case EZFS_NOENT:
2879 		return (BE_ERR_NOENT);
2880 	case EZFS_NOSPC:
2881 		return (BE_ERR_NOSPC);
2882 	case EZFS_MOUNTFAILED:
2883 		return (BE_ERR_MOUNT);
2884 	case EZFS_UMOUNTFAILED:
2885 		return (BE_ERR_UMOUNT);
2886 	case EZFS_EXISTS:
2887 		return (BE_ERR_BE_EXISTS);
2888 	case EZFS_BUSY:
2889 		return (BE_ERR_DEV_BUSY);
2890 	case EZFS_POOLREADONLY:
2891 		return (BE_ERR_ROFS);
2892 	case EZFS_NAMETOOLONG:
2893 		return (BE_ERR_NAMETOOLONG);
2894 	case EZFS_NODEVICE:
2895 		return (BE_ERR_NODEV);
2896 	case EZFS_POOL_INVALARG:
2897 		return (BE_ERR_INVAL);
2898 	case EZFS_PROPTYPE:
2899 		return (BE_ERR_INVALPROP);
2900 	case EZFS_BADTYPE:
2901 		return (BE_ERR_DSTYPE);
2902 	case EZFS_PROPNONINHERIT:
2903 		return (BE_ERR_NONINHERIT);
2904 	case EZFS_PROPREADONLY:
2905 		return (BE_ERR_READONLYPROP);
2906 	case EZFS_RESILVERING:
2907 	case EZFS_POOLUNAVAIL:
2908 		return (BE_ERR_UNAVAIL);
2909 	case EZFS_DSREADONLY:
2910 		return (BE_ERR_READONLYDS);
2911 	default:
2912 		return (BE_ERR_ZFS);
2913 	}
2914 }
2915 
2916 /*
2917  * Function:	errno_to_be_err
2918  * Description:	This function takes an errno and maps it to an be_errno_t.
2919  *		If there are no matching be_errno_t's then BE_ERR_UNKNOWN is
2920  *		returned.
2921  * Paramters:
2922  *		err - The errno we're compairing against.
2923  * Returns:
2924  *		be_errno_t
2925  * Scope:
2926  *		Semi-private (library wide use only)
2927  */
2928 int
2929 errno_to_be_err(int err)
2930 {
2931 	switch (err) {
2932 	case EPERM:
2933 		return (BE_ERR_PERM);
2934 	case EACCES:
2935 		return (BE_ERR_ACCESS);
2936 	case ECANCELED:
2937 		return (BE_ERR_CANCELED);
2938 	case EINTR:
2939 		return (BE_ERR_INTR);
2940 	case ENOENT:
2941 		return (BE_ERR_NOENT);
2942 	case ENOSPC:
2943 	case EDQUOT:
2944 		return (BE_ERR_NOSPC);
2945 	case EEXIST:
2946 		return (BE_ERR_BE_EXISTS);
2947 	case EBUSY:
2948 		return (BE_ERR_BUSY);
2949 	case EROFS:
2950 		return (BE_ERR_ROFS);
2951 	case ENAMETOOLONG:
2952 		return (BE_ERR_NAMETOOLONG);
2953 	case ENXIO:
2954 		return (BE_ERR_NXIO);
2955 	case EINVAL:
2956 		return (BE_ERR_INVAL);
2957 	case EFAULT:
2958 		return (BE_ERR_FAULT);
2959 	default:
2960 		return (BE_ERR_UNKNOWN);
2961 	}
2962 }
2963 
2964 /*
2965  * Function:	be_err_to_str
2966  * Description:	This function takes a be_errno_t and maps it to a message.
2967  *		If there are no matching be_errno_t's then NULL is returned.
2968  * Paramters:
2969  *		be_errno_t - The be_errno_t we're mapping.
2970  * Returns:
2971  *		string or NULL if the error code is not known.
2972  * Scope:
2973  *		Semi-private (library wide use only)
2974  */
2975 char *
2976 be_err_to_str(int err)
2977 {
2978 	switch (err) {
2979 	case BE_ERR_ACCESS:
2980 		return (gettext("Permission denied."));
2981 	case BE_ERR_ACTIVATE_CURR:
2982 		return (gettext("Activation of current BE failed."));
2983 	case BE_ERR_AUTONAME:
2984 		return (gettext("Auto naming failed."));
2985 	case BE_ERR_BE_NOENT:
2986 		return (gettext("No such BE."));
2987 	case BE_ERR_BUSY:
2988 		return (gettext("Mount busy."));
2989 	case BE_ERR_DEV_BUSY:
2990 		return (gettext("Device busy."));
2991 	case BE_ERR_CANCELED:
2992 		return (gettext("Operation canceled."));
2993 	case BE_ERR_CLONE:
2994 		return (gettext("BE clone failed."));
2995 	case BE_ERR_COPY:
2996 		return (gettext("BE copy failed."));
2997 	case BE_ERR_CREATDS:
2998 		return (gettext("Dataset creation failed."));
2999 	case BE_ERR_CURR_BE_NOT_FOUND:
3000 		return (gettext("Can't find current BE."));
3001 	case BE_ERR_DESTROY:
3002 		return (gettext("Failed to destroy BE or snapshot."));
3003 	case BE_ERR_DESTROY_CURR_BE:
3004 		return (gettext("Cannot destroy current BE."));
3005 	case BE_ERR_DEMOTE:
3006 		return (gettext("BE demotion failed."));
3007 	case BE_ERR_DSTYPE:
3008 		return (gettext("Invalid dataset type."));
3009 	case BE_ERR_BE_EXISTS:
3010 		return (gettext("BE exists."));
3011 	case BE_ERR_INIT:
3012 		return (gettext("be_zfs_init failed."));
3013 	case BE_ERR_INTR:
3014 		return (gettext("Interupted system call."));
3015 	case BE_ERR_INVAL:
3016 		return (gettext("Invalid argument."));
3017 	case BE_ERR_INVALPROP:
3018 		return (gettext("Invalid property for dataset."));
3019 	case BE_ERR_INVALMOUNTPOINT:
3020 		return (gettext("Unexpected mountpoint."));
3021 	case BE_ERR_MOUNT:
3022 		return (gettext("Mount failed."));
3023 	case BE_ERR_MOUNTED:
3024 		return (gettext("Already mounted."));
3025 	case BE_ERR_NAMETOOLONG:
3026 		return (gettext("name > BUFSIZ."));
3027 	case BE_ERR_NOENT:
3028 		return (gettext("Doesn't exist."));
3029 	case BE_ERR_POOL_NOENT:
3030 		return (gettext("No such pool."));
3031 	case BE_ERR_NODEV:
3032 		return (gettext("No such device."));
3033 	case BE_ERR_NOTMOUNTED:
3034 		return (gettext("File system not mounted."));
3035 	case BE_ERR_NOMEM:
3036 		return (gettext("Not enough memory."));
3037 	case BE_ERR_NONINHERIT:
3038 		return (gettext(
3039 		    "Property is not inheritable for the BE dataset."));
3040 	case BE_ERR_NXIO:
3041 		return (gettext("No such device or address."));
3042 	case BE_ERR_NOSPC:
3043 		return (gettext("No space on device."));
3044 	case BE_ERR_NOTSUP:
3045 		return (gettext("Operation not supported."));
3046 	case BE_ERR_OPEN:
3047 		return (gettext("Open failed."));
3048 	case BE_ERR_PERM:
3049 		return (gettext("Not owner."));
3050 	case BE_ERR_UNAVAIL:
3051 		return (gettext("The BE is currently unavailable."));
3052 	case BE_ERR_PROMOTE:
3053 		return (gettext("BE promotion failed."));
3054 	case BE_ERR_ROFS:
3055 		return (gettext("Read only file system."));
3056 	case BE_ERR_READONLYDS:
3057 		return (gettext("Read only dataset."));
3058 	case BE_ERR_READONLYPROP:
3059 		return (gettext("Read only property."));
3060 	case BE_ERR_RENAME_ACTIVE:
3061 		return (gettext("Renaming the active BE is not supported."));
3062 	case BE_ERR_SS_EXISTS:
3063 		return (gettext("Snapshot exists."));
3064 	case BE_ERR_SS_NOENT:
3065 		return (gettext("No such snapshot."));
3066 	case BE_ERR_UMOUNT:
3067 		return (gettext("Unmount failed."));
3068 	case BE_ERR_UMOUNT_CURR_BE:
3069 		return (gettext("Can't unmount the current BE."));
3070 	case BE_ERR_UMOUNT_SHARED:
3071 		return (gettext("Unmount of a shared File System failed."));
3072 	case BE_ERR_FAULT:
3073 		return (gettext("Bad address."));
3074 	case BE_ERR_UNKNOWN:
3075 		return (gettext("Unknown error."));
3076 	case BE_ERR_ZFS:
3077 		return (gettext("ZFS returned an error."));
3078 	case BE_ERR_GEN_UUID:
3079 		return (gettext("Failed to generate uuid."));
3080 	case BE_ERR_PARSE_UUID:
3081 		return (gettext("Failed to parse uuid."));
3082 	case BE_ERR_NO_UUID:
3083 		return (gettext("No uuid"));
3084 	case BE_ERR_ZONE_NO_PARENTBE:
3085 		return (gettext("No parent uuid"));
3086 	case BE_ERR_ZONE_MULTIPLE_ACTIVE:
3087 		return (gettext("Multiple active zone roots"));
3088 	case BE_ERR_ZONE_NO_ACTIVE_ROOT:
3089 		return (gettext("No active zone root"));
3090 	case BE_ERR_ZONE_ROOT_NOT_LEGACY:
3091 		return (gettext("Zone root not legacy"));
3092 	case BE_ERR_MOUNT_ZONEROOT:
3093 		return (gettext("Failed to mount a zone root."));
3094 	case BE_ERR_UMOUNT_ZONEROOT:
3095 		return (gettext("Failed to unmount a zone root."));
3096 	case BE_ERR_NO_MOUNTED_ZONE:
3097 		return (gettext("Zone is not mounted"));
3098 	case BE_ERR_ZONES_UNMOUNT:
3099 		return (gettext("Unable to unmount a zone BE."));
3100 	case BE_ERR_NO_MENU:
3101 		return (gettext("Missing boot menu file."));
3102 	case BE_ERR_BAD_MENU_PATH:
3103 		return (gettext("Invalid path for menu.lst file"));
3104 	case BE_ERR_ZONE_SS_EXISTS:
3105 		return (gettext("Zone snapshot exists."));
3106 	case BE_ERR_BOOTFILE_INST:
3107 		return (gettext("Error installing boot files."));
3108 	case BE_ERR_EXTCMD:
3109 		return (gettext("Error running an external command."));
3110 	default:
3111 		return (NULL);
3112 	}
3113 }
3114 
3115 /*
3116  * Function:    be_has_grub
3117  * Description: Boolean function indicating whether the current system
3118  *		uses grub.
3119  * Return:      B_FALSE - the system does not have grub
3120  *              B_TRUE - the system does have grub.
3121  * Scope:
3122  *		Semi-private (library wide use only)
3123  */
3124 boolean_t
3125 be_has_grub(void)
3126 {
3127 	static struct be_defaults be_defaults;
3128 	static boolean_t be_deflts_set = B_FALSE;
3129 
3130 	/* Cache the defaults, because be_has_grub is used often. */
3131 	if (be_deflts_set == B_FALSE) {
3132 		be_get_defaults(&be_defaults);
3133 		be_deflts_set = B_TRUE;
3134 	}
3135 
3136 	return (be_defaults.be_deflt_grub);
3137 }
3138 
3139 /*
3140  * Function:    be_is_isa
3141  * Description: Boolean function indicating whether the instruction set
3142  *              architecture of the executing system matches the name provided.
3143  *              The string must match a system defined architecture (e.g.
3144  *              "i386", "sparc") and is case sensitive.
3145  * Parameters:  name - string representing the name of instruction set
3146  *			architecture being tested
3147  * Returns:     B_FALSE - the system instruction set architecture is different
3148  *			from the one specified
3149  *              B_TRUE - the system instruction set architecture is the same
3150  *			as the one specified
3151  * Scope:
3152  *		Semi-private (library wide use only)
3153  */
3154 boolean_t
3155 be_is_isa(char *name)
3156 {
3157 	return ((strcmp((char *)be_get_default_isa(), name) == 0));
3158 }
3159 
3160 /*
3161  * Function: be_get_default_isa
3162  * Description:
3163  *      Returns the default instruction set architecture of the
3164  *      machine it is executed on. (eg. sparc, i386, ...)
3165  *      NOTE:   SYS_INST environment variable may override default
3166  *              return value
3167  * Parameters:
3168  *		none
3169  * Returns:
3170  *		NULL - the architecture returned by sysinfo() was too
3171  *			long for local variables
3172  *		char * - pointer to a string containing the default
3173  *			implementation
3174  * Scope:
3175  *		Semi-private (library wide use only)
3176  */
3177 char *
3178 be_get_default_isa(void)
3179 {
3180 	int	i;
3181 	char	*envp;
3182 	static char	default_inst[ARCH_LENGTH] = "";
3183 
3184 	if (default_inst[0] == '\0') {
3185 		if ((envp = getenv("SYS_INST")) != NULL) {
3186 			if ((int)strlen(envp) >= ARCH_LENGTH)
3187 				return (NULL);
3188 			else
3189 				(void) strcpy(default_inst, envp);
3190 		} else  {
3191 			i = sysinfo(SI_ARCHITECTURE, default_inst, ARCH_LENGTH);
3192 			if (i < 0 || i > ARCH_LENGTH)
3193 				return (NULL);
3194 		}
3195 	}
3196 	return (default_inst);
3197 }
3198 
3199 /*
3200  * Function: be_get_platform
3201  * Description:
3202  *      Returns the platfom name
3203  * Parameters:
3204  *		none
3205  * Returns:
3206  *		NULL - the platform name returned by sysinfo() was too
3207  *			long for local variables
3208  *		char * - pointer to a string containing the platform name
3209  * Scope:
3210  *		Semi-private (library wide use only)
3211  */
3212 char *
3213 be_get_platform(void)
3214 {
3215 	int	i;
3216 	static char	default_inst[ARCH_LENGTH] = "";
3217 
3218 	if (default_inst[0] == '\0') {
3219 		i = sysinfo(SI_PLATFORM, default_inst, ARCH_LENGTH);
3220 		if (i < 0 || i > ARCH_LENGTH)
3221 			return (NULL);
3222 	}
3223 	return (default_inst);
3224 }
3225 
3226 /*
3227  * Function: be_run_cmd
3228  * Description:
3229  *	Runs a command in a separate subprocess.  Splits out stdout from stderr
3230  *	and sends each to its own buffer.  Buffers must be pre-allocated and
3231  *	passed in as arguments.  Buffer sizes are also passed in as arguments.
3232  *
3233  *	Notes / caveats:
3234  *	- Command being run is assumed to not have any stdout or stderr
3235  *		redirection.
3236  *	- Commands which emit total stderr output of greater than PIPE_BUF
3237  *		bytes can hang.  For such commands, a different implementation
3238  *		which uses poll(2) must be used.
3239  *	- stdout_buf can be NULL.  In this case, stdout_bufsize is ignored, and
3240  *		the stream which would have gone to it is sent to the bit
3241  *		bucket.
3242  *	- stderr_buf cannot be NULL.
3243  *	- Only subprocess errors are appended to the stderr_buf.  Errors
3244  *		running the command are reported through be_print_err().
3245  *	- Data which would overflow its respective buffer is sent to the bit
3246  *		bucket.
3247  *
3248  * Parameters:
3249  *		command: command to run.  Assumed not to have embedded stdout
3250  *			or stderr redirection.  May have stdin redirection,
3251  *			however.
3252  *		stderr_buf: buffer returning subprocess stderr data.  Errors
3253  *			reported by this function are reported through
3254  *			be_print_err().
3255  *		stderr_bufsize: size of stderr_buf
3256  *		stdout_buf: buffer returning subprocess stdout data.
3257  *		stdout_bufsize: size of stdout_buf
3258  * Returns:
3259  *		BE_SUCCESS - The command ran successfully without returning
3260  *			errors.
3261  *		BE_ERR_EXTCMD
3262  *			- The command could not be run.
3263  *			- The command terminated with error status.
3264  *			- There were errors extracting or returning subprocess
3265  *				data.
3266  *		BE_ERR_NOMEM - The command exceeds the command buffer size.
3267  *		BE_ERR_INVAL - An invalid argument was specified.
3268  * Scope:
3269  *		Semi-private (library wide use only)
3270  */
3271 int
3272 be_run_cmd(char *command, char *stderr_buf, int stderr_bufsize,
3273     char *stdout_buf, int stdout_bufsize)
3274 {
3275 	char *temp_filename = strdup(tmpnam(NULL));
3276 	FILE *stdout_str = NULL;
3277 	FILE *stderr_str = NULL;
3278 	char cmdline[BUFSIZ];
3279 	char oneline[BUFSIZ];
3280 	int exit_status;
3281 	int rval = BE_SUCCESS;
3282 
3283 	if ((command == NULL) || (stderr_buf == NULL) ||
3284 	    (stderr_bufsize <= 0) || (stdout_bufsize <  0) ||
3285 	    ((stdout_buf != NULL) ^ (stdout_bufsize != 0))) {
3286 		return (BE_ERR_INVAL);
3287 	}
3288 
3289 	/* Set up command so popen returns stderr, not stdout */
3290 	if (snprintf(cmdline, BUFSIZ, "%s 2> %s", command,
3291 	    temp_filename) >= BUFSIZ) {
3292 		rval = BE_ERR_NOMEM;
3293 		goto cleanup;
3294 	}
3295 
3296 	/* Set up the fifo that will make stderr available. */
3297 	if (mkfifo(temp_filename, 0600) != 0) {
3298 		(void) be_print_err(gettext("be_run_cmd: mkfifo: %s\n"),
3299 		    strerror(errno));
3300 		rval = BE_ERR_EXTCMD;
3301 		goto cleanup;
3302 	}
3303 
3304 	if ((stdout_str = popen(cmdline, "r")) == NULL) {
3305 		(void) be_print_err(gettext("be_run_cmd: popen: %s\n"),
3306 		    strerror(errno));
3307 		rval = BE_ERR_EXTCMD;
3308 		goto cleanup;
3309 	}
3310 
3311 	if ((stderr_str = fopen(temp_filename, "r")) == NULL) {
3312 		(void) be_print_err(gettext("be_run_cmd: fopen: %s\n"),
3313 		    strerror(errno));
3314 		(void) pclose(stdout_str);
3315 		rval = BE_ERR_EXTCMD;
3316 		goto cleanup;
3317 	}
3318 
3319 	/* Read stdout first, as it usually outputs more than stderr. */
3320 	oneline[BUFSIZ-1] = '\0';
3321 	while (fgets(oneline, BUFSIZ-1, stdout_str) != NULL) {
3322 		if (stdout_str != NULL) {
3323 			(void) strlcat(stdout_buf, oneline, stdout_bufsize);
3324 		}
3325 	}
3326 
3327 	while (fgets(oneline, BUFSIZ-1, stderr_str) != NULL) {
3328 		(void) strlcat(stderr_buf, oneline, stderr_bufsize);
3329 	}
3330 
3331 	/* Close pipe, get exit status. */
3332 	if ((exit_status = pclose(stdout_str)) == -1) {
3333 		(void) be_print_err(gettext("be_run_cmd: pclose: %s\n"),
3334 		    strerror(errno));
3335 		rval = BE_ERR_EXTCMD;
3336 	} else if (WIFEXITED(exit_status)) {
3337 		exit_status = (int)((char)WEXITSTATUS(exit_status));
3338 		/*
3339 		 * error code BC_NOUPDT means more recent version
3340 		 * is installed
3341 		 */
3342 		if (exit_status != BC_SUCCESS && exit_status != BC_NOUPDT) {
3343 			(void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: "
3344 			    "command terminated with error status: %d\n"),
3345 			    exit_status);
3346 			(void) strlcat(stderr_buf, oneline, stderr_bufsize);
3347 			rval = BE_ERR_EXTCMD;
3348 		}
3349 	} else {
3350 		(void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: command "
3351 		    "terminated on signal: %s\n"),
3352 		    strsignal(WTERMSIG(exit_status)));
3353 		(void) strlcat(stderr_buf, oneline, stderr_bufsize);
3354 		rval = BE_ERR_EXTCMD;
3355 	}
3356 
3357 cleanup:
3358 	(void) unlink(temp_filename);
3359 	(void) free(temp_filename);
3360 
3361 	return (rval);
3362 }
3363 
3364 /* ********************************************************************	*/
3365 /*			Private Functions				*/
3366 /* ******************************************************************** */
3367 
3368 /*
3369  * Function:	update_dataset
3370  * Description:	This function takes a dataset name and replaces the zpool
3371  *		and be_name components of the dataset with the new be_name
3372  *		zpool passed in.
3373  * Parameters:
3374  *		dataset - name of dataset
3375  *		dataset_len - lenth of buffer in which dataset is passed in.
3376  *		be_name - name of new BE name to update to.
3377  *		old_rc_loc - dataset under which the root container dataset
3378  *			for the old BE lives.
3379  *		new_rc_loc - dataset under which the root container dataset
3380  *			for the new BE lives.
3381  * Returns:
3382  *		BE_SUCCESS - Success
3383  *		be_errno_t - Failure
3384  * Scope:
3385  *		Private
3386  */
3387 static int
3388 update_dataset(char *dataset, int dataset_len, char *be_name,
3389     char *old_rc_loc, char *new_rc_loc)
3390 {
3391 	char	*ds = NULL;
3392 	char	*sub_ds = NULL;
3393 
3394 	/* Tear off the BE container dataset */
3395 	if ((ds = be_make_name_from_ds(dataset, old_rc_loc)) == NULL) {
3396 		return (BE_ERR_INVAL);
3397 	}
3398 
3399 	/* Get dataset name relative to BE root, if there is one */
3400 	sub_ds = strchr(ds, '/');
3401 
3402 	/* Generate the BE root dataset name */
3403 	be_make_root_ds(new_rc_loc, be_name, dataset, dataset_len);
3404 
3405 	/* If a subordinate dataset name was found, append it */
3406 	if (sub_ds != NULL)
3407 		(void) strlcat(dataset, sub_ds, dataset_len);
3408 
3409 	free(ds);
3410 	return (BE_SUCCESS);
3411 }
3412 
3413 /*
3414  * Function:	_update_vfstab
3415  * Description:	This function updates a vfstab file to reflect the new
3416  *		root container dataset location and be_name for all
3417  *		entries listed in the be_fs_list_data_t structure passed in.
3418  * Parameters:
3419  *		vfstab - vfstab file to modify
3420  *		be_name - name of BE to update.
3421  *		old_rc_loc - dataset under which the root container dataset
3422  *			of the old BE resides in.
3423  *		new_rc_loc - dataset under which the root container dataset
3424  *			of the new BE resides in.
3425  *		fld - be_fs_list_data_t pointer providing the list of
3426  *			file systems to look for in vfstab.
3427  * Returns:
3428  *		BE_SUCCESS - Success
3429  *		be_errno_t - Failure
3430  * Scope:
3431  *		Private
3432  */
3433 static int
3434 _update_vfstab(char *vfstab, char *be_name, char *old_rc_loc,
3435     char *new_rc_loc, be_fs_list_data_t *fld)
3436 {
3437 	struct vfstab	vp;
3438 	char		*tmp_vfstab = NULL;
3439 	char		comments_buf[BUFSIZ];
3440 	FILE		*comments = NULL;
3441 	FILE		*vfs_ents = NULL;
3442 	FILE		*tfile = NULL;
3443 	struct stat	sb;
3444 	char		dev[MAXPATHLEN];
3445 	char		*c;
3446 	int		fd;
3447 	int		ret = BE_SUCCESS, err = 0;
3448 	int		i;
3449 	int		tmp_vfstab_len = 0;
3450 
3451 	errno = 0;
3452 
3453 	/*
3454 	 * Open vfstab for reading twice.  First is for comments,
3455 	 * second is for actual entries.
3456 	 */
3457 	if ((comments = fopen(vfstab, "r")) == NULL ||
3458 	    (vfs_ents = fopen(vfstab, "r")) == NULL) {
3459 		err = errno;
3460 		be_print_err(gettext("_update_vfstab: "
3461 		    "failed to open vfstab (%s): %s\n"), vfstab,
3462 		    strerror(err));
3463 		ret = errno_to_be_err(err);
3464 		goto cleanup;
3465 	}
3466 
3467 	/* Grab the stats of the original vfstab file */
3468 	if (stat(vfstab, &sb) != 0) {
3469 		err = errno;
3470 		be_print_err(gettext("_update_vfstab: "
3471 		    "failed to stat file %s: %s\n"), vfstab,
3472 		    strerror(err));
3473 		ret = errno_to_be_err(err);
3474 		goto cleanup;
3475 	}
3476 
3477 	/* Create tmp file for modified vfstab */
3478 	if ((tmp_vfstab = (char *)malloc(strlen(vfstab) + 7))
3479 	    == NULL) {
3480 		be_print_err(gettext("_update_vfstab: "
3481 		    "malloc failed\n"));
3482 		ret = BE_ERR_NOMEM;
3483 		goto cleanup;
3484 	}
3485 	tmp_vfstab_len = strlen(vfstab) + 7;
3486 	(void) memset(tmp_vfstab, 0, tmp_vfstab_len);
3487 	(void) strlcpy(tmp_vfstab, vfstab, tmp_vfstab_len);
3488 	(void) strlcat(tmp_vfstab, "XXXXXX", tmp_vfstab_len);
3489 	if ((fd = mkstemp(tmp_vfstab)) == -1) {
3490 		err = errno;
3491 		be_print_err(gettext("_update_vfstab: "
3492 		    "mkstemp failed: %s\n"), strerror(err));
3493 		ret = errno_to_be_err(err);
3494 		goto cleanup;
3495 	}
3496 	if ((tfile = fdopen(fd, "w")) == NULL) {
3497 		err = errno;
3498 		be_print_err(gettext("_update_vfstab: "
3499 		    "could not open file for write\n"));
3500 		(void) close(fd);
3501 		ret = errno_to_be_err(err);
3502 		goto cleanup;
3503 	}
3504 
3505 	while (fgets(comments_buf, BUFSIZ, comments)) {
3506 		for (c = comments_buf; *c != '\0' && isspace(*c); c++)
3507 			;
3508 		if (*c == '\0') {
3509 			continue;
3510 		} else if (*c == '#') {
3511 			/*
3512 			 * If line is a comment line, just put
3513 			 * it through to the tmp vfstab.
3514 			 */
3515 			(void) fputs(comments_buf, tfile);
3516 		} else {
3517 			/*
3518 			 * Else line is a vfstab entry, grab it
3519 			 * into a vfstab struct.
3520 			 */
3521 			if (getvfsent(vfs_ents, &vp) != 0) {
3522 				err = errno;
3523 				be_print_err(gettext("_update_vfstab: "
3524 				    "getvfsent failed: %s\n"), strerror(err));
3525 				ret = errno_to_be_err(err);
3526 				goto cleanup;
3527 			}
3528 
3529 			if (vp.vfs_special == NULL || vp.vfs_mountp == NULL) {
3530 				(void) putvfsent(tfile, &vp);
3531 				continue;
3532 			}
3533 
3534 			/*
3535 			 * If the entry is one of the entries in the list
3536 			 * of file systems to update, modify it's device
3537 			 * field to be correct for this BE.
3538 			 */
3539 			for (i = 0; i < fld->fs_num; i++) {
3540 				if (strcmp(vp.vfs_special, fld->fs_list[i])
3541 				    == 0) {
3542 					/*
3543 					 * Found entry that needs an update.
3544 					 * Replace the root container dataset
3545 					 * location and be_name in the
3546 					 * entry's device.
3547 					 */
3548 					(void) strlcpy(dev, vp.vfs_special,
3549 					    sizeof (dev));
3550 
3551 					if ((ret = update_dataset(dev,
3552 					    sizeof (dev), be_name, old_rc_loc,
3553 					    new_rc_loc)) != 0) {
3554 						be_print_err(
3555 						    gettext("_update_vfstab: "
3556 						    "Failed to update device "
3557 						    "field for vfstab entry "
3558 						    "%s\n"), fld->fs_list[i]);
3559 						goto cleanup;
3560 					}
3561 
3562 					vp.vfs_special = dev;
3563 					break;
3564 				}
3565 			}
3566 
3567 			/* Put entry through to tmp vfstab */
3568 			(void) putvfsent(tfile, &vp);
3569 		}
3570 	}
3571 
3572 	(void) fclose(comments);
3573 	comments = NULL;
3574 	(void) fclose(vfs_ents);
3575 	vfs_ents = NULL;
3576 	(void) fclose(tfile);
3577 	tfile = NULL;
3578 
3579 	/* Copy tmp vfstab into place */
3580 	if (rename(tmp_vfstab, vfstab) != 0) {
3581 		err = errno;
3582 		be_print_err(gettext("_update_vfstab: "
3583 		    "failed to rename file %s to %s: %s\n"), tmp_vfstab,
3584 		    vfstab, strerror(err));
3585 		ret = errno_to_be_err(err);
3586 		goto cleanup;
3587 	}
3588 
3589 	/* Set the perms and ownership of the updated file */
3590 	if (chmod(vfstab, sb.st_mode) != 0) {
3591 		err = errno;
3592 		be_print_err(gettext("_update_vfstab: "
3593 		    "failed to chmod %s: %s\n"), vfstab, strerror(err));
3594 		ret = errno_to_be_err(err);
3595 		goto cleanup;
3596 	}
3597 	if (chown(vfstab, sb.st_uid, sb.st_gid) != 0) {
3598 		err = errno;
3599 		be_print_err(gettext("_update_vfstab: "
3600 		    "failed to chown %s: %s\n"), vfstab, strerror(err));
3601 		ret = errno_to_be_err(err);
3602 		goto cleanup;
3603 	}
3604 
3605 cleanup:
3606 	if (comments != NULL)
3607 		(void) fclose(comments);
3608 	if (vfs_ents != NULL)
3609 		(void) fclose(vfs_ents);
3610 	(void) unlink(tmp_vfstab);
3611 	(void) free(tmp_vfstab);
3612 	if (tfile != NULL)
3613 		(void) fclose(tfile);
3614 
3615 	return (ret);
3616 }
3617 
3618 
3619 /*
3620  * Function:	be_get_auto_name
3621  * Description:	Generate an auto name constructed based on the BE name
3622  *		of the original BE or zone BE being cloned.
3623  * Parameters:
3624  *		obe_name - name of the original BE or zone BE being cloned.
3625  *              container_ds - container dataset for the zone.
3626  *                             Note: if zone_be is false this should be
3627  *                                  NULL.
3628  *		zone_be - flag that indicates if we are operating on a zone BE.
3629  * Returns:
3630  *		Success - pointer to auto generated BE name.  The name
3631  *			is allocated in heap storage so the caller is
3632  *			responsible for free'ing the name.
3633  *		Failure - NULL
3634  * Scope:
3635  *		Private
3636  */
3637 static char *
3638 be_get_auto_name(char *obe_name, char *be_container_ds, boolean_t zone_be)
3639 {
3640 	be_node_list_t	*be_nodes = NULL;
3641 	be_node_list_t	*cur_be = NULL;
3642 	char		auto_be_name[MAXPATHLEN];
3643 	char		base_be_name[MAXPATHLEN];
3644 	char		cur_be_name[MAXPATHLEN];
3645 	char		*num_str = NULL;
3646 	char		*c = NULL;
3647 	int		num = 0;
3648 	int		cur_num = 0;
3649 
3650 	errno = 0;
3651 
3652 	/*
3653 	 * Check if obe_name is already in an auto BE name format.
3654 	 * If it is, then strip off the increment number to get the
3655 	 * base name.
3656 	 */
3657 	(void) strlcpy(base_be_name, obe_name, sizeof (base_be_name));
3658 
3659 	if ((num_str = strrchr(base_be_name, BE_AUTO_NAME_DELIM))
3660 	    != NULL) {
3661 		/* Make sure remaining string is all digits */
3662 		c = num_str + 1;
3663 		while (c[0] != '\0' && isdigit(c[0]))
3664 			c++;
3665 		/*
3666 		 * If we're now at the end of the string strip off the
3667 		 * increment number.
3668 		 */
3669 		if (c[0] == '\0')
3670 			num_str[0] = '\0';
3671 	}
3672 
3673 	if (zone_be) {
3674 		if (be_container_ds == NULL)
3675 			return (NULL);
3676 		if (be_get_zone_be_list(obe_name, be_container_ds,
3677 		    &be_nodes) != BE_SUCCESS) {
3678 			be_print_err(gettext("be_get_auto_name: "
3679 			    "be_get_zone_be_list failed\n"));
3680 			return (NULL);
3681 		}
3682 	} else if (_be_list(NULL, &be_nodes) != BE_SUCCESS) {
3683 		be_print_err(gettext("be_get_auto_name: be_list failed\n"));
3684 		return (NULL);
3685 	}
3686 
3687 	for (cur_be = be_nodes; cur_be != NULL; cur_be = cur_be->be_next_node) {
3688 		(void) strlcpy(cur_be_name, cur_be->be_node_name,
3689 		    sizeof (cur_be_name));
3690 
3691 		/* If cur_be_name doesn't match at least base be name, skip. */
3692 		if (strncmp(cur_be_name, base_be_name, strlen(base_be_name))
3693 		    != 0)
3694 			continue;
3695 
3696 		/* Get the string following the base be name */
3697 		num_str = cur_be_name + strlen(base_be_name);
3698 
3699 		/*
3700 		 * If nothing follows the base be name, this cur_be_name
3701 		 * is the BE named with the base be name, skip.
3702 		 */
3703 		if (num_str == NULL || num_str[0] == '\0')
3704 			continue;
3705 
3706 		/*
3707 		 * Remove the name delimiter.  If its not there,
3708 		 * cur_be_name isn't part of this BE name stream, skip.
3709 		 */
3710 		if (num_str[0] == BE_AUTO_NAME_DELIM)
3711 			num_str++;
3712 		else
3713 			continue;
3714 
3715 		/* Make sure remaining string is all digits */
3716 		c = num_str;
3717 		while (c[0] != '\0' && isdigit(c[0]))
3718 			c++;
3719 		if (c[0] != '\0')
3720 			continue;
3721 
3722 		/* Convert the number string to an int */
3723 		cur_num = atoi(num_str);
3724 
3725 		/*
3726 		 * If failed to convert the string, skip it.  If its too
3727 		 * long to be converted to an int, we wouldn't auto generate
3728 		 * this number anyway so there couldn't be a conflict.
3729 		 * We treat it as a manually created BE name.
3730 		 */
3731 		if (cur_num == 0 && errno == EINVAL)
3732 			continue;
3733 
3734 		/*
3735 		 * Compare current number to current max number,
3736 		 * take higher of the two.
3737 		 */
3738 		if (cur_num > num)
3739 			num = cur_num;
3740 	}
3741 
3742 	/*
3743 	 * Store off a copy of 'num' incase we need it later.  If incrementing
3744 	 * 'num' causes it to roll over, this means 'num' is the largest
3745 	 * positive int possible; we'll need it later in the loop to determine
3746 	 * if we've exhausted all possible increment numbers.  We store it in
3747 	 * 'cur_num'.
3748 	 */
3749 	cur_num = num;
3750 
3751 	/* Increment 'num' to get new auto BE name number */
3752 	if (++num <= 0) {
3753 		int ret = 0;
3754 
3755 		/*
3756 		 * Since incrementing 'num' caused it to rollover, start
3757 		 * over at 0 and find the first available number.
3758 		 */
3759 		for (num = 0; num < cur_num; num++) {
3760 
3761 			(void) snprintf(cur_be_name, sizeof (cur_be_name),
3762 			    "%s%c%d", base_be_name, BE_AUTO_NAME_DELIM, num);
3763 
3764 			ret = zpool_iter(g_zfs, be_exists_callback,
3765 			    cur_be_name);
3766 
3767 			if (ret == 0) {
3768 				/*
3769 				 * BE name doesn't exist, break out
3770 				 * to use 'num'.
3771 				 */
3772 				break;
3773 			} else if (ret == 1) {
3774 				/* BE name exists, continue looking */
3775 				continue;
3776 			} else {
3777 				be_print_err(gettext("be_get_auto_name: "
3778 				    "zpool_iter failed: %s\n"),
3779 				    libzfs_error_description(g_zfs));
3780 				be_free_list(be_nodes);
3781 				return (NULL);
3782 			}
3783 		}
3784 
3785 		/*
3786 		 * If 'num' equals 'cur_num', we've exhausted all possible
3787 		 * auto BE names for this base BE name.
3788 		 */
3789 		if (num == cur_num) {
3790 			be_print_err(gettext("be_get_auto_name: "
3791 			    "No more available auto BE names for base "
3792 			    "BE name %s\n"), base_be_name);
3793 			be_free_list(be_nodes);
3794 			return (NULL);
3795 		}
3796 	}
3797 
3798 	be_free_list(be_nodes);
3799 
3800 	/*
3801 	 * Generate string for auto BE name.
3802 	 */
3803 	(void) snprintf(auto_be_name, sizeof (auto_be_name), "%s%c%d",
3804 	    base_be_name, BE_AUTO_NAME_DELIM, num);
3805 
3806 	if ((c = strdup(auto_be_name)) == NULL) {
3807 		be_print_err(gettext("be_get_auto_name: "
3808 		    "memory allocation failed\n"));
3809 		return (NULL);
3810 	}
3811 
3812 	return (c);
3813 }
3814 
3815 /*
3816  * Function:	be_get_console_prop
3817  * Description:	Determine console device.
3818  * Returns:
3819  *		Success - pointer to console setting.
3820  *		Failure - NULL
3821  * Scope:
3822  *		Private
3823  */
3824 static char *
3825 be_get_console_prop(void)
3826 {
3827 	di_node_t	dn;
3828 	char *console = NULL;
3829 
3830 	if ((dn = di_init("/", DINFOPROP)) == DI_NODE_NIL) {
3831 		be_print_err(gettext("be_get_console_prop: "
3832 		    "di_init() failed\n"));
3833 		return (NULL);
3834 	}
3835 
3836 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
3837 	    "console", &console) != -1) {
3838 		di_fini(dn);
3839 		return (console);
3840 	}
3841 
3842 	if (console == NULL) {
3843 		if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
3844 		    "output-device", &console) != -1) {
3845 			di_fini(dn);
3846 			if (strncmp(console, "screen", strlen("screen")) == 0)
3847 				console = BE_DEFAULT_CONSOLE;
3848 		}
3849 	}
3850 
3851 	/*
3852 	 * Default console to text
3853 	 */
3854 	if (console == NULL) {
3855 		console = BE_DEFAULT_CONSOLE;
3856 	}
3857 
3858 	return (console);
3859 }
3860 
3861 /*
3862  * Function:	be_create_menu
3863  * Description:
3864  *		This function is used if no menu.lst file exists. In
3865  *		this case a new file is created and if needed default
3866  *		lines are added to the file.
3867  * Parameters:
3868  *		pool - The name of the pool the menu.lst file is on
3869  *		menu_file - The name of the file we're creating.
3870  *		menu_fp - A pointer to the file pointer of the file we
3871  *			  created. This is also used to pass back the file
3872  *			  pointer to the newly created file.
3873  *		mode - the original mode used for the failed attempt to
3874  *		       non-existent file.
3875  * Returns:
3876  *		BE_SUCCESS - Success
3877  *		be_errno_t - Failure
3878  * Scope:
3879  *		Private
3880  */
3881 static int
3882 be_create_menu(
3883 	char *pool,
3884 	char *menu_file,
3885 	FILE **menu_fp,
3886 	char *mode)
3887 {
3888 	be_node_list_t	*be_nodes = NULL;
3889 	char *menu_path = NULL;
3890 	char *be_rpool = NULL;
3891 	char *be_name = NULL;
3892 	char *console = NULL;
3893 	errno = 0;
3894 
3895 	if (menu_file == NULL || menu_fp == NULL || mode == NULL)
3896 		return (BE_ERR_INVAL);
3897 
3898 	menu_path = strdup(menu_file);
3899 	if (menu_path == NULL)
3900 		return (BE_ERR_NOMEM);
3901 
3902 	(void) dirname(menu_path);
3903 	if (*menu_path == '.') {
3904 		free(menu_path);
3905 		return (BE_ERR_BAD_MENU_PATH);
3906 	}
3907 	if (mkdirp(menu_path,
3908 	    S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
3909 	    errno != EEXIST) {
3910 		free(menu_path);
3911 		be_print_err(gettext("be_create_menu: Failed to create the %s "
3912 		    "directory: %s\n"), menu_path, strerror(errno));
3913 		return (errno_to_be_err(errno));
3914 	}
3915 	free(menu_path);
3916 
3917 	/*
3918 	 * Check to see if this system supports grub
3919 	 */
3920 	if (be_has_grub()) {
3921 		/*
3922 		 * The grub menu is missing so we need to create it
3923 		 * and fill in the first few lines.
3924 		 */
3925 		FILE *temp_fp = fopen(menu_file, "a+");
3926 		if (temp_fp == NULL) {
3927 			*menu_fp = NULL;
3928 			return (errno_to_be_err(errno));
3929 		}
3930 
3931 		if ((console = be_get_console_prop()) != NULL) {
3932 
3933 			/*
3934 			 * If console is redirected to serial line,
3935 			 * GRUB splash screen will not be enabled.
3936 			 */
3937 			if (strncmp(console, "text", strlen("text")) == 0 ||
3938 			    strncmp(console, "graphics",
3939 			    strlen("graphics")) == 0) {
3940 
3941 				(void) fprintf(temp_fp, "%s\n", BE_GRUB_SPLASH);
3942 				(void) fprintf(temp_fp, "%s\n",
3943 				    BE_GRUB_FOREGROUND);
3944 				(void) fprintf(temp_fp, "%s\n",
3945 				    BE_GRUB_BACKGROUND);
3946 				(void) fprintf(temp_fp, "%s\n",
3947 				    BE_GRUB_DEFAULT);
3948 			} else {
3949 				be_print_err(gettext("be_create_menu: "
3950 				    "console on serial line, "
3951 				    "GRUB splash image will be disabled\n"));
3952 			}
3953 		}
3954 
3955 		(void) fprintf(temp_fp,	"timeout 30\n");
3956 		(void) fclose(temp_fp);
3957 
3958 	} else {
3959 		/*
3960 		 * The menu file doesn't exist so we need to create a
3961 		 * blank file.
3962 		 */
3963 		FILE *temp_fp = fopen(menu_file, "w+");
3964 		if (temp_fp == NULL) {
3965 			*menu_fp = NULL;
3966 			return (errno_to_be_err(errno));
3967 		}
3968 		(void) fclose(temp_fp);
3969 	}
3970 
3971 	/*
3972 	 * Now we need to add all the BE's back into the the file.
3973 	 */
3974 	if (_be_list(NULL, &be_nodes) == BE_SUCCESS) {
3975 		while (be_nodes != NULL) {
3976 			if (strcmp(pool, be_nodes->be_rpool) == 0) {
3977 				(void) be_append_menu(be_nodes->be_node_name,
3978 				    be_nodes->be_rpool, NULL, NULL, NULL);
3979 			}
3980 			if (be_nodes->be_active_on_boot) {
3981 				be_rpool = strdup(be_nodes->be_rpool);
3982 				be_name = strdup(be_nodes->be_node_name);
3983 			}
3984 
3985 			be_nodes = be_nodes->be_next_node;
3986 		}
3987 	}
3988 	be_free_list(be_nodes);
3989 
3990 	/*
3991 	 * Check to see if this system supports grub
3992 	 */
3993 	if (be_has_grub()) {
3994 		int err = be_change_grub_default(be_name, be_rpool);
3995 		if (err != BE_SUCCESS)
3996 			return (err);
3997 	}
3998 	*menu_fp = fopen(menu_file, mode);
3999 	if (*menu_fp == NULL)
4000 		return (errno_to_be_err(errno));
4001 
4002 	return (BE_SUCCESS);
4003 }
4004 
4005 /*
4006  * Function:	be_open_menu
4007  * Description:
4008  *		This function is used it open the menu.lst file. If this
4009  *              file does not exist be_create_menu is called to create it
4010  *              and the open file pointer is returned. If the file does
4011  *              exist it is simply opened using the mode passed in.
4012  * Parameters:
4013  *		pool - The name of the pool the menu.lst file is on
4014  *		menu_file - The name of the file we're opening.
4015  *		menu_fp - A pointer to the file pointer of the file we're
4016  *			  opening. This is also used to pass back the file
4017  *			  pointer.
4018  *		mode - the original mode to be used for opening the menu.lst
4019  *                     file.
4020  *              create_menu - If this is true and the menu.lst file does not
4021  *                            exist we will attempt to re-create it. However
4022  *                            if it's false the error returned from the fopen
4023  *                            will be returned.
4024  * Returns:
4025  *		BE_SUCCESS - Success
4026  *		be_errno_t - Failure
4027  * Scope:
4028  *		Private
4029  */
4030 static int
4031 be_open_menu(
4032 	char *pool,
4033 	char *menu_file,
4034 	FILE **menu_fp,
4035 	char *mode,
4036 	boolean_t create_menu)
4037 {
4038 	int	err = 0;
4039 	boolean_t	set_print = B_FALSE;
4040 
4041 	*menu_fp = fopen(menu_file, mode);
4042 	err = errno;
4043 	if (*menu_fp == NULL) {
4044 		if (err == ENOENT && create_menu) {
4045 			be_print_err(gettext("be_open_menu: menu.lst "
4046 			    "file %s does not exist,\n"), menu_file);
4047 			if (!do_print) {
4048 				set_print = B_TRUE;
4049 				do_print = B_TRUE;
4050 			}
4051 			be_print_err(gettext("WARNING: menu.lst "
4052 			    "file %s does not exist,\n         generating "
4053 			    "a new menu.lst file\n"), menu_file);
4054 			if (set_print)
4055 				do_print = B_FALSE;
4056 			err = 0;
4057 			if ((err = be_create_menu(pool, menu_file,
4058 			    menu_fp, mode)) == ENOENT)
4059 				return (BE_ERR_NO_MENU);
4060 			else if (err != BE_SUCCESS)
4061 				return (err);
4062 			else if (*menu_fp == NULL)
4063 				return (BE_ERR_NO_MENU);
4064 		} else {
4065 			be_print_err(gettext("be_open_menu: failed "
4066 			    "to open menu.lst file %s\n"), menu_file);
4067 			if (err == ENOENT)
4068 				return (BE_ERR_NO_MENU);
4069 			else
4070 				return (errno_to_be_err(err));
4071 		}
4072 	}
4073 	return (BE_SUCCESS);
4074 }
4075