]> git.proxmox.com Git - mirror_zfs.git/commitdiff
Add zfs_prepare_disk script for disk firmware install
authorTony Hutter <hutter2@llnl.gov>
Thu, 21 Sep 2023 15:36:26 +0000 (08:36 -0700)
committerGitHub <noreply@github.com>
Thu, 21 Sep 2023 15:36:26 +0000 (08:36 -0700)
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

14 files changed:
cmd/zed/agents/zfs_mod.c
cmd/zpool/zpool_iter.c
cmd/zpool/zpool_util.h
cmd/zpool/zpool_vdev.c
config/Rules.am
contrib/debian/openzfs-zfsutils.install
include/libzfs.h
lib/libzfs/libzfs.abi
lib/libzfs/libzfs_util.c
man/Makefile.am
man/man8/.gitignore
man/man8/zfs_prepare_disk.8.in [new file with mode: 0644]
scripts/Makefile.am
scripts/zfs_prepare_disk [new file with mode: 0755]

index 2f040ff7582cf53cf5817266397343570ef8c701..b2c008ad1d0eafe9a6d7c4e1b37cea117f7b5d78 100644 (file)
@@ -146,6 +146,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
@@ -200,6 +211,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;
@@ -383,6 +396,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
@@ -405,10 +434,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);
@@ -468,6 +505,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
index 7c6549b0ae546d4b37b9f419de6b804935f8da28..506b529dce486b9660d4e168b6e352793736f4e8 100644 (file)
@@ -443,37 +443,22 @@ vdev_run_cmd(vdev_cmd_data_t *data, char *cmd)
 {
        int rc;
        char *argv[2] = {cmd};
-       char *env[5] = {(char *)"PATH=/bin:/sbin:/usr/bin:/usr/sbin"};
+       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;
 
@@ -485,10 +470,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]);
 }
 
 /*
index b35dea0cd4499b674fb27cb2d5f3ab2b56b316ca..db8e631dc6be89253530197f5dbf20207d45a3df 100644 (file)
@@ -126,6 +126,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);
index 99a521aa2a28775fde59a98848f4e37cff69a7a9..3d0fc089c32fb3082c41af52ac6573e4e0f6a4e9 100644 (file)
@@ -936,6 +936,15 @@ zero_label(const 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
@@ -947,7 +956,7 @@ zero_label(const 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;
@@ -1032,6 +1041,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) {
@@ -1043,9 +1054,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
@@ -1082,19 +1111,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);
@@ -1752,7 +1781,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);
                }
@@ -1873,7 +1902,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);
        }
index abb4ced332335b31a1f5bdccb2f961b244cc2e20..7c266964f3f3cc0864a330a22e3962373f993f43 100644 (file)
@@ -33,6 +33,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)\"
index 301d8f67b3af1cd6a31b31cb6be9011963e36b1f..e2ce5084c09534f4a431bd5fb30e6f8c618f5b65 100644 (file)
@@ -35,6 +35,7 @@ usr/bin/zvol_wait
 usr/lib/modules-load.d/ lib/
 usr/lib/zfs-linux/zpool.d/
 usr/lib/zfs-linux/zpool_influxdb
+usr/lib/zfs-linux/zfs_prepare_disk
 usr/sbin/arc_summary
 usr/sbin/arcstat
 usr/sbin/dbufstat
@@ -88,6 +89,7 @@ usr/share/man/man8/zfs-wait.8
 usr/share/man/man8/zfs-zone.8
 usr/share/man/man8/zfs.8
 usr/share/man/man8/zfs_ids_to_path.8
+usr/share/man/man8/zfs_prepare_disk.8
 usr/share/man/man7/zfsconcepts.7
 usr/share/man/man7/zfsprops.7
 usr/share/man/man8/zgenhostid.8
index fa05b7921bb51a9712ac7c2e8196d69e1b9a64cd..0b5501bbe39f06cd205cf647e98e3221ac6d7793 100644 (file)
@@ -326,6 +326,15 @@ _LIBZFS_H nvlist_t *zpool_find_vdev_by_physpath(zpool_handle_t *, const char *,
     boolean_t *, boolean_t *, boolean_t *);
 _LIBZFS_H int zpool_label_disk(libzfs_handle_t *, zpool_handle_t *,
     const char *);
+_LIBZFS_H int zpool_prepare_disk(zpool_handle_t *zhp, nvlist_t *vdev_nv,
+    const char *prepare_str, char **lines[], int *lines_cnt);
+_LIBZFS_H 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);
+_LIBZFS_H 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);
+_LIBZFS_H void zpool_vdev_script_free_env(char **env);
 _LIBZFS_H uint64_t zpool_vdev_path_to_guid(zpool_handle_t *zhp,
     const char *path);
 
index 0a8e9bcbd74d9a97bc09eace6732f948b12b9a3c..907b0191f75bacb5726615e4a0bbde1a0e3f98b7 100644 (file)
     <elf-symbol name='zpool_open' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
     <elf-symbol name='zpool_open_canfail' 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'/>
     <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'/>
index b94abea3d58157bfd3e4220cee4688695688aa66..fdd1975fa6771f11ee11a2cf76468960ceabcff1 100644 (file)
@@ -2071,3 +2071,196 @@ printf_color(const char *color, const 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;
+       const 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);
+}
index 36c1aede106ec774e6935701db64165476b3b37b..45156571eec33cf47906fbccb66c5ffd65bd5fe6 100644 (file)
@@ -62,6 +62,7 @@ dist_man_MANS = \
        %D%/man8/zfs-userspace.8 \
        %D%/man8/zfs-wait.8 \
        %D%/man8/zfs_ids_to_path.8 \
+       %D%/man8/zfs_prepare_disk.8 \
        %D%/man8/zgenhostid.8 \
        %D%/man8/zinject.8 \
        %D%/man8/zpool.8 \
index f2fc702147e933b75101c0e0619e30b997e3794a..a468f9cbf9d30ea66d589ddc9445188d6312f89a 100644 (file)
@@ -1,2 +1,3 @@
 /zed.8
 /zfs-mount-generator.8
+/zfs_prepare_disk.8
diff --git a/man/man8/zfs_prepare_disk.8.in b/man/man8/zfs_prepare_disk.8.in
new file mode 100644 (file)
index 0000000..2a74153
--- /dev/null
@@ -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.
+.
index 4175d27ea32a2a10da6a6d69e820326f5c0122c9..0754345664379c16bb9a9baa52c2ec9ff96aaa24 100644 (file)
@@ -20,6 +20,8 @@ scripts_scripts = \
 
 if CONFIG_USER
 dist_scripts_SCRIPTS = $(scripts_scripts)
+dist_zfsexec_SCRIPTS = \
+       %D%/zfs_prepare_disk
 else
 dist_noinst_SCRIPTS += $(scripts_scripts)
 endif
diff --git a/scripts/zfs_prepare_disk b/scripts/zfs_prepare_disk
new file mode 100755 (executable)
index 0000000..02aa9f8
--- /dev/null
@@ -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