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));
452 buf_len = sizeof(buf);
455 if (event.type == YAML_SCALAR_EVENT) {
456 char *value = (char *)event.data.scalar.value;
458 if (strcmp(value, "index") == 0) {
459 yaml_event_delete(&event);
460 rc = yaml_parser_parse(reply, &event);
464 value = (char *)event.data.scalar.value;
465 snprintf(tmp, buf_len, "%3s ", value);
470 if (strcmp(value, "status") == 0 ||
471 strcmp(value, "type") == 0 ||
472 strcmp(value, "name") == 0 ||
473 strcmp(value, "uuid") == 0 ||
474 strcmp(value, "refcount") == 0) {
475 yaml_event_delete(&event);
476 rc = yaml_parser_parse(reply, &event);
480 value = (char *)event.data.scalar.value;
481 snprintf(tmp, buf_len, "%s ", value);
482 buf_len -= strlen(value) + 1;
483 tmp += strlen(value) + 1;
487 done = (event.type == YAML_DOCUMENT_END_EVENT);
489 size_t len = strlen(buf);
492 /* eat last white space */
494 if (flags & PARAM_FLAGS_EXTRA_DETAILS)
499 bzero(buf, sizeof(buf));
502 yaml_event_delete(&event);
508 int lcfg_param_get_yaml(yaml_parser_t *reply, struct nl_sock *sk,
509 int version, char *pattern)
511 char source[PATH_MAX / 2], group[GENL_NAMSIZ + 1];
512 char *family = "lustre", *tmp;
513 yaml_emitter_t request;
518 bzero(source, sizeof(source));
519 /* replace '/' with '.' to match conf_param and sysctl */
520 for (tmp = strchr(pattern, '/'); tmp != NULL;
521 tmp = strchr(tmp, '/'))
524 tmp = strrchr(pattern, '.');
526 size_t len = tmp - pattern;
528 strncpy(group, tmp + 1, GENL_NAMSIZ);
529 strncpy(source, pattern, len);
531 strncpy(group, pattern, GENL_NAMSIZ);
534 if (strcmp(group, "devices") == 0)
535 cmd = LUSTRE_CMD_DEVICES;
540 /* Setup parser to recieve Netlink packets */
541 rc = yaml_parser_initialize(reply);
545 rc = yaml_parser_set_input_netlink(reply, sk, false);
549 /* Create Netlink emitter to send request to kernel */
550 yaml_emitter_initialize(&request);
551 rc = yaml_emitter_set_output_netlink(&request, sk,
557 yaml_emitter_open(&request);
559 yaml_document_start_event_initialize(&event, NULL, NULL, NULL, 0);
560 rc = yaml_emitter_emit(&request, &event);
564 yaml_mapping_start_event_initialize(&event, NULL,
565 (yaml_char_t *)YAML_MAP_TAG,
566 1, YAML_ANY_MAPPING_STYLE);
567 rc = yaml_emitter_emit(&request, &event);
571 yaml_scalar_event_initialize(&event, NULL,
572 (yaml_char_t *)YAML_STR_TAG,
573 (yaml_char_t *)group,
575 YAML_PLAIN_SCALAR_STYLE);
576 rc = yaml_emitter_emit(&request, &event);
581 const char *key = cmd == LUSTRE_CMD_DEVICES ? "name" : "source";
583 /* Now fill in 'path' filter */
584 yaml_sequence_start_event_initialize(&event, NULL,
585 (yaml_char_t *)YAML_SEQ_TAG,
586 1, YAML_ANY_SEQUENCE_STYLE);
587 rc = yaml_emitter_emit(&request, &event);
591 yaml_mapping_start_event_initialize(&event, NULL,
592 (yaml_char_t *)YAML_MAP_TAG,
593 1, YAML_ANY_MAPPING_STYLE);
594 rc = yaml_emitter_emit(&request, &event);
598 yaml_scalar_event_initialize(&event, NULL,
599 (yaml_char_t *)YAML_STR_TAG,
600 (yaml_char_t *)key, strlen(key),
601 1, 0, YAML_PLAIN_SCALAR_STYLE);
602 rc = yaml_emitter_emit(&request, &event);
606 yaml_scalar_event_initialize(&event, NULL,
607 (yaml_char_t *)YAML_STR_TAG,
608 (yaml_char_t *)source,
609 strlen(source), 1, 0,
610 YAML_PLAIN_SCALAR_STYLE);
611 rc = yaml_emitter_emit(&request, &event);
615 yaml_mapping_end_event_initialize(&event);
616 rc = yaml_emitter_emit(&request, &event);
620 yaml_sequence_end_event_initialize(&event);
621 rc = yaml_emitter_emit(&request, &event);
625 yaml_scalar_event_initialize(&event, NULL,
626 (yaml_char_t *)YAML_STR_TAG,
629 YAML_PLAIN_SCALAR_STYLE);
630 rc = yaml_emitter_emit(&request, &event);
634 yaml_mapping_end_event_initialize(&event);
635 rc = yaml_emitter_emit(&request, &event);
639 yaml_document_end_event_initialize(&event, 0);
640 rc = yaml_emitter_emit(&request, &event);
644 yaml_emitter_close(&request);
647 yaml_emitter_log_error(&request, stderr);
650 yaml_emitter_delete(&request);
652 return rc == 1 ? 0 : -EINVAL;
655 int llapi_param_display_value(char *path, int version,
656 enum lctl_param_flags flags, FILE *fp)
662 /* version zero means just list sources. "devices is special case */
663 if (!version && strcmp(path, "devices") == 0) {
664 fprintf(fp, "devices\n");
668 sk = nl_socket_alloc();
672 rc = lcfg_param_get_yaml(&reply, sk, version, path);
676 if (flags & PARAM_FLAGS_YAML_FORMAT) {
677 yaml_document_t results;
678 yaml_emitter_t output;
680 /* load the reply results */
681 rc = yaml_parser_load(&reply, &results);
683 yaml_parser_log_error(&reply, stderr, "get_param: ");
684 yaml_document_delete(&results);
689 /* create emitter to output results */
690 rc = yaml_emitter_initialize(&output);
692 yaml_emitter_set_output_file(&output, fp);
694 rc = yaml_emitter_dump(&output, &results);
697 yaml_document_delete(&results);
699 yaml_emitter_log_error(&output, stderr);
702 yaml_emitter_delete(&output);
708 rc = yaml_parser_parse(&reply, &event);
712 if (event.type == YAML_SCALAR_EVENT) {
713 char *value = (char *)event.data.scalar.value;
715 if (strcmp(value, "devices") == 0)
716 rc = print_out_devices(&reply, flags);
721 done = (event.type == YAML_STREAM_END_EVENT);
722 yaml_event_delete(&event);
726 yaml_parser_log_error(&reply, stderr, "get_param: ");
731 yaml_parser_delete(&reply);
733 return rc == 1 ? 0 : rc;
737 * Read the value of the file with location \a path
740 * \param path[in] the location of a parameter file
741 * \param buf[in,out] a pointer to a pointer to a buffer
742 * \param buflen[in,out] the length of a pre-allocated buffer
743 * when passed in, and either the number
744 * of bytes written or the suggested
745 * size of *buf when passed out.
747 * There are 3 behaviors based on the value of buf.
748 * If buf == NULL, then the buffer size needed to read the file at
749 * \a path will be written to \a *buflen.
750 * If \a buf != NULL and \a *buf == NULL, the value of *buf will point
751 * to a buffer that will be automatically sized to fit the file
752 * contents. A NUL byte will be added to the end of the buffer.
753 * The value of \a *buflen will be set to the number of bytes written
754 * excuding the NUL byte.
755 * If \a buf != NULL and \a *buf != NULL, it will be assumed that \a *buf
756 * points to a pre-allocated buffer with a capacity of \a *buflen.
757 * If there is sufficient space, the file contents and NUL terminating
758 * byte will be written to the buffer at .\a *buf.
759 * Otherwise, the required size of \a *buflen with be written to \a *buflen.
761 * Returns 0 for success with null terminated string in \a *buf.
762 * Returns negative errno value on error.
763 * For case of \a buf != NULL and \a *buf != NULL, a return value
764 * of -EOVERFLOW indicates that it's possible retry with a larger
767 int llapi_param_get_value(const char *path, char **buf, size_t *buflen)
771 if (path == NULL || buflen == NULL)
773 else if (buf == NULL)
774 rc = required_size(path, buflen);
775 /* handle for buffer, but no buffer
776 * create a buffer of the required size
778 else if (*buf == NULL)
779 rc = copy_file_expandable(path, buf, buflen);
780 /* preallocated buffer given, attempt to copy
781 * file to it, return file size if buffer too small
784 rc = copy_file_fixed(path, *buf, buflen);
791 void llapi_param_paths_free(glob_t *paths)
793 cfs_free_param_data(paths);