xref: /freebsd/contrib/ofed/libibumad/sysfs.c (revision d6b92ffa)
1 /*
2  * Copyright (c) 2004-2008 Voltaire Inc.  All rights reserved.
3  *
4  * This software is available to you under a choice of one of two
5  * licenses.  You may choose to be licensed under the terms of the GNU
6  * General Public License (GPL) Version 2, available from the file
7  * COPYING in the main directory of this source tree, or the
8  * OpenIB.org BSD license below:
9  *
10  *     Redistribution and use in source and binary forms, with or
11  *     without modification, are permitted provided that the following
12  *     conditions are met:
13  *
14  *      - Redistributions of source code must retain the above
15  *        copyright notice, this list of conditions and the following
16  *        disclaimer.
17  *
18  *      - Redistributions in binary form must reproduce the above
19  *        copyright notice, this list of conditions and the following
20  *        disclaimer in the documentation and/or other materials
21  *        provided with the distribution.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30  * SOFTWARE.
31  *
32  */
33 #include <config.h>
34 
35 #include <infiniband/endian.h>
36 #include <inttypes.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <unistd.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <sys/sysctl.h>
45 #include <fcntl.h>
46 #include <dirent.h>
47 #include "sysfs.h"
48 
49 static int ret_code(void)
50 {
51 	int e = errno;
52 
53 	if (e > 0)
54 		return -e;
55 	return e;
56 }
57 
58 int sys_read_string(const char *dir_name, const char *file_name, char *str, int max_len)
59 {
60 	char path[256], *s;
61 	size_t len;
62 
63 	snprintf(path, sizeof(path), "%s/%s", dir_name, file_name);
64 
65 	for (s = &path[0]; *s != '\0'; s++)
66 		if (*s == '/')
67 			*s = '.';
68 
69 	len = max_len;
70 	if (sysctlbyname(&path[1], str, &len, NULL, 0) == -1)
71 		return ret_code();
72 
73 	str[(len < max_len) ? len : max_len - 1] = 0;
74 
75 	if ((s = strrchr(str, '\n')))
76 		*s = 0;
77 
78 	return 0;
79 }
80 
81 int sys_read_guid(const char *dir_name, const char *file_name, __be64 *net_guid)
82 {
83 	char buf[32], *str, *s;
84 	uint64_t guid;
85 	int r, i;
86 
87 	if ((r = sys_read_string(dir_name, file_name, buf, sizeof(buf))) < 0)
88 		return r;
89 
90 	guid = 0;
91 
92 	for (s = buf, i = 0; i < 4; i++) {
93 		if (!(str = strsep(&s, ": \t\n")))
94 			return -EINVAL;
95 		guid = (guid << 16) | (strtoul(str, NULL, 16) & 0xffff);
96 	}
97 
98 	*net_guid = htobe64(guid);
99 
100 	return 0;
101 }
102 
103 int sys_read_gid(const char *dir_name, const char *file_name,
104 		 union umad_gid *gid)
105 {
106 	char buf[64], *str, *s;
107 	__be16 *ugid = (__be16 *) gid;
108 	int r, i;
109 
110 	if ((r = sys_read_string(dir_name, file_name, buf, sizeof(buf))) < 0)
111 		return r;
112 
113 	for (s = buf, i = 0; i < 8; i++) {
114 		if (!(str = strsep(&s, ": \t\n")))
115 			return -EINVAL;
116 		ugid[i] = htobe16(strtoul(str, NULL, 16) & 0xffff);
117 	}
118 
119 	return 0;
120 }
121 
122 int sys_read_uint64(const char *dir_name, const char *file_name, uint64_t * u)
123 {
124 	char buf[32];
125 	int r;
126 
127 	if ((r = sys_read_string(dir_name, file_name, buf, sizeof(buf))) < 0)
128 		return r;
129 
130 	*u = strtoull(buf, NULL, 0);
131 
132 	return 0;
133 }
134 
135 int sys_read_uint(const char *dir_name, const char *file_name, unsigned *u)
136 {
137 	char buf[32];
138 	int r;
139 
140 	if ((r = sys_read_string(dir_name, file_name, buf, sizeof(buf))) < 0)
141 		return r;
142 
143 	*u = strtoul(buf, NULL, 0);
144 
145 	return 0;
146 }
147 
148 #define	DIRECTSIZ(namlen)						\
149 	(((uintptr_t)&((struct dirent *)0)->d_name +			\
150 	((namlen)+1)*sizeof(((struct dirent *)0)->d_name[0]) + 3) & ~3)
151 
152 int
153 sys_scandir(const char *dirname, struct dirent ***namelist,
154     int (*select)(const struct dirent *),
155     int (*compar)(const struct dirent **, const struct dirent **))
156 {
157 	struct dirent **names;
158 	struct dirent **names2;
159 	struct dirent *dp;
160 	char name[1024];
161 	int lsname[22];
162 	int chname[22];
163 	int name2[22];
164 	int oid[22];
165 	char *s;
166 	size_t n1, n2;
167 	size_t len, oidlen, namlen;
168 	int cnt, max;
169 	int err;
170 	int i;
171 
172 	*namelist = NULL;
173 	/* Skip the leading / */
174 	strncpy(name, &dirname[1], sizeof(name));
175 	for (s = &name[0]; *s != '\0'; s++)
176 		if (*s == '/')
177 			*s = '.';
178 	/*
179 	 * Resolve the path.
180 	 */
181 	len = sizeof(oid) / sizeof(int);
182 	namlen = strlen(name) + 1;
183 	if (sysctlnametomib(name, oid, &len) != 0)
184 		return (-errno);
185 	lsname[0] = 0;	/* Root */
186 	lsname[1] = 2;	/* Get next */
187 	memcpy(lsname+2, oid, len * sizeof(int));
188 	n1 = 2 + len;
189 	oidlen = len;
190 	/*
191 	 * Setup the return list of dirents.
192 	 */
193 	cnt = 0;
194 	max = 64;
195 	names = malloc(max * sizeof(void *));
196 	if (names == NULL)
197 		return (-ENOMEM);
198 
199 	for (;;) {
200 		n2 = sizeof(name2);
201 		if (sysctl(lsname, n1, name2, &n2, 0, 0) < 0) {
202 			if (errno == ENOENT)
203 				break;
204 			goto errout;
205 		}
206 		n2 /= sizeof(int);
207 		if (n2 < oidlen)
208 			break;
209 		for (i = 0; i < oidlen; i++)
210 			if (name2[i] != oid[i])
211 				goto out;
212 		chname[0] = 0;	/* root */
213 		chname[1] = 1;	/* oid name */
214 		memcpy(chname + 2, name2, n2 * sizeof(int));
215 		memcpy(lsname + 2, name2, n2 * sizeof(int));
216 		n1 = 2 + n2;
217 		/*
218 		 * scandir() is not supposed to go deeper than the requested
219 		 * directory but sysctl also doesn't return a node for
220 		 * 'subdirectories' so we have to find a file in the subdir
221 		 * and then truncate the name to report it.
222 	 	 */
223 		if (n2 > oidlen + 1) {
224 			/* Skip to the next name after this one. */
225 			n1 = 2 + oidlen + 1;
226 			lsname[n1 - 1]++;
227 		}
228 		len = sizeof(name);
229 		if (sysctl(chname, n2 + 2, name, &len, 0, 0) < 0)
230 			goto errout;
231 		if (len <= 0 || len < namlen)
232 			goto out;
233 		s = name + namlen;
234 		/* Just keep the first level name. */
235 		if (strchr(s, '.'))
236 			*strchr(s, '.') = '\0';
237 		len = strlen(s) + 1;
238 		dp = malloc(DIRECTSIZ(len));
239 		dp->d_reclen = DIRECTSIZ(len);
240 		dp->d_namlen = len;
241 		memcpy(&dp->d_name, s, len);
242 		if (select && !select(dp)) {
243 			free(dp);
244 			continue;
245 		}
246 		if (cnt == max) {
247 			max *= 2;
248 			names2 = realloc(names, max * sizeof(void *));
249 			if (names2 == NULL) {
250 				errno = ENOMEM;
251 				free(dp);
252 				goto errout;
253 			}
254 			names = names2;
255 		}
256 		names[cnt++] = dp;
257 	}
258 out:
259 	if (cnt && compar)
260 		qsort(names, cnt, sizeof(struct dirent *),
261 		    (int (*)(const void *, const void *))compar);
262 
263 	*namelist = names;
264 
265 	return (cnt);
266 
267 errout:
268 	err = errno;
269 	for (i = 0; i < cnt; i++)
270 		free(names[i]);
271 	free(names);
272 	return (-err);
273 }
274