Whamcloud - gitweb
LU-17308 mgs: move pool_cmd check to the kernel
[fs/lustre-release.git] / lustre / utils / liblustreapi_param.c
1 /*
2  * LGPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
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
11  *
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.
16  *
17  * LGPL HEADER END
18  */
19 /*
20  * lustre/utils/liblustreapi_param.c
21  *
22  * This code handles user interaction with the configuration interface
23  * to the Lustre file system to fine tune it.
24  *
25  * Copyright (c) 2016, Intel Corporation.
26  */
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <stdint.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <unistd.h>
35 #include <yaml.h>
36
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"
43
44 /**
45  * return the parameter's path for a specific device type or mountpoint
46  *
47  * \param param         the results returned to the caller
48  * \param obd_type      Lustre OBD device type
49  *
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.
55  *
56  * \param param_name    parameter name to fetch
57  *
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.
64  *
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.
69  *
70  * Return 0 for success, with the results stored in \param param.
71  * Return -ve value for error.
72  */
73 int
74 get_lustre_param_path(const char *obd_type, const char *filter,
75                       enum param_filter type, const char *param_name,
76                       glob_t *param)
77 {
78         char pattern[PATH_MAX];
79         int rc = 0;
80
81         if (filter == NULL && type != FILTER_BY_NONE)
82                 return -EINVAL;
83
84         switch (type) {
85         case FILTER_BY_PATH:
86                 rc = llapi_search_fsname(filter, pattern);
87                 if (rc) {
88                         llapi_error(LLAPI_MSG_ERROR, rc,
89                                     "'%s' is not on a Lustre filesystem",
90                                     filter);
91                         return rc;
92                 }
93                 if (strlen(pattern) + 3 > sizeof(pattern))
94                         return -E2BIG;
95                 strncat(pattern, "-*", sizeof(pattern) - 1);
96                 break;
97         case FILTER_BY_FS_NAME:
98                 rc = snprintf(pattern, sizeof(pattern) - 1, "%s-*", filter);
99                 if (rc < 0)
100                         return rc;
101                 else if (rc >= sizeof(pattern))
102                         return -EINVAL;
103                 rc = 0;
104                 break;
105         case FILTER_BY_EXACT:
106                 if (strlen(filter) + 1 > sizeof(pattern))
107                         return -E2BIG;
108                 strncpy(pattern, filter, sizeof(pattern));
109                 break;
110         case FILTER_BY_NONE:
111         default:
112                 break;
113         }
114
115         if (type == FILTER_BY_NONE) {
116                 if (cfs_get_param_paths(param, "%s", param_name) != 0)
117                         rc = -errno;
118         } else if (param_name != NULL) {
119                 if (cfs_get_param_paths(param, "%s/%s/%s",
120                                        obd_type, pattern, param_name) != 0)
121                         rc = -errno;
122         } else {
123                 if (cfs_get_param_paths(param, "%s/%s",
124                                        obd_type, pattern) != 0)
125                         rc = -errno;
126         }
127
128         return rc;
129 }
130
131 /**
132  * return a parameter of a single line value for a specific device type
133  * or mountpoint
134  *
135  * \param obd_type      Lustre OBD device type
136  *
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.
142  *
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
146  *
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.
153  *
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.
158  *
159  * Return 0 for success, with a NUL-terminated string in \param value.
160  * Return negative errno value for error.
161  */
162 int
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)
166 {
167         glob_t param;
168         FILE *fp;
169         int rc;
170
171         rc = get_lustre_param_path(obd_type, filter, type, param_name, &param);
172         if (rc != 0)
173                 return -ENOENT;
174
175         fp = fopen(param.gl_pathv[0], "r");
176         if (fp == NULL) {
177                 rc = -errno;
178                 llapi_error(LLAPI_MSG_ERROR, rc, "error: opening '%s'",
179                             param.gl_pathv[0]);
180                 goto err;
181         }
182
183         if (fgets(value, val_len, fp) == NULL) {
184                 if (!feof(fp))
185                         rc = -ferror(fp);
186         }
187         fclose(fp);
188 err:
189         cfs_free_param_data(&param);
190
191         return rc;
192 }
193
194 int llapi_param_get_paths(const char *pattern, glob_t *paths)
195 {
196         return get_lustre_param_path(NULL, NULL, FILTER_BY_NONE,
197                                      pattern, paths);
198 }
199
200 /**
201  *  Read to the end of the file and count the bytes read.
202  */
203 static int bytes_remaining(int fd, size_t *file_size)
204 {
205         int rc = 0;
206         size_t bytes_read = 0;
207         long page_size = sysconf(_SC_PAGESIZE);
208         char *temp_buf;
209
210         temp_buf = malloc(page_size);
211         if (temp_buf == NULL)
212                 return -ENOMEM;
213
214         while (1) {
215                 ssize_t count = read(fd, temp_buf, page_size);
216
217                 if (count == 0) {
218                         *file_size = bytes_read;
219                         break;
220                 }
221
222                 if (count < 0) {
223                         rc = -errno;
224                         break;
225                 }
226                 bytes_read += count;
227         }
228
229         free(temp_buf);
230         return rc;
231 }
232
233 /**
234  *  Determine the size of a file by reading it.
235  */
236 static int required_size(const char *path, size_t *file_size)
237 {
238         int rc = 0;
239         int fd;
240
241         fd = open(path, O_RDONLY);
242         if (fd < 0)
243                 return -errno;
244
245         rc = bytes_remaining(fd, file_size);
246
247         close(fd);
248         *file_size += 1;
249         return rc;
250 }
251
252 static
253 int copy_file_expandable(const char *path, char **buf, size_t *file_size)
254 {
255         long page_size = sysconf(_SC_PAGESIZE);
256         int rc = 0;
257         char *temp_buf;
258         int fd;
259         FILE *fp;
260
261         fp = open_memstream(buf, file_size);
262         if (fp == NULL) {
263                 rc = -errno;
264                 goto out;
265         }
266
267         fd = open(path, O_RDONLY);
268         if (fd < 0) {
269                 rc = -errno;
270                 goto close_stream;
271         }
272
273         temp_buf = calloc(1, page_size);
274         if (buf == NULL) {
275                 rc = -ENOMEM;
276                 goto close_file;
277         }
278
279         while (1) {
280                 ssize_t count = read(fd, temp_buf, page_size);
281
282                 if (count == 0)
283                         break;
284                 if (count < 0) {
285                         rc = -errno;
286                         break;
287                 }
288
289                 if (fwrite(temp_buf, 1, count, fp) != count) {
290                         rc = -errno;
291                         break;
292                 }
293         }
294
295         free(temp_buf);
296 close_file:
297         close(fd);
298 close_stream:
299         fclose(fp);
300 out:
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.
304          */
305         if (rc != 0) {
306                 free(*buf);
307                 *buf = NULL;
308         }
309         return rc;
310 }
311
312 /**
313  *  Copy file to a buffer and write the number of bytes copied
314  */
315 static int copy_file_fixed(const char *path, char *buf, size_t *buflen)
316 {
317         int rc = 0;
318         size_t bytes_read = 0;
319         size_t max_read = *buflen - 1;
320         size_t remaining = 0;
321         int fd;
322
323         fd = open(path, O_RDONLY);
324         if (fd < 0)
325                 return -errno;
326
327         while (bytes_read < max_read) {
328                 ssize_t count = read(fd,
329                                      buf + bytes_read,
330                                      max_read - bytes_read);
331
332                 /* read the entire file */
333                 if (count == 0) {
334                         *buflen = bytes_read + 1;
335                         buf[bytes_read] = '\0';
336                         goto out;
337                 }
338
339                 if (count < 0)
340                         goto check_size;
341
342                 bytes_read += count;
343         }
344
345 check_size:
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
348          */
349         buf[max_read] = '\0';
350
351         rc = bytes_remaining(fd, &remaining);
352         if (rc != 0) {
353                 rc = -errno;
354                 goto out;
355         }
356         *buflen = bytes_read + remaining;
357
358         /* file was not (*buflen - 1) bytes, add 1 for reallocating */
359         if (remaining != 0) {
360                 *buflen += 1;
361                 rc = -EOVERFLOW;
362         }
363
364 out:
365         close(fd);
366
367         return rc;
368 }
369
370 static void print_obd_line(char *s)
371 {
372         const char *param = "osc/%s/ost_conn_uuid";
373         char obd_name[MAX_OBD_NAME];
374         char buf[MAX_OBD_NAME];
375         FILE *fp = NULL;
376         glob_t path;
377         char *ptr;
378 retry:
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)
383                 goto try_mdc;
384         if (cfs_get_param_paths(&path, param, obd_name) != 0)
385                 goto try_mdc;
386         fp = fopen(path.gl_pathv[0], "r");
387         if (!fp) {
388                 /* need to free path data before retry */
389                 cfs_free_param_data(&path);
390 try_mdc:
391                 if (param[0] == 'o') { /* failed with osc, try mdc */
392                         param = "mdc/%s/mds_conn_uuid";
393                         goto retry;
394                 }
395                 buf[0] = '\0';
396                 goto fail_print;
397         }
398
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));
402                 goto fail_close;
403         }
404
405 fail_close:
406         fclose(fp);
407         cfs_free_param_data(&path);
408
409         /* trim trailing newlines */
410         ptr = strrchr(buf, '\n');
411         if (ptr)
412                 *ptr = '\0';
413 fail_print:
414         ptr = strrchr(s, '\n');
415         if (ptr)
416                 *ptr = '\0';
417         printf("%s%s%s\n", s, buf[0] ? " " : "", buf);
418 }
419
420 static int print_out_devices(yaml_parser_t *reply, enum lctl_param_flags flags)
421 {
422         char buf[PATH_MAX / 2], *tmp = NULL;
423         size_t buf_len = sizeof(buf);
424         yaml_event_t event;
425         bool done = false;
426         int rc;
427
428         if (flags & PARAM_FLAGS_SHOW_SOURCE) {
429                 snprintf(buf, buf_len, "devices=");
430                 printf("%s\n",  buf);
431         }
432         bzero(buf, sizeof(buf));
433
434         while (!done) {
435                 rc = yaml_parser_parse(reply, &event);
436                 if (rc == 0)
437                         break;
438
439                 if (event.type == YAML_MAPPING_START_EVENT) {
440                         size_t len = strlen(buf);
441
442                         if (len > 0 && strcmp(buf, "devices=\n") != 0) {
443                                 /* eat last white space */
444                                 buf[len - 1] = '\0';
445                                 if (flags & PARAM_FLAGS_EXTRA_DETAILS)
446                                         print_obd_line(buf);
447                                 else
448                                         printf("%s\n",  buf);
449                         }
450                         bzero(buf, sizeof(buf));
451                         tmp = buf;
452                         buf_len = sizeof(buf);
453                 }
454
455                 if (event.type == YAML_SCALAR_EVENT) {
456                         char *value = (char *)event.data.scalar.value;
457
458                         if (strcmp(value, "index") == 0) {
459                                 yaml_event_delete(&event);
460                                 rc = yaml_parser_parse(reply, &event);
461                                 if (rc == 0)
462                                         break;
463
464                                 value = (char *)event.data.scalar.value;
465                                 snprintf(tmp, buf_len, "%3s ", value);
466                                 buf_len -= 4;
467                                 tmp += 4;
468                         }
469
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);
477                                 if (rc == 0)
478                                         break;
479
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;
484                         }
485                 }
486
487                 done = (event.type == YAML_DOCUMENT_END_EVENT);
488                 if (done) {
489                         size_t len = strlen(buf);
490
491                         if (len > 0) {
492                                 /* eat last white space */
493                                 buf[len - 1] = '\0';
494                                 if (flags & PARAM_FLAGS_EXTRA_DETAILS)
495                                         print_obd_line(buf);
496                                 else
497                                         printf("%s\n", buf);
498                         }
499                         bzero(buf, sizeof(buf));
500                         tmp = buf;
501                 }
502                 yaml_event_delete(&event);
503         }
504
505         return rc;
506 }
507
508 static int lcfg_param_get_yaml(yaml_parser_t *reply, struct nl_sock *sk,
509                                int version, char *pattern)
510 {
511         char source[PATH_MAX / 2], group[GENL_NAMSIZ + 1];
512         char *family = "lustre", *tmp;
513         yaml_emitter_t request;
514         yaml_event_t event;
515         int cmd = 0;
516         int rc;
517
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, '/'))
522                 *tmp = '.';
523
524         tmp = strrchr(pattern, '.');
525         if (tmp) {
526                 size_t len = tmp - pattern;
527
528                 strncpy(group, tmp + 1, GENL_NAMSIZ);
529                 strncpy(source, pattern, len);
530         } else {
531                 strncpy(group, pattern, GENL_NAMSIZ);
532         }
533
534         if (strcmp(group, "devices") == 0)
535                 cmd = LUSTRE_CMD_DEVICES;
536
537         if (!cmd)
538                 return -EOPNOTSUPP;
539
540         /* Setup parser to recieve Netlink packets */
541         rc = yaml_parser_initialize(reply);
542         if (rc == 0)
543                 return -EOPNOTSUPP;
544
545         rc = yaml_parser_set_input_netlink(reply, sk, false);
546         if (rc == 0)
547                 return -EOPNOTSUPP;
548
549         /* Create Netlink emitter to send request to kernel */
550         yaml_emitter_initialize(&request);
551         rc = yaml_emitter_set_output_netlink(&request, sk,
552                                              family, version,
553                                              cmd, NLM_F_DUMP);
554         if (rc == 0)
555                 goto error;
556
557         yaml_emitter_open(&request);
558
559         yaml_document_start_event_initialize(&event, NULL, NULL, NULL, 0);
560         rc = yaml_emitter_emit(&request, &event);
561         if (rc == 0)
562                 goto error;
563
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);
568         if (rc == 0)
569                 goto error;
570
571         yaml_scalar_event_initialize(&event, NULL,
572                                      (yaml_char_t *)YAML_STR_TAG,
573                                      (yaml_char_t *)group,
574                                      strlen(group), 1, 0,
575                                      YAML_PLAIN_SCALAR_STYLE);
576         rc = yaml_emitter_emit(&request, &event);
577         if (rc == 0)
578                 goto error;
579
580         if (source[0]) {
581                 const char *key = cmd == LUSTRE_CMD_DEVICES ? "name" : "source";
582
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);
588                 if (rc == 0)
589                         goto error;
590
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);
595                 if (rc == 0)
596                         goto error;
597
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);
603                 if (rc == 0)
604                         goto error;
605
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);
612                 if (rc == 0)
613                         goto error;
614
615                 yaml_mapping_end_event_initialize(&event);
616                 rc = yaml_emitter_emit(&request, &event);
617                 if (rc == 0)
618                         goto error;
619
620                 yaml_sequence_end_event_initialize(&event);
621                 rc = yaml_emitter_emit(&request, &event);
622                 if (rc == 0)
623                         goto error;
624         } else {
625                 yaml_scalar_event_initialize(&event, NULL,
626                                              (yaml_char_t *)YAML_STR_TAG,
627                                              (yaml_char_t *)"",
628                                              strlen(""), 1, 0,
629                                              YAML_PLAIN_SCALAR_STYLE);
630                 rc = yaml_emitter_emit(&request, &event);
631                 if (rc == 0)
632                         goto error;
633         }
634         yaml_mapping_end_event_initialize(&event);
635         rc = yaml_emitter_emit(&request, &event);
636         if (rc == 0)
637                 goto error;
638
639         yaml_document_end_event_initialize(&event, 0);
640         rc = yaml_emitter_emit(&request, &event);
641         if (rc == 0)
642                 goto error;
643
644         yaml_emitter_close(&request);
645 error:
646         if (rc == 0) {
647                 yaml_emitter_log_error(&request, stderr);
648                 rc = -EINVAL;
649         }
650         yaml_emitter_delete(&request);
651
652         return rc == 1 ? 0 : -EINVAL;
653 }
654
655 int llapi_param_display_value(char *path, int version,
656                               enum lctl_param_flags flags, FILE *fp)
657 {
658         yaml_parser_t reply;
659         struct nl_sock *sk;
660         int rc;
661
662         /* version zero means just list sources. "devices is special case */
663         if (!version && strcmp(path, "devices") == 0) {
664                 fprintf(fp, "devices\n");
665                 return 0;
666         }
667
668         sk = nl_socket_alloc();
669         if (!sk)
670                 return -ENOMEM;
671
672         rc = lcfg_param_get_yaml(&reply, sk, version, path);
673         if (rc < 0)
674                 return rc;
675
676         if (flags & PARAM_FLAGS_YAML_FORMAT) {
677                 yaml_document_t results;
678                 yaml_emitter_t output;
679
680                 /* load the reply results */
681                 rc = yaml_parser_load(&reply, &results);
682                 if (rc == 0) {
683                         yaml_parser_log_error(&reply, stderr, "get_param: ");
684                         yaml_document_delete(&results);
685                         rc = -EINVAL;
686                         goto free_reply;
687                 }
688
689                 /* create emitter to output results */
690                 rc = yaml_emitter_initialize(&output);
691                 if (rc == 1) {
692                         yaml_emitter_set_output_file(&output, fp);
693
694                         rc = yaml_emitter_dump(&output, &results);
695                 }
696
697                 yaml_document_delete(&results);
698                 if (rc == 0) {
699                         yaml_emitter_log_error(&output, stderr);
700                         rc = -EINVAL;
701                 }
702                 yaml_emitter_delete(&output);
703         } else {
704                 yaml_event_t event;
705                 bool done = false;
706
707                 while (!done) {
708                         rc = yaml_parser_parse(&reply, &event);
709                         if (rc == 0)
710                                 break;
711
712                         if (event.type == YAML_SCALAR_EVENT) {
713                                 char *value = (char *)event.data.scalar.value;
714
715                                 if (strcmp(value, "devices") == 0)
716                                         rc = print_out_devices(&reply, flags);
717                                 if (rc == 0)
718                                         break;
719                         }
720
721                         done = (event.type == YAML_STREAM_END_EVENT);
722                         yaml_event_delete(&event);
723                 }
724
725                 if (rc == 0) {
726                         yaml_parser_log_error(&reply, stderr, "get_param: ");
727                         rc = -EINVAL;
728                 }
729         }
730 free_reply:
731         yaml_parser_delete(&reply);
732         nl_socket_free(sk);
733         return rc == 1 ? 0 : rc;
734 }
735
736 /**
737  * Read the value of the file with location \a path
738  * into a buffer.
739  *
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.
746  *
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.
760  *
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
765  * buffer.
766  */
767 int llapi_param_get_value(const char *path, char **buf, size_t *buflen)
768 {
769         int rc = 0;
770
771         if (path == NULL || buflen == NULL)
772                 rc = -EINVAL;
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
777          */
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
782          */
783         else
784                 rc = copy_file_fixed(path, *buf, buflen);
785
786         errno = -rc;
787
788         return rc;
789 }
790
791 void llapi_param_paths_free(glob_t *paths)
792 {
793         cfs_free_param_data(paths);
794 }