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