xref: /original-bsd/sys/kern/kern_sysctl.c (revision 4670e840)
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.31 (Berkeley) 03/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 	size_t	*oldlenp;
53 	void	*new;
54 	size_t	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 	size_t *oldlenp;
158 	void *newp;
159 	size_t 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 	size_t *oldlenp;
225 	void *newp;
226 	size_t 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 	size_t *oldlenp;
264 	void *newp;
265 	size_t 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 	size_t *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 	size_t *oldlenp;
310 	void *newp;
311 	size_t 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 	size_t *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 	size_t *oldlenp;
362 	void *newp, *sp;
363 	int len;
364 {
365 	int error = 0;
366 
367 	if (oldp && *oldlenp < len)
368 		return (ENOMEM);
369 	if (newp)
370 		return (EPERM);
371 	*oldlenp = len;
372 	if (oldp)
373 		error = copyout(sp, oldp, len);
374 	return (error);
375 }
376 
377 /*
378  * Get file structures.
379  */
380 sysctl_file(where, sizep)
381 	char *where;
382 	size_t *sizep;
383 {
384 	int buflen, error;
385 	struct file *fp;
386 	char *start = where;
387 
388 	buflen = *sizep;
389 	if (where == NULL) {
390 		/*
391 		 * overestimate by 10 files
392 		 */
393 		*sizep = sizeof(filehead) + (nfiles + 10) * sizeof(struct file);
394 		return (0);
395 	}
396 
397 	/*
398 	 * first copyout filehead
399 	 */
400 	if (buflen < sizeof(filehead)) {
401 		*sizep = 0;
402 		return (0);
403 	}
404 	if (error = copyout((caddr_t)&filehead, where, sizeof(filehead)))
405 		return (error);
406 	buflen += sizeof(filehead);
407 	where += sizeof(filehead);
408 
409 	/*
410 	 * followed by an array of file structures
411 	 */
412 	for (fp = filehead; fp != NULL; fp = fp->f_filef) {
413 		if (buflen < sizeof(struct file)) {
414 			*sizep = where - start;
415 			return (ENOMEM);
416 		}
417 		if (error = copyout((caddr_t)fp, where, sizeof (struct file)))
418 			return (error);
419 		buflen -= sizeof(struct file);
420 		where += sizeof(struct file);
421 	}
422 	*sizep = where - start;
423 	return (0);
424 }
425 
426 /*
427  * try over estimating by 5 procs
428  */
429 #define KERN_PROCSLOP	(5 * sizeof (struct kinfo_proc))
430 
431 sysctl_doproc(name, namelen, where, sizep)
432 	int *name;
433 	u_int namelen;
434 	char *where;
435 	size_t *sizep;
436 {
437 	register struct proc *p;
438 	register struct kinfo_proc *dp = (struct kinfo_proc *)where;
439 	register int needed = 0;
440 	int buflen = where != NULL ? *sizep : 0;
441 	int doingzomb;
442 	struct eproc eproc;
443 	int error = 0;
444 
445 	if (namelen != 2)
446 		return (EINVAL);
447 	p = (struct proc *)allproc;
448 	doingzomb = 0;
449 again:
450 	for (; p != NULL; p = p->p_nxt) {
451 		/*
452 		 * Skip embryonic processes.
453 		 */
454 		if (p->p_stat == SIDL)
455 			continue;
456 		/*
457 		 * TODO - make more efficient (see notes below).
458 		 * do by session.
459 		 */
460 		switch (name[0]) {
461 
462 		case KERN_PROC_PID:
463 			/* could do this with just a lookup */
464 			if (p->p_pid != (pid_t)name[1])
465 				continue;
466 			break;
467 
468 		case KERN_PROC_PGRP:
469 			/* could do this by traversing pgrp */
470 			if (p->p_pgrp->pg_id != (pid_t)name[1])
471 				continue;
472 			break;
473 
474 		case KERN_PROC_TTY:
475 			if ((p->p_flag&SCTTY) == 0 ||
476 			    p->p_session->s_ttyp == NULL ||
477 			    p->p_session->s_ttyp->t_dev != (dev_t)name[1])
478 				continue;
479 			break;
480 
481 		case KERN_PROC_UID:
482 			if (p->p_ucred->cr_uid != (uid_t)name[1])
483 				continue;
484 			break;
485 
486 		case KERN_PROC_RUID:
487 			if (p->p_cred->p_ruid != (uid_t)name[1])
488 				continue;
489 			break;
490 		}
491 		if (buflen >= sizeof(struct kinfo_proc)) {
492 			fill_eproc(p, &eproc);
493 			if (error = copyout((caddr_t)p, &dp->kp_proc,
494 			    sizeof(struct proc)))
495 				return (error);
496 			if (error = copyout((caddr_t)&eproc, &dp->kp_eproc,
497 			    sizeof(eproc)))
498 				return (error);
499 			dp++;
500 			buflen -= sizeof(struct kinfo_proc);
501 		}
502 		needed += sizeof(struct kinfo_proc);
503 	}
504 	if (doingzomb == 0) {
505 		p = zombproc;
506 		doingzomb++;
507 		goto again;
508 	}
509 	if (where != NULL) {
510 		*sizep = (caddr_t)dp - where;
511 		if (needed > *sizep)
512 			return (ENOMEM);
513 	} else {
514 		needed += KERN_PROCSLOP;
515 		*sizep = needed;
516 	}
517 	return (0);
518 }
519 
520 /*
521  * Fill in an eproc structure for the specified process.
522  */
523 void
524 fill_eproc(p, ep)
525 	register struct proc *p;
526 	register struct eproc *ep;
527 {
528 	register struct tty *tp;
529 
530 	ep->e_paddr = p;
531 	ep->e_sess = p->p_pgrp->pg_session;
532 	ep->e_pcred = *p->p_cred;
533 	ep->e_ucred = *p->p_ucred;
534 	if (p->p_stat == SIDL || p->p_stat == SZOMB) {
535 		ep->e_vm.vm_rssize = 0;
536 		ep->e_vm.vm_tsize = 0;
537 		ep->e_vm.vm_dsize = 0;
538 		ep->e_vm.vm_ssize = 0;
539 #ifndef sparc
540 		/* ep->e_vm.vm_pmap = XXX; */
541 #endif
542 	} else {
543 		register struct vmspace *vm = p->p_vmspace;
544 
545 		ep->e_vm.vm_rssize = vm->vm_rssize;
546 		ep->e_vm.vm_tsize = vm->vm_tsize;
547 		ep->e_vm.vm_dsize = vm->vm_dsize;
548 		ep->e_vm.vm_ssize = vm->vm_ssize;
549 #ifndef sparc
550 		ep->e_vm.vm_pmap = vm->vm_pmap;
551 #endif
552 	}
553 	if (p->p_pptr)
554 		ep->e_ppid = p->p_pptr->p_pid;
555 	else
556 		ep->e_ppid = 0;
557 	ep->e_pgid = p->p_pgrp->pg_id;
558 	ep->e_jobc = p->p_pgrp->pg_jobc;
559 	if ((p->p_flag&SCTTY) &&
560 	     (tp = ep->e_sess->s_ttyp)) {
561 		ep->e_tdev = tp->t_dev;
562 		ep->e_tpgid = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID;
563 		ep->e_tsess = tp->t_session;
564 	} else
565 		ep->e_tdev = NODEV;
566 	ep->e_flag = ep->e_sess->s_ttyvp ? EPROC_CTTY : 0;
567 	if (SESS_LEADER(p))
568 		ep->e_flag |= EPROC_SLEADER;
569 	if (p->p_wmesg)
570 		strncpy(ep->e_wmesg, p->p_wmesg, WMESGLEN);
571 	ep->e_xsize = ep->e_xrssize = 0;
572 	ep->e_xccount = ep->e_xswrss = 0;
573 }
574 
575 #ifdef COMPAT_43
576 #include <sys/socket.h>
577 #define	KINFO_PROC		(0<<8)
578 #define	KINFO_RT		(1<<8)
579 #define	KINFO_VNODE		(2<<8)
580 #define	KINFO_FILE		(3<<8)
581 #define	KINFO_METER		(4<<8)
582 #define	KINFO_LOADAVG		(5<<8)
583 #define	KINFO_CLOCKRATE		(6<<8)
584 
585 struct getkerninfo_args {
586 	int	op;
587 	char	*where;
588 	int	*size;
589 	int	arg;
590 };
591 
592 getkerninfo(p, uap, retval)
593 	struct proc *p;
594 	register struct getkerninfo_args *uap;
595 	int *retval;
596 {
597 	int error, name[5];
598 	u_int size;
599 
600 	if (uap->size &&
601 	    (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 	if (uap->size)
654 		error = copyout((caddr_t)&size, (caddr_t)uap->size,
655 		    sizeof(size));
656 	return (error);
657 }
658 #endif /* COMPAT_43 */
659