1979 libbe: be_activate needs to run installgrub BTS
authorevanl
Fri, 08 Aug 2008 00:17:31 -0600
changeset 185 c0a8976c4f8d
parent 184 9097061a0241
child 186 da04561ba07a
1979 libbe: be_activate needs to run installgrub
usr/src/lib/libbe/be_activate.c
usr/src/lib/libbe/libbe_priv.h
--- a/usr/src/lib/libbe/be_activate.c	Thu Aug 07 09:11:44 2008 -0600
+++ b/usr/src/lib/libbe/be_activate.c	Fri Aug 08 00:17:31 2008 -0600
@@ -31,6 +31,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <errno.h>
 #include <sys/mnttab.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -46,6 +47,9 @@
  */
 static int set_bootfs(char *boot_rpool, char *be_root_ds);
 static int set_canmount(be_node_list_t *, char *);
+static int be_do_installgrub(be_transaction_data_t *);
+static int be_get_grub_vers(be_transaction_data_t *, char **, char **);
+static int get_ver_from_capfile(char *, char **);
 
 /* ******************************************************************** */
 /*			Public Functions				*/
@@ -124,9 +128,9 @@
 {
 	be_transaction_data_t cb = { 0 };
 	char		root_ds[MAXPATHLEN];
-	be_node_list_t *be_nodes;
-	int		err = 0;
-	int		ret, entry;
+	char		*cur_vers = NULL, *new_vers = NULL;
+	be_node_list_t	*be_nodes;
+	int		ret, entry, err = 0;
 
 	/*
 	 * TODO: The BE needs to be validated to make sure that it is actually
@@ -157,6 +161,38 @@
 	be_make_root_ds(cb.obe_zpool, cb.obe_name, root_ds, sizeof (root_ds));
 	cb.obe_root_ds = strdup(root_ds);
 
+	if ((err = be_get_grub_vers(&cb, &cur_vers, &new_vers)) != 0) {
+		be_print_err(gettext("be_activate: failed to get grub "
+		    "versions from capability files.\n"));
+		return (err);
+	}
+	if (cur_vers != NULL) {
+		/*
+		 * We need to check to see if the version number from the
+		 * BE being activated is greater than the current one.
+		 */
+		if (new_vers != NULL &&
+		    atof(cur_vers) < atof(new_vers)) {
+			err = be_do_installgrub(&cb);
+			if (err) {
+				be_zfs_fini();
+				free(new_vers);
+				free(cur_vers);
+				return (err);
+			}
+			free(new_vers);
+		}
+		free(cur_vers);
+	} else if (new_vers != NULL) {
+		err = be_do_installgrub(&cb);
+		if (err) {
+			be_zfs_fini();
+			free(new_vers);
+			return (err);
+		}
+		free(new_vers);
+	}
+
 	err = _be_list(cb.obe_name, &be_nodes);
 	if (err) {
 		be_zfs_fini();
@@ -451,3 +487,361 @@
 	}
 	return (err);
 }
+
+/*
+ * Function:	be_get_grub_vers
+ * Description:	Gets the grub version number from /boot/grub/capability. If
+ *              capability file doesn't exist NULL is returned.
+ * Parameters:
+ *              bt - The transaction data for the BE we're getting the grub
+ *                   version for.
+ *              cur_vers - used to return the current version of grub from
+ *                         the root pool.
+ *              new_vers - used to return the grub version of the BE we're
+ *                         activating.
+ * Return:
+ *              0 - Success
+ *              be_errno_t - Failed to find version
+ * Scope:
+ *		Private
+ */
+static int
+be_get_grub_vers(be_transaction_data_t *bt, char **cur_vers, char **new_vers)
+{
+	zfs_handle_t	*zhp;
+	int err = 0;
+	char cap_file[MAXPATHLEN];
+	char *temp_mntpnt;
+	char *zpool_mntpt;
+	boolean_t be_mounted = B_FALSE;
+
+
+	if (bt == NULL || bt->obe_name == NULL || bt->obe_zpool == NULL ||
+	    bt->obe_root_ds == NULL) {
+		be_print_err(gettext("get_grub_vers: Invalid BE\n"));
+		return (BE_ERR_INVAL);
+	}
+
+	if ((zhp = zfs_open(g_zfs, bt->obe_zpool, ZFS_TYPE_FILESYSTEM)) ==
+	    NULL) {
+		be_print_err(gettext("get_grub_vers: zfs_open failed: %s\n"),
+		    libzfs_error_description(g_zfs));
+		return (zfs_err_to_be_err(g_zfs));
+	}
+	if (!zfs_is_mounted(zhp, &zpool_mntpt)) {
+		be_print_err(gettext("get_grub_vers: root pool is not "
+		    "mounted, can not access root grub directory\n"),
+		    bt->obe_zpool);
+		ZFS_CLOSE(zhp);
+		return (BE_ERR_NOTMOUNTED);
+	} else {
+		/*
+		 * get the version of the most recent grub update.
+		 */
+		(void) snprintf(cap_file, sizeof (cap_file), "/%s%s",
+		    zpool_mntpt, BE_CAP_FILE);
+		free(zpool_mntpt);
+		ZFS_CLOSE(zhp);
+		err = get_ver_from_capfile(cap_file, cur_vers);
+		if (err != 0)
+			return (err);
+	}
+
+	if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
+	    NULL) {
+		be_print_err(gettext("get_grub_vers: failed to "
+		    "open BE root dataset (%s): %s\n"), bt->obe_root_ds,
+		    libzfs_error_description(g_zfs));
+		free(cur_vers);
+		return (zfs_err_to_be_err(g_zfs));
+	}
+	if (!zfs_is_mounted(zhp, &temp_mntpnt)) {
+		err = _be_mount(bt->obe_name, &temp_mntpnt, 0);
+		if (err) {
+			be_print_err(gettext("get_grub_vers: failed to "
+			    "mount BE (%s)\n"), bt->obe_name);
+			free(*cur_vers);
+			*cur_vers = NULL;
+			ZFS_CLOSE(zhp);
+			return (NULL);
+		}
+		be_mounted = B_TRUE;
+	}
+	ZFS_CLOSE(zhp);
+
+	/*
+	 * Now get the grub version for the BE being activated.
+	 */
+	(void) snprintf(cap_file, sizeof (cap_file), "%s%s", temp_mntpnt,
+	    BE_CAP_FILE);
+	err = get_ver_from_capfile(cap_file, new_vers);
+	if (err != 0) {
+		free(*cur_vers);
+		*cur_vers = NULL;
+	}
+	if (be_mounted)
+		(void) _be_unmount(bt->obe_name, 0);
+	free(temp_mntpnt);
+	return (err);
+}
+
+/*
+ * Function:	get_ver_from_capfile
+ * Description: Parses the capability file passed in looking for the VERSION
+ *              line. If found the version is returned in vers, if not then
+ *              NULL is returned in vers.
+ *
+ * Parameters:
+ *              file - the path to the capability file we want to parse.
+ *              vers - the version string that will be passed back.
+ * Return:
+ *              0 - Success
+ *              be_errno_t - Failed to find version
+ * Scope:
+ *		Private
+ */
+static int
+get_ver_from_capfile(char *file, char **vers)
+{
+	FILE *fp = NULL;
+	char line[BUFSIZ];
+	char *last;
+	uint_t err = 0;
+
+	errno = 0;
+	fp = fopen(file, "r");
+	err = errno;
+
+	if (err == 0 && fp != NULL) {
+		while (fgets(line, BUFSIZ, fp)) {
+			char *tok = strtok_r(line, "=", &last);
+
+			if (tok == NULL || tok[0] == '#') {
+				continue;
+			} else if (strcmp(tok, "VERSION") == 0) {
+				fclose(fp);
+				*vers = strdup(last);
+				return (BE_SUCCESS);
+			}
+		}
+		fclose(fp);
+		fp = NULL;
+	} else if (err == ENOENT) {
+		/*
+		 * If capability file doesn't exist, set the version to NULL,
+		 * but return success.
+		 * This is correct because it is valid in older releases for
+		 * the capability file to be missing.
+		 */
+		*vers = NULL;
+		return (BE_SUCCESS);
+	} else {
+		be_print_err(gettext("get_ver_from_capfile: failed to open "
+		    "file %s with err %s\n"), file, strerror(err));
+		err = errno_to_be_err(err);
+		if (fp != NULL)
+			fclose(fp);
+	}
+	*vers = NULL;
+	return (err);
+}
+
+/*
+ * Function:	be_do_installgrub
+ * Description:	This function runs installgrub using the grub loader files
+ *              from the BE we're activating and installing them on the
+ *              pool the BE lives in.
+ *
+ * Parameters:
+ *              bt - The transaction data for the BE we're activating.
+ * Return:
+ *		0 - Success
+ *		be_errno_t - Failure
+ *
+ * Scope:
+ *		Private
+ */
+static int
+be_do_installgrub(be_transaction_data_t *bt)
+{
+	zpool_handle_t  *zphp;
+	zfs_handle_t	*zhp;
+	nvlist_t **child, *nv, *config;
+	uint_t c, children = 0;
+	char *tmp_mntpt = NULL;
+	FILE *cap_fp = NULL;
+	FILE *zpool_cap_fp = NULL;
+	char line[BUFSIZ];
+	char cap_file[MAXPATHLEN];
+	char zpool_cap_file[MAXPATHLEN];
+	char stage1[MAXPATHLEN];
+	char stage2[MAXPATHLEN];
+	char installgrub_cmd[MAXPATHLEN];
+	char *vname;
+	int err = 0;
+	boolean_t be_mounted = B_FALSE;
+
+	if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
+	    NULL) {
+		be_print_err(gettext("get_grub_vers: failed to "
+		    "open BE root dataset (%s): %s\n"), bt->obe_root_ds,
+		    libzfs_error_description(g_zfs));
+		err = zfs_err_to_be_err(g_zfs);
+		return (err);
+	}
+	if (!zfs_is_mounted(zhp, &tmp_mntpt)) {
+		if (_be_mount(bt->obe_name, &tmp_mntpt, 0) != 0) {
+			be_print_err(gettext("be_do_installgrub: failed to "
+			    "mount BE (%s)\n"), bt->obe_name);
+			return (BE_ERR_MOUNT);
+		}
+		be_mounted = B_TRUE;
+	}
+	ZFS_CLOSE(zhp);
+
+	(void) snprintf(stage1, sizeof (stage1), "%s%s", tmp_mntpt, BE_STAGE_1);
+	(void) snprintf(stage2, sizeof (stage2), "%s%s", tmp_mntpt, BE_STAGE_2);
+
+	if ((zphp = zpool_open(g_zfs, bt->obe_zpool)) == NULL) {
+		be_print_err(gettext("be_do_installgrub: failed to open "
+		    "pool (%s): %s\n"), bt->obe_zpool,
+		    libzfs_error_description(g_zfs));
+		err = zfs_err_to_be_err(g_zfs);
+		if (be_mounted)
+			(void) _be_unmount(bt->obe_name, 0);
+		free(tmp_mntpt);
+		return (err);
+	}
+
+	if ((config = zpool_get_config(zphp, NULL)) == NULL) {
+		be_print_err(gettext("be_do_installgrub: failed to get zpool "
+		    "configuration information. %s\n"),
+		    libzfs_error_description(g_zfs));
+		err = zfs_err_to_be_err(g_zfs);
+		goto done;
+	}
+
+	/*
+	 * Get the vdev tree
+	 */
+	if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nv) != 0) {
+		be_print_err(gettext("be_do_installgrub: failed to get vdev "
+		    "tree: %s\n"), libzfs_error_description(g_zfs));
+		err = zfs_err_to_be_err(g_zfs);
+		goto done;
+	}
+
+	if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child,
+	    &children) != 0) {
+		be_print_err(gettext("be_do_installgrub: failed to traverse "
+		    "the vdev tree: %s\n"), libzfs_error_description(g_zfs));
+		err = zfs_err_to_be_err(g_zfs);
+		goto done;
+	}
+	for (c = 0; c < children; c++) {
+		uint_t i, nchildren = 0;
+		nvlist_t **nvchild;
+		vname = zpool_vdev_name(g_zfs, zphp, child[c]);
+		if (vname == NULL) {
+			be_print_err(gettext(
+			    "be_do_installgrub: "
+			    "failed to get device name: %s\n"),
+			    libzfs_error_description(g_zfs));
+			err = zfs_err_to_be_err(g_zfs);
+			goto done;
+		}
+		if (strcmp(vname, "mirror") == 0 || vname[0] != 'c') {
+
+			if (nvlist_lookup_nvlist_array(child[c],
+			    ZPOOL_CONFIG_CHILDREN, &nvchild, &nchildren) != 0) {
+				be_print_err(gettext("be_do_installgrub: "
+				    "failed to traverse the vdev tree: %s\n"),
+				    libzfs_error_description(g_zfs));
+				err = zfs_err_to_be_err(g_zfs);
+				goto done;
+			}
+
+			for (i = 0; i < nchildren; i++) {
+				vname = zpool_vdev_name(g_zfs, zphp,
+				    nvchild[i]);
+				if (vname == NULL) {
+					be_print_err(gettext(
+					    "be_do_installgrub: "
+					    "failed to get device name: %s\n"),
+					    libzfs_error_description(g_zfs));
+					err = zfs_err_to_be_err(g_zfs);
+					goto done;
+				}
+
+				(void) snprintf(installgrub_cmd,
+				    sizeof (installgrub_cmd),
+				    "%s %s %s /dev/rdsk/%s", BE_INSTALL_GRUB,
+				    stage1, stage2, vname);
+				err = system(installgrub_cmd);
+
+				if (err) {
+					be_print_err(gettext(
+					    "be_do_installgrub: installgrub "
+					    "failed for device %s.\n"), vname);
+					free(vname);
+					err = errno_to_be_err(err);
+					goto done;
+				}
+				free(vname);
+			}
+		} else {
+			(void) snprintf(installgrub_cmd,
+			    sizeof (installgrub_cmd),
+			    "%s %s %s /dev/rdsk/%s", BE_INSTALL_GRUB,
+			    stage1, stage2, vname);
+			err = system(installgrub_cmd);
+
+			if (err) {
+				be_print_err(gettext(
+				    "be_do_installgrub: installgrub "
+				    "failed for device %s.\n"), vname);
+				free(vname);
+				err = errno_to_be_err(err);
+				goto done;
+			}
+			free(vname);
+		}
+	}
+
+	/*
+	 * Copy the grub capability file from the BE we're activating into
+	 * the root pool.
+	 */
+	(void) snprintf(cap_file, sizeof (cap_file), "%s%s", tmp_mntpt,
+	    BE_CAP_FILE);
+	(void) snprintf(zpool_cap_file, sizeof (zpool_cap_file), "/%s%s",
+	    bt->obe_zpool, BE_CAP_FILE);
+	if ((cap_fp = fopen(cap_file, "r")) == NULL) {
+		err = errno;
+		be_print_err(gettext("be_do_installgrub: failed to open grub "
+		    "capability file\n"));
+		err = errno_to_be_err(err);
+		goto done;
+	}
+	if ((zpool_cap_fp = fopen(zpool_cap_file, "w")) == NULL) {
+		err = errno;
+		be_print_err(gettext("be_do_installgrub: failed to open new "
+		    "grub capability file\n"));
+		err = errno_to_be_err(err);
+		goto done;
+	}
+
+	while (fgets(line, BUFSIZ, cap_fp)) {
+		fputs(line, zpool_cap_fp);
+	}
+
+	fclose(zpool_cap_fp);
+	fclose(cap_fp);
+
+done:
+	if (be_mounted)
+		(void) _be_unmount(bt->obe_name, 0);
+	zpool_close(zphp);
+	free(tmp_mntpt);
+	return (err);
+}
--- a/usr/src/lib/libbe/libbe_priv.h	Thu Aug 07 09:11:44 2008 -0600
+++ b/usr/src/lib/libbe/libbe_priv.h	Fri Aug 08 00:17:31 2008 -0600
@@ -38,6 +38,10 @@
 #define	BE_PLCY_VOLATILE	"volatile"
 #define	BE_GRUB_COMMENT		"#============ End of LIBBE entry ============="
 #define	BE_WHITE_SPACE		" \t\r\n"
+#define	BE_CAP_FILE		"/boot/grub/capability"
+#define	BE_INSTALL_GRUB		"/sbin/installgrub"
+#define	BE_STAGE_1		"/boot/grub/stage1"
+#define	BE_STAGE_2		"/boot/grub/stage2"
 #define	ZFS_CLOSE(_zhp) \
 	if (_zhp) { \
 		zfs_close(_zhp); \