4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * All rights reserved. This program and the accompanying materials
7 * are made available under the terms of the GNU Lesser General Public License
8 * (LGPL) version 2.1 or (at your discretion) any later version.
9 * (LGPL) version 2.1 accompanies this distribution, and is available at
10 * http://www.gnu.org/licenses/lgpl-2.1.html
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
20 * lustre/utils/liblustreapi_param.c
22 * This code handles user interaction with the configuration interface
23 * to the Lustre file system to fine tune it.
25 * Copyright (c) 2016, Intel Corporation.
33 #include <sys/types.h>
37 #include <libcfs/util/param.h>
38 #include <linux/lustre/lustre_kernelcomm.h>
39 #include <linux/lustre/lustre_user.h>
40 #include <lnetconfig/liblnetconfig.h>
41 #include <lustre/lustreapi.h>
42 #include "lustreapi_internal.h"
45 * return the parameter's path for a specific device type or mountpoint
47 * \param param the results returned to the caller
48 * \param obd_type Lustre OBD device type
50 * \param filter filter combined with the type agrument allow the
51 * \param type caller to limit the scope of the search for the
52 * parameter's path. Typical options are search by
53 * Lustre filesystem name or by the path to a file
54 * or directory in the filesystem.
56 * \param param_name parameter name to fetch
58 * Using filter and the type argument we can limit the scope of the
59 * search to either the parameter belonging to a specific lustre filesystem
60 * (if it exists) or using a given file or directory path located on a
61 * mounted Lustre filesystem. The last case it can do is a special search
62 * based on exactly what the user passed instead of scanning file paths
63 * or specific file systems.
65 * If "obd_type" matches a Lustre device then the first matching device
66 * (as with "lctl dl", constrained by \param filter and \param type)
67 * will be used to provide the return value, otherwise the first such
68 * device found will be used.
70 * Return 0 for success, with the results stored in \param param.
71 * Return -ve value for error.
74 get_lustre_param_path(const char *obd_type, const char *filter,
75 enum param_filter type, const char *param_name,
78 char pattern[PATH_MAX];
81 if (filter == NULL && type != FILTER_BY_NONE)
86 rc = llapi_search_fsname(filter, pattern);
88 llapi_error(LLAPI_MSG_ERROR, rc,
89 "'%s' is not on a Lustre filesystem",
93 if (strlen(pattern) + 3 > sizeof(pattern))
95 strncat(pattern, "-*", sizeof(pattern) - 1);
97 case FILTER_BY_FS_NAME:
98 rc = snprintf(pattern, sizeof(pattern) - 1, "%s-*", filter);
101 else if (rc >= sizeof(pattern))
105 case FILTER_BY_EXACT:
106 if (strlen(filter) + 1 > sizeof(pattern))
108 strncpy(pattern, filter, sizeof(pattern));
115 if (type == FILTER_BY_NONE) {
116 if (cfs_get_param_paths(param, "%s", param_name) != 0)
118 } else if (param_name != NULL) {
119 if (cfs_get_param_paths(param, "%s/%s/%s",
120 obd_type, pattern, param_name) != 0)
123 if (cfs_get_param_paths(param, "%s/%s",
124 obd_type, pattern) != 0)
132 * return a parameter of a single line value for a specific device type
135 * \param obd_type Lustre OBD device type
137 * \param filter filter combined with the type agruments allow the
138 * \param type caller to limit the scope of the search for the
139 * parameter's path. Typical options are search by
140 * Lustre filesystem name or by the path to a file
141 * or directory in the filesystem.
143 * \param param_name parameter name to fetch
144 * \param value return buffer for parameter value string
145 * \param val_len size of buffer for return value
147 * Using filter and the type argument we can limit the scope of the
148 * search to either the parameter belonging to a specific lustre filesystem
149 * (if it exists) or using a given file or directory path located on a
150 * mounted Lustre filesystem. The last case it can do is a special search
151 * based on exactly what the user passed instead of scanning file paths
152 * or specific file systems.
154 * If "obd_type" matches a Lustre device then the first matching device
155 * (as with "lctl dl", constrained by \param filter and \param type)
156 * will be used to provide the return value, otherwise the first such
157 * device found will be used.
159 * Return 0 for success, with a NUL-terminated string in \param value.
160 * Return negative errno value for error.
163 get_lustre_param_value(const char *obd_type, const char *filter,
164 enum param_filter type, const char *param_name,
165 char *value, size_t val_len)
171 rc = get_lustre_param_path(obd_type, filter, type, param_name, ¶m);
175 fp = fopen(param.gl_pathv[0], "r");
178 llapi_error(LLAPI_MSG_ERROR, rc, "error: opening '%s'",
183 if (fgets(value, val_len, fp) == NULL) {
189 cfs_free_param_data(¶m);
194 int llapi_param_get_paths(const char *pattern, glob_t *paths)
196 return get_lustre_param_path(NULL, NULL, FILTER_BY_NONE,
201 * Read to the end of the file and count the bytes read.
203 static int bytes_remaining(int fd, size_t *file_size)
206 size_t bytes_read = 0;
207 long page_size = sysconf(_SC_PAGESIZE);
210 temp_buf = malloc(page_size);
211 if (temp_buf == NULL)
215 ssize_t count = read(fd, temp_buf, page_size);
218 *file_size = bytes_read;
234 * Determine the size of a file by reading it.
236 static int required_size(const char *path, size_t *file_size)
241 fd = open(path, O_RDONLY);
245 rc = bytes_remaining(fd, file_size);
253 int copy_file_expandable(const char *path, char **buf, size_t *file_size)
255 long page_size = sysconf(_SC_PAGESIZE);
261 fp = open_memstream(buf, file_size);
267 fd = open(path, O_RDONLY);
273 temp_buf = calloc(1, page_size);
280 ssize_t count = read(fd, temp_buf, page_size);
289 if (fwrite(temp_buf, 1, count, fp) != count) {
301 /* If rc != 0 and *buf != NULL, the caller may retry.
302 * This would likely result in copy_file_fixed() being called
303 * on accident, and a likely memory error.
313 * Copy file to a buffer and write the number of bytes copied
315 static int copy_file_fixed(const char *path, char *buf, size_t *buflen)
318 size_t bytes_read = 0;
319 size_t max_read = *buflen - 1;
320 size_t remaining = 0;
323 fd = open(path, O_RDONLY);
327 while (bytes_read < max_read) {
328 ssize_t count = read(fd,
330 max_read - bytes_read);
332 /* read the entire file */
334 *buflen = bytes_read + 1;
335 buf[bytes_read] = '\0';
346 /* need to check size in case error due to buf being too small
347 * for read() or exited loop due to buf being full
349 buf[max_read] = '\0';
351 rc = bytes_remaining(fd, &remaining);
356 *buflen = bytes_read + remaining;
358 /* file was not (*buflen - 1) bytes, add 1 for reallocating */
359 if (remaining != 0) {
370 static void print_obd_line(char *s)
372 const char *param = "osc/%s/ost_conn_uuid";
373 char obd_name[MAX_OBD_NAME];
374 char buf[MAX_OBD_NAME];
379 /* obd device type is the first 3 characters of param name */
380 snprintf(buf, sizeof(buf), " %%*d %%*s %.3s %%%zus %%*s %%*d ",
381 param, sizeof(obd_name) - 1);
382 if (sscanf(s, buf, obd_name) == 0)
384 if (cfs_get_param_paths(&path, param, obd_name) != 0)
386 fp = fopen(path.gl_pathv[0], "r");
388 /* need to free path data before retry */
389 cfs_free_param_data(&path);
391 if (param[0] == 'o') { /* failed with osc, try mdc */
392 param = "mdc/%s/mds_conn_uuid";
399 /* should not ignore fgets(3)'s return value */
400 if (!fgets(buf, sizeof(buf), fp)) {
401 fprintf(stderr, "reading from %s: %s", buf, strerror(errno));
407 cfs_free_param_data(&path);
409 /* trim trailing newlines */
410 ptr = strrchr(buf, '\n');
414 ptr = strrchr(s, '\n');
417 printf("%s%s%s\n", s, buf[0] ? " " : "", buf);
420 static int print_out_devices(yaml_parser_t *reply, enum lctl_param_flags flags)
422 char buf[PATH_MAX / 2], *tmp = NULL;
423 size_t buf_len = sizeof(buf);
428 if (flags & PARAM_FLAGS_SHOW_SOURCE) {
429 snprintf(buf, buf_len, "devices=");
432 bzero(buf, sizeof(buf));
435 rc = yaml_parser_parse(reply, &event);
439 if (event.type == YAML_MAPPING_START_EVENT) {
440 size_t len = strlen(buf);
442 if (len > 0 && strcmp(buf, "devices=\n") != 0) {
443 /* eat last white space */
445 if (flags & PARAM_FLAGS_EXTRA_DETAILS)
450 bzero(buf, sizeof(buf));
454 if (event.type == YAML_SCALAR_EVENT) {
455 char *value = (char *)event.data.scalar.value;
457 if (strcmp(value, "index") == 0) {
458 yaml_event_delete(&event);
459 rc = yaml_parser_parse(reply, &event);
463 value = (char *)event.data.scalar.value;
464 snprintf(tmp, buf_len, "%3s ", value);
469 if (strcmp(value, "status") == 0 ||
470 strcmp(value, "type") == 0 ||
471 strcmp(value, "name") == 0 ||
472 strcmp(value, "uuid") == 0 ||
473 strcmp(value, "refcount") == 0) {
474 yaml_event_delete(&event);
475 rc = yaml_parser_parse(reply, &event);
479 value = (char *)event.data.scalar.value;
480 snprintf(tmp, buf_len, "%s ", value);
481 buf_len -= strlen(value) + 1;
482 tmp += strlen(value) + 1;
486 done = (event.type == YAML_DOCUMENT_END_EVENT);
488 size_t len = strlen(buf);
491 /* eat last white space */
493 if (flags & PARAM_FLAGS_EXTRA_DETAILS)
498 bzero(buf, sizeof(buf));
501 yaml_event_delete(&event);
507 int lcfg_param_get_yaml(yaml_parser_t *reply, struct nl_sock *sk,
508 int version, char *pattern)
510 char source[PATH_MAX / 2], group[GENL_NAMSIZ + 1];
511 char *family = "lustre", *tmp;
512 yaml_emitter_t request;
517 bzero(source, sizeof(source));
518 /* replace '/' with '.' to match conf_param and sysctl */
519 for (tmp = strchr(pattern, '/'); tmp != NULL;
520 tmp = strchr(tmp, '/'))
523 tmp = strrchr(pattern, '.');
525 size_t len = tmp - pattern;
527 strncpy(group, tmp + 1, GENL_NAMSIZ);
528 strncpy(source, pattern, len);
530 strncpy(group, pattern, GENL_NAMSIZ);
533 if (strcmp(group, "devices") == 0)
534 cmd = LUSTRE_CMD_DEVICES;
539 /* Setup parser to recieve Netlink packets */
540 rc = yaml_parser_initialize(reply);
544 rc = yaml_parser_set_input_netlink(reply, sk, false);
548 /* Create Netlink emitter to send request to kernel */
549 yaml_emitter_initialize(&request);
550 rc = yaml_emitter_set_output_netlink(&request, sk,
556 yaml_emitter_open(&request);
558 yaml_document_start_event_initialize(&event, NULL, NULL, NULL, 0);
559 rc = yaml_emitter_emit(&request, &event);
563 yaml_mapping_start_event_initialize(&event, NULL,
564 (yaml_char_t *)YAML_MAP_TAG,
565 1, YAML_ANY_MAPPING_STYLE);
566 rc = yaml_emitter_emit(&request, &event);
570 yaml_scalar_event_initialize(&event, NULL,
571 (yaml_char_t *)YAML_STR_TAG,
572 (yaml_char_t *)group,
574 YAML_PLAIN_SCALAR_STYLE);
575 rc = yaml_emitter_emit(&request, &event);
580 const char *key = cmd == LUSTRE_CMD_DEVICES ? "name" : "source";
582 /* Now fill in 'path' filter */
583 yaml_sequence_start_event_initialize(&event, NULL,
584 (yaml_char_t *)YAML_SEQ_TAG,
585 1, YAML_ANY_SEQUENCE_STYLE);
586 rc = yaml_emitter_emit(&request, &event);
590 yaml_mapping_start_event_initialize(&event, NULL,
591 (yaml_char_t *)YAML_MAP_TAG,
592 1, YAML_ANY_MAPPING_STYLE);
593 rc = yaml_emitter_emit(&request, &event);
597 yaml_scalar_event_initialize(&event, NULL,
598 (yaml_char_t *)YAML_STR_TAG,
599 (yaml_char_t *)key, strlen(key),
600 1, 0, YAML_PLAIN_SCALAR_STYLE);
601 rc = yaml_emitter_emit(&request, &event);
605 yaml_scalar_event_initialize(&event, NULL,
606 (yaml_char_t *)YAML_STR_TAG,
607 (yaml_char_t *)source,
608 strlen(source), 1, 0,
609 YAML_PLAIN_SCALAR_STYLE);
610 rc = yaml_emitter_emit(&request, &event);
614 yaml_mapping_end_event_initialize(&event);
615 rc = yaml_emitter_emit(&request, &event);
619 yaml_sequence_end_event_initialize(&event);
620 rc = yaml_emitter_emit(&request, &event);
624 yaml_scalar_event_initialize(&event, NULL,
625 (yaml_char_t *)YAML_STR_TAG,
628 YAML_PLAIN_SCALAR_STYLE);
629 rc = yaml_emitter_emit(&request, &event);
633 yaml_mapping_end_event_initialize(&event);
634 rc = yaml_emitter_emit(&request, &event);
638 yaml_document_end_event_initialize(&event, 0);
639 rc = yaml_emitter_emit(&request, &event);
643 yaml_emitter_close(&request);
646 yaml_emitter_log_error(&request, stderr);
649 yaml_emitter_delete(&request);
651 return rc == 1 ? 0 : -EINVAL;
654 int llapi_param_display_value(char *path, int version,
655 enum lctl_param_flags flags, FILE *fp)
661 /* version zero means just list sources. "devices is special case */
662 if (!version && strcmp(path, "devices") == 0) {
663 fprintf(fp, "devices\n");
667 sk = nl_socket_alloc();
671 rc = lcfg_param_get_yaml(&reply, sk, version, path);
675 if (flags & PARAM_FLAGS_YAML_FORMAT) {
676 yaml_document_t results;
677 yaml_emitter_t output;
679 /* load the reply results */
680 rc = yaml_parser_load(&reply, &results);
682 yaml_parser_log_error(&reply, stderr, "get_param: ");
683 yaml_document_delete(&results);
688 /* create emitter to output results */
689 rc = yaml_emitter_initialize(&output);
691 yaml_emitter_set_output_file(&output, fp);
693 rc = yaml_emitter_dump(&output, &results);
696 yaml_document_delete(&results);
698 yaml_emitter_log_error(&output, stderr);
701 yaml_emitter_delete(&output);
707 rc = yaml_parser_parse(&reply, &event);
711 if (event.type == YAML_SCALAR_EVENT) {
712 char *value = (char *)event.data.scalar.value;
714 if (strcmp(value, "devices") == 0)
715 rc = print_out_devices(&reply, flags);
720 done = (event.type == YAML_STREAM_END_EVENT);
721 yaml_event_delete(&event);
725 yaml_parser_log_error(&reply, stderr, "get_param: ");
730 yaml_parser_delete(&reply);
732 return rc == 1 ? 0 : rc;
736 * Read the value of the file with location \a path
739 * \param path[in] the location of a parameter file
740 * \param buf[in,out] a pointer to a pointer to a buffer
741 * \param buflen[in,out] the length of a pre-allocated buffer
742 * when passed in, and either the number
743 * of bytes written or the suggested
744 * size of *buf when passed out.
746 * There are 3 behaviors based on the value of buf.
747 * If buf == NULL, then the buffer size needed to read the file at
748 * \a path will be written to \a *buflen.
749 * If \a buf != NULL and \a *buf == NULL, the value of *buf will point
750 * to a buffer that will be automatically sized to fit the file
751 * contents. A NUL byte will be added to the end of the buffer.
752 * The value of \a *buflen will be set to the number of bytes written
753 * excuding the NUL byte.
754 * If \a buf != NULL and \a *buf != NULL, it will be assumed that \a *buf
755 * points to a pre-allocated buffer with a capacity of \a *buflen.
756 * If there is sufficient space, the file contents and NUL terminating
757 * byte will be written to the buffer at .\a *buf.
758 * Otherwise, the required size of \a *buflen with be written to \a *buflen.
760 * Returns 0 for success with null terminated string in \a *buf.
761 * Returns negative errno value on error.
762 * For case of \a buf != NULL and \a *buf != NULL, a return value
763 * of -EOVERFLOW indicates that it's possible retry with a larger
766 int llapi_param_get_value(const char *path, char **buf, size_t *buflen)
770 if (path == NULL || buflen == NULL)
772 else if (buf == NULL)
773 rc = required_size(path, buflen);
774 /* handle for buffer, but no buffer
775 * create a buffer of the required size
777 else if (*buf == NULL)
778 rc = copy_file_expandable(path, buf, buflen);
779 /* preallocated buffer given, attempt to copy
780 * file to it, return file size if buffer too small
783 rc = copy_file_fixed(path, *buf, buflen);
790 void llapi_param_paths_free(glob_t *paths)
792 cfs_free_param_data(paths);