xref: /openbsd/lib/libkvm/kvm_proc2.c (revision 4bdff4be)
1 /*	$OpenBSD: kvm_proc2.c,v 1.38 2022/02/22 17:35:01 deraadt Exp $	*/
2 /*	$NetBSD: kvm_proc.c,v 1.30 1999/03/24 05:50:50 mrg Exp $	*/
3 /*-
4  * Copyright (c) 1998 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Charles M. Hannum.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 /*-
32  * Copyright (c) 1994, 1995 Charles M. Hannum.  All rights reserved.
33  * Copyright (c) 1989, 1992, 1993
34  *	The Regents of the University of California.  All rights reserved.
35  *
36  * This code is derived from software developed by the Computer Systems
37  * Engineering group at Lawrence Berkeley Laboratory under DARPA contract
38  * BG 91-66 and contributed to Berkeley.
39  *
40  * Redistribution and use in source and binary forms, with or without
41  * modification, are permitted provided that the following conditions
42  * are met:
43  * 1. Redistributions of source code must retain the above copyright
44  *    notice, this list of conditions and the following disclaimer.
45  * 2. Redistributions in binary form must reproduce the above copyright
46  *    notice, this list of conditions and the following disclaimer in the
47  *    documentation and/or other materials provided with the distribution.
48  * 3. Neither the name of the University nor the names of its contributors
49  *    may be used to endorse or promote products derived from this software
50  *    without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62  * SUCH DAMAGE.
63  */
64 
65 /*
66  * Proc traversal interface for kvm.  ps and w are (probably) the exclusive
67  * users of this code, so we've factored it out into a separate module.
68  * Thus, we keep this grunge out of the other kvm applications (i.e.,
69  * most other applications are interested only in open/close/read/nlist).
70  */
71 
72 #define __need_process
73 #include <sys/param.h>	/* NODEV */
74 #include <sys/types.h>
75 #include <sys/signal.h>
76 #include <sys/proc.h>
77 #include <sys/exec.h>
78 #include <sys/stat.h>
79 #include <sys/ucred.h>
80 #include <sys/ioctl.h>
81 #include <sys/tty.h>
82 #include <sys/resource.h>
83 #include <sys/resourcevar.h>
84 #include <sys/signalvar.h>
85 #include <sys/pledge.h>
86 #include <sys/wait.h>
87 #include <stddef.h>
88 #include <stdlib.h>
89 #include <string.h>
90 #include <unistd.h>
91 #include <nlist.h>
92 #include <kvm.h>
93 
94 #include <uvm/uvm_extern.h>
95 #include <uvm/uvm_amap.h>
96 #include <machine/vmparam.h>
97 #include <machine/pmap.h>
98 
99 #include <sys/sysctl.h>
100 
101 #include <limits.h>
102 #include <errno.h>
103 #include <db.h>
104 #include <paths.h>
105 
106 #include "kvm_private.h"
107 
108 /*
109  * Read proc's from memory file into buffer bp, which has space to hold
110  * at most maxcnt procs.
111  */
112 static int
113 kvm_proclist(kvm_t *kd, int op, int arg, struct process *pr,
114     char *bp, int maxcnt, size_t esize)
115 {
116 	struct kinfo_proc kp;
117 	struct session sess;
118 	struct ucred ucred;
119 	struct proc proc, *p;
120 	struct process process, process2;
121 	struct pgrp pgrp;
122 	struct tty tty;
123 	struct timeval elapsed, monostart, monostop, realstart, realstop;
124 	struct nlist nl[3];
125 	struct sigacts sa, *sap;
126 	struct vmspace vm, *vmp;
127 	struct plimit limits, *limp;
128 	pid_t parent_pid, leader_pid;
129 	int cnt = 0;
130 	int dothreads = 0;
131 	int i;
132 
133 	dothreads = op & KERN_PROC_SHOW_THREADS;
134 	op &= ~KERN_PROC_SHOW_THREADS;
135 
136 	/* Anchor a time to compare process starting times from. */
137 	nl[0].n_name = "_time_second";
138 	nl[1].n_name = "_time_uptime";
139 	nl[2].n_name = NULL;
140 	if (kvm_nlist(kd, nl) != 0) {
141 		for (i = 0; nl[i].n_type != 0; ++i)
142 			continue;
143 		_kvm_err(kd, kd->program, "%s: no such symbol", nl[i].n_name);
144 		return (-1);
145 	}
146 	timerclear(&realstop);
147 	timerclear(&monostop);
148 	if (KREAD(kd, nl[0].n_value, &realstop.tv_sec)) {
149 		_kvm_err(kd, kd->program, "cannot read time_second");
150 		return (-1);
151 	}
152 	if (KREAD(kd, nl[1].n_value, &monostop.tv_sec)) {
153 		_kvm_err(kd, kd->program, "cannot read time_uptime");
154 		return (-1);
155 	}
156 
157 	/*
158 	 * Modelled on sysctl_doproc() in sys/kern/kern_sysctl.c
159 	 */
160 	for (; cnt < maxcnt && pr != NULL; pr = LIST_NEXT(&process, ps_list)) {
161 		if (KREAD(kd, (u_long)pr, &process)) {
162 			_kvm_err(kd, kd->program, "can't read process at %lx",
163 			    (u_long)pr);
164 			return (-1);
165 		}
166 		if (process.ps_pgrp == NULL)
167 			continue;
168 		if (process.ps_flags & PS_EMBRYO)
169 			continue;
170 		if (KREAD(kd, (u_long)process.ps_ucred, &ucred)) {
171 			_kvm_err(kd, kd->program, "can't read ucred at %lx",
172 			    (u_long)process.ps_ucred);
173 			return (-1);
174 		}
175 		if (KREAD(kd, (u_long)process.ps_pgrp, &pgrp)) {
176 			_kvm_err(kd, kd->program, "can't read pgrp at %lx",
177 			    (u_long)process.ps_pgrp);
178 			return (-1);
179 		}
180 		if (KREAD(kd, (u_long)pgrp.pg_session, &sess)) {
181 			_kvm_err(kd, kd->program, "can't read session at %lx",
182 			    (u_long)pgrp.pg_session);
183 			return (-1);
184 		}
185 		if ((process.ps_flags & PS_CONTROLT) && sess.s_ttyp != NULL &&
186 		    KREAD(kd, (u_long)sess.s_ttyp, &tty)) {
187 			_kvm_err(kd, kd->program, "can't read tty at %lx",
188 			    (u_long)sess.s_ttyp);
189 			return (-1);
190 		}
191 		if (process.ps_pptr) {
192 			if (KREAD(kd, (u_long)process.ps_pptr, &process2)) {
193 				_kvm_err(kd, kd->program,
194 				    "can't read process at %lx",
195 				    (u_long)process.ps_pptr);
196 				return (-1);
197 			}
198 			parent_pid = process2.ps_pid;
199 		}
200 		else
201 			parent_pid = 0;
202 		if (sess.s_leader) {
203 			if (KREAD(kd, (u_long)sess.s_leader, &process2)) {
204 				_kvm_err(kd, kd->program,
205 				    "can't read proc at %lx",
206 				    (u_long)sess.s_leader);
207 				return (-1);
208 			}
209 			leader_pid = process2.ps_pid;
210 		}
211 		else
212 			leader_pid = 0;
213 		if (process.ps_sigacts) {
214 			if (KREAD(kd, (u_long)process.ps_sigacts, &sa)) {
215 				_kvm_err(kd, kd->program,
216 				    "can't read sigacts at %lx",
217 				    (u_long)process.ps_sigacts);
218 				return (-1);
219 			}
220 			sap = &sa;
221 		}
222 		else
223 			sap = NULL;
224 
225 		switch (op) {
226 		case KERN_PROC_PID:
227 			if (process.ps_pid != (pid_t)arg)
228 				continue;
229 			break;
230 
231 		case KERN_PROC_PGRP:
232 			if (pgrp.pg_id != (pid_t)arg)
233 				continue;
234 			break;
235 
236 		case KERN_PROC_SESSION:
237 			if (sess.s_leader == NULL ||
238 			    leader_pid != (pid_t)arg)
239 				continue;
240 			break;
241 
242 		case KERN_PROC_TTY:
243 			if ((process.ps_flags & PS_CONTROLT) == 0 ||
244 			    sess.s_ttyp == NULL ||
245 			    tty.t_dev != (dev_t)arg)
246 				continue;
247 			break;
248 
249 		case KERN_PROC_UID:
250 			if (ucred.cr_uid != (uid_t)arg)
251 				continue;
252 			break;
253 
254 		case KERN_PROC_RUID:
255 			if (ucred.cr_ruid != (uid_t)arg)
256 				continue;
257 			break;
258 
259 		case KERN_PROC_ALL:
260 			if (process.ps_flags & PS_SYSTEM)
261 				continue;
262 			break;
263 
264 		case KERN_PROC_KTHREAD:
265 			/* no filtering */
266 			break;
267 
268 		default:
269 			_kvm_err(kd, kd->program, "invalid filter");
270 			return (-1);
271 		}
272 
273 		/*
274 		 * We're going to add another proc to the set.  If this
275 		 * will overflow the buffer, assume the reason is because
276 		 * nthreads (or the proc list) is corrupt and declare an error.
277 		 */
278 		if (cnt >= maxcnt) {
279 			_kvm_err(kd, kd->program, "nthreads corrupt");
280 			return (-1);
281 		}
282 
283 		/* set up stuff that might not always be there */
284 		limp = &limits;
285 		if (!process.ps_limit ||
286 		    KREAD(kd, (u_long)process.ps_limit, &limits))
287 			limp = NULL;
288 
289 		vmp = NULL;
290 
291 		if ((process.ps_flags & PS_ZOMBIE) == 0 &&
292 		    !KREAD(kd, (u_long)process.ps_vmspace, &vm))
293 			vmp = &vm;
294 
295 #define do_copy_str(_d, _s, _l)	kvm_read(kd, (u_long)(_s), (_d), (_l)-1)
296 		FILL_KPROC(&kp, do_copy_str, &proc, &process,
297 		    &ucred, &pgrp, process.ps_mainproc, pr, &sess,
298 		    vmp, limp, sap, 0, 1);
299 
300 		/* stuff that's too painful to generalize */
301 		kp.p_ppid = parent_pid;
302 		kp.p_sid = leader_pid;
303 		if ((process.ps_flags & PS_CONTROLT) && sess.s_ttyp != NULL) {
304 			kp.p_tdev = tty.t_dev;
305 			if (tty.t_pgrp != NULL &&
306 			    tty.t_pgrp != process.ps_pgrp &&
307 			    KREAD(kd, (u_long)tty.t_pgrp, &pgrp)) {
308 				_kvm_err(kd, kd->program,
309 				    "can't read tpgrp at %lx",
310 				    (u_long)tty.t_pgrp);
311 				return (-1);
312 			}
313 			kp.p_tpgid = tty.t_pgrp ? pgrp.pg_id : -1;
314 			kp.p_tsess = PTRTOINT64(tty.t_session);
315 		} else {
316 			kp.p_tpgid = -1;
317 			kp.p_tdev = NODEV;
318 		}
319 
320 		/* Convert the starting uptime to a starting UTC time. */
321 		if ((process.ps_flags & PS_ZOMBIE) == 0) {
322 			monostart.tv_sec = kp.p_ustart_sec;
323 			monostart.tv_usec = kp.p_ustart_usec;
324 			timersub(&monostop, &monostart, &elapsed);
325 			if (elapsed.tv_sec < 0)
326 				timerclear(&elapsed);
327 			timersub(&realstop, &elapsed, &realstart);
328 			kp.p_ustart_sec = realstart.tv_sec;
329 			kp.p_ustart_usec = realstart.tv_usec;
330 		}
331 
332 		/* update %cpu for all threads */
333 		if (dothreads) {
334 			if (KREAD(kd, (u_long)process.ps_mainproc, &proc)) {
335 				_kvm_err(kd, kd->program,
336 				    "can't read proc at %lx",
337 				    (u_long)process.ps_mainproc);
338 				return (-1);
339 			}
340 			kp.p_pctcpu = proc.p_pctcpu;
341 			kp.p_stat   = proc.p_stat;
342 		} else {
343 			kp.p_pctcpu = 0;
344 			kp.p_stat = (process.ps_flags & PS_ZOMBIE) ? SDEAD :
345 			    SIDL;
346 			for (p = TAILQ_FIRST(&process.ps_threads); p != NULL;
347 			    p = TAILQ_NEXT(&proc, p_thr_link)) {
348 				if (KREAD(kd, (u_long)p, &proc)) {
349 					_kvm_err(kd, kd->program,
350 					    "can't read proc at %lx",
351 					    (u_long)p);
352 					return (-1);
353 				}
354 				kp.p_pctcpu += proc.p_pctcpu;
355 				/*
356 				 * find best state:
357 				 * ONPROC > RUN > STOP > SLEEP > ...
358 				 */
359 				if (proc.p_stat == SONPROC ||
360 				    kp.p_stat == SONPROC)
361 					kp.p_stat = SONPROC;
362 				else if (proc.p_stat == SRUN ||
363 				    kp.p_stat == SRUN)
364 					kp.p_stat = SRUN;
365 				else if (proc.p_stat == SSTOP ||
366 				    kp.p_stat == SSTOP)
367 					kp.p_stat = SSTOP;
368 				else if (proc.p_stat == SSLEEP)
369 					kp.p_stat = SSLEEP;
370 			}
371                 }
372 
373 		memcpy(bp, &kp, esize);
374 		bp += esize;
375 		++cnt;
376 
377 		/* Skip per-thread entries if not required by op */
378 		if (!dothreads)
379 			continue;
380 
381 		for (p = TAILQ_FIRST(&process.ps_threads); p != NULL;
382 		    p = TAILQ_NEXT(&proc, p_thr_link)) {
383 			if (KREAD(kd, (u_long)p, &proc)) {
384 				_kvm_err(kd, kd->program,
385 				    "can't read proc at %lx",
386 				    (u_long)p);
387 				return (-1);
388 			}
389 			FILL_KPROC(&kp, do_copy_str, &proc, &process,
390 			    &ucred, &pgrp, p, pr, &sess, vmp, limp, sap,
391 			    1, 1);
392 
393 			/* see above */
394 			kp.p_ppid = parent_pid;
395 			kp.p_sid = leader_pid;
396 			if ((process.ps_flags & PS_CONTROLT) &&
397 			    sess.s_ttyp != NULL) {
398 				kp.p_tdev = tty.t_dev;
399 				if (tty.t_pgrp != NULL &&
400 				    tty.t_pgrp != process.ps_pgrp &&
401 				    KREAD(kd, (u_long)tty.t_pgrp, &pgrp)) {
402 					_kvm_err(kd, kd->program,
403 					    "can't read tpgrp at %lx",
404 					    (u_long)tty.t_pgrp);
405 					return (-1);
406 				}
407 				kp.p_tpgid = tty.t_pgrp ? pgrp.pg_id : -1;
408 				kp.p_tsess = PTRTOINT64(tty.t_session);
409 			} else {
410 				kp.p_tpgid = -1;
411 				kp.p_tdev = NODEV;
412 			}
413 		}
414 
415 		memcpy(bp, &kp, esize);
416 		bp += esize;
417 		++cnt;
418 #undef do_copy_str
419 	}
420 	return (cnt);
421 }
422 
423 struct kinfo_proc *
424 kvm_getprocs(kvm_t *kd, int op, int arg, size_t esize, int *cnt)
425 {
426 	int mib[6], st, nthreads;
427 	void *procbase;
428 	size_t size;
429 
430 	if ((ssize_t)esize < 0)
431 		return (NULL);
432 
433 	if (ISALIVE(kd)) {
434 		size = 0;
435 		mib[0] = CTL_KERN;
436 		mib[1] = KERN_PROC;
437 		mib[2] = op;
438 		mib[3] = arg;
439 		mib[4] = esize;
440 
441 		do {
442 			mib[5] = 0;
443 			st = sysctl(mib, 6, NULL, &size, NULL, 0);
444 			if (st == -1) {
445 				_kvm_syserr(kd, kd->program, "kvm_getprocs");
446 				return (NULL);
447 			}
448 
449 			size += size / 8; /* add ~10% */
450 
451 			procbase = _kvm_realloc(kd, kd->procbase, size);
452 			if (procbase == NULL)
453 				return (NULL);
454 
455 			kd->procbase = procbase;
456 
457 			mib[5] = size / esize;
458 			st = sysctl(mib, 6, kd->procbase, &size, NULL, 0);
459 			if (st == -1 && errno != ENOMEM) {
460 				_kvm_syserr(kd, kd->program, "kvm_getprocs");
461 				return (NULL);
462 			}
463 		} while (st == -1);
464 
465 		nthreads = size / esize;
466 	} else {
467 		struct nlist nl[5];
468 		int i, maxthread, maxprocess;
469 		struct process *pr;
470 		char *bp;
471 
472 		if (esize > sizeof(struct kinfo_proc)) {
473 			_kvm_syserr(kd, kd->program,
474 			    "kvm_getprocs: unknown fields requested: libkvm out of date?");
475 			return (NULL);
476 		}
477 
478 		memset(nl, 0, sizeof(nl));
479 		nl[0].n_name = "_nthreads";
480 		nl[1].n_name = "_nprocesses";
481 		nl[2].n_name = "_allprocess";
482 		nl[3].n_name = "_zombprocess";
483 		nl[4].n_name = NULL;
484 
485 		if (kvm_nlist(kd, nl) != 0) {
486 			for (i = 0; nl[i].n_type != 0; ++i)
487 				;
488 			_kvm_err(kd, kd->program,
489 			    "%s: no such symbol", nl[i].n_name);
490 			return (NULL);
491 		}
492 		if (KREAD(kd, nl[0].n_value, &maxthread)) {
493 			_kvm_err(kd, kd->program, "can't read nthreads");
494 			return (NULL);
495 		}
496 		if (KREAD(kd, nl[1].n_value, &maxprocess)) {
497 			_kvm_err(kd, kd->program, "can't read nprocesses");
498 			return (NULL);
499 		}
500 		maxthread += maxprocess;
501 
502 		kd->procbase = _kvm_reallocarray(kd, NULL, maxthread, esize);
503 		if (kd->procbase == 0)
504 			return (NULL);
505 		bp = (char *)kd->procbase;
506 
507 		/* allprocess */
508 		if (KREAD(kd, nl[2].n_value, &pr)) {
509 			_kvm_err(kd, kd->program, "cannot read allprocess");
510 			return (NULL);
511 		}
512 		nthreads = kvm_proclist(kd, op, arg, pr, bp, maxthread, esize);
513 		if (nthreads < 0)
514 			return (NULL);
515 
516 		/* zombprocess */
517 		if (KREAD(kd, nl[3].n_value, &pr)) {
518 			_kvm_err(kd, kd->program, "cannot read zombprocess");
519 			return (NULL);
520 		}
521 		i = kvm_proclist(kd, op, arg, pr, bp + (esize * nthreads),
522 		    maxthread - nthreads, esize);
523 		if (i > 0)
524 			nthreads += i;
525 	}
526 	if (kd->procbase != NULL)
527 		*cnt = nthreads;
528 	return (kd->procbase);
529 }
530