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