1/* 2 * top - a top users display for Unix 3 * 4 * SYNOPSIS: Any Sun running SunOS 5.x (Solaris 2.x) 5 * 6 * DESCRIPTION: 7 * This is the machine-dependent module for SunOS 5.x (Solaris 2). 8 * There is some support for MP architectures. 9 * This makes top work on all revisions of SunOS 5 from 5.0 10 * through 5.9 (otherwise known as Solaris 9). It has not been 11 * tested on SunOS 5.10. 12 * 13 * AUTHORS: Torsten Kasch <torsten@techfak.uni-bielefeld.de> 14 * Robert Boucher <boucher@sofkin.ca> 15 * CONTRIBUTORS: Marc Cohen <marc@aai.com> 16 * Charles Hedrick <hedrick@geneva.rutgers.edu> 17 * William L. Jones <jones@chpc> 18 * Petri Kutvonen <kutvonen@cs.helsinki.fi> 19 * Casper Dik <casper.dik@sun.com> 20 * Tim Pugh <tpugh@oce.orst.edu> 21 */ 22 23#define _KMEMUSER 24 25#include "os.h" 26#include "utils.h" 27#include "username.h" 28#include "display.h" 29 30#if (OSREV == 551) 31#undef OSREV 32#define OSREV 55 33#endif 34 35/* 36 * Starting with SunOS 5.6 the data in /proc changed along with the 37 * means by which it is accessed. In this case we define USE_NEW_PROC. 38 * Note that with USE_NEW_PROC defined the structure named "prpsinfo" 39 * is redefined to be "psinfo". This will be confusing as you read 40 * the code. 41 */ 42 43#if OSREV >= 56 44#define USE_NEW_PROC 45#endif 46 47#if defined(USE_NEW_PROC) 48#define _STRUCTURED_PROC 1 49#define prpsinfo psinfo 50#include <sys/procfs.h> 51#define pr_fill pr_nlwp 52/* the "px" macros are used where the actual member could be in a substructure */ 53#define px_state pr_lwp.pr_state 54#define px_nice pr_lwp.pr_nice 55#define px_pri pr_lwp.pr_pri 56#define px_onpro pr_lwp.pr_onpro 57#define ZOMBIE(p) ((p)->pr_nlwp == 0) 58#define SIZE_K(p) (long)((p)->pr_size) 59#define RSS_K(p) (long)((p)->pr_rssize) 60#else 61#define px_state pr_state 62#define px_oldpri pr_oldpri 63#define px_nice pr_nice 64#define px_pri pr_pri 65#define px_onpro pr_filler[5] 66#define ZOMBIE(p) ((p)->pr_zomb) 67#define SIZE_K(p) (long)((p)->pr_bysize/1024) 68#define RSS_K(p) (long)((p)->pr_byrssize/1024) 69#endif 70 71#include "top.h" 72#include "machine.h" 73#include <limits.h> 74#include <stdio.h> 75#include <fcntl.h> 76#include <unistd.h> 77#include <stdlib.h> 78#include <errno.h> 79#include <dirent.h> 80#include <nlist.h> 81#include <string.h> 82#include <kvm.h> 83#include <signal.h> 84#include <sys/types.h> 85#include <sys/param.h> 86#include <sys/signal.h> 87#include <sys/fault.h> 88#include <sys/sysinfo.h> 89#include <sys/sysmacros.h> 90#include <sys/syscall.h> 91#include <sys/user.h> 92#include <sys/proc.h> 93#include <sys/procfs.h> 94#include <sys/vm.h> 95#include <sys/var.h> 96#include <sys/cpuvar.h> 97#include <sys/file.h> 98#include <sys/time.h> 99#include <sys/priocntl.h> 100#include <sys/tspriocntl.h> 101#include <sys/processor.h> 102#include <sys/resource.h> 103#include <sys/swap.h> 104#include <sys/stat.h> 105#include <vm/anon.h> 106#include <math.h> 107#include <utmpx.h> 108#include "utils.h" 109#include "hash.h" 110 111#if OSREV >= 53 112#define USE_KSTAT 113#endif 114#ifdef USE_KSTAT 115#include <kstat.h> 116/* 117 * Some kstats are fixed at 32 bits, these will be specified as ui32; some 118 * are "natural" size (32 bit on 32 bit Solaris, 64 on 64 bit Solaris 119 * we'll make those unsigned long) 120 * Older Solaris doesn't define KSTAT_DATA_UINT32, those are always 32 bit. 121 */ 122# ifndef KSTAT_DATA_UINT32 123# define ui32 ul 124# endif 125#endif 126 127#define UNIX "/dev/ksyms" 128#define KMEM "/dev/kmem" 129#define PROCFS "/proc" 130#define CPUSTATES 5 131#ifndef PRIO_MIN 132#define PRIO_MIN -20 133#endif 134#ifndef PRIO_MAX 135#define PRIO_MAX 20 136#endif 137 138#ifndef FSCALE 139#define FSHIFT 8 /* bits to right of fixed binary point */ 140#define FSCALE (1<<FSHIFT) 141#endif /* FSCALE */ 142 143#define loaddouble(la) ((double)(la) / FSCALE) 144#define dbl_align(x) (((unsigned long)(x)+(sizeof(double)-1)) & \ 145 ~(sizeof(double)-1)) 146 147/* 148 * SunOS 5.4 and above track pctcpu in the proc structure as pr_pctcpu. 149 * These values are weighted over one minute whereas top output prefers 150 * a near-instantaneous measure of cpu utilization. So we choose to 151 * ignore pr_pctcpu: we calculate our own cpu percentage and store it in 152 * one of the spare slots in the prinfo structure. 153 */ 154 155#define percent_cpu(pp) (*(double *)dbl_align(&pp->pr_filler[0])) 156 157/* definitions for indices in the nlist array */ 158#define X_V 0 159#define X_MPID 1 160#define X_ANONINFO 2 161#define X_MAXMEM 3 162#define X_FREEMEM 4 163#define X_AVENRUN 5 164#define X_CPU 6 165#define X_NPROC 7 166#define X_NCPUS 8 167 168static struct nlist nlst[] = 169{ 170 {"v"}, /* 0 */ /* replaced by dynamic allocation */ 171 {"mpid"}, /* 1 */ 172#if OSREV >= 56 173 /* this structure really has some extra fields, but the first three match */ 174 {"k_anoninfo"}, /* 2 */ 175#else 176 {"anoninfo"}, /* 2 */ 177#endif 178 {"maxmem"}, /* 3 */ /* use sysconf */ 179 {"freemem"}, /* 4 */ /* available from kstat >= 2.5 */ 180 {"avenrun"}, /* 5 */ /* available from kstat */ 181 {"cpu"}, /* 6 */ /* available from kstat */ 182 {"nproc"}, /* 7 */ /* available from kstat */ 183 {"ncpus"}, /* 8 */ /* available from kstat */ 184 {0} 185}; 186 187static unsigned long avenrun_offset; 188static unsigned long mpid_offset; 189#ifdef USE_KSTAT 190static kstat_ctl_t *kc = NULL; 191static kid_t kcid = 0; 192#else 193static unsigned long *cpu_offset; 194#endif 195static unsigned long nproc_offset; 196static unsigned long freemem_offset; 197static unsigned long maxmem_offset; 198static unsigned long anoninfo_offset; 199static int maxfiles = 256; 200#define MAXFILES 2048 201static int *display_fields; 202static int show_threads = 0; 203static int show_fullcmd; 204 205/* get_process_info passes back a handle. This is what it looks like: */ 206struct handle 207{ 208 struct prpsinfo **next_proc;/* points to next valid proc pointer */ 209 int remaining; /* number of pointers remaining */ 210}; 211 212/* 213 * Structure for keeping track processes between updates. 214 * We keep these things in a hash table, which is updated at every cycle. 215 */ 216struct oldproc 217{ 218 pid_t pid; 219 id_t lwpid; 220 double oldtime; 221 double oldpct; 222 uid_t owner_uid; 223 int fd_psinfo; 224 int fd_lpsinfo; 225 int seen; 226}; 227 228#define TIMESPEC_TO_DOUBLE(ts) ((ts).tv_sec * 1.0e9 + (ts).tv_nsec) 229 230hash_table *prochash; 231hash_table *threadhash; 232 233/* 234 * Structure for tracking per-cpu information 235 */ 236struct cpustats 237{ 238 unsigned int states[CPUSTATES]; 239 uint_t pswitch; 240 uint_t trap; 241 uint_t intr; 242 uint_t syscall; 243 uint_t sysfork; 244 uint_t sysvfork; 245 uint_t pfault; 246 uint_t pgin; 247 uint_t pgout; 248}; 249 250/* 251 * GCC assumes that all doubles are aligned. Unfortunately it 252 * doesn't round up the structure size to be a multiple of 8. 253 * Thus we'll get a coredump when going through array. The 254 * following is a size rounded up to 8. 255 */ 256#define PRPSINFOSIZE dbl_align(sizeof(struct prpsinfo)) 257 258/* this defines one field (or column) in the process display */ 259 260struct proc_field { 261 char *name; 262 int width; 263 int rjust; 264 int min_screenwidth; 265 int (*format)(char *, int, struct prpsinfo *); 266}; 267 268#define PROCSTATES 8 269/* process state names for the "STATE" column of the display */ 270char *state_abbrev[] = 271{"", "sleep", "run", "zombie", "stop", "start", "cpu", "swap"}; 272 273int process_states[PROCSTATES]; 274char *procstatenames[] = 275{ 276 "", " sleeping, ", " running, ", " zombie, ", " stopped, ", 277 " starting, ", " on cpu, ", " swapped, ", 278 NULL 279}; 280 281int cpu_states[CPUSTATES]; 282char *cpustatenames[] = 283{"idle", "user", "kernel", "iowait", "swap", NULL}; 284#define CPUSTATE_IOWAIT 3 285#define CPUSTATE_SWAP 4 286 287 288/* these are for detailing the memory statistics */ 289long memory_stats[5]; 290char *memorynames[] = 291{"K phys mem, ", "K free mem, ", "K total swap, ", "K free swap", NULL}; 292#define MEMORY_TOTALMEM 0 293#define MEMORY_FREEMEM 1 294#define MEMORY_TOTALSWAP 2 295#define MEMORY_FREESWAP 3 296 297/* these are for detailing kernel statistics */ 298int kernel_stats[8]; 299char *kernelnames[] = 300{" ctxsw, ", " trap, ", " intr, ", " syscall, ", " fork, ", 301 " flt, ", " pgin, ", " pgout, ", NULL}; 302#define KERNEL_CSWITCH 0 303#define KERNEL_TRAP 1 304#define KERNEL_INTR 2 305#define KERNEL_SYSCALL 3 306#define KERNEL_FORK 4 307#define KERNEL_PFAULT 5 308#define KERNEL_PGIN 6 309#define KERNEL_PGOUT 7 310 311/* these are names given to allowed sorting orders -- first is default */ 312char *ordernames[] = 313{"cpu", "size", "res", "time", "pid", NULL}; 314 315/* forward definitions for comparison functions */ 316int compare_cpu(); 317int compare_size(); 318int compare_res(); 319int compare_time(); 320int compare_pid(); 321 322int (*proc_compares[])() = { 323 compare_cpu, 324 compare_size, 325 compare_res, 326 compare_time, 327 compare_pid, 328 NULL }; 329 330kvm_t *kd; 331static DIR *procdir; 332 333/* "cpucount" is used to store the value for the kernel variable "ncpus". 334 But since <sys/cpuvar.h> actually defines a variable "ncpus" we need 335 to use a different name here. --wnl */ 336static int cpucount; 337 338/* pagetok function is really a pointer to an appropriate function */ 339static int pageshift; 340static long (*p_pagetok) (); 341#define pagetok(size) ((*p_pagetok)(size)) 342 343/* useful externals */ 344extern char *myname; 345extern void perror (); 346extern int getptable (); 347extern void quit (); 348 349/* process formatting functions and data */ 350 351int 352fmt_pid(char *buf, int sz, struct prpsinfo *pp) 353 354{ 355 return snprintf(buf, sz, "%6d", (int)pp->pr_pid); 356} 357 358int 359fmt_username(char *buf, int sz, struct prpsinfo *pp) 360 361{ 362 return snprintf(buf, sz, "%-8.8s", username(pp->pr_uid)); 363} 364 365int 366fmt_uid(char *buf, int sz, struct prpsinfo *pp) 367 368{ 369 return snprintf(buf, sz, "%6d", (int)pp->pr_uid); 370} 371 372int 373fmt_nlwp(char *buf, int sz, struct prpsinfo *pp) 374 375{ 376 return snprintf(buf, sz, "%4d", pp->pr_fill < 999 ? pp->pr_fill: 999); 377} 378 379int 380fmt_pri(char *buf, int sz, struct prpsinfo *pp) 381 382{ 383 return snprintf(buf, sz, "%3d", pp->px_pri); 384} 385 386int 387fmt_nice(char *buf, int sz, struct prpsinfo *pp) 388 389{ 390 return snprintf(buf, sz, "%4d", pp->px_nice - NZERO); 391} 392 393int 394fmt_size(char *buf, int sz, struct prpsinfo *pp) 395 396{ 397 return snprintf(buf, sz, "%5s", format_k(SIZE_K(pp))); 398} 399 400int 401fmt_res(char *buf, int sz, struct prpsinfo *pp) 402 403{ 404 return snprintf(buf, sz, "%5s", format_k(RSS_K(pp))); 405} 406 407int 408fmt_state(char *buf, int sz, struct prpsinfo *pp) 409 410{ 411 if (pp->px_state == SONPROC && cpucount > 1) 412 { 413 /* large #s may overflow colums */ 414 if (pp->px_onpro < 100) 415 { 416 return snprintf(buf, sz, "cpu/%-2d", pp->px_onpro); 417 } 418 return snprintf(buf, sz, "cpu/**"); 419 } 420 421 return snprintf(buf, sz, "%-6s", state_abbrev[(int)pp->px_state]); 422} 423 424int 425fmt_time(char *buf, int sz, struct prpsinfo *pp) 426 427{ 428 return snprintf(buf, sz, "%6s", format_time(pp->pr_time.tv_sec)); 429} 430 431int 432fmt_cpu(char *buf, int sz, struct prpsinfo *pp) 433 434{ 435 return snprintf(buf, sz, "%5s%%", 436 format_percent(percent_cpu(pp) / cpucount)); 437} 438 439int 440fmt_command(char *buf, int sz, struct prpsinfo *pp) 441 442{ 443 return snprintf(buf, sz, "%s", 444 printable(show_fullcmd ? pp->pr_psargs : pp->pr_fname)); 445} 446 447int 448fmt_lwp(char *buf, int sz, struct prpsinfo *pp) 449 450{ 451 return snprintf(buf, sz, "%4d", ((int)pp->pr_lwp.pr_lwpid < 10000 ? 452 (int)pp->pr_lwp.pr_lwpid : 9999)); 453} 454 455struct proc_field proc_field[] = { 456 { "PID", 6, 1, 0, fmt_pid }, 457 { "USERNAME", 8, 0, 0, fmt_username }, 458#define FIELD_USERNAME 1 459 { "UID", 6, 1, 0, fmt_uid }, 460#define FIELD_UID 2 461 { "NLWP", 4, 1, 0, fmt_nlwp }, 462 { "PRI", 3, 1, 0, fmt_pri }, 463 { "NICE", 4, 1, 0, fmt_nice }, 464 { "SIZE", 5, 1, 0, fmt_size }, 465 { "RES", 5, 1, 0, fmt_res }, 466 { "STATE", 6, 0, 0, fmt_state }, 467 { "TIME", 6, 1, 0, fmt_time }, 468 { "CPU", 6, 1, 0, fmt_cpu }, 469 { "COMMAND", 7, 0, 0, fmt_command }, 470 { "LWP", 4, 1, 0, fmt_lwp }, 471}; 472#define MAX_FIELDS 13 473 474static int proc_display[MAX_FIELDS]; 475static int thr_display[MAX_FIELDS]; 476 477int 478field_index(char *col) 479 480{ 481 struct proc_field *fp; 482 int i = 0; 483 484 fp = proc_field; 485 while (fp->name != NULL) 486 { 487 if (strcmp(col, fp->name) == 0) 488 { 489 return i; 490 } 491 fp++; 492 i++; 493 } 494 495 return -1; 496} 497 498void 499field_subst(int *fp, int old, int new) 500 501{ 502 while (*fp != -1) 503 { 504 if (*fp == old) 505 { 506 *fp = new; 507 } 508 fp++; 509 } 510} 511 512/* p_pagetok points to one of the following, depending on which 513 direction data has to be shifted: */ 514 515long pagetok_none(long size) 516 517{ 518 return(size); 519} 520 521long pagetok_left(long size) 522 523{ 524 return(size << pageshift); 525} 526 527long pagetok_right(long size) 528 529{ 530 return(size >> pageshift); 531} 532 533/* 534 * getkval(offset, ptr, size, refstr) - get a value out of the kernel. 535 * "offset" is the byte offset into the kernel for the desired value, 536 * "ptr" points to a buffer into which the value is retrieved, 537 * "size" is the size of the buffer (and the object to retrieve), 538 * "refstr" is a reference string used when printing error meessages, 539 * if "refstr" starts with a '!', then a failure on read will not 540 * be fatal (this may seem like a silly way to do things, but I 541 * really didn't want the overhead of another argument). 542 * 543 */ 544int 545getkval (unsigned long offset, 546 int *ptr, 547 int size, 548 char *refstr) 549{ 550 dprintf("getkval(%08x, %08x, %d, %s)\n", offset, ptr, size, refstr); 551 552 if (kvm_read (kd, offset, (char *) ptr, size) != size) 553 { 554 dprintf("getkval: read failed\n"); 555 if (*refstr == '!') 556 { 557 return (0); 558 } 559 else 560 { 561 fprintf (stderr, "top: kvm_read for %s: %s\n", refstr, strerror(errno)); 562 quit (23); 563 } 564 } 565 566 dprintf("getkval read %d (%08x)\n", *ptr); 567 568 return (1); 569 570} 571 572/* procs structure memory management */ 573 574static struct prpsinfo **allprocs = NULL; 575static struct prpsinfo **nextproc = NULL; 576static int maxprocs = 0; 577static int idxprocs = 0; 578 579/* 580 * void procs_prealloc(int cnt) 581 * 582 * Preallocate "cnt" procs structures. If "cnt" is less than or equal 583 * to procs_max() then this function has no effect. 584 */ 585 586void 587procs_prealloc(int max) 588 589{ 590 int cnt; 591 struct prpsinfo *new; 592 struct prpsinfo **pp; 593 594 cnt = max - maxprocs; 595 if (cnt > 0) 596 { 597 dprintf("procs_prealloc: need %d, deficit %d\n", max, cnt); 598 allprocs = (struct prpsinfo **) 599 realloc((void *)allprocs, max * sizeof(struct prpsinfo *)); 600 pp = nextproc = allprocs + idxprocs; 601 new = (struct prpsinfo *)malloc(cnt * PRPSINFOSIZE); 602 dprintf("procs_prealloc: idxprocs %d, allprocs %08x, nextproc %08x, new %08x\n", 603 idxprocs, allprocs, nextproc, new); 604 while (--cnt >= 0) 605 { 606 *pp++ = new; 607 new = (struct prpsinfo *) ((char *)new + PRPSINFOSIZE); 608 } 609 dprintf("procs_prealloc: done filling at %08x\n", new); 610 maxprocs = max; 611 } 612} 613 614/* 615 * struct prpsinfo *procs_next() 616 * 617 * Return the next available procs structure, allocating a new one 618 * if needed. 619 */ 620 621struct prpsinfo * 622procs_next() 623 624{ 625 if (idxprocs >= maxprocs) 626 { 627 /* allocate some more */ 628 procs_prealloc(maxprocs + 128); 629 } 630 idxprocs++; 631 return *nextproc++; 632} 633 634struct prpsinfo * 635procs_dup(struct prpsinfo *p) 636 637{ 638 struct prpsinfo *n; 639 640 n = procs_next(); 641 memcpy(n, p, PRPSINFOSIZE); 642 return n; 643} 644 645/* 646 * struct prpsinfo *procs_start() 647 * 648 * Return the first procs structure. 649 */ 650 651struct prpsinfo * 652procs_start() 653 654{ 655 idxprocs = 0; 656 nextproc = allprocs; 657 return procs_next(); 658} 659 660/* 661 * int procs_max() 662 * 663 * Return the maximum number of procs structures currently allocated. 664 */ 665 666int 667procs_max() 668 669{ 670 return maxprocs; 671} 672 673/* 674 * check_nlist(nlst) - checks the nlist to see if any symbols were not 675 * found. For every symbol that was not found, a one-line 676 * message is printed to stderr. The routine returns the 677 * number of symbols NOT found. 678 */ 679int 680check_nlist (register struct nlist *nlst) 681{ 682 register int i; 683 684 /* check to see if we got ALL the symbols we requested */ 685 /* this will write one line to stderr for every symbol not found */ 686 687 i = 0; 688 while (nlst->n_name != NULL) 689 { 690 if (nlst->n_type == 0) 691 { 692 /* this one wasn't found */ 693 fprintf (stderr, "kernel: no symbol named `%s'\n", nlst->n_name); 694 i = 1; 695 } 696 nlst++; 697 } 698 return (i); 699} 700 701 702char * 703format_header (register char *uname_field) 704{ 705 return (""); 706} 707 708#ifdef USE_KSTAT 709 710long 711kstat_data_value_l(kstat_named_t *kn) 712 713{ 714#ifdef KSTAT_DATA_UINT32 715 switch(kn->data_type) 716 { 717 case KSTAT_DATA_INT32: 718 return ((long)(kn->value.i32)); 719 case KSTAT_DATA_UINT32: 720 return ((long)(kn->value.ui32)); 721 case KSTAT_DATA_INT64: 722 return ((long)(kn->value.i64)); 723 case KSTAT_DATA_UINT64: 724 return ((long)(kn->value.ui64)); 725 } 726 return 0; 727#else 728 return ((long)(kn->value.ui32)); 729#endif 730} 731 732int 733kstat_safe_retrieve(kstat_t **ksp, 734 char *module, int instance, char *name, void *buf) 735 736{ 737 kstat_t *ks; 738 kid_t new_kcid; 739 int changed; 740 741 dprintf("kstat_safe_retrieve(%08x -> %08x, %s, %d, %s, %08x)\n", 742 ksp, *ksp, module, instance, name, buf); 743 744 ks = *ksp; 745 do { 746 changed = 0; 747 /* if we dont already have the kstat, retrieve it */ 748 if (ks == NULL) 749 { 750 if ((ks = kstat_lookup(kc, module, instance, name)) == NULL) 751 { 752 return (-1); 753 } 754 *ksp = ks; 755 } 756 757 /* attempt to read it */ 758 new_kcid = kstat_read(kc, ks, buf); 759 /* chance for an infinite loop here if kstat_read keeps 760 returning -1 */ 761 762 /* if the chain changed, update it */ 763 if (new_kcid != kcid) 764 { 765 dprintf("kstat_safe_retrieve: chain changed to %d...updating\n", 766 new_kcid); 767 changed = 1; 768 kcid = kstat_chain_update(kc); 769 } 770 } while (changed); 771 772 return (0); 773} 774 775/* 776 * int kstat_safe_namematch(int num, kstat_t *ksp, char *name, void *buf) 777 * 778 * Safe scan of kstat chain for names starting with "name". Matches 779 * are copied in to "ksp", and kstat_read is called on each match using 780 * "buf" as a buffer of length "size". The actual number of records 781 * found is returned. Up to "num" kstats are copied in to "ksp", but 782 * no more. If any kstat_read indicates that the chain has changed, then 783 * the whole process is restarted. 784 */ 785 786int 787kstat_safe_namematch(int num, kstat_t **ksparg, char *name, void *buf, int size) 788 789{ 790 kstat_t *ks; 791 kstat_t **ksp; 792 kid_t new_kcid; 793 int namelen; 794 int count; 795 int changed; 796 char *cbuf; 797 798 dprintf("kstat_safe_namematch(%d, %08x, %s, %08x, %d)\n", 799 num, ksparg, name, buf, size); 800 801 namelen = strlen(name); 802 803 do { 804 /* initialize before the scan */ 805 cbuf = (char *)buf; 806 ksp = ksparg; 807 count = 0; 808 changed = 0; 809 810 /* scan the chain for matching kstats */ 811 for (ks = kc->kc_chain; ks != NULL; ks = ks->ks_next) 812 { 813 if (strncmp(ks->ks_name, name, namelen) == 0) 814 { 815 /* this kstat matches: save it if there is room */ 816 if (count++ < num) 817 { 818 /* read the kstat */ 819 new_kcid = kstat_read(kc, ks, cbuf); 820 821 /* if the chain changed, update it */ 822 if (new_kcid != kcid) 823 { 824 dprintf("kstat_safe_namematch: chain changed to %d...updating\n", 825 new_kcid); 826 changed = 1; 827 kcid = kstat_chain_update(kc); 828 829 /* there's no sense in continuing the scan */ 830 /* so break out of the for loop */ 831 break; 832 } 833 834 /* move to the next buffers */ 835 cbuf += size; 836 *ksp++ = ks; 837 } 838 } 839 } 840 } while(changed); 841 842 dprintf("kstat_safe_namematch returns %d\n", count); 843 844 return count; 845} 846 847static kstat_t *ks_system_misc = NULL; 848 849#endif /* USE_KSTAT */ 850 851 852int 853get_avenrun(int avenrun[3]) 854 855{ 856#ifdef USE_KSTAT 857 int status; 858 kstat_named_t *kn; 859 860 dprintf("get_avenrun(%08x)\n", avenrun); 861 862 if ((status = kstat_safe_retrieve(&ks_system_misc, 863 "unix", 0, "system_misc", NULL)) == 0) 864 { 865 if ((kn = kstat_data_lookup(ks_system_misc, "avenrun_1min")) != NULL) 866 { 867 avenrun[0] = kn->value.ui32; 868 } 869 if ((kn = kstat_data_lookup(ks_system_misc, "avenrun_5min")) != NULL) 870 { 871 avenrun[1] = kn->value.ui32; 872 } 873 if ((kn = kstat_data_lookup(ks_system_misc, "avenrun_15min")) != NULL) 874 { 875 avenrun[2] = kn->value.ui32; 876 } 877 } 878 dprintf("get_avenrun returns %d\n", status); 879 return (status); 880 881#else /* !USE_KSTAT */ 882 883 (void) getkval (avenrun_offset, (int *) avenrun, sizeof (int [3]), "avenrun"); 884 885 return 0; 886 887#endif /* USE_KSTAT */ 888} 889 890int 891get_ncpus() 892 893{ 894#ifdef USE_KSTAT 895 kstat_named_t *kn; 896 int ret = -1; 897 898 if ((kn = kstat_data_lookup(ks_system_misc, "ncpus")) != NULL) 899 { 900 ret = (int)(kn->value.ui32); 901 } 902 903 return ret; 904#else 905 int ret; 906 907 (void) getkval(nlst[X_NCPUS].n_value, (int *)(&ret), sizeof(ret), "ncpus"); 908 return ret; 909#endif 910} 911 912int 913get_nproc() 914 915{ 916#ifdef USE_KSTAT 917 kstat_named_t *kn; 918 int ret = -1; 919 920 if ((kn = kstat_data_lookup(ks_system_misc, "nproc")) != NULL) 921 { 922 ret = (int)(kn->value.ui32); 923 } 924#else 925 int ret; 926 927 (void) getkval (nproc_offset, (int *) (&ret), sizeof (ret), "nproc"); 928#endif 929 930 dprintf("get_nproc returns %d\n", ret); 931 return ret; 932} 933 934struct cpustats * 935get_cpustats(int *cnt, struct cpustats *cpustats) 936 937{ 938#ifdef USE_KSTAT 939 static kstat_t **cpu_ks = NULL; 940 static cpu_stat_t *cpu_stat = NULL; 941 static unsigned int nelems = 0; 942 cpu_stat_t *cpu_stat_p; 943 int i, cpu_num; 944 struct cpustats *cpustats_p; 945 946 dprintf("get_cpustats(%d -> %d, %08x)\n", cnt, *cnt, cpustats); 947 948 while (nelems > 0 ? 949 (cpu_num = kstat_safe_namematch(nelems, 950 cpu_ks, 951 "cpu_stat", 952 cpu_stat, 953 sizeof(cpu_stat_t))) > nelems : 954 (cpu_num = get_ncpus()) > 0) 955 { 956 /* reallocate the arrays */ 957 dprintf("realloc from %d to %d\n", nelems, cpu_num); 958 nelems = cpu_num; 959 if (cpu_ks != NULL) 960 { 961 free(cpu_ks); 962 } 963 cpu_ks = (kstat_t **)calloc(nelems, sizeof(kstat_t *)); 964 if (cpu_stat != NULL) 965 { 966 free(cpu_stat); 967 } 968 cpu_stat = (cpu_stat_t *)malloc(nelems * sizeof(cpu_stat_t)); 969 } 970 971 /* do we have more cpus than our caller? */ 972 if (cpu_num > *cnt) 973 { 974 /* yes, so realloc their array, too */ 975 dprintf("realloc array from %d to %d\n", *cnt, cpu_num); 976 *cnt = cpu_num; 977 cpustats = (struct cpustats *)realloc(cpustats, 978 cpu_num * sizeof(struct cpustats)); 979 } 980 981 cpu_stat_p = cpu_stat; 982 cpustats_p = cpustats; 983 for (i = 0; i < cpu_num; i++) 984 { 985 dprintf("cpu %d %08x: idle %u, user %u, syscall %u\n", i, cpu_stat_p, 986 cpu_stat_p->cpu_sysinfo.cpu[0], 987 cpu_stat_p->cpu_sysinfo.cpu[1], 988 cpu_stat_p->cpu_sysinfo.syscall); 989 990 cpustats_p->states[CPU_IDLE] = cpu_stat_p->cpu_sysinfo.cpu[CPU_IDLE]; 991 cpustats_p->states[CPU_USER] = cpu_stat_p->cpu_sysinfo.cpu[CPU_USER]; 992 cpustats_p->states[CPU_KERNEL] = cpu_stat_p->cpu_sysinfo.cpu[CPU_KERNEL]; 993 cpustats_p->states[CPUSTATE_IOWAIT] = cpu_stat_p->cpu_sysinfo.wait[W_IO] + 994 cpu_stat_p->cpu_sysinfo.wait[W_PIO]; 995 cpustats_p->states[CPUSTATE_SWAP] = cpu_stat_p->cpu_sysinfo.wait[W_SWAP]; 996 cpustats_p->pswitch = cpu_stat_p->cpu_sysinfo.pswitch; 997 cpustats_p->trap = cpu_stat_p->cpu_sysinfo.trap; 998 cpustats_p->intr = cpu_stat_p->cpu_sysinfo.intr; 999 cpustats_p->syscall = cpu_stat_p->cpu_sysinfo.syscall; 1000 cpustats_p->sysfork = cpu_stat_p->cpu_sysinfo.sysfork; 1001 cpustats_p->sysvfork = cpu_stat_p->cpu_sysinfo.sysvfork; 1002 cpustats_p->pfault = cpu_stat_p->cpu_vminfo.hat_fault + 1003 cpu_stat_p->cpu_vminfo.as_fault; 1004 cpustats_p->pgin = cpu_stat_p->cpu_vminfo.pgin; 1005 cpustats_p->pgout = cpu_stat_p->cpu_vminfo.pgout; 1006 cpustats_p++; 1007 cpu_stat_p++; 1008 } 1009 1010 cpucount = cpu_num; 1011 1012 dprintf("get_cpustats sees %d cpus and returns %08x\n", cpucount, cpustats); 1013 1014 return (cpustats); 1015#else /* !USE_KSTAT */ 1016 int i; 1017 struct cpu cpu; 1018 unsigned int (*cp_stats_p)[CPUSTATES]; 1019 1020 /* do we have more cpus than our caller? */ 1021 if (cpucount > *cnt) 1022 { 1023 /* yes, so realloc their array, too */ 1024 dprintf("realloc array from %d to %d\n", *cnt, cpucount); 1025 *cnt = cpucount; 1026 cp_stats = (unsigned int (*)[CPUSTATES])realloc(cp_stats, 1027 cpucount * sizeof(unsigned int) * CPUSTATES); 1028 } 1029 1030 cp_stats_p = cp_stats; 1031 for (i = 0; i < cpucount; i++) 1032 { 1033 if (cpu_offset[i] != 0) 1034 { 1035 /* get struct cpu for this processor */ 1036 (void) getkval (cpu_offset[i], (int *)(&cpu), sizeof (struct cpu), "cpu"); 1037 1038 (*cp_stats_p)[CPU_IDLE] = cpu.cpu_stat.cpu_sysinfo.cpu[CPU_IDLE]; 1039 (*cp_stats_p)[CPU_USER] = cpu.cpu_stat.cpu_sysinfo.cpu[CPU_USER]; 1040 (*cp_stats_p)[CPU_KERNEL] = cpu.cpu_stat.cpu_sysinfo.cpu[CPU_KERNEL]; 1041 (*cp_stats_p)[CPUSTATE_IOWAIT] = cpu.cpu_stat.cpu_sysinfo.wait[W_IO] + 1042 cpu.cpu_stat.cpu_sysinfo.wait[W_PIO]; 1043 (*cp_stats_p)[CPUSTATE_SWAP] = cpu.cpu_stat.cpu_sysinfo.wait[W_SWAP]; 1044 cp_stats_p++; 1045 } 1046 } 1047 1048 return (cp_stats); 1049#endif /* USE_KSTAT */ 1050} 1051 1052/* 1053 * void get_meminfo(long *total, long *fr) 1054 * 1055 * Get information about the system's physical memory. Pass back values 1056 * for total available and amount of memory that is free (in kilobytes). 1057 * It returns 0 on success and -1 on any kind of failure. 1058 */ 1059 1060int 1061get_meminfo(long *total, long *fr) 1062 1063{ 1064 long freemem; 1065 static kstat_t *ks = NULL; 1066 kstat_named_t *kn; 1067 1068 /* total comes from sysconf */ 1069 *total = pagetok(sysconf(_SC_PHYS_PAGES)); 1070 1071 /* free comes from the kernel's freemem or from kstat */ 1072 /* prefer kmem for this because kstat unix:0:system_pages 1073 can be slow on systems with lots of memory */ 1074 if (kd) 1075 { 1076 (void) getkval(freemem_offset, (int *)(&freemem), sizeof(freemem), 1077 "freemem"); 1078 } 1079 else 1080 { 1081#ifdef USE_KSTAT 1082 /* only need to grab kstat chain once */ 1083 if (ks == NULL) 1084 { 1085 ks = kstat_lookup(kc, "unix", 0, "system_pages"); 1086 } 1087 1088 if (ks != NULL && 1089 kstat_read(kc, ks, 0) != -1 && 1090 (kn = kstat_data_lookup(ks, "freemem")) != NULL) 1091 { 1092 freemem = kstat_data_value_l(kn); 1093 } 1094 else 1095 { 1096 freemem = -1; 1097 } 1098#else 1099 freemem = -1; 1100#endif 1101 } 1102 1103 *fr = freemem == -1 ? -1 : pagetok(freemem); 1104 1105 return (0); 1106} 1107 1108/* 1109 * void get_swapinfo(long *total, long *fr) 1110 * 1111 * Get information about the system's swap. Pass back values for 1112 * total swap available and amount of swap that is free (in kilobytes). 1113 * It returns 0 on success and -1 on any kind of failure. 1114 */ 1115 1116int 1117get_swapinfo(long *total, long *fr) 1118 1119{ 1120 register int cnt, i; 1121 register long t, f; 1122 struct swaptable *swt; 1123 struct swapent *ste; 1124 static char path[256]; 1125 1126 /* preset values to 0 just in case we have to return early */ 1127 *total = 0; 1128 *fr = 0; 1129 1130 /* get total number of swap entries */ 1131 if ((cnt = swapctl(SC_GETNSWP, 0)) == -1) 1132 { 1133 return (-1); 1134 } 1135 1136 /* allocate enough space to hold count + n swapents */ 1137 swt = (struct swaptable *)malloc(sizeof(int) + 1138 cnt * sizeof(struct swapent)); 1139 if (swt == NULL) 1140 { 1141 return (-1); 1142 } 1143 swt->swt_n = cnt; 1144 1145 /* fill in ste_path pointers: we don't care about the paths, so we point 1146 them all to the same buffer */ 1147 ste = &(swt->swt_ent[0]); 1148 i = cnt; 1149 while (--i >= 0) 1150 { 1151 ste++->ste_path = path; 1152 } 1153 1154 /* grab all swap info */ 1155 if (swapctl(SC_LIST, swt) == -1) 1156 { 1157 return (-1); 1158 } 1159 1160 /* walk thru the structs and sum up the fields */ 1161 t = f = 0; 1162 ste = &(swt->swt_ent[0]); 1163 i = cnt; 1164 while (--i >= 0) 1165 { 1166 /* dont count slots being deleted */ 1167 if (!(ste->ste_flags & ST_INDEL) && 1168 !(ste->ste_flags & ST_DOINGDEL)) 1169 { 1170 t += ste->ste_pages; 1171 f += ste->ste_free; 1172 } 1173 ste++; 1174 } 1175 1176 /* fill in the results */ 1177 *total = pagetok(t); 1178 *fr = pagetok(f); 1179 free(swt); 1180 1181 /* good to go */ 1182 return (0); 1183} 1184 1185int 1186machine_init (struct statics *statics) 1187{ 1188 struct utmpx ut; 1189 struct utmpx *up; 1190 struct rlimit rlim; 1191 int i; 1192 char *p; 1193 int *ip; 1194 int nproc; 1195#ifndef USE_KSTAT 1196 int offset; 1197#endif 1198 1199 /* There's a buffer overflow bug in curses that can be exploited when 1200 we run as root. By making sure that TERMINFO is set to something 1201 this bug is avoided. This code thanks to Casper */ 1202 if ((p = getenv("TERMINFO")) == NULL || *p == '\0') 1203 { 1204 putenv("TERMINFO=/usr/share/lib/terminfo/"); 1205 } 1206 1207 /* perform the kvm_open - suppress error here */ 1208 if ((kd = kvm_open (NULL, NULL, NULL, O_RDONLY, NULL)) == NULL) 1209 { 1210 /* save the error message: we may need it later */ 1211 p = strerror(errno); 1212 } 1213 dprintf("kvm_open: fd %d\n", kd); 1214 1215 /* 1216 * turn off super group/user privs - but beware; we might 1217 * want the privs back later and we still have a fd to 1218 * /dev/kmem open so we can't use setgid()/setuid() as that 1219 * would allow a debugger to attach to this process. CD 1220 */ 1221 setegid(getgid()); 1222 seteuid(getuid()); /* super user not needed for NEW_PROC */ 1223 1224#ifdef USE_KSTAT 1225 /* open kstat */ 1226 if ((kc = kstat_open()) == NULL) 1227 { 1228 fprintf(stderr, "Unable to open kstat.\n"); 1229 return(-1); 1230 } 1231 kcid = kc->kc_chain_id; 1232 dprintf("kstat_open: chain %d\n", kcid); 1233#endif 1234 1235 /* fill in the statics information */ 1236 statics->procstate_names = procstatenames; 1237 statics->cpustate_names = cpustatenames; 1238 statics->memory_names = memorynames; 1239 statics->kernel_names = kernelnames; 1240 statics->order_names = ordernames; 1241 statics->flags.fullcmds = 1; 1242 statics->flags.warmup = 1; 1243 statics->flags.threads = 1; 1244 1245 /* get boot time */ 1246 ut.ut_type = BOOT_TIME; 1247 if ((up = getutxid(&ut)) != NULL) 1248 { 1249 statics->boottime = up->ut_tv.tv_sec; 1250 } 1251 endutxent(); 1252 1253 /* if the kvm_open succeeded, get the nlist */ 1254 if (kd) 1255 { 1256 if (kvm_nlist (kd, nlst) < 0) 1257 { 1258 perror ("kvm_nlist"); 1259 return (-1); 1260 } 1261 if (check_nlist (nlst) != 0) 1262 return (-1); 1263 } 1264#ifndef USE_KSTAT 1265 /* if KSTAT is not available to us and we can't open /dev/kmem, 1266 this is a serious problem. 1267 */ 1268 else 1269 { 1270 /* Print the error message here */ 1271 (void) fprintf(stderr, "kvm_open: %s\n", p); 1272 return (-1); 1273 } 1274#endif 1275 1276 /* stash away certain offsets for later use */ 1277 mpid_offset = nlst[X_MPID].n_value; 1278 nproc_offset = nlst[X_NPROC].n_value; 1279 avenrun_offset = nlst[X_AVENRUN].n_value; 1280 anoninfo_offset = nlst[X_ANONINFO].n_value; 1281 freemem_offset = nlst[X_FREEMEM].n_value; 1282 maxmem_offset = nlst[X_MAXMEM].n_value; 1283 1284#ifndef USE_KSTAT 1285 (void) getkval (nlst[X_NCPUS].n_value, (int *) (&cpucount), 1286 sizeof (cpucount), "ncpus"); 1287 1288 cpu_offset = (unsigned long *) malloc (cpucount * sizeof (unsigned long)); 1289 for (i = offset = 0; i < cpucount; offset += sizeof(unsigned long)) { 1290 (void) getkval (nlst[X_CPU].n_value + offset, 1291 (int *)(&cpu_offset[i]), sizeof (unsigned long), 1292 nlst[X_CPU].n_name ); 1293 if (cpu_offset[i] != 0) 1294 i++; 1295 } 1296#endif 1297 1298 /* we need to get the current nproc */ 1299#ifdef USE_KSTAT 1300 /* get_nproc assumes that the chain has already been retrieved, 1301 so we need to do that here */ 1302 kstat_safe_retrieve(&ks_system_misc, "unix", 0, "system_misc", NULL); 1303#endif 1304 nproc = get_nproc(); 1305 dprintf("machine_init: nproc=%d\n", nproc); 1306 1307 /* hash table for procs and threads sized based on current nproc*/ 1308 prochash = hash_create(nproc > 100 ? nproc * 2 + 1 : 521); 1309 threadhash = hash_create(nproc > 100 ? nproc * 4 + 1 : 2053); 1310 1311 /* calculate pageshift value */ 1312 i = sysconf(_SC_PAGESIZE); 1313 pageshift = 0; 1314 while ((i >>= 1) > 0) 1315 { 1316 pageshift++; 1317 } 1318 1319 /* calculate an amount to shift to K values */ 1320 /* remember that log base 2 of 1024 is 10 (i.e.: 2^10 = 1024) */ 1321 pageshift -= 10; 1322 1323 /* now determine which pageshift function is appropriate for the 1324 result (have to because x << y is undefined for y < 0) */ 1325 if (pageshift > 0) 1326 { 1327 /* this is the most likely */ 1328 p_pagetok = pagetok_left; 1329 } 1330 else if (pageshift == 0) 1331 { 1332 p_pagetok = pagetok_none; 1333 } 1334 else 1335 { 1336 p_pagetok = pagetok_right; 1337 pageshift = -pageshift; 1338 } 1339 1340 /* we cache open files to improve performance, so we need to up 1341 the NOFILE limit */ 1342 if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) 1343 { 1344 /* set a new soft limit */ 1345 maxfiles = (int)(rlim.rlim_max < MAXFILES ? rlim.rlim_max : MAXFILES); 1346 rlim.rlim_cur = (rlim_t)maxfiles; 1347 (void)setrlimit(RLIMIT_NOFILE, &rlim); 1348 1349 /* now leave some wiggle room above the maximum */ 1350 maxfiles -= 20; 1351 } 1352 1353 /* set up the display indices */ 1354 ip = proc_display; 1355 *ip++ = field_index("PID"); 1356 *ip++ = field_index("USERNAME"); 1357 *ip++ = field_index("NLWP"); 1358 *ip++ = field_index("PRI"); 1359 *ip++ = field_index("NICE"); 1360 *ip++ = field_index("SIZE"); 1361 *ip++ = field_index("RES"); 1362 *ip++ = field_index("STATE"); 1363 *ip++ = field_index("TIME"); 1364 *ip++ = field_index("CPU"); 1365 *ip++ = field_index("COMMAND"); 1366 *ip = -1; 1367 ip = thr_display; 1368 *ip++ = field_index("PID"); 1369 *ip++ = field_index("LWP"); 1370 *ip++ = field_index("USERNAME"); 1371 *ip++ = field_index("PRI"); 1372 *ip++ = field_index("NICE"); 1373 *ip++ = field_index("SIZE"); 1374 *ip++ = field_index("RES"); 1375 *ip++ = field_index("STATE"); 1376 *ip++ = field_index("TIME"); 1377 *ip++ = field_index("CPU"); 1378 *ip++ = field_index("COMMAND"); 1379 *ip = -1; 1380 1381 if (!(procdir = opendir (PROCFS))) 1382 { 1383 (void) fprintf (stderr, "Unable to open %s\n", PROCFS); 1384 return (-1); 1385 } 1386 1387 if (chdir (PROCFS)) 1388 { /* handy for later on when we're reading it */ 1389 (void) fprintf (stderr, "Unable to chdir to %s\n", PROCFS); 1390 return (-1); 1391 } 1392 1393 /* all done! */ 1394 return (0); 1395} 1396 1397void 1398get_system_info (struct system_info *si) 1399{ 1400 int avenrun[3]; 1401 1402 static long cp_time[CPUSTATES]; 1403 static long cp_old[CPUSTATES]; 1404 static long cp_diff[CPUSTATES]; 1405 static struct cpustats *cpustats = NULL; 1406 static struct cpustats sum_current; 1407 static struct cpustats sum_old; 1408 static int cpus = 0; 1409 register int j, i; 1410 1411 /* remember the old values and zero out the current */ 1412 memcpy(&sum_old, &sum_current, sizeof(sum_current)); 1413 memset(&sum_current, 0, sizeof(sum_current)); 1414 1415 /* get important information */ 1416 get_avenrun(avenrun); 1417 1418 /* get the cpu statistics arrays */ 1419 cpustats = get_cpustats(&cpus, cpustats); 1420 1421 /* zero the cp_time array */ 1422 memset(cp_time, 0, sizeof(cp_time)); 1423 1424 /* sum stats in to a single array and a single structure */ 1425 for (i = 0; i < cpus; i++) 1426 { 1427 for (j = 0; j < CPUSTATES; j++) 1428 { 1429 cp_time[j] += cpustats[i].states[j]; 1430 } 1431 sum_current.pswitch += cpustats[i].pswitch; 1432 sum_current.trap += cpustats[i].trap; 1433 sum_current.intr += cpustats[i].intr; 1434 sum_current.syscall += cpustats[i].syscall; 1435 sum_current.sysfork += cpustats[i].sysfork; 1436 sum_current.sysvfork += cpustats[i].sysvfork; 1437 sum_current.pfault += cpustats[i].pfault; 1438 sum_current.pgin += cpustats[i].pgin; 1439 sum_current.pgout += cpustats[i].pgout; 1440 } 1441 1442 /* convert cp_time counts to percentages */ 1443 (void) percentages (CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); 1444 1445 /* get mpid -- process id of last process */ 1446 if (kd) 1447 (void) getkval(mpid_offset, &(si->last_pid), sizeof (si->last_pid), "mpid"); 1448 else 1449 si->last_pid = -1; 1450 1451 /* convert load averages to doubles */ 1452 for (i = 0; i < 3; i++) 1453 si->load_avg[i] = loaddouble (avenrun[i]); 1454 1455 /* get physical memory data */ 1456 if (get_meminfo(&(memory_stats[MEMORY_TOTALMEM]), 1457 &(memory_stats[MEMORY_FREEMEM])) == -1) 1458 { 1459 memory_stats[MEMORY_TOTALMEM] = memory_stats[MEMORY_FREEMEM] = -1; 1460 } 1461 1462 /* get swap data */ 1463 if (get_swapinfo(&(memory_stats[MEMORY_TOTALSWAP]), 1464 &(memory_stats[MEMORY_FREESWAP])) == -1) 1465 { 1466 memory_stats[MEMORY_TOTALSWAP] = memory_stats[MEMORY_FREESWAP] = -1; 1467 } 1468 1469 /* get kernel data */ 1470 kernel_stats[KERNEL_CSWITCH] = diff_per_second(sum_current.pswitch, sum_old.pswitch); 1471 kernel_stats[KERNEL_TRAP] = diff_per_second(sum_current.trap, sum_old.trap); 1472 kernel_stats[KERNEL_INTR] = diff_per_second(sum_current.intr, sum_old.intr); 1473 kernel_stats[KERNEL_SYSCALL] = diff_per_second(sum_current.syscall, sum_old.syscall); 1474 kernel_stats[KERNEL_FORK] = diff_per_second(sum_current.sysfork + sum_current.sysvfork, 1475 sum_old.sysfork + sum_old.sysvfork); 1476 kernel_stats[KERNEL_PFAULT] = diff_per_second(sum_current.pfault, sum_old.pfault); 1477 kernel_stats[KERNEL_PGIN] = pagetok(diff_per_second(sum_current.pgin, sum_old.pgin)); 1478 kernel_stats[KERNEL_PGOUT] = pagetok(diff_per_second(sum_current.pgout, sum_old.pgout)); 1479 1480 1481 /* set arrays and strings */ 1482 si->cpustates = cpu_states; 1483 si->memory = memory_stats; 1484 si->kernel = kernel_stats; 1485 1486 dprintf("get_system_info returns\n"); 1487} 1488 1489static struct handle handle; 1490 1491caddr_t 1492get_process_info ( 1493 struct system_info *si, 1494 struct process_select *sel, 1495 int compare_index) 1496{ 1497 register int i; 1498 register int total_procs; 1499 register int active_procs; 1500 register struct prpsinfo **prefp; 1501 register struct prpsinfo *pp; 1502 int nproc; 1503 int state; 1504 1505 /* these are copied out of sel for speed */ 1506 int show_idle; 1507 int show_system; 1508 int show_uid; 1509 char *show_command; 1510 1511 /* these persist across calls */ 1512 static struct prpsinfo **pref = NULL; 1513 static int pref_size = 0; 1514 1515 /* set up flags which define what we are going to select */ 1516 show_idle = sel->idle; 1517 show_system = sel->system; 1518 show_uid = sel->uid != -1; 1519 show_fullcmd = sel->fullcmd; 1520 show_command = sel->command; 1521 show_threads = sel->threads; 1522 1523 /* allocate enough space for twice our current needs */ 1524 nproc = get_nproc(); 1525 if (nproc > procs_max()) 1526 { 1527 procs_prealloc(2 * nproc); 1528 } 1529 1530 /* read all the proc structures */ 1531 nproc = getptable(); 1532 1533 /* allocate pref[] */ 1534 if (pref_size < nproc) 1535 { 1536 if (pref != NULL) 1537 { 1538 free(pref); 1539 } 1540 pref = (struct prpsinfo **)malloc(nproc * sizeof(struct prpsinfo *)); 1541 dprintf("get_process_info: allocated %d prinfo pointers at %08x\n", 1542 nproc, pref); 1543 pref_size = nproc; 1544 } 1545 1546 /* get a pointer to the states summary array */ 1547 si->procstates = process_states; 1548 1549 /* count up process states and get pointers to interesting procs */ 1550 total_procs = 0; 1551 active_procs = 0; 1552 (void) memset (process_states, 0, sizeof (process_states)); 1553 prefp = pref; 1554 1555 for (pp = procs_start(), i = 0; i < nproc; 1556 i++, pp = procs_next()) 1557 { 1558 dprintf("looking at #%d: %d.%d\n", i, 1559 pp->pr_pid, pp->pr_lwp.pr_lwpid); 1560 /* 1561 * Place pointers to each valid proc structure in pref[]. 1562 * Process slots that are actually in use have a non-zero 1563 * status field. Processes with SSYS set are system 1564 * processes---these get ignored unless show_sysprocs is set. 1565 */ 1566 if (pp->px_state != 0 && 1567 (show_system || ((pp->pr_flag & SSYS) == 0))) 1568 { 1569 total_procs++; 1570 state = (int)pp->px_state; 1571 if (state > 0 && state < PROCSTATES) 1572 { 1573 process_states[state]++; 1574 } 1575 else 1576 { 1577 dprintf("process %d.%d: state out of bounds %d\n", 1578 pp->pr_pid, pp->pr_lwp.pr_lwpid, state); 1579 } 1580 1581 if ((!ZOMBIE(pp)) && 1582 (show_idle || percent_cpu (pp) || (pp->px_state == SRUN) || (pp->px_state == SONPROC)) && 1583 (!show_uid || pp->pr_uid == (uid_t) sel->uid) && 1584 (show_command == NULL || 1585 strstr(pp->pr_fname, show_command) != NULL)) 1586 { 1587 *prefp++ = pp; 1588 active_procs++; 1589 } 1590 } 1591 } 1592 1593 dprintf("total_procs %d, active_procs %d\n", total_procs, active_procs); 1594 1595 /* if requested, sort the "interesting" processes */ 1596 qsort ((char *) pref, active_procs, sizeof (struct prpsinfo *), 1597 proc_compares[compare_index]); 1598 1599 /* remember active and total counts */ 1600 si->p_total = total_procs; 1601 si->p_active = active_procs; 1602 1603 /* pass back a handle */ 1604 handle.next_proc = pref; 1605 handle.remaining = active_procs; 1606 return ((caddr_t) & handle); 1607} 1608 1609static char p_header[MAX_COLS]; 1610 1611char * 1612format_process_header(struct process_select *sel, caddr_t handle, int count) 1613 1614{ 1615 int cols; 1616 char *p; 1617 int *fi; 1618 struct proc_field *fp; 1619 1620 /* check for null handle */ 1621 if (handle == NULL) 1622 { 1623 return(""); 1624 } 1625 1626 /* remember how many columns there are on the display */ 1627 cols = display_columns(); 1628 1629 /* mode & threads dictate format */ 1630 fi = display_fields = sel->threads ? thr_display : proc_display; 1631 1632 /* set username field correctly */ 1633 if (!sel->usernames) 1634 { 1635 /* display uids */ 1636 field_subst(fi, FIELD_USERNAME, FIELD_UID); 1637 } 1638 else 1639 { 1640 /* display usernames */ 1641 field_subst(fi, FIELD_UID, FIELD_USERNAME); 1642 } 1643 1644 /* walk thru fields and construct header */ 1645 /* are we worried about overflow??? */ 1646 p = p_header; 1647 while (*fi != -1) 1648 { 1649 fp = &(proc_field[*fi++]); 1650 if (fp->min_screenwidth <= cols) 1651 { 1652 p += sprintf(p, fp->rjust ? "%*s" : "%-*s", fp->width, fp->name); 1653 *p++ = ' '; 1654 } 1655 } 1656 *--p = '\0'; 1657 1658 return p_header; 1659} 1660 1661static char fmt[MAX_COLS]; /* static area where result is built */ 1662 1663char * 1664format_next_process(caddr_t handle, char *(*get_userid)(int)) 1665 1666{ 1667 struct prpsinfo *pp; 1668 struct handle *hp; 1669 struct proc_field *fp; 1670 int *fi; 1671 int i; 1672 int cols; 1673 char *p; 1674 int len; 1675 int x; 1676 1677 /* find and remember the next proc structure */ 1678 hp = (struct handle *)handle; 1679 pp = *(hp->next_proc++); 1680 hp->remaining--; 1681 1682 /* grab format descriptor */ 1683 fi = display_fields; 1684 1685 /* screen width is a consideration, too */ 1686 cols = display_columns(); 1687 1688 /* build output by field */ 1689 p = fmt; 1690 len = MAX_COLS; 1691 while ((i = *fi++) != -1) 1692 { 1693 fp = &(proc_field[i]); 1694 if (len > 0 && fp->min_screenwidth <= cols) 1695 { 1696 x = (*(fp->format))(p, len, pp); 1697 if (x >= len) 1698 { 1699 dprintf("format_next_process: formatter overflow: x %d, len %d, p %08x => %08x, fmt %08x - %08x\n", 1700 x, len, p, p + len, fmt, fmt + sizeof(fmt)); 1701 p += len; 1702 len = 0; 1703 } 1704 else 1705 { 1706 p += x; 1707 *p++ = ' '; 1708 len -= x + 1; 1709 } 1710 } 1711 } 1712 *--p = '\0'; 1713 1714 /* return the result */ 1715 return(fmt); 1716} 1717 1718/* comparison routines for qsort */ 1719 1720/* 1721 * There are currently four possible comparison routines. main selects 1722 * one of these by indexing in to the array proc_compares. 1723 * 1724 * Possible keys are defined as macros below. Currently these keys are 1725 * defined: percent cpu, cpu ticks, process state, resident set size, 1726 * total virtual memory usage. The process states are ordered as follows 1727 * (from least to most important): WAIT, zombie, sleep, stop, start, run. 1728 * The array declaration below maps a process state index into a number 1729 * that reflects this ordering. 1730 */ 1731 1732/* First, the possible comparison keys. These are defined in such a way 1733 that they can be merely listed in the source code to define the actual 1734 desired ordering. 1735 */ 1736 1737#define ORDERKEY_PCTCPU if (dresult = percent_cpu (p2) - percent_cpu (p1),\ 1738 (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0) 1739#define ORDERKEY_CPTICKS if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0) 1740#define ORDERKEY_STATE if ((result = (long) (sorted_state[(int)p2->px_state] - \ 1741 sorted_state[(int)p1->px_state])) == 0) 1742#define ORDERKEY_PRIO if ((result = p2->px_pri - p1->px_pri) == 0) 1743#define ORDERKEY_RSSIZE if ((result = p2->pr_rssize - p1->pr_rssize) == 0) 1744#define ORDERKEY_MEM if ((result = (p2->pr_size - p1->pr_size)) == 0) 1745#define ORDERKEY_LWP if ((result = (p1->pr_lwp.pr_lwpid - p2->pr_lwp.pr_lwpid)) == 0) 1746#define ORDERKEY_PID if ((result = (p1->pr_pid - p2->pr_pid)) == 0) 1747 1748/* Now the array that maps process state to a weight */ 1749 1750unsigned char sorted_state[] = 1751{ 1752 0, /* not used */ 1753 3, /* sleep */ 1754 6, /* run */ 1755 2, /* zombie */ 1756 4, /* stop */ 1757 5, /* start */ 1758 7, /* run on a processor */ 1759 1 /* being swapped (WAIT) */ 1760}; 1761 1762 1763/* compare_cpu - the comparison function for sorting by cpu percentage */ 1764 1765int 1766compare_cpu (struct prpsinfo **pp1, struct prpsinfo **pp2) 1767 1768{ 1769 register struct prpsinfo *p1; 1770 register struct prpsinfo *p2; 1771 register long result; 1772 double dresult; 1773 1774 /* remove one level of indirection */ 1775 p1 = *pp1; 1776 p2 = *pp2; 1777 1778 ORDERKEY_PCTCPU 1779 ORDERKEY_CPTICKS 1780 ORDERKEY_STATE 1781 ORDERKEY_PRIO 1782 ORDERKEY_RSSIZE 1783 ORDERKEY_MEM 1784 ORDERKEY_PID 1785 ORDERKEY_LWP 1786 ; 1787 1788 return (result); 1789} 1790 1791/* compare_size - the comparison function for sorting by total memory usage */ 1792 1793int 1794compare_size (struct prpsinfo **pp1, struct prpsinfo **pp2) 1795 1796{ 1797 register struct prpsinfo *p1; 1798 register struct prpsinfo *p2; 1799 register long result; 1800 double dresult; 1801 1802 /* remove one level of indirection */ 1803 p1 = *pp1; 1804 p2 = *pp2; 1805 1806 ORDERKEY_MEM 1807 ORDERKEY_RSSIZE 1808 ORDERKEY_PCTCPU 1809 ORDERKEY_CPTICKS 1810 ORDERKEY_STATE 1811 ORDERKEY_PRIO 1812 ORDERKEY_PID 1813 ORDERKEY_LWP 1814 ; 1815 1816 return (result); 1817} 1818 1819/* compare_res - the comparison function for sorting by resident set size */ 1820 1821int 1822compare_res (struct prpsinfo **pp1, struct prpsinfo **pp2) 1823 1824{ 1825 register struct prpsinfo *p1; 1826 register struct prpsinfo *p2; 1827 register long result; 1828 double dresult; 1829 1830 /* remove one level of indirection */ 1831 p1 = *pp1; 1832 p2 = *pp2; 1833 1834 ORDERKEY_RSSIZE 1835 ORDERKEY_MEM 1836 ORDERKEY_PCTCPU 1837 ORDERKEY_CPTICKS 1838 ORDERKEY_STATE 1839 ORDERKEY_PRIO 1840 ORDERKEY_PID 1841 ORDERKEY_LWP 1842 ; 1843 1844 return (result); 1845} 1846 1847/* compare_time - the comparison function for sorting by total cpu time */ 1848 1849int 1850compare_time (struct prpsinfo **pp1, struct prpsinfo **pp2) 1851 1852{ 1853 register struct prpsinfo *p1; 1854 register struct prpsinfo *p2; 1855 register long result; 1856 double dresult; 1857 1858 /* remove one level of indirection */ 1859 p1 = *pp1; 1860 p2 = *pp2; 1861 1862 ORDERKEY_CPTICKS 1863 ORDERKEY_PCTCPU 1864 ORDERKEY_STATE 1865 ORDERKEY_PRIO 1866 ORDERKEY_MEM 1867 ORDERKEY_RSSIZE 1868 ORDERKEY_PID 1869 ORDERKEY_LWP 1870 ; 1871 1872 return (result); 1873} 1874 1875/* compare_pid - the comparison function for sorting by process id */ 1876 1877int 1878compare_pid (struct prpsinfo **pp1, struct prpsinfo **pp2) 1879 1880{ 1881 register struct prpsinfo *p1; 1882 register struct prpsinfo *p2; 1883 register long result; 1884 1885 /* remove one level of indirection */ 1886 p1 = *pp1; 1887 p2 = *pp2; 1888 1889 ORDERKEY_PID 1890 ORDERKEY_LWP 1891 ; 1892 1893 return (result); 1894} 1895 1896/* get process table */ 1897int 1898getptable (struct prpsinfo *baseptr) 1899{ 1900 struct prpsinfo *currproc; /* pointer to current proc structure */ 1901#ifndef USE_NEW_PROC 1902 struct prstatus prstatus; /* for additional information */ 1903#endif 1904 int numprocs = 0; 1905 struct dirent *direntp; 1906 struct oldproc *op; 1907 hash_pos pos; 1908 hash_item_pid *hi; 1909 hash_item_pidthr *hip; 1910 prheader_t *prp; 1911 lwpsinfo_t *lwpp; 1912 pidthr_t pidthr; 1913 static struct timeval lasttime = 1914 {0, 0}; 1915 struct timeval thistime; 1916 struct stat st; 1917 double timediff; 1918 1919 gettimeofday (&thistime, NULL); 1920 /* 1921 * To avoid divides, we keep times in nanoseconds. This is 1922 * scaled by 1e7 rather than 1e9 so that when we divide we 1923 * get percent. 1924 */ 1925 if (lasttime.tv_sec) 1926 timediff = ((double) thistime.tv_sec * 1.0e7 + 1927 ((double) thistime.tv_usec * 10.0)) - 1928 ((double) lasttime.tv_sec * 1.0e7 + 1929 ((double) lasttime.tv_usec * 10.0)); 1930 else 1931 timediff = 1.0e7; 1932 1933 /* get our first procs pointer */ 1934 currproc = procs_start(); 1935 1936 /* before reading /proc files, turn on root privs */ 1937 /* (we don't care if this fails since it will be caught later) */ 1938#ifndef USE_NEW_PROC 1939 seteuid(0); 1940#endif 1941 1942 for (rewinddir (procdir); (direntp = readdir (procdir));) 1943 { 1944 int fd; 1945 int pid; 1946 char buf[40]; 1947 1948 /* skip dot files */ 1949 if (direntp->d_name[0] == '.') 1950 continue; 1951 1952 /* convert pid to a number (and make sure its valid) */ 1953 pid = atoi(direntp->d_name); 1954 if (pid <= 0) 1955 continue; 1956 1957 /* fetch the old proc data */ 1958 op = (struct oldproc *)hash_lookup_pid(prochash, pid); 1959 if (op == NULL) 1960 { 1961 /* new proc: create an entry for it */ 1962 op = (struct oldproc *)malloc(sizeof(struct oldproc)); 1963 hash_add_pid(prochash, pid, (void *)op); 1964 op->pid = pid; 1965 op->fd_psinfo = -1; 1966 op->fd_lpsinfo = -1; 1967 op->oldtime = 0.0; 1968 } 1969 1970 /* do we have a cached file? */ 1971 fd = op->fd_psinfo; 1972 if (fd == -1) 1973 { 1974 /* no: open the psinfo file */ 1975 snprintf(buf, sizeof(buf), "%s/psinfo", direntp->d_name); 1976 if ((fd = open(buf, O_RDONLY)) < 0) 1977 { 1978 /* cleanup??? */ 1979 continue; 1980 } 1981 } 1982 1983 /* read data from the file */ 1984#ifdef USE_NEW_PROC 1985 if (pread(fd, currproc, sizeof(psinfo_t), 0) != sizeof(psinfo_t)) 1986 { 1987 (void) close (fd); 1988 op->fd_psinfo = -1; 1989 continue; 1990 } 1991#else 1992 if (ioctl(fd, PIOCPSINFO, currproc) < 0) 1993 { 1994 (void) close(fd); 1995 op->fd_psinfo = -1; 1996 continue; 1997 } 1998 1999 if (ioctl(fd, PIOCSTATUS, &prstatus) < 0) 2000 { 2001 /* not a show stopper -- just fill in the needed values */ 2002 currproc->pr_fill = 0; 2003 currproc->px_onpro = 0; 2004 } 2005 else 2006 { 2007 /* copy over the values we need from prstatus */ 2008 currproc->pr_fill = (short)prstatus.pr_nlwp; 2009 currproc->px_onpro = prstatus.pr_processor; 2010 } 2011#endif 2012 2013 /* 2014 * We track our own cpu% usage. 2015 * We compute it based on CPU since the last update by calculating 2016 * the difference in cumulative cpu time and dividing by the amount 2017 * of time we measured between updates (timediff). 2018 * NOTE: Solaris 2.4 and higher do maintain CPU% in psinfo, 2019 * but it does not produce the kind of results we really want, 2020 * so we don't use it even though its there. 2021 */ 2022 if (lasttime.tv_sec > 0) 2023 { 2024 percent_cpu(currproc) = 2025 (TIMESPEC_TO_DOUBLE(currproc->pr_time) - op->oldtime) / timediff; 2026 } 2027 else 2028 { 2029 /* first screen -- no difference is possible */ 2030 percent_cpu(currproc) = 0.0; 2031 } 2032 2033 /* save data for next time */ 2034 op->pid = currproc->pr_pid; 2035 op->oldtime = TIMESPEC_TO_DOUBLE(currproc->pr_time); 2036 op->owner_uid = currproc->pr_uid; 2037 op->seen = 1; 2038 2039 /* cache the file descriptor if we can */ 2040 if (fd < maxfiles) 2041 { 2042 op->fd_psinfo = fd; 2043 } 2044 else 2045 { 2046 (void) close(fd); 2047 } 2048 2049#ifdef USE_NEW_PROC 2050 /* collect up the threads */ 2051 /* use cached lps file if it's there */ 2052 fd = op->fd_lpsinfo; 2053 if (fd == -1) 2054 { 2055 snprintf(buf, sizeof(buf), "%s/lpsinfo", direntp->d_name); 2056 fd = open(buf, O_RDONLY); 2057 } 2058 2059 /* make sure we have a valid descriptor and the file's current size */ 2060 if (fd >= 0 && fstat(fd, &st) != -1) 2061 { 2062 char *p; 2063 int i; 2064 2065 /* read the whole file */ 2066 p = malloc(st.st_size); 2067 (void)pread(fd, p, st.st_size, 0); 2068 2069 /* cache the file descriptor if we can */ 2070 if (fd < maxfiles) 2071 { 2072 op->fd_lpsinfo = fd; 2073 } 2074 else 2075 { 2076 (void)close(fd); 2077 } 2078 2079 /* the file starts with a struct prheader */ 2080 prp = (prheader_t *)p; 2081 p += sizeof(prheader_t); 2082 2083 /* there are prp->pr_nent entries in the file */ 2084 for (i = 0; i < prp->pr_nent; i++) 2085 { 2086 /* process this entry */ 2087 lwpp = (lwpsinfo_t *)p; 2088 p += prp->pr_entsize; 2089 2090 /* fetch the old thread data */ 2091 /* this hash is indexed by both pid and lwpid */ 2092 pidthr.k_pid = currproc->pr_pid; 2093 pidthr.k_thr = lwpp->pr_lwpid; 2094 dprintf("getptable: processing %d.%d\n", 2095 pidthr.k_pid, pidthr.k_thr); 2096 op = (struct oldproc *)hash_lookup_pidthr(threadhash, pidthr); 2097 if (op == NULL) 2098 { 2099 /* new thread: create an entry for it */ 2100 op = (struct oldproc *)malloc(sizeof(struct oldproc)); 2101 hash_add_pidthr(threadhash, pidthr, (void *)op); 2102 op->pid = pid; 2103 op->lwpid = lwpp->pr_lwpid; 2104 op->oldtime = 0.0; 2105 dprintf("getptable: %d.%d: new thread\n", 2106 pidthr.k_pid, pidthr.k_thr); 2107 } 2108 2109 /* are we showing individual threads? */ 2110 if (show_threads) 2111 { 2112 /* yes: if this is the first thread we reuse the proc 2113 entry we have, otherwise we create a new one by 2114 duping the current one */ 2115 if (i > 0) 2116 { 2117 currproc = procs_dup(currproc); 2118 numprocs++; 2119 } 2120 2121 /* yes: copy over thread-specific data */ 2122 currproc->pr_time = lwpp->pr_time; 2123 currproc->px_state = lwpp->pr_state; 2124 currproc->px_pri = lwpp->pr_pri; 2125 currproc->px_onpro = lwpp->pr_onpro; 2126 currproc->pr_lwp.pr_lwpid = lwpp->pr_lwpid; 2127 2128 /* calculate percent cpu for just this thread */ 2129 if (lasttime.tv_sec > 0) 2130 { 2131 percent_cpu(currproc) = 2132 (TIMESPEC_TO_DOUBLE(lwpp->pr_time) - op->oldtime) / 2133 timediff; 2134 } 2135 else 2136 { 2137 /* first screen -- no difference is possible */ 2138 percent_cpu(currproc) = 0.0; 2139 } 2140 2141 dprintf("getptable: %d.%d: time %.0f, state %d, pctcpu %.2f\n", 2142 currproc->pr_pid, lwpp->pr_lwpid, 2143 TIMESPEC_TO_DOUBLE(currproc->pr_time), 2144 currproc->px_state, percent_cpu(currproc)); 2145 } 2146 2147 /* save data for next time */ 2148 op->oldtime = TIMESPEC_TO_DOUBLE(lwpp->pr_time); 2149 op->seen = 1; 2150 } 2151 free(p); 2152 } 2153#endif 2154 2155 /* move to next */ 2156 numprocs++; 2157 currproc = procs_next(); 2158 } 2159 2160#ifndef USE_NEW_PROC 2161 /* turn off root privs */ 2162 seteuid(getuid()); 2163#endif 2164 2165 dprintf("getptable saw %d procs\n", numprocs); 2166 2167 /* scan the hash tables and remove dead entries */ 2168 hi = hash_first_pid(prochash, &pos); 2169 while (hi != NULL) 2170 { 2171 op = (struct oldproc *)(hi->value); 2172 if (op->seen) 2173 { 2174 op->seen = 0; 2175 } 2176 else 2177 { 2178 dprintf("removing %d from prochash\n", op->pid); 2179 if (op->fd_psinfo >= 0) 2180 { 2181 (void)close(op->fd_psinfo); 2182 } 2183 if (op->fd_lpsinfo >= 0) 2184 { 2185 (void)close(op->fd_lpsinfo); 2186 } 2187 hash_remove_pos_pid(&pos); 2188 free(op); 2189 } 2190 hi = hash_next_pid(&pos); 2191 } 2192 2193 hip = hash_first_pidthr(threadhash, &pos); 2194 while (hip != NULL) 2195 { 2196 op = (struct oldproc *)(hip->value); 2197 if (op->seen) 2198 { 2199 op->seen = 0; 2200 } 2201 else 2202 { 2203 dprintf("removing %d from threadhash\n", op->pid); 2204 hash_remove_pos_pidthr(&pos); 2205 free(op); 2206 } 2207 hip = hash_next_pidthr(&pos); 2208 } 2209 2210 lasttime = thistime; 2211 2212 return numprocs; 2213} 2214 2215/* 2216 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if 2217 * the process does not exist. 2218 * It is EXTREMLY IMPORTANT that this function work correctly. 2219 * If top runs setuid root (as in SVR4), then this function 2220 * is the only thing that stands in the way of a serious 2221 * security problem. It validates requests for the "kill" 2222 * and "renice" commands. 2223 */ 2224int 2225proc_owner (int pid) 2226{ 2227 struct oldproc *op; 2228 2229 /* we keep this information in the hash table */ 2230 op = (struct oldproc *)hash_lookup_pid(prochash, (pid_t)pid); 2231 if (op != NULL) 2232 { 2233 return((int)(op->owner_uid)); 2234 } 2235 return(-1); 2236} 2237 2238/* older revisions don't supply a setpriority */ 2239#if (OSREV < 55) 2240int 2241setpriority (int dummy, int who, int niceval) 2242{ 2243 int scale; 2244 int prio; 2245 pcinfo_t pcinfo; 2246 pcparms_t pcparms; 2247 tsparms_t *tsparms; 2248 2249 strcpy (pcinfo.pc_clname, "TS"); 2250 if (priocntl (0, 0, PC_GETCID, (caddr_t) & pcinfo) == -1) 2251 return (-1); 2252 2253 prio = niceval; 2254 if (prio > PRIO_MAX) 2255 prio = PRIO_MAX; 2256 else if (prio < PRIO_MIN) 2257 prio = PRIO_MIN; 2258 2259 tsparms = (tsparms_t *) pcparms.pc_clparms; 2260 scale = ((tsinfo_t *) pcinfo.pc_clinfo)->ts_maxupri; 2261 tsparms->ts_uprilim = tsparms->ts_upri = -(scale * prio) / 20; 2262 pcparms.pc_cid = pcinfo.pc_cid; 2263 2264 if (priocntl (P_PID, who, PC_SETPARMS, (caddr_t) & pcparms) == -1) 2265 return (-1); 2266 2267 return (0); 2268} 2269#endif 2270 2271