Whamcloud - gitweb
LU-11077 utils: move get/set/list_param into own file 42/55642/9
authorAndreas Dilger <adilger@whamcloud.com>
Wed, 3 Jul 2024 16:35:44 +0000 (10:35 -0600)
committerOleg Drokin <green@whamcloud.com>
Fri, 16 Aug 2024 23:51:57 +0000 (23:51 +0000)
Move the "lctl get_param/set_param/list_param" handling code into a
separate file so that the functions can be called within the code
elsewhere without the need for all the dependencies.

Remove obsolete "local_cfg" and "set_timeout" sub-commands that
have been obsolete since "set_param" was added.

Signed-off-by: Frederick Dilger <fdilger@whamcloud.com>
Change-Id: If4dc44abb1f5de4a3c24e043ba9a352ef7c0e1ec
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/55642
Reviewed-by: Oleg Drokin <green@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Arshad Hussain <arshad.hussain@aeoncomputing.com>
Reviewed-by: Timothy Day <timday@amazon.com>
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
lustre/utils/Makefile.am
lustre/utils/lctl.c
lustre/utils/lustre_cfg.c
lustre/utils/lustre_param.c [new file with mode: 0644]
lustre/utils/obdctl.h

index 74cabea..52eac88 100644 (file)
@@ -63,8 +63,8 @@ endif
 pkglib_LTLIBRARIES =
 lib_LTLIBRARIES = liblustreapi.la
 
-lctl_SOURCES = portals.c debug.c obd.c lustre_cfg.c lctl.c obdctl.h
-lctl_SOURCES += lctl_thread.c lctl_thread.h
+lctl_SOURCES = lctl.c debug.c lustre_cfg.c lustre_param.c
+lctl_SOURCES += lctl_thread.c lctl_thread.h obd.c obdctl.h portals.c
 if SERVER
 lctl_SOURCES += lustre_lfsck.c lsnapshot.c
 endif
index 0f91894..564acfd 100644 (file)
@@ -173,8 +173,6 @@ command_t cmdlist[] = {
        {"recover", jt_obd_recover, 0,
         "try to restore a lost connection immediately\n"
         "usage: recover [MDC/OSC device]"},
-       {"set_timeout", jt_lcfg_set_timeout, 0,
-        "usage: conf_param obd_timeout=<secs>\n"},
 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(3, 0, 53, 0)
        {"conf_param", jt_lcfg_confparam, 0,
         "set a permanent config parameter.\n"
@@ -182,8 +180,6 @@ command_t cmdlist[] = {
         "usage: conf_param [-d] <target.keyword=val>\n"
         "  -d  Delete the permanent setting from the configuration."},
 #endif
-       {"local_param", jt_lcfg_param, 0, "set a temporary, local param\n"
-        "usage: local_param <target.keyword=val>\n"},
        {"get_param", jt_lcfg_getparam, 0, "get the Lustre or LNET parameter\n"
         "usage: get_param [--classify|-F] [--header|-H] [--links|-l]\n"
         "                 [--no-links|-L] [--no-name|-n] [--only-name|-N]\n"
index a2fda2d..fc53e5f 100644 (file)
@@ -330,39 +330,6 @@ int jt_lcfg_del_mount_option(int argc, char **argv)
        return jt_lcfg_ioctl(&bufs, argv[0], LCFG_DEL_MOUNTOPT);
 }
 
-int jt_lcfg_set_timeout(int argc, char **argv)
-{
-       int rc;
-       struct lustre_cfg_bufs bufs;
-       struct lustre_cfg *lcfg;
-
-       fprintf(stderr,
-               "%s has been deprecated. Use conf_param instead.\ne.g. conf_param lustre-MDT0000 obd_timeout=50\n",
-               jt_cmdname(argv[0]));
-       return CMD_HELP;
-
-       if (argc != 2)
-               return CMD_HELP;
-
-       lustre_cfg_bufs_reset(&bufs, lcfg_devname);
-
-       lcfg = malloc(lustre_cfg_len(bufs.lcfg_bufcount, bufs.lcfg_buflen));
-       if (!lcfg) {
-               rc = -ENOMEM;
-       } else {
-               lustre_cfg_init(lcfg, LCFG_SET_TIMEOUT, &bufs);
-               lcfg->lcfg_num = atoi(argv[1]);
-
-               rc = lcfg_ioctl(argv[0], OBD_DEV_ID, lcfg);
-               free(lcfg);
-       }
-       if (rc < 0) {
-               fprintf(stderr, "error: %s: %s\n", jt_cmdname(argv[0]),
-                       strerror(rc = errno));
-       }
-       return rc;
-}
-
 int jt_lcfg_add_conn(int argc, char **argv)
 {
        struct lustre_cfg_bufs bufs;
@@ -494,7 +461,7 @@ static int lcfg_setparam_perm(char *func, char *buf)
  * This should be loaded after the individual config logs.
  * Called from set param with -P option.
  */
-static int jt_lcfg_setparam_perm(int argc, char **argv,
+int jt_lcfg_setparam_perm(int argc, char **argv,
                                 struct param_opts *popt)
 {
        int rc;
@@ -621,289 +588,6 @@ out:
        return rc;
 }
 
-/**
- * Display a parameter path in the same format as sysctl.
- * E.g. obdfilter.lustre-OST0000.stats
- *
- * \param[in] filename file name of the parameter
- * \param[in] st       parameter file stats
- * \param[in] popt     set/get param options
- *
- * \retval allocated pointer containing modified filename
- */
-static char *
-display_name(const char *filename, struct stat *st, struct param_opts *popt)
-{
-       size_t suffix_len = 0;
-       char *suffix = NULL;
-       char *param_name;
-       char *tmp;
-
-       if (popt->po_show_type) {
-               if (S_ISDIR(st->st_mode))
-                       suffix = "/";
-               else if (S_ISLNK(st->st_mode))
-                       suffix = "@";
-               else if (st->st_mode & S_IWUSR)
-                       suffix = "=";
-       }
-
-       /* Take the original filename string and chop off the glob addition */
-       tmp = strstr(filename, "/lustre/");
-       if (!tmp) {
-               tmp = strstr(filename, "/lnet/");
-               if (tmp)
-                       tmp += strlen("/lnet/");
-       } else {
-               tmp += strlen("/lustre/");
-       }
-
-       /* Allocate return string */
-       param_name = strdup(tmp);
-       if (!param_name)
-               return NULL;
-
-       /* replace '/' with '.' to match conf_param and sysctl */
-       for (tmp = strchr(param_name, '/'); tmp != NULL; tmp = strchr(tmp, '/'))
-               *tmp = '.';
-
-       /* Append the indicator to entries if needed. */
-       if (popt->po_show_type && suffix != NULL) {
-               suffix_len = strlen(suffix);
-
-               tmp = realloc(param_name, suffix_len + strlen(param_name) + 1);
-               if (tmp) {
-                       param_name = tmp;
-                       strncat(param_name, suffix,
-                               strlen(param_name) + suffix_len);
-               }
-       }
-
-       return param_name;
-}
-
-/**
- * Turns a lctl parameter string into a procfs/sysfs subdirectory path pattern.
- *
- * \param[in] popt             Used to control parameter usage. For this
- *                             function it is used to see if the path has
- *                             a added suffix.
- * \param[in,out] path         lctl parameter string that is turned into
- *                             the subdirectory path pattern that is used
- *                             to search the procfs/sysfs tree.
- *
- * \retval -errno on error.
- */
-static int
-clean_path(struct param_opts *popt, char *path)
-{
-       char *nidstart = NULL;
-       char *nidend = NULL;
-       char *tmp;
-
-       if (popt == NULL || path == NULL || strlen(path) == 0)
-               return -EINVAL;
-
-       /* If path contains a suffix we need to remove it */
-       if (popt->po_show_type) {
-               size_t path_end = strlen(path) - 1;
-
-               tmp = path + path_end;
-               switch (*tmp) {
-               case '@':
-               case '=':
-               case '/':
-                       *tmp = '\0';
-               default:
-                       break;
-               }
-       }
-
-       /* get rid of '\', glob doesn't like it */
-       tmp = strrchr(path, '\\');
-       if (tmp) {
-               char *tail = path + strlen(path);
-
-               while (tmp != path) {
-                       if (*tmp == '\\') {
-                               memmove(tmp, tmp + 1, tail - tmp);
-                               --tail;
-                       }
-                       --tmp;
-               }
-       }
-
-       /* Does path contain a NID string?  Skip '.->/' replacement for it. */
-       tmp = strchr(path, '@');
-       if (tmp) {
-               /* First find the NID start.  NIDs may have variable (0-4) '.',
-                * so find the common NID prefixes instead of trying to count
-                * the dots.  Not great, but there are only two, and faster
-                * than multiple speculative NID parses and bad DNS lookups.
-                */
-               if ((tmp = strstr(path, ".exports.")))
-                       nidstart = tmp + strlen(".exports.");
-               else if ((tmp = strstr(path, ".MGC")))
-                       nidstart = tmp + 1;
-
-               /* Next, find the end of the NID string. */
-               if (nidstart)
-                       nidend = strchrnul(strchr(nidstart, '@'), '.');
-       }
-
-       /* replace param '.' with '/' */
-       for (tmp = strchr(path, '.'); tmp != NULL; tmp = strchr(tmp, '.')) {
-               *tmp++ = '/';
-
-               /*
-                * There exist cases where some of the subdirectories of the
-                * the parameter tree has embedded in its name a NID string.
-                * This means that it is possible that these subdirectories
-                * could have actual '.' in its name. If this is the case we
-                * don't want to blindly replace the '.' with '/', so skip
-                * over the part of the parameter containing the NID.
-                */
-               if (tmp == nidstart)
-                       tmp = nidend;
-       }
-
-       return 0;
-}
-
-/**
- * The application lctl can perform three operations for lustre
- * tunables. This enum defines those four operations which are
- *
- * 1) LIST_PARAM       - list available tunables
- * 2) GET_PARAM                - report the current setting of a tunable
- * 3) SET_PARAM                - set the tunable to a new value
- * 4) LIST_PATHNAME    - list paths of available tunables
- */
-enum parameter_operation {
-       LIST_PARAM,
-       GET_PARAM,
-       SET_PARAM,
-       LIST_PATHNAME,
-};
-
-char *parameter_opname[] = {
-       [LIST_PARAM] = "list_param",
-       [GET_PARAM] = "get_param",
-       [SET_PARAM] = "set_param",
-       [LIST_PATHNAME] = "list_pathname",
-};
-
-/**
- * Read the value of parameter
- *
- * \param[in]  path            full path to the parameter
- * \param[in]  param_name      lctl parameter format of the
- *                             parameter path
- * \param[in]  popt            set/get param options
- *
- * \retval 0 on success.
- * \retval -errno on error.
- */
-static int
-read_param(const char *path, const char *param_name, struct param_opts *popt)
-{
-       int rc = 0;
-       char *buf = NULL;
-       size_t buflen;
-
-       rc = llapi_param_get_value(path, &buf, &buflen);
-       if (rc != 0) {
-               fprintf(stderr,
-                       "error: read_param: \'%s\': %s\n",
-                       path, strerror(-rc));
-               goto free_buf;
-       }
-       /* don't print anything for empty files */
-       if (buf[0] == '\0') {
-               if (popt->po_header)
-                       printf("%s=\n", param_name);
-               goto free_buf;
-       }
-
-       if (popt->po_header) {
-               char *oldbuf = buf;
-               char *next;
-
-               do {
-                       /* Split at first \n, if any */
-                       next = strchrnul(oldbuf, '\n');
-
-                       printf("%s=%.*s\n", param_name, (int)(next - oldbuf),
-                              oldbuf);
-
-                       buflen -= next - oldbuf + 1;
-                       oldbuf = next + 1;
-
-               } while (buflen > 0);
-
-       } else if (popt->po_show_name) {
-               bool multilines = memchr(buf, '\n', buflen - 1);
-
-               printf("%s=%s%s", param_name, multilines ? "\n" : "", buf);
-       } else {
-               printf("%s", buf);
-       }
-
-free_buf:
-       free(buf);
-       return rc;
-}
-
-/**
- * Set a parameter to a specified value
- *
- * \param[in] path             full path to the parameter
- * \param[in] param_name       lctl parameter format of the parameter path
- * \param[in] popt             set/get param options
- * \param[in] value            value to set the parameter to
- *
- * \retval number of bytes written on success.
- * \retval -errno on error.
- */
-int
-write_param(const char *path, const char *param_name, struct param_opts *popt,
-           const char *value)
-{
-       int fd, rc = 0;
-       ssize_t count;
-
-       if (!value)
-               return -EINVAL;
-
-       /* Write the new value to the file */
-       fd = open(path, O_WRONLY);
-       if (fd < 0) {
-               rc = -errno;
-               fprintf(stderr, "error: set_param: opening '%s': %s\n",
-                       path, strerror(errno));
-               return rc;
-       }
-
-       count = write(fd, value, strlen(value));
-       if (count < 0) {
-               rc = -errno;
-               if (errno != EIO) {
-                       fprintf(stderr, "error: set_param: setting %s=%s: %s\n",
-                               path, value, strerror(errno));
-               }
-       } else if (count < strlen(value)) { /* Truncate case */
-               rc = -EINVAL;
-               fprintf(stderr,
-                       "error: set_param: setting %s=%s: wrote only %zd\n",
-                       path, value, count);
-       } else if (popt->po_show_name) {
-               printf("%s=%s\n", param_name, value);
-       }
-       close(fd);
-
-       return rc;
-}
-
 static void print_obd_line(char *s)
 {
        const char *param = "osc/%s/ost_conn_uuid";
@@ -1102,435 +786,6 @@ error:
        return rc;
 }
 
-
-/**
- * Perform a read, write or just a listing of a parameter
- *
- * \param[in] popt     list,set,get parameter options
- * \param[in] pattern  search filter for the path of the parameter
- * \param[in] value    value to set the parameter if write operation
- * \param[in] oper     what operation to perform with the parameter
- * \param[out] wq      the work queue to which work items will be added or NULL
- *                     if not in parallel
- *
- * \retval number of bytes written on success.
- * \retval -errno on error and prints error message.
- */
-static int
-do_param_op(struct param_opts *popt, char *pattern, char *value,
-           enum parameter_operation oper, struct sp_workq *wq)
-{
-       int dup_count = 0;
-       char **dup_cache;
-       glob_t paths;
-       char *opname = parameter_opname[oper];
-       int rc, i;
-
-       if (!wq && popt_is_parallel(*popt))
-               return -EINVAL;
-
-       rc = llapi_param_get_paths(pattern, &paths);
-       if (rc) {
-               rc = -errno;
-               if (!popt->po_recursive && !(rc == -ENOENT && getuid() != 0)) {
-                       fprintf(stderr, "error: %s: param_path '%s': %s\n",
-                               opname, pattern, strerror(errno));
-               }
-               return rc;
-       }
-
-       if (popt_is_parallel(*popt) && paths.gl_pathc > 1) {
-               /* Allocate space for the glob paths in advance. */
-               rc = spwq_expand(wq, paths.gl_pathc);
-               if (rc < 0)
-                       goto out_param;
-       }
-
-       dup_cache = calloc(paths.gl_pathc, sizeof(char *));
-       if (!dup_cache) {
-               rc = -ENOMEM;
-               fprintf(stderr,
-                       "error: %s: allocating '%s' dup_cache[%zd]: %s\n",
-                       opname, pattern, paths.gl_pathc, strerror(-rc));
-               goto out_param;
-       }
-
-       for (i = 0; i < paths.gl_pathc; i++) {
-               char *param_name = NULL, *tmp;
-               char pathname[PATH_MAX], param_dir[PATH_MAX + 2];
-               struct stat st;
-               int rc2, j;
-
-               if (!popt->po_follow_symlinks)
-                       rc2 = lstat(paths.gl_pathv[i], &st);
-               else
-                       rc2 = stat(paths.gl_pathv[i], &st);
-
-               if (rc2 == -1) {
-                       fprintf(stderr, "error: %s: stat '%s': %s\n",
-                               opname, paths.gl_pathv[i], strerror(errno));
-                       if (!rc)
-                               rc = -errno;
-                       continue;
-               }
-
-               if (S_ISLNK(st.st_mode) && !popt->po_follow_symlinks)
-                       continue;
-               if (popt->po_only_dir && !S_ISDIR(st.st_mode))
-                       continue;
-
-               param_name = display_name(paths.gl_pathv[i], &st, popt);
-               if (!param_name) {
-                       fprintf(stderr,
-                               "error: %s: generating name for '%s': %s\n",
-                               opname, paths.gl_pathv[i], strerror(ENOMEM));
-                       if (!rc)
-                               rc = -ENOMEM;
-                       continue;
-               }
-
-               switch (oper) {
-               case GET_PARAM:
-                       /* Read the contents of file to stdout */
-                       if (S_ISREG(st.st_mode)) {
-                               rc2 = read_param(paths.gl_pathv[i], param_name,
-                                                popt);
-                               if (rc2 < 0 && !rc)
-                                       rc = rc2;
-                       }
-                       break;
-               case SET_PARAM:
-                       if (S_ISREG(st.st_mode)) {
-                               if (popt_is_parallel(*popt))
-                                       rc2 = spwq_add_item(wq,
-                                                           paths.gl_pathv[i],
-                                                           param_name, value);
-                               else
-                                       rc2 = write_param(paths.gl_pathv[i],
-                                                         param_name, popt,
-                                                         value);
-
-                               if (rc2 < 0 && !rc)
-                                       rc = rc2;
-                       }
-                       break;
-               case LIST_PARAM:
-                       /**
-                        * For the upstream client the parameter files locations
-                        * are split between under both /sys/kernel/debug/lustre
-                        * and /sys/fs/lustre. The parameter files containing
-                        * small amounts of data, less than a page in size, are
-                        * located under /sys/fs/lustre and in the case of large
-                        * parameter data files, think stats for example, are
-                        * located in the debugfs tree. Since the files are
-                        * split across two trees the directories are often
-                        * duplicated which means these directories are listed
-                        * twice which leads to duplicate output to the user.
-                        * To avoid scanning a directory twice we have to cache
-                        * any directory and check if a search has been
-                        * requested twice.
-                        */
-                       for (j = 0; j < dup_count; j++) {
-                               if (!strcmp(dup_cache[j], param_name))
-                                       break;
-                       }
-                       if (j != dup_count) {
-                               free(param_name);
-                               param_name = NULL;
-                               continue;
-                       }
-                       dup_cache[dup_count++] = strdup(param_name);
-
-                       if (popt->po_show_name)
-                               printf("%s\n", param_name);
-                       break;
-               case LIST_PATHNAME:
-                       for (j = 0; j < dup_count; j++) {
-                               if (!strcmp(dup_cache[j], param_name))
-                                       break;
-                       }
-                       if (j != dup_count) {
-                               free(param_name);
-                               param_name = NULL;
-                               continue;
-                       }
-                       dup_cache[dup_count++] = strdup(param_name);
-                       if (popt->po_show_name)
-                               printf("%s\n", paths.gl_pathv[i]);
-                       break;
-               }
-
-               /*
-                * Only directories are searched recursively if
-                * requested by the user
-                */
-               if (!S_ISDIR(st.st_mode) || !popt->po_recursive) {
-                       free(param_name);
-                       param_name = NULL;
-                       continue;
-               }
-
-               /* Turn param_name into file path format */
-               rc2 = clean_path(popt, param_name);
-               if (rc2 < 0) {
-                       fprintf(stderr, "error: %s: cleaning '%s': %s\n",
-                               opname, param_name, strerror(-rc2));
-                       free(param_name);
-                       param_name = NULL;
-                       if (!rc)
-                               rc = rc2;
-                       continue;
-               }
-
-               /* Use param_name to grab subdirectory tree from full path */
-               snprintf(param_dir, sizeof(param_dir), "/%s", param_name);
-               tmp = strstr(paths.gl_pathv[i], param_dir);
-
-               /* cleanup paramname now that we are done with it */
-               free(param_name);
-               param_name = NULL;
-               memset(&param_dir, '\0', sizeof(param_dir));
-
-               /* Shouldn't happen but just in case */
-               if (!tmp) {
-                       if (!rc)
-                               rc = -EINVAL;
-                       continue;
-               }
-               tmp++;
-
-               rc2 = snprintf(pathname, sizeof(pathname), "%s/*", tmp);
-               if (rc2 < 0) {
-                       /*
-                        * snprintf() should never an error, and if it does
-                        * there isn't much point trying to use fprintf()
-                        */
-                       continue;
-               }
-               if (rc2 >= sizeof(pathname)) {
-                       fprintf(stderr, "error: %s: overflow processing '%s'\n",
-                               opname, pathname);
-                       if (!rc)
-                               rc = -EINVAL;
-                       continue;
-               }
-
-               rc2 = do_param_op(popt, pathname, value, oper, wq);
-               if (!rc2 && rc2 != -ENOENT) {
-                       /* errors will be printed by do_param_op() */
-                       if (!rc)
-                               rc = rc2;
-                       continue;
-               }
-       }
-
-       for (i = 0; i < dup_count; i++)
-               free(dup_cache[i]);
-       free(dup_cache);
-out_param:
-       llapi_param_paths_free(&paths);
-       return rc;
-}
-
-static int listparam_cmdline(int argc, char **argv, struct param_opts *popt)
-{
-       struct option long_opts[] = {
-       { .val = 'D',   .name = "dir-only",     .has_arg = no_argument},
-       { .val = 'D',   .name = "directory-only", .has_arg = no_argument},
-       { .val = 'F',   .name = "classify",     .has_arg = no_argument},
-       { .val = 'l',   .name = "links",        .has_arg = no_argument},
-       { .val = 'L',   .name = "no-links",     .has_arg = no_argument},
-       { .val = 'R',   .name = "recursive",    .has_arg = no_argument},
-       };
-
-       int ch;
-
-       popt->po_show_name = 1;
-       popt->po_only_name = 1;
-       popt->po_follow_symlinks = 1;
-
-       while ((ch = getopt_long(argc, argv, "DFlLpR",
-                                     long_opts, NULL)) != -1) {
-               switch (ch) {
-               case 'D':
-                       popt->po_only_dir = 1;
-                       break;
-               case 'F':
-                       popt->po_show_type = 1;
-                       break;
-               case 'l':
-                       popt->po_follow_symlinks = 1;
-                       break;
-               case 'L':
-                       popt->po_follow_symlinks = 0;
-                       break;
-               case 'p':
-                       popt->po_only_pathname = 1;
-                       break;
-               case 'R':
-                       popt->po_recursive = 1;
-                       break;
-               default:
-                       return -1;
-               }
-       }
-
-       return optind;
-}
-
-int jt_lcfg_listparam(int argc, char **argv)
-{
-       int rc = 0, index, i;
-       struct param_opts popt;
-       char *path;
-
-       memset(&popt, 0, sizeof(popt));
-       index = listparam_cmdline(argc, argv, &popt);
-       if (index < 0 || index >= argc)
-               return CMD_HELP;
-
-       for (i = index; i < argc; i++) {
-               int rc2;
-
-               path = argv[i];
-
-               rc2 = clean_path(&popt, path);
-               if (rc2 < 0) {
-                       fprintf(stderr, "error: %s: cleaning '%s': %s\n",
-                               jt_cmdname(argv[0]), path, strerror(-rc2));
-                       if (rc == 0)
-                               rc = rc2;
-                       continue;
-               }
-
-               rc2 = do_param_op(&popt, path, NULL, popt.po_only_pathname ?
-                                 LIST_PATHNAME : LIST_PARAM, NULL);
-               if (rc2 < 0) {
-                       if (rc == 0)
-                               rc = rc2;
-
-                       if (rc2 == -ENOENT && getuid() != 0)
-                               rc2 = llapi_param_display_value(path, 0, 0,
-                                                               stdout);
-                       if (rc2 < 0) {
-                               fprintf(stderr, "error: %s: listing '%s': %s\n",
-                                       jt_cmdname(argv[0]), path,
-                                       strerror(-rc2));
-                       }
-                       continue;
-               }
-       }
-
-       return rc;
-}
-
-static int getparam_cmdline(int argc, char **argv, struct param_opts *popt)
-{
-       struct option long_opts[] = {
-       { .val = 'F',   .name = "classify",     .has_arg = no_argument},
-       { .val = 'H',   .name = "header",       .has_arg = no_argument},
-       { .val = 'l',   .name = "links",        .has_arg = no_argument},
-       { .val = 'L',   .name = "no-links",     .has_arg = no_argument},
-       { .val = 'n',   .name = "no-name",      .has_arg = no_argument},
-       { .val = 'N',   .name = "only-name",    .has_arg = no_argument},
-       { .val = 'R',   .name = "recursive",    .has_arg = no_argument},
-       { .val = 'y',   .name = "yaml",         .has_arg = no_argument},
-       };
-
-       int ch;
-
-       popt->po_show_name = 1;
-       popt->po_follow_symlinks = 1;
-
-       while ((ch = getopt_long(argc, argv, "FHlLnNRy",
-                                     long_opts, NULL)) != -1) {
-               switch (ch) {
-               case 'F':
-                       popt->po_show_type = 1;
-                       break;
-               case 'H':
-                       popt->po_header = 1;
-                       break;
-               case 'l':
-                       popt->po_follow_symlinks = 1;
-                       break;
-               case 'L':
-                       popt->po_follow_symlinks = 0;
-                       break;
-               case 'n':
-                       popt->po_show_name = 0;
-                       break;
-               case 'N':
-                       popt->po_only_name = 1;
-                       break;
-               case 'R':
-                       popt->po_recursive = 1;
-                       break;
-               case 'y':
-                       popt->po_yaml = 1;
-                       break;
-               default:
-                       return -1;
-               }
-       }
-
-       return optind;
-}
-
-int jt_lcfg_getparam(int argc, char **argv)
-{
-       int version = LUSTRE_GENL_VERSION;
-       enum parameter_operation mode;
-       int rc = 0, index, i;
-       struct param_opts popt;
-       int flags = 0;
-       char *path;
-
-       memset(&popt, 0, sizeof(popt));
-       index = getparam_cmdline(argc, argv, &popt);
-       if (index < 0 || index >= argc)
-               return CMD_HELP;
-
-       mode = popt.po_only_name ? LIST_PARAM : GET_PARAM;
-       if (mode == LIST_PARAM)
-               version = 0;
-
-       if (popt.po_yaml)
-               flags |= PARAM_FLAGS_YAML_FORMAT;
-       if (popt.po_show_name)
-               flags |= PARAM_FLAGS_SHOW_SOURCE;
-
-       for (i = index; i < argc; i++) {
-               int rc2;
-
-               path = argv[i];
-
-               rc2 = clean_path(&popt, path);
-               if (rc2 < 0) {
-                       fprintf(stderr, "error: %s: cleaning '%s': %s\n",
-                               jt_cmdname(argv[0]), path, strerror(-rc2));
-                       if (rc == 0)
-                               rc = rc2;
-                       continue;
-               }
-
-               rc2 = do_param_op(&popt, path, NULL,
-                                 popt.po_only_name ? LIST_PARAM : GET_PARAM,
-                                 NULL);
-               if (rc2 < 0) {
-                       if (rc == 0)
-                               rc = rc2;
-
-                       if (rc2 == -ENOENT && getuid() != 0)
-                               rc2 = llapi_param_display_value(path, version,
-                                                               flags, stdout);
-                       continue;
-               }
-       }
-
-       return rc;
-}
-
 /* get device list by netlink or debugfs */
 int jt_device_list(int argc, char **argv)
 {
@@ -1663,6 +918,12 @@ int parse_devname(char *func, char *name, int dev_id)
        return rc;
 }
 
+enum paramtype {
+       PT_NONE = 0,
+       PT_SETPARAM,
+       PT_CONFPARAM
+};
+
 #ifdef HAVE_SERVER_SUPPORT
 /**
  * Output information about nodemaps.
@@ -1691,14 +952,17 @@ int jt_nodemap_info(int argc, char **argv)
 
        if (argc == 1 || strcmp("list", argv[1]) == 0) {
                popt.po_only_dir = 1;
-               rc = do_param_op(&popt, "nodemap/*", NULL, LIST_PARAM, NULL);
+               rc = jt_lcfg_listparam(3, (char*[3])
+                                      { "list_param", "-D", "nodemap/*" });
        } else if (strcmp("all", argv[1]) == 0) {
-               rc = do_param_op(&popt, "nodemap/*/*", NULL, GET_PARAM, NULL);
+               rc = jt_lcfg_getparam(3, (char*[3])
+                                     { "get_param", "-N", "nodemap/*/*" });
        } else {
                char    pattern[PATH_MAX];
 
                snprintf(pattern, sizeof(pattern), "nodemap/%s/*", argv[1]);
-               rc = do_param_op(&popt, pattern, NULL, GET_PARAM, NULL);
+               rc = jt_lcfg_getparam(3, (char*[3])
+                                     { "get_param", "-N", pattern });
                if (rc == -ESRCH)
                        fprintf(stderr,
                                "error: nodemap_info: cannot find nodemap %s\n",
@@ -1708,136 +972,6 @@ int jt_nodemap_info(int argc, char **argv)
 }
 #endif
 
-/**
- * Parses the command-line options to set_param.
- *
- * \param[in] argc     count of arguments given to set_param
- * \param[in] argv     array of arguments given to set_param
- * \param[out] popt    where set_param options will be saved
- *
- * \retval index in argv of the first non-option argv element (optind value)
- */
-static int setparam_cmdline(int argc, char **argv, struct param_opts *popt)
-{
-       struct option long_opts[] = {
-       { .val = 'd',   .name = "delete",       .has_arg = no_argument},
-       { .val = 'F',   .name = "file",         .has_arg = no_argument},
-       { .val = 'n',   .name = "no-name",      .has_arg = no_argument},
-       { .val = 'P',   .name = "perm",         .has_arg = no_argument},
-       { .val = 'P',   .name = "permanent",    .has_arg = no_argument},
-       { .val = 't',   .name = "thread",       .has_arg = optional_argument},
-       };
-
-       int ch;
-
-       popt->po_show_name = 1;
-       popt->po_only_name = 0;
-       popt->po_show_type = 0;
-       popt->po_recursive = 0;
-       popt->po_perm = 0;
-       popt->po_delete = 0;
-       popt->po_file = 0;
-       popt->po_parallel_threads = 0;
-       popt->po_follow_symlinks = 1;
-       opterr = 0;
-
-       while ((ch = getopt_long(argc, argv, "dFnPt::",
-                           long_opts, NULL)) != -1) {
-               switch (ch) {
-               case 'n':
-                       popt->po_show_name = 0;
-                       break;
-               case 't':
-#if HAVE_LIBPTHREAD
-                       if (optarg)
-                               popt->po_parallel_threads = atoi(optarg);
-                       else
-                               popt->po_parallel_threads = LCFG_THREADS_DEF;
-                       if (popt->po_parallel_threads < 2)
-                               return -EINVAL;
-                       break;
-#else
-                       {
-                       static bool printed;
-
-                       if (!printed) {
-                               printed = true;
-                               fprintf(stderr,
-                                       "warning: set_param: no pthread support, proceeding serially.\n");
-                       }
-                       }
-#endif
-                       break;
-               case 'P':
-                       popt->po_perm = 1;
-                       break;
-               case 'd':
-                       popt->po_delete = 1;
-                       break;
-               case 'F':
-                       popt->po_file = 1;
-                       break;
-               default:
-                       return -1;
-               }
-       }
-       if (popt->po_perm && popt->po_file) {
-               fprintf(stderr, "warning: ignoring -P option\n");
-               popt->po_perm = 0;
-       }
-       if (popt->po_delete && !popt->po_perm)
-               popt->po_perm = 1;
-       return optind;
-}
-
-/**
- * Parse the arguments to set_param and return the first parameter and value
- * pair and the number of arguments consumed.
- *
- * \param[in] argc   number of arguments remaining in argv
- * \param[in] argv   list of param-value arguments to set_param (this function
- *                   will modify the strings by overwriting '=' with '\0')
- * \param[out] param the parameter name
- * \param[out] value the parameter value
- *
- * \retval the number of args consumed from argv (1 for "param=value" format, 2
- *         for "param value" format)
- * \retval -errno if unsuccessful
- */
-static int sp_parse_param_value(int argc, char **argv, char **param,
-                               char **value)
-{
-       char *tmp;
-
-       if (argc < 1 || !(argv && param && value))
-               return -EINVAL;
-
-       *param = argv[0];
-       tmp = strchr(*param, '=');
-       if (tmp) {
-               /* format: set_param a=b */
-               *tmp = '\0';
-               tmp++;
-               if (*tmp == '\0')
-                       return -EINVAL;
-               *value = tmp;
-               return 1;
-       }
-
-       /* format: set_param a b */
-       if (argc < 2)
-               return -EINVAL;
-       *value = argv[1];
-
-       return 2;
-}
-
-enum paramtype {
-       PT_NONE = 0,
-       PT_SETPARAM,
-       PT_CONFPARAM
-};
-
 #define PS_NONE 0
 #define PS_PARAM_FOUND 1
 #define PS_PARAM_SET 2
@@ -2064,114 +1198,6 @@ out_open:
 
 int jt_lcfg_applyyaml(int argc, char **argv)
 {
-       int index;
-       struct param_opts popt = {0};
-
-       index = setparam_cmdline(argc, argv, &popt);
-       if (index < 0 || index >= argc)
-               return CMD_HELP;
-
-       return lcfg_apply_param_yaml(argv[0], argv[index]);
-}
-
-/**
- * Main set_param function.
- *
- * \param[in] argc     count of arguments given to set_param
- * \param[in] argv     array of arguments given to set_param
- *
- * \retval 0 if successful
- * \retval -errno if unsuccessful
- */
-int jt_lcfg_setparam(int argc, char **argv)
-{
-       int rc = 0;
-       int index = 0;
-       struct param_opts popt;
-       struct sp_workq wq;
-       struct sp_workq *wq_ptr = NULL;
-
-       memset(&popt, 0, sizeof(popt));
-       index = setparam_cmdline(argc, argv, &popt);
-       if (index < 0 || index >= argc)
-               return CMD_HELP;
-
-       if (popt.po_perm)
-               /*
-                * We can't delete parameters that were
-                * set with old conf_param interface
-                */
-               return jt_lcfg_setparam_perm(argc, argv, &popt);
-
-       if (popt.po_file) {
-#if LUSTRE_VERSION >= OBD_OCD_VERSION(2,17,0,0)
-               fprintf(stderr, "warning: 'lctl set_param -F' is deprecated, use 'lctl apply_yaml' instead\n");
-               return -EINVAL; 
-#else
-               printf("This option left for backward compatibility, please use 'lctl apply_yaml' instead\n");
-               return lcfg_apply_param_yaml(argv[0], argv[index]);
-#endif
-       }
-
-       if (popt_is_parallel(popt)) {
-               rc = spwq_init(&wq, &popt);
-               if (rc < 0) {
-                       fprintf(stderr,
-                               "warning: parallel %s: failed to init work queue: %s. Proceeding serially.\n",
-                               jt_cmdname(argv[0]), strerror(-rc));
-                       rc = 0;
-                       popt.po_parallel_threads = 0;
-               } else {
-                       wq_ptr = &wq;
-               }
-       }
-
-       while (index < argc) {
-               char *path = NULL;
-               char *value = NULL;
-
-               rc = sp_parse_param_value(argc - index, argv + index,
-                                         &path, &value);
-               if (rc < 0) {
-                       fprintf(stderr, "error: %s: setting %s: %s\n",
-                               jt_cmdname(argv[0]), path, strerror(-rc));
-                       break;
-               }
-               /* Increment index by the number of arguments consumed. */
-               index += rc;
-
-               rc = clean_path(&popt, path);
-               if (rc < 0)
-                       break;
-
-               rc = do_param_op(&popt, path, value, SET_PARAM, wq_ptr);
-               if (rc < 0)
-                       fprintf(stderr, "error: %s: setting '%s'='%s': %s\n",
-                               jt_cmdname(argv[0]), path, value,
-                               strerror(-rc));
-       }
-
-       if (popt_is_parallel(popt)) {
-               int rc2;
-               /* Spawn threads to set the parameters which made it into the
-                * work queue to emulate serial set_param behavior when errors
-                * are encountered above.
-                */
-               rc2 = sp_run_threads(&wq);
-               if (rc2 < 0) {
-                       fprintf(stderr,
-                               "error: parallel %s: failed to run threads: %s\n",
-                               jt_cmdname(argv[0]), strerror(-rc2));
-                       if (!rc)
-                               rc = rc2;
-               }
-               rc2 = spwq_destroy(&wq);
-               if (rc2 < 0) {
-                       fprintf(stderr,
-                               "warning: parallel %s: failed to cleanup work queue: %s\n",
-                               jt_cmdname(argv[0]), strerror(-rc2));
-               }
-       }
-
-       return rc;
+       /* the file should be the last argument */
+       return lcfg_apply_param_yaml(argv[0], argv[argc - 1]);
 }
diff --git a/lustre/utils/lustre_param.c b/lustre/utils/lustre_param.c
new file mode 100644 (file)
index 0000000..758baf9
--- /dev/null
@@ -0,0 +1,990 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2016, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ *
+ * lustre/utils/lustre_param.c
+ *
+ * Author: Peter J. Braam <braam@clusterfs.com>
+ * Author: Phil Schwan <phil@clusterfs.com>
+ * Author: Andreas Dilger <adilger@clusterfs.com>
+ * Author: Robert Read <rread@clusterfs.com>
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <libcfs/util/ioctl.h>
+#include <libcfs/util/string.h>
+#include <libcfs/util/param.h>
+#include <libcfs/util/parser.h>
+#include <lustre/lustreapi.h>
+#include <linux/lnet/nidstr.h>
+#include <linux/lustre/lustre_cfg.h>
+#include <linux/lustre/lustre_ioctl.h>
+#include <linux/lustre/lustre_ver.h>
+
+#include "lctl_thread.h"
+#include "lustreapi_internal.h"
+
+#include <sys/un.h>
+#include <time.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <string.h>
+
+#include "obdctl.h"
+#include <stdio.h>
+#include <yaml.h>
+
+static int sp_parse_param_value(int argc, char **argv, char **param,
+                               char **value)
+{
+       char *tmp;
+
+       if (argc < 1 || !(argv && param && value))
+               return -EINVAL;
+
+       *param = argv[0];
+       tmp = strchr(*param, '=');
+       if (tmp) {
+               /* format: set_param a=b */
+               *tmp = '\0';
+               tmp++;
+               if (*tmp == '\0')
+                       return -EINVAL;
+               *value = tmp;
+               return 1;
+       }
+
+       /* format: set_param a b */
+       if (argc < 2)
+               return -EINVAL;
+       *value = argv[1];
+
+       return 2;
+}
+
+/**
+ * Display a parameter path in the same format as sysctl.
+ * E.g. obdfilter.lustre-OST0000.stats
+ *
+ * \param[in] filename file name of the parameter
+ * \param[in] st       parameter file stats
+ * \param[in] popt     set/get param options
+ *
+ * \retval allocated pointer containing modified filename
+ */
+static char *display_name(const char *filename, struct stat *st,
+                         struct param_opts *popt)
+{
+       size_t suffix_len = 0;
+       char *suffix = NULL;
+       char *param_name;
+       char *tmp;
+
+       if (popt->po_show_type) {
+               if (S_ISDIR(st->st_mode))
+                       suffix = "/";
+               else if (S_ISLNK(st->st_mode))
+                       suffix = "@";
+               else if (st->st_mode & S_IWUSR)
+                       suffix = "=";
+       }
+
+       /* Take the original filename string and chop off the glob addition */
+       tmp = strstr(filename, "/lustre/");
+       if (!tmp) {
+               tmp = strstr(filename, "/lnet/");
+               if (tmp)
+                       tmp += strlen("/lnet/");
+       } else {
+               tmp += strlen("/lustre/");
+       }
+
+       /* Allocate return string */
+       param_name = strdup(tmp);
+       if (!param_name)
+               return NULL;
+
+       /* replace '/' with '.' to match conf_param and sysctl */
+       for (tmp = strchr(param_name, '/'); tmp != NULL; tmp = strchr(tmp, '/'))
+               *tmp = '.';
+
+       /* Append the indicator to entries if needed. */
+       if (popt->po_show_type && suffix != NULL) {
+               suffix_len = strlen(suffix);
+
+               tmp = realloc(param_name, suffix_len + strlen(param_name) + 1);
+               if (tmp) {
+                       param_name = tmp;
+                       strncat(param_name, suffix,
+                               strlen(param_name) + suffix_len);
+               }
+       }
+
+       return param_name;
+}
+
+/**
+ * Turns a lctl parameter string into a procfs/sysfs subdirectory path pattern.
+ *
+ * \param[in] popt             Used to control parameter usage. For this
+ *                             function it is used to see if the path has
+ *                             a added suffix.
+ * \param[in,out] path         lctl parameter string that is turned into
+ *                             the subdirectory path pattern that is used
+ *                             to search the procfs/sysfs tree.
+ *
+ * \retval -errno on error.
+ */
+static int clean_path(struct param_opts *popt, char *path)
+{
+       char *nidstart = NULL;
+       char *nidend = NULL;
+       char *tmp;
+
+       if (popt == NULL || path == NULL || strlen(path) == 0)
+               return -EINVAL;
+
+       /* If path contains a suffix we need to remove it */
+       if (popt->po_show_type) {
+               size_t path_end = strlen(path) - 1;
+
+               tmp = path + path_end;
+               switch (*tmp) {
+               case '@':
+               case '=':
+               case '/':
+                       *tmp = '\0';
+               default:
+                       break;
+               }
+       }
+
+       /* get rid of '\', glob doesn't like it */
+       tmp = strrchr(path, '\\');
+       if (tmp) {
+               char *tail = path + strlen(path);
+
+               while (tmp != path) {
+                       if (*tmp == '\\') {
+                               memmove(tmp, tmp + 1, tail - tmp);
+                               --tail;
+                       }
+                       --tmp;
+               }
+       }
+
+       /* Does path contain a NID string?  Skip '.->/' replacement for it. */
+       tmp = strchr(path, '@');
+       if (tmp) {
+               /* First find the NID start.  NIDs may have variable (0-4) '.',
+                * so find the common NID prefixes instead of trying to count
+                * the dots.  Not great, but there are only two, and faster
+                * than multiple speculative NID parses and bad DNS lookups.
+                */
+               if ((tmp = strstr(path, ".exports.")))
+                       nidstart = tmp + strlen(".exports.");
+               else if ((tmp = strstr(path, ".MGC")))
+                       nidstart = tmp + 1;
+
+               /* Next, find the end of the NID string. */
+               if (nidstart)
+                       nidend = strchrnul(strchr(nidstart, '@'), '.');
+       }
+
+       /* replace param '.' with '/' */
+       for (tmp = strchr(path, '.'); tmp != NULL; tmp = strchr(tmp, '.')) {
+               *tmp++ = '/';
+
+               /*
+                * There exist cases where some of the subdirectories of the
+                * parameter tree has embedded in its name a NID string.
+                * This means that it is possible that these subdirectories
+                * could have actual '.' in its name. If this is the case we
+                * don't want to blindly replace the '.' with '/', so skip
+                * over the part of the parameter containing the NID.
+                */
+               if (tmp == nidstart)
+                       tmp = nidend;
+       }
+
+       return 0;
+}
+
+/**
+ * The application lctl can perform three operations for lustre
+ * tunables. This enum defines those four operations which are
+ *
+ * 1) LIST_PARAM       - list available tunables
+ * 2) GET_PARAM                - report the current setting of a tunable
+ * 3) SET_PARAM                - set the tunable to a new value
+ * 4) LIST_PATHNAME    - list paths of available tunables
+ */
+enum parameter_operation {
+       LIST_PARAM,
+       GET_PARAM,
+       SET_PARAM,
+       LIST_PATHNAME,
+};
+
+char *parameter_opname[] = {
+       [LIST_PARAM] = "list_param",
+       [GET_PARAM] = "get_param",
+       [SET_PARAM] = "set_param",
+       [LIST_PATHNAME] = "list_pathname",
+};
+
+/**
+ * Read the value of parameter
+ *
+ * \param[in]  path            full path to the parameter
+ * \param[in]  param_name      lctl parameter format of the
+ *                             parameter path
+ * \param[in]  popt            set/get param options
+ *
+ * \retval 0 on success.
+ * \retval -errno on error.
+ */
+static int read_param(const char *path, const char *param_name,
+                     struct param_opts *popt)
+{
+       int rc = 0;
+       char *buf = NULL;
+       size_t buflen;
+
+       rc = llapi_param_get_value(path, &buf, &buflen);
+       if (rc != 0) {
+               fprintf(stderr,
+                       "error: %s: '%s': %s\n",
+                       "read_param", path, strerror(-rc));
+               goto free_buf;
+       }
+       /* don't print anything for empty files */
+       if (buf[0] == '\0') {
+               if (popt->po_header)
+                       printf("%s=\n", param_name);
+               goto free_buf;
+       }
+
+       if (popt->po_header) {
+               char *oldbuf = buf;
+               char *next;
+
+               do {
+                       /* Split at first \n, if any */
+                       next = strchrnul(oldbuf, '\n');
+
+                       printf("%s=%.*s\n", param_name, (int)(next - oldbuf),
+                              oldbuf);
+
+                       buflen -= next - oldbuf + 1;
+                       oldbuf = next + 1;
+
+               } while (buflen > 0);
+
+       } else if (popt->po_show_name) {
+               bool multilines = memchr(buf, '\n', buflen - 1);
+
+               printf("%s=%s%s", param_name, multilines ? "\n" : "", buf);
+       } else {
+               printf("%s", buf);
+       }
+
+free_buf:
+       free(buf);
+       return rc;
+}
+
+/**
+ * Set a parameter to a specified value
+ *
+ * \param[in] path             full path to the parameter
+ * \param[in] param_name       lctl parameter format of the parameter path
+ * \param[in] popt             set/get param options
+ * \param[in] value            value to set the parameter to
+ *
+ * \retval number of bytes written on success.
+ * \retval -errno on error.
+ */
+int write_param(const char *path, const char *param_name,
+               struct param_opts *popt, const char *value)
+{
+       int fd, rc = 0;
+       ssize_t count;
+
+       if (!value)
+               return -EINVAL;
+
+       /* Write the new value to the file */
+       fd = open(path, O_WRONLY);
+       if (fd < 0) {
+               rc = -errno;
+               fprintf(stderr, "error: set_param: opening '%s': %s\n",
+                       path, strerror(errno));
+               return rc;
+       }
+
+       count = write(fd, value, strlen(value));
+       if (count < 0) {
+               rc = -errno;
+               if (errno != EIO) {
+                       fprintf(stderr, "error: set_param: setting %s=%s: %s\n",
+                               path, value, strerror(errno));
+               }
+       } else if (count < strlen(value)) { /* Truncate case */
+               rc = -EINVAL;
+               fprintf(stderr,
+                       "error: set_param: setting %s=%s: wrote only %zd\n",
+                       path, value, count);
+       } else if (popt->po_show_name) {
+               printf("%s=%s\n", param_name, value);
+       }
+       close(fd);
+
+       return rc;
+}
+
+/**
+ * Perform a read, write or just a listing of a parameter
+ *
+ * \param[in] popt     list,set,get parameter options
+ * \param[in] pattern  search filter for the path of the parameter
+ * \param[in] value    value to set the parameter if write operation
+ * \param[in] oper     what operation to perform with the parameter
+ * \param[out] wq      the work queue to which work items will be added or NULL
+ *                     if not in parallel
+ *
+ * \retval number of bytes written on success.
+ * \retval -errno on error and prints error message.
+ */
+static int do_param_op(struct param_opts *popt, char *pattern, char *value,
+                      enum parameter_operation oper, struct sp_workq *wq)
+{
+       int dup_count = 0;
+       char **dup_cache;
+       glob_t paths;
+       char *opname = parameter_opname[oper];
+       int rc, i;
+
+       if (!wq && popt_is_parallel(*popt))
+               return -EINVAL;
+
+       rc = llapi_param_get_paths(pattern, &paths);
+       if (rc) {
+               rc = -errno;
+               if (!popt->po_recursive && !(rc == -ENOENT && getuid() != 0)) {
+                       fprintf(stderr, "error: %s: param_path '%s': %s\n",
+                               opname, pattern, strerror(errno));
+               }
+               return rc;
+       }
+
+       if (popt_is_parallel(*popt) && paths.gl_pathc > 1) {
+               /* Allocate space for the glob paths in advance. */
+               rc = spwq_expand(wq, paths.gl_pathc);
+               if (rc < 0)
+                       goto out_param;
+       }
+
+       dup_cache = calloc(paths.gl_pathc, sizeof(char *));
+       if (!dup_cache) {
+               rc = -ENOMEM;
+               fprintf(stderr,
+                       "error: %s: allocating '%s' dup_cache[%zd]: %s\n",
+                       opname, pattern, paths.gl_pathc, strerror(-rc));
+               goto out_param;
+       }
+
+       for (i = 0; i < paths.gl_pathc; i++) {
+               char *param_name = NULL, *tmp;
+               char pathname[PATH_MAX], param_dir[PATH_MAX + 2];
+               struct stat st;
+               int rc2, j;
+
+               if (!popt->po_follow_symlinks)
+                       rc2 = lstat(paths.gl_pathv[i], &st);
+               else
+                       rc2 = stat(paths.gl_pathv[i], &st);
+
+               if (rc2 == -1) {
+                       fprintf(stderr, "error: %s: stat '%s': %s\n",
+                               opname, paths.gl_pathv[i], strerror(errno));
+                       if (!rc)
+                               rc = -errno;
+                       continue;
+               }
+
+               if (S_ISLNK(st.st_mode) && !popt->po_follow_symlinks)
+                       continue;
+               if (popt->po_only_dir && !S_ISDIR(st.st_mode))
+                       continue;
+
+               param_name = display_name(paths.gl_pathv[i], &st, popt);
+               if (!param_name) {
+                       fprintf(stderr,
+                               "error: %s: generating name for '%s': %s\n",
+                               opname, paths.gl_pathv[i], strerror(ENOMEM));
+                       if (!rc)
+                               rc = -ENOMEM;
+                       continue;
+               }
+
+               switch (oper) {
+               case GET_PARAM:
+                       /* Read the contents of file to stdout */
+                       if (S_ISREG(st.st_mode)) {
+                               rc2 = read_param(paths.gl_pathv[i], param_name,
+                                                popt);
+                               if (rc2 < 0 && !rc)
+                                       rc = rc2;
+                       }
+                       break;
+               case SET_PARAM:
+                       if (S_ISREG(st.st_mode)) {
+                               if (popt_is_parallel(*popt))
+                                       rc2 = spwq_add_item(wq,
+                                                           paths.gl_pathv[i],
+                                                           param_name, value);
+                               else
+                                       rc2 = write_param(paths.gl_pathv[i],
+                                                         param_name, popt,
+                                                         value);
+
+                               if (rc2 < 0 && !rc)
+                                       rc = rc2;
+                       }
+                       break;
+               case LIST_PARAM:
+                       /**
+                        * For the upstream client the parameter files locations
+                        * are split between under both /sys/kernel/debug/lustre
+                        * and /sys/fs/lustre. The parameter files containing
+                        * small amounts of data, less than a page in size, are
+                        * located under /sys/fs/lustre and in the case of large
+                        * parameter data files, think stats for example, are
+                        * located in the debugfs tree. Since the files are
+                        * split across two trees the directories are often
+                        * duplicated which means these directories are listed
+                        * twice which leads to duplicate output to the user.
+                        * To avoid scanning a directory twice we have to cache
+                        * any directory and check if a search has been
+                        * requested twice.
+                        */
+                       for (j = 0; j < dup_count; j++) {
+                               if (!strcmp(dup_cache[j], param_name))
+                                       break;
+                       }
+                       if (j != dup_count) {
+                               free(param_name);
+                               param_name = NULL;
+                               continue;
+                       }
+                       dup_cache[dup_count++] = strdup(param_name);
+
+                       if (popt->po_show_name)
+                               printf("%s\n", param_name);
+                       break;
+               case LIST_PATHNAME:
+                       for (j = 0; j < dup_count; j++) {
+                               if (!strcmp(dup_cache[j], param_name))
+                                       break;
+                       }
+                       if (j != dup_count) {
+                               free(param_name);
+                               param_name = NULL;
+                               continue;
+                       }
+                       dup_cache[dup_count++] = strdup(param_name);
+                       if (popt->po_show_name)
+                               printf("%s\n", paths.gl_pathv[i]);
+                       break;
+               }
+
+               /*
+                * Only directories are searched recursively if
+                * requested by the user
+                */
+               if (!S_ISDIR(st.st_mode) || !popt->po_recursive) {
+                       free(param_name);
+                       param_name = NULL;
+                       continue;
+               }
+
+               /* Turn param_name into file path format */
+               rc2 = clean_path(popt, param_name);
+               if (rc2 < 0) {
+                       fprintf(stderr, "error: %s: cleaning '%s': %s\n",
+                               opname, param_name, strerror(-rc2));
+                       free(param_name);
+                       param_name = NULL;
+                       if (!rc)
+                               rc = rc2;
+                       continue;
+               }
+
+               /* Use param_name to grab subdirectory tree from full path */
+               snprintf(param_dir, sizeof(param_dir), "/%s", param_name);
+               tmp = strstr(paths.gl_pathv[i], param_dir);
+
+               /* cleanup paramname now that we are done with it */
+               free(param_name);
+               param_name = NULL;
+               memset(&param_dir, '\0', sizeof(param_dir));
+
+               /* Shouldn't happen but just in case */
+               if (!tmp) {
+                       if (!rc)
+                               rc = -EINVAL;
+                       continue;
+               }
+               tmp++;
+
+               rc2 = snprintf(pathname, sizeof(pathname), "%s/*", tmp);
+               if (rc2 < 0) {
+                       /*
+                        * snprintf() should never an error, and if it does
+                        * there isn't much point trying to use fprintf()
+                        */
+                       continue;
+               }
+               if (rc2 >= sizeof(pathname)) {
+                       fprintf(stderr, "error: %s: overflow processing '%s'\n",
+                               opname, pathname);
+                       if (!rc)
+                               rc = -EINVAL;
+                       continue;
+               }
+
+               rc2 = do_param_op(popt, pathname, value, oper, wq);
+               if (!rc2 && rc2 != -ENOENT) {
+                       /* errors will be printed by do_param_op() */
+                       if (!rc)
+                               rc = rc2;
+                       continue;
+               }
+       }
+
+       for (i = 0; i < dup_count; i++)
+               free(dup_cache[i]);
+       free(dup_cache);
+out_param:
+       llapi_param_paths_free(&paths);
+       return rc;
+}
+
+static int listparam_cmdline(int argc, char **argv, struct param_opts *popt)
+{
+       struct option long_opts[] = {
+       { .val = 'D',   .name = "dir-only",     .has_arg = no_argument},
+       { .val = 'D',   .name = "directory-only", .has_arg = no_argument},
+       { .val = 'F',   .name = "classify",     .has_arg = no_argument},
+       { .val = 'l',   .name = "links",        .has_arg = no_argument},
+       { .val = 'L',   .name = "no-links",     .has_arg = no_argument},
+       { .val = 'R',   .name = "recursive",    .has_arg = no_argument},
+       };
+
+       int ch;
+
+       popt->po_show_name = 1;
+       popt->po_only_name = 1;
+       popt->po_follow_symlinks = 1;
+
+       while ((ch = getopt_long(argc, argv, "DFlLpR",
+                                     long_opts, NULL)) != -1) {
+               switch (ch) {
+               case 'D':
+                       popt->po_only_dir = 1;
+                       break;
+               case 'F':
+                       popt->po_show_type = 1;
+                       break;
+               case 'l':
+                       popt->po_follow_symlinks = 1;
+                       break;
+               case 'L':
+                       popt->po_follow_symlinks = 0;
+                       break;
+               case 'p':
+                       popt->po_only_pathname = 1;
+                       break;
+               case 'R':
+                       popt->po_recursive = 1;
+                       break;
+               default:
+                       return -1;
+               }
+       }
+
+       return optind;
+}
+
+int jt_lcfg_listparam(int argc, char **argv)
+{
+       int rc = 0, index, i;
+       struct param_opts popt;
+       char *path;
+
+       memset(&popt, 0, sizeof(popt));
+       index = listparam_cmdline(argc, argv, &popt);
+       if (index < 0 || index >= argc)
+               return CMD_HELP;
+
+       for (i = index; i < argc; i++) {
+               int rc2;
+
+               path = argv[i];
+
+               rc2 = clean_path(&popt, path);
+               if (rc2 < 0) {
+                       fprintf(stderr, "error: %s: cleaning '%s': %s\n",
+                               jt_cmdname(argv[0]), path, strerror(-rc2));
+                       if (rc == 0)
+                               rc = rc2;
+                       continue;
+               }
+
+               rc2 = do_param_op(&popt, path, NULL, popt.po_only_pathname ?
+                                 LIST_PATHNAME : LIST_PARAM, NULL);
+               if (rc2 < 0) {
+                       if (rc == 0)
+                               rc = rc2;
+
+                       if (rc2 == -ENOENT && getuid() != 0)
+                               rc2 = llapi_param_display_value(path, 0, 0,
+                                                               stdout);
+                       if (rc2 < 0) {
+                               fprintf(stderr, "error: %s: listing '%s': %s\n",
+                                       jt_cmdname(argv[0]), path,
+                                       strerror(-rc2));
+                       }
+                       continue;
+               }
+       }
+
+       return rc;
+}
+
+static int getparam_cmdline(int argc, char **argv, struct param_opts *popt)
+{
+       struct option long_opts[] = {
+       { .val = 'F',   .name = "classify",     .has_arg = no_argument},
+       { .val = 'H',   .name = "header",       .has_arg = no_argument},
+       { .val = 'l',   .name = "links",        .has_arg = no_argument},
+       { .val = 'L',   .name = "no-links",     .has_arg = no_argument},
+       { .val = 'n',   .name = "no-name",      .has_arg = no_argument},
+       { .val = 'N',   .name = "only-name",    .has_arg = no_argument},
+       { .val = 'R',   .name = "recursive",    .has_arg = no_argument},
+       { .val = 'y',   .name = "yaml",         .has_arg = no_argument},
+       };
+
+       int ch;
+
+       popt->po_show_name = 1;
+       popt->po_follow_symlinks = 1;
+
+       while ((ch = getopt_long(argc, argv, "FHlLnNRy",
+                                     long_opts, NULL)) != -1) {
+               switch (ch) {
+               case 'F':
+                       popt->po_show_type = 1;
+                       break;
+               case 'H':
+                       popt->po_header = 1;
+                       break;
+               case 'l':
+                       popt->po_follow_symlinks = 1;
+                       break;
+               case 'L':
+                       popt->po_follow_symlinks = 0;
+                       break;
+               case 'n':
+                       popt->po_show_name = 0;
+                       break;
+               case 'N':
+                       popt->po_only_name = 1;
+                       break;
+               case 'R':
+                       popt->po_recursive = 1;
+                       break;
+               case 'y':
+                       popt->po_yaml = 1;
+                       break;
+               default:
+                       return -1;
+               }
+       }
+
+       return optind;
+}
+
+int jt_lcfg_getparam(int argc, char **argv)
+{
+       int version = LUSTRE_GENL_VERSION;
+       enum parameter_operation mode;
+       int rc = 0, index, i;
+       struct param_opts popt;
+       int flags = 0;
+       char *path;
+
+       memset(&popt, 0, sizeof(popt));
+       index = getparam_cmdline(argc, argv, &popt);
+       if (index < 0 || index >= argc)
+               return CMD_HELP;
+
+       mode = popt.po_only_name ? LIST_PARAM : GET_PARAM;
+       if (mode == LIST_PARAM)
+               version = 0;
+
+       if (popt.po_yaml)
+               flags |= PARAM_FLAGS_YAML_FORMAT;
+       if (popt.po_show_name)
+               flags |= PARAM_FLAGS_SHOW_SOURCE;
+
+       for (i = index; i < argc; i++) {
+               int rc2;
+
+               path = argv[i];
+
+               rc2 = clean_path(&popt, path);
+               if (rc2 < 0) {
+                       fprintf(stderr, "error: %s: cleaning '%s': %s\n",
+                               jt_cmdname(argv[0]), path, strerror(-rc2));
+                       if (rc == 0)
+                               rc = rc2;
+                       continue;
+               }
+
+               rc2 = do_param_op(&popt, path, NULL,
+                                 popt.po_only_name ? LIST_PARAM : GET_PARAM,
+                                 NULL);
+               if (rc2 < 0) {
+                       if (rc == 0)
+                               rc = rc2;
+
+                       if (rc2 == -ENOENT && getuid() != 0)
+                               rc2 = llapi_param_display_value(path, version,
+                                                               flags, stdout);
+                       continue;
+               }
+       }
+
+       return rc;
+}
+
+/**
+ * Parses the commandline options to set_param.
+ *
+ * \param[in] argc     count of arguments given to set_param
+ * \param[in] argv     array of arguments given to set_param
+ * \param[out] popt    where set_param options will be saved
+ *
+ * \retval index in argv of the first nonoption argv element (optind value)
+ */
+static int setparam_cmdline(int argc, char **argv, struct param_opts *popt)
+{
+       struct option long_opts[] = {
+       { .val = 'd',   .name = "delete",       .has_arg = no_argument},
+       { .val = 'F',   .name = "file",         .has_arg = no_argument},
+       { .val = 'n',   .name = "noname",       .has_arg = no_argument},
+       { .val = 'P',   .name = "perm",         .has_arg = no_argument},
+       { .val = 'P',   .name = "permanent",    .has_arg = no_argument},
+       { .val = 't',   .name = "thread",       .has_arg = optional_argument},
+       };
+
+       int ch;
+
+       popt->po_show_name = 1;
+       popt->po_only_name = 0;
+       popt->po_show_type = 0;
+       popt->po_recursive = 0;
+       popt->po_perm = 0;
+       popt->po_delete = 0;
+       popt->po_file = 0;
+       popt->po_parallel_threads = 0;
+       popt->po_follow_symlinks = 1;
+       opterr = 0;
+
+       /* reset optind for each getopt_long() in case of multiple calls */
+       optind = 0;
+       while ((ch = getopt_long(argc, argv, "dFnPt::",
+                                long_opts, NULL)) != -1) {
+               switch (ch) {
+               case 'n':
+                       popt->po_show_name = 0;
+                       break;
+               case 't':
+#if HAVE_LIBPTHREAD
+                       if (optarg)
+                               popt->po_parallel_threads = atoi(optarg);
+                       else
+                               popt->po_parallel_threads = LCFG_THREADS_DEF;
+                       if (popt->po_parallel_threads < 2)
+                               return -EINVAL;
+#else
+                       {
+                               static bool printed;
+
+                               if (!printed) {
+                                       printed = true;
+                                       fprintf(stderr,
+                                               "warning: set_param: no pthread support, proceeding serially.\n");
+                               }
+                       }
+#endif
+                       break;
+               case 'P':
+                       popt->po_perm = 1;
+                       break;
+               case 'd':
+                       popt->po_delete = 1;
+                       break;
+               case 'F':
+                       popt->po_file = 1;
+                       break;
+               default:
+                       return -1;
+               }
+       }
+       if (popt->po_perm && popt->po_file) {
+               fprintf(stderr, "warning: ignoring -P option\n");
+               popt->po_perm = 0;
+       }
+       if (popt->po_delete && !popt->po_perm)
+               popt->po_perm = 1;
+       return optind;
+}
+
+/**
+ * Main set_param function.
+ *
+ * \param[in] argc     count of arguments given to set_param
+ * \param[in] argv     array of arguments given to set_param
+ *
+ * \retval 0 if successful
+ * \retval -errno if unsuccessful
+ */
+int jt_lcfg_setparam(int argc, char **argv)
+{
+       int rc = 0;
+       int index = 0;
+       struct param_opts popt;
+       struct sp_workq wq;
+       struct sp_workq *wq_ptr = NULL;
+
+       memset(&popt, 0, sizeof(popt));
+       index = setparam_cmdline(argc, argv, &popt);
+       if (index < 0 || index >= argc)
+               return CMD_HELP;
+
+       if (popt.po_perm)
+               /*
+                * We can't delete parameters that were
+                * set with old conf_param interface
+                */
+               return jt_lcfg_setparam_perm(argc, argv, &popt);
+
+       if (popt.po_file) {
+               fprintf(stderr,
+                       "warning: 'lctl set_param -F' is deprecated, use 'lctl apply_yaml' instead\n");
+               return -EINVAL;
+       }
+
+       if (popt_is_parallel(popt)) {
+               rc = spwq_init(&wq, &popt);
+               if (rc < 0) {
+                       fprintf(stderr,
+                               "warning: parallel %s: failed to init work queue: %s. Proceeding serially.\n",
+                               jt_cmdname(argv[0]), strerror(-rc));
+                       rc = 0;
+                       popt.po_parallel_threads = 0;
+               } else {
+                       wq_ptr = &wq;
+               }
+       }
+
+       while (index < argc) {
+               char *path = NULL;
+               char *value = NULL;
+
+               rc = sp_parse_param_value(argc - index, argv + index,
+                                         &path, &value);
+               if (rc < 0) {
+                       fprintf(stderr, "error: %s: setting %s: %s\n",
+                               jt_cmdname(argv[0]), path, strerror(-rc));
+                       break;
+               }
+               /* Increment index by the number of arguments consumed. */
+               index += rc;
+
+               rc = clean_path(&popt, path);
+               if (rc < 0)
+                       break;
+
+               rc = do_param_op(&popt, path, value, SET_PARAM, wq_ptr);
+               if (rc < 0)
+                       fprintf(stderr, "error: %s: setting '%s'='%s': %s\n",
+                               jt_cmdname(argv[0]), path, value,
+                               strerror(-rc));
+       }
+
+       if (popt_is_parallel(popt)) {
+               int rc2;
+               /* Spawn threads to set the parameters which made it into the
+                * work queue to emulate serial set_param behavior when errors
+                * are encountered above.
+                */
+               rc2 = sp_run_threads(&wq);
+               if (rc2 < 0) {
+                       fprintf(stderr,
+                               "error: parallel %s: failed to run threads: %s\n",
+                               jt_cmdname(argv[0]), strerror(-rc2));
+                       if (!rc)
+                               rc = rc2;
+               }
+               rc2 = spwq_destroy(&wq);
+               if (rc2 < 0) {
+                       fprintf(stderr,
+                               "warning: parallel %s: failed to cleanup work queue: %s\n",
+                               jt_cmdname(argv[0]), strerror(-rc2));
+               }
+       }
+
+       return rc;
+}
index 487370b..16b089f 100644 (file)
@@ -138,6 +138,12 @@ int parse_devname(char *func, char *name, int dev_id);
 char *jt_cmdname(char *func);
 
 
+/* lustre_param.c */
+struct param_opts;
+int jt_lcfg_getparam(int argc, char **argv);
+int jt_lcfg_setparam(int argc, char **argv);
+int jt_lcfg_listparam(int argc, char **argv);
+
 /* lustre_cfg.c */
 int lcfg_set_devname(char *name);
 char *lcfg_get_devname(void);
@@ -152,10 +158,8 @@ int jt_lcfg_add_conn(int argc, char **argv);
 int jt_lcfg_del_conn(int argc, char **argv);
 int jt_lcfg_param(int argc, char **argv);
 int jt_lcfg_confparam(int argc, char **argv);
-int jt_lcfg_getparam(int argc, char **argv);
-int jt_lcfg_setparam(int argc, char **argv);
 int jt_lcfg_applyyaml(int argc, char **argv);
-int jt_lcfg_listparam(int argc, char **argv);
+int jt_lcfg_setparam_perm(int argc, char **argv, struct param_opts *popt);
 
 int jt_pool_cmd(int argc, char **argv);
 int jt_del_ost(int argc, char **argv);