+ 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_path) {
+ 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] mode what operation to perform with the parameter
+ *
+ * \retval number of bytes written on success.
+ * \retval -errno on error.
+ */
+static int
+param_display(struct param_opts *popt, char *pattern, char *value,
+ enum parameter_operation mode)
+{
+ int dir_count = 0;
+ char **dir_cache;
+ glob_t paths;
+ int rc, i;
+
+ rc = cfs_get_param_paths(&paths, "%s", pattern);
+ if (rc != 0) {
+ rc = -errno;
+ if (!popt->po_recursive) {
+ fprintf(stderr, "error: '%s': %s\n",
+ pattern, strerror(errno));
+ }
+ return rc;
+ }
+
+ dir_cache = calloc(paths.gl_pathc, sizeof(char *));
+ if (dir_cache == NULL) {
+ rc = -ENOMEM;
+ goto out_param;
+ }
+
+ for (i = 0; i < paths.gl_pathc; i++) {
+ char *param_name = NULL, *tmp;
+ char pathname[PATH_MAX];
+ struct stat st;
+
+ if (stat(paths.gl_pathv[i], &st) == -1) {
+ rc = -errno;
+ break;
+ }
+
+ param_name = display_name(paths.gl_pathv[i], &st, popt);
+ if (param_name == NULL) {
+ rc = -ENOMEM;
+ break;
+ }
+
+ /**
+ * 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.
+ */
+ if (S_ISDIR(st.st_mode)) {
+ int j;
+
+ for (j = 0; j < dir_count; j++) {
+ if (!strcmp(dir_cache[j], param_name))
+ break;
+ }
+ if (j != dir_count) {
+ free(param_name);
+ param_name = NULL;
+ continue;
+ }
+ dir_cache[dir_count++] = strdup(param_name);
+ }
+
+ switch (mode) {
+ case GET_PARAM:
+ /* Read the contents of file to stdout */
+ if (S_ISREG(st.st_mode))
+ read_param(paths.gl_pathv[i], param_name, popt);
+ break;
+ case SET_PARAM:
+ if (S_ISREG(st.st_mode)) {
+ rc = write_param(paths.gl_pathv[i], param_name,
+ popt, value);
+ }
+ break;
+ case LIST_PARAM:
+ default:
+ if (popt->po_show_path)
+ printf("%s\n", param_name);
+ 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 */
+ rc = clean_path(popt, param_name);
+ if (rc < 0) {
+ free(param_name);
+ param_name = NULL;
+ break;
+ }
+
+ /* Use param_name to grab subdirectory tree from full path */
+ tmp = strstr(paths.gl_pathv[i], param_name);
+
+ /* cleanup paramname now that we are done with it */
+ free(param_name);
+ param_name = NULL;
+
+ /* Shouldn't happen but just in case */
+ if (tmp == NULL) {
+ rc = -EINVAL;
+ break;
+ }
+
+ rc = snprintf(pathname, sizeof(pathname), "%s/*", tmp);
+ if (rc < 0) {
+ break;
+ } else if (rc >= sizeof(pathname)) {
+ rc = -EINVAL;
+ break;
+ }
+
+ rc = param_display(popt, pathname, value, mode);
+ if (rc != 0 && rc != -ENOENT)
+ break;
+ }
+
+ for (i = 0; i < dir_count; i++)
+ free(dir_cache[i]);
+ free(dir_cache);
+out_param:
+ cfs_free_param_data(&paths);