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