1 /* $OpenBSD: machine.c,v 1.113 2023/01/07 05:24:59 guenther Exp $ */
2
3 /*-
4 * Copyright (c) 1994 Thorsten Lockert <tholo@sigmasoft.com>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * AUTHOR: Thorsten Lockert <tholo@sigmasoft.com>
30 * Adapted from BSD4.4 by Christos Zoulas <christos@ee.cornell.edu>
31 * Patch for process wait display by Jarl F. Greipsland <jarle@idt.unit.no>
32 * Patch for -DORDER by Kenneth Stailey <kstailey@disclosure.com>
33 * Patch for new swapctl(2) by Tobias Weingartner <weingart@openbsd.org>
34 */
35
36 #include <sys/param.h> /* DEV_BSIZE PZERO */
37 #include <sys/types.h>
38 #include <sys/signal.h>
39 #include <sys/mount.h>
40 #include <sys/proc.h>
41 #include <sys/sched.h>
42 #include <sys/swap.h>
43 #include <sys/sysctl.h>
44
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <err.h>
50 #include <errno.h>
51
52 #include "top.h"
53 #include "display.h"
54 #include "machine.h"
55 #include "utils.h"
56
57 static int swapmode(int *, int *);
58 static char *state_abbr(struct kinfo_proc *);
59 static char *format_comm(struct kinfo_proc *);
60 static int cmd_matches(struct kinfo_proc *, char *);
61 static char **get_proc_args(struct kinfo_proc *);
62
63 /* get_process_info passes back a handle. This is what it looks like: */
64
65 struct handle {
66 struct kinfo_proc **next_proc; /* points to next valid proc pointer */
67 };
68
69 /* what we consider to be process size: */
70 #define PROCSIZE(pp) ((pp)->p_vm_tsize + (pp)->p_vm_dsize + (pp)->p_vm_ssize)
71
72 /*
73 * These definitions control the format of the per-process area
74 */
75 static char header[] =
76 " PID X PRI NICE SIZE RES STATE WAIT TIME CPU COMMAND";
77
78 /* offsets in the header line to start alternative columns */
79 #define UNAME_START 6
80 #define RTABLE_START 46
81
82 #define Proc_format \
83 "%5d %-8.8s %3d %4d %5s %5s %-9s %-7.7s %6s %5.2f%% %s"
84
85 /* process state names for the "STATE" column of the display */
86 char *state_abbrev[] = {
87 "", "start", "run", "sleep", "stop", "zomb", "dead", "onproc"
88 };
89
90 /* these are for calculating cpu state percentages */
91 static struct cpustats *cp_time;
92 static struct cpustats *cp_old;
93 static struct cpustats *cp_diff;
94
95 /* these are for detailing the process states */
96 int process_states[8];
97 char *procstatenames[] = {
98 "", " starting, ", " running, ", " idle, ",
99 " stopped, ", " zombie, ", " dead, ", " on processor, ",
100 NULL
101 };
102
103 /* these are for detailing the cpu states */
104 int64_t *cpu_states;
105 char *cpustatenames[] = {
106 "user", "nice", "sys", "spin", "intr", "idle", NULL
107 };
108
109 /* this is for tracking which cpus are online */
110 int *cpu_online;
111
112 /* these are for detailing the memory statistics */
113 int memory_stats[10];
114 char *memorynames[] = {
115 "Real: ", "K/", "K act/tot ", "Free: ", "K ",
116 "Cache: ", "K ",
117 "Swap: ", "K/", "K",
118 NULL
119 };
120
121 /* these are names given to allowed sorting orders -- first is default */
122 char *ordernames[] = {
123 "cpu", "size", "res", "time", "pri", "pid", "command", NULL
124 };
125
126 /* these are for keeping track of the proc array */
127 static int nproc;
128 static int onproc = -1;
129 static int pref_len;
130 static struct kinfo_proc *pbase;
131 static struct kinfo_proc **pref;
132
133 /* these are for getting the memory statistics */
134 static int pageshift; /* log base 2 of the pagesize */
135
136 /* define pagetok in terms of pageshift */
137 #define pagetok(size) ((size) << pageshift)
138
139 int ncpu;
140 int ncpuonline;
141 int fscale;
142
143 unsigned int maxslp;
144
145 int
getfscale(void)146 getfscale(void)
147 {
148 int mib[] = { CTL_KERN, KERN_FSCALE };
149 size_t size = sizeof(fscale);
150
151 if (sysctl(mib, sizeof(mib) / sizeof(mib[0]),
152 &fscale, &size, NULL, 0) == -1)
153 return (-1);
154 return fscale;
155 }
156
157 int
getncpu(void)158 getncpu(void)
159 {
160 int mib[] = { CTL_HW, HW_NCPU };
161 int numcpu;
162 size_t size = sizeof(numcpu);
163
164 if (sysctl(mib, sizeof(mib) / sizeof(mib[0]),
165 &numcpu, &size, NULL, 0) == -1)
166 return (-1);
167
168 return (numcpu);
169 }
170
171 int
getncpuonline(void)172 getncpuonline(void)
173 {
174 int mib[] = { CTL_HW, HW_NCPUONLINE };
175 int numcpu;
176 size_t size = sizeof(numcpu);
177
178 if (sysctl(mib, sizeof(mib) / sizeof(mib[0]),
179 &numcpu, &size, NULL, 0) == -1)
180 return (-1);
181
182 return (numcpu);
183 }
184
185 int
machine_init(struct statics * statics)186 machine_init(struct statics *statics)
187 {
188 int pagesize;
189
190 ncpu = getncpu();
191 if (ncpu == -1)
192 return (-1);
193 if (getfscale() == -1)
194 return (-1);
195 cpu_states = calloc(ncpu, CPUSTATES * sizeof(int64_t));
196 if (cpu_states == NULL)
197 err(1, NULL);
198 cp_time = calloc(ncpu, sizeof(*cp_time));
199 cp_old = calloc(ncpu, sizeof(*cp_old));
200 cp_diff = calloc(ncpu, sizeof(*cp_diff));
201 if (cp_time == NULL || cp_old == NULL || cp_diff == NULL)
202 err(1, NULL);
203 cpu_online = calloc(ncpu, sizeof(*cpu_online));
204 if (cpu_online == NULL)
205 err(1, NULL);
206
207 /*
208 * get the page size with "getpagesize" and calculate pageshift from
209 * it
210 */
211 pagesize = getpagesize();
212 pageshift = 0;
213 while (pagesize > 1) {
214 pageshift++;
215 pagesize >>= 1;
216 }
217
218 /* we only need the amount of log(2)1024 for our conversion */
219 pageshift -= LOG1024;
220
221 /* fill in the statics information */
222 statics->procstate_names = procstatenames;
223 statics->cpustate_names = cpustatenames;
224 statics->memory_names = memorynames;
225 statics->order_names = ordernames;
226 return (0);
227 }
228
229 char *
format_header(char * second_field,char * eighth_field)230 format_header(char *second_field, char *eighth_field)
231 {
232 char *second_fieldp = second_field, *eighth_fieldp = eighth_field, *ptr;
233
234 ptr = header + UNAME_START;
235 while (*second_fieldp != '\0')
236 *ptr++ = *second_fieldp++;
237 ptr = header + RTABLE_START;
238 while (*eighth_fieldp != '\0')
239 *ptr++ = *eighth_fieldp++;
240 return (header);
241 }
242
243 void
get_system_info(struct system_info * si)244 get_system_info(struct system_info *si)
245 {
246 static int cpustats_mib[] = {CTL_KERN, KERN_CPUSTATS, /*fillme*/0};
247 static int sysload_mib[] = {CTL_VM, VM_LOADAVG};
248 static int uvmexp_mib[] = {CTL_VM, VM_UVMEXP};
249 static int bcstats_mib[] = {CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT};
250 struct loadavg sysload;
251 struct uvmexp uvmexp;
252 struct bcachestats bcstats;
253 double *infoloadp;
254 size_t size;
255 int i;
256 int64_t *tmpstate;
257
258 size = sizeof(*cp_time);
259 for (i = 0; i < ncpu; i++) {
260 cpustats_mib[2] = i;
261 tmpstate = cpu_states + (CPUSTATES * i);
262 if (sysctl(cpustats_mib, 3, &cp_time[i], &size, NULL, 0) == -1)
263 warn("sysctl kern.cpustats failed");
264 /* convert cpustats counts to percentages */
265 (void) percentages(CPUSTATES, tmpstate, cp_time[i].cs_time,
266 cp_old[i].cs_time, cp_diff[i].cs_time);
267 /* note whether the cpu is online */
268 cpu_online[i] = (cp_time[i].cs_flags & CPUSTATS_ONLINE) != 0;
269 }
270
271 size = sizeof(sysload);
272 if (sysctl(sysload_mib, 2, &sysload, &size, NULL, 0) == -1)
273 warn("sysctl failed");
274 infoloadp = si->load_avg;
275 for (i = 0; i < 3; i++)
276 *infoloadp++ = ((double) sysload.ldavg[i]) / sysload.fscale;
277
278
279 /* get total -- systemwide main memory usage structure */
280 size = sizeof(uvmexp);
281 if (sysctl(uvmexp_mib, 2, &uvmexp, &size, NULL, 0) == -1) {
282 warn("sysctl failed");
283 bzero(&uvmexp, sizeof(uvmexp));
284 }
285 size = sizeof(bcstats);
286 if (sysctl(bcstats_mib, 3, &bcstats, &size, NULL, 0) == -1) {
287 warn("sysctl failed");
288 bzero(&bcstats, sizeof(bcstats));
289 }
290 /* convert memory stats to Kbytes */
291 memory_stats[0] = -1;
292 memory_stats[1] = pagetok(uvmexp.active);
293 memory_stats[2] = pagetok(uvmexp.npages - uvmexp.free);
294 memory_stats[3] = -1;
295 memory_stats[4] = pagetok(uvmexp.free);
296 memory_stats[5] = -1;
297 memory_stats[6] = pagetok(bcstats.numbufpages);
298 memory_stats[7] = -1;
299
300 if (!swapmode(&memory_stats[8], &memory_stats[9])) {
301 memory_stats[8] = 0;
302 memory_stats[9] = 0;
303 }
304
305 /* set arrays and strings */
306 si->cpustates = cpu_states;
307 si->cpuonline = cpu_online;
308 si->memory = memory_stats;
309 }
310
311 static struct handle handle;
312
313 struct kinfo_proc *
getprocs(int op,int arg,int * cnt)314 getprocs(int op, int arg, int *cnt)
315 {
316 size_t size;
317 int mib[6] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0,
318 sizeof(struct kinfo_proc), 0};
319 static int maxslp_mib[] = {CTL_VM, VM_MAXSLP};
320 static struct kinfo_proc *procbase;
321 int st;
322
323 mib[2] = op;
324 mib[3] = arg;
325
326 size = sizeof(maxslp);
327 if (sysctl(maxslp_mib, 2, &maxslp, &size, NULL, 0) == -1) {
328 warn("sysctl vm.maxslp failed");
329 return (0);
330 }
331 retry:
332 free(procbase);
333 st = sysctl(mib, 6, NULL, &size, NULL, 0);
334 if (st == -1) {
335 /* _kvm_syserr(kd, kd->program, "kvm_getprocs"); */
336 return (0);
337 }
338 size = 5 * size / 4; /* extra slop */
339 if ((procbase = malloc(size)) == NULL)
340 return (0);
341 mib[5] = (int)(size / sizeof(struct kinfo_proc));
342 st = sysctl(mib, 6, procbase, &size, NULL, 0);
343 if (st == -1) {
344 if (errno == ENOMEM)
345 goto retry;
346 /* _kvm_syserr(kd, kd->program, "kvm_getprocs"); */
347 return (0);
348 }
349 *cnt = (int)(size / sizeof(struct kinfo_proc));
350 return (procbase);
351 }
352
353 static char **
get_proc_args(struct kinfo_proc * kp)354 get_proc_args(struct kinfo_proc *kp)
355 {
356 static char **s;
357 static size_t siz = 1023;
358 int mib[4];
359
360 if (!s && !(s = malloc(siz)))
361 err(1, NULL);
362
363 mib[0] = CTL_KERN;
364 mib[1] = KERN_PROC_ARGS;
365 mib[2] = kp->p_pid;
366 mib[3] = KERN_PROC_ARGV;
367 for (;;) {
368 size_t space = siz;
369 if (sysctl(mib, 4, s, &space, NULL, 0) == 0)
370 break;
371 if (errno != ENOMEM)
372 return NULL;
373 siz *= 2;
374 if ((s = realloc(s, siz)) == NULL)
375 err(1, NULL);
376 }
377 return s;
378 }
379
380 static int
cmd_matches(struct kinfo_proc * proc,char * term)381 cmd_matches(struct kinfo_proc *proc, char *term)
382 {
383 extern int show_args;
384 char **args = NULL;
385
386 if (!term) {
387 /* No command filter set */
388 return 1;
389 } else {
390 /* Filter set, process name needs to contain term */
391 if (strstr(proc->p_comm, term))
392 return 1;
393 /* If thread name set, search that too */
394 if (strstr(proc->p_name, term))
395 return 1;
396 /* If showing arguments, search those as well */
397 if (show_args) {
398 args = get_proc_args(proc);
399
400 if (args == NULL) {
401 /* Failed to get args, so can't search them */
402 return 0;
403 }
404
405 while (*args != NULL) {
406 if (strstr(*args, term))
407 return 1;
408 args++;
409 }
410 }
411 }
412 return 0;
413 }
414
415 struct handle *
get_process_info(struct system_info * si,struct process_select * sel,int (* compare)(const void *,const void *))416 get_process_info(struct system_info *si, struct process_select *sel,
417 int (*compare) (const void *, const void *))
418 {
419 int show_idle, show_system, show_threads, show_uid, show_pid, show_cmd;
420 int show_rtableid, hide_rtableid, hide_uid;
421 int total_procs, active_procs;
422 struct kinfo_proc **prefp, *pp;
423 int what = KERN_PROC_ALL;
424
425 show_system = sel->system;
426 show_threads = sel->threads;
427
428 if (show_system)
429 what = KERN_PROC_KTHREAD;
430 if (show_threads)
431 what |= KERN_PROC_SHOW_THREADS;
432
433 if ((pbase = getprocs(what, 0, &nproc)) == NULL) {
434 /* warnx("%s", kvm_geterr(kd)); */
435 quit(23);
436 }
437 if (nproc > onproc)
438 pref = reallocarray(pref, (onproc = nproc),
439 sizeof(struct kinfo_proc *));
440 if (pref == NULL) {
441 warnx("Out of memory.");
442 quit(23);
443 }
444 /* get a pointer to the states summary array */
445 si->procstates = process_states;
446
447 /* set up flags which define what we are going to select */
448 show_idle = sel->idle;
449 show_uid = sel->uid != (uid_t)-1;
450 hide_uid = sel->huid != (uid_t)-1;
451 show_pid = sel->pid != (pid_t)-1;
452 show_rtableid = sel->rtableid != -1;
453 hide_rtableid = sel->hrtableid != -1;
454 show_cmd = sel->command != NULL;
455
456 /* count up process states and get pointers to interesting procs */
457 total_procs = 0;
458 active_procs = 0;
459 memset((char *) process_states, 0, sizeof(process_states));
460 prefp = pref;
461 for (pp = pbase; pp < &pbase[nproc]; pp++) {
462 /*
463 * When showing threads, we want to ignore the structure
464 * that represents the entire process, which has TID == -1
465 */
466 if (show_threads && pp->p_tid == -1)
467 continue;
468 /*
469 * Place pointers to each valid proc structure in pref[].
470 * Process slots that are actually in use have a non-zero
471 * status field.
472 */
473 if (pp->p_stat != 0) {
474 total_procs++;
475 process_states[(unsigned char) pp->p_stat]++;
476 if ((pp->p_psflags & PS_ZOMBIE) == 0 &&
477 (show_idle || pp->p_pctcpu != 0 ||
478 pp->p_stat == SRUN) &&
479 (!hide_uid || pp->p_ruid != sel->huid) &&
480 (!show_uid || pp->p_ruid == sel->uid) &&
481 (!show_pid || pp->p_pid == sel->pid) &&
482 (!hide_rtableid || pp->p_rtableid != sel->hrtableid) &&
483 (!show_rtableid || pp->p_rtableid == sel->rtableid) &&
484 (!show_cmd || cmd_matches(pp, sel->command))) {
485 *prefp++ = pp;
486 active_procs++;
487 }
488 }
489 }
490
491 qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *), compare);
492 /* remember active and total counts */
493 si->p_total = total_procs;
494 si->p_active = pref_len = active_procs;
495
496 /* pass back a handle */
497 handle.next_proc = pref;
498 return &handle;
499 }
500
501 char fmt[MAX_COLS]; /* static area where result is built */
502
503 static char *
state_abbr(struct kinfo_proc * pp)504 state_abbr(struct kinfo_proc *pp)
505 {
506 static char buf[10];
507
508 if (ncpu > 1 && pp->p_cpuid != KI_NOCPU)
509 snprintf(buf, sizeof buf, "%s/%llu",
510 state_abbrev[(unsigned char)pp->p_stat], pp->p_cpuid);
511 else
512 snprintf(buf, sizeof buf, "%s",
513 state_abbrev[(unsigned char)pp->p_stat]);
514 return buf;
515 }
516
517 static char *
format_comm(struct kinfo_proc * kp)518 format_comm(struct kinfo_proc *kp)
519 {
520 static char buf[MAX_COLS];
521 char **p, **s;
522 extern int show_args;
523
524 if (show_args && (s = get_proc_args(kp)) != NULL) {
525 buf[0] = '\0';
526 for (p = s; *p != NULL; p++) {
527 if (p != s)
528 strlcat(buf, " ", sizeof(buf));
529 strlcat(buf, *p, sizeof(buf));
530 }
531 if (buf[0] != '\0')
532 return buf;
533 }
534 if (kp->p_name[0] != '\0') {
535 snprintf(buf, sizeof buf, "%s/%s", kp->p_comm,
536 kp->p_name);
537 return buf;
538 }
539 return kp->p_comm;
540 }
541
542 void
skip_processes(struct handle * hndl,int n)543 skip_processes(struct handle *hndl, int n)
544 {
545 hndl->next_proc += n;
546 }
547
548 char *
format_next_process(struct handle * hndl,const char * (* get_userid)(uid_t,int),int rtable,pid_t * pid)549 format_next_process(struct handle *hndl, const char *(*get_userid)(uid_t, int),
550 int rtable, pid_t *pid)
551 {
552 struct kinfo_proc *pp;
553 int cputime;
554 double pct;
555 char second_buf[16], eighth_buf[8];
556
557 /* find and remember the next proc structure */
558 pp = *(hndl->next_proc++);
559
560 cputime = pp->p_rtime_sec + ((pp->p_rtime_usec + 500000) / 1000000);
561
562 /* calculate the base for cpu percentages */
563 pct = (double)pp->p_pctcpu / fscale;
564
565 if (get_userid == NULL)
566 snprintf(second_buf, sizeof(second_buf), "%8d", pp->p_tid);
567 else
568 strlcpy(second_buf, (*get_userid)(pp->p_ruid, 0),
569 sizeof(second_buf));
570
571 if (rtable)
572 snprintf(eighth_buf, sizeof(eighth_buf), "%7d", pp->p_rtableid);
573 else
574 strlcpy(eighth_buf, pp->p_wmesg[0] ? pp->p_wmesg : "-",
575 sizeof(eighth_buf));
576
577 /* format this entry */
578 snprintf(fmt, sizeof(fmt), Proc_format, pp->p_pid, second_buf,
579 pp->p_priority - PZERO, pp->p_nice - NZERO,
580 format_k(pagetok(PROCSIZE(pp))),
581 format_k(pagetok(pp->p_vm_rssize)),
582 (pp->p_stat == SSLEEP && pp->p_slptime > maxslp) ?
583 "idle" : state_abbr(pp),
584 eighth_buf, format_time(cputime), 100.0 * pct,
585 printable(format_comm(pp)));
586
587 *pid = pp->p_pid;
588 /* return the result */
589 return (fmt);
590 }
591
592 /* comparison routine for qsort */
593 static unsigned char sorted_state[] =
594 {
595 0, /* not used */
596 4, /* start */
597 5, /* run */
598 2, /* sleep */
599 3, /* stop */
600 1 /* zombie */
601 };
602
603 extern int rev_order;
604
605 /*
606 * proc_compares - comparison functions for "qsort"
607 */
608
609 /*
610 * First, the possible comparison keys. These are defined in such a way
611 * that they can be merely listed in the source code to define the actual
612 * desired ordering.
613 */
614
615 #define ORDERKEY_PCTCPU \
616 if ((result = (int)(p2->p_pctcpu - p1->p_pctcpu)) == 0)
617 #define ORDERKEY_CPUTIME \
618 if ((result = p2->p_rtime_sec - p1->p_rtime_sec) == 0) \
619 if ((result = p2->p_rtime_usec - p1->p_rtime_usec) == 0)
620 #define ORDERKEY_STATE \
621 if ((result = sorted_state[(unsigned char)p2->p_stat] - \
622 sorted_state[(unsigned char)p1->p_stat]) == 0)
623 #define ORDERKEY_PRIO \
624 if ((result = p2->p_priority - p1->p_priority) == 0)
625 #define ORDERKEY_RSSIZE \
626 if ((result = p2->p_vm_rssize - p1->p_vm_rssize) == 0)
627 #define ORDERKEY_MEM \
628 if ((result = PROCSIZE(p2) - PROCSIZE(p1)) == 0)
629 #define ORDERKEY_PID \
630 if ((result = p1->p_pid - p2->p_pid) == 0)
631 #define ORDERKEY_CMD \
632 if ((result = strcmp(p1->p_comm, p2->p_comm)) == 0)
633
634 /* remove one level of indirection and set sort order */
635 #define SETORDER do { \
636 if (rev_order) { \
637 p1 = *(struct kinfo_proc **) v2; \
638 p2 = *(struct kinfo_proc **) v1; \
639 } else { \
640 p1 = *(struct kinfo_proc **) v1; \
641 p2 = *(struct kinfo_proc **) v2; \
642 } \
643 } while (0)
644
645 /* compare_cpu - the comparison function for sorting by cpu percentage */
646 static int
compare_cpu(const void * v1,const void * v2)647 compare_cpu(const void *v1, const void *v2)
648 {
649 struct kinfo_proc *p1, *p2;
650 int result;
651
652 SETORDER;
653
654 ORDERKEY_PCTCPU
655 ORDERKEY_CPUTIME
656 ORDERKEY_STATE
657 ORDERKEY_PRIO
658 ORDERKEY_RSSIZE
659 ORDERKEY_MEM
660 ;
661 return (result);
662 }
663
664 /* compare_size - the comparison function for sorting by total memory usage */
665 static int
compare_size(const void * v1,const void * v2)666 compare_size(const void *v1, const void *v2)
667 {
668 struct kinfo_proc *p1, *p2;
669 int result;
670
671 SETORDER;
672
673 ORDERKEY_MEM
674 ORDERKEY_RSSIZE
675 ORDERKEY_PCTCPU
676 ORDERKEY_CPUTIME
677 ORDERKEY_STATE
678 ORDERKEY_PRIO
679 ;
680 return (result);
681 }
682
683 /* compare_res - the comparison function for sorting by resident set size */
684 static int
compare_res(const void * v1,const void * v2)685 compare_res(const void *v1, const void *v2)
686 {
687 struct kinfo_proc *p1, *p2;
688 int result;
689
690 SETORDER;
691
692 ORDERKEY_RSSIZE
693 ORDERKEY_MEM
694 ORDERKEY_PCTCPU
695 ORDERKEY_CPUTIME
696 ORDERKEY_STATE
697 ORDERKEY_PRIO
698 ;
699 return (result);
700 }
701
702 /* compare_time - the comparison function for sorting by CPU time */
703 static int
compare_time(const void * v1,const void * v2)704 compare_time(const void *v1, const void *v2)
705 {
706 struct kinfo_proc *p1, *p2;
707 int result;
708
709 SETORDER;
710
711 ORDERKEY_CPUTIME
712 ORDERKEY_PCTCPU
713 ORDERKEY_STATE
714 ORDERKEY_PRIO
715 ORDERKEY_MEM
716 ORDERKEY_RSSIZE
717 ;
718 return (result);
719 }
720
721 /* compare_prio - the comparison function for sorting by CPU time */
722 static int
compare_prio(const void * v1,const void * v2)723 compare_prio(const void *v1, const void *v2)
724 {
725 struct kinfo_proc *p1, *p2;
726 int result;
727
728 SETORDER;
729
730 ORDERKEY_PRIO
731 ORDERKEY_PCTCPU
732 ORDERKEY_CPUTIME
733 ORDERKEY_STATE
734 ORDERKEY_RSSIZE
735 ORDERKEY_MEM
736 ;
737 return (result);
738 }
739
740 static int
compare_pid(const void * v1,const void * v2)741 compare_pid(const void *v1, const void *v2)
742 {
743 struct kinfo_proc *p1, *p2;
744 int result;
745
746 SETORDER;
747
748 ORDERKEY_PID
749 ORDERKEY_PCTCPU
750 ORDERKEY_CPUTIME
751 ORDERKEY_STATE
752 ORDERKEY_PRIO
753 ORDERKEY_RSSIZE
754 ORDERKEY_MEM
755 ;
756 return (result);
757 }
758
759 static int
compare_cmd(const void * v1,const void * v2)760 compare_cmd(const void *v1, const void *v2)
761 {
762 struct kinfo_proc *p1, *p2;
763 int result;
764
765 SETORDER;
766
767 ORDERKEY_CMD
768 ORDERKEY_PCTCPU
769 ORDERKEY_CPUTIME
770 ORDERKEY_STATE
771 ORDERKEY_PRIO
772 ORDERKEY_RSSIZE
773 ORDERKEY_MEM
774 ;
775 return (result);
776 }
777
778
779 int (*proc_compares[])(const void *, const void *) = {
780 compare_cpu,
781 compare_size,
782 compare_res,
783 compare_time,
784 compare_prio,
785 compare_pid,
786 compare_cmd,
787 NULL
788 };
789
790 /*
791 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
792 * the process does not exist.
793 * It is EXTREMELY IMPORTANT that this function work correctly.
794 * If top runs setuid root (as in SVR4), then this function
795 * is the only thing that stands in the way of a serious
796 * security problem. It validates requests for the "kill"
797 * and "renice" commands.
798 */
799 uid_t
proc_owner(pid_t pid)800 proc_owner(pid_t pid)
801 {
802 struct kinfo_proc **prefp, *pp;
803 int cnt;
804
805 prefp = pref;
806 cnt = pref_len;
807 while (--cnt >= 0) {
808 pp = *prefp++;
809 if (pp->p_pid == pid)
810 return ((uid_t)pp->p_ruid);
811 }
812 return (uid_t)(-1);
813 }
814
815 /*
816 * swapmode is rewritten by Tobias Weingartner <weingart@openbsd.org>
817 * to be based on the new swapctl(2) system call.
818 */
819 static int
swapmode(int * used,int * total)820 swapmode(int *used, int *total)
821 {
822 struct swapent *swdev;
823 int nswap, rnswap, i;
824
825 nswap = swapctl(SWAP_NSWAP, 0, 0);
826 if (nswap == 0)
827 return 0;
828
829 swdev = calloc(nswap, sizeof(*swdev));
830 if (swdev == NULL)
831 return 0;
832
833 rnswap = swapctl(SWAP_STATS, swdev, nswap);
834 if (rnswap == -1) {
835 free(swdev);
836 return 0;
837 }
838
839 /* if rnswap != nswap, then what? */
840
841 /* Total things up */
842 *total = *used = 0;
843 for (i = 0; i < nswap; i++) {
844 if (swdev[i].se_flags & SWF_ENABLE) {
845 *used += (swdev[i].se_inuse / (1024 / DEV_BSIZE));
846 *total += (swdev[i].se_nblks / (1024 / DEV_BSIZE));
847 }
848 }
849 free(swdev);
850 return 1;
851 }
852