Whamcloud - gitweb
LU-17705 ptlrpc: replace synchronize_rcu() with rcu_barrier()
[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
36 #include <libcfs/util/param.h>
37 #include <linux/lustre/lustre_user.h>
38 #include <lustre/lustreapi.h>
39 #include "lustreapi_internal.h"
40
41 /**
42  * return the parameter's path for a specific device type or mountpoint
43  *
44  * \param param         the results returned to the caller
45  * \param obd_type      Lustre OBD device type
46  *
47  * \param filter        filter combined with the type agrument allow the
48  * \param type          caller to limit the scope of the search for the
49  *                      parameter's path. Typical options are search by
50  *                      Lustre filesystem name or by the path to a file
51  *                      or directory in the filesystem.
52  *
53  * \param param_name    parameter name to fetch
54  *
55  * Using filter and the type argument we can limit the scope of the
56  * search to either the parameter belonging to a specific lustre filesystem
57  * (if it exists) or using a given file or directory path located on a
58  * mounted Lustre filesystem. The last case it can do is a special search
59  * based on exactly what the user passed instead of scanning file paths
60  * or specific file systems.
61  *
62  * If "obd_type" matches a Lustre device then the first matching device
63  * (as with "lctl dl", constrained by \param filter and \param type)
64  * will be used to provide the return value, otherwise the first such
65  * device found will be used.
66  *
67  * Return 0 for success, with the results stored in \param param.
68  * Return -ve value for error.
69  */
70 int
71 get_lustre_param_path(const char *obd_type, const char *filter,
72                       enum param_filter type, const char *param_name,
73                       glob_t *param)
74 {
75         char pattern[PATH_MAX];
76         int rc = 0;
77
78         if (filter == NULL && type != FILTER_BY_NONE)
79                 return -EINVAL;
80
81         switch (type) {
82         case FILTER_BY_PATH:
83                 rc = llapi_search_fsname(filter, pattern);
84                 if (rc) {
85                         llapi_error(LLAPI_MSG_ERROR, rc,
86                                     "'%s' is not on a Lustre filesystem",
87                                     filter);
88                         return rc;
89                 }
90                 if (strlen(pattern) + 3 > sizeof(pattern))
91                         return -E2BIG;
92                 strncat(pattern, "-*", sizeof(pattern) - 1);
93                 break;
94         case FILTER_BY_FS_NAME:
95                 rc = snprintf(pattern, sizeof(pattern) - 1, "%s-*", filter);
96                 if (rc < 0)
97                         return rc;
98                 else if (rc >= sizeof(pattern))
99                         return -EINVAL;
100                 rc = 0;
101                 break;
102         case FILTER_BY_EXACT:
103                 if (strlen(filter) + 1 > sizeof(pattern))
104                         return -E2BIG;
105                 strncpy(pattern, filter, sizeof(pattern));
106                 break;
107         case FILTER_BY_NONE:
108         default:
109                 break;
110         }
111
112         if (type == FILTER_BY_NONE) {
113                 if (cfs_get_param_paths(param, "%s", param_name) != 0)
114                         rc = -errno;
115         } else if (param_name != NULL) {
116                 if (cfs_get_param_paths(param, "%s/%s/%s",
117                                        obd_type, pattern, param_name) != 0)
118                         rc = -errno;
119         } else {
120                 if (cfs_get_param_paths(param, "%s/%s",
121                                        obd_type, pattern) != 0)
122                         rc = -errno;
123         }
124
125         return rc;
126 }
127
128 /**
129  * return a parameter of a single line value for a specific device type
130  * or mountpoint
131  *
132  * \param obd_type      Lustre OBD device type
133  *
134  * \param filter        filter combined with the type agruments allow the
135  * \param type          caller to limit the scope of the search for the
136  *                      parameter's path. Typical options are search by
137  *                      Lustre filesystem name or by the path to a file
138  *                      or directory in the filesystem.
139  *
140  * \param param_name    parameter name to fetch
141  * \param value         return buffer for parameter value string
142  * \param val_len       size of buffer for return value
143  *
144  * Using filter and the type argument we can limit the scope of the
145  * search to either the parameter belonging to a specific lustre filesystem
146  * (if it exists) or using a given file or directory path located on a
147  * mounted Lustre filesystem. The last case it can do is a special search
148  * based on exactly what the user passed instead of scanning file paths
149  * or specific file systems.
150  *
151  * If "obd_type" matches a Lustre device then the first matching device
152  * (as with "lctl dl", constrained by \param filter and \param type)
153  * will be used to provide the return value, otherwise the first such
154  * device found will be used.
155  *
156  * Return 0 for success, with a NUL-terminated string in \param value.
157  * Return negative errno value for error.
158  */
159 int
160 get_lustre_param_value(const char *obd_type, const char *filter,
161                        enum param_filter type, const char *param_name,
162                        char *value, size_t val_len)
163 {
164         glob_t param;
165         FILE *fp;
166         int rc;
167
168         rc = get_lustre_param_path(obd_type, filter, type, param_name, &param);
169         if (rc != 0)
170                 return -ENOENT;
171
172         fp = fopen(param.gl_pathv[0], "r");
173         if (fp == NULL) {
174                 rc = -errno;
175                 llapi_error(LLAPI_MSG_ERROR, rc, "error: opening '%s'",
176                             param.gl_pathv[0]);
177                 goto err;
178         }
179
180         if (fgets(value, val_len, fp) == NULL) {
181                 if (!feof(fp))
182                         rc = -ferror(fp);
183         }
184         fclose(fp);
185 err:
186         cfs_free_param_data(&param);
187
188         return rc;
189 }
190
191 int llapi_param_get_paths(const char *pattern, glob_t *paths)
192 {
193         return get_lustre_param_path(NULL, NULL, FILTER_BY_NONE,
194                                      pattern, paths);
195 }
196
197 /**
198  *  Read to the end of the file and count the bytes read.
199  */
200 static int bytes_remaining(int fd, size_t *file_size)
201 {
202         int rc = 0;
203         size_t bytes_read = 0;
204         long page_size = sysconf(_SC_PAGESIZE);
205         char *temp_buf;
206
207         temp_buf = malloc(page_size);
208         if (temp_buf == NULL)
209                 return -ENOMEM;
210
211         while (1) {
212                 ssize_t count = read(fd, temp_buf, page_size);
213
214                 if (count == 0) {
215                         *file_size = bytes_read;
216                         break;
217                 }
218
219                 if (count < 0) {
220                         rc = -errno;
221                         break;
222                 }
223                 bytes_read += count;
224         }
225
226         free(temp_buf);
227         return rc;
228 }
229
230 /**
231  *  Determine the size of a file by reading it.
232  */
233 static int required_size(const char *path, size_t *file_size)
234 {
235         int rc = 0;
236         int fd;
237
238         fd = open(path, O_RDONLY);
239         if (fd < 0)
240                 return -errno;
241
242         rc = bytes_remaining(fd, file_size);
243
244         close(fd);
245         *file_size += 1;
246         return rc;
247 }
248
249 static
250 int copy_file_expandable(const char *path, char **buf, size_t *file_size)
251 {
252         long page_size = sysconf(_SC_PAGESIZE);
253         int rc = 0;
254         char *temp_buf;
255         int fd;
256         FILE *fp;
257
258         fp = open_memstream(buf, file_size);
259         if (fp == NULL) {
260                 rc = -errno;
261                 goto out;
262         }
263
264         fd = open(path, O_RDONLY);
265         if (fd < 0) {
266                 rc = -errno;
267                 goto close_stream;
268         }
269
270         temp_buf = calloc(1, page_size);
271         if (buf == NULL) {
272                 rc = -ENOMEM;
273                 goto close_file;
274         }
275
276         while (1) {
277                 ssize_t count = read(fd, temp_buf, page_size);
278
279                 if (count == 0)
280                         break;
281                 if (count < 0) {
282                         rc = -errno;
283                         break;
284                 }
285
286                 if (fwrite(temp_buf, 1, count, fp) != count) {
287                         rc = -errno;
288                         break;
289                 }
290         }
291
292         free(temp_buf);
293 close_file:
294         close(fd);
295 close_stream:
296         fclose(fp);
297 out:
298         /* If rc != 0 and *buf != NULL, the caller may retry.
299          * This would likely result in copy_file_fixed() being called
300          * on accident, and a likely memory error.
301          */
302         if (rc != 0) {
303                 free(*buf);
304                 *buf = NULL;
305         }
306         return rc;
307 }
308
309 /**
310  *  Copy file to a buffer and write the number of bytes copied
311  */
312 static int copy_file_fixed(const char *path, char *buf, size_t *buflen)
313 {
314         int rc = 0;
315         size_t bytes_read = 0;
316         size_t max_read = *buflen - 1;
317         size_t remaining = 0;
318         int fd;
319
320         fd = open(path, O_RDONLY);
321         if (fd < 0)
322                 return -errno;
323
324         while (bytes_read < max_read) {
325                 ssize_t count = read(fd,
326                                      buf + bytes_read,
327                                      max_read - bytes_read);
328
329                 /* read the entire file */
330                 if (count == 0) {
331                         *buflen = bytes_read + 1;
332                         buf[bytes_read] = '\0';
333                         goto out;
334                 }
335
336                 if (count < 0)
337                         goto check_size;
338
339                 bytes_read += count;
340         }
341
342 check_size:
343         /* need to check size in case error due to buf being too small
344          * for read() or exited loop due to buf being full
345          */
346         buf[max_read] = '\0';
347
348         rc = bytes_remaining(fd, &remaining);
349         if (rc != 0) {
350                 rc = -errno;
351                 goto out;
352         }
353         *buflen = bytes_read + remaining;
354
355         /* file was not (*buflen - 1) bytes, add 1 for reallocating */
356         if (remaining != 0) {
357                 *buflen += 1;
358                 rc = -EOVERFLOW;
359         }
360
361 out:
362         close(fd);
363
364         return rc;
365 }
366
367 /**
368  * Read the value of the file with location \a path
369  * into a buffer.
370  *
371  * \param path[in]           the location of a parameter file
372  * \param buf[in,out]        a pointer to a pointer to a buffer
373  * \param buflen[in,out]     the length of a pre-allocated buffer
374  *                           when passed in, and either the number
375  *                           of bytes written or the suggested
376  *                           size of *buf when passed out.
377  *
378  * There are 3 behaviors based on the value of buf.
379  * If buf == NULL, then the buffer size needed to read the file at
380  * \a path will be written to \a *buflen.
381  * If \a buf != NULL and \a *buf == NULL, the value of *buf will point
382  * to a buffer that will be automatically sized to fit the file
383  * contents. A NUL byte will be added to the end of the buffer.
384  * The value of \a *buflen will be set to the number of bytes written
385  * excuding the NUL byte.
386  * If \a buf != NULL and \a *buf != NULL, it will be assumed that \a *buf
387  * points to a pre-allocated buffer with a capacity of \a *buflen.
388  * If there is sufficient space, the file contents and NUL terminating
389  * byte will be written to the buffer at .\a *buf.
390  * Otherwise, the required size of \a *buflen with be written to \a *buflen.
391  *
392  * Returns 0 for success with null terminated string in \a *buf.
393  * Returns negative errno value on error.
394  * For case of \a buf != NULL and \a *buf != NULL, a return value
395  * of -EOVERFLOW indicates that it's possible retry with a larger
396  * buffer.
397  */
398 int llapi_param_get_value(const char *path, char **buf, size_t *buflen)
399 {
400         int rc = 0;
401
402         if (path == NULL || buflen == NULL)
403                 rc = -EINVAL;
404         else if (buf == NULL)
405                 rc = required_size(path, buflen);
406         /* handle for buffer, but no buffer
407          * create a buffer of the required size
408          */
409         else if (*buf == NULL)
410                 rc = copy_file_expandable(path, buf, buflen);
411         /* preallocated buffer given, attempt to copy
412          * file to it, return file size if buffer too small
413          */
414         else
415                 rc = copy_file_fixed(path, *buf, buflen);
416
417         errno = -rc;
418
419         return rc;
420 }
421
422 void llapi_param_paths_free(glob_t *paths)
423 {
424         cfs_free_param_data(paths);
425 }