sysctl.c revision 1.35
1/*	$NetBSD: sysctl.c,v 1.35 2015/02/05 16:05:20 christos Exp $	*/
2
3/*-
4 * Copyright (c) 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#if defined(LIBC_SCCS) && !defined(lint)
34#if 0
35static char sccsid[] = "@(#)sysctl.c	8.2 (Berkeley) 1/4/94";
36#else
37__RCSID("$NetBSD: sysctl.c,v 1.35 2015/02/05 16:05:20 christos Exp $");
38#endif
39#endif /* LIBC_SCCS and not lint */
40
41#include "namespace.h"
42#include <sys/param.h>
43#define __COMPAT_SYSCTL
44#include <sys/sysctl.h>
45
46#include <assert.h>
47#include <errno.h>
48#include <paths.h>
49#include <stdio.h>
50#include <string.h>
51#include <unistd.h>
52#include "extern.h"
53
54#ifdef __weak_alias
55__weak_alias(sysctl,_sysctl)
56#endif
57
58/*
59 * handles requests off the user subtree
60 */
61static int user_sysctl(const int *, u_int, void *, size_t *,
62			const void *, size_t);
63
64/*
65 * copies out individual nodes taking target version into account
66 */
67static size_t __cvt_node_out(uint, const struct sysctlnode *, void **,
68			     size_t *);
69
70#include <stdlib.h>
71
72int
73sysctl(const int *name, unsigned int namelen,
74	void *oldp, size_t *oldlenp,
75	const void *newp, size_t newlen)
76{
77	size_t oldlen, savelen;
78	int error;
79
80	if (name[0] != CTL_USER)
81		return (__sysctl(name, namelen, oldp, oldlenp,
82				 newp, newlen));
83
84	oldlen = (oldlenp == NULL) ? 0 : *oldlenp;
85	savelen = oldlen;
86	error = user_sysctl(name + 1, namelen - 1, oldp, &oldlen, newp, newlen);
87
88	if (error != 0) {
89		errno = error;
90		return (-1);
91	}
92
93	if (oldlenp != NULL) {
94		*oldlenp = oldlen;
95		if (oldp != NULL && oldlen > savelen) {
96			errno = ENOMEM;
97			return (-1);
98		}
99	}
100
101	return (0);
102}
103
104static int
105user_sysctl(const int *name, unsigned int namelen,
106	void *oldp, size_t *oldlenp,
107	const void *newp, size_t newlen)
108{
109#define _INT(s, n, v, d) {					\
110	.sysctl_flags = CTLFLAG_IMMEDIATE|CTLFLAG_PERMANENT|	\
111			CTLTYPE_INT|SYSCTL_VERSION,		\
112	.sysctl_size = sizeof(int),				\
113	.sysctl_name = (s),					\
114	.sysctl_num = (n),					\
115	.sysctl_un.scu_idata = (v),				\
116	.sysctl_desc = (d),					\
117	}
118
119	/*
120	 * the nodes under the "user" node
121	 */
122	static const struct sysctlnode sysctl_usermib[] = {
123#if defined(lint)
124		/*
125		 * lint doesn't like my initializers
126		 */
127		0
128#else /* !lint */
129		{
130			.sysctl_flags = SYSCTL_VERSION|CTLFLAG_PERMANENT|
131				CTLTYPE_STRING,
132			.sysctl_size = sizeof(_PATH_STDPATH),
133			.sysctl_name = "cs_path",
134			.sysctl_num = USER_CS_PATH,
135			.sysctl_data = __UNCONST(_PATH_STDPATH),
136			.sysctl_desc = __UNCONST(
137				"A value for the PATH environment variable "
138				"that finds all the standard utilities"),
139		},
140		_INT("bc_base_max", USER_BC_BASE_MAX, BC_BASE_MAX,
141		     "The maximum ibase/obase values in the bc(1) utility"),
142		_INT("bc_dim_max", USER_BC_DIM_MAX, BC_DIM_MAX,
143		     "The maximum array size in the bc(1) utility"),
144		_INT("bc_scale_max", USER_BC_SCALE_MAX, BC_SCALE_MAX,
145		     "The maximum scale value in the bc(1) utility"),
146		_INT("bc_string_max", USER_BC_STRING_MAX, BC_STRING_MAX,
147		     "The maximum string length in the bc(1) utility"),
148		_INT("coll_weights_max", USER_COLL_WEIGHTS_MAX,
149		     COLL_WEIGHTS_MAX, "The maximum number of weights that can "
150		     "be assigned to any entry of the LC_COLLATE order keyword "
151		     "in the locale definition file"),
152		_INT("expr_nest_max", USER_EXPR_NEST_MAX, EXPR_NEST_MAX,
153		     "The maximum number of expressions that can be nested "
154		     "within parenthesis by the expr(1) utility"),
155		_INT("line_max", USER_LINE_MAX, LINE_MAX, "The maximum length "
156		     "in bytes of a text-processing utility's input line"),
157		_INT("re_dup_max", USER_RE_DUP_MAX, RE_DUP_MAX, "The maximum "
158		     "number of repeated occurrences of a regular expression "
159		     "permitted when using interval notation"),
160		_INT("posix2_version", USER_POSIX2_VERSION, _POSIX2_VERSION,
161		     "The version of POSIX 1003.2 with which the system "
162		     "attempts to comply"),
163#ifdef _POSIX2_C_BIND
164		_INT("posix2_c_bind", USER_POSIX2_C_BIND, 1,
165		     "Whether the system's C-language development facilities "
166		     "support the C-Language Bindings Option"),
167#else
168		_INT("posix2_c_bind", USER_POSIX2_C_BIND, 0,
169		     "Whether the system's C-language development facilities "
170		     "support the C-Language Bindings Option"),
171#endif
172#ifdef POSIX2_C_DEV
173		_INT("posix2_c_dev", USER_POSIX2_C_DEV, 1,
174		     "Whether the system supports the C-Language Development "
175		     "Utilities Option"),
176#else
177		_INT("posix2_c_dev", USER_POSIX2_C_DEV, 0,
178		     "Whether the system supports the C-Language Development "
179		     "Utilities Option"),
180#endif
181#ifdef POSIX2_CHAR_TERM
182		_INT("posix2_char_term", USER_POSIX2_CHAR_TERM, 1,
183		     "Whether the system supports at least one terminal type "
184		     "capable of all operations described in POSIX 1003.2"),
185#else
186		_INT("posix2_char_term", USER_POSIX2_CHAR_TERM, 0,
187		     "Whether the system supports at least one terminal type "
188		     "capable of all operations described in POSIX 1003.2"),
189#endif
190#ifdef POSIX2_FORT_DEV
191		_INT("posix2_fort_dev", USER_POSIX2_FORT_DEV, 1,
192		     "Whether the system supports the FORTRAN Development "
193		     "Utilities Option"),
194#else
195		_INT("posix2_fort_dev", USER_POSIX2_FORT_DEV, 0,
196		     "Whether the system supports the FORTRAN Development "
197		     "Utilities Option"),
198#endif
199#ifdef POSIX2_FORT_RUN
200		_INT("posix2_fort_run", USER_POSIX2_FORT_RUN, 1,
201		     "Whether the system supports the FORTRAN Runtime "
202		     "Utilities Option"),
203#else
204		_INT("posix2_fort_run", USER_POSIX2_FORT_RUN, 0,
205		     "Whether the system supports the FORTRAN Runtime "
206		     "Utilities Option"),
207#endif
208#ifdef POSIX2_LOCALEDEF
209		_INT("posix2_localedef", USER_POSIX2_LOCALEDEF, 1,
210		     "Whether the system supports the creation of locales"),
211#else
212		_INT("posix2_localedef", USER_POSIX2_LOCALEDEF, 0,
213		     "Whether the system supports the creation of locales"),
214#endif
215#ifdef POSIX2_SW_DEV
216		_INT("posix2_sw_dev", USER_POSIX2_SW_DEV, 1,
217		     "Whether the system supports the Software Development "
218		     "Utilities Option"),
219#else
220		_INT("posix2_sw_dev", USER_POSIX2_SW_DEV, 0,
221		     "Whether the system supports the Software Development "
222		     "Utilities Option"),
223#endif
224#ifdef POSIX2_UPE
225		_INT("posix2_upe", USER_POSIX2_UPE, 1,
226		     "Whether the system supports the User Portability "
227		     "Utilities Option"),
228#else
229		_INT("posix2_upe", USER_POSIX2_UPE, 0,
230		     "Whether the system supports the User Portability "
231		     "Utilities Option"),
232#endif
233		_INT("stream_max", USER_STREAM_MAX, FOPEN_MAX,
234		     "The minimum maximum number of streams that a process "
235		     "may have open at any one time"),
236		_INT("tzname_max", USER_TZNAME_MAX, NAME_MAX,
237		     "The minimum maximum number of types supported for the "
238		     "name of a timezone"),
239		_INT("atexit_max", USER_ATEXIT_MAX, -1,
240		     "The maximum number of functions that may be registered "
241		     "with atexit(3)"),
242#endif /* !lint */
243	};
244#undef _INT
245
246	static const int clen = sizeof(sysctl_usermib) /
247		sizeof(sysctl_usermib[0]);
248
249	const struct sysctlnode *node;
250	int ni;
251	size_t l, sz;
252
253	/*
254	 * none of these nodes are writable and they're all terminal (for now)
255	 */
256	if (namelen != 1)
257		return (EINVAL);
258
259	l = *oldlenp;
260	if (name[0] == CTL_QUERY) {
261		uint v;
262		node = newp;
263		if (node == NULL)
264			return (EINVAL);
265		else if (SYSCTL_VERS(node->sysctl_flags) == SYSCTL_VERS_1 &&
266			 newlen == sizeof(struct sysctlnode))
267			v = SYSCTL_VERS_1;
268		else
269			return (EINVAL);
270
271		sz = 0;
272		for (ni = 0; ni < clen; ni++)
273			sz += __cvt_node_out(v, &sysctl_usermib[ni], &oldp, &l);
274		*oldlenp = sz;
275		return (0);
276	}
277
278	if (name[0] == CTL_DESCRIBE) {
279		/*
280		 * XXX make sure this is larger than the largest
281		 * "user" description
282		 */
283		char buf[192];
284		struct sysctldesc *d1 = (void *)&buf[0], *d2 = oldp;
285		size_t d;
286
287		node = newp;
288		if (node != NULL &&
289		    (SYSCTL_VERS(node->sysctl_flags) < SYSCTL_VERS_1 ||
290		     newlen != sizeof(struct sysctlnode)))
291			return (EINVAL);
292
293		sz = 0;
294		for (ni = 0; ni < clen; ni++) {
295			memset(&buf[0], 0, sizeof(buf));
296			if (node != NULL &&
297			    node->sysctl_num != sysctl_usermib[ni].sysctl_num)
298				continue;
299			d1->descr_num = sysctl_usermib[ni].sysctl_num;
300			d1->descr_ver = sysctl_usermib[ni].sysctl_ver;
301			if (sysctl_usermib[ni].sysctl_desc == NULL)
302				d1->descr_len = 1;
303			else {
304				size_t dlen;
305				(void)strlcpy(d1->descr_str,
306					sysctl_usermib[ni].sysctl_desc,
307					sizeof(buf) - sizeof(*d1));
308				dlen = strlen(d1->descr_str) + 1;
309				_DIAGASSERT(__type_fit(uint32_t, dlen));
310				d1->descr_len = (uint32_t)dlen;
311			}
312			d = (size_t)__sysc_desc_adv(NULL, d1->descr_len);
313			if (d2 != NULL)
314				memcpy(d2, d1, d);
315			sz += d;
316			d2 = (struct sysctldesc *)(void *)((char *)d2 + d);
317			if (node != NULL)
318				break;
319		}
320		*oldlenp = sz;
321		if (sz == 0 && node != NULL)
322			return (ENOENT);
323		return (0);
324
325	}
326
327	/*
328	 * none of these nodes are writable
329	 */
330	if (newp != NULL || newlen != 0)
331		return (EPERM);
332
333	node = &sysctl_usermib[0];
334	for (ni = 0; ni	< clen; ni++)
335		if (name[0] == node[ni].sysctl_num)
336			break;
337	if (ni == clen)
338		return (EOPNOTSUPP);
339
340	node = &node[ni];
341	if (node->sysctl_flags & CTLFLAG_IMMEDIATE) {
342		switch (SYSCTL_TYPE(node->sysctl_flags)) {
343		case CTLTYPE_INT:
344			newp = &node->sysctl_idata;
345			break;
346		case CTLTYPE_QUAD:
347			newp = &node->sysctl_qdata;
348			break;
349		default:
350			return (EINVAL);
351		}
352	}
353	else
354		newp = node->sysctl_data;
355
356	l = MIN(l, node->sysctl_size);
357	if (oldp != NULL)
358		memcpy(oldp, newp, l);
359	*oldlenp = node->sysctl_size;
360
361	return (0);
362}
363
364static size_t
365__cvt_node_out(uint v, const struct sysctlnode *n, void **o, size_t *l)
366{
367	const void *src = n;
368	size_t sz;
369
370	switch (v) {
371#if (SYSCTL_VERSION != SYSCTL_VERS_1)
372#error __cvt_node_out: no support for SYSCTL_VERSION
373#endif /* (SYSCTL_VERSION != SYSCTL_VERS_1) */
374
375	case SYSCTL_VERSION:
376		sz = sizeof(struct sysctlnode);
377		break;
378
379	default:
380		sz = 0;
381		break;
382	}
383
384	if (sz > 0 && *o != NULL && *l >= sz) {
385		memcpy(*o, src, sz);
386		*o = sz + (caddr_t)*o;
387		*l -= sz;
388	}
389
390	return(sz);
391}
392