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