xref: /original-bsd/sys/kern/kern_sysctl.c (revision ee0c2e02)
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.26 (Berkeley) 02/04/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 	*oldlenp = len;
307 	if (oldp)
308 		error = copyout(str, oldp, len);
309 	if (error == 0 && newp) {
310 		error = copyin(newp, str, newlen);
311 		str[newlen] = 0;
312 	}
313 	return (error);
314 }
315 
316 /*
317  * As above, but read-only.
318  */
319 sysctl_rdstring(oldp, oldlenp, newp, str)
320 	void *oldp;
321 	u_int *oldlenp;
322 	void *newp;
323 	char *str;
324 {
325 	int len, error = 0;
326 
327 	len = strlen(str) + 1;
328 	if (oldp && *oldlenp < len)
329 		return (ENOMEM);
330 	if (newp)
331 		return (EPERM);
332 	*oldlenp = len;
333 	if (oldp)
334 		error = copyout(str, oldp, len);
335 	return (error);
336 }
337 
338 /*
339  * Validate parameters and get old parameters
340  * for a structure oriented sysctl function.
341  */
342 sysctl_rdstruct(oldp, oldlenp, newp, sp, len)
343 	void *oldp;
344 	u_int *oldlenp;
345 	void *newp;
346 	void *sp;
347 	int len;
348 {
349 	int error = 0;
350 
351 	if (oldp && *oldlenp < len)
352 		return (ENOMEM);
353 	if (newp)
354 		return (EPERM);
355 	*oldlenp = len;
356 	if (oldp)
357 		error = copyout(sp, oldp, len);
358 	return (error);
359 }
360 
361 /*
362  * Get file structures.
363  */
364 sysctl_file(where, sizep)
365 	char *where;
366 	int *sizep;
367 {
368 	int buflen, error;
369 	struct file *fp;
370 	char *start = where;
371 
372 	buflen = *sizep;
373 	if (where == NULL) {
374 		/*
375 		 * overestimate by 10 files
376 		 */
377 		*sizep = sizeof(filehead) + (nfiles + 10) * sizeof(struct file);
378 		return (0);
379 	}
380 
381 	/*
382 	 * first copyout filehead
383 	 */
384 	if (buflen < sizeof(filehead)) {
385 		*sizep = 0;
386 		return (0);
387 	}
388 	if (error = copyout((caddr_t)&filehead, where, sizeof(filehead)))
389 		return (error);
390 	buflen += sizeof(filehead);
391 	where += sizeof(filehead);
392 
393 	/*
394 	 * followed by an array of file structures
395 	 */
396 	for (fp = filehead; fp != NULL; fp = fp->f_filef) {
397 		if (buflen < sizeof(struct file)) {
398 			*sizep = where - start;
399 			return (ENOMEM);
400 		}
401 		if (error = copyout((caddr_t)fp, where, sizeof (struct file)))
402 			return (error);
403 		buflen -= sizeof(struct file);
404 		where += sizeof(struct file);
405 	}
406 	*sizep = where - start;
407 	return (0);
408 }
409 
410 /*
411  * try over estimating by 5 procs
412  */
413 #define KERN_PROCSLOP	(5 * sizeof (struct kinfo_proc))
414 
415 sysctl_doproc(name, namelen, where, sizep)
416 	int *name;
417 	int namelen;
418 	char *where;
419 	int *sizep;
420 {
421 	register struct proc *p;
422 	register struct kinfo_proc *dp = (struct kinfo_proc *)where;
423 	register int needed = 0;
424 	int buflen = where != NULL ? *sizep : 0;
425 	int doingzomb;
426 	struct eproc eproc;
427 	int error = 0;
428 
429 	if (namelen != 2)
430 		return (EINVAL);
431 	p = (struct proc *)allproc;
432 	doingzomb = 0;
433 again:
434 	for (; p != NULL; p = p->p_nxt) {
435 		/*
436 		 * Skip embryonic processes.
437 		 */
438 		if (p->p_stat == SIDL)
439 			continue;
440 		/*
441 		 * TODO - make more efficient (see notes below).
442 		 * do by session.
443 		 */
444 		switch (name[0]) {
445 
446 		case KERN_PROC_PID:
447 			/* could do this with just a lookup */
448 			if (p->p_pid != (pid_t)name[1])
449 				continue;
450 			break;
451 
452 		case KERN_PROC_PGRP:
453 			/* could do this by traversing pgrp */
454 			if (p->p_pgrp->pg_id != (pid_t)name[1])
455 				continue;
456 			break;
457 
458 		case KERN_PROC_TTY:
459 			if ((p->p_flag&SCTTY) == 0 ||
460 			    p->p_session->s_ttyp == NULL ||
461 			    p->p_session->s_ttyp->t_dev != (dev_t)name[1])
462 				continue;
463 			break;
464 
465 		case KERN_PROC_UID:
466 			if (p->p_ucred->cr_uid != (uid_t)name[1])
467 				continue;
468 			break;
469 
470 		case KERN_PROC_RUID:
471 			if (p->p_cred->p_ruid != (uid_t)name[1])
472 				continue;
473 			break;
474 		}
475 		if (buflen >= sizeof(struct kinfo_proc)) {
476 			fill_eproc(p, &eproc);
477 			if (error = copyout((caddr_t)p, &dp->kp_proc,
478 			    sizeof(struct proc)))
479 				return (error);
480 			if (error = copyout((caddr_t)&eproc, &dp->kp_eproc,
481 			    sizeof(eproc)))
482 				return (error);
483 			dp++;
484 			buflen -= sizeof(struct kinfo_proc);
485 		}
486 		needed += sizeof(struct kinfo_proc);
487 	}
488 	if (doingzomb == 0) {
489 		p = zombproc;
490 		doingzomb++;
491 		goto again;
492 	}
493 	if (where != NULL) {
494 		*sizep = (caddr_t)dp - where;
495 		if (needed > *sizep)
496 			return (ENOMEM);
497 	} else {
498 		needed += KERN_PROCSLOP;
499 		*sizep = needed;
500 	}
501 	return (0);
502 }
503 
504 /*
505  * Fill in an eproc structure for the specified process.
506  */
507 void
508 fill_eproc(p, ep)
509 	register struct proc *p;
510 	register struct eproc *ep;
511 {
512 	register struct tty *tp;
513 
514 	ep->e_paddr = p;
515 	ep->e_sess = p->p_pgrp->pg_session;
516 	ep->e_pcred = *p->p_cred;
517 	ep->e_ucred = *p->p_ucred;
518 	if (p->p_stat == SIDL || p->p_stat == SZOMB) {
519 		ep->e_vm.vm_rssize = 0;
520 		ep->e_vm.vm_tsize = 0;
521 		ep->e_vm.vm_dsize = 0;
522 		ep->e_vm.vm_ssize = 0;
523 #ifndef sparc
524 		/* ep->e_vm.vm_pmap = XXX; */
525 #endif
526 	} else {
527 		register struct vmspace *vm = p->p_vmspace;
528 
529 		ep->e_vm.vm_rssize = vm->vm_rssize;
530 		ep->e_vm.vm_tsize = vm->vm_tsize;
531 		ep->e_vm.vm_dsize = vm->vm_dsize;
532 		ep->e_vm.vm_ssize = vm->vm_ssize;
533 #ifndef sparc
534 		ep->e_vm.vm_pmap = vm->vm_pmap;
535 #endif
536 	}
537 	if (p->p_pptr)
538 		ep->e_ppid = p->p_pptr->p_pid;
539 	else
540 		ep->e_ppid = 0;
541 	ep->e_pgid = p->p_pgrp->pg_id;
542 	ep->e_jobc = p->p_pgrp->pg_jobc;
543 	if ((p->p_flag&SCTTY) &&
544 	     (tp = ep->e_sess->s_ttyp)) {
545 		ep->e_tdev = tp->t_dev;
546 		ep->e_tpgid = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID;
547 		ep->e_tsess = tp->t_session;
548 	} else
549 		ep->e_tdev = NODEV;
550 	ep->e_flag = ep->e_sess->s_ttyvp ? EPROC_CTTY : 0;
551 	if (SESS_LEADER(p))
552 		ep->e_flag |= EPROC_SLEADER;
553 	if (p->p_wmesg)
554 		strncpy(ep->e_wmesg, p->p_wmesg, WMESGLEN);
555 	ep->e_xsize = ep->e_xrssize = 0;
556 	ep->e_xccount = ep->e_xswrss = 0;
557 }
558 
559 #ifdef COMPAT_43
560 #include <sys/kinfo.h>
561 #include <sys/socket.h>
562 
563 struct getkerninfo_args {
564 	int	op;
565 	char	*where;
566 	int	*size;
567 	int	arg;
568 };
569 
570 getkerninfo(p, uap, retval)
571 	struct proc *p;
572 	register struct getkerninfo_args *uap;
573 	int *retval;
574 {
575 	int error, name[5];
576 	u_int size;
577 
578 	if (error = copyin((caddr_t)uap->size, (caddr_t)&size, sizeof(size)))
579 		return (error);
580 
581 	switch (ki_type(uap->op)) {
582 
583 	case KINFO_RT:
584 		name[0] = PF_ROUTE;
585 		name[1] = 0;
586 		name[2] = (uap->op & 0xff0000) >> 16;
587 		name[3] = uap->op & 0xff;
588 		name[4] = uap->arg;
589 		error = net_sysctl(name, 5, uap->where, &size, NULL, 0);
590 		break;
591 
592 	case KINFO_VNODE:
593 		name[0] = KERN_VNODE;
594 		error = kern_sysctl(name, 1, uap->where, &size, NULL, 0);
595 		break;
596 
597 	case KINFO_PROC:
598 		name[0] = KERN_PROC;
599 		name[1] = uap->op & 0xff;
600 		name[2] = uap->arg;
601 		error = kern_sysctl(name, 3, uap->where, &size, NULL, 0);
602 		break;
603 
604 	case KINFO_FILE:
605 		name[0] = KERN_FILE;
606 		error = kern_sysctl(name, 1, uap->where, &size, NULL, 0);
607 		break;
608 
609 	case KINFO_METER:
610 		name[0] = VM_METER;
611 		error = vm_sysctl(name, 1, uap->where, &size, NULL, 0);
612 		break;
613 
614 	case KINFO_LOADAVG:
615 		name[0] = VM_LOADAVG;
616 		error = vm_sysctl(name, 1, uap->where, &size, NULL, 0);
617 		break;
618 
619 	case KINFO_CLOCKRATE:
620 		name[0] = KERN_CLOCKRATE;
621 		error = kern_sysctl(name, 1, uap->where, &size, NULL, 0);
622 		break;
623 
624 	default:
625 		return (EINVAL);
626 	}
627 	if (error)
628 		return (error);
629 	*retval = size;
630 	return (copyout((caddr_t)&size, (caddr_t)uap->size, sizeof(size)));
631 }
632 #endif /* COMPAT_43 */
633