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