sysctl.c revision 1.3
1/*	$NetBSD: sysctl.c,v 1.9 1995/09/30 07:12:50 thorpej 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. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *	This product includes software developed by the University of
18 *	California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#ifndef lint
37static char copyright[] =
38"@(#) Copyright (c) 1993\n\
39	The Regents of the University of California.  All rights reserved.\n";
40#endif /* not lint */
41
42#ifndef lint
43#if 0
44static char sccsid[] = "@(#)sysctl.c	8.1 (Berkeley) 6/6/93";
45#else
46static char *rcsid = "$NetBSD: sysctl.c,v 1.9 1995/09/30 07:12:50 thorpej Exp $";
47#endif
48#endif /* not lint */
49
50#include <sys/param.h>
51#include <sys/gmon.h>
52#include <sys/stat.h>
53#include <sys/sysctl.h>
54#include <sys/socket.h>
55#include <vm/vm_param.h>
56#include <machine/cpu.h>
57
58#include <netinet/in.h>
59#include <netinet/in_systm.h>
60#include <netinet/ip.h>
61#include <netinet/ip_icmp.h>
62#include <netinet/icmp_var.h>
63#include <netinet/ip_var.h>
64#include <netinet/udp.h>
65#include <netinet/udp_var.h>
66#include <netinet/tcp.h>
67#include <netinet/tcp_timer.h>
68#include <netinet/tcp_var.h>
69#include <ddb/db_var.h>
70
71#include <errno.h>
72#include <stdio.h>
73#include <stdlib.h>
74#include <string.h>
75
76struct ctlname topname[] = CTL_NAMES;
77struct ctlname kernname[] = CTL_KERN_NAMES;
78struct ctlname vmname[] = CTL_VM_NAMES;
79struct ctlname fsname[] = CTL_FS_NAMES;
80struct ctlname netname[] = CTL_NET_NAMES;
81struct ctlname hwname[] = CTL_HW_NAMES;
82struct ctlname username[] = CTL_USER_NAMES;
83struct ctlname debugname[CTL_DEBUG_MAXID];
84#ifdef CTL_MACHDEP_NAMES
85struct ctlname machdepname[] = CTL_MACHDEP_NAMES;
86#endif
87struct ctlname ddbname[] = CTL_DDB_NAMES;
88char names[BUFSIZ];
89
90struct list {
91	struct	ctlname *list;
92	int	size;
93};
94struct list toplist = { topname, CTL_MAXID };
95struct list secondlevel[] = {
96	{ 0, 0 },			/* CTL_UNSPEC */
97	{ kernname, KERN_MAXID },	/* CTL_KERN */
98	{ vmname, VM_MAXID },		/* CTL_VM */
99	{ fsname, FS_MAXID },		/* CTL_FS */
100	{ netname, NET_MAXID },		/* CTL_NET */
101	{ 0, CTL_DEBUG_MAXID },		/* CTL_DEBUG */
102	{ hwname, HW_MAXID },		/* CTL_HW */
103#ifdef CTL_MACHDEP_NAMES
104	{ machdepname, CPU_MAXID },	/* CTL_MACHDEP */
105#else
106	{ 0, 0 },			/* CTL_MACHDEP */
107#endif
108	{ username, USER_MAXID },	/* CTL_USER_NAMES */
109	{ ddbname, DBCTL_MAXID },	/* CTL_DDB_NAMES */
110};
111
112int	Aflag, aflag, nflag, wflag;
113
114/*
115 * Variables requiring special processing.
116 */
117#define	CLOCK		0x00000001
118#define	BOOTTIME	0x00000002
119#define	CONSDEV		0x00000004
120
121int
122main(argc, argv)
123	int argc;
124	char *argv[];
125{
126	extern char *optarg;
127	extern int optind;
128	int ch, lvl1;
129
130	while ((ch = getopt(argc, argv, "Aanw")) != EOF) {
131		switch (ch) {
132
133		case 'A':
134			Aflag = 1;
135			break;
136
137		case 'a':
138			aflag = 1;
139			break;
140
141		case 'n':
142			nflag = 1;
143			break;
144
145		case 'w':
146			wflag = 1;
147			break;
148
149		default:
150			usage();
151		}
152	}
153	argc -= optind;
154	argv += optind;
155
156	if (Aflag || aflag) {
157		debuginit();
158		for (lvl1 = 1; lvl1 < CTL_MAXID; lvl1++)
159			listall(topname[lvl1].ctl_name, &secondlevel[lvl1]);
160		exit(0);
161	}
162	if (argc == 0)
163		usage();
164	while (argc-- > 0)
165		parse(*argv++, 1);
166	exit(0);
167}
168
169/*
170 * List all variables known to the system.
171 */
172listall(prefix, lp)
173	char *prefix;
174	struct list *lp;
175{
176	int lvl2;
177	char *cp, name[BUFSIZ];
178
179	if (lp->list == 0)
180		return;
181	strcpy(name, prefix);
182	cp = &name[strlen(name)];
183	*cp++ = '.';
184	for (lvl2 = 0; lvl2 < lp->size; lvl2++) {
185		if (lp->list[lvl2].ctl_name == 0)
186			continue;
187		strcpy(cp, lp->list[lvl2].ctl_name);
188		parse(name, Aflag);
189	}
190}
191
192/*
193 * Parse a name into a MIB entry.
194 * Lookup and print out the MIB entry if it exists.
195 * Set a new value if requested.
196 */
197parse(string, flags)
198	char *string;
199	int flags;
200{
201	int indx, type, state, len;
202	int special = 0;
203	void *newval = 0;
204	int intval, newsize = 0;
205	quad_t quadval;
206	size_t size;
207	struct list *lp;
208	int mib[CTL_MAXNAME];
209	char *cp, *bufp, buf[BUFSIZ], strval[BUFSIZ];
210
211	bufp = buf;
212	snprintf(buf, BUFSIZ, "%s", string);
213	if ((cp = strchr(string, '=')) != NULL) {
214		if (!wflag) {
215			fprintf(stderr, "Must specify -w to set variables\n");
216			exit(2);
217		}
218		*strchr(buf, '=') = '\0';
219		*cp++ = '\0';
220		while (isspace(*cp))
221			cp++;
222		newval = cp;
223		newsize = strlen(cp);
224	}
225	if ((indx = findname(string, "top", &bufp, &toplist)) == -1)
226		return;
227	mib[0] = indx;
228	if (indx == CTL_DEBUG)
229		debuginit();
230	lp = &secondlevel[indx];
231	if (lp->list == 0) {
232		fprintf(stderr, "%s: class is not implemented\n",
233		    topname[indx]);
234		return;
235	}
236	if (bufp == NULL) {
237		listall(topname[indx].ctl_name, lp);
238		return;
239	}
240	if ((indx = findname(string, "second", &bufp, lp)) == -1)
241		return;
242	mib[1] = indx;
243	type = lp->list[indx].ctl_type;
244	len = 2;
245	switch (mib[0]) {
246
247	case CTL_KERN:
248		switch (mib[1]) {
249		case KERN_PROF:
250			mib[2] = GPROF_STATE;
251			size = sizeof state;
252			if (sysctl(mib, 3, &state, &size, NULL, 0) < 0) {
253				if (flags == 0)
254					return;
255				if (!nflag)
256					fprintf(stdout, "%s: ", string);
257				fprintf(stderr,
258				    "kernel is not compiled for profiling\n");
259				return;
260			}
261			if (!nflag)
262				fprintf(stdout, "%s: %s\n", string,
263				    state == GMON_PROF_OFF ? "off" : "running");
264			return;
265		case KERN_VNODE:
266		case KERN_FILE:
267			if (flags == 0)
268				return;
269			fprintf(stderr,
270			    "Use pstat to view %s information\n", string);
271			return;
272		case KERN_PROC:
273			if (flags == 0)
274				return;
275			fprintf(stderr,
276			    "Use ps to view %s information\n", string);
277			return;
278		case KERN_NTPTIME:
279			if (flags == 0)
280				return;
281			fprintf(stderr,
282			    "Use xntpd to view %s information\n", string);
283			return;
284		case KERN_CLOCKRATE:
285			special |= CLOCK;
286			break;
287		case KERN_BOOTTIME:
288			special |= BOOTTIME;
289			break;
290		}
291		break;
292
293	case CTL_HW:
294		break;
295
296	case CTL_VM:
297		if (mib[1] == VM_LOADAVG) {
298			double loads[3];
299
300			getloadavg(loads, 3);
301			if (!nflag)
302				fprintf(stdout, "%s: ", string);
303			fprintf(stdout, "%.2f %.2f %.2f\n",
304			    loads[0], loads[1], loads[2]);
305			return;
306		}
307		if (flags == 0)
308			return;
309		fprintf(stderr,
310		    "Use vmstat or systat to view %s information\n", string);
311		return;
312
313	case CTL_NET:
314		if (mib[1] == PF_INET) {
315			len = sysctl_inet(string, &bufp, mib, flags, &type);
316			if (len >= 0)
317				break;
318			return;
319		}
320		if (flags == 0)
321			return;
322		fprintf(stderr, "Use netstat to view %s information\n", string);
323		return;
324
325	case CTL_DEBUG:
326		mib[2] = CTL_DEBUG_VALUE;
327		len = 3;
328		break;
329
330	case CTL_MACHDEP:
331#ifdef CPU_CONSDEV
332		if (mib[1] == CPU_CONSDEV)
333			special |= CONSDEV;
334#endif
335		break;
336
337	case CTL_FS:
338		len = sysctl_fs(string, &bufp, mib, flags, &type);
339		if (len >= 0)
340			break;
341		return;
342
343	case CTL_USER:
344	case CTL_DDB:
345		break;
346
347	default:
348		fprintf(stderr, "Illegal top level value: %d\n", mib[0]);
349		return;
350
351	}
352	if (bufp) {
353		fprintf(stderr, "name %s in %s is unknown\n", bufp, string);
354		return;
355	}
356	if (newsize > 0) {
357		switch (type) {
358		case CTLTYPE_INT:
359			intval = atoi(newval);
360			newval = &intval;
361			newsize = sizeof intval;
362			break;
363
364		case CTLTYPE_QUAD:
365			sscanf(newval, "%qd", &quadval);
366			newval = &quadval;
367			newsize = sizeof quadval;
368			break;
369		}
370	}
371	size = BUFSIZ;
372	if (sysctl(mib, len, buf, &size, newsize ? newval : 0, newsize) == -1) {
373		if (flags == 0)
374			return;
375		switch (errno) {
376		case EOPNOTSUPP:
377			fprintf(stderr, "%s: value is not available\n", string);
378			return;
379		case ENOTDIR:
380			fprintf(stderr, "%s: specification is incomplete\n",
381			    string);
382			return;
383		case ENOMEM:
384			fprintf(stderr, "%s: type is unknown to this program\n",
385			    string);
386			return;
387		default:
388			perror(string);
389			return;
390		}
391	}
392	if (special & CLOCK) {
393		struct clockinfo *clkp = (struct clockinfo *)buf;
394
395		if (!nflag)
396			fprintf(stdout, "%s: ", string);
397		fprintf(stdout,
398		    "tick = %d, tickadj = %d, hz = %d, profhz = %d, stathz = %d\n",
399		    clkp->tick, clkp->tickadj, clkp->hz, clkp->profhz, clkp->stathz);
400		return;
401	}
402	if (special & BOOTTIME) {
403		struct timeval *btp = (struct timeval *)buf;
404		time_t boottime;
405
406		if (!nflag) {
407			boottime = btp->tv_sec;
408			fprintf(stdout, "%s = %s\n", string, ctime(&boottime));
409		} else
410			fprintf(stdout, "%d\n", btp->tv_sec);
411		return;
412	}
413	if (special & CONSDEV) {
414		dev_t dev = *(dev_t *)buf;
415
416		if (!nflag)
417			fprintf(stdout, "%s = %s\n", string,
418			    devname(dev, S_IFCHR));
419		else
420			fprintf(stdout, "0x%x\n", dev);
421		return;
422	}
423	switch (type) {
424	case CTLTYPE_INT:
425		if (newsize == 0) {
426			if (!nflag)
427				fprintf(stdout, "%s = ", string);
428			fprintf(stdout, "%d\n", *(int *)buf);
429		} else {
430			if (!nflag)
431				fprintf(stdout, "%s: %d -> ", string,
432				    *(int *)buf);
433			fprintf(stdout, "%d\n", *(int *)newval);
434		}
435		return;
436
437	case CTLTYPE_STRING:
438		if (newsize == 0) {
439			if (!nflag)
440				fprintf(stdout, "%s = ", string);
441			fprintf(stdout, "%s\n", buf);
442		} else {
443			if (!nflag)
444				fprintf(stdout, "%s: %s -> ", string, buf);
445			fprintf(stdout, "%s\n", newval);
446		}
447		return;
448
449	case CTLTYPE_QUAD:
450		if (newsize == 0) {
451			if (!nflag)
452				fprintf(stdout, "%s = ", string);
453			fprintf(stdout, "%qd\n", *(quad_t *)buf);
454		} else {
455			if (!nflag)
456				fprintf(stdout, "%s: %qd -> ", string,
457				    *(quad_t *)buf);
458			fprintf(stdout, "%qd\n", *(quad_t *)newval);
459		}
460		return;
461
462	case CTLTYPE_STRUCT:
463		fprintf(stderr, "%s: unknown structure returned\n",
464		    string);
465		return;
466
467	default:
468	case CTLTYPE_NODE:
469		fprintf(stderr, "%s: unknown type returned\n",
470		    string);
471		return;
472	}
473}
474
475/*
476 * Initialize the set of debugging names
477 */
478debuginit()
479{
480	int mib[3], loc, i;
481	size_t size;
482
483	if (secondlevel[CTL_DEBUG].list != 0)
484		return;
485	secondlevel[CTL_DEBUG].list = debugname;
486	mib[0] = CTL_DEBUG;
487	mib[2] = CTL_DEBUG_NAME;
488	for (loc = 0, i = 0; i < CTL_DEBUG_MAXID; i++) {
489		mib[1] = i;
490		size = BUFSIZ - loc;
491		if (sysctl(mib, 3, &names[loc], &size, NULL, 0) == -1)
492			continue;
493		debugname[i].ctl_name = &names[loc];
494		debugname[i].ctl_type = CTLTYPE_INT;
495		loc += size;
496	}
497}
498
499struct ctlname posixname[] = CTL_FS_POSIX_NAMES;
500struct list fslist = { posixname, FS_POSIX_MAXID };
501
502/*
503 * handle file system requests
504 */
505sysctl_fs(string, bufpp, mib, flags, typep)
506	char *string;
507	char **bufpp;
508	int mib[];
509	int flags;
510	int *typep;
511{
512	struct list *lp;
513	int indx;
514
515	if (*bufpp == NULL) {
516		listall(string, &fslist);
517		return (-1);
518	}
519	if ((indx = findname(string, "third", bufpp, &fslist)) == -1)
520		return (-1);
521	mib[2] = indx;
522	*typep = fslist.list[indx].ctl_type;
523	return (3);
524}
525
526struct ctlname inetname[] = CTL_IPPROTO_NAMES;
527struct ctlname ipname[] = IPCTL_NAMES;
528struct ctlname icmpname[] = ICMPCTL_NAMES;
529struct ctlname tcpname[] = TCPCTL_NAMES;
530struct ctlname udpname[] = UDPCTL_NAMES;
531struct list inetlist = { inetname, IPPROTO_MAXID };
532struct list inetvars[] = {
533	{ ipname, IPCTL_MAXID },	/* ip */
534	{ icmpname, ICMPCTL_MAXID },	/* icmp */
535	{ 0, 0 },			/* igmp */
536	{ 0, 0 },			/* ggmp */
537	{ 0, 0 },
538	{ 0, 0 },
539	{ tcpname, TCPCTL_MAXID },	/* tcp */
540	{ 0, 0 },
541	{ 0, 0 },			/* egp */
542	{ 0, 0 },
543	{ 0, 0 },
544	{ 0, 0 },
545	{ 0, 0 },			/* pup */
546	{ 0, 0 },
547	{ 0, 0 },
548	{ 0, 0 },
549	{ 0, 0 },
550	{ udpname, UDPCTL_MAXID },	/* udp */
551};
552
553/*
554 * handle internet requests
555 */
556sysctl_inet(string, bufpp, mib, flags, typep)
557	char *string;
558	char **bufpp;
559	int mib[];
560	int flags;
561	int *typep;
562{
563	struct list *lp;
564	int indx;
565
566	if (*bufpp == NULL) {
567		listall(string, &inetlist);
568		return (-1);
569	}
570	if ((indx = findname(string, "third", bufpp, &inetlist)) == -1)
571		return (-1);
572	mib[2] = indx;
573	if (indx <= IPPROTO_UDP && inetvars[indx].list != NULL)
574		lp = &inetvars[indx];
575	else if (!flags)
576		return (-1);
577	else {
578		fprintf(stderr, "%s: no variables defined for this protocol\n",
579		    string);
580		return (-1);
581	}
582	if (*bufpp == NULL) {
583		listall(string, lp);
584		return (-1);
585	}
586	if ((indx = findname(string, "fourth", bufpp, lp)) == -1)
587		return (-1);
588	mib[3] = indx;
589	*typep = lp->list[indx].ctl_type;
590	return (4);
591}
592
593/*
594 * Scan a list of names searching for a particular name.
595 */
596findname(string, level, bufp, namelist)
597	char *string;
598	char *level;
599	char **bufp;
600	struct list *namelist;
601{
602	char *name;
603	int i;
604
605	if (namelist->list == 0 || (name = strsep(bufp, ".")) == NULL) {
606		fprintf(stderr, "%s: incomplete specification\n", string);
607		return (-1);
608	}
609	for (i = 0; i < namelist->size; i++)
610		if (namelist->list[i].ctl_name != NULL &&
611		    strcmp(name, namelist->list[i].ctl_name) == 0)
612			break;
613	if (i == namelist->size) {
614		fprintf(stderr, "%s level name %s in %s is invalid\n",
615		    level, name, string);
616		return (-1);
617	}
618	return (i);
619}
620
621usage()
622{
623
624	(void)fprintf(stderr, "usage:\t%s\n\t%s\n\t%s\n\t%s\n",
625	    "sysctl [-n] variable ...", "sysctl [-n] -w variable=value ...",
626	    "sysctl [-n] -a", "sysctl [-n] -A");
627	exit(1);
628}
629