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