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
105 static struct nlist nlst[] = {
106 { "mpid" },
107 { 0 }
108 };
109
110 /*
111 * These definitions control the format of the per-process area
112 */
113
114 static 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
124 char *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 */
131 static int kmem;
132 static struct pst_status *pst;
133
134 /* these are retrieved from the OS in _init */
135 static int nproc;
136 static int ncpu = 0;
137
138 /* these are offsets obtained via nlist and used in the get_ functions */
139 static unsigned long mpid_offset;
140
141 /* these are for calculating cpu state percentages */
142 static long cp_time[PST_MAX_CPUSTATES];
143 static long cp_old[PST_MAX_CPUSTATES];
144 static long cp_diff[PST_MAX_CPUSTATES];
145
146 /* these are for detailing the process states */
147 int process_states[7];
148 char *procstatenames[] = {
149 "", " sleeping, ", " running, ", " stopped, ", " zombie, ",
150 " trans, ", " starting, ",
151 NULL
152 };
153
154 /* these are for detailing the cpu states */
155 int cpu_states[PST_MAX_CPUSTATES];
156 char *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 */
163 long memory_stats[8];
164 char *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 */
170 static 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 */
176 struct ttymap {
177 dev_t dev;
178 char name [9];
179 };
180 static struct ttymap *ttynames = NULL;
181 static int nttys = 0;
182 static 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
197 static 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
208 proc_compare(p1, p2)
209 struct pst_status *p1;
210 struct 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
247 machine_init(statics)
248
249 struct 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
format_header(uname_field)308 char *format_header(uname_field)
309 char *uname_field;
310 {
311 char *ptr = header + UNAME_START;
312 while (*uname_field != '\0')
313 *ptr++ = *uname_field++;
314
315 return header;
316 }
317
318 void
get_system_info(si)319 get_system_info(si)
320
321 struct 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
get_process_info(si,sel,compare_index)396 caddr_t get_process_info(si, sel, compare_index)
397
398 struct system_info *si;
399 struct process_select *sel;
400 int 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 */
term_name(term)461 static char *term_name (term)
462 struct 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
format_next_process(handle,get_userid)479 char *format_next_process(handle, get_userid)
480
481 caddr_t handle;
482 char *(*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
getkval(offset,ptr,size,refstr)588 getkval(offset, ptr, size, refstr)
589
590 unsigned long offset;
591 int *ptr;
592 int size;
593 char *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
615 void (*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 */
proc_owner(pid)639 int proc_owner(pid)
640 int 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
get_tty_names(dir,m)653 static get_tty_names (dir, m)
654 char *dir;
655 int *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