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:  2.x with thread eliding
37 *
38 * DESCRIPTION:
39 * This is the machine-dependent module for Linux 2.x that elides threads
40 * from the output.
41 *
42 * CFLAGS: -DHAVE_GETOPT -DHAVE_STRERROR -DORDER
43 *
44 * TERMCAP: -lcurses
45 *
46 * AUTHOR: Richard Henderson <rth@tamu.edu>
47 * Order support added by Alexey Klimkin <kad@klon.tme.mcst.ru>
48 * Ported to 2.4 by William LeFebvre
49 * Thread eliding by William LeFebvre
50 */
51
52#include "config.h"
53
54#include <sys/types.h>
55#include <stdio.h>
56#include <fcntl.h>
57#include <unistd.h>
58#include <stdlib.h>
59#include <errno.h>
60#include <dirent.h>
61#include <string.h>
62#include <math.h>
63#include <ctype.h>
64#include <sys/time.h>
65#include <sys/stat.h>
66#include <sys/vfs.h>
67
68#include <sys/param.h>		/* for HZ */
69#include <asm/page.h>		/* for PAGE_SHIFT */
70
71#if 0
72#include <linux/proc_fs.h>	/* for PROC_SUPER_MAGIC */
73#else
74#define PROC_SUPER_MAGIC 0x9fa0
75#endif
76
77#include "top.h"
78#include "machine.h"
79#include "utils.h"
80
81#define PROCFS "/proc"
82extern char *myname;
83
84/*=PROCESS INFORMATION==================================================*/
85
86struct top_proc
87{
88    pid_t pid;
89    pid_t ppid;
90    uid_t uid;
91    char *name;
92    int pri, nice;
93    unsigned long size, rss;	/* in k */
94    int state;
95    unsigned long time;
96    unsigned long start_time;
97    unsigned long otime;
98    unsigned long start_code;
99    unsigned long end_code;
100    unsigned long start_stack;
101    unsigned int threads;
102    double pcpu, wcpu;
103    struct top_proc *next;
104};
105
106
107/*=STATE IDENT STRINGS==================================================*/
108
109#define NPROCSTATES 7
110static char *state_abbrev[NPROCSTATES+1] =
111{
112    "", "run", "sleep", "disk", "zomb", "stop", "swap",
113    NULL
114};
115
116static char *procstatenames[NPROCSTATES+1] =
117{
118    "", " running, ", " sleeping, ", " uninterruptable, ",
119    " zombie, ", " stopped, ", " swapping, ",
120    NULL
121};
122
123#define NCPUSTATES 4
124static char *cpustatenames[NCPUSTATES+1] =
125{
126    "user", "nice", "system", "idle",
127    NULL
128};
129
130#define MEMUSED    0
131#define MEMFREE    1
132#define MEMSHARED  2
133#define MEMBUFFERS 3
134#define MEMCACHED  4
135#define NMEMSTATS  5
136static char *memorynames[NMEMSTATS+1] =
137{
138    "K used, ", "K free, ", "K shared, ", "K buffers, ", "K cached",
139    NULL
140};
141
142#define SWAPUSED   0
143#define SWAPFREE   1
144#define SWAPCACHED 2
145#define NSWAPSTATS 3
146static char *swapnames[NSWAPSTATS+1] =
147{
148    "K used, ", "K free, ", "K cached",
149    NULL
150};
151
152static char fmt_header[] =
153"  PID X        THR PRI NICE  SIZE   RES STATE   TIME    CPU COMMAND";
154
155/* these are names given to allowed sorting orders -- first is default */
156char *ordernames[] =
157{"cpu", "size", "res", "time", "command", NULL};
158
159/* forward definitions for comparison functions */
160int compare_cpu();
161int compare_size();
162int compare_res();
163int compare_time();
164int compare_cmd();
165
166int (*proc_compares[])() = {
167    compare_cpu,
168    compare_size,
169    compare_res,
170    compare_time,
171    compare_cmd,
172    NULL };
173
174/*=SYSTEM STATE INFO====================================================*/
175
176/* these are for calculating cpu state percentages */
177
178static long cp_time[NCPUSTATES];
179static long cp_old[NCPUSTATES];
180static long cp_diff[NCPUSTATES];
181
182/* for calculating the exponential average */
183
184static struct timeval lasttime;
185
186/* these are for keeping track of processes */
187
188#define HASH_SIZE	     (1003)
189#define INITIAL_ACTIVE_SIZE  (256)
190#define PROCBLOCK_SIZE       (32)
191static struct top_proc *ptable[HASH_SIZE];
192static struct top_proc **pactive;
193static struct top_proc **nextactive;
194static unsigned int activesize = 0;
195static time_t boottime = -1;
196
197/* these are for passing data back to the machine independant portion */
198
199static int cpu_states[NCPUSTATES];
200static int process_states[NPROCSTATES];
201static long memory_stats[NMEMSTATS];
202static long swap_stats[NSWAPSTATS];
203
204/* usefull macros */
205#define bytetok(x)	(((x) + 512) >> 10)
206#define pagetok(x)	((x) << (PAGE_SHIFT - 10))
207#define HASH(x)		(((x) * 1686629713U) % HASH_SIZE)
208
209/*======================================================================*/
210
211static inline char *
212skip_ws(const char *p)
213{
214    while (isspace(*p)) p++;
215    return (char *)p;
216}
217
218static inline char *
219skip_token(const char *p)
220{
221    while (isspace(*p)) p++;
222    while (*p && !isspace(*p)) p++;
223    return (char *)p;
224}
225
226static void
227xfrm_cmdline(char *p, int len)
228{
229    while (--len > 0)
230    {
231	if (*p == '\0')
232	{
233	    *p = ' ';
234	}
235	p++;
236    }
237}
238
239static void
240update_procname(struct top_proc *proc, char *cmd)
241
242{
243    printable(cmd);
244
245    if (proc->name == NULL)
246    {
247	proc->name = strdup(cmd);
248    }
249    else if (strcmp(proc->name, cmd) != 0)
250    {
251	free(proc->name);
252	proc->name = strdup(cmd);
253    }
254}
255
256
257
258
259/*
260 * Process structures are allocated and freed as needed.  Here we
261 * keep big pools of them, adding more pool as needed.  When a
262 * top_proc structure is freed, it is added to a freelist and reused.
263 */
264
265static struct top_proc *freelist = NULL;
266static struct top_proc *procblock = NULL;
267static struct top_proc *procmax = NULL;
268
269static struct top_proc *
270new_proc()
271{
272    struct top_proc *p;
273
274    if (freelist)
275    {
276	p = freelist;
277	freelist = freelist->next;
278    }
279    else if (procblock)
280    {
281	p = procblock;
282	if (++procblock >= procmax)
283	{
284	    procblock = NULL;
285	}
286    }
287    else
288    {
289	p = procblock = (struct top_proc *)calloc(PROCBLOCK_SIZE,
290						  sizeof(struct top_proc));
291	procmax = procblock++ + PROCBLOCK_SIZE;
292    }
293
294    /* initialization */
295    if (p->name != NULL)
296    {
297	free(p->name);
298	p->name = NULL;
299    }
300
301    return p;
302}
303
304static void
305free_proc(struct top_proc *proc)
306{
307    proc->next = freelist;
308    freelist = proc;
309}
310
311
312int
313machine_init(struct statics *statics)
314
315{
316    /* make sure the proc filesystem is mounted */
317    {
318	struct statfs sb;
319	if (statfs(PROCFS, &sb) < 0 || sb.f_type != PROC_SUPER_MAGIC)
320	{
321	    fprintf(stderr, "%s: proc filesystem not mounted on " PROCFS "\n",
322		    myname);
323	    return -1;
324	}
325    }
326
327    /* chdir to the proc filesystem to make things easier */
328    chdir(PROCFS);
329
330    /* get a boottime */
331    {
332	int fd;
333	char buff[64];
334	char *p;
335	unsigned long uptime;
336	struct timeval tv;
337
338	if ((fd = open("uptime", 0)) != -1)
339	{
340	    if (read(fd, buff, sizeof(buff)) > 0)
341	    {
342		uptime = strtoul(buff, &p, 10);
343		gettimeofday(&tv, 0);
344		boottime = tv.tv_sec - uptime;
345	    }
346	    close(fd);
347	}
348    }
349
350    /* fill in the statics information */
351    statics->procstate_names = procstatenames;
352    statics->cpustate_names = cpustatenames;
353    statics->memory_names = memorynames;
354    statics->swap_names = swapnames;
355    statics->order_names = ordernames;
356    statics->boottime = boottime;
357    statics->flags.fullcmds = 1;
358    statics->flags.warmup = 1;
359
360    /* allocate needed space */
361    pactive = (struct top_proc **)malloc(sizeof(struct top_proc *) * INITIAL_ACTIVE_SIZE);
362    activesize = INITIAL_ACTIVE_SIZE;
363
364    /* make sure the hash table is empty */
365    memset(ptable, 0, HASH_SIZE * sizeof(struct top_proc *));
366
367    /* all done! */
368    return 0;
369}
370
371
372void
373get_system_info(struct system_info *info)
374
375{
376    char buffer[4096+1];
377    int fd, len;
378    char *p;
379
380    /* get load averages */
381
382    if ((fd = open("loadavg", O_RDONLY)) != -1)
383    {
384	if ((len = read(fd, buffer, sizeof(buffer)-1)) > 0)
385	{
386	    buffer[len] = '\0';
387
388	    info->load_avg[0] = strtod(buffer, &p);
389	    info->load_avg[1] = strtod(p, &p);
390	    info->load_avg[2] = strtod(p, &p);
391	    p = skip_token(p);			/* skip running/tasks */
392	    p = skip_ws(p);
393	    if (*p)
394	    {
395		info->last_pid = atoi(p);
396	    }
397	    else
398	    {
399		info->last_pid = -1;
400	    }
401	}
402	close(fd);
403    }
404
405    /* get the cpu time info */
406    if ((fd = open("stat", O_RDONLY)) != -1)
407    {
408	if ((len = read(fd, buffer, sizeof(buffer)-1)) > 0)
409	{
410	    buffer[len] = '\0';
411	    p = skip_token(buffer);			/* "cpu" */
412	    cp_time[0] = strtoul(p, &p, 0);
413	    cp_time[1] = strtoul(p, &p, 0);
414	    cp_time[2] = strtoul(p, &p, 0);
415	    cp_time[3] = strtoul(p, &p, 0);
416
417	    /* convert cp_time counts to percentages */
418	    percentages(4, cpu_states, cp_time, cp_old, cp_diff);
419	}
420	close(fd);
421    }
422
423    /* get system wide memory usage */
424    if ((fd = open("meminfo", O_RDONLY)) != -1)
425    {
426	char *p;
427	int mem = 0;
428	int swap = 0;
429	unsigned long memtotal = 0;
430	unsigned long memfree = 0;
431	unsigned long swaptotal = 0;
432
433	if ((len = read(fd, buffer, sizeof(buffer)-1)) > 0)
434	{
435	    buffer[len] = '\0';
436	    p = buffer-1;
437
438	    /* iterate thru the lines */
439	    while (p != NULL)
440	    {
441		p++;
442		if (p[0] == ' ' || p[0] == '\t')
443		{
444		    /* skip */
445		}
446		else if (strncmp(p, "Mem:", 4) == 0)
447		{
448		    p = skip_token(p);			/* "Mem:" */
449		    p = skip_token(p);			/* total memory */
450		    memory_stats[MEMUSED] = strtoul(p, &p, 10);
451		    memory_stats[MEMFREE] = strtoul(p, &p, 10);
452		    memory_stats[MEMSHARED] = strtoul(p, &p, 10);
453		    memory_stats[MEMBUFFERS] = strtoul(p, &p, 10);
454		    memory_stats[MEMCACHED] = strtoul(p, &p, 10);
455		    memory_stats[MEMUSED] = bytetok(memory_stats[MEMUSED]);
456		    memory_stats[MEMFREE] = bytetok(memory_stats[MEMFREE]);
457		    memory_stats[MEMSHARED] = bytetok(memory_stats[MEMSHARED]);
458		    memory_stats[MEMBUFFERS] = bytetok(memory_stats[MEMBUFFERS]);
459		    memory_stats[MEMCACHED] = bytetok(memory_stats[MEMCACHED]);
460		    mem = 1;
461		}
462		else if (strncmp(p, "Swap:", 5) == 0)
463		{
464		    p = skip_token(p);			/* "Swap:" */
465		    p = skip_token(p);			/* total swap */
466		    swap_stats[SWAPUSED] = strtoul(p, &p, 10);
467		    swap_stats[SWAPFREE] = strtoul(p, &p, 10);
468		    swap_stats[SWAPUSED] = bytetok(swap_stats[SWAPUSED]);
469		    swap_stats[SWAPFREE] = bytetok(swap_stats[SWAPFREE]);
470		    swap = 1;
471		}
472		else if (!mem && strncmp(p, "MemTotal:", 9) == 0)
473		{
474		    p = skip_token(p);
475		    memtotal = strtoul(p, &p, 10);
476		}
477		else if (!mem && memtotal > 0 && strncmp(p, "MemFree:", 8) == 0)
478		{
479		    p = skip_token(p);
480		    memfree = strtoul(p, &p, 10);
481		    memory_stats[MEMUSED] = memtotal - memfree;
482		    memory_stats[MEMFREE] = memfree;
483		}
484		else if (!mem && strncmp(p, "MemShared:", 10) == 0)
485		{
486		    p = skip_token(p);
487		    memory_stats[MEMSHARED] = strtoul(p, &p, 10);
488		}
489		else if (!mem && strncmp(p, "Buffers:", 8) == 0)
490		{
491		    p = skip_token(p);
492		    memory_stats[MEMBUFFERS] = strtoul(p, &p, 10);
493		}
494		else if (!mem && strncmp(p, "Cached:", 7) == 0)
495		{
496		    p = skip_token(p);
497		    memory_stats[MEMCACHED] = strtoul(p, &p, 10);
498		}
499		else if (!swap && strncmp(p, "SwapTotal:", 10) == 0)
500		{
501		    p = skip_token(p);
502		    swaptotal = strtoul(p, &p, 10);
503		}
504		else if (!swap && swaptotal > 0 && strncmp(p, "SwapFree:", 9) == 0)
505		{
506		    p = skip_token(p);
507		    memfree = strtoul(p, &p, 10);
508		    swap_stats[SWAPUSED] = swaptotal - memfree;
509		    swap_stats[SWAPFREE] = memfree;
510		}
511		else if (!mem && strncmp(p, "SwapCached:", 11) == 0)
512		{
513		    p = skip_token(p);
514		    swap_stats[SWAPCACHED] = strtoul(p, &p, 10);
515		}
516
517		/* move to the next line */
518		p = strchr(p, '\n');
519	    }
520	}
521	close(fd);
522    }
523
524    /* set arrays and strings */
525    info->cpustates = cpu_states;
526    info->memory = memory_stats;
527    info->swap = swap_stats;
528}
529
530
531static void
532read_one_proc_stat(pid_t pid, struct top_proc *proc, struct process_select *sel)
533{
534    char buffer[4096], *p, *q;
535    int fd, len;
536    int fullcmd;
537
538    /* if anything goes wrong, we return with proc->state == 0 */
539    proc->state = 0;
540
541    /* full cmd handling */
542    fullcmd = sel->fullcmd;
543    if (fullcmd)
544    {
545	sprintf(buffer, "%d/cmdline", pid);
546	if ((fd = open(buffer, O_RDONLY)) != -1)
547	{
548	    /* read command line data */
549	    /* (theres no sense in reading more than we can fit) */
550	    if ((len = read(fd, buffer, MAX_COLS)) > 1)
551	    {
552		buffer[len] = '\0';
553		xfrm_cmdline(buffer, len);
554		update_procname(proc, buffer);
555	    }
556	    else
557	    {
558		fullcmd = 0;
559	    }
560	    close(fd);
561	}
562	else
563	{
564	    fullcmd = 0;
565	}
566    }
567
568    /* grab the proc stat info in one go */
569    sprintf(buffer, "%d/stat", pid);
570
571    fd = open(buffer, O_RDONLY);
572    len = read(fd, buffer, sizeof(buffer)-1);
573    close(fd);
574
575    buffer[len] = '\0';
576
577    proc->uid = (uid_t)proc_owner((int)pid);
578
579    /* parse out the status */
580
581    /* skip pid and locate command, which is in parentheses */
582    if ((p = strchr(buffer, '(')) == NULL)
583    {
584	return;
585    }
586    if ((q = strrchr(++p, ')')) == NULL)
587    {
588	return;
589    }
590
591    /* set the procname */
592    *q = '\0';
593    if (!fullcmd)
594    {
595	update_procname(proc, p);
596    }
597
598    /* scan the rest of the line */
599    p = q+1;
600    p = skip_ws(p);
601    switch (*p++)				/* state */
602    {
603    case 'R': proc->state = 1; break;
604    case 'S': proc->state = 2; break;
605    case 'D': proc->state = 3; break;
606    case 'Z': proc->state = 4; break;
607    case 'T': proc->state = 5; break;
608    case 'W': proc->state = 6; break;
609    case '\0': return;
610    }
611
612    proc->ppid = strtoul(p, &p, 10);		/* ppid */
613    p = skip_token(p);				/* skip pgrp */
614    p = skip_token(p);				/* skip session */
615    p = skip_token(p);				/* skip tty */
616    p = skip_token(p);				/* skip tty pgrp */
617    p = skip_token(p);				/* skip flags */
618    p = skip_token(p);				/* skip min flt */
619    p = skip_token(p);				/* skip cmin flt */
620    p = skip_token(p);				/* skip maj flt */
621    p = skip_token(p);				/* skip cmaj flt */
622
623    proc->time = strtoul(p, &p, 10);		/* utime */
624    proc->time += strtoul(p, &p, 10);		/* stime */
625
626    p = skip_token(p);				/* skip cutime */
627    p = skip_token(p);				/* skip cstime */
628
629    proc->pri = strtol(p, &p, 10);		/* priority */
630    proc->nice = strtol(p, &p, 10);		/* nice */
631
632    p = skip_token(p);				/* skip timeout */
633    p = skip_token(p);				/* skip it_real_val */
634    proc->start_time = strtoul(p, &p, 10);	/* start_time */
635
636    proc->size = bytetok(strtoul(p, &p, 10));	/* vsize */
637    proc->rss = pagetok(strtoul(p, &p, 10));	/* rss */
638
639    p = skip_token(p);				/* skip rlim */
640    proc->start_code = strtoul(p, &p, 10);	/* start_code */
641    proc->end_code = strtoul(p, &p, 10);	/* end_code */
642    proc->start_stack = strtoul(p, &p, 10);	/* start_stack */
643
644    /* for the record, here are the rest of the fields */
645#if 0
646    p = skip_token(p);				/* skip sp */
647    p = skip_token(p);				/* skip pc */
648    p = skip_token(p);				/* skip signal */
649    p = skip_token(p);				/* skip sigblocked */
650    p = skip_token(p);				/* skip sigignore */
651    p = skip_token(p);				/* skip sigcatch */
652    p = skip_token(p);				/* skip wchan */
653#endif
654}
655
656
657caddr_t
658get_process_info(struct system_info *si,
659		 struct process_select *sel,
660		 int compare_index)
661{
662    struct timeval thistime;
663    double timediff, alpha, beta;
664    struct top_proc *proc;
665    pid_t pid;
666    unsigned long now;
667    unsigned long elapsed;
668    int i;
669
670    /* calculate the time difference since our last check */
671    gettimeofday(&thistime, 0);
672    if (lasttime.tv_sec)
673    {
674	timediff = ((thistime.tv_sec - lasttime.tv_sec) +
675		    (thistime.tv_usec - lasttime.tv_usec) * 1e-6);
676    }
677    else
678    {
679	timediff = 0;
680    }
681    lasttime = thistime;
682
683    /* round current time to a second */
684    now = (unsigned long)thistime.tv_sec;
685    if (thistime.tv_usec >= 500000)
686    {
687	now++;
688    }
689
690    /* calculate constants for the exponental average */
691    if (timediff > 0.0 && timediff < 30.0)
692    {
693	alpha = 0.5 * (timediff / 30.0);
694	beta = 1.0 - alpha;
695    }
696    else
697	alpha = beta = 0.5;
698    timediff *= HZ;  /* convert to ticks */
699
700    /* mark all hash table entries as not seen */
701    for (i = 0; i < HASH_SIZE; ++i)
702    {
703	for (proc = ptable[i]; proc; proc = proc->next)
704	{
705	    proc->state = 0;
706	}
707    }
708
709    /* read the process information */
710    {
711	DIR *dir = opendir(".");
712	struct dirent *ent;
713	int total_procs = 0;
714	struct top_proc **active;
715
716	int show_idle = sel->idle;
717	int show_uid = sel->uid != -1;
718
719	memset(process_states, 0, sizeof(process_states));
720
721	while ((ent = readdir(dir)) != NULL)
722	{
723	    struct top_proc *pp;
724
725	    if (!isdigit(ent->d_name[0]))
726		continue;
727
728	    pid = atoi(ent->d_name);
729
730	    /* look up hash table entry */
731	    proc = pp = ptable[HASH(pid)];
732	    while (proc && proc->pid != pid)
733	    {
734		proc = proc->next;
735	    }
736
737	    /* if we came up empty, create a new entry */
738	    if (proc == NULL)
739	    {
740		proc = new_proc();
741		proc->pid = pid;
742		proc->next = pp;
743		ptable[HASH(pid)] = proc;
744		proc->time = proc->otime = 0;
745	    }
746
747	    read_one_proc_stat(pid, proc, sel);
748	    proc->threads = 1;
749
750	    if (proc->state == 0)
751		continue;
752
753	    total_procs++;
754	    process_states[proc->state]++;
755
756	    /* calculate cpu percentage */
757	    if (timediff > 0.0)
758	    {
759		if ((proc->pcpu = (proc->time - proc->otime) / timediff) < 0.0001)
760		{
761		    proc->pcpu = 0;
762		}
763	    }
764	    else if ((elapsed = (now - boottime)*HZ - proc->start_time) > 0)
765	    {
766		if ((proc->pcpu = (double)proc->time / (double)elapsed) < 0.0001)
767		{
768		    proc->pcpu;
769		}
770	    }
771	    else
772	    {
773		proc->pcpu = 0.0;
774	    }
775
776	    /* remember time for next time */
777	    proc->otime = proc->time;
778	}
779	closedir(dir);
780
781	/* make sure we have enough slots for the active procs */
782	if (activesize < total_procs)
783	{
784	    pactive = (struct top_proc **)realloc(pactive,
785			  sizeof(struct top_proc *) * total_procs);
786	    activesize = total_procs;
787	}
788
789	/* set up the active procs and flush dead entries */
790	/* also coalesce threads */
791	active = pactive;
792	for (i = 0; i < HASH_SIZE; i++)
793	{
794	    struct top_proc *last;
795	    struct top_proc *ptmp;
796	    struct top_proc *parent;
797
798	    last = NULL;
799	    proc = ptable[i];
800	    while (proc != NULL)
801	    {
802		if (proc->state == 0)
803		{
804		    ptmp = proc;
805		    if (last)
806		    {
807			proc = last->next = proc->next;
808		    }
809		    else
810		    {
811			proc = ptable[i] = proc->next;
812		    }
813		    free_proc(ptmp);
814		}
815		else
816		{
817		    /* look up hash table entry for parent */
818		    parent = proc;
819		    do {
820			pid = parent->ppid;
821			parent = ptable[HASH(pid)];
822			while (parent && parent->pid != pid)
823			{
824			    parent = parent->next;
825			}
826		    } while (parent && parent->state == 0);
827
828		    /* does this look like a thread of its parent? */
829		    if (parent && proc->size == parent->size &&
830			proc->rss == parent->rss &&
831			proc->start_code == parent->start_code &&
832			proc->end_code == parent->end_code &&
833			proc->start_stack == parent->start_stack)
834		    {
835			/* yes it does: roll up the cumulative numbers */
836			parent->threads += proc->threads;
837			parent->time += proc->time;
838			parent->pcpu += proc->pcpu;
839
840			/* mark this process as dead (undisplayable) */
841			proc->state = 0;
842		    }
843		    else if ((show_idle || proc->state == 1 || proc->pcpu) &&
844			     (!show_uid || proc->uid == sel->uid))
845		    {
846			*active++ = proc;
847			last = proc;
848		    }
849		    proc = proc->next;
850		}
851	    }
852	}
853
854	si->p_active = active - pactive;
855	si->p_total = total_procs;
856	si->procstates = process_states;
857    }
858
859    /* if requested, sort the "active" procs */
860    if (si->p_active)
861	qsort(pactive, si->p_active, sizeof(struct top_proc *),
862	      proc_compares[compare_index]);
863
864    /* don't even pretend that the return value thing here isn't bogus */
865    nextactive = pactive;
866    return (caddr_t)0;
867}
868
869
870char *
871format_header(char *uname_field)
872
873{
874    int uname_len = strlen(uname_field);
875    if (uname_len > 8)
876	uname_len = 8;
877
878    memcpy(strchr(fmt_header, 'X'), uname_field, uname_len);
879
880    return fmt_header;
881}
882
883
884char *
885format_next_process(caddr_t handle, char *(*get_userid)(int))
886
887{
888    static char fmt[MAX_COLS];	/* static area where result is built */
889    struct top_proc *p = *nextactive++;
890
891    snprintf(fmt, sizeof(fmt),
892	    "%5d %-8.8s %3d %3d %4d %5s %5s %-5s %6s %5.2f%% %s",
893	    p->pid,
894	    (*get_userid)(p->uid),
895	    p->threads,
896	    p->pri < -99 ? -99 : p->pri,
897	    p->nice,
898	    format_k(p->size),
899	    format_k(p->rss),
900	    state_abbrev[p->state],
901	    format_time(p->time / HZ),
902	    p->pcpu * 100.0,
903	    p->name);
904
905    /* return the result */
906    return (fmt);
907}
908
909/* comparison routines for qsort */
910
911/*
912 * There are currently four possible comparison routines.  main selects
913 * one of these by indexing in to the array proc_compares.
914 *
915 * Possible keys are defined as macros below.  Currently these keys are
916 * defined:  percent cpu, cpu ticks, process state, resident set size,
917 * total virtual memory usage.  The process states are ordered as follows
918 * (from least to most important):  WAIT, zombie, sleep, stop, start, run.
919 * The array declaration below maps a process state index into a number
920 * that reflects this ordering.
921 */
922
923/* First, the possible comparison keys.  These are defined in such a way
924   that they can be merely listed in the source code to define the actual
925   desired ordering.
926 */
927
928#define ORDERKEY_PCTCPU  if (dresult = p2->pcpu - p1->pcpu,\
929							 (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0)
930#define ORDERKEY_CPTICKS if ((result = (long)p2->time - (long)p1->time) == 0)
931#define ORDERKEY_STATE   if ((result = (sort_state[p2->state] - \
932			sort_state[p1->state])) == 0)
933#define ORDERKEY_PRIO    if ((result = p2->pri - p1->pri) == 0)
934#define ORDERKEY_RSSIZE  if ((result = p2->rss - p1->rss) == 0)
935#define ORDERKEY_MEM     if ((result = p2->size - p1->size) == 0)
936#define ORDERKEY_NAME    if ((result = strcmp(p1->name, p2->name)) == 0)
937
938/* Now the array that maps process state to a weight */
939
940unsigned char sort_state[] =
941{
942	0,	/* empty */
943	6, 	/* run */
944	3,	/* sleep */
945	5,	/* disk wait */
946	1,	/* zombie */
947	2,	/* stop */
948	4	/* swap */
949};
950
951
952/* compare_cpu - the comparison function for sorting by cpu percentage */
953
954int
955compare_cpu (
956	       struct top_proc **pp1,
957	       struct top_proc **pp2)
958  {
959    register struct top_proc *p1;
960    register struct top_proc *p2;
961    register long result;
962    double dresult;
963
964    /* remove one level of indirection */
965    p1 = *pp1;
966    p2 = *pp2;
967
968    ORDERKEY_PCTCPU
969    ORDERKEY_CPTICKS
970    ORDERKEY_STATE
971    ORDERKEY_PRIO
972    ORDERKEY_RSSIZE
973    ORDERKEY_MEM
974    ;
975
976    return result == 0 ? 0 : result < 0 ? -1 : 1;
977  }
978
979/* compare_size - the comparison function for sorting by total memory usage */
980
981int
982compare_size (
983	       struct top_proc **pp1,
984	       struct top_proc **pp2)
985  {
986    register struct top_proc *p1;
987    register struct top_proc *p2;
988    register long result;
989    double dresult;
990
991    /* remove one level of indirection */
992    p1 = *pp1;
993    p2 = *pp2;
994
995    ORDERKEY_MEM
996    ORDERKEY_RSSIZE
997    ORDERKEY_PCTCPU
998    ORDERKEY_CPTICKS
999    ORDERKEY_STATE
1000    ORDERKEY_PRIO
1001    ;
1002
1003    return result == 0 ? 0 : result < 0 ? -1 : 1;
1004  }
1005
1006/* compare_res - the comparison function for sorting by resident set size */
1007
1008int
1009compare_res (
1010	       struct top_proc **pp1,
1011	       struct top_proc **pp2)
1012  {
1013    register struct top_proc *p1;
1014    register struct top_proc *p2;
1015    register long result;
1016    double dresult;
1017
1018    /* remove one level of indirection */
1019    p1 = *pp1;
1020    p2 = *pp2;
1021
1022    ORDERKEY_RSSIZE
1023    ORDERKEY_MEM
1024    ORDERKEY_PCTCPU
1025    ORDERKEY_CPTICKS
1026    ORDERKEY_STATE
1027    ORDERKEY_PRIO
1028    ;
1029
1030    return result == 0 ? 0 : result < 0 ? -1 : 1;
1031  }
1032
1033/* compare_time - the comparison function for sorting by total cpu time */
1034
1035int
1036compare_time (
1037	       struct top_proc **pp1,
1038	       struct top_proc **pp2)
1039  {
1040    register struct top_proc *p1;
1041    register struct top_proc *p2;
1042    register long result;
1043    double dresult;
1044
1045    /* remove one level of indirection */
1046    p1 = *pp1;
1047    p2 = *pp2;
1048
1049    ORDERKEY_CPTICKS
1050    ORDERKEY_PCTCPU
1051    ORDERKEY_STATE
1052    ORDERKEY_PRIO
1053    ORDERKEY_MEM
1054    ORDERKEY_RSSIZE
1055    ;
1056
1057    return result == 0 ? 0 : result < 0 ? -1 : 1;
1058  }
1059
1060/* compare_cmd - the comparison function for sorting by command name */
1061
1062int
1063compare_cmd (
1064	       struct top_proc **pp1,
1065	       struct top_proc **pp2)
1066  {
1067    register struct top_proc *p1;
1068    register struct top_proc *p2;
1069    register long result;
1070    double dresult;
1071
1072    /* remove one level of indirection */
1073    p1 = *pp1;
1074    p2 = *pp2;
1075
1076    ORDERKEY_NAME
1077    ORDERKEY_PCTCPU
1078    ORDERKEY_CPTICKS
1079    ORDERKEY_STATE
1080    ORDERKEY_PRIO
1081    ORDERKEY_RSSIZE
1082    ORDERKEY_MEM
1083    ;
1084
1085    return result == 0 ? 0 : result < 0 ? -1 : 1;
1086  }
1087
1088
1089/*
1090 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
1091 *              the process does not exist.
1092 *              It is EXTREMLY IMPORTANT that this function work correctly.
1093 *              If top runs setuid root (as in SVR4), then this function
1094 *              is the only thing that stands in the way of a serious
1095 *              security problem.  It validates requests for the "kill"
1096 *              and "renice" commands.
1097 */
1098
1099int
1100proc_owner(int pid)
1101
1102{
1103    struct stat sb;
1104    char buffer[32];
1105    sprintf(buffer, "%d", pid);
1106
1107    if (stat(buffer, &sb) < 0)
1108	return -1;
1109    else
1110	return (int)sb.st_uid;
1111}
1112