1219820Sjeff/*
2219820Sjeff * Copyright (c) 2004-2008 Voltaire Inc.  All rights reserved.
3219820Sjeff *
4219820Sjeff * This software is available to you under a choice of one of two
5219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
6219820Sjeff * General Public License (GPL) Version 2, available from the file
7219820Sjeff * COPYING in the main directory of this source tree, or the
8219820Sjeff * OpenIB.org BSD license below:
9219820Sjeff *
10219820Sjeff *     Redistribution and use in source and binary forms, with or
11219820Sjeff *     without modification, are permitted provided that the following
12219820Sjeff *     conditions are met:
13219820Sjeff *
14219820Sjeff *      - Redistributions of source code must retain the above
15219820Sjeff *        copyright notice, this list of conditions and the following
16219820Sjeff *        disclaimer.
17219820Sjeff *
18219820Sjeff *      - Redistributions in binary form must reproduce the above
19219820Sjeff *        copyright notice, this list of conditions and the following
20219820Sjeff *        disclaimer in the documentation and/or other materials
21219820Sjeff *        provided with the distribution.
22219820Sjeff *
23219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30219820Sjeff * SOFTWARE.
31219820Sjeff *
32219820Sjeff */
33219820Sjeff
34219820Sjeff#define _GNU_SOURCE
35219820Sjeff
36219820Sjeff#if HAVE_CONFIG_H
37219820Sjeff#  include <config.h>
38219820Sjeff#endif /* HAVE_CONFIG_H */
39219820Sjeff
40219820Sjeff#include <inttypes.h>
41219820Sjeff#include <string.h>
42219820Sjeff#include <errno.h>
43219820Sjeff#include <stdio.h>
44219820Sjeff#include <stdlib.h>
45219820Sjeff#include <unistd.h>
46219820Sjeff#include <stdarg.h>
47219820Sjeff#include <sys/types.h>
48219820Sjeff#include <sys/stat.h>
49219820Sjeff#include <fcntl.h>
50219820Sjeff#include <sys/ioctl.h>
51219820Sjeff#include <unistd.h>
52219820Sjeff#include <string.h>
53219820Sjeff#include <endian.h>
54219820Sjeff#include <byteswap.h>
55219820Sjeff#include <sys/poll.h>
56219820Sjeff#include <syslog.h>
57219820Sjeff#include <netinet/in.h>
58219820Sjeff#include <errno.h>
59219820Sjeff
60219820Sjeff#include <sys/types.h>
61219820Sjeff#include <sys/sysctl.h>
62219820Sjeff
63219820Sjeff#include "common.h"
64219820Sjeff
65219820Sjeffstatic int
66219820Sjeffret_code(void)
67219820Sjeff{
68219820Sjeff	int e = errno;
69219820Sjeff
70219820Sjeff	if (e > 0)
71219820Sjeff		return -e;
72219820Sjeff	return e;
73219820Sjeff}
74219820Sjeff
75219820Sjeffint
76219820Sjeffsys_read_string(char *dir_name, char *file_name, char *str, int max_len)
77219820Sjeff{
78219820Sjeff	char path[256], *s;
79219820Sjeff	size_t len;
80219820Sjeff
81219820Sjeff	snprintf(path, sizeof(path), "%s/%s", dir_name, file_name);
82219820Sjeff
83219820Sjeff	for (s = &path[0]; *s != '\0'; s++)
84219820Sjeff		if (*s == '/')
85219820Sjeff			*s = '.';
86219820Sjeff
87219820Sjeff	len = max_len;
88219820Sjeff	if (sysctlbyname(&path[1], str, &len, NULL, 0) == -1)
89219820Sjeff		return ret_code();
90219820Sjeff
91219820Sjeff	str[(len < max_len) ? len : max_len - 1] = 0;
92219820Sjeff
93219820Sjeff	if ((s = strrchr(str, '\n')))
94219820Sjeff		*s = 0;
95219820Sjeff
96219820Sjeff	return 0;
97219820Sjeff}
98219820Sjeff
99219820Sjeffint
100219820Sjeffsys_read_guid(char *dir_name, char *file_name, uint64_t *net_guid)
101219820Sjeff{
102219820Sjeff	char buf[32], *str, *s;
103219820Sjeff	uint64_t guid;
104219820Sjeff	int r, i;
105219820Sjeff
106219820Sjeff	if ((r = sys_read_string(dir_name, file_name, buf, sizeof(buf))) < 0)
107219820Sjeff		return r;
108219820Sjeff
109219820Sjeff	guid = 0;
110219820Sjeff
111219820Sjeff	for (s = buf, i = 0 ; i < 4; i++) {
112219820Sjeff		if (!(str = strsep(&s, ": \t\n")))
113219820Sjeff			return -EINVAL;
114219820Sjeff		guid = (guid << 16) | (strtoul(str, 0, 16) & 0xffff);
115219820Sjeff	}
116219820Sjeff
117219820Sjeff	*net_guid = htonll(guid);
118219820Sjeff
119219820Sjeff	return 0;
120219820Sjeff}
121219820Sjeff
122219820Sjeffint
123219820Sjeffsys_read_gid(char *dir_name, char *file_name, uint8_t *gid)
124219820Sjeff{
125219820Sjeff	char buf[64], *str, *s;
126219820Sjeff	uint16_t *ugid = (uint16_t *)gid;
127219820Sjeff	int r, i;
128219820Sjeff
129219820Sjeff	if ((r = sys_read_string(dir_name, file_name, buf, sizeof(buf))) < 0)
130219820Sjeff		return r;
131219820Sjeff
132219820Sjeff	for (s = buf, i = 0 ; i < 8; i++) {
133219820Sjeff		if (!(str = strsep(&s, ": \t\n")))
134219820Sjeff			return -EINVAL;
135219820Sjeff		ugid[i] = htons(strtoul(str, 0, 16) & 0xffff);
136219820Sjeff	}
137219820Sjeff
138219820Sjeff	return 0;
139219820Sjeff}
140219820Sjeff
141219820Sjeffint
142219820Sjeffsys_read_uint64(char *dir_name, char *file_name, uint64_t *u)
143219820Sjeff{
144219820Sjeff	char buf[32];
145219820Sjeff	int r;
146219820Sjeff
147219820Sjeff	if ((r = sys_read_string(dir_name, file_name, buf, sizeof(buf))) < 0)
148219820Sjeff		return r;
149219820Sjeff
150219820Sjeff	*u = strtoull(buf, 0, 0);
151219820Sjeff
152219820Sjeff	return 0;
153219820Sjeff}
154219820Sjeff
155219820Sjeffint
156219820Sjeffsys_read_uint(char *dir_name, char *file_name, unsigned *u)
157219820Sjeff{
158219820Sjeff	char buf[32];
159219820Sjeff	int r;
160219820Sjeff
161219820Sjeff	if ((r = sys_read_string(dir_name, file_name, buf, sizeof(buf))) < 0)
162219820Sjeff		return r;
163219820Sjeff
164219820Sjeff	*u = strtoul(buf, 0, 0);
165219820Sjeff
166219820Sjeff	return 0;
167219820Sjeff}
168219820Sjeff
169219820Sjeff#define	DIRECTSIZ(namlen)						\
170219820Sjeff	(((uintptr_t)&((struct dirent *)0)->d_name +			\
171219820Sjeff	((namlen)+1)*sizeof(((struct dirent *)0)->d_name[0]) + 3) & ~3)
172219820Sjeff
173219820Sjeffint
174219820Sjeffsys_scandir(const char *dirname, struct dirent ***namelist,
175219820Sjeff    int (*select)(const struct dirent *),
176219820Sjeff    int (*compar)(const struct dirent **, const struct dirent **))
177219820Sjeff{
178219820Sjeff	struct dirent **names;
179219820Sjeff	struct dirent **names2;
180219820Sjeff	struct dirent *dp;
181219820Sjeff	char name[1024];
182219820Sjeff	int lsname[22];
183219820Sjeff	int chname[22];
184219820Sjeff	int name2[22];
185219820Sjeff	int oid[22];
186219820Sjeff	char *s;
187219820Sjeff	size_t n1, n2;
188219820Sjeff	size_t len, oidlen, namlen;
189219820Sjeff	int cnt, max;
190219820Sjeff	int err;
191219820Sjeff	int i;
192219820Sjeff
193219820Sjeff	*namelist = NULL;
194219820Sjeff	/* Skip the leading / */
195219820Sjeff	strncpy(name, &dirname[1], sizeof(name));
196219820Sjeff	for (s = &name[0]; *s != '\0'; s++)
197219820Sjeff		if (*s == '/')
198219820Sjeff			*s = '.';
199219820Sjeff	/*
200219820Sjeff	 * Resolve the path.
201219820Sjeff	 */
202219820Sjeff	len = sizeof(oid) / sizeof(int);
203219820Sjeff	namlen = strlen(name) + 1;
204219820Sjeff	if (sysctlnametomib(name, oid, &len) != 0)
205219820Sjeff		return (-errno);
206219820Sjeff	lsname[0] = 0;	/* Root */
207219820Sjeff	lsname[1] = 2;	/* Get next */
208219820Sjeff	memcpy(lsname+2, oid, len * sizeof(int));
209219820Sjeff	n1 = 2 + len;
210219820Sjeff	oidlen = len;
211219820Sjeff	/*
212219820Sjeff	 * Setup the return list of dirents.
213219820Sjeff	 */
214219820Sjeff	cnt = 0;
215219820Sjeff	max = 64;
216219820Sjeff	names = malloc(max * sizeof(void *));
217219820Sjeff	if (names == NULL)
218219820Sjeff		return (-ENOMEM);
219219820Sjeff
220219820Sjeff	for (;;) {
221219820Sjeff		n2 = sizeof(name2);
222219820Sjeff		if (sysctl(lsname, n1, name2, &n2, 0, 0) < 0) {
223219820Sjeff			if (errno == ENOENT)
224219820Sjeff				break;
225219820Sjeff			goto errout;
226219820Sjeff		}
227219820Sjeff		n2 /= sizeof(int);
228219820Sjeff		if (n2 < oidlen)
229219820Sjeff			break;
230219820Sjeff		for (i = 0; i < oidlen; i++)
231219820Sjeff			if (name2[i] != oid[i])
232219820Sjeff				goto out;
233219820Sjeff		chname[0] = 0;	/* root */
234219820Sjeff		chname[1] = 1;	/* oid name */
235219820Sjeff		memcpy(chname + 2, name2, n2 * sizeof(int));
236219820Sjeff		memcpy(lsname + 2, name2, n2 * sizeof(int));
237219820Sjeff		n1 = 2 + n2;
238219820Sjeff		/*
239219820Sjeff		 * scandir() is not supposed to go deeper than the requested
240219820Sjeff		 * directory but sysctl also doesn't return a node for
241219820Sjeff		 * 'subdirectories' so we have to find a file in the subdir
242219820Sjeff		 * and then truncate the name to report it.
243219820Sjeff	 	 */
244219820Sjeff		if (n2 > oidlen + 1) {
245219820Sjeff			/* Skip to the next name after this one. */
246219820Sjeff			n1 = 2 + oidlen + 1;
247219820Sjeff			lsname[n1 - 1]++;
248219820Sjeff		}
249219820Sjeff		len = sizeof(name);
250219820Sjeff		if (sysctl(chname, n2 + 2, name, &len, 0, 0) < 0)
251219820Sjeff			goto errout;
252219820Sjeff		if (len <= 0 || len < namlen)
253219820Sjeff			goto out;
254219820Sjeff		s = name + namlen;
255219820Sjeff		/* Just keep the first level name. */
256219820Sjeff		if (strchr(s, '.'))
257219820Sjeff			*strchr(s, '.') = '\0';
258219820Sjeff		len = strlen(s) + 1;
259219820Sjeff		dp = malloc(DIRECTSIZ(len));
260219820Sjeff		dp->d_reclen = DIRECTSIZ(len);
261219820Sjeff		dp->d_namlen = len;
262219820Sjeff		memcpy(&dp->d_name, s, len);
263219820Sjeff		if (select && !select(dp)) {
264219820Sjeff			free(dp);
265219820Sjeff			continue;
266219820Sjeff		}
267219820Sjeff		if (cnt == max) {
268219820Sjeff			max *= 2;
269219820Sjeff			names2 = realloc(names, max * sizeof(void *));
270219820Sjeff			if (names2 == NULL) {
271219820Sjeff				errno = ENOMEM;
272219820Sjeff				free(dp);
273219820Sjeff				goto errout;
274219820Sjeff			}
275219820Sjeff			names = names2;
276219820Sjeff		}
277219820Sjeff		names[cnt++] = dp;
278219820Sjeff	}
279219820Sjeffout:
280219820Sjeff	if (cnt && compar)
281219820Sjeff		qsort(names, cnt, sizeof(struct dirent *),
282219820Sjeff		    (int (*)(const void *, const void *))compar);
283219820Sjeff
284219820Sjeff	*namelist = names;
285219820Sjeff
286219820Sjeff	return (cnt);
287219820Sjeff
288219820Sjefferrout:
289219820Sjeff	err = errno;
290219820Sjeff	for (i = 0; i < cnt; i++)
291219820Sjeff		free(names[i]);
292219820Sjeff	free(names);
293219820Sjeff	return (-err);
294219820Sjeff}
295