xref: /original-bsd/sys/kern/kern_sysctl.c (revision 02e832b2)
1 /*
2  * Copyright (c) 1982, 1986, 1989 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  *
7  *	@(#)kern_sysctl.c	7.25 (Berkeley) 10/11/92
8  */
9 
10 #include <sys/param.h>
11 #include <sys/proc.h>
12 #include <sys/kinfo.h>
13 #include <sys/ioctl.h>
14 #include <sys/tty.h>
15 #include <sys/buf.h>
16 #include <sys/file.h>
17 
18 #include <vm/vm.h>
19 
20 #include <sys/kinfo_proc.h>
21 
22 #define snderr(e) { error = (e); goto release;}
23 extern int kinfo_doproc(), kinfo_rtable(), kinfo_vnode(), kinfo_file();
24 extern int kinfo_meter(), kinfo_loadavg(), kinfo_clockrate();
25 struct kinfo_lock kinfo_lock;
26 
27 struct getkerninfo_args {
28 	int	op;
29 	char	*where;
30 	int	*size;
31 	int	arg;
32 };
33 /* ARGSUSED */
34 getkerninfo(p, uap, retval)
35 	struct proc *p;
36 	register struct getkerninfo_args *uap;
37 	int *retval;
38 {
39 	int bufsize;		/* max size of users buffer */
40 	int needed, locked, (*server)(), error = 0;
41 
42 	switch (ki_type(uap->op)) {
43 
44 	case KINFO_PROC:
45 		server = kinfo_doproc;
46 		break;
47 
48 	case KINFO_RT:
49 		server = kinfo_rtable;
50 		break;
51 
52 	case KINFO_VNODE:
53 		server = kinfo_vnode;
54 		break;
55 
56 	case KINFO_FILE:
57 		server = kinfo_file;
58 		break;
59 
60 	case KINFO_METER:
61 		server = kinfo_meter;
62 		break;
63 
64 	case KINFO_LOADAVG:
65 		server = kinfo_loadavg;
66 		break;
67 
68 	case KINFO_CLOCKRATE:
69 		server = kinfo_clockrate;
70 		break;
71 
72 	default:
73 		error = EINVAL;
74 		goto done;
75 	}
76 	if (uap->where == NULL || uap->size == NULL) {
77 		error = (*server)(uap->op, NULL, NULL, uap->arg, &needed);
78 		goto done;
79 	}
80 	if (error = copyin((caddr_t)uap->size, (caddr_t)&bufsize,
81 	    sizeof (bufsize)))
82 		goto done;
83 	while (kinfo_lock.kl_lock) {
84 		kinfo_lock.kl_want = 1;
85 		sleep((caddr_t)&kinfo_lock, PRIBIO+1);
86 		kinfo_lock.kl_locked++;
87 	}
88 	kinfo_lock.kl_lock = 1;
89 
90 	if (!useracc(uap->where, bufsize, B_WRITE))
91 		snderr(EFAULT);
92 	if (server != kinfo_vnode)	/* XXX */
93 		vslock(uap->where, bufsize);
94 	locked = bufsize;
95 	error = (*server)(uap->op, uap->where, &bufsize, uap->arg, &needed);
96 	if (server != kinfo_vnode)	/* XXX */
97 		vsunlock(uap->where, locked, B_WRITE);
98 	if (error == 0)
99 		error = copyout((caddr_t)&bufsize,
100 				(caddr_t)uap->size, sizeof (bufsize));
101 release:
102 	kinfo_lock.kl_lock = 0;
103 	if (kinfo_lock.kl_want) {
104 		kinfo_lock.kl_want = 0;
105 		wakeup((caddr_t)&kinfo_lock);
106 	}
107 done:
108 	if (!error)
109 		*retval = needed;
110 	return (error);
111 }
112 
113 /*
114  * try over estimating by 5 procs
115  */
116 #define KINFO_PROCSLOP	(5 * sizeof (struct kinfo_proc))
117 
118 kinfo_doproc(op, where, acopysize, arg, aneeded)
119 	int op;
120 	char *where;
121 	int *acopysize, arg, *aneeded;
122 {
123 	register struct proc *p;
124 	register struct kinfo_proc *dp = (struct kinfo_proc *)where;
125 	register needed = 0;
126 	int buflen = where != NULL ? *acopysize : 0;
127 	int doingzomb;
128 	struct eproc eproc;
129 	int error = 0;
130 
131 	p = (struct proc *)allproc;
132 	doingzomb = 0;
133 again:
134 	for (; p != NULL; p = p->p_nxt) {
135 		/*
136 		 * Skip embryonic processes.
137 		 */
138 		if (p->p_stat == SIDL)
139 			continue;
140 		/*
141 		 * TODO - make more efficient (see notes below).
142 		 * do by session.
143 		 */
144 		switch (ki_op(op)) {
145 
146 		case KINFO_PROC_PID:
147 			/* could do this with just a lookup */
148 			if (p->p_pid != (pid_t)arg)
149 				continue;
150 			break;
151 
152 		case KINFO_PROC_PGRP:
153 			/* could do this by traversing pgrp */
154 			if (p->p_pgrp->pg_id != (pid_t)arg)
155 				continue;
156 			break;
157 
158 		case KINFO_PROC_TTY:
159 			if ((p->p_flag&SCTTY) == 0 ||
160 			    p->p_session->s_ttyp == NULL ||
161 			    p->p_session->s_ttyp->t_dev != (dev_t)arg)
162 				continue;
163 			break;
164 
165 		case KINFO_PROC_UID:
166 			if (p->p_ucred->cr_uid != (uid_t)arg)
167 				continue;
168 			break;
169 
170 		case KINFO_PROC_RUID:
171 			if (p->p_cred->p_ruid != (uid_t)arg)
172 				continue;
173 			break;
174 		}
175 		if (buflen >= sizeof (struct kinfo_proc)) {
176 			fill_eproc(p, &eproc);
177 			if (error = copyout((caddr_t)p, &dp->kp_proc,
178 			    sizeof (struct proc)))
179 				return (error);
180 			if (error = copyout((caddr_t)&eproc, &dp->kp_eproc,
181 			    sizeof (eproc)))
182 				return (error);
183 			dp++;
184 			buflen -= sizeof (struct kinfo_proc);
185 		}
186 		needed += sizeof (struct kinfo_proc);
187 	}
188 	if (doingzomb == 0) {
189 		p = zombproc;
190 		doingzomb++;
191 		goto again;
192 	}
193 	if (where != NULL)
194 		*acopysize = (caddr_t)dp - where;
195 	else
196 		needed += KINFO_PROCSLOP;
197 	*aneeded = needed;
198 
199 	return (0);
200 }
201 
202 /*
203  * Fill in an eproc structure for the specified process.
204  */
205 void
206 fill_eproc(p, ep)
207 	register struct proc *p;
208 	register struct eproc *ep;
209 {
210 	register struct tty *tp;
211 
212 	ep->e_paddr = p;
213 	ep->e_sess = p->p_pgrp->pg_session;
214 	ep->e_pcred = *p->p_cred;
215 	ep->e_ucred = *p->p_ucred;
216 	if (p->p_stat == SIDL || p->p_stat == SZOMB) {
217 		ep->e_vm.vm_rssize = 0;
218 		ep->e_vm.vm_tsize = 0;
219 		ep->e_vm.vm_dsize = 0;
220 		ep->e_vm.vm_ssize = 0;
221 #ifndef sparc
222 		/* ep->e_vm.vm_pmap = XXX; */
223 #endif
224 	} else {
225 		register struct vmspace *vm = p->p_vmspace;
226 
227 		ep->e_vm.vm_rssize = vm->vm_rssize;
228 		ep->e_vm.vm_tsize = vm->vm_tsize;
229 		ep->e_vm.vm_dsize = vm->vm_dsize;
230 		ep->e_vm.vm_ssize = vm->vm_ssize;
231 #ifndef sparc
232 		ep->e_vm.vm_pmap = vm->vm_pmap;
233 #endif
234 	}
235 	if (p->p_pptr)
236 		ep->e_ppid = p->p_pptr->p_pid;
237 	else
238 		ep->e_ppid = 0;
239 	ep->e_pgid = p->p_pgrp->pg_id;
240 	ep->e_jobc = p->p_pgrp->pg_jobc;
241 	if ((p->p_flag&SCTTY) &&
242 	     (tp = ep->e_sess->s_ttyp)) {
243 		ep->e_tdev = tp->t_dev;
244 		ep->e_tpgid = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID;
245 		ep->e_tsess = tp->t_session;
246 	} else
247 		ep->e_tdev = NODEV;
248 	ep->e_flag = ep->e_sess->s_ttyvp ? EPROC_CTTY : 0;
249 	if (SESS_LEADER(p))
250 		ep->e_flag |= EPROC_SLEADER;
251 	if (p->p_wmesg)
252 		strncpy(ep->e_wmesg, p->p_wmesg, WMESGLEN);
253 	ep->e_xsize = ep->e_xrssize = 0;
254 	ep->e_xccount = ep->e_xswrss = 0;
255 }
256 
257 /*
258  * Get file structures.
259  */
260 kinfo_file(op, where, acopysize, arg, aneeded)
261 	int op;
262 	register char *where;
263 	int *acopysize, arg, *aneeded;
264 {
265 	int buflen, needed, error;
266 	struct file *fp;
267 	char *start = where;
268 
269 	if (where == NULL) {
270 		/*
271 		 * overestimate by 10 files
272 		 */
273 		*aneeded = sizeof (filehead) +
274 			(nfiles + 10) * sizeof (struct file);
275 		return (0);
276 	}
277 	buflen = *acopysize;
278 	needed = 0;
279 
280 	/*
281 	 * first copyout filehead
282 	 */
283 	if (buflen > sizeof (filehead)) {
284 		if (error = copyout((caddr_t)&filehead, where,
285 		    sizeof (filehead)))
286 			return (error);
287 		buflen -= sizeof (filehead);
288 		where += sizeof (filehead);
289 	}
290 	needed += sizeof (filehead);
291 
292 	/*
293 	 * followed by an array of file structures
294 	 */
295 	for (fp = filehead; fp != NULL; fp = fp->f_filef) {
296 		if (buflen > sizeof (struct file)) {
297 			if (error = copyout((caddr_t)fp, where,
298 			    sizeof (struct file)))
299 				return (error);
300 			buflen -= sizeof (struct file);
301 			where += sizeof (struct file);
302 		}
303 		needed += sizeof (struct file);
304 	}
305 	*acopysize = where - start;
306 	*aneeded = needed;
307 
308 	return (0);
309 }
310