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)
205 size_t bytes_read = 0;
210 page_size = sysconf(_SC_PAGESIZE);
214 temp_buf = malloc(page_size);
215 if (temp_buf == NULL)
219 ssize_t count = read(fd, temp_buf, page_size);
222 *file_size = bytes_read;
238 * Determine the size of a file by reading it.
240 static int required_size(const char *path, size_t *file_size)
245 fd = open(path, O_RDONLY);
249 rc = bytes_remaining(fd, file_size);
257 int copy_file_expandable(const char *path, char **buf, size_t *file_size)
264 page_size = sysconf(_SC_PAGESIZE);
270 fp = open_memstream(buf, file_size);
276 fd = open(path, O_RDONLY);
282 temp_buf = calloc(1, page_size);
289 ssize_t count = read(fd, temp_buf, page_size);
298 if (fwrite(temp_buf, 1, count, fp) != count) {
310 /* If rc != 0 and *buf != NULL, the caller may retry.
311 * This would likely result in copy_file_fixed() being called
312 * on accident, and a likely memory error.
322 * Copy file to a buffer and write the number of bytes copied
324 static int copy_file_fixed(const char *path, char *buf, size_t *buflen)
327 size_t bytes_read = 0;
328 size_t max_read = *buflen - 1;
329 size_t remaining = 0;
332 fd = open(path, O_RDONLY);
336 while (bytes_read < max_read) {
337 ssize_t count = read(fd,
339 max_read - bytes_read);
341 /* read the entire file */
343 *buflen = bytes_read + 1;
344 buf[bytes_read] = '\0';
355 /* need to check size in case error due to buf being too small
356 * for read() or exited loop due to buf being full
358 buf[max_read] = '\0';
360 rc = bytes_remaining(fd, &remaining);
365 *buflen = bytes_read + remaining;
367 /* file was not (*buflen - 1) bytes, add 1 for reallocating */
368 if (remaining != 0) {
379 static void print_obd_line(char *s)
381 const char *param = "osc/%s/ost_conn_uuid";
382 char obd_name[MAX_OBD_NAME];
383 char buf[MAX_OBD_NAME];
388 /* obd device type is the first 3 characters of param name */
389 snprintf(buf, sizeof(buf), " %%*d %%*s %.3s %%%zus %%*s %%*d ",
390 param, sizeof(obd_name) - 1);
391 if (sscanf(s, buf, obd_name) == 0)
393 if (cfs_get_param_paths(&path, param, obd_name) != 0)
395 fp = fopen(path.gl_pathv[0], "r");
397 /* need to free path data before retry */
398 cfs_free_param_data(&path);
400 if (param[0] == 'o') { /* failed with osc, try mdc */
401 param = "mdc/%s/mds_conn_uuid";
408 /* should not ignore fgets(3)'s return value */
409 if (!fgets(buf, sizeof(buf), fp)) {
410 fprintf(stderr, "reading from %s: %s", buf, strerror(errno));
416 cfs_free_param_data(&path);
418 /* trim trailing newlines */
419 ptr = strrchr(buf, '\n');
423 ptr = strrchr(s, '\n');
426 printf("%s%s%s\n", s, buf[0] ? " " : "", buf);
429 static int print_out_devices(yaml_parser_t *reply, enum lctl_param_flags flags)
431 char buf[PATH_MAX / 2], *tmp = NULL;
432 size_t buf_len = sizeof(buf);
437 if (flags & PARAM_FLAGS_SHOW_SOURCE) {
438 snprintf(buf, buf_len, "devices=");
441 bzero(buf, sizeof(buf));
444 rc = yaml_parser_parse(reply, &event);
448 if (event.type == YAML_MAPPING_START_EVENT) {
449 size_t len = strlen(buf);
451 if (len > 0 && strcmp(buf, "devices=\n") != 0) {
452 /* eat last white space */
454 if (flags & PARAM_FLAGS_EXTRA_DETAILS)
459 bzero(buf, sizeof(buf));
461 buf_len = sizeof(buf);
464 if (event.type == YAML_SCALAR_EVENT) {
465 char *value = (char *)event.data.scalar.value;
467 if (strcmp(value, "index") == 0) {
468 yaml_event_delete(&event);
469 rc = yaml_parser_parse(reply, &event);
473 value = (char *)event.data.scalar.value;
474 snprintf(tmp, buf_len, "%3s ", value);
479 if (strcmp(value, "status") == 0 ||
480 strcmp(value, "type") == 0 ||
481 strcmp(value, "name") == 0 ||
482 strcmp(value, "uuid") == 0 ||
483 strcmp(value, "refcount") == 0) {
484 yaml_event_delete(&event);
485 rc = yaml_parser_parse(reply, &event);
489 value = (char *)event.data.scalar.value;
490 snprintf(tmp, buf_len, "%s ", value);
491 buf_len -= strlen(value) + 1;
492 tmp += strlen(value) + 1;
496 done = (event.type == YAML_DOCUMENT_END_EVENT);
498 size_t len = strlen(buf);
501 /* eat last white space */
503 if (flags & PARAM_FLAGS_EXTRA_DETAILS)
508 bzero(buf, sizeof(buf));
511 yaml_event_delete(&event);
517 static int lcfg_param_get_yaml(yaml_parser_t *reply, struct nl_sock *sk,
518 int version, char *pattern)
520 char source[PATH_MAX / 2], group[GENL_NAMSIZ + 1];
521 char *family = "lustre", *tmp;
522 yaml_emitter_t request;
527 bzero(source, sizeof(source));
528 /* replace '/' with '.' to match conf_param and sysctl */
529 for (tmp = strchr(pattern, '/'); tmp != NULL;
530 tmp = strchr(tmp, '/'))
533 tmp = strrchr(pattern, '.');
535 size_t len = tmp - pattern;
537 strncpy(group, tmp + 1, GENL_NAMSIZ);
538 strncpy(source, pattern, len);
540 strncpy(group, pattern, GENL_NAMSIZ);
543 if (strcmp(group, "devices") == 0)
544 cmd = LUSTRE_CMD_DEVICES;
549 /* Setup parser to recieve Netlink packets */
550 rc = yaml_parser_initialize(reply);
554 rc = yaml_parser_set_input_netlink(reply, sk, false);
558 /* Create Netlink emitter to send request to kernel */
559 yaml_emitter_initialize(&request);
560 rc = yaml_emitter_set_output_netlink(&request, sk,
566 yaml_emitter_open(&request);
568 yaml_document_start_event_initialize(&event, NULL, NULL, NULL, 0);
569 rc = yaml_emitter_emit(&request, &event);
573 yaml_mapping_start_event_initialize(&event, NULL,
574 (yaml_char_t *)YAML_MAP_TAG,
575 1, YAML_ANY_MAPPING_STYLE);
576 rc = yaml_emitter_emit(&request, &event);
580 yaml_scalar_event_initialize(&event, NULL,
581 (yaml_char_t *)YAML_STR_TAG,
582 (yaml_char_t *)group,
584 YAML_PLAIN_SCALAR_STYLE);
585 rc = yaml_emitter_emit(&request, &event);
590 const char *key = cmd == LUSTRE_CMD_DEVICES ? "name" : "source";
592 /* Now fill in 'path' filter */
593 yaml_sequence_start_event_initialize(&event, NULL,
594 (yaml_char_t *)YAML_SEQ_TAG,
595 1, YAML_ANY_SEQUENCE_STYLE);
596 rc = yaml_emitter_emit(&request, &event);
600 yaml_mapping_start_event_initialize(&event, NULL,
601 (yaml_char_t *)YAML_MAP_TAG,
602 1, YAML_ANY_MAPPING_STYLE);
603 rc = yaml_emitter_emit(&request, &event);
607 yaml_scalar_event_initialize(&event, NULL,
608 (yaml_char_t *)YAML_STR_TAG,
609 (yaml_char_t *)key, strlen(key),
610 1, 0, YAML_PLAIN_SCALAR_STYLE);
611 rc = yaml_emitter_emit(&request, &event);
615 yaml_scalar_event_initialize(&event, NULL,
616 (yaml_char_t *)YAML_STR_TAG,
617 (yaml_char_t *)source,
618 strlen(source), 1, 0,
619 YAML_PLAIN_SCALAR_STYLE);
620 rc = yaml_emitter_emit(&request, &event);
624 yaml_mapping_end_event_initialize(&event);
625 rc = yaml_emitter_emit(&request, &event);
629 yaml_sequence_end_event_initialize(&event);
630 rc = yaml_emitter_emit(&request, &event);
634 yaml_scalar_event_initialize(&event, NULL,
635 (yaml_char_t *)YAML_STR_TAG,
638 YAML_PLAIN_SCALAR_STYLE);
639 rc = yaml_emitter_emit(&request, &event);
643 yaml_mapping_end_event_initialize(&event);
644 rc = yaml_emitter_emit(&request, &event);
648 yaml_document_end_event_initialize(&event, 0);
649 rc = yaml_emitter_emit(&request, &event);
653 yaml_emitter_close(&request);
656 yaml_emitter_log_error(&request, stderr);
659 yaml_emitter_delete(&request);
661 return rc == 1 ? 0 : -EINVAL;
664 int llapi_param_display_value(char *path, int version,
665 enum lctl_param_flags flags, FILE *fp)
671 /* version zero means just list sources. "devices is special case */
672 if (!version && strcmp(path, "devices") == 0) {
673 fprintf(fp, "devices\n");
677 sk = nl_socket_alloc();
681 rc = lcfg_param_get_yaml(&reply, sk, version, path);
685 if (flags & PARAM_FLAGS_YAML_FORMAT) {
686 yaml_document_t results;
687 yaml_emitter_t output;
689 /* load the reply results */
690 rc = yaml_parser_load(&reply, &results);
692 yaml_parser_log_error(&reply, stderr, "get_param: ");
693 yaml_document_delete(&results);
698 /* create emitter to output results */
699 rc = yaml_emitter_initialize(&output);
701 yaml_emitter_set_output_file(&output, fp);
703 rc = yaml_emitter_dump(&output, &results);
706 yaml_document_delete(&results);
708 yaml_emitter_log_error(&output, stderr);
711 yaml_emitter_delete(&output);
717 rc = yaml_parser_parse(&reply, &event);
721 if (event.type == YAML_SCALAR_EVENT) {
722 char *value = (char *)event.data.scalar.value;
724 if (strcmp(value, "devices") == 0)
725 rc = print_out_devices(&reply, flags);
730 done = (event.type == YAML_STREAM_END_EVENT);
731 yaml_event_delete(&event);
735 yaml_parser_log_error(&reply, stderr, "get_param: ");
740 yaml_parser_delete(&reply);
742 return rc == 1 ? 0 : rc;
746 * Read the value of the file with location \a path
749 * \param path[in] the location of a parameter file
750 * \param buf[in,out] a pointer to a pointer to a buffer
751 * \param buflen[in,out] the length of a pre-allocated buffer
752 * when passed in, and either the number
753 * of bytes written or the suggested
754 * size of *buf when passed out.
756 * There are 3 behaviors based on the value of buf.
757 * If buf == NULL, then the buffer size needed to read the file at
758 * \a path will be written to \a *buflen.
759 * If \a buf != NULL and \a *buf == NULL, the value of *buf will point
760 * to a buffer that will be automatically sized to fit the file
761 * contents. A NUL byte will be added to the end of the buffer.
762 * The value of \a *buflen will be set to the number of bytes written
763 * excuding the NUL byte.
764 * If \a buf != NULL and \a *buf != NULL, it will be assumed that \a *buf
765 * points to a pre-allocated buffer with a capacity of \a *buflen.
766 * If there is sufficient space, the file contents and NUL terminating
767 * byte will be written to the buffer at .\a *buf.
768 * Otherwise, the required size of \a *buflen with be written to \a *buflen.
770 * Returns 0 for success with null terminated string in \a *buf.
771 * Returns negative errno value on error.
772 * For case of \a buf != NULL and \a *buf != NULL, a return value
773 * of -EOVERFLOW indicates that it's possible retry with a larger
776 int llapi_param_get_value(const char *path, char **buf, size_t *buflen)
780 if (path == NULL || buflen == NULL)
782 else if (buf == NULL)
783 rc = required_size(path, buflen);
784 /* handle for buffer, but no buffer
785 * create a buffer of the required size
787 else if (*buf == NULL)
788 rc = copy_file_expandable(path, buf, buflen);
789 /* preallocated buffer given, attempt to copy
790 * file to it, return file size if buffer too small
793 rc = copy_file_fixed(path, *buf, buflen);
800 void llapi_param_paths_free(glob_t *paths)
802 cfs_free_param_data(paths);