Whamcloud - gitweb
74a37262420295f56248e96868d309094c85f5d7
[fs/lustre-release.git] / libcfs / libcfs / util / 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  * libcfs/libcfs/utils/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 #include <errno.h>
26 #include <fcntl.h>
27 #include <glob.h>
28 #include <mntent.h>
29 #include <paths.h>
30 #include <stdarg.h>
31 #include <stdbool.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/mount.h>
36 #include <sys/stat.h>
37 #include <sys/types.h>
38 #include <unistd.h>
39 #include <linux/limits.h>
40 #include <libcfs/util/string.h>
41
42 /**
43  * Get parameter path matching the pattern
44  *
45  * \param[out] paths    glob_t structure used to hold the final result
46  * \param[in]  pattern  the pattern containing sprintf format specifiers
47  *                      which will be used to create the path to match
48  *
49  * The \param pattern is appended to the default path glob to complete the
50  * absolute path to the file the caller is requesting. If the results point
51  * to one or more files that exist those results are stored in the \param
52  * paths glob_t structure that is passed by the caller.
53  *
54  * Lustre tunables traditionally were in /proc/{sys,fs}/{lnet,lustre}
55  * but in upstream kernels starting with Linux 4.2 these parameters
56  * have been moved to /sys/fs/lustre and /sys/kernel/debug/{lnet,lustre}
57  * so the user tools need to check both locations.
58  *
59  * \retval       0 for success, with results stored in \param paths.
60  * \retval      -1 for failure with errno set to report the reason.
61  */
62 int
63 cfs_get_param_paths(glob_t *paths, const char *pattern, ...)
64 {
65         char path[PATH_MAX] = "{/sys/{fs,kernel/debug}/{lnet,lustre}/,"
66                                "/proc/{fs,sys}/{lnet,lustre}/}";
67         static bool test_mounted = false;
68         size_t len = strlen(path);
69         char buf[PATH_MAX];
70         va_list args;
71         int rc;
72
73         if (test_mounted)
74                 goto skip_mounting;
75         test_mounted = true;
76
77         if (mount("none", "/sys/kernel/debug", "debugfs", 0, "") == -1) {
78                 /* Already mounted or don't have permission to mount is okay */
79                 if (errno != EPERM && errno != EBUSY)
80                         fprintf(stderr, "Warning: failed to mount debug: %s\n",
81                                 strerror(errno));
82         } else {
83                 struct stat mtab;
84
85                 /* This is all for RHEL6 which is old school. Can be removed
86                  * later when RHEL6 client support is dropped. */
87                 rc = lstat(_PATH_MOUNTED, &mtab);
88                 if (!rc && !S_ISLNK(mtab.st_mode)) {
89                         FILE *fp = setmntent(_PATH_MOUNTED, "r+");
90
91                         if (fp != NULL) {
92                                 const struct mntent fs = {
93                                         .mnt_fsname     = "debugfs",
94                                         .mnt_dir        = "/sys/kernel/debug",
95                                         .mnt_type       = "debugfs",
96                                         .mnt_opts       = "rw,relatime",
97                                 };
98
99                                 rc = addmntent(fp, &fs);
100                                 if (rc) {
101                                         fprintf(stderr,
102                                                 "failed to add debugfs to %s: %s\n",
103                                                 _PATH_MOUNTED, strerror(errno));
104                                 }
105                                 endmntent(fp);
106                         } else {
107                                 fprintf(stderr, "could not open %s: %s\n",
108                                         _PATH_MOUNTED, strerror(errno));
109                         }
110                 }
111         }
112 skip_mounting:
113         va_start(args, pattern);
114         rc = vsnprintf(buf, sizeof(buf), pattern, args);
115         va_end(args);
116         if (rc < 0) {
117                 return rc;
118         } else if (rc >= sizeof(buf)) {
119                 errno = EINVAL;
120                 return -1;
121         }
122         len += rc;
123
124         if (strlcat(path, buf, sizeof(path)) != len) {
125                 errno = E2BIG;
126                 return -1;
127         }
128
129         rc = glob(path, GLOB_BRACE, NULL, paths);
130         if (rc != 0) {
131                 switch (rc) {
132                 case GLOB_NOSPACE:
133                         errno = ENOMEM;
134                         break;
135                 case GLOB_ABORTED:
136                         errno = ENODEV;
137                         break;
138                 case GLOB_NOMATCH:
139                 default:
140                         errno = ENOENT;
141                         break;
142                 }
143                 rc = -1;
144         }
145
146         return rc;
147 }