xref: /csrg-svn/sys/kern/kern_sysctl.c (revision 58128)
1 /*-
2  * Copyright (c) 1982, 1986, 1989, 1993 Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Mike Karels at Berkeley Software Design, Inc.
7  *
8  * %sccs.include.redist.c%
9  *
10  *	@(#)kern_sysctl.c	7.28 (Berkeley) 02/21/93
11  */
12 
13 /*
14  * sysctl system call.
15  */
16 
17 #include <sys/param.h>
18 #include <sys/systm.h>
19 #include <sys/malloc.h>
20 #include <sys/proc.h>
21 #include <sys/file.h>
22 #include <sys/sysctl.h>
23 #include <sys/unistd.h>
24 #include <sys/buf.h>
25 #include <sys/ioctl.h>
26 #include <sys/tty.h>
27 
28 #include <vm/vm.h>
29 
30 #include <sys/kinfo_proc.h>
31 
32 sysctlfn kern_sysctl;
33 sysctlfn hw_sysctl;
34 extern sysctlfn vm_sysctl;
35 extern sysctlfn fs_sysctl;
36 extern sysctlfn net_sysctl;
37 extern sysctlfn cpu_sysctl;
38 
39 /*
40  * Locking and stats
41  */
42 static struct sysctl_lock {
43 	int	sl_lock;
44 	int	sl_want;
45 	int	sl_locked;
46 } memlock;
47 
48 struct sysctl_args {
49 	int	*name;
50 	u_int	namelen;
51 	void	*old;
52 	u_int	*oldlenp;
53 	void	*new;
54 	u_int	newlen;
55 };
56 
57 #define	STK_PARAMS	32	/* largest old/new values on stack */
58 
59 sysctl(p, uap, retval)
60 	struct proc *p;
61 	register struct sysctl_args *uap;
62 	int *retval;
63 {
64 	int error, dolock = 1;
65 	u_int savelen, oldlen = 0;
66 	sysctlfn *fn;
67 	int name[CTL_MAXNAME];
68 
69 	if (uap->new != NULL && (error = suser(p->p_ucred, &p->p_acflag)))
70 		return (error);
71 	/*
72 	 * all top-level sysctl names are non-terminal
73 	 */
74 	if (uap->namelen > CTL_MAXNAME || uap->namelen < 2)
75 		return (EINVAL);
76 	if (error = copyin(uap->name, &name, uap->namelen * sizeof(int)))
77 		return (error);
78 
79 	switch (name[0]) {
80 	case CTL_KERN:
81 		fn = kern_sysctl;
82 		if (name[2] != KERN_VNODE)	/* XXX */
83 			dolock = 0;
84 		break;
85 	case CTL_HW:
86 		fn = hw_sysctl;
87 		break;
88 	case CTL_VM:
89 		fn = vm_sysctl;
90 		break;
91 	case CTL_NET:
92 		fn = net_sysctl;
93 		break;
94 #ifdef notyet
95 	case CTL_FS:
96 		fn = fs_sysctl;
97 		break;
98 	case CTL_DEBUG:
99 		fn = debug_sysctl;
100 		break;
101 	case CTL_MACHDEP:
102 		fn = cpu_sysctl;
103 		break;
104 #endif
105 	default:
106 		return (EOPNOTSUPP);
107 	}
108 
109 	if (uap->oldlenp &&
110 	    (error = copyin(uap->oldlenp, &oldlen, sizeof(oldlen))))
111 		return (error);
112 	if (uap->old != NULL) {
113 		if (!useracc(uap->old, oldlen, B_WRITE))
114 			return (EFAULT);
115 		while (memlock.sl_lock) {
116 			memlock.sl_want = 1;
117 			sleep((caddr_t)&memlock, PRIBIO+1);
118 			memlock.sl_locked++;
119 		}
120 		memlock.sl_lock = 1;
121 		if (dolock)
122 			vslock(uap->old, oldlen);
123 		savelen = oldlen;
124 	}
125 	error = (*fn)(name + 1, uap->namelen - 1, uap->old, &oldlen,
126 	    uap->new, uap->newlen);
127 	if (uap->old != NULL) {
128 		if (dolock)
129 			vsunlock(uap->old, savelen, B_WRITE);
130 		memlock.sl_lock = 0;
131 		if (memlock.sl_want) {
132 			memlock.sl_want = 0;
133 			wakeup((caddr_t)&memlock);
134 		}
135 	}
136 	if (error)
137 		return (error);
138 	if (uap->oldlenp)
139 		error = copyout(&oldlen, uap->oldlenp, sizeof(oldlen));
140 	*retval = oldlen;
141 	return (0);
142 }
143 
144 /*
145  * Attributes stored in the kernel.
146  */
147 char hostname[MAXHOSTNAMELEN];
148 int hostnamelen;
149 long hostid;
150 
151 kern_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
152 	int *name;
153 	u_int namelen;
154 	void *oldp;
155 	u_int *oldlenp;
156 	void *newp;
157 	u_int newlen;
158 {
159 	int error;
160 	extern char ostype[], osrelease[], version[];
161 
162 	/* all sysctl names at this level are terminal */
163 	if (namelen != 1 && name[0] != KERN_PROC)
164 		return (ENOTDIR);		/* overloaded */
165 
166 	switch (name[0]) {
167 	case KERN_OSTYPE:
168 		return (sysctl_rdstring(oldp, oldlenp, newp, ostype));
169 	case KERN_OSRELEASE:
170 		return (sysctl_rdstring(oldp, oldlenp, newp, osrelease));
171 	case KERN_OSREV:
172 		return (sysctl_rdint(oldp, oldlenp, newp, BSD));
173 	case KERN_VERSION:
174 		return (sysctl_rdstring(oldp, oldlenp, newp, version));
175 	case KERN_POSIX1:
176 		return (sysctl_rdint(oldp, oldlenp, newp, _POSIX_VERSION));
177 	case KERN_MAXPROC:
178 		return (sysctl_int(oldp, oldlenp, newp, newlen, &maxproc));
179 	case KERN_MAXFILES:
180 		return (sysctl_int(oldp, oldlenp, newp, newlen, &maxfiles));
181 	case KERN_ARGMAX:
182 		return (sysctl_rdint(oldp, oldlenp, newp, ARG_MAX));
183 	case KERN_HOSTNAME:
184 		error = sysctl_string(oldp, oldlenp, newp, newlen,
185 		    hostname, sizeof(hostname));
186 		if (!error)
187 			hostnamelen = newlen;
188 		return (error);
189 	case KERN_HOSTID:
190 		return (sysctl_int(oldp, oldlenp, newp, newlen, &hostid));
191 	case KERN_CLOCKRATE:
192 		return (sysctl_clockrate(oldp, oldlenp));
193 	case KERN_FILE:
194 		return (sysctl_file(oldp, oldlenp));
195 	case KERN_VNODE:
196 		return (sysctl_vnode(oldp, oldlenp));
197 	case KERN_PROC:
198 		return (sysctl_doproc(name + 1, namelen - 1, oldp, oldlenp));
199 	default:
200 		return (EOPNOTSUPP);
201 	}
202 	/* NOTREACHED */
203 }
204 
205 hw_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
206 	int *name;
207 	u_int namelen;
208 	void *oldp;
209 	u_int *oldlenp;
210 	void *newp;
211 	u_int newlen;
212 {
213 	extern char machine[], cpu_model[];
214 
215 	/* all sysctl names at this level are terminal */
216 	if (namelen != 1)
217 		return (ENOTDIR);		/* overloaded */
218 
219 	switch (name[0]) {
220 	case HW_MACHINE:
221 		return (sysctl_rdstring(oldp, oldlenp, newp, machine));
222 	case HW_MODEL:
223 		return (sysctl_rdstring(oldp, oldlenp, newp, cpu_model));
224 	case HW_NCPU:
225 		return (sysctl_rdint(oldp, oldlenp, newp, 1));	/* XXX */
226 	case HW_CPUSPEED:
227 		return (sysctl_rdint(oldp, oldlenp, newp, cpuspeed));
228 	case HW_PHYSMEM:
229 		return (sysctl_rdint(oldp, oldlenp, newp, ctob(physmem)));
230 	case HW_USERMEM:
231 		return (sysctl_rdint(oldp, oldlenp, newp,
232 		    ctob(physmem - cnt.v_wire_count)));
233 	case HW_PAGESIZE:
234 		return (sysctl_rdint(oldp, oldlenp, newp, PAGE_SIZE));
235 	default:
236 		return (EOPNOTSUPP);
237 	}
238 	/* NOTREACHED */
239 }
240 
241 /*
242  * Validate parameters and get old / set new parameters
243  * for an integer-valued sysctl function.
244  */
245 sysctl_int(oldp, oldlenp, newp, newlen, valp)
246 	void *oldp;
247 	u_int *oldlenp;
248 	void *newp;
249 	u_int newlen;
250 	int *valp;
251 {
252 	int error = 0;
253 
254 	if (oldp && *oldlenp < sizeof(int))
255 		return (ENOMEM);
256 	if (newp && newlen != sizeof(int))
257 		return (EINVAL);
258 	*oldlenp = sizeof(int);
259 	if (oldp)
260 		error = copyout(valp, oldp, sizeof(int));
261 	if (error == 0 && newp)
262 		error = copyin(newp, valp, sizeof(int));
263 	return (error);
264 }
265 
266 /*
267  * As above, but read-only.
268  */
269 sysctl_rdint(oldp, oldlenp, newp, val)
270 	void *oldp;
271 	u_int *oldlenp;
272 	void *newp;
273 	int val;
274 {
275 	int error = 0;
276 
277 	if (oldp && *oldlenp < sizeof(int))
278 		return (ENOMEM);
279 	if (newp)
280 		return (EPERM);
281 	*oldlenp = sizeof(int);
282 	if (oldp)
283 		error = copyout((caddr_t)&val, oldp, sizeof(int));
284 	return (error);
285 }
286 
287 /*
288  * Validate parameters and get old / set new parameters
289  * for a string-valued sysctl function.
290  */
291 sysctl_string(oldp, oldlenp, newp, newlen, str, maxlen)
292 	void *oldp;
293 	u_int *oldlenp;
294 	void *newp;
295 	u_int newlen;
296 	char *str;
297 	int maxlen;
298 {
299 	int len, error = 0;
300 
301 	len = strlen(str) + 1;
302 	if (oldp && *oldlenp < len)
303 		return (ENOMEM);
304 	if (newp && newlen >= maxlen)
305 		return (EINVAL);
306 	if (oldp) {
307 		*oldlenp = len;
308 		error = copyout(str, oldp, len);
309 	}
310 	if (error == 0 && newp) {
311 		error = copyin(newp, str, newlen);
312 		str[newlen] = 0;
313 	}
314 	return (error);
315 }
316 
317 /*
318  * As above, but read-only.
319  */
320 sysctl_rdstring(oldp, oldlenp, newp, str)
321 	void *oldp;
322 	u_int *oldlenp;
323 	void *newp;
324 	char *str;
325 {
326 	int len, error = 0;
327 
328 	len = strlen(str) + 1;
329 	if (oldp && *oldlenp < len)
330 		return (ENOMEM);
331 	if (newp)
332 		return (EPERM);
333 	*oldlenp = len;
334 	if (oldp)
335 		error = copyout(str, oldp, len);
336 	return (error);
337 }
338 
339 /*
340  * Validate parameters and get old parameters
341  * for a structure oriented sysctl function.
342  */
343 sysctl_rdstruct(oldp, oldlenp, newp, sp, len)
344 	void *oldp;
345 	u_int *oldlenp;
346 	void *newp;
347 	void *sp;
348 	int len;
349 {
350 	int error = 0;
351 
352 	if (oldp && *oldlenp < len)
353 		return (ENOMEM);
354 	if (newp)
355 		return (EPERM);
356 	*oldlenp = len;
357 	if (oldp)
358 		error = copyout(sp, oldp, len);
359 	return (error);
360 }
361 
362 /*
363  * Get file structures.
364  */
365 sysctl_file(where, sizep)
366 	char *where;
367 	int *sizep;
368 {
369 	int buflen, error;
370 	struct file *fp;
371 	char *start = where;
372 
373 	buflen = *sizep;
374 	if (where == NULL) {
375 		/*
376 		 * overestimate by 10 files
377 		 */
378 		*sizep = sizeof(filehead) + (nfiles + 10) * sizeof(struct file);
379 		return (0);
380 	}
381 
382 	/*
383 	 * first copyout filehead
384 	 */
385 	if (buflen < sizeof(filehead)) {
386 		*sizep = 0;
387 		return (0);
388 	}
389 	if (error = copyout((caddr_t)&filehead, where, sizeof(filehead)))
390 		return (error);
391 	buflen += sizeof(filehead);
392 	where += sizeof(filehead);
393 
394 	/*
395 	 * followed by an array of file structures
396 	 */
397 	for (fp = filehead; fp != NULL; fp = fp->f_filef) {
398 		if (buflen < sizeof(struct file)) {
399 			*sizep = where - start;
400 			return (ENOMEM);
401 		}
402 		if (error = copyout((caddr_t)fp, where, sizeof (struct file)))
403 			return (error);
404 		buflen -= sizeof(struct file);
405 		where += sizeof(struct file);
406 	}
407 	*sizep = where - start;
408 	return (0);
409 }
410 
411 /*
412  * try over estimating by 5 procs
413  */
414 #define KERN_PROCSLOP	(5 * sizeof (struct kinfo_proc))
415 
416 sysctl_doproc(name, namelen, where, sizep)
417 	int *name;
418 	int namelen;
419 	char *where;
420 	int *sizep;
421 {
422 	register struct proc *p;
423 	register struct kinfo_proc *dp = (struct kinfo_proc *)where;
424 	register int needed = 0;
425 	int buflen = where != NULL ? *sizep : 0;
426 	int doingzomb;
427 	struct eproc eproc;
428 	int error = 0;
429 
430 	if (namelen != 2)
431 		return (EINVAL);
432 	p = (struct proc *)allproc;
433 	doingzomb = 0;
434 again:
435 	for (; p != NULL; p = p->p_nxt) {
436 		/*
437 		 * Skip embryonic processes.
438 		 */
439 		if (p->p_stat == SIDL)
440 			continue;
441 		/*
442 		 * TODO - make more efficient (see notes below).
443 		 * do by session.
444 		 */
445 		switch (name[0]) {
446 
447 		case KERN_PROC_PID:
448 			/* could do this with just a lookup */
449 			if (p->p_pid != (pid_t)name[1])
450 				continue;
451 			break;
452 
453 		case KERN_PROC_PGRP:
454 			/* could do this by traversing pgrp */
455 			if (p->p_pgrp->pg_id != (pid_t)name[1])
456 				continue;
457 			break;
458 
459 		case KERN_PROC_TTY:
460 			if ((p->p_flag&SCTTY) == 0 ||
461 			    p->p_session->s_ttyp == NULL ||
462 			    p->p_session->s_ttyp->t_dev != (dev_t)name[1])
463 				continue;
464 			break;
465 
466 		case KERN_PROC_UID:
467 			if (p->p_ucred->cr_uid != (uid_t)name[1])
468 				continue;
469 			break;
470 
471 		case KERN_PROC_RUID:
472 			if (p->p_cred->p_ruid != (uid_t)name[1])
473 				continue;
474 			break;
475 		}
476 		if (buflen >= sizeof(struct kinfo_proc)) {
477 			fill_eproc(p, &eproc);
478 			if (error = copyout((caddr_t)p, &dp->kp_proc,
479 			    sizeof(struct proc)))
480 				return (error);
481 			if (error = copyout((caddr_t)&eproc, &dp->kp_eproc,
482 			    sizeof(eproc)))
483 				return (error);
484 			dp++;
485 			buflen -= sizeof(struct kinfo_proc);
486 		}
487 		needed += sizeof(struct kinfo_proc);
488 	}
489 	if (doingzomb == 0) {
490 		p = zombproc;
491 		doingzomb++;
492 		goto again;
493 	}
494 	if (where != NULL) {
495 		*sizep = (caddr_t)dp - where;
496 		if (needed > *sizep)
497 			return (ENOMEM);
498 	} else {
499 		needed += KERN_PROCSLOP;
500 		*sizep = needed;
501 	}
502 	return (0);
503 }
504 
505 /*
506  * Fill in an eproc structure for the specified process.
507  */
508 void
509 fill_eproc(p, ep)
510 	register struct proc *p;
511 	register struct eproc *ep;
512 {
513 	register struct tty *tp;
514 
515 	ep->e_paddr = p;
516 	ep->e_sess = p->p_pgrp->pg_session;
517 	ep->e_pcred = *p->p_cred;
518 	ep->e_ucred = *p->p_ucred;
519 	if (p->p_stat == SIDL || p->p_stat == SZOMB) {
520 		ep->e_vm.vm_rssize = 0;
521 		ep->e_vm.vm_tsize = 0;
522 		ep->e_vm.vm_dsize = 0;
523 		ep->e_vm.vm_ssize = 0;
524 #ifndef sparc
525 		/* ep->e_vm.vm_pmap = XXX; */
526 #endif
527 	} else {
528 		register struct vmspace *vm = p->p_vmspace;
529 
530 		ep->e_vm.vm_rssize = vm->vm_rssize;
531 		ep->e_vm.vm_tsize = vm->vm_tsize;
532 		ep->e_vm.vm_dsize = vm->vm_dsize;
533 		ep->e_vm.vm_ssize = vm->vm_ssize;
534 #ifndef sparc
535 		ep->e_vm.vm_pmap = vm->vm_pmap;
536 #endif
537 	}
538 	if (p->p_pptr)
539 		ep->e_ppid = p->p_pptr->p_pid;
540 	else
541 		ep->e_ppid = 0;
542 	ep->e_pgid = p->p_pgrp->pg_id;
543 	ep->e_jobc = p->p_pgrp->pg_jobc;
544 	if ((p->p_flag&SCTTY) &&
545 	     (tp = ep->e_sess->s_ttyp)) {
546 		ep->e_tdev = tp->t_dev;
547 		ep->e_tpgid = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID;
548 		ep->e_tsess = tp->t_session;
549 	} else
550 		ep->e_tdev = NODEV;
551 	ep->e_flag = ep->e_sess->s_ttyvp ? EPROC_CTTY : 0;
552 	if (SESS_LEADER(p))
553 		ep->e_flag |= EPROC_SLEADER;
554 	if (p->p_wmesg)
555 		strncpy(ep->e_wmesg, p->p_wmesg, WMESGLEN);
556 	ep->e_xsize = ep->e_xrssize = 0;
557 	ep->e_xccount = ep->e_xswrss = 0;
558 }
559 
560 #ifdef COMPAT_43
561 #include <sys/socket.h>
562 #define	KINFO_PROC		(0<<8)
563 #define	KINFO_RT		(1<<8)
564 #define	KINFO_VNODE		(2<<8)
565 #define	KINFO_FILE		(3<<8)
566 #define	KINFO_METER		(4<<8)
567 #define	KINFO_LOADAVG		(5<<8)
568 #define	KINFO_CLOCKRATE		(6<<8)
569 
570 struct getkerninfo_args {
571 	int	op;
572 	char	*where;
573 	int	*size;
574 	int	arg;
575 };
576 
577 getkerninfo(p, uap, retval)
578 	struct proc *p;
579 	register struct getkerninfo_args *uap;
580 	int *retval;
581 {
582 	int error, name[5];
583 	u_int size;
584 
585 	if (error = copyin((caddr_t)uap->size, (caddr_t)&size, sizeof(size)))
586 		return (error);
587 
588 	switch (uap->op & 0xff00) {
589 
590 	case KINFO_RT:
591 		name[0] = PF_ROUTE;
592 		name[1] = 0;
593 		name[2] = (uap->op & 0xff0000) >> 16;
594 		name[3] = uap->op & 0xff;
595 		name[4] = uap->arg;
596 		error = net_sysctl(name, 5, uap->where, &size, NULL, 0);
597 		break;
598 
599 	case KINFO_VNODE:
600 		name[0] = KERN_VNODE;
601 		error = kern_sysctl(name, 1, uap->where, &size, NULL, 0);
602 		break;
603 
604 	case KINFO_PROC:
605 		name[0] = KERN_PROC;
606 		name[1] = uap->op & 0xff;
607 		name[2] = uap->arg;
608 		error = kern_sysctl(name, 3, uap->where, &size, NULL, 0);
609 		break;
610 
611 	case KINFO_FILE:
612 		name[0] = KERN_FILE;
613 		error = kern_sysctl(name, 1, uap->where, &size, NULL, 0);
614 		break;
615 
616 	case KINFO_METER:
617 		name[0] = VM_METER;
618 		error = vm_sysctl(name, 1, uap->where, &size, NULL, 0);
619 		break;
620 
621 	case KINFO_LOADAVG:
622 		name[0] = VM_LOADAVG;
623 		error = vm_sysctl(name, 1, uap->where, &size, NULL, 0);
624 		break;
625 
626 	case KINFO_CLOCKRATE:
627 		name[0] = KERN_CLOCKRATE;
628 		error = kern_sysctl(name, 1, uap->where, &size, NULL, 0);
629 		break;
630 
631 	default:
632 		return (EINVAL);
633 	}
634 	if (error)
635 		return (error);
636 	*retval = size;
637 	return (copyout((caddr_t)&size, (caddr_t)uap->size, sizeof(size)));
638 }
639 #endif /* COMPAT_43 */
640