1 /* $OpenBSD: kvm_proc2.c,v 1.39 2024/07/08 13:18:26 claudio 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
kvm_proclist(kvm_t * kd,int op,int arg,struct process * pr,char * bp,int maxcnt,size_t esize)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, &process.ps_tu, 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 &proc.p_tu, 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 *
kvm_getprocs(kvm_t * kd,int op,int arg,size_t esize,int * cnt)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