Whamcloud - gitweb
LU-13224 utils: expose llapi_param* functions 45/37545/15
authorGian-Carlo DeFazio <defazio1@llnl.gov>
Tue, 11 Feb 2020 21:08:18 +0000 (13:08 -0800)
committerOleg Drokin <green@whamcloud.com>
Tue, 7 Apr 2020 17:19:08 +0000 (17:19 +0000)
Added an interface to find files in sysfs,procfs,etc.,
and read them into a file or buffer.
The interface is meant to return the same results as
the "lctl list_param" and "lctl get_param" commands,
but doesn't require starting an lctl process.

The interface is being used by lctl for parts of
its "list_param" and "get_param" commands.

The output of lctl get_param is altered slightly.
Previously, for some multi-line files lctl would
print the param name and first line of the file on
the same line such as:

param=line 1
line 2
line 3

Now multi-line files consistently put a newline
after the param name and before the content such as:

param=
line 1
line 2
line 3

Added debug output to failing test sanity 300d.

Signed-off-by: Gian-Carlo DeFazio <defazio1@llnl.gov>
Change-Id: I2726b27b0042d58c97284f8348970deb6efc43d1
Reviewed-on: https://review.whamcloud.com/37545
Tested-by: jenkins <devops@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Olaf Faaland-LLNL <faaland1@llnl.gov>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: James Simmons <jsimmons@infradead.org>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/doc/Makefile.am
lustre/doc/llapi_param_get_paths.3 [new file with mode: 0644]
lustre/doc/llapi_param_get_value.3 [new file with mode: 0644]
lustre/include/lustre/lustreapi.h
lustre/tests/sanity.sh
lustre/utils/liblustreapi_param.c
lustre/utils/lustre_cfg.c

index 03a83a6..54819f0 100644 (file)
@@ -142,6 +142,8 @@ MANFILES =                                  \
        llapi_layout_stripe_count_set.3         \
        llapi_layout_stripe_size_get.3          \
        llapi_layout_stripe_size_set.3          \
        llapi_layout_stripe_count_set.3         \
        llapi_layout_stripe_size_get.3          \
        llapi_layout_stripe_size_set.3          \
+       llapi_param_get_paths.3                 \
+       llapi_param_get_value.3                 \
        llapi_path2fid.3                        \
        llapi_path2parent.3                     \
        llapi_pcc_attach.3                      \
        llapi_path2fid.3                        \
        llapi_path2parent.3                     \
        llapi_pcc_attach.3                      \
diff --git a/lustre/doc/llapi_param_get_paths.3 b/lustre/doc/llapi_param_get_paths.3
new file mode 100644 (file)
index 0000000..852cc7e
--- /dev/null
@@ -0,0 +1,31 @@
+.TH llapi_param_get_paths 3 "2020 Feb 24" "Lustre User API"
+.SH NAME
+llapi_param_get_paths \- get a list of Lustre parameter file paths
+that match the given pattern
+.SH SYNOPSIS
+.B #include <lustre/lustreapi.h>
+.nf
+.BI "int llapi_param_get_paths(const char" " *pattern, " "glob_t" \
+" *paths" ")"
+.SH DESCRIPTION
+The
+.B llapi_param_get_paths(\|)
+function searches the locations in the filesystem that expose
+Lustre parameters. All file names that match
+.I pattern
+and are in
+the correct locations in the file system are returned.
+
+.SH RETURN VALUES
+.TP
+.B 0
+on success with the parameter paths stored in
+.IR paths .
+.TP
+-ve
+error code on failure and sets errno appropriately.
+
+.SH SEE ALSO
+.BR llapi_param_get_value (3),
+.BR lustreapi (7),
+.BR lctl-get_param (8)
diff --git a/lustre/doc/llapi_param_get_value.3 b/lustre/doc/llapi_param_get_value.3
new file mode 100644 (file)
index 0000000..9392164
--- /dev/null
@@ -0,0 +1,103 @@
+.TH llapi_param_get_value 3 "2020 Feb 24" "Lustre User API"
+.SH NAME
+llapi_param_get_value \- read parameter files for Lustre to a buffer
+.SH SYNOPSIS
+.nf
+.B #include <lustre/lustreapi.h>
+.sp
+.BI "int llapi_param_get_value(const char "  "*path"  ", char " "**buf" \
+", size_t " "*buflen" ");"
+.SH DESCRIPTION
+.LP
+The
+.B llapi_param_get_value()
+function reads the contents of files in procfs, sysfs, etc. into a buffer.
+If the buffer is preallocated, it will have a fixed size.
+If the buffer is not preallocated, it will be automatically sized to fit
+the contents of the file. There is also an option to query the size of a file.
+
+The behavior of
+.B llapi_param_get_value()
+depends on the value of
+.IR buf .
+If
+.I buf
+is NULL, then the contents of
+.I path
+will not be saved into
+.IR *buf ,
+but the number of bytes needed to hold the file will be stored in
+.IR buflen .
+This size will include the space needed for the NUL terminating byte.
+However, the size of the file can change because it is dynamically generated.
+This means that it's possible for the file size to increase which will cause
+the value stored in
+.I buflen
+to still not be large enough when used in a subsequent call.
+.sp
+If
+.I buf
+is not NULL but
+.I *buf
+is NULL, then
+.I *buf
+will point to an automatically allocated buffer that will hold the contents of
+.IR path ,
+and buflen will hold the number of bytes written excluding the NUL terminating
+byte.
+.sp
+If both
+.I buf
+and
+.I *buf
+are not NULL, then
+.I buf
+is assumed to be a pre-allocated buffer of
+.I *buflen
+size. The files contents will be put into
+.I *buf
+and the number of bytes written to
+.IR *buflen ,
+excluding the NUL terminating byte. In the case that
+.I buflen
+is too small to hold the NUL-terminated contents of the file,
+.I buflen
+will contain a suggested size for
+.IR *buf .
+This suggested size includes the NUL terminating byte, just as in the case where
+.I buf
+is NULL.
+
+.SH RETURN VALUES
+.TP
+.B 0
+on success
+.TP
+-ve
+error code on failure and sets errno appropriately.
+
+.SH ERRORS
+.TP
+-EINVAL
+if either
+.I path
+or
+.I buflen
+are NULL.
+.TP
+-EOVERFLOW
+if
+.I buf
+and
+.I *buf
+are not NULL, and the pre-allocated buffer is too small.
+.TP
+-ENOMEM
+if
+.I buf
+is not NULL, but the buffer allocation failed
+
+.SH SEE ALSO
+.BR llapi_get_param_paths (3),
+.BR lustreapi (7),
+.BR lctl-get_param (8)
index 39a8090..7ed703e 100644 (file)
@@ -38,6 +38,7 @@
  * @{
  */
 
  * @{
  */
 
+#include <glob.h>
 #include <stdarg.h>
 #include <stdint.h>
 #include <time.h>
 #include <stdarg.h>
 #include <stdint.h>
 #include <time.h>
@@ -1118,6 +1119,10 @@ int llapi_layout_sanity(struct llapi_layout *layout, bool incomplete, bool flr);
 void llapi_layout_sanity_perror(int error);
 int llapi_layout_dom_size(struct llapi_layout *layout, uint64_t *size);
 
 void llapi_layout_sanity_perror(int error);
 int llapi_layout_dom_size(struct llapi_layout *layout, uint64_t *size);
 
+int llapi_param_get_paths(const char *pattern, glob_t *paths);
+int llapi_param_get_value(const char *path, char **buf, size_t *buflen);
+void llapi_param_paths_free(glob_t *paths);
+
 /** @} llapi */
 
 #if defined(__cplusplus)
 /** @} llapi */
 
 #if defined(__cplusplus)
index 3ff0118..18a1390 100755 (executable)
@@ -19690,12 +19690,22 @@ test_300d() {
        #local striped directory
        $LFS setdirstripe -i 0 -c 2 -H all_char $DIR/$tdir/striped_dir ||
                error "set striped dir error"
        #local striped directory
        $LFS setdirstripe -i 0 -c 2 -H all_char $DIR/$tdir/striped_dir ||
                error "set striped dir error"
+        #look at the directories for debug purposes
+       ls -l $DIR/$tdir
+       $LFS getdirstripe $DIR/$tdir
+       ls -l $DIR/$tdir/striped_dir
+       $LFS getdirstripe $DIR/$tdir/striped_dir
        createmany -o $DIR/$tdir/striped_dir/f 10 ||
                error "create 10 files failed"
 
        #remote striped directory
        $LFS setdirstripe -i 1 -c 2 $DIR/$tdir/remote_striped_dir ||
                error "set striped dir error"
        createmany -o $DIR/$tdir/striped_dir/f 10 ||
                error "create 10 files failed"
 
        #remote striped directory
        $LFS setdirstripe -i 1 -c 2 $DIR/$tdir/remote_striped_dir ||
                error "set striped dir error"
+        #look at the directories for debug purposes
+       ls -l $DIR/$tdir
+       $LFS getdirstripe $DIR/$tdir
+       ls -l $DIR/$tdir/remote_striped_dir
+       $LFS getdirstripe $DIR/$tdir/remote_striped_dir
        createmany -o $DIR/$tdir/remote_striped_dir/f 10 ||
                error "create 10 files failed"
 
        createmany -o $DIR/$tdir/remote_striped_dir/f 10 ||
                error "create 10 files failed"
 
index 3dd79cd..8a8ae45 100644 (file)
  * Copyright (c) 2016, Intel Corporation.
  */
 #include <errno.h>
  * Copyright (c) 2016, Intel Corporation.
  */
 #include <errno.h>
+#include <fcntl.h>
 #include <stdint.h>
 #include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include <libcfs/util/param.h>
 #include <linux/lustre/lustre_user.h>
 
 #include <libcfs/util/param.h>
 #include <linux/lustre/lustre_user.h>
@@ -181,3 +187,239 @@ err:
 
        return rc;
 }
 
        return rc;
 }
+
+int llapi_param_get_paths(const char *pattern, glob_t *paths)
+{
+       return get_lustre_param_path(NULL, NULL, FILTER_BY_NONE,
+                                    pattern, paths);
+}
+
+/**
+ *  Read to the end of the file and count the bytes read.
+ */
+static int bytes_remaining(int fd, size_t *file_size)
+{
+       int rc = 0;
+       size_t bytes_read = 0;
+       long page_size = sysconf(_SC_PAGESIZE);
+       char *temp_buf;
+
+       temp_buf = malloc(page_size);
+       if (temp_buf == NULL)
+               return -ENOMEM;
+
+       while (1) {
+               ssize_t count = read(fd, temp_buf, page_size);
+
+               if (count == 0) {
+                       *file_size = bytes_read;
+                       break;
+               }
+
+               if (count < 0) {
+                       rc = -errno;
+                       break;
+               }
+               bytes_read += count;
+       }
+
+       free(temp_buf);
+       return rc;
+}
+
+/**
+ *  Determine the size of a file by reading it.
+ */
+static int required_size(const char *path, size_t *file_size)
+{
+       int rc = 0;
+       int fd;
+
+       fd = open(path, O_RDONLY);
+       if (fd < 0)
+               return -errno;
+
+       rc = bytes_remaining(fd, file_size);
+
+       close(fd);
+       *file_size += 1;
+       return rc;
+}
+
+static
+int copy_file_expandable(const char *path, char **buf, size_t *file_size)
+{
+       long page_size = sysconf(_SC_PAGESIZE);
+       int rc = 0;
+       char *temp_buf;
+       int fd;
+       FILE *fp;
+
+       fp = open_memstream(buf, file_size);
+       if (fp == NULL) {
+               rc = -errno;
+               goto out;
+       }
+
+       fd = open(path, O_RDONLY);
+       if (fd < 0) {
+               rc = -errno;
+               goto close_stream;
+       }
+
+       temp_buf = calloc(1, page_size);
+       if (buf == NULL) {
+               rc = -ENOMEM;
+               goto close_file;
+       }
+
+       while (1) {
+               ssize_t count = read(fd, temp_buf, page_size);
+
+               if (count == 0)
+                       break;
+               if (count < 0) {
+                       rc = -errno;
+                       break;
+               }
+
+               if (fwrite(temp_buf, 1, count, fp) != count) {
+                       rc = -errno;
+                       break;
+               }
+       }
+
+       free(temp_buf);
+close_file:
+       close(fd);
+close_stream:
+       fclose(fp);
+out:
+       /* If rc != 0 and *buf != NULL, the caller may retry.
+        * This would likely result in copy_file_fixed() being called
+        * on accident, and a likely memory error.
+        */
+       if (rc != 0) {
+               free(*buf);
+               *buf = NULL;
+       }
+       return rc;
+}
+
+/**
+ *  Copy file to a buffer and write the number of bytes copied
+ */
+static int copy_file_fixed(const char *path, char *buf, size_t *buflen)
+{
+       int rc = 0;
+       size_t bytes_read = 0;
+       size_t max_read = *buflen - 1;
+       size_t remaining = 0;
+       int fd;
+
+       fd = open(path, O_RDONLY);
+       if (fd < 0)
+               return -errno;
+
+       while (bytes_read < max_read) {
+               ssize_t count = read(fd,
+                                    buf + bytes_read,
+                                    max_read - bytes_read);
+
+               /* read the entire file */
+               if (count == 0) {
+                       *buflen = bytes_read + 1;
+                       buf[bytes_read] = '\0';
+                       goto out;
+               }
+
+               if (count < 0)
+                       goto check_size;
+
+               bytes_read += count;
+       }
+
+check_size:
+       /* need to check size in case error due to buf being too small
+        * for read() or exited loop due to buf being full
+        */
+       buf[max_read] = '\0';
+
+       rc = bytes_remaining(fd, &remaining);
+       if (rc != 0) {
+               rc = -errno;
+               goto out;
+       }
+       *buflen = bytes_read + remaining;
+
+       /* file was not (*buflen - 1) bytes, add 1 for reallocating */
+       if (remaining != 0) {
+               *buflen += 1;
+               rc = -EOVERFLOW;
+       }
+
+out:
+       close(fd);
+
+       return rc;
+}
+
+/**
+ * Read the value of the file with location \a path
+ * into a buffer.
+ *
+ * \param path[in]           the location of a parameter file
+ * \param buf[in,out]        a pointer to a pointer to a buffer
+ * \param buflen[in,out]     the length of a pre-allocated buffer
+ *                           when passed in, and either the number
+ *                           of bytes written or the suggested
+ *                           size of *buf when passed out.
+ *
+ * There are 3 behaviors based on the value of buf.
+ * If buf == NULL, then the buffer size needed to read the file at
+ * \a path will be written to \a *buflen.
+ * If \a buf != NULL and \a *buf == NULL, the value of *buf will point
+ * to a buffer that will be automatically sized to fit the file
+ * contents. A NUL byte will be added to the end of the buffer.
+ * The value of \a *buflen will be set to the number of bytes written
+ * excuding the NUL byte.
+ * If \a buf != NULL and \a *buf != NULL, it will be assumed that \a *buf
+ * points to a pre-allocated buffer with a capacity of \a *buflen.
+ * If there is sufficient space, the file contents and NUL terminating
+ * byte will be written to the buffer at .\a *buf.
+ * Otherwise, the required size of \a *buflen with be written to \a *buflen.
+ *
+ * Returns 0 for success with null terminated string in \a *buf.
+ * Returns negative errno value on error.
+ * For case of \a buf != NULL and \a *buf != NULL, a return value
+ * of -EOVERFLOW indicates that it's possible retry with a larger
+ * buffer.
+ */
+int llapi_param_get_value(const char *path, char **buf, size_t *buflen)
+{
+       int rc = 0;
+
+       if (path == NULL || buflen == NULL)
+               rc = -EINVAL;
+       else if (buf == NULL)
+               rc = required_size(path, buflen);
+       /* handle for buffer, but no buffer
+        * create a buffer of the required size
+        */
+       else if (*buf == NULL)
+               rc = copy_file_expandable(path, buf, buflen);
+       /* preallocated buffer given, attempt to copy
+        * file to it, return file size if buffer too small
+        */
+       else
+               rc = copy_file_fixed(path, *buf, buflen);
+
+       errno = -rc;
+
+       return rc;
+}
+
+void llapi_param_paths_free(glob_t *paths)
+{
+       cfs_free_param_data(paths);
+}
index a6a783b..60f2ef8 100644 (file)
@@ -54,6 +54,7 @@
 #include <libcfs/util/string.h>
 #include <libcfs/util/param.h>
 #include <libcfs/util/parser.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/lnet/lnetctl.h>
 #include <linux/lustre/lustre_cfg.h>
 #include <linux/lnet/nidstr.h>
 #include <linux/lnet/lnetctl.h>
 #include <linux/lustre/lustre_cfg.h>
@@ -835,74 +836,32 @@ char *parameter_opname[] = {
 static int
 read_param(const char *path, const char *param_name, struct param_opts *popt)
 {
 static int
 read_param(const char *path, const char *param_name, struct param_opts *popt)
 {
-       bool display_path = popt->po_show_path;
-       long page_size = sysconf(_SC_PAGESIZE);
        int rc = 0;
        int rc = 0;
-       char *buf;
-       int fd;
-
-       /* Read the contents of file to stdout */
-       fd = open(path, O_RDONLY);
-       if (fd < 0) {
-               rc = -errno;
-               fprintf(stderr,
-                       "error: get_param: opening '%s': %s\n",
-                       path, strerror(errno));
-               return rc;
-       }
+       char *buf = NULL;
+       size_t buflen;
 
 
-       buf = calloc(1, page_size);
-       if (buf == NULL) {
+       rc = llapi_param_get_value(path, &buf, &buflen);
+       if (rc != 0) {
                fprintf(stderr,
                fprintf(stderr,
-                       "error: get_param: allocating '%s' buffer: %s\n",
-                       path, strerror(errno));
-               close(fd);
-               return -ENOMEM;
+                       "error: read_param: \'%s\': %s\n",
+                       path, strerror(-rc));
+               goto free_buf;
        }
        }
+       /* don't print anything for empty files */
+       if (buf[0] == '\0')
+               goto free_buf;
 
 
-       while (1) {
-               ssize_t count = read(fd, buf, page_size);
+       if (popt->po_show_path) {
+               bool longbuf;
 
 
-               if (count == 0)
-                       break;
-               if (count < 0) {
-                       rc = -errno;
-                       if (errno != EIO) {
-                               fprintf(stderr, "error: get_param: "
-                                       "reading '%s': %s\n",
-                                       param_name, strerror(errno));
-                       }
-                       break;
-               }
-
-               /* Print the output in the format path=value if the value does
-                * not contain a new line character and the output can fit in
-                * a single line, else print value on new line */
-               if (display_path) {
-                       bool longbuf;
-
-                       longbuf = strnchr(buf, count - 1, '\n') != NULL ||
-                                         count + strlen(param_name) >= 80;
-                       printf("%s=%s", param_name, longbuf ? "\n" : buf);
-
-                       /* Make sure it doesn't print again while looping */
-                       display_path = false;
-
-                       if (!longbuf)
-                               continue;
-               }
-
-               if (fwrite(buf, 1, count, stdout) != count) {
-                       rc = -errno;
-                       fprintf(stderr,
-                               "error: get_param: write to stdout: %s\n",
-                               strerror(errno));
-                       break;
-               }
+               longbuf = strnchr(buf, buflen - 1, '\n') != NULL ||
+                       buflen + strlen(param_name) >= 80;
+               printf("%s=%s", param_name, longbuf ? "\n" : "");
        }
        }
-       close(fd);
-       free(buf);
+       printf("%s", buf);
 
 
+free_buf:
+       free(buf);
        return rc;
 }
 
        return rc;
 }
 
@@ -976,7 +935,7 @@ param_display(struct param_opts *popt, char *pattern, char *value,
        char *opname = parameter_opname[mode];
        int rc, i;
 
        char *opname = parameter_opname[mode];
        int rc, i;
 
-       rc = cfs_get_param_paths(&paths, "%s", pattern);
+       rc = llapi_param_get_paths(pattern, &paths);
        if (rc != 0) {
                rc = -errno;
                if (!popt->po_recursive) {
        if (rc != 0) {
                rc = -errno;
                if (!popt->po_recursive) {
@@ -1136,7 +1095,7 @@ param_display(struct param_opts *popt, char *pattern, char *value,
                free(dup_cache[i]);
        free(dup_cache);
 out_param:
                free(dup_cache[i]);
        free(dup_cache);
 out_param:
-       cfs_free_param_data(&paths);
+       llapi_param_paths_free(&paths);
        return rc;
 }
 
        return rc;
 }