Whamcloud - gitweb
LU-17000 utils: In mydaemon() check after calling open()
[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         size_t bytes_read = 0;
206         long page_size;
207         char *temp_buf;
208         int rc = 0;
209
210         page_size = sysconf(_SC_PAGESIZE);
211         if (page_size < 0)
212                 return -EINVAL;
213
214         temp_buf = malloc(page_size);
215         if (temp_buf == NULL)
216                 return -ENOMEM;
217
218         while (1) {
219                 ssize_t count = read(fd, temp_buf, page_size);
220
221                 if (count == 0) {
222                         *file_size = bytes_read;
223                         break;
224                 }
225
226                 if (count < 0) {
227                         rc = -errno;
228                         break;
229                 }
230                 bytes_read += count;
231         }
232
233         free(temp_buf);
234         return rc;
235 }
236
237 /**
238  *  Determine the size of a file by reading it.
239  */
240 static int required_size(const char *path, size_t *file_size)
241 {
242         int rc = 0;
243         int fd;
244
245         fd = open(path, O_RDONLY);
246         if (fd < 0)
247                 return -errno;
248
249         rc = bytes_remaining(fd, file_size);
250
251         close(fd);
252         *file_size += 1;
253         return rc;
254 }
255
256 static
257 int copy_file_expandable(const char *path, char **buf, size_t *file_size)
258 {
259         long page_size;
260         char *temp_buf;
261         int rc = 0, fd;
262         FILE *fp;
263
264         page_size = sysconf(_SC_PAGESIZE);
265         if (page_size < 0) {
266                 rc = -errno;
267                 goto out;
268         }
269
270         fp = open_memstream(buf, file_size);
271         if (fp == NULL) {
272                 rc = -errno;
273                 goto out;
274         }
275
276         fd = open(path, O_RDONLY);
277         if (fd < 0) {
278                 rc = -errno;
279                 goto close_stream;
280         }
281
282         temp_buf = calloc(1, page_size);
283         if (buf == NULL) {
284                 rc = -ENOMEM;
285                 goto close_file;
286         }
287
288         while (1) {
289                 ssize_t count = read(fd, temp_buf, page_size);
290
291                 if (count == 0)
292                         break;
293                 if (count < 0) {
294                         rc = -errno;
295                         break;
296                 }
297
298                 if (fwrite(temp_buf, 1, count, fp) != count) {
299                         rc = -errno;
300                         break;
301                 }
302         }
303
304         free(temp_buf);
305 close_file:
306         close(fd);
307 close_stream:
308         fclose(fp);
309 out:
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.
313          */
314         if (rc != 0) {
315                 free(*buf);
316                 *buf = NULL;
317         }
318         return rc;
319 }
320
321 /**
322  *  Copy file to a buffer and write the number of bytes copied
323  */
324 static int copy_file_fixed(const char *path, char *buf, size_t *buflen)
325 {
326         int rc = 0;
327         size_t bytes_read = 0;
328         size_t max_read = *buflen - 1;
329         size_t remaining = 0;
330         int fd;
331
332         fd = open(path, O_RDONLY);
333         if (fd < 0)
334                 return -errno;
335
336         while (bytes_read < max_read) {
337                 ssize_t count = read(fd,
338                                      buf + bytes_read,
339                                      max_read - bytes_read);
340
341                 /* read the entire file */
342                 if (count == 0) {
343                         *buflen = bytes_read + 1;
344                         buf[bytes_read] = '\0';
345                         goto out;
346                 }
347
348                 if (count < 0)
349                         goto check_size;
350
351                 bytes_read += count;
352         }
353
354 check_size:
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
357          */
358         buf[max_read] = '\0';
359
360         rc = bytes_remaining(fd, &remaining);
361         if (rc != 0) {
362                 rc = -errno;
363                 goto out;
364         }
365         *buflen = bytes_read + remaining;
366
367         /* file was not (*buflen - 1) bytes, add 1 for reallocating */
368         if (remaining != 0) {
369                 *buflen += 1;
370                 rc = -EOVERFLOW;
371         }
372
373 out:
374         close(fd);
375
376         return rc;
377 }
378
379 static void print_obd_line(char *s)
380 {
381         const char *param = "osc/%s/ost_conn_uuid";
382         char obd_name[MAX_OBD_NAME];
383         char buf[MAX_OBD_NAME];
384         FILE *fp = NULL;
385         glob_t path;
386         char *ptr;
387 retry:
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)
392                 goto try_mdc;
393         if (cfs_get_param_paths(&path, param, obd_name) != 0)
394                 goto try_mdc;
395         fp = fopen(path.gl_pathv[0], "r");
396         if (!fp) {
397                 /* need to free path data before retry */
398                 cfs_free_param_data(&path);
399 try_mdc:
400                 if (param[0] == 'o') { /* failed with osc, try mdc */
401                         param = "mdc/%s/mds_conn_uuid";
402                         goto retry;
403                 }
404                 buf[0] = '\0';
405                 goto fail_print;
406         }
407
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));
411                 goto fail_close;
412         }
413
414 fail_close:
415         fclose(fp);
416         cfs_free_param_data(&path);
417
418         /* trim trailing newlines */
419         ptr = strrchr(buf, '\n');
420         if (ptr)
421                 *ptr = '\0';
422 fail_print:
423         ptr = strrchr(s, '\n');
424         if (ptr)
425                 *ptr = '\0';
426         printf("%s%s%s\n", s, buf[0] ? " " : "", buf);
427 }
428
429 static int print_out_devices(yaml_parser_t *reply, enum lctl_param_flags flags)
430 {
431         char buf[PATH_MAX / 2], *tmp = NULL;
432         size_t buf_len = sizeof(buf);
433         yaml_event_t event;
434         bool done = false;
435         int rc;
436
437         if (flags & PARAM_FLAGS_SHOW_SOURCE) {
438                 snprintf(buf, buf_len, "devices=");
439                 printf("%s\n",  buf);
440         }
441         bzero(buf, sizeof(buf));
442
443         while (!done) {
444                 rc = yaml_parser_parse(reply, &event);
445                 if (rc == 0)
446                         break;
447
448                 if (event.type == YAML_MAPPING_START_EVENT) {
449                         size_t len = strlen(buf);
450
451                         if (len > 0 && strcmp(buf, "devices=\n") != 0) {
452                                 /* eat last white space */
453                                 buf[len - 1] = '\0';
454                                 if (flags & PARAM_FLAGS_EXTRA_DETAILS)
455                                         print_obd_line(buf);
456                                 else
457                                         printf("%s\n",  buf);
458                         }
459                         bzero(buf, sizeof(buf));
460                         tmp = buf;
461                         buf_len = sizeof(buf);
462                 }
463
464                 if (event.type == YAML_SCALAR_EVENT) {
465                         char *value = (char *)event.data.scalar.value;
466
467                         if (strcmp(value, "index") == 0) {
468                                 yaml_event_delete(&event);
469                                 rc = yaml_parser_parse(reply, &event);
470                                 if (rc == 0)
471                                         break;
472
473                                 value = (char *)event.data.scalar.value;
474                                 snprintf(tmp, buf_len, "%3s ", value);
475                                 buf_len -= 4;
476                                 tmp += 4;
477                         }
478
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);
486                                 if (rc == 0)
487                                         break;
488
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;
493                         }
494                 }
495
496                 done = (event.type == YAML_DOCUMENT_END_EVENT);
497                 if (done) {
498                         size_t len = strlen(buf);
499
500                         if (len > 0) {
501                                 /* eat last white space */
502                                 buf[len - 1] = '\0';
503                                 if (flags & PARAM_FLAGS_EXTRA_DETAILS)
504                                         print_obd_line(buf);
505                                 else
506                                         printf("%s\n", buf);
507                         }
508                         bzero(buf, sizeof(buf));
509                         tmp = buf;
510                 }
511                 yaml_event_delete(&event);
512         }
513
514         return rc;
515 }
516
517 static int lcfg_param_get_yaml(yaml_parser_t *reply, struct nl_sock *sk,
518                                int version, char *pattern)
519 {
520         char source[PATH_MAX / 2], group[GENL_NAMSIZ + 1];
521         char *family = "lustre", *tmp;
522         yaml_emitter_t request;
523         yaml_event_t event;
524         int cmd = 0;
525         int rc;
526
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, '/'))
531                 *tmp = '.';
532
533         tmp = strrchr(pattern, '.');
534         if (tmp) {
535                 size_t len = tmp - pattern;
536
537                 strncpy(group, tmp + 1, GENL_NAMSIZ);
538                 strncpy(source, pattern, len);
539         } else {
540                 strncpy(group, pattern, GENL_NAMSIZ);
541         }
542
543         if (strcmp(group, "devices") == 0)
544                 cmd = LUSTRE_CMD_DEVICES;
545
546         if (!cmd)
547                 return -EOPNOTSUPP;
548
549         /* Setup parser to recieve Netlink packets */
550         rc = yaml_parser_initialize(reply);
551         if (rc == 0)
552                 return -EOPNOTSUPP;
553
554         rc = yaml_parser_set_input_netlink(reply, sk, false);
555         if (rc == 0)
556                 return -EOPNOTSUPP;
557
558         /* Create Netlink emitter to send request to kernel */
559         yaml_emitter_initialize(&request);
560         rc = yaml_emitter_set_output_netlink(&request, sk,
561                                              family, version,
562                                              cmd, NLM_F_DUMP);
563         if (rc == 0)
564                 goto error;
565
566         yaml_emitter_open(&request);
567
568         yaml_document_start_event_initialize(&event, NULL, NULL, NULL, 0);
569         rc = yaml_emitter_emit(&request, &event);
570         if (rc == 0)
571                 goto error;
572
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);
577         if (rc == 0)
578                 goto error;
579
580         yaml_scalar_event_initialize(&event, NULL,
581                                      (yaml_char_t *)YAML_STR_TAG,
582                                      (yaml_char_t *)group,
583                                      strlen(group), 1, 0,
584                                      YAML_PLAIN_SCALAR_STYLE);
585         rc = yaml_emitter_emit(&request, &event);
586         if (rc == 0)
587                 goto error;
588
589         if (source[0]) {
590                 const char *key = cmd == LUSTRE_CMD_DEVICES ? "name" : "source";
591
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);
597                 if (rc == 0)
598                         goto error;
599
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);
604                 if (rc == 0)
605                         goto error;
606
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);
612                 if (rc == 0)
613                         goto error;
614
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);
621                 if (rc == 0)
622                         goto error;
623
624                 yaml_mapping_end_event_initialize(&event);
625                 rc = yaml_emitter_emit(&request, &event);
626                 if (rc == 0)
627                         goto error;
628
629                 yaml_sequence_end_event_initialize(&event);
630                 rc = yaml_emitter_emit(&request, &event);
631                 if (rc == 0)
632                         goto error;
633         } else {
634                 yaml_scalar_event_initialize(&event, NULL,
635                                              (yaml_char_t *)YAML_STR_TAG,
636                                              (yaml_char_t *)"",
637                                              strlen(""), 1, 0,
638                                              YAML_PLAIN_SCALAR_STYLE);
639                 rc = yaml_emitter_emit(&request, &event);
640                 if (rc == 0)
641                         goto error;
642         }
643         yaml_mapping_end_event_initialize(&event);
644         rc = yaml_emitter_emit(&request, &event);
645         if (rc == 0)
646                 goto error;
647
648         yaml_document_end_event_initialize(&event, 0);
649         rc = yaml_emitter_emit(&request, &event);
650         if (rc == 0)
651                 goto error;
652
653         yaml_emitter_close(&request);
654 error:
655         if (rc == 0) {
656                 yaml_emitter_log_error(&request, stderr);
657                 rc = -EINVAL;
658         }
659         yaml_emitter_delete(&request);
660
661         return rc == 1 ? 0 : -EINVAL;
662 }
663
664 int llapi_param_display_value(char *path, int version,
665                               enum lctl_param_flags flags, FILE *fp)
666 {
667         yaml_parser_t reply;
668         struct nl_sock *sk;
669         int rc;
670
671         /* version zero means just list sources. "devices is special case */
672         if (!version && strcmp(path, "devices") == 0) {
673                 fprintf(fp, "devices\n");
674                 return 0;
675         }
676
677         sk = nl_socket_alloc();
678         if (!sk)
679                 return -ENOMEM;
680
681         rc = lcfg_param_get_yaml(&reply, sk, version, path);
682         if (rc < 0)
683                 return rc;
684
685         if (flags & PARAM_FLAGS_YAML_FORMAT) {
686                 yaml_document_t results;
687                 yaml_emitter_t output;
688
689                 /* load the reply results */
690                 rc = yaml_parser_load(&reply, &results);
691                 if (rc == 0) {
692                         yaml_parser_log_error(&reply, stderr, "get_param: ");
693                         yaml_document_delete(&results);
694                         rc = -EINVAL;
695                         goto free_reply;
696                 }
697
698                 /* create emitter to output results */
699                 rc = yaml_emitter_initialize(&output);
700                 if (rc == 1) {
701                         yaml_emitter_set_output_file(&output, fp);
702
703                         rc = yaml_emitter_dump(&output, &results);
704                 }
705
706                 yaml_document_delete(&results);
707                 if (rc == 0) {
708                         yaml_emitter_log_error(&output, stderr);
709                         rc = -EINVAL;
710                 }
711                 yaml_emitter_delete(&output);
712         } else {
713                 yaml_event_t event;
714                 bool done = false;
715
716                 while (!done) {
717                         rc = yaml_parser_parse(&reply, &event);
718                         if (rc == 0)
719                                 break;
720
721                         if (event.type == YAML_SCALAR_EVENT) {
722                                 char *value = (char *)event.data.scalar.value;
723
724                                 if (strcmp(value, "devices") == 0)
725                                         rc = print_out_devices(&reply, flags);
726                                 if (rc == 0)
727                                         break;
728                         }
729
730                         done = (event.type == YAML_STREAM_END_EVENT);
731                         yaml_event_delete(&event);
732                 }
733
734                 if (rc == 0) {
735                         yaml_parser_log_error(&reply, stderr, "get_param: ");
736                         rc = -EINVAL;
737                 }
738         }
739 free_reply:
740         yaml_parser_delete(&reply);
741         nl_socket_free(sk);
742         return rc == 1 ? 0 : rc;
743 }
744
745 /**
746  * Read the value of the file with location \a path
747  * into a buffer.
748  *
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.
755  *
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.
769  *
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
774  * buffer.
775  */
776 int llapi_param_get_value(const char *path, char **buf, size_t *buflen)
777 {
778         int rc = 0;
779
780         if (path == NULL || buflen == NULL)
781                 rc = -EINVAL;
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
786          */
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
791          */
792         else
793                 rc = copy_file_fixed(path, *buf, buflen);
794
795         errno = -rc;
796
797         return rc;
798 }
799
800 void llapi_param_paths_free(glob_t *paths)
801 {
802         cfs_free_param_data(paths);
803 }