Home | History | Annotate | Line # | Download | only in machine
      1 /*
      2  * Copyright (c) 1984 through 2008, William LeFebvre
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *
     16  *     * Neither the name of William LeFebvre nor the names of other
     17  * contributors may be used to endorse or promote products derived from
     18  * this software without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 /*
     34  * top - a top users display for Unix
     35  *
     36  * SYNOPSIS:  Linux 1.2.x, 1.3.x, 2.x, using the /proc filesystem
     37  *
     38  * DESCRIPTION:
     39  * This is the machine-dependent module for Linux 1.2.x, 1.3.x or 2.x.
     40  *
     41  * LIBS:
     42  *
     43  * CFLAGS: -DHAVE_GETOPT -DHAVE_STRERROR -DORDER
     44  *
     45  * TERMCAP: -lcurses
     46  *
     47  * AUTHOR: Richard Henderson <rth (at) tamu.edu>
     48  * Order support added by Alexey Klimkin <kad (at) klon.tme.mcst.ru>
     49  * Ported to 2.4 by William LeFebvre
     50  * Additions for 2.6 by William LeFebvre
     51  */
     52 
     53 #include "config.h"
     54 
     55 #include <sys/types.h>
     56 #include <time.h>
     57 #include <stdio.h>
     58 #include <fcntl.h>
     59 #include <unistd.h>
     60 #include <stdlib.h>
     61 #include <errno.h>
     62 #include <dirent.h>
     63 #include <string.h>
     64 #include <math.h>
     65 #include <ctype.h>
     66 #include <sys/time.h>
     67 #include <sys/stat.h>
     68 #include <sys/vfs.h>
     69 
     70 #include <sys/param.h>		/* for HZ */
     71 #include <asm/page.h>		/* for PAGE_SHIFT */
     72 
     73 #if 0
     74 #include <linux/proc_fs.h>	/* for PROC_SUPER_MAGIC */
     75 #else
     76 #define PROC_SUPER_MAGIC 0x9fa0
     77 #endif
     78 
     79 #include "top.h"
     80 #include "hash.h"
     81 #include "machine.h"
     82 #include "utils.h"
     83 #include "username.h"
     84 
     85 #define PROCFS "/proc"
     86 extern char *myname;
     87 
     88 /*=PROCESS INFORMATION==================================================*/
     89 
     90 struct top_proc
     91 {
     92     pid_t pid;
     93     uid_t uid;
     94     char *name;
     95     int pri, nice, threads;
     96     unsigned long size, rss, shared;	/* in k */
     97     int state;
     98     unsigned long time;
     99     unsigned long start_time;
    100     double pcpu;
    101     struct top_proc *next;
    102 };
    103 
    104 
    105 /*=STATE IDENT STRINGS==================================================*/
    106 
    107 #define NPROCSTATES 7
    108 static char *state_abbrev[NPROCSTATES+1] =
    109 {
    110     "", "run", "sleep", "disk", "zomb", "stop", "swap",
    111     NULL
    112 };
    113 
    114 static char *procstatenames[NPROCSTATES+1] =
    115 {
    116     "", " running, ", " sleeping, ", " uninterruptable, ",
    117     " zombie, ", " stopped, ", " swapping, ",
    118     NULL
    119 };
    120 
    121 #define NCPUSTATES 5
    122 static char *cpustatenames[NCPUSTATES+1] =
    123 {
    124     "user", "nice", "system", "idle", "iowait",
    125     NULL
    126 };
    127 static int show_iowait = 0;
    128 
    129 #define KERNELCTXT    0
    130 #define KERNELFLT     1
    131 #define KERNELINTR    2
    132 #define KERNELNEWPROC 3
    133 #define NKERNELSTATS  4
    134 static char *kernelnames[NKERNELSTATS+1] =
    135 {
    136     " ctxsw, ", " flt, ", " intr, ", " newproc",
    137     NULL
    138 };
    139 
    140 #define MEMUSED    0
    141 #define MEMFREE    1
    142 #define MEMSHARED  2
    143 #define MEMBUFFERS 3
    144 #define MEMCACHED  4
    145 #define NMEMSTATS  5
    146 static char *memorynames[NMEMSTATS+1] =
    147 {
    148     "K used, ", "K free, ", "K shared, ", "K buffers, ", "K cached",
    149     NULL
    150 };
    151 
    152 #define SWAPUSED   0
    153 #define SWAPFREE   1
    154 #define SWAPCACHED 2
    155 #define NSWAPSTATS 3
    156 static char *swapnames[NSWAPSTATS+1] =
    157 {
    158     "K used, ", "K free, ", "K cached",
    159     NULL
    160 };
    161 
    162 static char fmt_header[] =
    163 "  PID X         THR PRI NICE  SIZE   RES STATE   TIME    CPU COMMAND";
    164 
    165 static char proc_header_thr[] =
    166 "  PID %-9s THR PRI NICE  SIZE   RES   SHR STATE   TIME    CPU COMMAND";
    167 
    168 static char proc_header_nothr[] =
    169 "  PID %-9s PRI NICE  SIZE   RES   SHR STATE   TIME    CPU COMMAND";
    170 
    171 /* these are names given to allowed sorting orders -- first is default */
    172 char *ordernames[] =
    173 {"cpu", "size", "res", "time", "command", NULL};
    174 
    175 /* forward definitions for comparison functions */
    176 int compare_cpu();
    177 int compare_size();
    178 int compare_res();
    179 int compare_time();
    180 int compare_cmd();
    181 
    182 int (*proc_compares[])() = {
    183     compare_cpu,
    184     compare_size,
    185     compare_res,
    186     compare_time,
    187     compare_cmd,
    188     NULL };
    189 
    190 /*=SYSTEM STATE INFO====================================================*/
    191 
    192 /* these are for calculating cpu state percentages */
    193 
    194 static long cp_time[NCPUSTATES];
    195 static long cp_old[NCPUSTATES];
    196 static long cp_diff[NCPUSTATES];
    197 
    198 /* for calculating the exponential average */
    199 
    200 static struct timeval lasttime = { 0, 0 };
    201 static struct timeval timediff = { 0, 0 };
    202 static long elapsed_msecs;
    203 
    204 /* these are for keeping track of processes and tasks */
    205 
    206 #define HASH_SIZE	     (1003)
    207 #define INITIAL_ACTIVE_SIZE  (256)
    208 #define PROCBLOCK_SIZE       (32)
    209 static hash_table *ptable;
    210 static hash_table *tasktable;
    211 static struct top_proc **pactive;
    212 static struct top_proc **nextactive;
    213 static unsigned int activesize = 0;
    214 static time_t boottime = -1;
    215 static int have_task = 0;
    216 
    217 /* these are counters that need to be track */
    218 static unsigned long last_ctxt = 0;
    219 static unsigned long last_intr = 0;
    220 static unsigned long last_newproc = 0;
    221 static unsigned long last_flt = 0;
    222 
    223 /* these are for passing data back to the machine independant portion */
    224 
    225 static int cpu_states[NCPUSTATES];
    226 static int process_states[NPROCSTATES];
    227 static int kernel_stats[NKERNELSTATS];
    228 static long memory_stats[NMEMSTATS];
    229 static long swap_stats[NSWAPSTATS];
    230 
    231 /* useful macros */
    232 #define bytetok(x)	(((x) + 512) >> 10)
    233 #define pagetok(x)	((x) << (PAGE_SHIFT - 10))
    234 #define HASH(x)		(((x) * 1686629713U) % HASH_SIZE)
    235 
    236 /* calculate a per-second rate using milliseconds */
    237 #define per_second(n, msec)   (((n) * 1000) / (msec))
    238 
    239 /*======================================================================*/
    240 
    241 static inline char *
    242 skip_ws(const char *p)
    243 {
    244     while (isspace(*p)) p++;
    245     return (char *)p;
    246 }
    247 
    248 static inline char *
    249 skip_token(const char *p)
    250 {
    251     while (isspace(*p)) p++;
    252     while (*p && !isspace(*p)) p++;
    253     return (char *)p;
    254 }
    255 
    256 static void
    257 xfrm_cmdline(char *p, int len)
    258 {
    259     while (--len > 0)
    260     {
    261 	if (*p == '\0')
    262 	{
    263 	    *p = ' ';
    264 	}
    265 	p++;
    266     }
    267 }
    268 
    269 static void
    270 update_procname(struct top_proc *proc, char *cmd)
    271 
    272 {
    273     printable(cmd);
    274 
    275     if (proc->name == NULL)
    276     {
    277 	proc->name = strdup(cmd);
    278     }
    279     else if (strcmp(proc->name, cmd) != 0)
    280     {
    281 	free(proc->name);
    282 	proc->name = strdup(cmd);
    283     }
    284 }
    285 
    286 /*
    287  * Process structures are allocated and freed as needed.  Here we
    288  * keep big pools of them, adding more pool as needed.  When a
    289  * top_proc structure is freed, it is added to a freelist and reused.
    290  */
    291 
    292 static struct top_proc *freelist = NULL;
    293 static struct top_proc *procblock = NULL;
    294 static struct top_proc *procmax = NULL;
    295 
    296 static struct top_proc *
    297 new_proc()
    298 {
    299     struct top_proc *p;
    300 
    301     if (freelist)
    302     {
    303 	p = freelist;
    304 	freelist = freelist->next;
    305     }
    306     else if (procblock)
    307     {
    308 	p = procblock;
    309 	if (++procblock >= procmax)
    310 	{
    311 	    procblock = NULL;
    312 	}
    313     }
    314     else
    315     {
    316 	p = procblock = (struct top_proc *)calloc(PROCBLOCK_SIZE,
    317 						  sizeof(struct top_proc));
    318 	procmax = procblock++ + PROCBLOCK_SIZE;
    319     }
    320 
    321     /* initialization */
    322     if (p->name != NULL)
    323     {
    324 	free(p->name);
    325 	p->name = NULL;
    326     }
    327 
    328     return p;
    329 }
    330 
    331 static void
    332 free_proc(struct top_proc *proc)
    333 {
    334     proc->next = freelist;
    335     freelist = proc;
    336 }
    337 
    338 
    339 int
    340 machine_init(struct statics *statics)
    341 
    342 {
    343     /* make sure the proc filesystem is mounted */
    344     {
    345 	struct statfs sb;
    346 	if (statfs(PROCFS, &sb) < 0 || sb.f_type != PROC_SUPER_MAGIC)
    347 	{
    348 	    fprintf(stderr, "%s: proc filesystem not mounted on " PROCFS "\n",
    349 		    myname);
    350 	    return -1;
    351 	}
    352     }
    353 
    354     /* chdir to the proc filesystem to make things easier */
    355     chdir(PROCFS);
    356 
    357     /* a few preliminary checks */
    358     {
    359 	int fd;
    360 	char buff[128];
    361 	char *p;
    362 	int cnt = 0;
    363 	unsigned long uptime;
    364 	struct timeval tv;
    365 	struct stat st;
    366 
    367 	/* get a boottime */
    368 	if ((fd = open("uptime", 0)) != -1)
    369 	{
    370 	    if (read(fd, buff, sizeof(buff)) > 0)
    371 	    {
    372 		uptime = strtoul(buff, &p, 10);
    373 		gettimeofday(&tv, 0);
    374 		boottime = tv.tv_sec - uptime;
    375 	    }
    376 	    close(fd);
    377 	}
    378 
    379 	/* see how many states we get from stat */
    380 	if ((fd = open("stat", 0)) != -1)
    381 	{
    382 	    if (read(fd, buff, sizeof(buff)) > 0)
    383 	    {
    384 		if ((p = strchr(buff, '\n')) != NULL)
    385 		{
    386 		    *p = '\0';
    387 		    p = buff;
    388 		    while (*p != '\0')
    389 		    {
    390 			if (*p++ == ' ')
    391 			{
    392 			    cnt++;
    393 			}
    394 		    }
    395 		}
    396 	    }
    397 
    398 	    close(fd);
    399 	}
    400 	if (cnt > 5)
    401 	{
    402 	    /* we have iowait */
    403 	    show_iowait = 1;
    404 	}
    405 
    406 	/* see if we have task subdirs */
    407 	if (stat("self/task", &st) != -1 && S_ISDIR(st.st_mode))
    408 	{
    409 	    dprintf("we have task directories\n");
    410 	    have_task = 1;
    411 	}
    412     }
    413 
    414     /* if we aren't showing iowait, then we have to tweak cpustatenames */
    415     if (!show_iowait)
    416     {
    417 	cpustatenames[4] = NULL;
    418     }
    419 
    420     /* fill in the statics information */
    421     statics->procstate_names = procstatenames;
    422     statics->cpustate_names = cpustatenames;
    423     statics->kernel_names = kernelnames;
    424     statics->memory_names = memorynames;
    425     statics->swap_names = swapnames;
    426     statics->order_names = ordernames;
    427     statics->boottime = boottime;
    428     statics->flags.fullcmds = 1;
    429     statics->flags.warmup = 1;
    430     statics->flags.threads = 1;
    431 
    432     /* allocate needed space */
    433     pactive = (struct top_proc **)malloc(sizeof(struct top_proc *) * INITIAL_ACTIVE_SIZE);
    434     activesize = INITIAL_ACTIVE_SIZE;
    435 
    436     /* create process and task hashes */
    437     ptable = hash_create(HASH_SIZE);
    438     tasktable = hash_create(HASH_SIZE);
    439 
    440     /* all done! */
    441     return 0;
    442 }
    443 
    444 
    445 void
    446 get_system_info(struct system_info *info)
    447 
    448 {
    449     char buffer[4096+1];
    450     int fd, len;
    451     char *p;
    452     struct timeval thistime;
    453     unsigned long intr = 0;
    454     unsigned long ctxt = 0;
    455     unsigned long newproc = 0;
    456     unsigned long flt = 0;
    457 
    458     /* timestamp and time difference */
    459     gettimeofday(&thistime, 0);
    460     timersub(&thistime, &lasttime, &timediff);
    461     elapsed_msecs = timediff.tv_sec * 1000 + timediff.tv_usec / 1000;
    462     lasttime = thistime;
    463 
    464     /* get load averages */
    465     if ((fd = open("loadavg", O_RDONLY)) != -1)
    466     {
    467 	if ((len = read(fd, buffer, sizeof(buffer)-1)) > 0)
    468 	{
    469 	    buffer[len] = '\0';
    470 	    info->load_avg[0] = strtod(buffer, &p);
    471 	    info->load_avg[1] = strtod(p, &p);
    472 	    info->load_avg[2] = strtod(p, &p);
    473 	    p = skip_token(p);			/* skip running/tasks */
    474 	    p = skip_ws(p);
    475 	    if (*p)
    476 	    {
    477 		info->last_pid = atoi(p);
    478 	    }
    479 	    else
    480 	    {
    481 		info->last_pid = -1;
    482 	    }
    483 	}
    484 	close(fd);
    485     }
    486 
    487     /* get the cpu time info */
    488     if ((fd = open("stat", O_RDONLY)) != -1)
    489     {
    490 	if ((len = read(fd, buffer, sizeof(buffer)-1)) > 0)
    491 	{
    492 	    buffer[len] = '\0';
    493 	    p = skip_token(buffer);			/* "cpu" */
    494 	    cp_time[0] = strtoul(p, &p, 0);
    495 	    cp_time[1] = strtoul(p, &p, 0);
    496 	    cp_time[2] = strtoul(p, &p, 0);
    497 	    cp_time[3] = strtoul(p, &p, 0);
    498 	    if (show_iowait)
    499 	    {
    500 		cp_time[4] = strtoul(p, &p, 0);
    501 	    }
    502 
    503 	    /* convert cp_time counts to percentages */
    504 	    percentages(NCPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
    505 
    506 	    /* get the rest of it */
    507 	    p = strchr(p, '\n');
    508 	    while (p != NULL)
    509 	    {
    510 		p++;
    511 		if (strncmp(p, "intr ", 5) == 0)
    512 		{
    513 		    p = skip_token(p);
    514 		    intr = strtoul(p, &p, 10);
    515 		}
    516 		else if (strncmp(p, "ctxt ", 5) == 0)
    517 		{
    518 		    p = skip_token(p);
    519 		    ctxt = strtoul(p, &p, 10);
    520 		}
    521 		else if (strncmp(p, "processes ", 10) == 0)
    522 		{
    523 		    p = skip_token(p);
    524 		    newproc = strtoul(p, &p, 10);
    525 		}
    526 
    527 		p = strchr(p, '\n');
    528 	    }
    529 
    530 	    kernel_stats[KERNELINTR] = per_second(intr - last_intr, elapsed_msecs);
    531 	    kernel_stats[KERNELCTXT] = per_second(ctxt - last_ctxt, elapsed_msecs);
    532 	    kernel_stats[KERNELNEWPROC] = per_second(newproc - last_newproc, elapsed_msecs);
    533 	    last_intr = intr;
    534 	    last_ctxt = ctxt;
    535 	    last_newproc = newproc;
    536 	}
    537 	close(fd);
    538     }
    539 
    540     /* get system wide memory usage */
    541     if ((fd = open("meminfo", O_RDONLY)) != -1)
    542     {
    543 	char *p;
    544 	int mem = 0;
    545 	int swap = 0;
    546 	unsigned long memtotal = 0;
    547 	unsigned long memfree = 0;
    548 	unsigned long swaptotal = 0;
    549 
    550 	if ((len = read(fd, buffer, sizeof(buffer)-1)) > 0)
    551 	{
    552 	    buffer[len] = '\0';
    553 	    p = buffer-1;
    554 
    555 	    /* iterate thru the lines */
    556 	    while (p != NULL)
    557 	    {
    558 		p++;
    559 		if (p[0] == ' ' || p[0] == '\t')
    560 		{
    561 		    /* skip */
    562 		}
    563 		else if (strncmp(p, "Mem:", 4) == 0)
    564 		{
    565 		    p = skip_token(p);			/* "Mem:" */
    566 		    p = skip_token(p);			/* total memory */
    567 		    memory_stats[MEMUSED] = strtoul(p, &p, 10);
    568 		    memory_stats[MEMFREE] = strtoul(p, &p, 10);
    569 		    memory_stats[MEMSHARED] = strtoul(p, &p, 10);
    570 		    memory_stats[MEMBUFFERS] = strtoul(p, &p, 10);
    571 		    memory_stats[MEMCACHED] = strtoul(p, &p, 10);
    572 		    memory_stats[MEMUSED] = bytetok(memory_stats[MEMUSED]);
    573 		    memory_stats[MEMFREE] = bytetok(memory_stats[MEMFREE]);
    574 		    memory_stats[MEMSHARED] = bytetok(memory_stats[MEMSHARED]);
    575 		    memory_stats[MEMBUFFERS] = bytetok(memory_stats[MEMBUFFERS]);
    576 		    memory_stats[MEMCACHED] = bytetok(memory_stats[MEMCACHED]);
    577 		    mem = 1;
    578 		}
    579 		else if (strncmp(p, "Swap:", 5) == 0)
    580 		{
    581 		    p = skip_token(p);			/* "Swap:" */
    582 		    p = skip_token(p);			/* total swap */
    583 		    swap_stats[SWAPUSED] = strtoul(p, &p, 10);
    584 		    swap_stats[SWAPFREE] = strtoul(p, &p, 10);
    585 		    swap_stats[SWAPUSED] = bytetok(swap_stats[SWAPUSED]);
    586 		    swap_stats[SWAPFREE] = bytetok(swap_stats[SWAPFREE]);
    587 		    swap = 1;
    588 		}
    589 		else if (!mem && strncmp(p, "MemTotal:", 9) == 0)
    590 		{
    591 		    p = skip_token(p);
    592 		    memtotal = strtoul(p, &p, 10);
    593 		}
    594 		else if (!mem && memtotal > 0 && strncmp(p, "MemFree:", 8) == 0)
    595 		{
    596 		    p = skip_token(p);
    597 		    memfree = strtoul(p, &p, 10);
    598 		    memory_stats[MEMUSED] = memtotal - memfree;
    599 		    memory_stats[MEMFREE] = memfree;
    600 		}
    601 		else if (!mem && strncmp(p, "MemShared:", 10) == 0)
    602 		{
    603 		    p = skip_token(p);
    604 		    memory_stats[MEMSHARED] = strtoul(p, &p, 10);
    605 		}
    606 		else if (!mem && strncmp(p, "Buffers:", 8) == 0)
    607 		{
    608 		    p = skip_token(p);
    609 		    memory_stats[MEMBUFFERS] = strtoul(p, &p, 10);
    610 		}
    611 		else if (!mem && strncmp(p, "Cached:", 7) == 0)
    612 		{
    613 		    p = skip_token(p);
    614 		    memory_stats[MEMCACHED] = strtoul(p, &p, 10);
    615 		}
    616 		else if (!swap && strncmp(p, "SwapTotal:", 10) == 0)
    617 		{
    618 		    p = skip_token(p);
    619 		    swaptotal = strtoul(p, &p, 10);
    620 		}
    621 		else if (!swap && swaptotal > 0 && strncmp(p, "SwapFree:", 9) == 0)
    622 		{
    623 		    p = skip_token(p);
    624 		    memfree = strtoul(p, &p, 10);
    625 		    swap_stats[SWAPUSED] = swaptotal - memfree;
    626 		    swap_stats[SWAPFREE] = memfree;
    627 		}
    628 		else if (!mem && strncmp(p, "SwapCached:", 11) == 0)
    629 		{
    630 		    p = skip_token(p);
    631 		    swap_stats[SWAPCACHED] = strtoul(p, &p, 10);
    632 		}
    633 
    634 		/* move to the next line */
    635 		p = strchr(p, '\n');
    636 	    }
    637 	}
    638 	close(fd);
    639     }
    640 
    641     /* get vm related stuff */
    642     if ((fd = open("vmstat", O_RDONLY)) != -1)
    643     {
    644 	char *p;
    645 
    646 	if ((len = read(fd, buffer, sizeof(buffer)-1)) > 0)
    647 	{
    648 	    buffer[len] = '\0';
    649 	    p = buffer;
    650 
    651 	    /* iterate thru the lines */
    652 	    while (p != NULL)
    653 	    {
    654 		if (strncmp(p, "pgmajfault ", 11) == 0)
    655 		{
    656 		    p = skip_token(p);
    657 		    flt = strtoul(p, &p, 10);
    658 		    kernel_stats[KERNELFLT] = per_second(flt - last_flt, elapsed_msecs);
    659 		    last_flt = flt;
    660 		    break;
    661 		}
    662 
    663 		/* move to the next line */
    664 		p = strchr(p, '\n');
    665 		p++;
    666 	    }
    667 	}
    668 	close(fd);
    669     }
    670 
    671     /* set arrays and strings */
    672     info->cpustates = cpu_states;
    673     info->memory = memory_stats;
    674     info->swap = swap_stats;
    675     info->kernel = kernel_stats;
    676 }
    677 
    678 static void
    679 read_one_proc_stat(pid_t pid, pid_t taskpid, struct top_proc *proc, struct process_select *sel)
    680 {
    681     char buffer[4096], *p, *q;
    682     int fd, len;
    683     int fullcmd;
    684 
    685     dprintf("reading proc %d - %d\n", pid, taskpid);
    686 
    687     /* if anything goes wrong, we return with proc->state == 0 */
    688     proc->state = 0;
    689 
    690     /* full cmd handling */
    691     fullcmd = sel->fullcmd;
    692     if (fullcmd)
    693     {
    694 	if (taskpid == -1)
    695 	{
    696 	    sprintf(buffer, "%d/cmdline", pid);
    697 	}
    698 	else
    699 	{
    700 	    sprintf(buffer, "%d/task/%d/cmdline", pid, taskpid);
    701 	}
    702 	if ((fd = open(buffer, O_RDONLY)) != -1)
    703 	{
    704 	    /* read command line data */
    705 	    /* (theres no sense in reading more than we can fit) */
    706 	    if ((len = read(fd, buffer, MAX_COLS)) > 1)
    707 	    {
    708 		buffer[len] = '\0';
    709 		xfrm_cmdline(buffer, len);
    710 		update_procname(proc, buffer);
    711 	    }
    712 	    else
    713 	    {
    714 		fullcmd = 0;
    715 	    }
    716 	    close(fd);
    717 	}
    718 	else
    719 	{
    720 	    fullcmd = 0;
    721 	}
    722     }
    723 
    724     /* grab the shared memory size */
    725     sprintf(buffer, "%d/statm", pid);
    726     fd = open(buffer, O_RDONLY);
    727     len = read(fd, buffer, sizeof(buffer)-1);
    728     close(fd);
    729     buffer[len] = '\0';
    730     p = buffer;
    731     p = skip_token(p);		/* skip size */
    732     p = skip_token(p);		/* skip resident */
    733     proc->shared = pagetok(strtoul(p, &p, 10));
    734 
    735     /* grab the proc stat info in one go */
    736     if (taskpid == -1)
    737     {
    738 	sprintf(buffer, "%d/stat", pid);
    739     }
    740     else
    741     {
    742 	sprintf(buffer, "%d/task/%d/stat", pid, taskpid);
    743     }
    744 
    745     fd = open(buffer, O_RDONLY);
    746     len = read(fd, buffer, sizeof(buffer)-1);
    747     close(fd);
    748 
    749     buffer[len] = '\0';
    750 
    751     proc->uid = (uid_t)proc_owner((int)pid);
    752 
    753     /* parse out the status */
    754 
    755     /* skip pid and locate command, which is in parentheses */
    756     if ((p = strchr(buffer, '(')) == NULL)
    757     {
    758 	return;
    759     }
    760     if ((q = strrchr(++p, ')')) == NULL)
    761     {
    762 	return;
    763     }
    764 
    765     /* set the procname */
    766     *q = '\0';
    767     if (!fullcmd)
    768     {
    769 	update_procname(proc, p);
    770     }
    771 
    772     /* scan the rest of the line */
    773     p = q+1;
    774     p = skip_ws(p);
    775     switch (*p++)				/* state */
    776     {
    777     case 'R': proc->state = 1; break;
    778     case 'S': proc->state = 2; break;
    779     case 'D': proc->state = 3; break;
    780     case 'Z': proc->state = 4; break;
    781     case 'T': proc->state = 5; break;
    782     case 'W': proc->state = 6; break;
    783     case '\0': return;
    784     }
    785 
    786     p = skip_token(p);				/* skip ppid */
    787     p = skip_token(p);				/* skip pgrp */
    788     p = skip_token(p);				/* skip session */
    789     p = skip_token(p);				/* skip tty */
    790     p = skip_token(p);				/* skip tty pgrp */
    791     p = skip_token(p);				/* skip flags */
    792     p = skip_token(p);				/* skip min flt */
    793     p = skip_token(p);				/* skip cmin flt */
    794     p = skip_token(p);				/* skip maj flt */
    795     p = skip_token(p);				/* skip cmaj flt */
    796 
    797     proc->time = strtoul(p, &p, 10);		/* utime */
    798     proc->time += strtoul(p, &p, 10);		/* stime */
    799 
    800     p = skip_token(p);				/* skip cutime */
    801     p = skip_token(p);				/* skip cstime */
    802 
    803     proc->pri = strtol(p, &p, 10);		/* priority */
    804     proc->nice = strtol(p, &p, 10);		/* nice */
    805     proc->threads = strtol(p, &p, 10);		/* threads */
    806 
    807     p = skip_token(p);				/* skip it_real_val */
    808     proc->start_time = strtoul(p, &p, 10);	/* start_time */
    809 
    810     proc->size = bytetok(strtoul(p, &p, 10));	/* vsize */
    811     proc->rss = pagetok(strtoul(p, &p, 10));	/* rss */
    812 
    813 #if 0
    814     /* for the record, here are the rest of the fields */
    815     p = skip_token(p);				/* skip rlim */
    816     p = skip_token(p);				/* skip start_code */
    817     p = skip_token(p);				/* skip end_code */
    818     p = skip_token(p);				/* skip start_stack */
    819     p = skip_token(p);				/* skip sp */
    820     p = skip_token(p);				/* skip pc */
    821     p = skip_token(p);				/* skip signal */
    822     p = skip_token(p);				/* skip sigblocked */
    823     p = skip_token(p);				/* skip sigignore */
    824     p = skip_token(p);				/* skip sigcatch */
    825     p = skip_token(p);				/* skip wchan */
    826 #endif
    827 
    828 }
    829 
    830 static int show_usernames;
    831 static int show_threads;
    832 
    833 
    834 caddr_t
    835 get_process_info(struct system_info *si,
    836 		 struct process_select *sel,
    837 		 int compare_index)
    838 {
    839     struct top_proc *proc;
    840     struct top_proc *taskproc;
    841     pid_t pid;
    842     pid_t taskpid;
    843     unsigned long now;
    844     unsigned long elapsed;
    845     hash_item_pid *hi;
    846     hash_pos pos;
    847 
    848     /* round current time to a second */
    849     now = (unsigned long)lasttime.tv_sec;
    850     if (lasttime.tv_usec >= 500000)
    851     {
    852 	now++;
    853     }
    854 
    855     /* calculate number of ticks since our last check */
    856     elapsed = timediff.tv_sec * HZ + (timediff.tv_usec * HZ) / 1000000;
    857     if (elapsed <= 0)
    858     {
    859 	elapsed = 1;
    860     }
    861     dprintf("get_process_info: elapsed %d ticks\n", elapsed);
    862 
    863     /* mark all hash table entries as not seen */
    864     hi = hash_first_pid(ptable, &pos);
    865     while (hi != NULL)
    866     {
    867 	((struct top_proc *)(hi->value))->state = 0;
    868 	hi = hash_next_pid(&pos);
    869     }
    870     /* mark all hash table entries as not seen */
    871     hi = hash_first_pid(tasktable, &pos);
    872     while (hi != NULL)
    873     {
    874 	((struct top_proc *)(hi->value))->state = 0;
    875 	hi = hash_next_pid(&pos);
    876     }
    877 
    878     /* read the process information */
    879     {
    880 	DIR *dir = opendir(".");
    881 	DIR *taskdir;
    882 	struct dirent *ent;
    883 	struct dirent *taskent;
    884 	int total_procs = 0;
    885 	struct top_proc **active;
    886 	hash_item_pid *hi;
    887 	hash_pos pos;
    888 	char buffer[64];
    889 
    890 	int show_idle = sel->idle;
    891 	int show_uid = sel->uid != -1;
    892 	char *show_command = sel->command;
    893 
    894 	show_usernames = sel->usernames;
    895 	show_threads = sel->threads && have_task;
    896 
    897 	memset(process_states, 0, sizeof(process_states));
    898 
    899 	taskdir = NULL;
    900 	taskent = NULL;
    901 	taskpid = -1;
    902 
    903 	while ((ent = readdir(dir)) != NULL)
    904 	{
    905 	    unsigned long otime;
    906 
    907 	    if (!isdigit(ent->d_name[0]))
    908 		continue;
    909 
    910 	    pid = atoi(ent->d_name);
    911 
    912 	    /* look up hash table entry */
    913 	    proc = hash_lookup_pid(ptable, pid);
    914 
    915 	    /* if we came up empty, create a new entry */
    916 	    if (proc == NULL)
    917 	    {
    918 		proc = new_proc();
    919 		proc->pid = pid;
    920 		proc->time = 0;
    921 		hash_add_pid(ptable, pid, (void *)proc);
    922 	    }
    923 
    924 	    /* remember the previous cpu time */
    925 	    otime = proc->time;
    926 
    927 	    /* get current data */
    928 	    read_one_proc_stat(pid, -1, proc, sel);
    929 
    930 	    /* continue on if this isn't really a process */
    931 	    if (proc->state == 0)
    932 		continue;
    933 
    934 	    /* reset linked list (for threads) */
    935 	    proc->next = NULL;
    936 
    937 	    /* accumulate process state data */
    938 	    total_procs++;
    939 	    process_states[proc->state]++;
    940 
    941 	    /* calculate pcpu */
    942 	    if ((proc->pcpu = (proc->time - otime) / (double)elapsed) < 0.0001)
    943 	    {
    944 		proc->pcpu = 0;
    945 	    }
    946 
    947 	    /* if we have task subdirs and this process has more than
    948 	       one thread, collect data on each thread */
    949 	    if (have_task && proc->threads > 1)
    950 	    {
    951 		snprintf(buffer, sizeof(buffer), "%d/task", pid);
    952 		if ((taskdir = opendir(buffer)) != NULL)
    953 		{
    954 		    while ((taskent = readdir(taskdir)) != NULL)
    955 		    {
    956 			if (!isdigit(taskent->d_name[0]))
    957 			    continue;
    958 
    959 			/* lookup entry in tasktable */
    960 			taskpid = atoi(taskent->d_name);
    961 			taskproc = hash_lookup_pid(tasktable, taskpid);
    962 
    963 			/* if we came up empty, create a new entry */
    964 			if (taskproc == NULL)
    965 			{
    966 			    taskproc = new_proc();
    967 			    taskproc->pid = taskpid;
    968 			    taskproc->time = 0;
    969 			    hash_add_pid(tasktable, taskpid, (void *)taskproc);
    970 			}
    971 
    972 			/* remember the previous cpu time */
    973 			otime = taskproc->time;
    974 
    975 			/* get current data */
    976 			read_one_proc_stat(pid, taskpid, taskproc, sel);
    977 
    978 			/* ignore if it isnt real */
    979 			if (taskproc->state == 0)
    980 			    continue;
    981 
    982 			/* when showing threads, add this to the accumulated
    983 			   process state data, but remember that the first
    984 			   thread is already accounted for */
    985 			if (show_threads && pid != taskpid)
    986 			{
    987 			    total_procs++;
    988 			    process_states[taskproc->state]++;
    989 			}
    990 
    991 			/* calculate pcpu */
    992 			if ((taskproc->pcpu = (taskproc->time - otime) /
    993 			     (double)elapsed) < 0.0)
    994 			{
    995 			    taskproc->pcpu = 0;
    996 			}
    997 
    998 			/* link this in to the proc's list */
    999 			taskproc->next = proc->next;
   1000 			proc->next = taskproc;
   1001 		    }
   1002 		    closedir(taskdir);
   1003 		}
   1004 	    }
   1005 	}
   1006 	closedir(dir);
   1007 
   1008 	/* make sure we have enough slots for the active procs */
   1009 	if (activesize < total_procs)
   1010 	{
   1011 	    pactive = (struct top_proc **)realloc(pactive,
   1012 				  sizeof(struct top_proc *) * total_procs);
   1013 	    activesize = total_procs;
   1014 	}
   1015 
   1016 	/* set up the active procs and flush dead entries */
   1017 	active = pactive;
   1018 	hi = hash_first_pid(ptable, &pos);
   1019 	while (hi != NULL)
   1020 	{
   1021 	    proc = (struct top_proc *)(hi->value);
   1022 	    if (proc->state == 0)
   1023 	    {
   1024 		/* dead entry */
   1025 		hash_remove_pos_pid(&pos);
   1026 		free_proc(proc);
   1027 	    }
   1028 	    else
   1029 	    {
   1030 		/* check to see if it qualifies as active */
   1031 		if ((show_idle || proc->state == 1 || proc->pcpu) &&
   1032 		    (!show_uid || proc->uid == sel->uid) &&
   1033 		    (show_command == NULL ||
   1034 		     strstr(proc->name, show_command) != NULL))
   1035 		{
   1036 		    /* are we showing threads and does this proc have any? */
   1037 		    if (show_threads && proc->threads > 1 && proc->next != NULL)
   1038 		    {
   1039 			/* then add just the thread info -- the main process
   1040 			   info is included in the list */
   1041 			proc = proc->next;
   1042 			while (proc != NULL)
   1043 			{
   1044 			    *active++ = proc;
   1045 			    proc = proc->next;
   1046 			}
   1047 		    }
   1048 		    else
   1049 		    {
   1050 			/* add the process */
   1051 			*active++ = proc;
   1052 		    }
   1053 		}
   1054 	    }
   1055 
   1056 	    hi = hash_next_pid(&pos);
   1057 	}
   1058 
   1059 	si->p_active = active - pactive;
   1060 	si->p_total = total_procs;
   1061 	si->procstates = process_states;
   1062     }
   1063 
   1064     /* if requested, sort the "active" procs */
   1065     if (si->p_active)
   1066 	qsort(pactive, si->p_active, sizeof(struct top_proc *),
   1067 	      proc_compares[compare_index]);
   1068 
   1069     /* don't even pretend that the return value thing here isn't bogus */
   1070     nextactive = pactive;
   1071     return (caddr_t)0;
   1072 }
   1073 
   1074 
   1075 char *
   1076 format_header(char *uname_field)
   1077 
   1078 {
   1079     int uname_len = strlen(uname_field);
   1080     if (uname_len > 8)
   1081 	uname_len = 8;
   1082 
   1083     memcpy(strchr(fmt_header, 'X'), uname_field, uname_len);
   1084 
   1085     return fmt_header;
   1086 }
   1087 
   1088 static char p_header[MAX_COLS];
   1089 
   1090 char *
   1091 format_process_header(struct process_select *sel, caddr_t handle, int count)
   1092 
   1093 {
   1094     char *h;
   1095 
   1096     h = sel->threads ? proc_header_nothr : proc_header_thr;
   1097 
   1098     snprintf(p_header, MAX_COLS, h, sel->usernames ? "USERNAME" : "UID");
   1099 
   1100     return p_header;
   1101 }
   1102 
   1103 
   1104 char *
   1105 format_next_process(caddr_t handle, char *(*get_userid)(int))
   1106 
   1107 {
   1108     static char fmt[MAX_COLS];	/* static area where result is built */
   1109     struct top_proc *p = *nextactive++;
   1110     char *userbuf;
   1111 
   1112     userbuf = show_usernames ? username(p->uid) : itoa_w(p->uid, 7);
   1113 
   1114     if (show_threads)
   1115     {
   1116 	snprintf(fmt, sizeof(fmt),
   1117 		 "%5d %-8.8s  %3d %4d %5s %5s %5s %-5s %6s %5s%% %s",
   1118 		 p->pid,
   1119 		 userbuf,
   1120 		 p->pri < -99 ? -99 : p->pri,
   1121 		 p->nice,
   1122 		 format_k(p->size),
   1123 		 format_k(p->rss),
   1124 		 format_k(p->shared),
   1125 		 state_abbrev[p->state],
   1126 		 format_time(p->time / HZ),
   1127 		 format_percent(p->pcpu * 100.0),
   1128 		 p->name);
   1129     }
   1130     else
   1131     {
   1132 	snprintf(fmt, sizeof(fmt),
   1133 		 "%5d %-8.8s %4d %3d %4d %5s %5s %5s %-5s %6s %5s%% %s",
   1134 		 p->pid,
   1135 		 userbuf,
   1136 		 p->threads <= 9999 ? p->threads : 9999,
   1137 		 p->pri < -99 ? -99 : p->pri,
   1138 		 p->nice,
   1139 		 format_k(p->size),
   1140 		 format_k(p->rss),
   1141 		 format_k(p->shared),
   1142 		 state_abbrev[p->state],
   1143 		 format_time(p->time / HZ),
   1144 		 format_percent(p->pcpu * 100.0),
   1145 		 p->name);
   1146     }
   1147 
   1148     /* return the result */
   1149     return (fmt);
   1150 }
   1151 
   1152 /* comparison routines for qsort */
   1153 
   1154 /*
   1155  * There are currently four possible comparison routines.  main selects
   1156  * one of these by indexing in to the array proc_compares.
   1157  *
   1158  * Possible keys are defined as macros below.  Currently these keys are
   1159  * defined:  percent cpu, cpu ticks, process state, resident set size,
   1160  * total virtual memory usage.  The process states are ordered as follows
   1161  * (from least to most important):  WAIT, zombie, sleep, stop, start, run.
   1162  * The array declaration below maps a process state index into a number
   1163  * that reflects this ordering.
   1164  */
   1165 
   1166 /* First, the possible comparison keys.  These are defined in such a way
   1167    that they can be merely listed in the source code to define the actual
   1168    desired ordering.
   1169  */
   1170 
   1171 #define ORDERKEY_PCTCPU  if (dresult = p2->pcpu - p1->pcpu,\
   1172 			 (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0)
   1173 #define ORDERKEY_CPTICKS if ((result = (long)p2->time - (long)p1->time) == 0)
   1174 #define ORDERKEY_STATE   if ((result = (sort_state[p2->state] - \
   1175 			 sort_state[p1->state])) == 0)
   1176 #define ORDERKEY_PRIO    if ((result = p2->pri - p1->pri) == 0)
   1177 #define ORDERKEY_RSSIZE  if ((result = p2->rss - p1->rss) == 0)
   1178 #define ORDERKEY_MEM     if ((result = p2->size - p1->size) == 0)
   1179 #define ORDERKEY_NAME    if ((result = strcmp(p1->name, p2->name)) == 0)
   1180 
   1181 /* Now the array that maps process state to a weight */
   1182 
   1183 unsigned char sort_state[] =
   1184 {
   1185 	0,	/* empty */
   1186 	6, 	/* run */
   1187 	3,	/* sleep */
   1188 	5,	/* disk wait */
   1189 	1,	/* zombie */
   1190 	2,	/* stop */
   1191 	4	/* swap */
   1192 };
   1193 
   1194 
   1195 /* compare_cpu - the comparison function for sorting by cpu percentage */
   1196 
   1197 int
   1198 compare_cpu (
   1199 	       struct top_proc **pp1,
   1200 	       struct top_proc **pp2)
   1201   {
   1202     register struct top_proc *p1;
   1203     register struct top_proc *p2;
   1204     register long result;
   1205     double dresult;
   1206 
   1207     /* remove one level of indirection */
   1208     p1 = *pp1;
   1209     p2 = *pp2;
   1210 
   1211     ORDERKEY_PCTCPU
   1212     ORDERKEY_CPTICKS
   1213     ORDERKEY_STATE
   1214     ORDERKEY_PRIO
   1215     ORDERKEY_RSSIZE
   1216     ORDERKEY_MEM
   1217     ;
   1218 
   1219     return result == 0 ? 0 : result < 0 ? -1 : 1;
   1220   }
   1221 
   1222 /* compare_size - the comparison function for sorting by total memory usage */
   1223 
   1224 int
   1225 compare_size (
   1226 	       struct top_proc **pp1,
   1227 	       struct top_proc **pp2)
   1228   {
   1229     register struct top_proc *p1;
   1230     register struct top_proc *p2;
   1231     register long result;
   1232     double dresult;
   1233 
   1234     /* remove one level of indirection */
   1235     p1 = *pp1;
   1236     p2 = *pp2;
   1237 
   1238     ORDERKEY_MEM
   1239     ORDERKEY_RSSIZE
   1240     ORDERKEY_PCTCPU
   1241     ORDERKEY_CPTICKS
   1242     ORDERKEY_STATE
   1243     ORDERKEY_PRIO
   1244     ;
   1245 
   1246     return result == 0 ? 0 : result < 0 ? -1 : 1;
   1247   }
   1248 
   1249 /* compare_res - the comparison function for sorting by resident set size */
   1250 
   1251 int
   1252 compare_res (
   1253 	       struct top_proc **pp1,
   1254 	       struct top_proc **pp2)
   1255   {
   1256     register struct top_proc *p1;
   1257     register struct top_proc *p2;
   1258     register long result;
   1259     double dresult;
   1260 
   1261     /* remove one level of indirection */
   1262     p1 = *pp1;
   1263     p2 = *pp2;
   1264 
   1265     ORDERKEY_RSSIZE
   1266     ORDERKEY_MEM
   1267     ORDERKEY_PCTCPU
   1268     ORDERKEY_CPTICKS
   1269     ORDERKEY_STATE
   1270     ORDERKEY_PRIO
   1271     ;
   1272 
   1273     return result == 0 ? 0 : result < 0 ? -1 : 1;
   1274   }
   1275 
   1276 /* compare_time - the comparison function for sorting by total cpu time */
   1277 
   1278 int
   1279 compare_time (
   1280 	       struct top_proc **pp1,
   1281 	       struct top_proc **pp2)
   1282   {
   1283     register struct top_proc *p1;
   1284     register struct top_proc *p2;
   1285     register long result;
   1286     double dresult;
   1287 
   1288     /* remove one level of indirection */
   1289     p1 = *pp1;
   1290     p2 = *pp2;
   1291 
   1292     ORDERKEY_CPTICKS
   1293     ORDERKEY_PCTCPU
   1294     ORDERKEY_STATE
   1295     ORDERKEY_PRIO
   1296     ORDERKEY_MEM
   1297     ORDERKEY_RSSIZE
   1298     ;
   1299 
   1300     return result == 0 ? 0 : result < 0 ? -1 : 1;
   1301   }
   1302 
   1303 
   1304 /* compare_cmd - the comparison function for sorting by command name */
   1305 
   1306 int
   1307 compare_cmd (
   1308 	       struct top_proc **pp1,
   1309 	       struct top_proc **pp2)
   1310   {
   1311     register struct top_proc *p1;
   1312     register struct top_proc *p2;
   1313     register long result;
   1314     double dresult;
   1315 
   1316     /* remove one level of indirection */
   1317     p1 = *pp1;
   1318     p2 = *pp2;
   1319 
   1320     ORDERKEY_NAME
   1321     ORDERKEY_PCTCPU
   1322     ORDERKEY_CPTICKS
   1323     ORDERKEY_STATE
   1324     ORDERKEY_PRIO
   1325     ORDERKEY_RSSIZE
   1326     ORDERKEY_MEM
   1327     ;
   1328 
   1329     return result == 0 ? 0 : result < 0 ? -1 : 1;
   1330   }
   1331 
   1332 
   1333 /*
   1334  * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
   1335  *              the process does not exist.
   1336  *              It is EXTREMLY IMPORTANT that this function work correctly.
   1337  *              If top runs setuid root (as in SVR4), then this function
   1338  *              is the only thing that stands in the way of a serious
   1339  *              security problem.  It validates requests for the "kill"
   1340  *              and "renice" commands.
   1341  */
   1342 
   1343 int
   1344 proc_owner(int pid)
   1345 
   1346 {
   1347     struct stat sb;
   1348     char buffer[32];
   1349     sprintf(buffer, "%d", pid);
   1350 
   1351     if (stat(buffer, &sb) < 0)
   1352 	return -1;
   1353     else
   1354 	return (int)sb.st_uid;
   1355 }
   1356