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 \
--- /dev/null
+.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)
--- /dev/null
+.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)
* @{
*/
+#include <glob.h>
#include <stdarg.h>
#include <stdint.h>
#include <time.h>
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)
#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"
+ #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"
* Copyright (c) 2016, Intel Corporation.
*/
#include <errno.h>
+#include <fcntl.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>
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);
+}
#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>
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;
- 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,
- "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;
}
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) {
free(dup_cache[i]);
free(dup_cache);
out_param:
- cfs_free_param_data(&paths);
+ llapi_param_paths_free(&paths);
return rc;
}