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