mirror of
				https://git.proxmox.com/git/mirror_zfs.git
				synced 2025-10-26 18:05:04 +03:00 
			
		
		
		
	Add zfs_prepare_disk script for disk firmware install
Have libzfs call a special `zfs_prepare_disk` script before a disk is included into the pool. The user can edit this script to add things like a disk firmware update or a disk health check. Use of the script is totally optional. See the zfs_prepare_disk manpage for full details. Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Tony Hutter <hutter2@llnl.gov> Closes #15243
This commit is contained in:
		
							parent
							
								
									35252ae0fd
								
							
						
					
					
						commit
						11574a7934
					
				@ -141,6 +141,17 @@ zfs_unavail_pool(zpool_handle_t *zhp, void *data)
 | 
			
		||||
	return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Write an array of strings to the zed log
 | 
			
		||||
 */
 | 
			
		||||
static void lines_to_zed_log_msg(char **lines, int lines_cnt)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	for (i = 0; i < lines_cnt; i++) {
 | 
			
		||||
		zed_log_msg(LOG_INFO, "%s", lines[i]);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Two stage replace on Linux
 | 
			
		||||
 * since we get disk notifications
 | 
			
		||||
@ -195,6 +206,8 @@ zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t labeled)
 | 
			
		||||
	boolean_t is_mpath_wholedisk = B_FALSE;
 | 
			
		||||
	uint_t c;
 | 
			
		||||
	vdev_stat_t *vs;
 | 
			
		||||
	char **lines = NULL;
 | 
			
		||||
	int lines_cnt = 0;
 | 
			
		||||
 | 
			
		||||
	if (nvlist_lookup_string(vdev, ZPOOL_CONFIG_PATH, &path) != 0)
 | 
			
		||||
		return;
 | 
			
		||||
@ -377,6 +390,22 @@ zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t labeled)
 | 
			
		||||
 | 
			
		||||
	if (is_mpath_wholedisk) {
 | 
			
		||||
		/* Don't label device mapper or multipath disks. */
 | 
			
		||||
		zed_log_msg(LOG_INFO,
 | 
			
		||||
		    "  it's a multipath wholedisk, don't label");
 | 
			
		||||
		if (zpool_prepare_disk(zhp, vdev, "autoreplace", &lines,
 | 
			
		||||
		    &lines_cnt) != 0) {
 | 
			
		||||
			zed_log_msg(LOG_INFO,
 | 
			
		||||
			    "  zpool_prepare_disk: could not "
 | 
			
		||||
			    "prepare '%s' (%s)", fullpath,
 | 
			
		||||
			    libzfs_error_description(g_zfshdl));
 | 
			
		||||
			if (lines_cnt > 0) {
 | 
			
		||||
				zed_log_msg(LOG_INFO,
 | 
			
		||||
				    "  zfs_prepare_disk output:");
 | 
			
		||||
				lines_to_zed_log_msg(lines, lines_cnt);
 | 
			
		||||
			}
 | 
			
		||||
			libzfs_free_str_array(lines, lines_cnt);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
	} else if (!labeled) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * we're auto-replacing a raw disk, so label it first
 | 
			
		||||
@ -399,10 +428,18 @@ zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t labeled)
 | 
			
		||||
		 * If this is a request to label a whole disk, then attempt to
 | 
			
		||||
		 * write out the label.
 | 
			
		||||
		 */
 | 
			
		||||
		if (zpool_label_disk(g_zfshdl, zhp, leafname) != 0) {
 | 
			
		||||
			zed_log_msg(LOG_INFO, "  zpool_label_disk: could not "
 | 
			
		||||
		if (zpool_prepare_and_label_disk(g_zfshdl, zhp, leafname,
 | 
			
		||||
		    vdev, "autoreplace", &lines, &lines_cnt) != 0) {
 | 
			
		||||
			zed_log_msg(LOG_INFO,
 | 
			
		||||
			    "  zpool_prepare_and_label_disk: could not "
 | 
			
		||||
			    "label '%s' (%s)", leafname,
 | 
			
		||||
			    libzfs_error_description(g_zfshdl));
 | 
			
		||||
			if (lines_cnt > 0) {
 | 
			
		||||
				zed_log_msg(LOG_INFO,
 | 
			
		||||
				"  zfs_prepare_disk output:");
 | 
			
		||||
				lines_to_zed_log_msg(lines, lines_cnt);
 | 
			
		||||
			}
 | 
			
		||||
			libzfs_free_str_array(lines, lines_cnt);
 | 
			
		||||
 | 
			
		||||
			(void) zpool_vdev_online(zhp, fullpath,
 | 
			
		||||
			    ZFS_ONLINE_FORCEFAULT, &newstate);
 | 
			
		||||
@ -457,6 +494,8 @@ zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t labeled)
 | 
			
		||||
		    DEV_BYID_PATH, new_devid);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	libzfs_free_str_array(lines, lines_cnt);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Construct the root vdev to pass to zpool_vdev_attach().  While adding
 | 
			
		||||
	 * the entire vdev structure is harmless, we construct a reduced set of
 | 
			
		||||
 | 
			
		||||
@ -439,39 +439,23 @@ static void
 | 
			
		||||
vdev_run_cmd(vdev_cmd_data_t *data, char *cmd)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	char *argv[2] = {cmd, 0};
 | 
			
		||||
	char *env[5] = {"PATH=/bin:/sbin:/usr/bin:/usr/sbin", NULL, NULL, NULL,
 | 
			
		||||
	    NULL};
 | 
			
		||||
	char *argv[2] = {cmd};
 | 
			
		||||
	char **env;
 | 
			
		||||
	char **lines = NULL;
 | 
			
		||||
	int lines_cnt = 0;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	/* Setup our custom environment variables */
 | 
			
		||||
	rc = asprintf(&env[1], "VDEV_PATH=%s",
 | 
			
		||||
	    data->path ? data->path : "");
 | 
			
		||||
	if (rc == -1) {
 | 
			
		||||
		env[1] = NULL;
 | 
			
		||||
	env = zpool_vdev_script_alloc_env(data->pool, data->path, data->upath,
 | 
			
		||||
	    data->vdev_enc_sysfs_path, NULL, NULL);
 | 
			
		||||
	if (env == NULL)
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = asprintf(&env[2], "VDEV_UPATH=%s",
 | 
			
		||||
	    data->upath ? data->upath : "");
 | 
			
		||||
	if (rc == -1) {
 | 
			
		||||
		env[2] = NULL;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = asprintf(&env[3], "VDEV_ENC_SYSFS_PATH=%s",
 | 
			
		||||
	    data->vdev_enc_sysfs_path ?
 | 
			
		||||
	    data->vdev_enc_sysfs_path : "");
 | 
			
		||||
	if (rc == -1) {
 | 
			
		||||
		env[3] = NULL;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Run the command */
 | 
			
		||||
	rc = libzfs_run_process_get_stdout_nopath(cmd, argv, env, &lines,
 | 
			
		||||
	    &lines_cnt);
 | 
			
		||||
 | 
			
		||||
	zpool_vdev_script_free_env(env);
 | 
			
		||||
 | 
			
		||||
	if (rc != 0)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
@ -483,10 +467,6 @@ vdev_run_cmd(vdev_cmd_data_t *data, char *cmd)
 | 
			
		||||
out:
 | 
			
		||||
	if (lines != NULL)
 | 
			
		||||
		libzfs_free_str_array(lines, lines_cnt);
 | 
			
		||||
 | 
			
		||||
	/* Start with i = 1 since env[0] was statically allocated */
 | 
			
		||||
	for (i = 1; i < ARRAY_SIZE(env); i++)
 | 
			
		||||
		free(env[i]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
@ -124,6 +124,10 @@ vdev_cmd_data_list_t *all_pools_for_each_vdev_run(int argc, char **argv,
 | 
			
		||||
 | 
			
		||||
void free_vdev_cmd_data_list(vdev_cmd_data_list_t *vcdl);
 | 
			
		||||
 | 
			
		||||
void free_vdev_cmd_data(vdev_cmd_data_t *data);
 | 
			
		||||
 | 
			
		||||
int vdev_run_cmd_simple(char *path, char *cmd);
 | 
			
		||||
 | 
			
		||||
int check_device(const char *path, boolean_t force,
 | 
			
		||||
    boolean_t isspare, boolean_t iswholedisk);
 | 
			
		||||
boolean_t check_sector_size_database(char *path, int *sector_size);
 | 
			
		||||
 | 
			
		||||
@ -921,6 +921,15 @@ zero_label(char *path)
 | 
			
		||||
	return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
lines_to_stderr(char *lines[], int lines_cnt)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	for (i = 0; i < lines_cnt; i++) {
 | 
			
		||||
		fprintf(stderr, "%s\n", lines[i]);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Go through and find any whole disks in the vdev specification, labelling them
 | 
			
		||||
 * as appropriate.  When constructing the vdev spec, we were unable to open this
 | 
			
		||||
@ -932,7 +941,7 @@ zero_label(char *path)
 | 
			
		||||
 * need to get the devid after we label the disk.
 | 
			
		||||
 */
 | 
			
		||||
static int
 | 
			
		||||
make_disks(zpool_handle_t *zhp, nvlist_t *nv)
 | 
			
		||||
make_disks(zpool_handle_t *zhp, nvlist_t *nv, boolean_t replacing)
 | 
			
		||||
{
 | 
			
		||||
	nvlist_t **child;
 | 
			
		||||
	uint_t c, children;
 | 
			
		||||
@ -1017,6 +1026,8 @@ make_disks(zpool_handle_t *zhp, nvlist_t *nv)
 | 
			
		||||
		 */
 | 
			
		||||
		if (!is_exclusive && !is_spare(NULL, udevpath)) {
 | 
			
		||||
			char *devnode = strrchr(devpath, '/') + 1;
 | 
			
		||||
			char **lines = NULL;
 | 
			
		||||
			int lines_cnt = 0;
 | 
			
		||||
 | 
			
		||||
			ret = strncmp(udevpath, UDISK_ROOT, strlen(UDISK_ROOT));
 | 
			
		||||
			if (ret == 0) {
 | 
			
		||||
@ -1028,9 +1039,27 @@ make_disks(zpool_handle_t *zhp, nvlist_t *nv)
 | 
			
		||||
			/*
 | 
			
		||||
			 * When labeling a pool the raw device node name
 | 
			
		||||
			 * is provided as it appears under /dev/.
 | 
			
		||||
			 *
 | 
			
		||||
			 * Note that 'zhp' will be NULL when we're creating a
 | 
			
		||||
			 * pool.
 | 
			
		||||
			 */
 | 
			
		||||
			if (zpool_label_disk(g_zfs, zhp, devnode) == -1)
 | 
			
		||||
			if (zpool_prepare_and_label_disk(g_zfs, zhp, devnode,
 | 
			
		||||
			    nv, zhp == NULL ? "create" :
 | 
			
		||||
			    replacing ? "replace" : "add", &lines,
 | 
			
		||||
			    &lines_cnt) != 0) {
 | 
			
		||||
				(void) fprintf(stderr,
 | 
			
		||||
				    gettext(
 | 
			
		||||
				    "Error preparing/labeling disk.\n"));
 | 
			
		||||
				if (lines_cnt > 0) {
 | 
			
		||||
					(void) fprintf(stderr,
 | 
			
		||||
					gettext("zfs_prepare_disk output:\n"));
 | 
			
		||||
					lines_to_stderr(lines, lines_cnt);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				libzfs_free_str_array(lines, lines_cnt);
 | 
			
		||||
				return (-1);
 | 
			
		||||
			}
 | 
			
		||||
			libzfs_free_str_array(lines, lines_cnt);
 | 
			
		||||
 | 
			
		||||
			/*
 | 
			
		||||
			 * Wait for udev to signal the device is available
 | 
			
		||||
@ -1067,19 +1096,19 @@ make_disks(zpool_handle_t *zhp, nvlist_t *nv)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (c = 0; c < children; c++)
 | 
			
		||||
		if ((ret = make_disks(zhp, child[c])) != 0)
 | 
			
		||||
		if ((ret = make_disks(zhp, child[c], replacing)) != 0)
 | 
			
		||||
			return (ret);
 | 
			
		||||
 | 
			
		||||
	if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES,
 | 
			
		||||
	    &child, &children) == 0)
 | 
			
		||||
		for (c = 0; c < children; c++)
 | 
			
		||||
			if ((ret = make_disks(zhp, child[c])) != 0)
 | 
			
		||||
			if ((ret = make_disks(zhp, child[c], replacing)) != 0)
 | 
			
		||||
				return (ret);
 | 
			
		||||
 | 
			
		||||
	if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
 | 
			
		||||
	    &child, &children) == 0)
 | 
			
		||||
		for (c = 0; c < children; c++)
 | 
			
		||||
			if ((ret = make_disks(zhp, child[c])) != 0)
 | 
			
		||||
			if ((ret = make_disks(zhp, child[c], replacing)) != 0)
 | 
			
		||||
				return (ret);
 | 
			
		||||
 | 
			
		||||
	return (0);
 | 
			
		||||
@ -1740,7 +1769,7 @@ split_mirror_vdev(zpool_handle_t *zhp, char *newname, nvlist_t *props,
 | 
			
		||||
			return (NULL);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!flags.dryrun && make_disks(zhp, newroot) != 0) {
 | 
			
		||||
		if (!flags.dryrun && make_disks(zhp, newroot, B_FALSE) != 0) {
 | 
			
		||||
			nvlist_free(newroot);
 | 
			
		||||
			return (NULL);
 | 
			
		||||
		}
 | 
			
		||||
@ -1861,7 +1890,7 @@ make_root_vdev(zpool_handle_t *zhp, nvlist_t *props, int force, int check_rep,
 | 
			
		||||
	/*
 | 
			
		||||
	 * Run through the vdev specification and label any whole disks found.
 | 
			
		||||
	 */
 | 
			
		||||
	if (!dryrun && make_disks(zhp, newroot) != 0) {
 | 
			
		||||
	if (!dryrun && make_disks(zhp, newroot, replacing) != 0) {
 | 
			
		||||
		nvlist_free(newroot);
 | 
			
		||||
		return (NULL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -41,6 +41,7 @@ AM_CPPFLAGS += -D_REENTRANT
 | 
			
		||||
AM_CPPFLAGS += -D_FILE_OFFSET_BITS=64
 | 
			
		||||
AM_CPPFLAGS += -D_LARGEFILE64_SOURCE
 | 
			
		||||
AM_CPPFLAGS += -DLIBEXECDIR=\"$(libexecdir)\"
 | 
			
		||||
AM_CPPFLAGS += -DZFSEXECDIR=\"$(zfsexecdir)\"
 | 
			
		||||
AM_CPPFLAGS += -DRUNSTATEDIR=\"$(runstatedir)\"
 | 
			
		||||
AM_CPPFLAGS += -DSBINDIR=\"$(sbindir)\"
 | 
			
		||||
AM_CPPFLAGS += -DSYSCONFDIR=\"$(sysconfdir)\"
 | 
			
		||||
 | 
			
		||||
@ -318,6 +318,15 @@ extern nvlist_t *zpool_find_vdev(zpool_handle_t *, const char *, boolean_t *,
 | 
			
		||||
extern nvlist_t *zpool_find_vdev_by_physpath(zpool_handle_t *, const char *,
 | 
			
		||||
    boolean_t *, boolean_t *, boolean_t *);
 | 
			
		||||
extern int zpool_label_disk(libzfs_handle_t *, zpool_handle_t *, const char *);
 | 
			
		||||
extern int zpool_prepare_disk(zpool_handle_t *zhp, nvlist_t *vdev_nv,
 | 
			
		||||
    const char *prepare_str, char **lines[], int *lines_cnt);
 | 
			
		||||
extern int zpool_prepare_and_label_disk(libzfs_handle_t *hdl,
 | 
			
		||||
    zpool_handle_t *, const char *, nvlist_t *vdev_nv, const char *prepare_str,
 | 
			
		||||
    char **lines[], int *lines_cnt);
 | 
			
		||||
extern char ** zpool_vdev_script_alloc_env(const char *pool_name,
 | 
			
		||||
    const char *vdev_path, const char *vdev_upath,
 | 
			
		||||
    const char *vdev_enc_sysfs_path, const char *opt_key, const char *opt_val);
 | 
			
		||||
extern void zpool_vdev_script_free_env(char **env);
 | 
			
		||||
extern uint64_t zpool_vdev_path_to_guid(zpool_handle_t *zhp, const char *path);
 | 
			
		||||
 | 
			
		||||
const char *zpool_get_state_str(zpool_handle_t *);
 | 
			
		||||
 | 
			
		||||
@ -333,6 +333,8 @@
 | 
			
		||||
    <elf-symbol name='zpool_open_canfail' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
 | 
			
		||||
    <elf-symbol name='zpool_open_silent' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
 | 
			
		||||
    <elf-symbol name='zpool_pool_state_to_name' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
 | 
			
		||||
    <elf-symbol name='zpool_prepare_and_label_disk' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
 | 
			
		||||
    <elf-symbol name='zpool_prepare_disk' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
 | 
			
		||||
    <elf-symbol name='zpool_print_unsup_feat' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
 | 
			
		||||
    <elf-symbol name='zpool_prop_align_right' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
 | 
			
		||||
    <elf-symbol name='zpool_prop_column_name' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
 | 
			
		||||
@ -380,6 +382,8 @@
 | 
			
		||||
    <elf-symbol name='zpool_vdev_remove' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
 | 
			
		||||
    <elf-symbol name='zpool_vdev_remove_cancel' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
 | 
			
		||||
    <elf-symbol name='zpool_vdev_remove_wanted' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
 | 
			
		||||
    <elf-symbol name='zpool_vdev_script_alloc_env' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
 | 
			
		||||
    <elf-symbol name='zpool_vdev_script_free_env' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
 | 
			
		||||
    <elf-symbol name='zpool_vdev_split' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
 | 
			
		||||
    <elf-symbol name='zpool_wait' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
 | 
			
		||||
    <elf-symbol name='zpool_wait_status' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
 | 
			
		||||
 | 
			
		||||
@ -2121,3 +2121,196 @@ printf_color(const char *color, char *format, ...)
 | 
			
		||||
 | 
			
		||||
	return (rc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* PATH + 5 env vars + a NULL entry = 7 */
 | 
			
		||||
#define	ZPOOL_VDEV_SCRIPT_ENV_COUNT 7
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * There's a few places where ZFS will call external scripts (like the script
 | 
			
		||||
 * in zpool.d/ and `zfs_prepare_disk`).  These scripts are called with a
 | 
			
		||||
 * reduced $PATH, and some vdev specific environment vars set.  This function
 | 
			
		||||
 * will allocate an populate the environment variable array that is passed to
 | 
			
		||||
 * these scripts.  The user must free the arrays with zpool_vdev_free_env() when
 | 
			
		||||
 * they are done.
 | 
			
		||||
 *
 | 
			
		||||
 * The following env vars will be set (but value could be blank):
 | 
			
		||||
 *
 | 
			
		||||
 * POOL_NAME
 | 
			
		||||
 * VDEV_PATH
 | 
			
		||||
 * VDEV_UPATH
 | 
			
		||||
 * VDEV_ENC_SYSFS_PATH
 | 
			
		||||
 *
 | 
			
		||||
 * In addition, you can set an optional environment variable named 'opt_key'
 | 
			
		||||
 * to 'opt_val' if you want.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns allocated env[] array on success, NULL otherwise.
 | 
			
		||||
 */
 | 
			
		||||
char **
 | 
			
		||||
zpool_vdev_script_alloc_env(const char *pool_name,
 | 
			
		||||
    const char *vdev_path, const char *vdev_upath,
 | 
			
		||||
    const char *vdev_enc_sysfs_path, const char *opt_key, const char *opt_val)
 | 
			
		||||
{
 | 
			
		||||
	char **env = NULL;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	env = calloc(ZPOOL_VDEV_SCRIPT_ENV_COUNT, sizeof (*env));
 | 
			
		||||
	if (!env)
 | 
			
		||||
		return (NULL);
 | 
			
		||||
 | 
			
		||||
	env[0] = strdup("PATH=/bin:/sbin:/usr/bin:/usr/sbin");
 | 
			
		||||
	if (!env[0])
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	/* Setup our custom environment variables */
 | 
			
		||||
	rc = asprintf(&env[1], "POOL_NAME=%s", pool_name ? pool_name : "");
 | 
			
		||||
	if (rc == -1) {
 | 
			
		||||
		env[1] = NULL;
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = asprintf(&env[2], "VDEV_PATH=%s", vdev_path ? vdev_path : "");
 | 
			
		||||
	if (rc == -1) {
 | 
			
		||||
		env[2] = NULL;
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = asprintf(&env[3], "VDEV_UPATH=%s", vdev_upath ? vdev_upath : "");
 | 
			
		||||
	if (rc == -1) {
 | 
			
		||||
		env[3] = NULL;
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = asprintf(&env[4], "VDEV_ENC_SYSFS_PATH=%s",
 | 
			
		||||
	    vdev_enc_sysfs_path ?  vdev_enc_sysfs_path : "");
 | 
			
		||||
	if (rc == -1) {
 | 
			
		||||
		env[4] = NULL;
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (opt_key != NULL) {
 | 
			
		||||
		rc = asprintf(&env[5], "%s=%s", opt_key,
 | 
			
		||||
		    opt_val ? opt_val : "");
 | 
			
		||||
		if (rc == -1) {
 | 
			
		||||
			env[5] = NULL;
 | 
			
		||||
			goto error;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (env);
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	for (int i = 0; i < ZPOOL_VDEV_SCRIPT_ENV_COUNT; i++)
 | 
			
		||||
		free(env[i]);
 | 
			
		||||
 | 
			
		||||
	free(env);
 | 
			
		||||
 | 
			
		||||
	return (NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Free the env[] array that was allocated by zpool_vdev_script_alloc_env().
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
zpool_vdev_script_free_env(char **env)
 | 
			
		||||
{
 | 
			
		||||
	for (int i = 0; i < ZPOOL_VDEV_SCRIPT_ENV_COUNT; i++)
 | 
			
		||||
		free(env[i]);
 | 
			
		||||
 | 
			
		||||
	free(env);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Prepare a disk by (optionally) running a program before labeling the disk.
 | 
			
		||||
 * This can be useful for installing disk firmware or doing some pre-flight
 | 
			
		||||
 * checks on the disk before it becomes part of the pool.  The program run is
 | 
			
		||||
 * located at ZFSEXECDIR/zfs_prepare_disk
 | 
			
		||||
 * (E.x: /usr/local/libexec/zfs/zfs_prepare_disk).
 | 
			
		||||
 *
 | 
			
		||||
 * Return 0 on success, non-zero on failure.
 | 
			
		||||
 */
 | 
			
		||||
int
 | 
			
		||||
zpool_prepare_disk(zpool_handle_t *zhp, nvlist_t *vdev_nv,
 | 
			
		||||
    const char *prepare_str, char **lines[], int *lines_cnt)
 | 
			
		||||
{
 | 
			
		||||
	const char *script_path = ZFSEXECDIR "/zfs_prepare_disk";
 | 
			
		||||
	const char *pool_name;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
 | 
			
		||||
	/* Path to script and a NULL entry */
 | 
			
		||||
	char *argv[2] = {(char *)script_path};
 | 
			
		||||
	char **env = NULL;
 | 
			
		||||
	char *path = NULL, *enc_sysfs_path = NULL;
 | 
			
		||||
	char *upath;
 | 
			
		||||
	*lines_cnt = 0;
 | 
			
		||||
 | 
			
		||||
	if (access(script_path, X_OK) != 0) {
 | 
			
		||||
		/* No script, nothing to do */
 | 
			
		||||
		return (0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	(void) nvlist_lookup_string(vdev_nv, ZPOOL_CONFIG_PATH, &path);
 | 
			
		||||
	(void) nvlist_lookup_string(vdev_nv, ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH,
 | 
			
		||||
	    &enc_sysfs_path);
 | 
			
		||||
 | 
			
		||||
	upath = zfs_get_underlying_path(path);
 | 
			
		||||
	pool_name = zhp ? zpool_get_name(zhp) : NULL;
 | 
			
		||||
 | 
			
		||||
	env = zpool_vdev_script_alloc_env(pool_name, path, upath,
 | 
			
		||||
	    enc_sysfs_path, "VDEV_PREPARE", prepare_str);
 | 
			
		||||
 | 
			
		||||
	free(upath);
 | 
			
		||||
 | 
			
		||||
	if (env == NULL) {
 | 
			
		||||
		return (ENOMEM);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = libzfs_run_process_get_stdout(script_path, argv, env, lines,
 | 
			
		||||
	    lines_cnt);
 | 
			
		||||
 | 
			
		||||
	zpool_vdev_script_free_env(env);
 | 
			
		||||
 | 
			
		||||
	return (rc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Optionally run a script and then label a disk.  The script can be used to
 | 
			
		||||
 * prepare a disk for inclusion into the pool.  For example, it might update
 | 
			
		||||
 * the disk's firmware or check its health.
 | 
			
		||||
 *
 | 
			
		||||
 * The 'name' provided is the short name, stripped of any leading
 | 
			
		||||
 * /dev path, and is passed to zpool_label_disk. vdev_nv is the nvlist for
 | 
			
		||||
 * the vdev.  prepare_str is a string that gets passed as the VDEV_PREPARE
 | 
			
		||||
 * env variable to the script.
 | 
			
		||||
 *
 | 
			
		||||
 * The following env vars are passed to the script:
 | 
			
		||||
 *
 | 
			
		||||
 * POOL_NAME:		The pool name (blank during zpool create)
 | 
			
		||||
 * VDEV_PREPARE:	Reason why the disk is being prepared for inclusion:
 | 
			
		||||
 *			"create", "add", "replace", or "autoreplace"
 | 
			
		||||
 * VDEV_PATH:		Path to the disk
 | 
			
		||||
 * VDEV_UPATH:		One of the 'underlying paths' to the disk.  This is
 | 
			
		||||
 * 			useful for DM devices.
 | 
			
		||||
 * VDEV_ENC_SYSFS_PATH:	Path to the disk's enclosure sysfs path, if available.
 | 
			
		||||
 *
 | 
			
		||||
 * Note, some of these values can be blank.
 | 
			
		||||
 *
 | 
			
		||||
 * Return 0 on success, non-zero otherwise.
 | 
			
		||||
 */
 | 
			
		||||
int
 | 
			
		||||
zpool_prepare_and_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp,
 | 
			
		||||
    const char *name, nvlist_t *vdev_nv, const char *prepare_str,
 | 
			
		||||
    char **lines[], int *lines_cnt)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	char vdev_path[MAXPATHLEN];
 | 
			
		||||
	(void) snprintf(vdev_path, sizeof (vdev_path), "%s/%s", DISK_ROOT,
 | 
			
		||||
	    name);
 | 
			
		||||
 | 
			
		||||
	/* zhp will be NULL when creating a pool */
 | 
			
		||||
	rc = zpool_prepare_disk(zhp, vdev_nv, prepare_str, lines, lines_cnt);
 | 
			
		||||
	if (rc != 0)
 | 
			
		||||
		return (rc);
 | 
			
		||||
 | 
			
		||||
	rc = zpool_label_disk(hdl, zhp, name);
 | 
			
		||||
	return (rc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -104,7 +104,8 @@ dist_man_MANS = \
 | 
			
		||||
 | 
			
		||||
nodist_man_MANS = \
 | 
			
		||||
	man8/zed.8 \
 | 
			
		||||
	man8/zfs-mount-generator.8
 | 
			
		||||
	man8/zfs-mount-generator.8 \
 | 
			
		||||
	man8/zfs_prepare_disk.8
 | 
			
		||||
 | 
			
		||||
SUBSTFILES += $(nodist_man_MANS)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								man/man8/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								man/man8/.gitignore
									
									
									
									
										vendored
									
									
								
							@ -1,2 +1,3 @@
 | 
			
		||||
/zed.8
 | 
			
		||||
/zfs-mount-generator.8
 | 
			
		||||
/zfs_prepare_disk.8
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										70
									
								
								man/man8/zfs_prepare_disk.8.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								man/man8/zfs_prepare_disk.8.in
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,70 @@
 | 
			
		||||
.\"
 | 
			
		||||
.\" Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
 | 
			
		||||
.\" Copyright (C) 2023 Lawrence Livermore National Security, LLC.
 | 
			
		||||
.\" Refer to the OpenZFS git commit log for authoritative copyright attribution.
 | 
			
		||||
.\"
 | 
			
		||||
.\" The contents of this file are subject to the terms of the
 | 
			
		||||
.\" Common Development and Distribution License Version 1.0 (CDDL-1.0).
 | 
			
		||||
.\" You can obtain a copy of the license from the top-level file
 | 
			
		||||
.\" "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
 | 
			
		||||
.\" You may not use this file except in compliance with the license.
 | 
			
		||||
.\"
 | 
			
		||||
.\" Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049)
 | 
			
		||||
.\"
 | 
			
		||||
.Dd August 30, 2023
 | 
			
		||||
.Dt ZFS_PREPARE_DISK 8
 | 
			
		||||
.Os
 | 
			
		||||
.
 | 
			
		||||
.Sh NAME
 | 
			
		||||
.Nm zfs_prepare_disk
 | 
			
		||||
.Nd special script that gets run before bringing a disk into a pool
 | 
			
		||||
.Sh DESCRIPTION
 | 
			
		||||
.Nm
 | 
			
		||||
is an optional script that gets called by libzfs before bringing a disk into a
 | 
			
		||||
pool.
 | 
			
		||||
It can be modified by the user to run whatever commands are necessary to prepare
 | 
			
		||||
a disk for inclusion into the pool.
 | 
			
		||||
For example, users can add lines to
 | 
			
		||||
.Nm zfs_prepare_disk
 | 
			
		||||
to do things like update the drive's firmware or check the drive's health.
 | 
			
		||||
.Nm zfs_prepare_disk
 | 
			
		||||
is optional and can be removed if not needed.
 | 
			
		||||
libzfs will look for the script at @zfsexecdir@/zfs_prepare_disk.
 | 
			
		||||
.
 | 
			
		||||
.Ss Properties
 | 
			
		||||
.Nm zfs_prepare_disk
 | 
			
		||||
will be passed the following environment variables:
 | 
			
		||||
.sp
 | 
			
		||||
.Bl -tag -compact -width "VDEV_ENC_SYSFS_PATH"
 | 
			
		||||
.
 | 
			
		||||
.It Nm POOL_NAME
 | 
			
		||||
.No Name of the pool
 | 
			
		||||
.It Nm VDEV_PATH
 | 
			
		||||
.No Path to the disk (like /dev/sda)
 | 
			
		||||
.It Nm VDEV_PREPARE
 | 
			
		||||
.No Reason why the disk is being prepared for inclusion
 | 
			
		||||
('create', 'add', 'replace', or 'autoreplace').
 | 
			
		||||
This can be useful if you only want the script to be run under certain actions.
 | 
			
		||||
.It Nm VDEV_UPATH
 | 
			
		||||
.No Path to one of the underlying devices for the
 | 
			
		||||
disk.
 | 
			
		||||
For multipath this would return one of the /dev/sd* paths to the disk.
 | 
			
		||||
If the device is not a device mapper device, then
 | 
			
		||||
.Nm VDEV_UPATH
 | 
			
		||||
just returns the same value as
 | 
			
		||||
.Nm VDEV_PATH
 | 
			
		||||
.It Nm VDEV_ENC_SYSFS_PATH
 | 
			
		||||
.No Path to the disk's enclosure sysfs path, if available
 | 
			
		||||
.El
 | 
			
		||||
.Pp
 | 
			
		||||
Note that some of these variables may have a blank value.
 | 
			
		||||
.Nm POOL_NAME
 | 
			
		||||
is blank at pool creation time, for example.
 | 
			
		||||
.Sh ENVIRONMENT
 | 
			
		||||
.Nm zfs_prepare_disk
 | 
			
		||||
runs with a limited $PATH.
 | 
			
		||||
.Sh EXIT STATUS
 | 
			
		||||
.Nm zfs_prepare_disk
 | 
			
		||||
should return 0 on success, non-zero otherwise.
 | 
			
		||||
If non-zero is returned, the disk will not be included in the pool.
 | 
			
		||||
.
 | 
			
		||||
@ -9,6 +9,9 @@ dist_pkgdata_SCRIPTS = \
 | 
			
		||||
	zloop.sh \
 | 
			
		||||
	zfs-helpers.sh
 | 
			
		||||
 | 
			
		||||
dist_zfsexec_SCRIPTS = \
 | 
			
		||||
	zfs_prepare_disk
 | 
			
		||||
 | 
			
		||||
EXTRA_SCRIPTS = \
 | 
			
		||||
	commitcheck.sh \
 | 
			
		||||
	common.sh.in \
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										17
									
								
								scripts/zfs_prepare_disk
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										17
									
								
								scripts/zfs_prepare_disk
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
#
 | 
			
		||||
# This is an optional helper script that is automatically called by libzfs
 | 
			
		||||
# before a disk is about to be added into the pool.  It can be modified by
 | 
			
		||||
# the user to run whatever commands are necessary to prepare a disk for
 | 
			
		||||
# inclusion into the pool.  For example, users can add lines to this
 | 
			
		||||
# script to do things like update the drive's firmware or check the drive's
 | 
			
		||||
# health.  The script is optional and can be removed if it is not needed.
 | 
			
		||||
#
 | 
			
		||||
# See the zfs_prepare_disk(8) man page for details.
 | 
			
		||||
#
 | 
			
		||||
# Example:
 | 
			
		||||
#
 | 
			
		||||
# echo "Prepare disk $VDEV_PATH ($VDEV_UPATH) for $VDEV_PREPARE in $POOL_NAME"
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
exit 0
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user