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