xref: /netbsd-src/lib/libc/gen/sysctl.c (revision bc7a3fe21e5e02f45a38df18fec1474656d84a65)
1 /*	$NetBSD: sysctl.c,v 1.38 2021/03/30 15:31:51 rillig 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
35 static char sccsid[] = "@(#)sysctl.c	8.2 (Berkeley) 1/4/94";
36 #else
37 __RCSID("$NetBSD: sysctl.c,v 1.38 2021/03/30 15:31:51 rillig 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  */
61 static 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  */
67 static size_t __cvt_node_out(uint, const struct sysctlnode *, void **,
68 			     size_t *);
69 
70 #include <stdlib.h>
71 
72 int
sysctl(const int * name,unsigned int namelen,void * oldp,size_t * oldlenp,const void * newp,size_t newlen)73 sysctl(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 
104 static int
user_sysctl(const int * name,unsigned int namelen,void * oldp,size_t * oldlenp,const void * newp,size_t newlen)105 user_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 		{
124 			.sysctl_flags = SYSCTL_VERSION|CTLFLAG_PERMANENT|
125 				CTLTYPE_STRING,
126 			.sysctl_size = sizeof(_PATH_STDPATH),
127 			.sysctl_name = "cs_path",
128 			.sysctl_num = USER_CS_PATH,
129 			.sysctl_data = __UNCONST(_PATH_STDPATH),
130 			.sysctl_desc = __UNCONST(
131 				"A value for the PATH environment variable "
132 				"that finds all the standard utilities"),
133 		},
134 		_INT("bc_base_max", USER_BC_BASE_MAX, BC_BASE_MAX,
135 		     "The maximum ibase/obase values in the bc(1) utility"),
136 		_INT("bc_dim_max", USER_BC_DIM_MAX, BC_DIM_MAX,
137 		     "The maximum array size in the bc(1) utility"),
138 		_INT("bc_scale_max", USER_BC_SCALE_MAX, BC_SCALE_MAX,
139 		     "The maximum scale value in the bc(1) utility"),
140 		_INT("bc_string_max", USER_BC_STRING_MAX, BC_STRING_MAX,
141 		     "The maximum string length in the bc(1) utility"),
142 		_INT("coll_weights_max", USER_COLL_WEIGHTS_MAX,
143 		     COLL_WEIGHTS_MAX, "The maximum number of weights that can "
144 		     "be assigned to any entry of the LC_COLLATE order keyword "
145 		     "in the locale definition file"),
146 		_INT("expr_nest_max", USER_EXPR_NEST_MAX, EXPR_NEST_MAX,
147 		     "The maximum number of expressions that can be nested "
148 		     "within parenthesis by the expr(1) utility"),
149 		_INT("line_max", USER_LINE_MAX, LINE_MAX, "The maximum length "
150 		     "in bytes of a text-processing utility's input line"),
151 		_INT("re_dup_max", USER_RE_DUP_MAX, RE_DUP_MAX, "The maximum "
152 		     "number of repeated occurrences of a regular expression "
153 		     "permitted when using interval notation"),
154 		_INT("posix2_version", USER_POSIX2_VERSION, _POSIX2_VERSION,
155 		     "The version of POSIX 1003.2 with which the system "
156 		     "attempts to comply"),
157 #ifdef _POSIX2_C_BIND
158 		_INT("posix2_c_bind", USER_POSIX2_C_BIND, 1,
159 		     "Whether the system's C-language development facilities "
160 		     "support the C-Language Bindings Option"),
161 #else
162 		_INT("posix2_c_bind", USER_POSIX2_C_BIND, 0,
163 		     "Whether the system's C-language development facilities "
164 		     "support the C-Language Bindings Option"),
165 #endif
166 #ifdef POSIX2_C_DEV
167 		_INT("posix2_c_dev", USER_POSIX2_C_DEV, 1,
168 		     "Whether the system supports the C-Language Development "
169 		     "Utilities Option"),
170 #else
171 		_INT("posix2_c_dev", USER_POSIX2_C_DEV, 0,
172 		     "Whether the system supports the C-Language Development "
173 		     "Utilities Option"),
174 #endif
175 #ifdef POSIX2_CHAR_TERM
176 		_INT("posix2_char_term", USER_POSIX2_CHAR_TERM, 1,
177 		     "Whether the system supports at least one terminal type "
178 		     "capable of all operations described in POSIX 1003.2"),
179 #else
180 		_INT("posix2_char_term", USER_POSIX2_CHAR_TERM, 0,
181 		     "Whether the system supports at least one terminal type "
182 		     "capable of all operations described in POSIX 1003.2"),
183 #endif
184 #ifdef POSIX2_FORT_DEV
185 		_INT("posix2_fort_dev", USER_POSIX2_FORT_DEV, 1,
186 		     "Whether the system supports the FORTRAN Development "
187 		     "Utilities Option"),
188 #else
189 		_INT("posix2_fort_dev", USER_POSIX2_FORT_DEV, 0,
190 		     "Whether the system supports the FORTRAN Development "
191 		     "Utilities Option"),
192 #endif
193 #ifdef POSIX2_FORT_RUN
194 		_INT("posix2_fort_run", USER_POSIX2_FORT_RUN, 1,
195 		     "Whether the system supports the FORTRAN Runtime "
196 		     "Utilities Option"),
197 #else
198 		_INT("posix2_fort_run", USER_POSIX2_FORT_RUN, 0,
199 		     "Whether the system supports the FORTRAN Runtime "
200 		     "Utilities Option"),
201 #endif
202 #ifdef POSIX2_LOCALEDEF
203 		_INT("posix2_localedef", USER_POSIX2_LOCALEDEF, 1,
204 		     "Whether the system supports the creation of locales"),
205 #else
206 		_INT("posix2_localedef", USER_POSIX2_LOCALEDEF, 0,
207 		     "Whether the system supports the creation of locales"),
208 #endif
209 #ifdef POSIX2_SW_DEV
210 		_INT("posix2_sw_dev", USER_POSIX2_SW_DEV, 1,
211 		     "Whether the system supports the Software Development "
212 		     "Utilities Option"),
213 #else
214 		_INT("posix2_sw_dev", USER_POSIX2_SW_DEV, 0,
215 		     "Whether the system supports the Software Development "
216 		     "Utilities Option"),
217 #endif
218 #ifdef POSIX2_UPE
219 		_INT("posix2_upe", USER_POSIX2_UPE, 1,
220 		     "Whether the system supports the User Portability "
221 		     "Utilities Option"),
222 #else
223 		_INT("posix2_upe", USER_POSIX2_UPE, 0,
224 		     "Whether the system supports the User Portability "
225 		     "Utilities Option"),
226 #endif
227 		_INT("stream_max", USER_STREAM_MAX, FOPEN_MAX,
228 		     "The minimum maximum number of streams that a process "
229 		     "may have open at any one time"),
230 		_INT("tzname_max", USER_TZNAME_MAX, NAME_MAX,
231 		     "The minimum maximum number of types supported for the "
232 		     "name of a timezone"),
233 		_INT("atexit_max", USER_ATEXIT_MAX, -1,
234 		     "The maximum number of functions that may be registered "
235 		     "with atexit(3)"),
236 	};
237 #undef _INT
238 
239 	static const int clen = sizeof(sysctl_usermib) /
240 		sizeof(sysctl_usermib[0]);
241 
242 	const struct sysctlnode *node;
243 	int ni;
244 	size_t l, sz;
245 
246 	/*
247 	 * none of these nodes are writable and they're all terminal (for now)
248 	 */
249 	if (namelen != 1)
250 		return (EINVAL);
251 
252 	l = *oldlenp;
253 	if (name[0] == CTL_QUERY) {
254 		uint v;
255 		node = newp;
256 		if (node == NULL)
257 			return (EINVAL);
258 		else if (SYSCTL_VERS(node->sysctl_flags) == SYSCTL_VERS_1 &&
259 			 newlen == sizeof(struct sysctlnode))
260 			v = SYSCTL_VERS_1;
261 		else
262 			return (EINVAL);
263 
264 		sz = 0;
265 		for (ni = 0; ni < clen; ni++)
266 			sz += __cvt_node_out(v, &sysctl_usermib[ni], &oldp, &l);
267 		*oldlenp = sz;
268 		return (0);
269 	}
270 
271 	if (name[0] == CTL_DESCRIBE) {
272 		/*
273 		 * XXX make sure this is larger than the largest
274 		 * "user" description
275 		 */
276 		char buf[192];
277 		struct sysctldesc *d1 = (void *)&buf[0], *d2 = oldp;
278 		size_t d;
279 
280 		node = newp;
281 		if (node != NULL &&
282 		    (SYSCTL_VERS(node->sysctl_flags) < SYSCTL_VERS_1 ||
283 		     newlen != sizeof(struct sysctlnode)))
284 			return (EINVAL);
285 
286 		sz = 0;
287 		for (ni = 0; ni < clen; ni++) {
288 			memset(&buf[0], 0, sizeof(buf));
289 			if (node != NULL &&
290 			    node->sysctl_num != sysctl_usermib[ni].sysctl_num)
291 				continue;
292 			d1->descr_num = sysctl_usermib[ni].sysctl_num;
293 			d1->descr_ver = sysctl_usermib[ni].sysctl_ver;
294 			if (sysctl_usermib[ni].sysctl_desc == NULL)
295 				d1->descr_len = 1;
296 			else {
297 				size_t dlen;
298 				(void)strlcpy(d1->descr_str,
299 					sysctl_usermib[ni].sysctl_desc,
300 					sizeof(buf) - sizeof(*d1));
301 				dlen = strlen(d1->descr_str) + 1;
302 				_DIAGASSERT(__type_fit(uint32_t, dlen));
303 				d1->descr_len = (uint32_t)dlen;
304 			}
305 			d = (size_t)__sysc_desc_len(d1->descr_len);
306 			if (d2 != NULL)
307 				memcpy(d2, d1, d);
308 			sz += d;
309 			d2 = (void *)((char *)(void *)d2 + d);
310 			if (node != NULL)
311 				break;
312 		}
313 		*oldlenp = sz;
314 		if (sz == 0 && node != NULL)
315 			return (ENOENT);
316 		return (0);
317 
318 	}
319 
320 	/*
321 	 * none of these nodes are writable
322 	 */
323 	if (newp != NULL || newlen != 0)
324 		return (EPERM);
325 
326 	node = &sysctl_usermib[0];
327 	for (ni = 0; ni	< clen; ni++)
328 		if (name[0] == node[ni].sysctl_num)
329 			break;
330 	if (ni == clen)
331 		return (EOPNOTSUPP);
332 
333 	node = &node[ni];
334 	if (node->sysctl_flags & CTLFLAG_IMMEDIATE) {
335 		switch (SYSCTL_TYPE(node->sysctl_flags)) {
336 		case CTLTYPE_INT:
337 			newp = &node->sysctl_idata;
338 			break;
339 		case CTLTYPE_QUAD:
340 			newp = &node->sysctl_qdata;
341 			break;
342 		default:
343 			return (EINVAL);
344 		}
345 	}
346 	else
347 		newp = node->sysctl_data;
348 
349 	l = MIN(l, node->sysctl_size);
350 	if (oldp != NULL)
351 		memcpy(oldp, newp, l);
352 	*oldlenp = node->sysctl_size;
353 
354 	return (0);
355 }
356 
357 static size_t
__cvt_node_out(uint v,const struct sysctlnode * n,void ** o,size_t * l)358 __cvt_node_out(uint v, const struct sysctlnode *n, void **o, size_t *l)
359 {
360 	const void *src = n;
361 	size_t sz;
362 
363 	switch (v) {
364 #if (SYSCTL_VERSION != SYSCTL_VERS_1)
365 #error __cvt_node_out: no support for SYSCTL_VERSION
366 #endif /* (SYSCTL_VERSION != SYSCTL_VERS_1) */
367 
368 	case SYSCTL_VERSION:
369 		sz = sizeof(struct sysctlnode);
370 		break;
371 
372 	default:
373 		sz = 0;
374 		break;
375 	}
376 
377 	if (sz > 0 && *o != NULL && *l >= sz) {
378 		memcpy(*o, src, sz);
379 		*o = sz + (caddr_t)*o;
380 		*l -= sz;
381 	}
382 
383 	return(sz);
384 }
385