sysctl.c revision 1.2
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 netname[] = CTL_NET_NAMES;
80struct ctlname hwname[] = CTL_HW_NAMES;
81struct ctlname username[] = CTL_USER_NAMES;
82struct ctlname debugname[CTL_DEBUG_MAXID];
83#ifdef CTL_MACHDEP_NAMES
84struct ctlname machdepname[] = CTL_MACHDEP_NAMES;
85#endif
86struct ctlname ddbname[] = CTL_DDB_NAMES;
87char names[BUFSIZ];
88
89struct list {
90	struct	ctlname *list;
91	int	size;
92};
93struct list toplist = { topname, CTL_MAXID };
94struct list secondlevel[] = {
95	{ 0, 0 },			/* CTL_UNSPEC */
96	{ kernname, KERN_MAXID },	/* CTL_KERN */
97	{ vmname, VM_MAXID },		/* CTL_VM */
98	{ 0, 0 },			/* CTL_FS */
99	{ netname, NET_MAXID },		/* CTL_NET */
100	{ 0, CTL_DEBUG_MAXID },		/* CTL_DEBUG */
101	{ hwname, HW_MAXID },		/* CTL_HW */
102#ifdef CTL_MACHDEP_NAMES
103	{ machdepname, CPU_MAXID },	/* CTL_MACHDEP */
104#else
105	{ 0, 0 },			/* CTL_MACHDEP */
106#endif
107	{ username, USER_MAXID },	/* CTL_USER_NAMES */
108	{ ddbname, DBCTL_MAXID },	/* CTL_DDB_NAMES */
109};
110
111int	Aflag, aflag, nflag, wflag;
112
113/*
114 * Variables requiring special processing.
115 */
116#define	CLOCK		0x00000001
117#define	BOOTTIME	0x00000002
118#define	CONSDEV		0x00000004
119
120int
121main(argc, argv)
122	int argc;
123	char *argv[];
124{
125	extern char *optarg;
126	extern int optind;
127	int ch, lvl1;
128
129	while ((ch = getopt(argc, argv, "Aanw")) != EOF) {
130		switch (ch) {
131
132		case 'A':
133			Aflag = 1;
134			break;
135
136		case 'a':
137			aflag = 1;
138			break;
139
140		case 'n':
141			nflag = 1;
142			break;
143
144		case 'w':
145			wflag = 1;
146			break;
147
148		default:
149			usage();
150		}
151	}
152	argc -= optind;
153	argv += optind;
154
155	if (Aflag || aflag) {
156		debuginit();
157		for (lvl1 = 1; lvl1 < CTL_MAXID; lvl1++)
158			listall(topname[lvl1].ctl_name, &secondlevel[lvl1]);
159		exit(0);
160	}
161	if (argc == 0)
162		usage();
163	while (argc-- > 0)
164		parse(*argv++, 1);
165	exit(0);
166}
167
168/*
169 * List all variables known to the system.
170 */
171listall(prefix, lp)
172	char *prefix;
173	struct list *lp;
174{
175	int lvl2;
176	char *cp, name[BUFSIZ];
177
178	if (lp->list == 0)
179		return;
180	strcpy(name, prefix);
181	cp = &name[strlen(name)];
182	*cp++ = '.';
183	for (lvl2 = 0; lvl2 < lp->size; lvl2++) {
184		if (lp->list[lvl2].ctl_name == 0)
185			continue;
186		strcpy(cp, lp->list[lvl2].ctl_name);
187		parse(name, Aflag);
188	}
189}
190
191/*
192 * Parse a name into a MIB entry.
193 * Lookup and print out the MIB entry if it exists.
194 * Set a new value if requested.
195 */
196parse(string, flags)
197	char *string;
198	int flags;
199{
200	int indx, type, state, len;
201	int special = 0;
202	void *newval = 0;
203	int intval, newsize = 0;
204	quad_t quadval;
205	size_t size;
206	struct list *lp;
207	int mib[CTL_MAXNAME];
208	char *cp, *bufp, buf[BUFSIZ], strval[BUFSIZ];
209
210	bufp = buf;
211	snprintf(buf, BUFSIZ, "%s", string);
212	if ((cp = strchr(string, '=')) != NULL) {
213		if (!wflag) {
214			fprintf(stderr, "Must specify -w to set variables\n");
215			exit(2);
216		}
217		*strchr(buf, '=') = '\0';
218		*cp++ = '\0';
219		while (isspace(*cp))
220			cp++;
221		newval = cp;
222		newsize = strlen(cp);
223	}
224	if ((indx = findname(string, "top", &bufp, &toplist)) == -1)
225		return;
226	mib[0] = indx;
227	if (indx == CTL_DEBUG)
228		debuginit();
229	lp = &secondlevel[indx];
230	if (lp->list == 0) {
231		fprintf(stderr, "%s: class is not implemented\n",
232		    topname[indx]);
233		return;
234	}
235	if (bufp == NULL) {
236		listall(topname[indx].ctl_name, lp);
237		return;
238	}
239	if ((indx = findname(string, "second", &bufp, lp)) == -1)
240		return;
241	mib[1] = indx;
242	type = lp->list[indx].ctl_type;
243	len = 2;
244	switch (mib[0]) {
245
246	case CTL_KERN:
247		switch (mib[1]) {
248		case KERN_PROF:
249			mib[2] = GPROF_STATE;
250			size = sizeof state;
251			if (sysctl(mib, 3, &state, &size, NULL, 0) < 0) {
252				if (flags == 0)
253					return;
254				if (!nflag)
255					fprintf(stdout, "%s: ", string);
256				fprintf(stderr,
257				    "kernel is not compiled for profiling\n");
258				return;
259			}
260			if (!nflag)
261				fprintf(stdout, "%s: %s\n", string,
262				    state == GMON_PROF_OFF ? "off" : "running");
263			return;
264		case KERN_VNODE:
265		case KERN_FILE:
266			if (flags == 0)
267				return;
268			fprintf(stderr,
269			    "Use pstat to view %s information\n", string);
270			return;
271		case KERN_PROC:
272			if (flags == 0)
273				return;
274			fprintf(stderr,
275			    "Use ps to view %s information\n", string);
276			return;
277		case KERN_CLOCKRATE:
278			special |= CLOCK;
279			break;
280		case KERN_BOOTTIME:
281			special |= BOOTTIME;
282			break;
283		}
284		break;
285
286	case CTL_HW:
287		break;
288
289	case CTL_VM:
290		if (mib[1] == VM_LOADAVG) {
291			double loads[3];
292
293			getloadavg(loads, 3);
294			if (!nflag)
295				fprintf(stdout, "%s: ", string);
296			fprintf(stdout, "%.2f %.2f %.2f\n",
297			    loads[0], loads[1], loads[2]);
298			return;
299		}
300		if (flags == 0)
301			return;
302		fprintf(stderr,
303		    "Use vmstat or systat to view %s information\n", string);
304		return;
305
306	case CTL_NET:
307		if (mib[1] == PF_INET) {
308			len = sysctl_inet(string, &bufp, mib, flags, &type);
309			if (len >= 0)
310				break;
311			return;
312		}
313		if (flags == 0)
314			return;
315		fprintf(stderr, "Use netstat to view %s information\n", string);
316		return;
317
318	case CTL_DEBUG:
319		mib[2] = CTL_DEBUG_VALUE;
320		len = 3;
321		break;
322
323	case CTL_MACHDEP:
324#ifdef CPU_CONSDEV
325		if (mib[1] == CPU_CONSDEV)
326			special |= CONSDEV;
327#endif
328		break;
329
330	case CTL_FS:
331	case CTL_USER:
332	case CTL_DDB:
333		break;
334
335	default:
336		fprintf(stderr, "Illegal top level value: %d\n", mib[0]);
337		return;
338
339	}
340	if (bufp) {
341		fprintf(stderr, "name %s in %s is unknown\n", bufp, string);
342		return;
343	}
344	if (newsize > 0) {
345		switch (type) {
346		case CTLTYPE_INT:
347			intval = atoi(newval);
348			newval = &intval;
349			newsize = sizeof intval;
350			break;
351
352		case CTLTYPE_QUAD:
353			sscanf(newval, "%qd", &quadval);
354			newval = &quadval;
355			newsize = sizeof quadval;
356			break;
357		}
358	}
359	size = BUFSIZ;
360	if (sysctl(mib, len, buf, &size, newsize ? newval : 0, newsize) == -1) {
361		if (flags == 0)
362			return;
363		switch (errno) {
364		case EOPNOTSUPP:
365			fprintf(stderr, "%s: value is not available\n", string);
366			return;
367		case ENOTDIR:
368			fprintf(stderr, "%s: specification is incomplete\n",
369			    string);
370			return;
371		case ENOMEM:
372			fprintf(stderr, "%s: type is unknown to this program\n",
373			    string);
374			return;
375		default:
376			perror(string);
377			return;
378		}
379	}
380	if (special & CLOCK) {
381		struct clockinfo *clkp = (struct clockinfo *)buf;
382
383		if (!nflag)
384			fprintf(stdout, "%s: ", string);
385		fprintf(stdout,
386		    "tick = %d, tickadj = %d, hz = %d, profhz = %d, stathz = %d\n",
387		    clkp->tick, clkp->tickadj, clkp->hz, clkp->profhz, clkp->stathz);
388		return;
389	}
390	if (special & BOOTTIME) {
391		struct timeval *btp = (struct timeval *)buf;
392		time_t boottime;
393
394		if (!nflag) {
395			boottime = btp->tv_sec;
396			fprintf(stdout, "%s = %s\n", string, ctime(&boottime));
397		} else
398			fprintf(stdout, "%d\n", btp->tv_sec);
399		return;
400	}
401	if (special & CONSDEV) {
402		dev_t dev = *(dev_t *)buf;
403
404		if (!nflag)
405			fprintf(stdout, "%s = %s\n", string,
406			    devname(dev, S_IFCHR));
407		else
408			fprintf(stdout, "0x%x\n", dev);
409		return;
410	}
411	switch (type) {
412	case CTLTYPE_INT:
413		if (newsize == 0) {
414			if (!nflag)
415				fprintf(stdout, "%s = ", string);
416			fprintf(stdout, "%d\n", *(int *)buf);
417		} else {
418			if (!nflag)
419				fprintf(stdout, "%s: %d -> ", string,
420				    *(int *)buf);
421			fprintf(stdout, "%d\n", *(int *)newval);
422		}
423		return;
424
425	case CTLTYPE_STRING:
426		if (newsize == 0) {
427			if (!nflag)
428				fprintf(stdout, "%s = ", string);
429			fprintf(stdout, "%s\n", buf);
430		} else {
431			if (!nflag)
432				fprintf(stdout, "%s: %s -> ", string, buf);
433			fprintf(stdout, "%s\n", newval);
434		}
435		return;
436
437	case CTLTYPE_QUAD:
438		if (newsize == 0) {
439			if (!nflag)
440				fprintf(stdout, "%s = ", string);
441			fprintf(stdout, "%qd\n", *(quad_t *)buf);
442		} else {
443			if (!nflag)
444				fprintf(stdout, "%s: %qd -> ", string,
445				    *(quad_t *)buf);
446			fprintf(stdout, "%qd\n", *(quad_t *)newval);
447		}
448		return;
449
450	case CTLTYPE_STRUCT:
451		fprintf(stderr, "%s: unknown structure returned\n",
452		    string);
453		return;
454
455	default:
456	case CTLTYPE_NODE:
457		fprintf(stderr, "%s: unknown type returned\n",
458		    string);
459		return;
460	}
461}
462
463/*
464 * Initialize the set of debugging names
465 */
466debuginit()
467{
468	int mib[3], loc, i;
469	size_t size;
470
471	if (secondlevel[CTL_DEBUG].list != 0)
472		return;
473	secondlevel[CTL_DEBUG].list = debugname;
474	mib[0] = CTL_DEBUG;
475	mib[2] = CTL_DEBUG_NAME;
476	for (loc = 0, i = 0; i < CTL_DEBUG_MAXID; i++) {
477		mib[1] = i;
478		size = BUFSIZ - loc;
479		if (sysctl(mib, 3, &names[loc], &size, NULL, 0) == -1)
480			continue;
481		debugname[i].ctl_name = &names[loc];
482		debugname[i].ctl_type = CTLTYPE_INT;
483		loc += size;
484	}
485}
486
487struct ctlname inetname[] = CTL_IPPROTO_NAMES;
488struct ctlname ipname[] = IPCTL_NAMES;
489struct ctlname icmpname[] = ICMPCTL_NAMES;
490struct ctlname tcpname[] = TCPCTL_NAMES;
491struct ctlname udpname[] = UDPCTL_NAMES;
492struct list inetlist = { inetname, IPPROTO_MAXID };
493struct list inetvars[] = {
494	{ ipname, IPCTL_MAXID },	/* ip */
495	{ icmpname, ICMPCTL_MAXID },	/* icmp */
496	{ 0, 0 },			/* igmp */
497	{ 0, 0 },			/* ggmp */
498	{ 0, 0 },
499	{ 0, 0 },
500	{ tcpname, TCPCTL_MAXID },	/* tcp */
501	{ 0, 0 },
502	{ 0, 0 },			/* egp */
503	{ 0, 0 },
504	{ 0, 0 },
505	{ 0, 0 },
506	{ 0, 0 },			/* pup */
507	{ 0, 0 },
508	{ 0, 0 },
509	{ 0, 0 },
510	{ 0, 0 },
511	{ udpname, UDPCTL_MAXID },	/* udp */
512};
513
514/*
515 * handle internet requests
516 */
517sysctl_inet(string, bufpp, mib, flags, typep)
518	char *string;
519	char **bufpp;
520	int mib[];
521	int flags;
522	int *typep;
523{
524	struct list *lp;
525	int indx;
526
527	if (*bufpp == NULL) {
528		listall(string, &inetlist);
529		return (-1);
530	}
531	if ((indx = findname(string, "third", bufpp, &inetlist)) == -1)
532		return (-1);
533	mib[2] = indx;
534	if (indx <= IPPROTO_UDP && inetvars[indx].list != NULL)
535		lp = &inetvars[indx];
536	else if (!flags)
537		return (-1);
538	else {
539		fprintf(stderr, "%s: no variables defined for this protocol\n",
540		    string);
541		return (-1);
542	}
543	if (*bufpp == NULL) {
544		listall(string, lp);
545		return (-1);
546	}
547	if ((indx = findname(string, "fourth", bufpp, lp)) == -1)
548		return (-1);
549	mib[3] = indx;
550	*typep = lp->list[indx].ctl_type;
551	return (4);
552}
553
554/*
555 * Scan a list of names searching for a particular name.
556 */
557findname(string, level, bufp, namelist)
558	char *string;
559	char *level;
560	char **bufp;
561	struct list *namelist;
562{
563	char *name;
564	int i;
565
566	if (namelist->list == 0 || (name = strsep(bufp, ".")) == NULL) {
567		fprintf(stderr, "%s: incomplete specification\n", string);
568		return (-1);
569	}
570	for (i = 0; i < namelist->size; i++)
571		if (namelist->list[i].ctl_name != NULL &&
572		    strcmp(name, namelist->list[i].ctl_name) == 0)
573			break;
574	if (i == namelist->size) {
575		fprintf(stderr, "%s level name %s in %s is invalid\n",
576		    level, name, string);
577		return (-1);
578	}
579	return (i);
580}
581
582usage()
583{
584
585	(void)fprintf(stderr, "usage:\t%s\n\t%s\n\t%s\n\t%s\n",
586	    "sysctl [-n] variable ...", "sysctl [-n] -w variable=value ...",
587	    "sysctl [-n] -a", "sysctl [-n] -A");
588	exit(1);
589}
590