machine.c revision 1.13
11558Srgrimes/*	$OpenBSD: machine.c,v 1.13 1998/09/20 06:19:14 niklas Exp $	*/
21558Srgrimes
31558Srgrimes/*
41558Srgrimes * top - a top users display for Unix
51558Srgrimes *
61558Srgrimes * SYNOPSIS:  For an OpenBSD system
71558Srgrimes *
81558Srgrimes * DESCRIPTION:
91558Srgrimes * This is the machine-dependent module for OpenBSD
101558Srgrimes * Tested on:
111558Srgrimes *	i386
121558Srgrimes *
131558Srgrimes * LIBS: -lkvm
141558Srgrimes *
151558Srgrimes * TERMCAP: -ltermlib
161558Srgrimes *
171558Srgrimes * CFLAGS: -DHAVE_GETOPT -DORDER
181558Srgrimes *
191558Srgrimes * AUTHOR:  Thorsten Lockert <tholo@sigmasoft.com>
201558Srgrimes *          Adapted from BSD4.4 by Christos Zoulas <christos@ee.cornell.edu>
211558Srgrimes *          Patch for process wait display by Jarl F. Greipsland <jarle@idt.unit.no>
221558Srgrimes *	    Patch for -DORDER by Kenneth Stailey <kstailey@disclosure.com>
231558Srgrimes */
241558Srgrimes
251558Srgrimes#include <sys/types.h>
261558Srgrimes#include <sys/signal.h>
271558Srgrimes#include <sys/param.h>
281558Srgrimes
291558Srgrimes#define DOSWAP
301558Srgrimes
311558Srgrimes#include <stdio.h>
321558Srgrimes#include <stdlib.h>
331558Srgrimes#include <string.h>
341558Srgrimes#include <limits.h>
351558Srgrimes#include <err.h>
361558Srgrimes#include <nlist.h>
371558Srgrimes#include <math.h>
381558Srgrimes#include <kvm.h>
391558Srgrimes#include <unistd.h>
401558Srgrimes#include <sys/errno.h>
411558Srgrimes#include <sys/sysctl.h>
421558Srgrimes#include <sys/dir.h>
431558Srgrimes#include <sys/dkstat.h>
4415770Swollman#include <sys/file.h>
451558Srgrimes#include <sys/time.h>
4615770Swollman#include <sys/resource.h>
4715770Swollman
4818286Sbde#ifdef DOSWAP
491558Srgrimes#include <err.h>
501558Srgrimes#include <sys/map.h>
511558Srgrimes#include <sys/conf.h>
521558Srgrimes#endif
531558Srgrimes
541558Srgrimesstatic int check_nlist __P((struct nlist *));
551558Srgrimesstatic int getkval __P((unsigned long, int *, int, char *));
561558Srgrimesstatic int swapmode __P((int *, int *));
571558Srgrimes
581558Srgrimes#include "top.h"
591558Srgrimes#include "display.h"
601558Srgrimes#include "machine.h"
611558Srgrimes#include "utils.h"
621558Srgrimes
631558Srgrimes/* get_process_info passes back a handle.  This is what it looks like: */
641558Srgrimes
651558Srgrimesstruct handle
669336Sdfr{
6714024Smarkm    struct kinfo_proc **next_proc;	/* points to next valid proc pointer */
681558Srgrimes    int remaining;		/* number of pointers remaining */
691558Srgrimes};
701558Srgrimes
711558Srgrimes/* declarations for load_avg */
729336Sdfr#include "loadavg.h"
739336Sdfr
741558Srgrimes#define PP(pp, field) ((pp)->kp_proc . field)
759336Sdfr#define EP(pp, field) ((pp)->kp_eproc . field)
761558Srgrimes#define VP(pp, field) ((pp)->kp_eproc.e_vm . field)
771558Srgrimes
781558Srgrimes/* what we consider to be process size: */
791558Srgrimes#define PROCSIZE(pp) (VP((pp), vm_tsize) + VP((pp), vm_dsize) + VP((pp), vm_ssize))
801558Srgrimes
811558Srgrimes/* definitions for indices in the nlist array */
821558Srgrimes#define X_CP_TIME	0
831558Srgrimes#define X_HZ		1
841558Srgrimes
851558Srgrimes#ifdef DOSWAP
861558Srgrimes#define	VM_SWAPMAP	2
871558Srgrimes#define	VM_NSWAPMAP	3
881558Srgrimes#define	VM_SWDEVT	4
8915770Swollman#define	VM_NSWAP	5
901558Srgrimes#define	VM_NSWDEV	6
911558Srgrimes#define	VM_DMMAX	7
921558Srgrimes#define	VM_NISWAP	8
931558Srgrimes#define	VM_NISWDEV	9
949336Sdfr#endif
954065Swollman
964065Swollmanstatic struct nlist nlst[] = {
974065Swollman    { "_cp_time" },		/* 0 */
984065Swollman    { "_hz" },			/* 1 */
994065Swollman#ifdef DOSWAP
1009336Sdfr    { "_swapmap" },		/* 2 */
1019336Sdfr    { "_nswapmap" },		/* 3 */
1029336Sdfr    { "_swdevt" },		/* 4 */
1034065Swollman    { "_nswap" },		/* 5 */
1044065Swollman    { "_nswdev" },		/* 6 */
1054065Swollman    { "_dmmax" },		/* 7 */
1064065Swollman    { "_niswap" },		/* 8 */
1074065Swollman    { "_niswdev" },		/* 9 */
1089230Skarl#endif
1094065Swollman    { 0 }
1101558Srgrimes};
1111558Srgrimes
1121558Srgrimes/*
1131558Srgrimes *  These definitions control the format of the per-process area
1144065Swollman */
1154065Swollman
1164065Swollmanstatic char header[] =
1174065Swollman  "  PID X        PRI NICE  SIZE   RES STATE WAIT     TIME    CPU COMMAND";
1189336Sdfr/* 0123456   -- field to fill in starts at header+6 */
1194065Swollman#define UNAME_START 6
1204065Swollman
1219336Sdfr#define Proc_format \
1229336Sdfr	"%5d %-8.8s %3d %4d %5s %5s %-5s %-6.6s %6s %5.2f%% %.14s"
1239336Sdfr
1244065Swollman
1254065Swollman/* process state names for the "STATE" column of the display */
1264065Swollman/* the extra nulls in the string "run" are for adding a slash and
1274065Swollman   the processor number when needed */
1284065Swollman
1294065Swollmanchar *state_abbrev[] =
1304065Swollman{
1319230Skarl    "", "start", "run\0\0\0", "sleep", "stop", "zomb",
1321558Srgrimes};
1331558Srgrimes
1349336Sdfr
1359336Sdfrstatic kvm_t *kd;
1369336Sdfr
1379336Sdfr/* these are retrieved from the kernel in _init */
1389336Sdfr
1399336Sdfrstatic          int hz;
1409336Sdfr
1419336Sdfr/* these are offsets obtained via nlist and used in the get_ functions */
1421558Srgrimes
1431558Srgrimesstatic unsigned long cp_time_offset;
1441558Srgrimes
1451558Srgrimes/* these are for calculating cpu state percentages */
1461558Srgrimesstatic long cp_time[CPUSTATES];
1471558Srgrimesstatic long cp_old[CPUSTATES];
1489336Sdfrstatic long cp_diff[CPUSTATES];
1491558Srgrimes
1509336Sdfr/* these are for detailing the process states */
1511558Srgrimes
1521558Srgrimesint process_states[7];
1539336Sdfrchar *procstatenames[] = {
1549336Sdfr    "", " starting, ", " running, ", " idle, ", " stopped, ", " zombie, ",
1551558Srgrimes    NULL
1561558Srgrimes};
1571558Srgrimes
1581558Srgrimes/* these are for detailing the cpu states */
1591558Srgrimes
1601558Srgrimesint cpu_states[CPUSTATES];
1611558Srgrimeschar *cpustatenames[] = {
1621558Srgrimes    "user", "nice", "system", "interrupt", "idle", NULL
1631558Srgrimes};
1649336Sdfr
1659336Sdfr/* these are for detailing the memory statistics */
1669336Sdfr
1679336Sdfrint memory_stats[8];
1689336Sdfrchar *memorynames[] = {
1691558Srgrimes    "Real: ", "K/", "K act/tot  ", "Free: ", "K  ",
1701558Srgrimes#ifdef DOSWAP
1711558Srgrimes    "Swap: ", "K/", "K used/tot",
1721558Srgrimes#endif
1731558Srgrimes    NULL
1741558Srgrimes};
1759336Sdfr
1769336Sdfr#ifdef ORDER
1779230Skarl/* these are names given to allowed sorting orders -- first is default */
1781558Srgrimes
1799336Sdfrchar *ordernames[] = {"cpu", "size", "res", "time", "pri", NULL};
1801558Srgrimes#endif
1811558Srgrimes
1829336Sdfr/* these are for keeping track of the proc array */
1839336Sdfr
1849336Sdfrstatic int nproc;
1859336Sdfrstatic int onproc = -1;
1869336Sdfrstatic int pref_len;
1879336Sdfrstatic struct kinfo_proc *pbase;
1889336Sdfrstatic struct kinfo_proc **pref;
1899336Sdfr
1909336Sdfr/* these are for getting the memory statistics */
1919336Sdfr
1921558Srgrimesstatic int pageshift;		/* log base 2 of the pagesize */
1931558Srgrimes
1941558Srgrimes/* define pagetok in terms of pageshift */
1951558Srgrimes
1961558Srgrimes#define pagetok(size) ((size) << pageshift)
1971558Srgrimes
1981558Srgrimesint
19918286Sbdemachine_init(statics)
2001558Srgrimes
2011558Srgrimesstruct statics *statics;
2021558Srgrimes
2031558Srgrimes{
2041558Srgrimes    register int i = 0;
2051558Srgrimes    register int pagesize;
2061558Srgrimes    char errbuf[_POSIX2_LINE_MAX];
2071558Srgrimes
2081558Srgrimes    if ((kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf)) == NULL) {
2091558Srgrimes	warnx("%s", errbuf);
2101558Srgrimes	return(-1);
2111558Srgrimes    }
2124065Swollman
2131558Srgrimes    setegid(getgid());
2142999Swollman    setgid(getgid());
2159336Sdfr
2161558Srgrimes    /* get the list of symbols we want to access in the kernel */
2171558Srgrimes    if (kvm_nlist(kd, nlst) <= 0) {
2181558Srgrimes	warnx("nlist failed");
2191558Srgrimes	return(-1);
2209336Sdfr    }
2219336Sdfr
2229336Sdfr    /* make sure they were all found */
2239336Sdfr    if (i > 0 && check_nlist(nlst) > 0)
2249336Sdfr	return(-1);
2259336Sdfr
2261558Srgrimes    /* get the symbol values out of kmem */
2271558Srgrimes    (void) getkval(nlst[X_HZ].n_value,     (int *)(&hz),	sizeof(hz),
2281558Srgrimes	    nlst[X_HZ].n_name);
2294065Swollman
2301558Srgrimes    /* stash away certain offsets for later use */
2311558Srgrimes    cp_time_offset = nlst[X_CP_TIME].n_value;
2321558Srgrimes
2339336Sdfr    pbase = NULL;
2341558Srgrimes    pref = NULL;
2359336Sdfr    onproc = -1;
2369336Sdfr    nproc = 0;
2379336Sdfr
2381558Srgrimes    /* get the page size with "getpagesize" and calculate pageshift from it */
2391558Srgrimes    pagesize = getpagesize();
2401558Srgrimes    pageshift = 0;
2411558Srgrimes    while (pagesize > 1)
2421558Srgrimes    {
2431558Srgrimes	pageshift++;
2441558Srgrimes	pagesize >>= 1;
2451558Srgrimes    }
2461558Srgrimes
2471558Srgrimes    /* we only need the amount of log(2)1024 for our conversion */
2481558Srgrimes    pageshift -= LOG1024;
2491558Srgrimes
2501558Srgrimes    /* fill in the statics information */
2511558Srgrimes    statics->procstate_names = procstatenames;
2521558Srgrimes    statics->cpustate_names = cpustatenames;
2531558Srgrimes    statics->memory_names = memorynames;
2541558Srgrimes#ifdef ORDER
2551558Srgrimes    statics->order_names = ordernames;
2561558Srgrimes#endif
2571558Srgrimes
2581558Srgrimes    /* all done! */
2591558Srgrimes    return(0);
2601558Srgrimes}
2611558Srgrimes
2621558Srgrimeschar *format_header(uname_field)
2631558Srgrimes
2641558Srgrimesregister char *uname_field;
2659336Sdfr
2661558Srgrimes{
2679336Sdfr    register char *ptr;
2681558Srgrimes
2691558Srgrimes    ptr = header + UNAME_START;
2701558Srgrimes    while (*uname_field != '\0')
2719336Sdfr    {
2729336Sdfr	*ptr++ = *uname_field++;
2739336Sdfr    }
2749336Sdfr
2759336Sdfr    return(header);
2769336Sdfr}
2779336Sdfr
2781558Srgrimesvoid
2791558Srgrimesget_system_info(si)
2801558Srgrimes
2819336Sdfrstruct system_info *si;
2821558Srgrimes
2831558Srgrimes{
2841558Srgrimes    int total;
2851558Srgrimes
2861558Srgrimes    /* get the cp_time array */
2871558Srgrimes    (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time),
2881558Srgrimes		   "_cp_time");
2891558Srgrimes
2901558Srgrimes    /* convert load averages to doubles */
2911558Srgrimes    {
2921558Srgrimes	register int i;
2931558Srgrimes	register double *infoloadp;
2949336Sdfr	struct loadavg sysload;
2951558Srgrimes	size_t size = sizeof(sysload);
2969336Sdfr	static int mib[] = { CTL_VM, VM_LOADAVG };
2971558Srgrimes
2981558Srgrimes	if (sysctl(mib, 2, &sysload, &size, NULL, 0) < 0) {
2991558Srgrimes	    warn("sysctl failed");
3001558Srgrimes	    bzero(&total, sizeof(total));
3011558Srgrimes	}
3021558Srgrimes
3039336Sdfr	infoloadp = si->load_avg;
3044065Swollman	for (i = 0; i < 3; i++)
3054065Swollman	    *infoloadp++ = ((double) sysload.ldavg[i]) / sysload.fscale;
3064065Swollman    }
3074065Swollman
3084065Swollman    /* convert cp_time counts to percentages */
3094065Swollman    total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
3104065Swollman
3114065Swollman    /* sum memory statistics */
3124065Swollman    {
3139336Sdfr	struct vmtotal total;
3144065Swollman	size_t size = sizeof(total);
3154065Swollman	static int mib[] = { CTL_VM, VM_METER };
3164065Swollman
3179336Sdfr	/* get total -- systemwide main memory usage structure */
3189336Sdfr	if (sysctl(mib, 2, &total, &size, NULL, 0) < 0) {
3199336Sdfr	    warn("sysctl failed");
3209336Sdfr	    bzero(&total, sizeof(total));
3219336Sdfr	}
3229336Sdfr	/* convert memory stats to Kbytes */
3234065Swollman	memory_stats[0] = -1;
3244065Swollman	memory_stats[1] = pagetok(total.t_arm);
3254065Swollman	memory_stats[2] = pagetok(total.t_rm);
3264065Swollman	memory_stats[3] = -1;
3274065Swollman	memory_stats[4] = pagetok(total.t_free);
3284065Swollman	memory_stats[5] = -1;
3294065Swollman#ifdef DOSWAP
3309336Sdfr	if (!swapmode(&memory_stats[6], &memory_stats[7])) {
3314065Swollman	    memory_stats[6] = 0;
3324065Swollman	    memory_stats[7] = 0;
3339336Sdfr	}
3344065Swollman#endif
3359336Sdfr    }
3369336Sdfr
3379230Skarl    /* set arrays and strings */
3389230Skarl    si->cpustates = cpu_states;
3394065Swollman    si->memory = memory_stats;
3409336Sdfr    si->last_pid = -1;
3419336Sdfr}
3429336Sdfr
3431558Srgrimesstatic struct handle handle;
3441558Srgrimes
3451558Srgrimescaddr_t get_process_info(si, sel, compare)
3461558Srgrimes
3471558Srgrimesstruct system_info *si;
3481558Srgrimesstruct process_select *sel;
3491558Srgrimesint (*compare) __P((const void *, const void *));
3501558Srgrimes
3511558Srgrimes{
3521558Srgrimes    register int i;
3539336Sdfr    register int total_procs;
3541558Srgrimes    register int active_procs;
3551558Srgrimes    register struct kinfo_proc **prefp;
3561558Srgrimes    register struct kinfo_proc *pp;
3571558Srgrimes
3581558Srgrimes    /* these are copied out of sel for speed */
3591558Srgrimes    int show_idle;
3601558Srgrimes    int show_system;
3611558Srgrimes    int show_uid;
3621558Srgrimes    int show_command;
3631558Srgrimes
3641558Srgrimes
3651558Srgrimes    if ((pbase = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc)) == NULL) {
3661558Srgrimes	warnx("%s", kvm_geterr(kd));
3671558Srgrimes	quit(23);
3681558Srgrimes    }
3691558Srgrimes    if (nproc > onproc)
3701558Srgrimes	pref = (struct kinfo_proc **) realloc(pref, sizeof(struct kinfo_proc *)
3711558Srgrimes		* (onproc = nproc));
3721558Srgrimes    if (pref == NULL) {
3739336Sdfr	warnx("Out of memory.");
3741558Srgrimes	quit(23);
3751558Srgrimes    }
3761558Srgrimes    /* get a pointer to the states summary array */
3771558Srgrimes    si->procstates = process_states;
3781558Srgrimes
3791558Srgrimes    /* set up flags which define what we are going to select */
3801558Srgrimes    show_idle = sel->idle;
3811558Srgrimes    show_system = sel->system;
3821558Srgrimes    show_uid = sel->uid != -1;
3831558Srgrimes    show_command = sel->command != NULL;
3841558Srgrimes
3851558Srgrimes    /* count up process states and get pointers to interesting procs */
3861558Srgrimes    total_procs = 0;
3871558Srgrimes    active_procs = 0;
3881558Srgrimes    memset((char *)process_states, 0, sizeof(process_states));
3891558Srgrimes    prefp = pref;
3901558Srgrimes    for (pp = pbase, i = 0; i < nproc; pp++, i++)
3911558Srgrimes    {
3921558Srgrimes	/*
3931558Srgrimes	 *  Place pointers to each valid proc structure in pref[].
3941558Srgrimes	 *  Process slots that are actually in use have a non-zero
3951558Srgrimes	 *  status field.  Processes with SSYS set are system
3969336Sdfr	 *  processes---these get ignored unless show_sysprocs is set.
3979336Sdfr	 */
3989336Sdfr	if (PP(pp, p_stat) != 0 &&
3991558Srgrimes	    (show_system || ((PP(pp, p_flag) & P_SYSTEM) == 0)))
4001558Srgrimes	{
4011558Srgrimes	    total_procs++;
4021558Srgrimes	    process_states[(unsigned char) PP(pp, p_stat)]++;
4031558Srgrimes	    if ((PP(pp, p_stat) != SZOMB) &&
4041558Srgrimes		(show_idle || (PP(pp, p_pctcpu) != 0) ||
4051558Srgrimes		 (PP(pp, p_stat) == SRUN)) &&
4061558Srgrimes		(!show_uid || EP(pp, e_pcred.p_ruid) == (uid_t)sel->uid))
4075966Sdg	    {
4081558Srgrimes		*prefp++ = pp;
4091558Srgrimes		active_procs++;
4101558Srgrimes	    }
4111558Srgrimes	}
4121558Srgrimes    }
4131558Srgrimes
4142999Swollman    /* if requested, sort the "interesting" processes */
4159336Sdfr    if (compare != NULL)
4162999Swollman    {
4172999Swollman	qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *), compare);
4182999Swollman    }
41915770Swollman
4202999Swollman    /* remember active and total counts */
4212999Swollman    si->p_total = total_procs;
4222999Swollman    si->p_active = pref_len = active_procs;
42315770Swollman
42415770Swollman    /* pass back a handle */
4252999Swollman    handle.next_proc = pref;
42615770Swollman    handle.remaining = active_procs;
4279336Sdfr    return((caddr_t)&handle);
4289336Sdfr}
4299336Sdfr
4301558Srgrimeschar fmt[MAX_COLS];		/* static area where result is built */
4311558Srgrimes
4321558Srgrimeschar *format_next_process(handle, get_userid)
4331558Srgrimes
4341558Srgrimescaddr_t handle;
4351558Srgrimeschar *(*get_userid)();
4361558Srgrimes
4371558Srgrimes{
4381558Srgrimes    register struct kinfo_proc *pp;
4391558Srgrimes    register int cputime;
4401558Srgrimes    register double pct;
4411558Srgrimes    struct handle *hp;
4421558Srgrimes    char waddr[sizeof(void *) * 2 + 3];	/* Hexify void pointer */
4431558Srgrimes    char *p_wait;
4441558Srgrimes
4451558Srgrimes    /* find and remember the next proc structure */
4461558Srgrimes    hp = (struct handle *)handle;
4471558Srgrimes    pp = *(hp->next_proc++);
4481558Srgrimes    hp->remaining--;
4491558Srgrimes
4501558Srgrimes
4511558Srgrimes    /* get the process's user struct and set cputime */
4521558Srgrimes    if ((PP(pp, p_flag) & P_INMEM) == 0) {
4531558Srgrimes	/*
4549336Sdfr	 * Print swapped processes as <pname>
4551558Srgrimes	 */
4561558Srgrimes	char *comm = PP(pp, p_comm);
4571558Srgrimes#define COMSIZ sizeof(PP(pp, p_comm))
4581558Srgrimes	char buf[COMSIZ];
4591558Srgrimes	(void) strncpy(buf, comm, COMSIZ);
4601558Srgrimes	comm[0] = '<';
4619336Sdfr	(void) strncpy(&comm[1], buf, COMSIZ - 2);
4629336Sdfr	comm[COMSIZ - 2] = '\0';
4639336Sdfr	(void) strncat(comm, ">", COMSIZ - 1);
4649336Sdfr	comm[COMSIZ - 1] = '\0';
4659336Sdfr    }
4661558Srgrimes
4671558Srgrimes    cputime = (PP(pp, p_uticks) + PP(pp, p_sticks) + PP(pp, p_iticks)) / hz;
4686043Sdfr
4696043Sdfr    /* calculate the base for cpu percentages */
4706043Sdfr    pct = pctdouble(PP(pp, p_pctcpu));
4716043Sdfr
4721558Srgrimes    if (PP(pp, p_wchan))
4731558Srgrimes        if (PP(pp, p_wmesg))
4749336Sdfr	    p_wait = EP(pp, e_wmesg);
4759336Sdfr	else {
4769336Sdfr	    snprintf(waddr, sizeof(waddr), "%lx",
4779336Sdfr		(unsigned long)(PP(pp, p_wchan)) & ~KERNBASE);
4789336Sdfr	    p_wait = waddr;
4799336Sdfr        }
4809336Sdfr    else
4819336Sdfr	p_wait = "-";
4821558Srgrimes
4839336Sdfr    /* format this entry */
4849336Sdfr    snprintf(fmt, MAX_COLS,
4859336Sdfr	    Proc_format,
4869336Sdfr	    PP(pp, p_pid),
4879336Sdfr	    (*get_userid)(EP(pp, e_pcred.p_ruid)),
4889336Sdfr	    PP(pp, p_priority) - PZERO,
4899336Sdfr	    PP(pp, p_nice) - NZERO,
4909336Sdfr	    format_k(pagetok(PROCSIZE(pp))),
4919336Sdfr	    format_k(pagetok(VP(pp, vm_rssize))),
4929336Sdfr	    (PP(pp, p_stat) == SSLEEP && PP(pp, p_slptime) > MAXSLP)
4939336Sdfr	     ? "idle" : state_abbrev[(unsigned char) PP(pp, p_stat)],
4949336Sdfr	    p_wait,
4959336Sdfr	    format_time(cputime),
4969336Sdfr	    100.0 * pct,
4979336Sdfr	    printable(PP(pp, p_comm)));
4989336Sdfr
4999336Sdfr    /* return the result */
5009336Sdfr    return(fmt);
5019336Sdfr}
5029336Sdfr
5039336Sdfr
5049336Sdfr/*
5059336Sdfr * check_nlist(nlst) - checks the nlist to see if any symbols were not
5069336Sdfr *		found.  For every symbol that was not found, a one-line
5079336Sdfr *		message is printed to stderr.  The routine returns the
5089336Sdfr *		number of symbols NOT found.
5099336Sdfr */
5109336Sdfr
5119336Sdfrstatic int check_nlist(nlst)
5129336Sdfr
5139336Sdfrregister struct nlist *nlst;
5149336Sdfr
5159336Sdfr{
5169336Sdfr    register int i;
5179336Sdfr
5189336Sdfr    /* check to see if we got ALL the symbols we requested */
5199336Sdfr    /* this will write one line to stderr for every symbol not found */
5209336Sdfr
5219336Sdfr    i = 0;
5229336Sdfr    while (nlst->n_name != NULL)
5239336Sdfr    {
5249336Sdfr	if (nlst->n_type == 0)
5259336Sdfr	{
5269336Sdfr	    /* this one wasn't found */
5279336Sdfr	    (void) fprintf(stderr, "kernel: no symbol named `%s'\n",
5289336Sdfr			   nlst->n_name);
5299336Sdfr	    i = 1;
5301558Srgrimes	}
5311558Srgrimes	nlst++;
5321558Srgrimes    }
5331558Srgrimes
5341558Srgrimes    return(i);
5351558Srgrimes}
5361558Srgrimes
5371558Srgrimes
5381558Srgrimes/*
5391558Srgrimes *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
5401558Srgrimes *	"offset" is the byte offset into the kernel for the desired value,
5411558Srgrimes *  	"ptr" points to a buffer into which the value is retrieved,
5421558Srgrimes *  	"size" is the size of the buffer (and the object to retrieve),
5431558Srgrimes *  	"refstr" is a reference string used when printing error meessages,
5441558Srgrimes *	    if "refstr" starts with a '!', then a failure on read will not
5451558Srgrimes *  	    be fatal (this may seem like a silly way to do things, but I
5461558Srgrimes *  	    really didn't want the overhead of another argument).
5471558Srgrimes *
5481558Srgrimes */
5491558Srgrimes
5509336Sdfrstatic int getkval(offset, ptr, size, refstr)
5511558Srgrimes
5529336Sdfrunsigned long offset;
5531558Srgrimesint *ptr;
5541558Srgrimesint size;
5551558Srgrimeschar *refstr;
5561558Srgrimes
5571558Srgrimes{
5581558Srgrimes    if (kvm_read(kd, offset, (char *) ptr, size) != size)
5591558Srgrimes    {
5601558Srgrimes	if (*refstr == '!')
5611558Srgrimes	{
5621558Srgrimes	    return(0);
5631558Srgrimes	}
5641558Srgrimes	else
5651558Srgrimes	{
5661558Srgrimes	    warn("kvm_read for %s", refstr);
5671558Srgrimes	    quit(23);
5681558Srgrimes	}
5691558Srgrimes    }
5701558Srgrimes    return(1);
5711558Srgrimes}
5721558Srgrimes
5731558Srgrimes/* comparison routine for qsort */
5741558Srgrimes
5751558Srgrimesstatic unsigned char sorted_state[] =
5761558Srgrimes{
5771558Srgrimes    0,	/* not used		*/
5781558Srgrimes    4,	/* start		*/
5791558Srgrimes    5,	/* run			*/
5801558Srgrimes    2,	/* sleep		*/
5811558Srgrimes    3,	/* stop			*/
5821558Srgrimes    1	/* zombie		*/
5831558Srgrimes};
5841558Srgrimes
5851558Srgrimes#ifdef ORDER
5861558Srgrimes
5871558Srgrimes/*
5881558Srgrimes *  proc_compares - comparison functions for "qsort"
5891558Srgrimes */
5901558Srgrimes
5911558Srgrimes/*
5921558Srgrimes * First, the possible comparison keys.  These are defined in such a way
5931558Srgrimes * that they can be merely listed in the source code to define the actual
5941558Srgrimes * desired ordering.
5951558Srgrimes */
5961558Srgrimes
5971558Srgrimes
5981558Srgrimes#define ORDERKEY_PCTCPU \
5991558Srgrimes	if (lresult = (pctcpu)PP(p2, p_pctcpu) - (pctcpu)PP(p1, p_pctcpu), \
6001558Srgrimes           (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0)
6011558Srgrimes#define ORDERKEY_CPUTIME \
6021558Srgrimes	if ((result = PP(p2, p_rtime.tv_sec) - PP(p1, p_rtime.tv_sec)) == 0) \
6031558Srgrimes		if ((result = PP(p2, p_rtime.tv_usec) - \
6041558Srgrimes		     PP(p1, p_rtime.tv_usec)) == 0)
6051558Srgrimes#define ORDERKEY_STATE \
6061558Srgrimes	if ((result = sorted_state[(unsigned char) PP(p2, p_stat)] - \
6071558Srgrimes                      sorted_state[(unsigned char) PP(p1, p_stat)])  == 0)
6081558Srgrimes#define ORDERKEY_PRIO \
6091558Srgrimes	if ((result = PP(p2, p_priority) - PP(p1, p_priority)) == 0)
6101558Srgrimes#define ORDERKEY_RSSIZE \
6112776Sphk	if ((result = VP(p2, vm_rssize) - VP(p1, vm_rssize)) == 0)
6122776Sphk#define ORDERKEY_MEM \
6132776Sphk	if ((result = PROCSIZE(p2) - PROCSIZE(p1)) == 0)
6142776Sphk
6152776Sphk
6162776Sphk/* compare_cpu - the comparison function for sorting by cpu percentage */
6179336Sdfr
6182776Sphkint
6192776Sphkcompare_cpu(v1, v2)
6201558Srgrimes
6211558Srgrimesconst void *v1, *v2;
6221558Srgrimes
6231558Srgrimes{
6242776Sphk    register struct proc **pp1 = (struct proc **)v1;
6251558Srgrimes    register struct proc **pp2 = (struct proc **)v2;
6261558Srgrimes    register struct kinfo_proc *p1;
6271558Srgrimes    register struct kinfo_proc *p2;
6281558Srgrimes    register int result;
6291558Srgrimes    register pctcpu lresult;
6309336Sdfr
6311558Srgrimes    /* remove one level of indirection */
6329336Sdfr    p1 = *(struct kinfo_proc **) pp1;
6339336Sdfr    p2 = *(struct kinfo_proc **) pp2;
6349336Sdfr
6359336Sdfr    ORDERKEY_PCTCPU
6369336Sdfr    ORDERKEY_CPUTIME
6379336Sdfr    ORDERKEY_STATE
6389336Sdfr    ORDERKEY_PRIO
6391558Srgrimes    ORDERKEY_RSSIZE
6401558Srgrimes    ORDERKEY_MEM
6411558Srgrimes    ;
6421558Srgrimes    return(result);
6439230Skarl}
6449230Skarl
6459336Sdfr/* compare_size - the comparison function for sorting by total memory usage */
6461558Srgrimes
6471558Srgrimesint
6481558Srgrimescompare_size(v1, v2)
6491558Srgrimes
6501558Srgrimesconst void *v1, *v2;
6511558Srgrimes
6529336Sdfr{
6539336Sdfr    register struct proc **pp1 = (struct proc **)v1;
6549336Sdfr    register struct proc **pp2 = (struct proc **)v2;
6559336Sdfr    register struct kinfo_proc *p1;
6569336Sdfr    register struct kinfo_proc *p2;
6579336Sdfr    register int result;
6589336Sdfr    register pctcpu lresult;
6591558Srgrimes
6604822Sats    /* remove one level of indirection */
6611558Srgrimes    p1 = *(struct kinfo_proc **) pp1;
6621558Srgrimes    p2 = *(struct kinfo_proc **) pp2;
6631558Srgrimes
6641558Srgrimes    ORDERKEY_MEM
6659336Sdfr    ORDERKEY_RSSIZE
6669336Sdfr    ORDERKEY_PCTCPU
6679336Sdfr    ORDERKEY_CPUTIME
6689336Sdfr    ORDERKEY_STATE
6699336Sdfr    ORDERKEY_PRIO
6701558Srgrimes    ;
6711558Srgrimes
6721558Srgrimes    return(result);
6731558Srgrimes}
6741558Srgrimes
6751558Srgrimes/* compare_res - the comparison function for sorting by resident set size */
6761558Srgrimes
6771558Srgrimesint
6781558Srgrimescompare_res(v1, v2)
6791558Srgrimes
6801558Srgrimesconst void *v1, *v2;
6811558Srgrimes
6821558Srgrimes{
6831558Srgrimes    register struct proc **pp1 = (struct proc **)v1;
6841558Srgrimes    register struct proc **pp2 = (struct proc **)v2;
6851558Srgrimes    register struct kinfo_proc *p1;
6861558Srgrimes    register struct kinfo_proc *p2;
6871558Srgrimes    register int result;
6881558Srgrimes    register pctcpu lresult;
6891558Srgrimes
6901558Srgrimes    /* remove one level of indirection */
6911558Srgrimes    p1 = *(struct kinfo_proc **) pp1;
6921558Srgrimes    p2 = *(struct kinfo_proc **) pp2;
6931558Srgrimes
6941558Srgrimes    ORDERKEY_RSSIZE
6951558Srgrimes    ORDERKEY_MEM
6961558Srgrimes    ORDERKEY_PCTCPU
6971558Srgrimes    ORDERKEY_CPUTIME
6981558Srgrimes    ORDERKEY_STATE
6991558Srgrimes    ORDERKEY_PRIO
7001558Srgrimes    ;
7011558Srgrimes
7021558Srgrimes    return(result);
7031558Srgrimes}
7045966Sdg
7051558Srgrimes/* compare_time - the comparison function for sorting by CPU time */
7061558Srgrimes
7071558Srgrimesint
7081558Srgrimescompare_time(v1, v2)
7091558Srgrimes
7101558Srgrimesconst void *v1, *v2;
7111558Srgrimes
7121558Srgrimes{
7131558Srgrimes    register struct proc **pp1 = (struct proc **)v1;
7141558Srgrimes    register struct proc **pp2 = (struct proc **)v2;
7151558Srgrimes    register struct kinfo_proc *p1;
7161558Srgrimes    register struct kinfo_proc *p2;
7171558Srgrimes    register int result;
7181558Srgrimes    register pctcpu lresult;
7199336Sdfr
7209336Sdfr    /* remove one level of indirection */
7211558Srgrimes    p1 = *(struct kinfo_proc **) pp1;
7221558Srgrimes    p2 = *(struct kinfo_proc **) pp2;
7231558Srgrimes
7241558Srgrimes    ORDERKEY_CPUTIME
7251558Srgrimes    ORDERKEY_PCTCPU
7261558Srgrimes    ORDERKEY_STATE
7271558Srgrimes    ORDERKEY_PRIO
7281558Srgrimes    ORDERKEY_MEM
7291558Srgrimes    ORDERKEY_RSSIZE
7301558Srgrimes    ;
7311558Srgrimes
7321558Srgrimes    return(result);
7331558Srgrimes}
7341558Srgrimes
7351558Srgrimes/* compare_prio - the comparison function for sorting by CPU time */
7361558Srgrimes
7371558Srgrimesint
7381558Srgrimescompare_prio(v1, v2)
7399336Sdfr
7401558Srgrimesconst void *v1, *v2;
7419336Sdfr
7429336Sdfr{
7439336Sdfr    register struct proc **pp1 = (struct proc **)v1;
7449336Sdfr    register struct proc **pp2 = (struct proc **)v2;
7451558Srgrimes    register struct kinfo_proc *p1;
7461558Srgrimes    register struct kinfo_proc *p2;
7471558Srgrimes    register int result;
7489336Sdfr    register pctcpu lresult;
7499336Sdfr
7509336Sdfr    /* remove one level of indirection */
7519336Sdfr    p1 = *(struct kinfo_proc **) pp1;
7529336Sdfr    p2 = *(struct kinfo_proc **) pp2;
7539336Sdfr
7549336Sdfr    ORDERKEY_PRIO
7559336Sdfr    ORDERKEY_PCTCPU
7569336Sdfr    ORDERKEY_CPUTIME
7579336Sdfr    ORDERKEY_STATE
7589336Sdfr    ORDERKEY_RSSIZE
7599336Sdfr    ORDERKEY_MEM
7609336Sdfr    ;
7619336Sdfr
7629336Sdfr    return(result);
7639336Sdfr}
7649336Sdfr
7659336Sdfrint (*proc_compares[])() = {
7669336Sdfr    compare_cpu,
7679336Sdfr    compare_size,
7689336Sdfr    compare_res,
7699336Sdfr    compare_time,
7709336Sdfr    compare_prio,
7719336Sdfr    NULL
7729336Sdfr};
7739336Sdfr#else
7749336Sdfr/*
7759336Sdfr *  proc_compare - comparison function for "qsort"
7769336Sdfr *	Compares the resource consumption of two processes using five
7771558Srgrimes *  	distinct keys.  The keys (in descending order of importance) are:
7781558Srgrimes *  	percent cpu, cpu ticks, state, resident set size, total virtual
77918286Sbde *  	memory usage.  The process states are ordered as follows (from least
7801558Srgrimes *  	to most important):  zombie, sleep, stop, start, run.  The array
7811558Srgrimes *  	declaration below maps a process state index into a number that
7829336Sdfr *  	reflects this ordering.
7839336Sdfr */
7849336Sdfr
7859336Sdfrint
7869336Sdfrproc_compare(v1, v2)
7871558Srgrimes
7881558Srgrimesconst void *v1, *v2;
789
790{
791    register struct proc **pp1 = (struct proc **)v1;
792    register struct proc **pp2 = (struct proc **)v2;
793    register struct kinfo_proc *p1;
794    register struct kinfo_proc *p2;
795    register int result;
796    register pctcpu lresult;
797
798    /* remove one level of indirection */
799    p1 = *(struct kinfo_proc **) pp1;
800    p2 = *(struct kinfo_proc **) pp2;
801
802    /* compare percent cpu (pctcpu) */
803    if ((lresult = PP(p2, p_pctcpu) - PP(p1, p_pctcpu)) == 0)
804    {
805	/* use CPU usage to break the tie */
806	if ((result = PP(p2, p_rtime).tv_sec - PP(p1, p_rtime).tv_sec) == 0)
807	{
808	    /* use process state to break the tie */
809	    if ((result = sorted_state[(unsigned char) PP(p2, p_stat)] -
810			  sorted_state[(unsigned char) PP(p1, p_stat)])  == 0)
811	    {
812		/* use priority to break the tie */
813		if ((result = PP(p2, p_priority) - PP(p1, p_priority)) == 0)
814		{
815		    /* use resident set size (rssize) to break the tie */
816		    if ((result = VP(p2, vm_rssize) - VP(p1, vm_rssize)) == 0)
817		    {
818			/* use total memory to break the tie */
819			result = PROCSIZE(p2) - PROCSIZE(p1);
820		    }
821		}
822	    }
823	}
824    }
825    else
826    {
827	result = lresult < 0 ? -1 : 1;
828    }
829
830    return(result);
831}
832#endif
833
834/*
835 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
836 *		the process does not exist.
837 *		It is EXTREMLY IMPORTANT that this function work correctly.
838 *		If top runs setuid root (as in SVR4), then this function
839 *		is the only thing that stands in the way of a serious
840 *		security problem.  It validates requests for the "kill"
841 *		and "renice" commands.
842 */
843
844int proc_owner(pid)
845
846pid_t pid;
847
848{
849    register int cnt;
850    register struct kinfo_proc **prefp;
851    register struct kinfo_proc *pp;
852
853    prefp = pref;
854    cnt = pref_len;
855    while (--cnt >= 0)
856    {
857	pp = *prefp++;
858	if (PP(pp, p_pid) == pid)
859	{
860	    return((int)EP(pp, e_pcred.p_ruid));
861	}
862    }
863    return(-1);
864}
865
866#ifdef DOSWAP
867/*
868 * swapmode is based on a program called swapinfo written
869 * by Kevin Lahey <kml@rokkaku.atl.ga.us>.
870 */
871
872#define	SVAR(var) __STRING(var)	/* to force expansion */
873#define	KGET(idx, var)							\
874	KGET1(idx, &var, sizeof(var), SVAR(var))
875#define	KGET1(idx, p, s, msg)						\
876	KGET2(nlst[idx].n_value, p, s, msg)
877#define	KGET2(addr, p, s, msg)						\
878	if (kvm_read(kd, (u_long)(addr), p, s) != s)			\
879		warnx("cannot read %s: %s", msg, kvm_geterr(kd))
880
881static int
882swapmode(used, total)
883int *used;
884int *total;
885{
886	int nswap, nswdev, dmmax, nswapmap, niswap, niswdev;
887	int s, e, i, l, nfree;
888	struct swdevt *sw;
889	long *perdev;
890	struct map *swapmap, *kswapmap;
891	struct mapent *mp, *freemp;
892
893	KGET(VM_NSWAP, nswap);
894	KGET(VM_NSWDEV, nswdev);
895	KGET(VM_DMMAX, dmmax);
896	KGET(VM_NSWAPMAP, nswapmap);
897	KGET(VM_SWAPMAP, kswapmap);	/* kernel `swapmap' is a pointer */
898	if (nswap == 0) {
899		*used = 0;
900		*total = 0;
901		return (1);
902	}
903	if ((sw = malloc(nswdev * sizeof(*sw))) == NULL ||
904	    (perdev = malloc(nswdev * sizeof(*perdev))) == NULL ||
905	    (freemp = mp = malloc(nswapmap * sizeof(*mp))) == NULL)
906		err(1, "malloc");
907	KGET1(VM_SWDEVT, sw, nswdev * sizeof(*sw), "swdevt");
908	KGET2((long)kswapmap, mp, nswapmap * sizeof(*mp), "swapmap");
909
910	/* Supports sequential swap */
911	if (nlst[VM_NISWAP].n_value != 0) {
912		KGET(VM_NISWAP, niswap);
913		KGET(VM_NISWDEV, niswdev);
914	} else {
915		niswap = nswap;
916		niswdev = nswdev;
917	}
918
919	/* First entry in map is `struct map'; rest are mapent's. */
920	swapmap = (struct map *)mp;
921	if (nswapmap != swapmap->m_limit - (struct mapent *)kswapmap)
922		errx(1, "panic: nswapmap goof");
923
924	/* Count up swap space. */
925	nfree = 0;
926	memset(perdev, 0, nswdev * sizeof(*perdev));
927	for (mp++; mp->m_addr != 0; mp++) {
928		s = mp->m_addr;			/* start of swap region */
929		e = mp->m_addr + mp->m_size;	/* end of region */
930		nfree += mp->m_size;
931
932		/*
933		 * Swap space is split up among the configured disks.
934		 *
935		 * For interleaved swap devices, the first dmmax blocks
936		 * of swap space some from the first disk, the next dmmax
937		 * blocks from the next, and so on up to niswap blocks.
938		 *
939		 * Sequential swap devices follow the interleaved devices
940		 * (i.e. blocks starting at niswap) in the order in which
941		 * they appear in the swdev table.  The size of each device
942		 * will be a multiple of dmmax.
943		 *
944		 * The list of free space joins adjacent free blocks,
945		 * ignoring device boundries.  If we want to keep track
946		 * of this information per device, we'll just have to
947		 * extract it ourselves.  We know that dmmax-sized chunks
948		 * cannot span device boundaries (interleaved or sequential)
949		 * so we loop over such chunks assigning them to devices.
950		 */
951		i = -1;
952		while (s < e) {		/* XXX this is inefficient */
953			int bound = roundup(s+1, dmmax);
954
955			if (bound > e)
956				bound = e;
957			if (bound <= niswap) {
958				/* Interleaved swap chunk. */
959				if (i == -1)
960					i = (s / dmmax) % niswdev;
961				perdev[i] += bound - s;
962				if (++i >= niswdev)
963					i = 0;
964			} else {
965				/* Sequential swap chunk. */
966				if (i < niswdev) {
967					i = niswdev;
968					l = niswap + sw[i].sw_nblks;
969				}
970				while (s >= l) {
971					/* XXX don't die on bogus blocks */
972					if (i == nswdev-1)
973						break;
974					l += sw[++i].sw_nblks;
975				}
976				perdev[i] += bound - s;
977			}
978			s = bound;
979		}
980	}
981
982	*total = 0;
983	for (i = 0; i < nswdev; i++) {
984		int xsize, xfree;
985
986		xsize = sw[i].sw_nblks;
987		xfree = perdev[i];
988		*total += xsize;
989	}
990
991	/*
992	 * If only one partition has been set up via swapon(8), we don't
993	 * need to bother with totals.
994	 */
995#if DEV_BSHIFT < 10
996	*used = (*total - nfree) >> (10 - DEV_BSHIFT);
997	*total >>= 10 - DEV_BSHIFT;
998#elif DEV_BSHIFT > 10
999	*used = (*total - nfree) >> (DEV_BSHIFT - 10);
1000	*total >>= DEV_BSHIFT - 10;
1001#endif
1002	free (sw); free (freemp); free (perdev);
1003	return 1;
1004}
1005#endif
1006