xref: /openbsd-src/sys/kern/kern_sysctl.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: kern_sysctl.c,v 1.55 2001/07/17 20:57:49 deraadt Exp $	*/
2 /*	$NetBSD: kern_sysctl.c,v 1.17 1996/05/20 17:49:05 mrg Exp $	*/
3 
4 /*-
5  * Copyright (c) 1982, 1986, 1989, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Mike Karels at Berkeley Software Design, Inc.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the University of
22  *	California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  *
39  *	@(#)kern_sysctl.c	8.4 (Berkeley) 4/14/94
40  */
41 
42 /*
43  * sysctl system call.
44  */
45 
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/kernel.h>
49 #include <sys/malloc.h>
50 #include <sys/proc.h>
51 #include <sys/resourcevar.h>
52 #include <sys/file.h>
53 #include <sys/vnode.h>
54 #include <sys/unistd.h>
55 #include <sys/buf.h>
56 #include <sys/ioctl.h>
57 #include <sys/tty.h>
58 #include <sys/disklabel.h>
59 #include <sys/disk.h>
60 #include <vm/vm.h>
61 #include <sys/sysctl.h>
62 #include <sys/msgbuf.h>
63 #include <sys/dkstat.h>
64 #include <sys/vmmeter.h>
65 #include <sys/namei.h>
66 
67 #include <uvm/uvm_extern.h>
68 
69 #include <sys/mount.h>
70 #include <sys/syscallargs.h>
71 #include <dev/rndvar.h>
72 
73 #ifdef DDB
74 #include <ddb/db_var.h>
75 #endif
76 
77 extern struct forkstat forkstat;
78 extern struct nchstats nchstats;
79 extern int nselcoll, fscale;
80 extern struct disklist_head disklist;
81 extern fixpt_t ccpu;
82 
83 int sysctl_diskinit(int, struct proc *);
84 
85 /*
86  * Lock to avoid too many processes vslocking a large amount of memory
87  * at the same time.
88  */
89 struct lock sysctl_lock, sysctl_disklock;
90 
91 #if defined(KMEMSTATS) || defined(DIAGNOSTIC) || defined(FFS_SOFTUPDATES)
92 struct lock sysctl_kmemlock;
93 #endif
94 
95 void
96 sysctl_init()
97 {
98 	lockinit(&sysctl_lock, PLOCK|PCATCH, "sysctl", 0, 0);
99 	lockinit(&sysctl_disklock, PLOCK|PCATCH, "sysctl_disklock", 0, 0);
100 
101 #if defined(KMEMSTATS) || defined(DIAGNOSTIC) || defined(FFS_SOFTUPDATES)
102 	lockinit(&sysctl_kmemlock, PLOCK|PCATCH, "sysctl_kmemlock", 0, 0);
103 #endif
104 }
105 
106 int
107 sys___sysctl(p, v, retval)
108 	struct proc *p;
109 	void *v;
110 	register_t *retval;
111 {
112 	register struct sys___sysctl_args /* {
113 		syscallarg(int *) name;
114 		syscallarg(u_int) namelen;
115 		syscallarg(void *) old;
116 		syscallarg(size_t *) oldlenp;
117 		syscallarg(void *) new;
118 		syscallarg(size_t) newlen;
119 	} */ *uap = v;
120 	int error, dolock = 1;
121 	size_t savelen = 0, oldlen = 0;
122 	sysctlfn *fn;
123 	int name[CTL_MAXNAME];
124 
125 	if (SCARG(uap, new) != NULL &&
126 	    (error = suser(p->p_ucred, &p->p_acflag)))
127 		return (error);
128 	/*
129 	 * all top-level sysctl names are non-terminal
130 	 */
131 	if (SCARG(uap, namelen) > CTL_MAXNAME || SCARG(uap, namelen) < 2)
132 		return (EINVAL);
133 	error = copyin(SCARG(uap, name), &name,
134 		       SCARG(uap, namelen) * sizeof(int));
135 	if (error)
136 		return (error);
137 
138 	switch (name[0]) {
139 	case CTL_KERN:
140 		fn = kern_sysctl;
141 		if (name[2] == KERN_VNODE)	/* XXX */
142 			dolock = 0;
143 		break;
144 	case CTL_HW:
145 		fn = hw_sysctl;
146 		break;
147 	case CTL_VM:
148 		fn = uvm_sysctl;
149 		break;
150 	case CTL_NET:
151 		fn = net_sysctl;
152 		break;
153 	case CTL_FS:
154 		fn = fs_sysctl;
155 		break;
156 	case CTL_VFS:
157 		fn = vfs_sysctl;
158 		break;
159 	case CTL_MACHDEP:
160 		fn = cpu_sysctl;
161 		break;
162 #ifdef DEBUG
163 	case CTL_DEBUG:
164 		fn = debug_sysctl;
165 		break;
166 #endif
167 #ifdef DDB
168 	case CTL_DDB:
169 		fn = ddb_sysctl;
170 		break;
171 #endif
172 	default:
173 		return (EOPNOTSUPP);
174 	}
175 
176 	if (SCARG(uap, oldlenp) &&
177 	    (error = copyin(SCARG(uap, oldlenp), &oldlen, sizeof(oldlen))))
178 		return (error);
179 	if (SCARG(uap, old) != NULL) {
180 		if ((error = lockmgr(&sysctl_lock, LK_EXCLUSIVE, NULL, p)) != 0)
181 			return (error);
182 		if (dolock)
183 			if (uvm_vslock(p, SCARG(uap, old), oldlen,
184 			    VM_PROT_READ|VM_PROT_WRITE) != KERN_SUCCESS) {
185 				lockmgr(&sysctl_lock, LK_RELEASE, NULL, p);
186 				return EFAULT;
187 			}
188 		savelen = oldlen;
189 	}
190 	error = (*fn)(name + 1, SCARG(uap, namelen) - 1, SCARG(uap, old),
191 	    &oldlen, SCARG(uap, new), SCARG(uap, newlen), p);
192 	if (SCARG(uap, old) != NULL) {
193 		if (dolock)
194 			uvm_vsunlock(p, SCARG(uap, old), savelen);
195 		lockmgr(&sysctl_lock, LK_RELEASE, NULL, p);
196 	}
197 	if (error)
198 		return (error);
199 	if (SCARG(uap, oldlenp))
200 		error = copyout(&oldlen, SCARG(uap, oldlenp), sizeof(oldlen));
201 	return (error);
202 }
203 
204 /*
205  * Attributes stored in the kernel.
206  */
207 char hostname[MAXHOSTNAMELEN];
208 int hostnamelen;
209 char domainname[MAXHOSTNAMELEN];
210 int domainnamelen;
211 long hostid;
212 char *disknames = NULL;
213 struct diskstats *diskstats = NULL;
214 #ifdef INSECURE
215 int securelevel = -1;
216 #else
217 int securelevel;
218 #endif
219 
220 /*
221  * kernel related system variables.
222  */
223 int
224 kern_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p)
225 	int *name;
226 	u_int namelen;
227 	void *oldp;
228 	size_t *oldlenp;
229 	void *newp;
230 	size_t newlen;
231 	struct proc *p;
232 {
233 	int error, level, inthostid;
234 	extern char ostype[], osrelease[], osversion[], version[];
235 	extern int somaxconn, sominconn;
236 	extern int usermount, nosuidcoredump;
237 	extern long cp_time[CPUSTATES];
238 
239 	/* all sysctl names at this level are terminal */
240 	if (namelen != 1 && !(name[0] == KERN_PROC || name[0] == KERN_PROF ||
241 	    name[0] == KERN_MALLOCSTATS || name[0] == KERN_TTY ||
242 	    name[0] == KERN_POOL))
243 		return (ENOTDIR);		/* overloaded */
244 
245 	switch (name[0]) {
246 	case KERN_OSTYPE:
247 		return (sysctl_rdstring(oldp, oldlenp, newp, ostype));
248 	case KERN_OSRELEASE:
249 		return (sysctl_rdstring(oldp, oldlenp, newp, osrelease));
250 	case KERN_OSREV:
251 		return (sysctl_rdint(oldp, oldlenp, newp, OpenBSD));
252 	case KERN_OSVERSION:
253 		return (sysctl_rdstring(oldp, oldlenp, newp, osversion));
254 	case KERN_VERSION:
255 		return (sysctl_rdstring(oldp, oldlenp, newp, version));
256 	case KERN_MAXVNODES:
257 		return(sysctl_int(oldp, oldlenp, newp, newlen, &desiredvnodes));
258 	case KERN_MAXPROC:
259 		return (sysctl_int(oldp, oldlenp, newp, newlen, &maxproc));
260 	case KERN_MAXFILES:
261 		return (sysctl_int(oldp, oldlenp, newp, newlen, &maxfiles));
262 	case KERN_ARGMAX:
263 		return (sysctl_rdint(oldp, oldlenp, newp, ARG_MAX));
264 	case KERN_NSELCOLL:
265 		return (sysctl_rdint(oldp, oldlenp, newp, nselcoll));
266 	case KERN_SECURELVL:
267 		level = securelevel;
268 		if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &level)) ||
269 		    newp == NULL)
270 			return (error);
271 		if ((securelevel > 0 || level < -1)
272 		    && level < securelevel && p->p_pid != 1)
273 			return (EPERM);
274 		securelevel = level;
275 		return (0);
276 	case KERN_HOSTNAME:
277 		error = sysctl_tstring(oldp, oldlenp, newp, newlen,
278 		    hostname, sizeof(hostname));
279 		if (newp && !error)
280 			hostnamelen = newlen;
281 		return (error);
282 	case KERN_DOMAINNAME:
283 		error = sysctl_tstring(oldp, oldlenp, newp, newlen,
284 		    domainname, sizeof(domainname));
285 		if (newp && !error)
286 			domainnamelen = newlen;
287 		return (error);
288 	case KERN_HOSTID:
289 		inthostid = hostid;  /* XXX assumes sizeof long <= sizeof int */
290 		error =  sysctl_int(oldp, oldlenp, newp, newlen, &inthostid);
291 		hostid = inthostid;
292 		return (error);
293 	case KERN_CLOCKRATE:
294 		return (sysctl_clockrate(oldp, oldlenp));
295 	case KERN_BOOTTIME:
296 		return (sysctl_rdstruct(oldp, oldlenp, newp, &boottime,
297 		    sizeof(struct timeval)));
298 	case KERN_VNODE:
299 		return (sysctl_vnode(oldp, oldlenp, p));
300 	case KERN_PROC:
301 		return (sysctl_doproc(name + 1, namelen - 1, oldp, oldlenp));
302 	case KERN_FILE:
303 		return (sysctl_file(oldp, oldlenp));
304 #ifdef GPROF
305 	case KERN_PROF:
306 		return (sysctl_doprof(name + 1, namelen - 1, oldp, oldlenp,
307 		    newp, newlen));
308 #endif
309 	case KERN_POSIX1:
310 		return (sysctl_rdint(oldp, oldlenp, newp, _POSIX_VERSION));
311 	case KERN_NGROUPS:
312 		return (sysctl_rdint(oldp, oldlenp, newp, NGROUPS_MAX));
313 	case KERN_JOB_CONTROL:
314 		return (sysctl_rdint(oldp, oldlenp, newp, 1));
315 	case KERN_SAVED_IDS:
316 #ifdef _POSIX_SAVED_IDS
317 		return (sysctl_rdint(oldp, oldlenp, newp, 1));
318 #else
319 		return (sysctl_rdint(oldp, oldlenp, newp, 0));
320 #endif
321 	case KERN_MAXPARTITIONS:
322 		return (sysctl_rdint(oldp, oldlenp, newp, MAXPARTITIONS));
323 	case KERN_RAWPARTITION:
324 		return (sysctl_rdint(oldp, oldlenp, newp, RAW_PART));
325 	case KERN_NTPTIME:
326 		return (sysctl_ntptime(oldp, oldlenp));
327 	case KERN_SOMAXCONN:
328 		return (sysctl_int(oldp, oldlenp, newp, newlen, &somaxconn));
329 	case KERN_SOMINCONN:
330 		return (sysctl_int(oldp, oldlenp, newp, newlen, &sominconn));
331 	case KERN_USERMOUNT:
332 		return (sysctl_int(oldp, oldlenp, newp, newlen, &usermount));
333 	case KERN_RND:
334 		return (sysctl_rdstruct(oldp, oldlenp, newp, &rndstats,
335 		    sizeof(rndstats)));
336 	case KERN_ARND:
337 		return (sysctl_rdint(oldp, oldlenp, newp, arc4random()));
338 	case KERN_NOSUIDCOREDUMP:
339 		return (sysctl_int(oldp, oldlenp, newp, newlen, &nosuidcoredump));
340 	case KERN_FSYNC:
341 		return (sysctl_rdint(oldp, oldlenp, newp, 1));
342 	case KERN_SYSVMSG:
343 #ifdef SYSVMSG
344 		return (sysctl_rdint(oldp, oldlenp, newp, 1));
345 #else
346 		return (sysctl_rdint(oldp, oldlenp, newp, 0));
347 #endif
348 	case KERN_SYSVSEM:
349 #ifdef SYSVSEM
350 		return (sysctl_rdint(oldp, oldlenp, newp, 1));
351 #else
352 		return (sysctl_rdint(oldp, oldlenp, newp, 0));
353 #endif
354 	case KERN_SYSVSHM:
355 #ifdef SYSVSHM
356 		return (sysctl_rdint(oldp, oldlenp, newp, 1));
357 #else
358 		return (sysctl_rdint(oldp, oldlenp, newp, 0));
359 #endif
360 	case KERN_MSGBUFSIZE:
361 		/*
362 		 * deal with cases where the message buffer has
363 		 * become corrupted.
364 		 */
365 		if (!msgbufp || msgbufp->msg_magic != MSG_MAGIC)
366 			return (ENXIO);
367 		return (sysctl_rdint(oldp, oldlenp, newp, msgbufp->msg_bufs));
368 	case KERN_MSGBUF:
369 		/* see note above */
370 		if (!msgbufp || msgbufp->msg_magic != MSG_MAGIC)
371 			return (ENXIO);
372 		return (sysctl_rdstruct(oldp, oldlenp, newp, msgbufp,
373 		    msgbufp->msg_bufs + offsetof(struct msgbuf, msg_bufc)));
374 	case KERN_MALLOCSTATS:
375 		return (sysctl_malloc(name + 1, namelen - 1, oldp, oldlenp,
376 		    newp, newlen, p));
377 	case KERN_CPTIME:
378 		return (sysctl_rdstruct(oldp, oldlenp, newp, &cp_time,
379 		    sizeof(cp_time)));
380 	case KERN_NCHSTATS:
381 		return (sysctl_rdstruct(oldp, oldlenp, newp, &nchstats,
382 		    sizeof(struct nchstats)));
383 	case KERN_FORKSTAT:
384 		return (sysctl_rdstruct(oldp, oldlenp, newp, &forkstat,
385 		    sizeof(struct forkstat)));
386 	case KERN_TTY:
387 		return (sysctl_tty(name + 1, namelen - 1, oldp, oldlenp,
388 		    newp, newlen));
389 	case KERN_FSCALE:
390 		return (sysctl_rdint(oldp, oldlenp, newp, fscale));
391 	case KERN_CCPU:
392 		return (sysctl_rdint(oldp, oldlenp, newp, ccpu));
393 	case KERN_NPROCS:
394 		return (sysctl_rdint(oldp, oldlenp, newp, nprocs));
395 	case KERN_POOL:
396 		return (sysctl_dopool(name + 1, namelen - 1, oldp, oldlenp));
397 	default:
398 		return (EOPNOTSUPP);
399 	}
400 	/* NOTREACHED */
401 }
402 
403 /*
404  * hardware related system variables.
405  */
406 int
407 hw_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p)
408 	int *name;
409 	u_int namelen;
410 	void *oldp;
411 	size_t *oldlenp;
412 	void *newp;
413 	size_t newlen;
414 	struct proc *p;
415 {
416 	extern char machine[], cpu_model[];
417 	int err;
418 
419 	/* all sysctl names at this level are terminal */
420 	if (namelen != 1)
421 		return (ENOTDIR);		/* overloaded */
422 
423 	switch (name[0]) {
424 	case HW_MACHINE:
425 		return (sysctl_rdstring(oldp, oldlenp, newp, machine));
426 	case HW_MODEL:
427 		return (sysctl_rdstring(oldp, oldlenp, newp, cpu_model));
428 	case HW_NCPU:
429 		return (sysctl_rdint(oldp, oldlenp, newp, 1));	/* XXX */
430 	case HW_BYTEORDER:
431 		return (sysctl_rdint(oldp, oldlenp, newp, BYTE_ORDER));
432 	case HW_PHYSMEM:
433 		return (sysctl_rdint(oldp, oldlenp, newp, ctob(physmem)));
434 	case HW_USERMEM:
435 		return (sysctl_rdint(oldp, oldlenp, newp,
436 		    ctob(physmem - uvmexp.wired)));
437 	case HW_PAGESIZE:
438 		return (sysctl_rdint(oldp, oldlenp, newp, PAGE_SIZE));
439 	case HW_DISKNAMES:
440 		err = sysctl_diskinit(0, p);
441 		if (err)
442 			return err;
443 		return (sysctl_rdstring(oldp, oldlenp, newp, disknames));
444 	case HW_DISKSTATS:
445 		err = sysctl_diskinit(1, p);
446 		if (err)
447 			return err;
448 		return (sysctl_rdstruct(oldp, oldlenp, newp, diskstats,
449 		    disk_count * sizeof(struct diskstats)));
450 	case HW_DISKCOUNT:
451 		return (sysctl_rdint(oldp, oldlenp, newp, disk_count));
452 	default:
453 		return (EOPNOTSUPP);
454 	}
455 	/* NOTREACHED */
456 }
457 
458 #ifdef DEBUG
459 /*
460  * Debugging related system variables.
461  */
462 struct ctldebug debug0, debug1, debug2, debug3, debug4;
463 struct ctldebug debug5, debug6, debug7, debug8, debug9;
464 struct ctldebug debug10, debug11, debug12, debug13, debug14;
465 struct ctldebug debug15, debug16, debug17, debug18, debug19;
466 static struct ctldebug *debugvars[CTL_DEBUG_MAXID] = {
467 	&debug0, &debug1, &debug2, &debug3, &debug4,
468 	&debug5, &debug6, &debug7, &debug8, &debug9,
469 	&debug10, &debug11, &debug12, &debug13, &debug14,
470 	&debug15, &debug16, &debug17, &debug18, &debug19,
471 };
472 int
473 debug_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p)
474 	int *name;
475 	u_int namelen;
476 	void *oldp;
477 	size_t *oldlenp;
478 	void *newp;
479 	size_t newlen;
480 	struct proc *p;
481 {
482 	struct ctldebug *cdp;
483 
484 	/* all sysctl names at this level are name and field */
485 	if (namelen != 2)
486 		return (ENOTDIR);		/* overloaded */
487 	cdp = debugvars[name[0]];
488 	if (cdp->debugname == 0)
489 		return (EOPNOTSUPP);
490 	switch (name[1]) {
491 	case CTL_DEBUG_NAME:
492 		return (sysctl_rdstring(oldp, oldlenp, newp, cdp->debugname));
493 	case CTL_DEBUG_VALUE:
494 		return (sysctl_int(oldp, oldlenp, newp, newlen, cdp->debugvar));
495 	default:
496 		return (EOPNOTSUPP);
497 	}
498 	/* NOTREACHED */
499 }
500 #endif /* DEBUG */
501 
502 /*
503  * Validate parameters and get old / set new parameters
504  * for an integer-valued sysctl function.
505  */
506 int
507 sysctl_int(oldp, oldlenp, newp, newlen, valp)
508 	void *oldp;
509 	size_t *oldlenp;
510 	void *newp;
511 	size_t newlen;
512 	int *valp;
513 {
514 	int error = 0;
515 
516 	if (oldp && *oldlenp < sizeof(int))
517 		return (ENOMEM);
518 	if (newp && newlen != sizeof(int))
519 		return (EINVAL);
520 	*oldlenp = sizeof(int);
521 	if (oldp)
522 		error = copyout(valp, oldp, sizeof(int));
523 	if (error == 0 && newp)
524 		error = copyin(newp, valp, sizeof(int));
525 	return (error);
526 }
527 
528 /*
529  * As above, but read-only.
530  */
531 int
532 sysctl_rdint(oldp, oldlenp, newp, val)
533 	void *oldp;
534 	size_t *oldlenp;
535 	void *newp;
536 	int val;
537 {
538 	int error = 0;
539 
540 	if (oldp && *oldlenp < sizeof(int))
541 		return (ENOMEM);
542 	if (newp)
543 		return (EPERM);
544 	*oldlenp = sizeof(int);
545 	if (oldp)
546 		error = copyout((caddr_t)&val, oldp, sizeof(int));
547 	return (error);
548 }
549 
550 /*
551  * Validate parameters and get old / set new parameters
552  * for an integer-valued sysctl function.
553  */
554 int
555 sysctl_quad(oldp, oldlenp, newp, newlen, valp)
556 	void *oldp;
557 	size_t *oldlenp;
558 	void *newp;
559 	size_t newlen;
560 	int64_t *valp;
561 {
562 	int error = 0;
563 
564 	if (oldp && *oldlenp < sizeof(int64_t))
565 		return (ENOMEM);
566 	if (newp && newlen != sizeof(int64_t))
567 		return (EINVAL);
568 	*oldlenp = sizeof(int64_t);
569 	if (oldp)
570 		error = copyout(valp, oldp, sizeof(int64_t));
571 	if (error == 0 && newp)
572 		error = copyin(newp, valp, sizeof(int64_t));
573 	return (error);
574 }
575 
576 /*
577  * As above, but read-only.
578  */
579 int
580 sysctl_rdquad(oldp, oldlenp, newp, val)
581 	void *oldp;
582 	size_t *oldlenp;
583 	void *newp;
584 	int64_t val;
585 {
586 	int error = 0;
587 
588 	if (oldp && *oldlenp < sizeof(int64_t))
589 		return (ENOMEM);
590 	if (newp)
591 		return (EPERM);
592 	*oldlenp = sizeof(int64_t);
593 	if (oldp)
594 		error = copyout((caddr_t)&val, oldp, sizeof(int64_t));
595 	return (error);
596 }
597 
598 /*
599  * Validate parameters and get old / set new parameters
600  * for a string-valued sysctl function.
601  */
602 int
603 sysctl_string(oldp, oldlenp, newp, newlen, str, maxlen)
604 	void *oldp;
605 	size_t *oldlenp;
606 	void *newp;
607 	size_t newlen;
608 	char *str;
609 	int maxlen;
610 {
611 	return sysctl__string(oldp, oldlenp, newp, newlen, str, maxlen, 0);
612 }
613 
614 int
615 sysctl_tstring(oldp, oldlenp, newp, newlen, str, maxlen)
616 	void *oldp;
617 	size_t *oldlenp;
618 	void *newp;
619 	size_t newlen;
620 	char *str;
621 	int maxlen;
622 {
623 	return sysctl__string(oldp, oldlenp, newp, newlen, str, maxlen, 1);
624 }
625 
626 int
627 sysctl__string(oldp, oldlenp, newp, newlen, str, maxlen, trunc)
628 	void *oldp;
629 	size_t *oldlenp;
630 	void *newp;
631 	size_t newlen;
632 	char *str;
633 	int maxlen;
634 	int trunc;
635 {
636 	int len, error = 0;
637 	char c;
638 
639 	len = strlen(str) + 1;
640 	if (oldp && *oldlenp < len) {
641 		if (trunc == 0 || *oldlenp == 0)
642 			return (ENOMEM);
643 	}
644 	if (newp && newlen >= maxlen)
645 		return (EINVAL);
646 	if (oldp) {
647 		if (trunc && *oldlenp < len) {
648 			/* save & zap NUL terminator while copying */
649 			c = str[*oldlenp-1];
650 			str[*oldlenp-1] = '\0';
651 			error = copyout(str, oldp, *oldlenp);
652 			str[*oldlenp-1] = c;
653 		} else {
654 			*oldlenp = len;
655 			error = copyout(str, oldp, len);
656 		}
657 	}
658 	if (error == 0 && newp) {
659 		error = copyin(newp, str, newlen);
660 		str[newlen] = 0;
661 	}
662 	return (error);
663 }
664 
665 /*
666  * As above, but read-only.
667  */
668 int
669 sysctl_rdstring(oldp, oldlenp, newp, str)
670 	void *oldp;
671 	size_t *oldlenp;
672 	void *newp;
673 	char *str;
674 {
675 	int len, error = 0;
676 
677 	len = strlen(str) + 1;
678 	if (oldp && *oldlenp < len)
679 		return (ENOMEM);
680 	if (newp)
681 		return (EPERM);
682 	*oldlenp = len;
683 	if (oldp)
684 		error = copyout(str, oldp, len);
685 	return (error);
686 }
687 
688 /*
689  * Validate parameters and get old / set new parameters
690  * for a structure oriented sysctl function.
691  */
692 int
693 sysctl_struct(oldp, oldlenp, newp, newlen, sp, len)
694 	void *oldp;
695 	size_t *oldlenp;
696 	void *newp;
697 	size_t newlen;
698 	void *sp;
699 	int len;
700 {
701 	int error = 0;
702 
703 	if (oldp && *oldlenp < len)
704 		return (ENOMEM);
705 	if (newp && newlen > len)
706 		return (EINVAL);
707 	if (oldp) {
708 		*oldlenp = len;
709 		error = copyout(sp, oldp, len);
710 	}
711 	if (error == 0 && newp)
712 		error = copyin(newp, sp, len);
713 	return (error);
714 }
715 
716 /*
717  * Validate parameters and get old parameters
718  * for a structure oriented sysctl function.
719  */
720 int
721 sysctl_rdstruct(oldp, oldlenp, newp, sp, len)
722 	void *oldp;
723 	size_t *oldlenp;
724 	void *newp, *sp;
725 	int len;
726 {
727 	int error = 0;
728 
729 	if (oldp && *oldlenp < len)
730 		return (ENOMEM);
731 	if (newp)
732 		return (EPERM);
733 	*oldlenp = len;
734 	if (oldp)
735 		error = copyout(sp, oldp, len);
736 	return (error);
737 }
738 
739 /*
740  * Get file structures.
741  */
742 int
743 sysctl_file(where, sizep)
744 	char *where;
745 	size_t *sizep;
746 {
747 	int buflen, error;
748 	struct file *fp;
749 	char *start = where;
750 
751 	buflen = *sizep;
752 	if (where == NULL) {
753 		/*
754 		 * overestimate by 10 files
755 		 */
756 		*sizep = sizeof(filehead) + (nfiles + 10) * sizeof(struct file);
757 		return (0);
758 	}
759 
760 	/*
761 	 * first copyout filehead
762 	 */
763 	if (buflen < sizeof(filehead)) {
764 		*sizep = 0;
765 		return (0);
766 	}
767 	error = copyout((caddr_t)&filehead, where, sizeof(filehead));
768 	if (error)
769 		return (error);
770 	buflen -= sizeof(filehead);
771 	where += sizeof(filehead);
772 
773 	/*
774 	 * followed by an array of file structures
775 	 */
776 	for (fp = filehead.lh_first; fp != 0; fp = fp->f_list.le_next) {
777 		if (buflen < sizeof(struct file)) {
778 			*sizep = where - start;
779 			return (ENOMEM);
780 		}
781 		error = copyout((caddr_t)fp, where, sizeof (struct file));
782 		if (error)
783 			return (error);
784 		buflen -= sizeof(struct file);
785 		where += sizeof(struct file);
786 	}
787 	*sizep = where - start;
788 	return (0);
789 }
790 
791 /*
792  * try over estimating by 5 procs
793  */
794 #define KERN_PROCSLOP	(5 * sizeof (struct kinfo_proc))
795 
796 int
797 sysctl_doproc(name, namelen, where, sizep)
798 	int *name;
799 	u_int namelen;
800 	char *where;
801 	size_t *sizep;
802 {
803 	register struct proc *p;
804 	register struct kinfo_proc *dp = (struct kinfo_proc *)where;
805 	register int needed = 0;
806 	int buflen = where != NULL ? *sizep : 0;
807 	int doingzomb;
808 	struct eproc eproc;
809 	int error = 0;
810 
811 	if (namelen != 2 && !(namelen == 1 &&
812 	    (name[0] == KERN_PROC_ALL || name[0] == KERN_PROC_KTHREAD)))
813 		return (EINVAL);
814 	p = LIST_FIRST(&allproc);
815 	doingzomb = 0;
816 again:
817 	for (; p != 0; p = LIST_NEXT(p, p_list)) {
818 		/*
819 		 * Skip embryonic processes.
820 		 */
821 		if (p->p_stat == SIDL)
822 			continue;
823 		/*
824 		 * TODO - make more efficient (see notes below).
825 		 * do by session.
826 		 */
827 		switch (name[0]) {
828 
829 		case KERN_PROC_PID:
830 			/* could do this with just a lookup */
831 			if (p->p_pid != (pid_t)name[1])
832 				continue;
833 			break;
834 
835 		case KERN_PROC_PGRP:
836 			/* could do this by traversing pgrp */
837 			if (p->p_pgrp->pg_id != (pid_t)name[1])
838 				continue;
839 			break;
840 
841 		case KERN_PROC_TTY:
842 			if ((p->p_flag & P_CONTROLT) == 0 ||
843 			    p->p_session->s_ttyp == NULL ||
844 			    p->p_session->s_ttyp->t_dev != (dev_t)name[1])
845 				continue;
846 			break;
847 
848 		case KERN_PROC_UID:
849 			if (p->p_ucred->cr_uid != (uid_t)name[1])
850 				continue;
851 			break;
852 
853 		case KERN_PROC_RUID:
854 			if (p->p_cred->p_ruid != (uid_t)name[1])
855 				continue;
856 			break;
857 
858 		case KERN_PROC_ALL:
859 			if (p->p_flag & P_SYSTEM)
860 				continue;
861 			break;
862 		}
863 		if (buflen >= sizeof(struct kinfo_proc)) {
864 			fill_eproc(p, &eproc);
865 			error = copyout((caddr_t)p, &dp->kp_proc,
866 					sizeof(struct proc));
867 			if (error)
868 				return (error);
869 			error = copyout((caddr_t)&eproc, &dp->kp_eproc,
870 					sizeof(eproc));
871 			if (error)
872 				return (error);
873 			dp++;
874 			buflen -= sizeof(struct kinfo_proc);
875 		}
876 		needed += sizeof(struct kinfo_proc);
877 	}
878 	if (doingzomb == 0) {
879 		p = LIST_FIRST(&zombproc);
880 		doingzomb++;
881 		goto again;
882 	}
883 	if (where != NULL) {
884 		*sizep = (caddr_t)dp - where;
885 		if (needed > *sizep)
886 			return (ENOMEM);
887 	} else {
888 		needed += KERN_PROCSLOP;
889 		*sizep = needed;
890 	}
891 	return (0);
892 }
893 
894 /*
895  * Fill in an eproc structure for the specified process.
896  */
897 void
898 fill_eproc(p, ep)
899 	register struct proc *p;
900 	register struct eproc *ep;
901 {
902 	register struct tty *tp;
903 
904 	ep->e_paddr = p;
905 	ep->e_sess = p->p_pgrp->pg_session;
906 	ep->e_pcred = *p->p_cred;
907 	ep->e_ucred = *p->p_ucred;
908 	if (p->p_stat == SIDL || P_ZOMBIE(p)) {
909 		ep->e_vm.vm_rssize = 0;
910 		ep->e_vm.vm_tsize = 0;
911 		ep->e_vm.vm_dsize = 0;
912 		ep->e_vm.vm_ssize = 0;
913 	} else {
914 		register struct vmspace *vm = p->p_vmspace;
915 
916 		ep->e_vm.vm_rssize = vm_resident_count(vm);
917 		ep->e_vm.vm_tsize = vm->vm_tsize;
918 		ep->e_vm.vm_dsize = vm->vm_dsize;
919 		ep->e_vm.vm_ssize = vm->vm_ssize;
920 	}
921 	if (p->p_pptr)
922 		ep->e_ppid = p->p_pptr->p_pid;
923 	else
924 		ep->e_ppid = 0;
925 	ep->e_pgid = p->p_pgrp->pg_id;
926 	ep->e_jobc = p->p_pgrp->pg_jobc;
927 	if ((p->p_flag & P_CONTROLT) &&
928 	     (tp = ep->e_sess->s_ttyp)) {
929 		ep->e_tdev = tp->t_dev;
930 		ep->e_tpgid = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID;
931 		ep->e_tsess = tp->t_session;
932 	} else
933 		ep->e_tdev = NODEV;
934 	ep->e_flag = ep->e_sess->s_ttyvp ? EPROC_CTTY : 0;
935 	if (SESS_LEADER(p))
936 		ep->e_flag |= EPROC_SLEADER;
937 	strncpy(ep->e_wmesg, p->p_wmesg ? p->p_wmesg : "", WMESGLEN);
938 	ep->e_wmesg[WMESGLEN] = '\0';
939 	ep->e_xsize = ep->e_xrssize = 0;
940 	ep->e_xccount = ep->e_xswrss = 0;
941 	strncpy(ep->e_login, ep->e_sess->s_login, MAXLOGNAME-1);
942 	ep->e_login[MAXLOGNAME-1] = '\0';
943 	strncpy(ep->e_emul, p->p_emul->e_name, EMULNAMELEN);
944 	ep->e_emul[EMULNAMELEN] = '\0';
945 	ep->e_maxrss = p->p_rlimit ? p->p_rlimit[RLIMIT_RSS].rlim_cur : 0;
946 }
947 
948 /*
949  * Initialize disknames/diskstats for export by sysctl. If update is set,
950  * then we simply update the disk statistics information.
951  */
952 int
953 sysctl_diskinit(update, p)
954 	int update;
955 	struct proc *p;
956 {
957 	struct diskstats *sdk;
958 	struct disk *dk;
959 	int i, tlen, l;
960 
961 	if ((i = lockmgr(&sysctl_disklock, LK_EXCLUSIVE, NULL, p)) != 0)
962 		return i;
963 
964 	if (disk_change) {
965 		for (dk = TAILQ_FIRST(&disklist), tlen = 0; dk;
966 		    dk = TAILQ_NEXT(dk, dk_link))
967 			tlen += strlen(dk->dk_name) + 1;
968 		tlen++;
969 
970 		if (disknames)
971 			free(disknames, M_SYSCTL);
972 		if (diskstats)
973 			free(diskstats, M_SYSCTL);
974 		diskstats = NULL;
975 		disknames = NULL;
976 		diskstats = malloc(disk_count * sizeof(struct diskstats),
977 		    M_SYSCTL, M_WAITOK);
978 		disknames = malloc(tlen, M_SYSCTL, M_WAITOK);
979 		disknames[0] = '\0';
980 
981 		for (dk = TAILQ_FIRST(&disklist), i = 0, l = 0; dk;
982 		    dk = TAILQ_NEXT(dk, dk_link), i++) {
983 			l += sprintf(disknames + l, "%s,",
984 			    dk->dk_name ? dk->dk_name : "");
985 			sdk = diskstats + i;
986 			sdk->ds_busy = dk->dk_busy;
987 			sdk->ds_xfer = dk->dk_xfer;
988 			sdk->ds_seek = dk->dk_seek;
989 			sdk->ds_bytes = dk->dk_bytes;
990 			sdk->ds_attachtime = dk->dk_attachtime;
991 			sdk->ds_timestamp = dk->dk_timestamp;
992 			sdk->ds_time = dk->dk_time;
993 		}
994 
995 		/* Eliminate trailing comma */
996 		if (l != 0)
997 			disknames[l - 1] = '\0';
998 		disk_change = 0;
999 	} else if (update) {
1000 		/* Just update, number of drives hasn't changed */
1001 		for (dk = TAILQ_FIRST(&disklist), i = 0; dk;
1002 		    dk = TAILQ_NEXT(dk, dk_link), i++) {
1003 			sdk = diskstats + i;
1004 			sdk->ds_busy = dk->dk_busy;
1005 			sdk->ds_xfer = dk->dk_xfer;
1006 			sdk->ds_seek = dk->dk_seek;
1007 			sdk->ds_bytes = dk->dk_bytes;
1008 			sdk->ds_attachtime = dk->dk_attachtime;
1009 			sdk->ds_timestamp = dk->dk_timestamp;
1010 			sdk->ds_time = dk->dk_time;
1011 		}
1012 	}
1013 	lockmgr(&sysctl_disklock, LK_RELEASE, NULL, p);
1014 	return 0;
1015 }
1016