sysctl.c revision 1.6
1/*	$OpenBSD: sysctl.c,v 1.6 1997/01/16 03:57:31 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.6 1997/01/16 03:57:31 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		} else if (mib[1] == VM_PSSTRINGS) {
325			struct _ps_strings _ps;
326
327			len = sizeof(_ps);
328			sysctl(mib, 2, &_ps, &len, NULL, 0);
329			if (!nflag)
330				fprintf(stdout, "%s: ", string);
331			fprintf(stdout, "%ld\n", _ps.val);
332			return;
333		}
334		if (flags == 0)
335			return;
336		fprintf(stderr,
337		    "Use vmstat or systat to view %s information\n", string);
338		return;
339
340	case CTL_NET:
341		if (mib[1] == PF_INET) {
342			len = sysctl_inet(string, &bufp, mib, flags, &type);
343			if (len >= 0)
344				break;
345			return;
346		}
347		if (mib[1] == PF_IPX) {
348			len = sysctl_ipx(string, &bufp, mib, flags, &type);
349			if (len >= 0)
350				break;
351			return;
352		}
353		if (flags == 0)
354			return;
355		fprintf(stderr, "Use netstat to view %s information\n", string);
356		return;
357
358	case CTL_DEBUG:
359		mib[2] = CTL_DEBUG_VALUE;
360		len = 3;
361		break;
362
363	case CTL_MACHDEP:
364#ifdef CPU_CONSDEV
365		if (mib[1] == CPU_CONSDEV)
366			special |= CONSDEV;
367#endif
368		break;
369
370	case CTL_FS:
371		len = sysctl_fs(string, &bufp, mib, flags, &type);
372		if (len >= 0)
373			break;
374		return;
375
376	case CTL_USER:
377	case CTL_DDB:
378		break;
379
380	default:
381		fprintf(stderr, "Illegal top level value: %d\n", mib[0]);
382		return;
383
384	}
385	if (bufp) {
386		fprintf(stderr, "name %s in %s is unknown\n", bufp, string);
387		return;
388	}
389	if (newsize > 0) {
390		switch (type) {
391		case CTLTYPE_INT:
392			intval = atoi(newval);
393			newval = &intval;
394			newsize = sizeof intval;
395			break;
396
397		case CTLTYPE_QUAD:
398			sscanf(newval, "%qd", &quadval);
399			newval = &quadval;
400			newsize = sizeof quadval;
401			break;
402		}
403	}
404	size = BUFSIZ;
405	if (sysctl(mib, len, buf, &size, newsize ? newval : 0, newsize) == -1) {
406		if (flags == 0)
407			return;
408		switch (errno) {
409		case EOPNOTSUPP:
410			fprintf(stderr, "%s: value is not available\n", string);
411			return;
412		case ENOTDIR:
413			fprintf(stderr, "%s: specification is incomplete\n",
414			    string);
415			return;
416		case ENOMEM:
417			fprintf(stderr, "%s: type is unknown to this program\n",
418			    string);
419			return;
420		default:
421			perror(string);
422			return;
423		}
424	}
425	if (special & CLOCK) {
426		struct clockinfo *clkp = (struct clockinfo *)buf;
427
428		if (!nflag)
429			fprintf(stdout, "%s: ", string);
430		fprintf(stdout,
431		    "tick = %d, tickadj = %d, hz = %d, profhz = %d, stathz = %d\n",
432		    clkp->tick, clkp->tickadj, clkp->hz, clkp->profhz, clkp->stathz);
433		return;
434	}
435	if (special & BOOTTIME) {
436		struct timeval *btp = (struct timeval *)buf;
437		time_t boottime;
438
439		if (!nflag) {
440			boottime = btp->tv_sec;
441			fprintf(stdout, "%s = %s\n", string, ctime(&boottime));
442		} else
443			fprintf(stdout, "%ld\n", btp->tv_sec);
444		return;
445	}
446	if (special & CONSDEV) {
447		dev_t dev = *(dev_t *)buf;
448
449		if (!nflag)
450			fprintf(stdout, "%s = %s\n", string,
451			    devname(dev, S_IFCHR));
452		else
453			fprintf(stdout, "0x%x\n", dev);
454		return;
455	}
456	switch (type) {
457	case CTLTYPE_INT:
458		if (newsize == 0) {
459			if (!nflag)
460				fprintf(stdout, "%s = ", string);
461			fprintf(stdout, "%d\n", *(int *)buf);
462		} else {
463			if (!nflag)
464				fprintf(stdout, "%s: %d -> ", string,
465				    *(int *)buf);
466			fprintf(stdout, "%d\n", *(int *)newval);
467		}
468		return;
469
470	case CTLTYPE_STRING:
471		if (newsize == 0) {
472			if (!nflag)
473				fprintf(stdout, "%s = ", string);
474			fprintf(stdout, "%s\n", buf);
475		} else {
476			if (!nflag)
477				fprintf(stdout, "%s: %s -> ", string, buf);
478			fprintf(stdout, "%s\n", (char *)newval);
479		}
480		return;
481
482	case CTLTYPE_QUAD:
483		if (newsize == 0) {
484			if (!nflag)
485				fprintf(stdout, "%s = ", string);
486			fprintf(stdout, "%qd\n", *(quad_t *)buf);
487		} else {
488			if (!nflag)
489				fprintf(stdout, "%s: %qd -> ", string,
490				    *(quad_t *)buf);
491			fprintf(stdout, "%qd\n", *(quad_t *)newval);
492		}
493		return;
494
495	case CTLTYPE_STRUCT:
496		fprintf(stderr, "%s: unknown structure returned\n",
497		    string);
498		return;
499
500	default:
501	case CTLTYPE_NODE:
502		fprintf(stderr, "%s: unknown type returned\n",
503		    string);
504		return;
505	}
506}
507
508/*
509 * Initialize the set of debugging names
510 */
511void
512debuginit()
513{
514	int mib[3], loc, i;
515	size_t size;
516
517	if (secondlevel[CTL_DEBUG].list != 0)
518		return;
519	secondlevel[CTL_DEBUG].list = debugname;
520	mib[0] = CTL_DEBUG;
521	mib[2] = CTL_DEBUG_NAME;
522	for (loc = 0, i = 0; i < CTL_DEBUG_MAXID; i++) {
523		mib[1] = i;
524		size = BUFSIZ - loc;
525		if (sysctl(mib, 3, &names[loc], &size, NULL, 0) == -1)
526			continue;
527		debugname[i].ctl_name = &names[loc];
528		debugname[i].ctl_type = CTLTYPE_INT;
529		loc += size;
530	}
531}
532
533struct ctlname posixname[] = CTL_FS_POSIX_NAMES;
534struct list fslist = { posixname, FS_POSIX_MAXID };
535
536/*
537 * handle file system requests
538 */
539int
540sysctl_fs(string, bufpp, mib, flags, typep)
541	char *string;
542	char **bufpp;
543	int mib[];
544	int flags;
545	int *typep;
546{
547	int indx;
548
549	if (*bufpp == NULL) {
550		listall(string, &fslist);
551		return (-1);
552	}
553	if ((indx = findname(string, "third", bufpp, &fslist)) == -1)
554		return (-1);
555	mib[2] = indx;
556	*typep = fslist.list[indx].ctl_type;
557	return (3);
558}
559
560struct ctlname inetname[] = CTL_IPPROTO_NAMES;
561struct ctlname ipname[] = IPCTL_NAMES;
562struct ctlname icmpname[] = ICMPCTL_NAMES;
563struct ctlname tcpname[] = TCPCTL_NAMES;
564struct ctlname udpname[] = UDPCTL_NAMES;
565struct list inetlist = { inetname, IPPROTO_MAXID };
566struct list inetvars[] = {
567	{ ipname, IPCTL_MAXID },	/* ip */
568	{ icmpname, ICMPCTL_MAXID },	/* icmp */
569	{ 0, 0 },			/* igmp */
570	{ 0, 0 },			/* ggmp */
571	{ 0, 0 },
572	{ 0, 0 },
573	{ tcpname, TCPCTL_MAXID },	/* tcp */
574	{ 0, 0 },
575	{ 0, 0 },			/* egp */
576	{ 0, 0 },
577	{ 0, 0 },
578	{ 0, 0 },
579	{ 0, 0 },			/* pup */
580	{ 0, 0 },
581	{ 0, 0 },
582	{ 0, 0 },
583	{ 0, 0 },
584	{ udpname, UDPCTL_MAXID },	/* udp */
585};
586
587/*
588 * handle internet requests
589 */
590int
591sysctl_inet(string, bufpp, mib, flags, typep)
592	char *string;
593	char **bufpp;
594	int mib[];
595	int flags;
596	int *typep;
597{
598	struct list *lp;
599	int indx;
600
601	if (*bufpp == NULL) {
602		listall(string, &inetlist);
603		return (-1);
604	}
605	if ((indx = findname(string, "third", bufpp, &inetlist)) == -1)
606		return (-1);
607	mib[2] = indx;
608	if (indx <= IPPROTO_UDP && inetvars[indx].list != NULL)
609		lp = &inetvars[indx];
610	else if (!flags)
611		return (-1);
612	else {
613		fprintf(stderr, "%s: no variables defined for this protocol\n",
614		    string);
615		return (-1);
616	}
617	if (*bufpp == NULL) {
618		listall(string, lp);
619		return (-1);
620	}
621	if ((indx = findname(string, "fourth", bufpp, lp)) == -1)
622		return (-1);
623	mib[3] = indx;
624	*typep = lp->list[indx].ctl_type;
625	return (4);
626}
627
628struct ctlname ipxname[] = CTL_IPXPROTO_NAMES;
629struct ctlname ipxpname[] = IPXCTL_NAMES;
630struct ctlname spxpname[] = SPXCTL_NAMES;
631struct list ipxlist = { ipxname, IPXCTL_MAXID };
632struct list ipxvars[] = {
633	{ ipxpname, IPXCTL_MAXID },	/* ipx */
634	{ 0, 0 },
635	{ 0, 0 },
636	{ 0, 0 },
637	{ 0, 0 },
638	{ spxpname, SPXCTL_MAXID },
639};
640
641/*
642 * handle internet requests
643 */
644int
645sysctl_ipx(string, bufpp, mib, flags, typep)
646	char *string;
647	char **bufpp;
648	int mib[];
649	int flags;
650	int *typep;
651{
652	struct list *lp;
653	int indx;
654
655	if (*bufpp == NULL) {
656		listall(string, &ipxlist);
657		return (-1);
658	}
659	if ((indx = findname(string, "third", bufpp, &ipxlist)) == -1)
660		return (-1);
661	mib[2] = indx;
662	if (indx <= IPXPROTO_SPX && ipxvars[indx].list != NULL)
663		lp = &ipxvars[indx];
664	else if (!flags)
665		return (-1);
666	else {
667		fprintf(stderr, "%s: no variables defined for this protocol\n",
668		    string);
669		return (-1);
670	}
671	if (*bufpp == NULL) {
672		listall(string, lp);
673		return (-1);
674	}
675	if ((indx = findname(string, "fourth", bufpp, lp)) == -1)
676		return (-1);
677	mib[3] = indx;
678	*typep = lp->list[indx].ctl_type;
679	return (4);
680}
681
682/*
683 * Scan a list of names searching for a particular name.
684 */
685int
686findname(string, level, bufp, namelist)
687	char *string;
688	char *level;
689	char **bufp;
690	struct list *namelist;
691{
692	char *name;
693	int i;
694
695	if (namelist->list == 0 || (name = strsep(bufp, ".")) == NULL) {
696		fprintf(stderr, "%s: incomplete specification\n", string);
697		return (-1);
698	}
699	for (i = 0; i < namelist->size; i++)
700		if (namelist->list[i].ctl_name != NULL &&
701		    strcmp(name, namelist->list[i].ctl_name) == 0)
702			break;
703	if (i == namelist->size) {
704		fprintf(stderr, "%s level name %s in %s is invalid\n",
705		    level, name, string);
706		return (-1);
707	}
708	return (i);
709}
710
711void
712usage()
713{
714
715	(void)fprintf(stderr, "usage:\t%s\n\t%s\n\t%s\n\t%s\n",
716	    "sysctl [-n] variable ...", "sysctl [-n] -w variable=value ...",
717	    "sysctl [-n] -a", "sysctl [-n] -A");
718	exit(1);
719}
720