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:  any hp9000 running hpux version 10.x
37 *
38 * DESCRIPTION:
39 * This is the machine-dependent module for HPUX 10/11 that uses pstat.
40 * It has been tested on HP/UX 10.01, 10.20, and 11.00.  It is presumed
41 * to work also on 10.10.
42 * Idle processes are marked by being either runnable or having a %CPU
43 * of at least 0.1%.  This fraction is defined by CPU_IDLE_THRESH and
44 * can be adjusted at compile time.
45 *
46 * CFLAGS: -DHAVE_GETOPT
47 *
48 * LIBS:
49 *
50 * AUTHOR: John Haxby <john_haxby@hp.com>
51 * AUTHOR: adapted from Rich Holland <holland@synopsys.com>
52 * AUTHOR: adapted from Kevin Schmidt <kevin@mcl.ucsb.edu>
53 */
54
55#include "config.h"
56#include <stdio.h>
57#include <errno.h>
58#include <unistd.h>
59#include <ctype.h>
60#include <signal.h>
61#include <nlist.h>
62#include <fcntl.h>
63#include <stdlib.h>
64
65#include <sys/types.h>
66#include <sys/param.h>
67#include <sys/pstat.h>
68#include <sys/dk.h>
69#include <sys/stat.h>
70#include <sys/dirent.h>
71
72#include "top.h"
73#include "machine.h"
74#include "utils.h"
75
76/*
77 * The idle threshold (CPU_IDLE_THRESH) is an extension to the normal
78 * idle process check.  Basically, we regard a process as idle if it is
79 * both asleep and using less that CPU_IDLE_THRESH percent cpu time.  I
80 * believe this makes the "i" option more useful, but if you don't, add
81 * "-DCPU_IDLE_THRESH=0.0" to the CFLAGS.
82 */
83#ifndef CPU_IDLE_THRESH
84#define CPU_IDLE_THRESH 0.1
85#endif
86
87# define P_RSSIZE(p) (p)->pst_rssize
88# define P_TSIZE(p) (p)->pst_tsize
89# define P_DSIZE(p) (p)->pst_dsize
90# define P_SSIZE(p) (p)->pst_ssize
91
92#define VMUNIX	"/stand/vmunix"
93#define KMEM	"/dev/kmem"
94#define MEM	"/dev/mem"
95#ifdef DOSWAP
96#define SWAP	"/dev/dmem"
97#endif
98
99/* what we consider to be process size: */
100#define PROCSIZE(pp) (P_TSIZE(pp) + P_DSIZE(pp) + P_SSIZE(pp))
101
102/* definitions for indices in the nlist array */
103#define X_MPID		0
104
105static struct nlist nlst[] = {
106    { "mpid" },
107    { 0 }
108};
109
110/*
111 *  These definitions control the format of the per-process area
112 */
113
114static char header[] =
115  "     TTY   PID X         PRI NICE  SIZE   RES STATE   TIME    CPU COMMAND";
116/* 0123456789.12345 -- field to fill in starts at header+6 */
117#define UNAME_START 15
118
119#define Proc_format \
120	"%8.8s %5d %-8.8s %4d %4d %5s %5s %-5s %6s %5.2f%% %s"
121
122/* process state names for the "STATE" column of the display */
123
124char *state_abbrev[] =
125{
126    "", "sleep", "run", "stop", "zomb", "trans", "start"
127};
128
129
130/* values that we stash away in _init and use in later routines */
131static int kmem;
132static struct pst_status *pst;
133
134/* these are retrieved from the OS in _init */
135static int nproc;
136static int ncpu = 0;
137
138/* these are offsets obtained via nlist and used in the get_ functions */
139static unsigned long mpid_offset;
140
141/* these are for calculating cpu state percentages */
142static long cp_time[PST_MAX_CPUSTATES];
143static long cp_old[PST_MAX_CPUSTATES];
144static long cp_diff[PST_MAX_CPUSTATES];
145
146/* these are for detailing the process states */
147int process_states[7];
148char *procstatenames[] = {
149    "", " sleeping, ", " running, ", " stopped, ", " zombie, ",
150    " trans, ", " starting, ",
151    NULL
152};
153
154/* these are for detailing the cpu states */
155int cpu_states[PST_MAX_CPUSTATES];
156char *cpustatenames[] = {
157    /* roll "swait" into "block" and "ssys" into "sys" */
158    "usr", "nice", "sys", "idle", "", "block", "\0swait", "intr", "\0ssys",
159    NULL
160};
161
162/* these are for detailing the memory statistics */
163long memory_stats[8];
164char *memorynames[] = {
165    "Real: ", "K act, ", "K tot  ", "Virtual: ", "K act, ",
166    "K tot, ", "K free", NULL
167};
168
169/* these are for getting the memory statistics */
170static int pageshift;		/* log base 2 of the pagesize */
171
172/* define pagetok in terms of pageshift */
173#define pagetok(size) ((size) << pageshift)
174
175/* Mapping TTY major/minor numbers is done through this structure */
176struct ttymap {
177    dev_t dev;
178    char name [9];
179};
180static struct ttymap *ttynames = NULL;
181static int nttys = 0;
182static get_tty_names ();
183
184/* comparison routine for qsort */
185
186/*
187 *  proc_compare - comparison function for "qsort"
188 *	Compares the resource consumption of two processes using five
189 *  	distinct keys.  The keys (in descending order of importance) are:
190 *  	percent cpu, cpu ticks, state, resident set size, total virtual
191 *  	memory usage.  The process states are ordered as follows (from least
192 *  	to most important):  WAIT, zombie, sleep, stop, start, run.  The
193 *  	array declaration below maps a process state index into a number
194 *  	that reflects this ordering.
195 */
196
197static unsigned char sorted_state[] =
198{
199    0,	/* not used		*/
200    3,	/* sleep		*/
201    6,	/* run			*/
202    4,	/* stop			*/
203    2,	/* zombie		*/
204    5,	/* start		*/
205    1,  /* other                */
206};
207
208proc_compare(p1, p2)
209struct pst_status *p1;
210struct pst_status *p2;
211
212{
213    int result;
214    float lresult;
215
216    /* compare percent cpu (pctcpu) */
217    if ((lresult = p2->pst_pctcpu - p1->pst_pctcpu) == 0)
218    {
219	/* use cpticks to break the tie */
220	if ((result = p2->pst_cpticks - p1->pst_cpticks) == 0)
221	{
222	    /* use process state to break the tie */
223	    if ((result = sorted_state[p2->pst_stat] -
224			  sorted_state[p1->pst_stat])  == 0)
225	    {
226		/* use priority to break the tie */
227		if ((result = p2->pst_pri - p1->pst_pri) == 0)
228		{
229		    /* use resident set size (rssize) to break the tie */
230		    if ((result = P_RSSIZE(p2) - P_RSSIZE(p1)) == 0)
231		    {
232			/* use total memory to break the tie */
233			result = PROCSIZE(p2) - PROCSIZE(p1);
234		    }
235		}
236	    }
237	}
238    }
239    else
240    {
241	result = lresult < 0 ? -1 : 1;
242    }
243
244    return(result);
245}
246
247machine_init(statics)
248
249struct statics *statics;
250
251{
252    struct pst_static info;
253    int i = 0;
254    int pagesize;
255
256    /* If we can get mpid from the kernel, we'll use it, otherwise    */
257    /* we'll guess from the most recently started proces              */
258    if ((kmem = open (KMEM, O_RDONLY)) < 0 ||
259	(nlist (VMUNIX, nlst)) < 0 ||
260	(nlst[X_MPID].n_type) == 0)
261	mpid_offset = 0;
262    else
263	mpid_offset = nlst[X_MPID].n_value;
264
265    if (pstat_getstatic (&info, sizeof (info), 1, 0) < 0)
266    {
267	perror ("pstat_getstatic");
268	return -1;
269    }
270
271    /*
272     * Allocate space for the per-process structures (pst_status).  To
273     * make life easier, simply allocate enough storage to hold all the
274     * process information at once.  This won't normally be a problem
275     * since machines with lots of processes configured will also have
276     * lots of memory.
277     */
278    nproc = info.max_proc;
279    pst = (struct pst_status *) malloc (nproc * sizeof (struct pst_status));
280    if (pst == NULL)
281    {
282	fprintf (stderr, "out of memory\n");
283	return -1;
284    }
285
286    /*
287     * Calculate pageshift -- the value needed to convert pages to Kbytes.
288     * This will usually be 2.
289     */
290    pageshift = 0;
291    for (pagesize = info.page_size; pagesize > 1; pagesize >>= 1)
292	pageshift += 1;
293    pageshift -= LOG1024;
294
295    /* get tty name information */
296    i = 0;
297    get_tty_names ("/dev", &i);
298
299    /* fill in the statics information */
300    statics->procstate_names = procstatenames;
301    statics->cpustate_names = cpustatenames;
302    statics->memory_names = memorynames;
303
304    /* all done! */
305    return(0);
306}
307
308char *format_header(uname_field)
309char *uname_field;
310{
311    char *ptr = header + UNAME_START;
312    while (*uname_field != '\0')
313	*ptr++ = *uname_field++;
314
315    return header;
316}
317
318void
319get_system_info(si)
320
321struct system_info *si;
322
323{
324    static struct pst_dynamic dynamic;
325    int i, n;
326    long total;
327
328    pstat_getdynamic (&dynamic, sizeof (dynamic), 1, 0);
329    ncpu = dynamic.psd_proc_cnt;  /* need this later */
330
331    /* Load average */
332    si->load_avg[0] = dynamic.psd_avg_1_min;
333    si->load_avg[1] = dynamic.psd_avg_5_min;
334    si->load_avg[2] = dynamic.psd_avg_15_min;
335
336    /*
337     * CPU times
338     * to avoid space problems, we roll SWAIT (kernel semaphore block)
339     * into BLOCK (spin lock block) and SSYS (kernel process) into SYS
340     * (system time) Ideally, all screens would be wider :-)
341     */
342    dynamic.psd_cpu_time [CP_BLOCK] += dynamic.psd_cpu_time [CP_SWAIT];
343    dynamic.psd_cpu_time [CP_SWAIT] = 0;
344    dynamic.psd_cpu_time [CP_SYS] += dynamic.psd_cpu_time [CP_SSYS];
345    dynamic.psd_cpu_time [CP_SSYS] = 0;
346    for (i = 0; i < PST_MAX_CPUSTATES; i++)
347	cp_time [i] = dynamic.psd_cpu_time [i];
348    percentages(PST_MAX_CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
349    si->cpustates = cpu_states;
350
351    /*
352     * VM statistics
353     */
354    memory_stats[0] = -1;
355    memory_stats[1] = pagetok (dynamic.psd_arm);
356    memory_stats[2] = pagetok (dynamic.psd_rm);
357    memory_stats[3] = -1;
358    memory_stats[4] = pagetok (dynamic.psd_avm);
359    memory_stats[5] = pagetok (dynamic.psd_vm);
360    memory_stats[6] = pagetok (dynamic.psd_free);
361    si->memory = memory_stats;
362
363    /*
364     * If we can get mpid from the kernel, then we will do so now.
365     * Otherwise we'll guess at mpid from the most recently started
366     * process time.  Note that this requires us to get the pst array
367     * now rather than in get_process_info().  We rely on
368     * get_system_info() being called before get_system_info() for this
369     * to work reliably.
370     */
371    for (i = 0; i < nproc; i++)
372	pst[i].pst_pid = -1;
373    n = pstat_getproc (pst, sizeof (*pst), nproc, 0);
374
375    if (kmem >= 0 && mpid_offset > 0)
376	(void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid), "mpid");
377    else
378    {
379	static int last_start_time = 0;
380	int pid = 0;
381
382	for (i = 0; i < n; i++)
383	{
384	    if (last_start_time <= pst[i].pst_start)
385	    {
386	    	last_start_time = pst[i].pst_start;
387		if (pid <= pst[i].pst_pid)
388		    pid = pst[i].pst_pid;
389	    }
390	}
391	if (pid != 0)
392	    si->last_pid = pid;
393    }
394}
395
396caddr_t get_process_info(si, sel, compare_index)
397
398struct system_info *si;
399struct process_select *sel;
400int compare_index;
401
402{
403    static int handle;
404    int i, active, total;
405
406    /*
407     * Eliminate unwanted processes
408     * and tot up all the wanted processes by state
409     */
410    for (i = 0; i < sizeof (process_states)/sizeof (process_states[0]); i++)
411	process_states [i] = 0;
412
413    for (total = 0, active = 0, i = 0; pst[i].pst_pid >= 0; i++)
414    {
415	int state = pst[i].pst_stat;
416
417	process_states [state] += 1;
418	total += 1;
419
420	if (!sel->system && (pst[i].pst_flag & PS_SYS))
421	{
422	    pst[i].pst_stat = -1;
423	    continue;
424	}
425
426	/*
427	 * If we are eliminating idle processes, then a process is regarded
428	 * as idle if it is in a short term sleep and not using much
429	 * CPU, or stopped, or simple dead.
430	 */
431	if (!sel->idle
432	    && (state == PS_SLEEP || state == PS_STOP || state == PS_ZOMBIE)
433	    && (state != PS_SLEEP && pst[i].pst_pctcpu < CPU_IDLE_THRESH/100.0))
434	    pst[i].pst_stat = -1;
435
436	if (sel->uid > 0 && sel->uid != pst[i].pst_uid)
437	    pst[i].pst_stat = -1;
438
439	if (sel->command != NULL &&
440	    strncmp (sel->command, pst[i].pst_ucomm, strlen (pst[i].pst_ucomm)) != 0)
441	    pst[i].pst_stat = -1;
442
443	if (pst[i].pst_stat >= 0)
444	    active += 1;
445    }
446    si->procstates = process_states;
447    si->p_total = total;
448    si->p_active = active;
449
450    qsort ((char *)pst, i, sizeof(*pst), proc_compare);
451
452    /* handle is simply an index into the process structures */
453    handle = 0;
454    return (caddr_t) &handle;
455}
456
457/*
458 * Find the terminal name associated with a particular
459 * major/minor number pair
460 */
461static char *term_name (term)
462struct psdev *term;
463{
464    dev_t dev;
465    int i;
466
467    if (term->psd_major == -1 && term->psd_minor == -1)
468	return "?";
469
470    dev = makedev (term->psd_major, term->psd_minor);
471    for (i = 0; i < nttys && ttynames[i].name[0] != '\0'; i++)
472    {
473	if (dev == ttynames[i].dev)
474	    return ttynames[i].name;
475    }
476    return "<unk>";
477}
478
479char *format_next_process(handle, get_userid)
480
481caddr_t handle;
482char *(*get_userid)();
483
484{
485    static char fmt[MAX_COLS];	/* static area where result is built */
486    char run [sizeof ("runNN")];
487    int idx;
488    struct pst_status *proc;
489    char *state;
490    int size;
491
492    register long cputime;
493    register double pct;
494    int where;
495    struct handle *hp;
496    struct timeval time;
497    struct timezone timezone;
498
499    /* sanity check */
500    if (handle == NULL)
501	return "";
502
503    idx = *((int *) handle);
504    while (idx < nproc && pst[idx].pst_stat < 0)
505	idx += 1;
506    if (idx >= nproc || pst[idx].pst_stat < 0)
507	return "";
508    proc = &pst[idx];
509    *((int *) handle) = idx+1;
510
511    /* set ucomm for system processes, although we shouldn't need to */
512    if (proc->pst_ucomm[0] == '\0')
513    {
514	if (proc->pst_pid == 0)
515	    strcpy (proc->pst_ucomm, "Swapper");
516	else if (proc->pst_pid == 2)
517	    strcpy (proc->pst_ucomm, "Pager");
518    }
519
520    size = proc->pst_tsize + proc->pst_dsize + proc->pst_ssize;
521
522    if (ncpu > 1 && proc->pst_stat == PS_RUN)
523    {
524	sprintf (run, "run%02d", proc->pst_procnum);
525	state = run;
526    }
527    else if (proc->pst_stat == PS_SLEEP)
528    {
529	switch (proc->pst_pri+PTIMESHARE) {
530	case PSWP:	state = "SWP"; break; /* also PMEM */
531	case PRIRWLOCK:	state = "RWLOCK"; break;
532	case PRIBETA:	state = "BETA"; break;
533	case PRIALPHA:	state = "ALPHA"; break;
534	case PRISYNC:	state = "SYNC"; break;
535	case PINOD:	state = "INOD"; break;
536	case PRIBIO:	state = "BIO"; break;
537	case PLLIO:	state = "LLIO"; break; /* also PRIUBA  */
538	case PZERO:	state = "ZERO"; break;
539	case PPIPE:	state = "pipe"; break;
540	case PVFS:	state = "vfs"; break;
541	case PWAIT:	state = "wait"; break;
542	case PLOCK:	state = "lock"; break;
543	case PSLEP:	state = "slep"; break;
544	case PUSER:	state = "user"; break;
545	default:
546	    if (proc->pst_pri < PZERO-PTIMESHARE)
547		state = "SLEEP";
548	    else
549		state = "sleep";
550	}
551    }
552    else
553	state = state_abbrev [proc->pst_stat];
554
555    /* format this entry */
556    sprintf(fmt,
557	    Proc_format,
558	    term_name (&proc->pst_term),
559	    proc->pst_pid,
560	    (*get_userid)(proc->pst_uid),
561	    proc->pst_pri,
562	    proc->pst_nice - NZERO,
563	    format_k(size),
564	    format_k(proc->pst_rssize),
565	    state,
566	    format_time(proc->pst_utime + proc->pst_stime),
567	    100.0 * proc->pst_pctcpu,
568	    printable(proc->pst_ucomm));
569
570    /* return the result */
571    return(fmt);
572}
573
574
575
576/*
577 *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
578 *	"offset" is the byte offset into the kernel for the desired value,
579 *  	"ptr" points to a buffer into which the value is retrieved,
580 *  	"size" is the size of the buffer (and the object to retrieve),
581 *  	"refstr" is a reference string used when printing error meessages,
582 *	    if "refstr" starts with a '!', then a failure on read will not
583 *  	    be fatal (this may seem like a silly way to do things, but I
584 *  	    really didn't want the overhead of another argument).
585 *
586 */
587
588getkval(offset, ptr, size, refstr)
589
590unsigned long offset;
591int *ptr;
592int size;
593char *refstr;
594
595{
596    if (lseek(kmem, (long)offset, SEEK_SET) == -1) {
597        if (*refstr == '!')
598            refstr++;
599        (void) fprintf(stderr, "%s: lseek to %s: %s\n", KMEM,
600		       refstr, strerror(errno));
601        quit(23);
602    }
603    if (read(kmem, (char *) ptr, size) == -1) {
604        if (*refstr == '!')
605            return(0);
606        else {
607            (void) fprintf(stderr, "%s: reading %s: %s\n", KMEM,
608			   refstr, strerror(errno));
609            quit(23);
610        }
611    }
612    return(1);
613}
614
615void (*signal(sig, func))()
616    int sig;
617    void (*func)();
618{
619    struct sigaction act;
620    struct sigaction oact;
621
622    memset (&act, 0, sizeof (act));
623    act.sa_handler = func;
624
625    if (sigaction (sig, &act, &oact) < 0)
626	return BADSIG;
627    return oact.sa_handler;
628}
629
630/*
631 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
632 *		the process does not exist.
633 *		It is EXTREMLY IMPORTANT that this function work correctly.
634 *		If top runs setuid root (as in SVR4), then this function
635 *		is the only thing that stands in the way of a serious
636 *		security problem.  It validates requests for the "kill"
637 *		and "renice" commands.
638 */
639int proc_owner(pid)
640int pid;
641{
642    int i;
643
644    for (i = 0;  i < nproc; i++)
645    {
646	if (pst[i].pst_pid == pid)
647	    return pst[i].pst_uid;
648    }
649    return -1;
650}
651
652
653static get_tty_names (dir, m)
654char *dir;
655int *m;
656{
657    char name [MAXPATHLEN+1];
658    struct dirent **namelist;
659    int i, n;
660
661    if ((n = scandir (dir, &namelist, NULL, NULL)) < 0)
662	return;
663
664    if (ttynames == NULL)
665    {
666	nttys = n;
667	ttynames = malloc (n*sizeof (*ttynames));
668    }
669    else
670    {
671	nttys += n;
672	ttynames = realloc (ttynames, nttys*sizeof (*ttynames));
673    }
674
675    for (i = 0; i < n; i++)
676    {
677	struct stat statbuf;
678	char *str = namelist[i]->d_name;
679	if (*str == '.')
680	    continue;
681	sprintf (name, "%s/%s", dir, str);
682	if (stat (name, &statbuf) < 0)
683	    continue;
684
685	if (!isalpha (*str))
686	    str = name + sizeof ("/dev");
687	if (S_ISCHR (statbuf.st_mode))
688	{
689	    ttynames [*m].dev = statbuf.st_rdev;
690	    strncpy (ttynames[*m].name, str, 8);
691	    ttynames[*m].name[9] = '\0';
692	    *m += 1;
693	}
694	else if (S_ISDIR (statbuf.st_mode))
695	    get_tty_names (name, m);
696    }
697    if (*m < nttys)
698	ttynames[*m].name[0] = '\0';
699    free (namelist);
700}
701
702