machine.c revision 1.12
1/* $OpenBSD: machine.c,v 1.12 1998/09/20 05:58:54 niklas Exp $ */ 2 3/* 4 * top - a top users display for Unix 5 * 6 * SYNOPSIS: For an OpenBSD system 7 * 8 * DESCRIPTION: 9 * This is the machine-dependent module for OpenBSD 10 * Tested on: 11 * i386 12 * 13 * LIBS: -lkvm 14 * 15 * TERMCAP: -ltermlib 16 * 17 * CFLAGS: -DHAVE_GETOPT -DORDER 18 * 19 * AUTHOR: Thorsten Lockert <tholo@sigmasoft.com> 20 * Adapted from BSD4.4 by Christos Zoulas <christos@ee.cornell.edu> 21 * Patch for process wait display by Jarl F. Greipsland <jarle@idt.unit.no> 22 * Patch for -DORDER by Kenneth Stailey <kstailey@disclosure.com> 23 */ 24 25#include <sys/types.h> 26#include <sys/signal.h> 27#include <sys/param.h> 28 29#define DOSWAP 30 31#include <stdio.h> 32#include <stdlib.h> 33#include <string.h> 34#include <limits.h> 35#include <err.h> 36#include <nlist.h> 37#include <math.h> 38#include <kvm.h> 39#include <unistd.h> 40#include <sys/errno.h> 41#include <sys/sysctl.h> 42#include <sys/dir.h> 43#include <sys/dkstat.h> 44#include <sys/file.h> 45#include <sys/time.h> 46#include <sys/resource.h> 47 48#ifdef DOSWAP 49#include <err.h> 50#include <sys/map.h> 51#include <sys/conf.h> 52#endif 53 54static int check_nlist __P((struct nlist *)); 55static int getkval __P((unsigned long, int *, int, char *)); 56static int swapmode __P((int *, int *)); 57 58#include "top.h" 59#include "display.h" 60#include "machine.h" 61#include "utils.h" 62 63/* get_process_info passes back a handle. This is what it looks like: */ 64 65struct handle 66{ 67 struct kinfo_proc **next_proc; /* points to next valid proc pointer */ 68 int remaining; /* number of pointers remaining */ 69}; 70 71/* declarations for load_avg */ 72#include "loadavg.h" 73 74#define PP(pp, field) ((pp)->kp_proc . field) 75#define EP(pp, field) ((pp)->kp_eproc . field) 76#define VP(pp, field) ((pp)->kp_eproc.e_vm . field) 77 78/* what we consider to be process size: */ 79#define PROCSIZE(pp) (VP((pp), vm_tsize) + VP((pp), vm_dsize) + VP((pp), vm_ssize)) 80 81/* definitions for indices in the nlist array */ 82#define X_CP_TIME 0 83#define X_HZ 1 84 85#ifdef DOSWAP 86#define VM_SWAPMAP 2 87#define VM_NSWAPMAP 3 88#define VM_SWDEVT 4 89#define VM_NSWAP 5 90#define VM_NSWDEV 6 91#define VM_DMMAX 7 92#define VM_NISWAP 8 93#define VM_NISWDEV 9 94#endif 95 96static struct nlist nlst[] = { 97 { "_cp_time" }, /* 0 */ 98 { "_hz" }, /* 1 */ 99#ifdef DOSWAP 100 { "_swapmap" }, /* 2 */ 101 { "_nswapmap" }, /* 3 */ 102 { "_swdevt" }, /* 4 */ 103 { "_nswap" }, /* 5 */ 104 { "_nswdev" }, /* 6 */ 105 { "_dmmax" }, /* 7 */ 106 { "_niswap" }, /* 8 */ 107 { "_niswdev" }, /* 9 */ 108#endif 109 { 0 } 110}; 111 112/* 113 * These definitions control the format of the per-process area 114 */ 115 116static char header[] = 117 " PID X PRI NICE SIZE RES STATE WAIT TIME CPU COMMAND"; 118/* 0123456 -- field to fill in starts at header+6 */ 119#define UNAME_START 6 120 121#define Proc_format \ 122 "%5d %-8.8s %3d %4d %5s %5s %-5s %-6.6s %6s %5.2f%% %.14s" 123 124 125/* process state names for the "STATE" column of the display */ 126/* the extra nulls in the string "run" are for adding a slash and 127 the processor number when needed */ 128 129char *state_abbrev[] = 130{ 131 "", "start", "run\0\0\0", "sleep", "stop", "zomb", 132}; 133 134 135static kvm_t *kd; 136 137/* these are retrieved from the kernel in _init */ 138 139static int hz; 140 141/* these are offsets obtained via nlist and used in the get_ functions */ 142 143static unsigned long cp_time_offset; 144 145/* these are for calculating cpu state percentages */ 146static int cp_time[CPUSTATES]; 147static int cp_old[CPUSTATES]; 148static int cp_diff[CPUSTATES]; 149 150/* these are for detailing the process states */ 151 152int process_states[7]; 153char *procstatenames[] = { 154 "", " starting, ", " running, ", " idle, ", " stopped, ", " zombie, ", 155 NULL 156}; 157 158/* these are for detailing the cpu states */ 159 160int cpu_states[CPUSTATES]; 161char *cpustatenames[] = { 162 "user", "nice", "system", "interrupt", "idle", NULL 163}; 164 165/* these are for detailing the memory statistics */ 166 167int memory_stats[8]; 168char *memorynames[] = { 169 "Real: ", "K/", "K act/tot ", "Free: ", "K ", 170#ifdef DOSWAP 171 "Swap: ", "K/", "K used/tot", 172#endif 173 NULL 174}; 175 176#ifdef ORDER 177/* these are names given to allowed sorting orders -- first is default */ 178 179char *ordernames[] = {"cpu", "size", "res", "time", "pri", NULL}; 180#endif 181 182/* these are for keeping track of the proc array */ 183 184static int nproc; 185static int onproc = -1; 186static int pref_len; 187static struct kinfo_proc *pbase; 188static struct kinfo_proc **pref; 189 190/* these are for getting the memory statistics */ 191 192static int pageshift; /* log base 2 of the pagesize */ 193 194/* define pagetok in terms of pageshift */ 195 196#define pagetok(size) ((size) << pageshift) 197 198int 199machine_init(statics) 200 201struct statics *statics; 202 203{ 204 register int i = 0; 205 register int pagesize; 206 char errbuf[_POSIX2_LINE_MAX]; 207 208 if ((kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf)) == NULL) { 209 warnx("%s", errbuf); 210 return(-1); 211 } 212 213 setegid(getgid()); 214 setgid(getgid()); 215 216 /* get the list of symbols we want to access in the kernel */ 217 if (kvm_nlist(kd, nlst) <= 0) { 218 warnx("nlist failed"); 219 return(-1); 220 } 221 222 /* make sure they were all found */ 223 if (i > 0 && check_nlist(nlst) > 0) 224 return(-1); 225 226 /* get the symbol values out of kmem */ 227 (void) getkval(nlst[X_HZ].n_value, (int *)(&hz), sizeof(hz), 228 nlst[X_HZ].n_name); 229 230 /* stash away certain offsets for later use */ 231 cp_time_offset = nlst[X_CP_TIME].n_value; 232 233 pbase = NULL; 234 pref = NULL; 235 onproc = -1; 236 nproc = 0; 237 238 /* get the page size with "getpagesize" and calculate pageshift from it */ 239 pagesize = getpagesize(); 240 pageshift = 0; 241 while (pagesize > 1) 242 { 243 pageshift++; 244 pagesize >>= 1; 245 } 246 247 /* we only need the amount of log(2)1024 for our conversion */ 248 pageshift -= LOG1024; 249 250 /* fill in the statics information */ 251 statics->procstate_names = procstatenames; 252 statics->cpustate_names = cpustatenames; 253 statics->memory_names = memorynames; 254#ifdef ORDER 255 statics->order_names = ordernames; 256#endif 257 258 /* all done! */ 259 return(0); 260} 261 262char *format_header(uname_field) 263 264register char *uname_field; 265 266{ 267 register char *ptr; 268 269 ptr = header + UNAME_START; 270 while (*uname_field != '\0') 271 { 272 *ptr++ = *uname_field++; 273 } 274 275 return(header); 276} 277 278void 279get_system_info(si) 280 281struct system_info *si; 282 283{ 284 int total; 285 286 /* get the cp_time array */ 287 (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time), 288 "_cp_time"); 289 290 /* convert load averages to doubles */ 291 { 292 register int i; 293 register double *infoloadp; 294 struct loadavg sysload; 295 size_t size = sizeof(sysload); 296 static int mib[] = { CTL_VM, VM_LOADAVG }; 297 298 if (sysctl(mib, 2, &sysload, &size, NULL, 0) < 0) { 299 warn("sysctl failed"); 300 bzero(&total, sizeof(total)); 301 } 302 303 infoloadp = si->load_avg; 304 for (i = 0; i < 3; i++) 305 *infoloadp++ = ((double) sysload.ldavg[i]) / sysload.fscale; 306 } 307 308 /* convert cp_time counts to percentages */ 309 total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); 310 311 /* sum memory statistics */ 312 { 313 struct vmtotal total; 314 size_t size = sizeof(total); 315 static int mib[] = { CTL_VM, VM_METER }; 316 317 /* get total -- systemwide main memory usage structure */ 318 if (sysctl(mib, 2, &total, &size, NULL, 0) < 0) { 319 warn("sysctl failed"); 320 bzero(&total, sizeof(total)); 321 } 322 /* convert memory stats to Kbytes */ 323 memory_stats[0] = -1; 324 memory_stats[1] = pagetok(total.t_arm); 325 memory_stats[2] = pagetok(total.t_rm); 326 memory_stats[3] = -1; 327 memory_stats[4] = pagetok(total.t_free); 328 memory_stats[5] = -1; 329#ifdef DOSWAP 330 if (!swapmode(&memory_stats[6], &memory_stats[7])) { 331 memory_stats[6] = 0; 332 memory_stats[7] = 0; 333 } 334#endif 335 } 336 337 /* set arrays and strings */ 338 si->cpustates = cpu_states; 339 si->memory = memory_stats; 340 si->last_pid = -1; 341} 342 343static struct handle handle; 344 345caddr_t get_process_info(si, sel, compare) 346 347struct system_info *si; 348struct process_select *sel; 349int (*compare) __P((const void *, const void *)); 350 351{ 352 register int i; 353 register int total_procs; 354 register int active_procs; 355 register struct kinfo_proc **prefp; 356 register struct kinfo_proc *pp; 357 358 /* these are copied out of sel for speed */ 359 int show_idle; 360 int show_system; 361 int show_uid; 362 int show_command; 363 364 365 if ((pbase = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc)) == NULL) { 366 warnx("%s", kvm_geterr(kd)); 367 quit(23); 368 } 369 if (nproc > onproc) 370 pref = (struct kinfo_proc **) realloc(pref, sizeof(struct kinfo_proc *) 371 * (onproc = nproc)); 372 if (pref == NULL) { 373 warnx("Out of memory."); 374 quit(23); 375 } 376 /* get a pointer to the states summary array */ 377 si->procstates = process_states; 378 379 /* set up flags which define what we are going to select */ 380 show_idle = sel->idle; 381 show_system = sel->system; 382 show_uid = sel->uid != -1; 383 show_command = sel->command != NULL; 384 385 /* count up process states and get pointers to interesting procs */ 386 total_procs = 0; 387 active_procs = 0; 388 memset((char *)process_states, 0, sizeof(process_states)); 389 prefp = pref; 390 for (pp = pbase, i = 0; i < nproc; pp++, i++) 391 { 392 /* 393 * Place pointers to each valid proc structure in pref[]. 394 * Process slots that are actually in use have a non-zero 395 * status field. Processes with SSYS set are system 396 * processes---these get ignored unless show_sysprocs is set. 397 */ 398 if (PP(pp, p_stat) != 0 && 399 (show_system || ((PP(pp, p_flag) & P_SYSTEM) == 0))) 400 { 401 total_procs++; 402 process_states[(unsigned char) PP(pp, p_stat)]++; 403 if ((PP(pp, p_stat) != SZOMB) && 404 (show_idle || (PP(pp, p_pctcpu) != 0) || 405 (PP(pp, p_stat) == SRUN)) && 406 (!show_uid || EP(pp, e_pcred.p_ruid) == (uid_t)sel->uid)) 407 { 408 *prefp++ = pp; 409 active_procs++; 410 } 411 } 412 } 413 414 /* if requested, sort the "interesting" processes */ 415 if (compare != NULL) 416 { 417 qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *), compare); 418 } 419 420 /* remember active and total counts */ 421 si->p_total = total_procs; 422 si->p_active = pref_len = active_procs; 423 424 /* pass back a handle */ 425 handle.next_proc = pref; 426 handle.remaining = active_procs; 427 return((caddr_t)&handle); 428} 429 430char fmt[MAX_COLS]; /* static area where result is built */ 431 432char *format_next_process(handle, get_userid) 433 434caddr_t handle; 435char *(*get_userid)(); 436 437{ 438 register struct kinfo_proc *pp; 439 register int cputime; 440 register double pct; 441 struct handle *hp; 442 char waddr[sizeof(void *) * 2 + 3]; /* Hexify void pointer */ 443 char *p_wait; 444 445 /* find and remember the next proc structure */ 446 hp = (struct handle *)handle; 447 pp = *(hp->next_proc++); 448 hp->remaining--; 449 450 451 /* get the process's user struct and set cputime */ 452 if ((PP(pp, p_flag) & P_INMEM) == 0) { 453 /* 454 * Print swapped processes as <pname> 455 */ 456 char *comm = PP(pp, p_comm); 457#define COMSIZ sizeof(PP(pp, p_comm)) 458 char buf[COMSIZ]; 459 (void) strncpy(buf, comm, COMSIZ); 460 comm[0] = '<'; 461 (void) strncpy(&comm[1], buf, COMSIZ - 2); 462 comm[COMSIZ - 2] = '\0'; 463 (void) strncat(comm, ">", COMSIZ - 1); 464 comm[COMSIZ - 1] = '\0'; 465 } 466 467 cputime = (PP(pp, p_uticks) + PP(pp, p_sticks) + PP(pp, p_iticks)) / hz; 468 469 /* calculate the base for cpu percentages */ 470 pct = pctdouble(PP(pp, p_pctcpu)); 471 472 if (PP(pp, p_wchan)) 473 if (PP(pp, p_wmesg)) 474 p_wait = EP(pp, e_wmesg); 475 else { 476 snprintf(waddr, sizeof(waddr), "%lx", 477 (unsigned long)(PP(pp, p_wchan)) & ~KERNBASE); 478 p_wait = waddr; 479 } 480 else 481 p_wait = "-"; 482 483 /* format this entry */ 484 snprintf(fmt, MAX_COLS, 485 Proc_format, 486 PP(pp, p_pid), 487 (*get_userid)(EP(pp, e_pcred.p_ruid)), 488 PP(pp, p_priority) - PZERO, 489 PP(pp, p_nice) - NZERO, 490 format_k(pagetok(PROCSIZE(pp))), 491 format_k(pagetok(VP(pp, vm_rssize))), 492 (PP(pp, p_stat) == SSLEEP && PP(pp, p_slptime) > MAXSLP) 493 ? "idle" : state_abbrev[(unsigned char) PP(pp, p_stat)], 494 p_wait, 495 format_time(cputime), 496 100.0 * pct, 497 printable(PP(pp, p_comm))); 498 499 /* return the result */ 500 return(fmt); 501} 502 503 504/* 505 * check_nlist(nlst) - checks the nlist to see if any symbols were not 506 * found. For every symbol that was not found, a one-line 507 * message is printed to stderr. The routine returns the 508 * number of symbols NOT found. 509 */ 510 511static int check_nlist(nlst) 512 513register struct nlist *nlst; 514 515{ 516 register int i; 517 518 /* check to see if we got ALL the symbols we requested */ 519 /* this will write one line to stderr for every symbol not found */ 520 521 i = 0; 522 while (nlst->n_name != NULL) 523 { 524 if (nlst->n_type == 0) 525 { 526 /* this one wasn't found */ 527 (void) fprintf(stderr, "kernel: no symbol named `%s'\n", 528 nlst->n_name); 529 i = 1; 530 } 531 nlst++; 532 } 533 534 return(i); 535} 536 537 538/* 539 * getkval(offset, ptr, size, refstr) - get a value out of the kernel. 540 * "offset" is the byte offset into the kernel for the desired value, 541 * "ptr" points to a buffer into which the value is retrieved, 542 * "size" is the size of the buffer (and the object to retrieve), 543 * "refstr" is a reference string used when printing error meessages, 544 * if "refstr" starts with a '!', then a failure on read will not 545 * be fatal (this may seem like a silly way to do things, but I 546 * really didn't want the overhead of another argument). 547 * 548 */ 549 550static int getkval(offset, ptr, size, refstr) 551 552unsigned long offset; 553int *ptr; 554int size; 555char *refstr; 556 557{ 558 if (kvm_read(kd, offset, (char *) ptr, size) != size) 559 { 560 if (*refstr == '!') 561 { 562 return(0); 563 } 564 else 565 { 566 warn("kvm_read for %s", refstr); 567 quit(23); 568 } 569 } 570 return(1); 571} 572 573/* comparison routine for qsort */ 574 575static unsigned char sorted_state[] = 576{ 577 0, /* not used */ 578 4, /* start */ 579 5, /* run */ 580 2, /* sleep */ 581 3, /* stop */ 582 1 /* zombie */ 583}; 584 585#ifdef ORDER 586 587/* 588 * proc_compares - comparison functions for "qsort" 589 */ 590 591/* 592 * First, the possible comparison keys. These are defined in such a way 593 * that they can be merely listed in the source code to define the actual 594 * desired ordering. 595 */ 596 597 598#define ORDERKEY_PCTCPU \ 599 if (lresult = (pctcpu)PP(p2, p_pctcpu) - (pctcpu)PP(p1, p_pctcpu), \ 600 (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0) 601#define ORDERKEY_CPUTIME \ 602 if ((result = PP(p2, p_rtime.tv_sec) - PP(p1, p_rtime.tv_sec)) == 0) \ 603 if ((result = PP(p2, p_rtime.tv_usec) - \ 604 PP(p1, p_rtime.tv_usec)) == 0) 605#define ORDERKEY_STATE \ 606 if ((result = sorted_state[(unsigned char) PP(p2, p_stat)] - \ 607 sorted_state[(unsigned char) PP(p1, p_stat)]) == 0) 608#define ORDERKEY_PRIO \ 609 if ((result = PP(p2, p_priority) - PP(p1, p_priority)) == 0) 610#define ORDERKEY_RSSIZE \ 611 if ((result = VP(p2, vm_rssize) - VP(p1, vm_rssize)) == 0) 612#define ORDERKEY_MEM \ 613 if ((result = PROCSIZE(p2) - PROCSIZE(p1)) == 0) 614 615 616/* compare_cpu - the comparison function for sorting by cpu percentage */ 617 618int 619compare_cpu(v1, v2) 620 621const void *v1, *v2; 622 623{ 624 register struct proc **pp1 = (struct proc **)v1; 625 register struct proc **pp2 = (struct proc **)v2; 626 register struct kinfo_proc *p1; 627 register struct kinfo_proc *p2; 628 register int result; 629 register pctcpu lresult; 630 631 /* remove one level of indirection */ 632 p1 = *(struct kinfo_proc **) pp1; 633 p2 = *(struct kinfo_proc **) pp2; 634 635 ORDERKEY_PCTCPU 636 ORDERKEY_CPUTIME 637 ORDERKEY_STATE 638 ORDERKEY_PRIO 639 ORDERKEY_RSSIZE 640 ORDERKEY_MEM 641 ; 642 return(result); 643} 644 645/* compare_size - the comparison function for sorting by total memory usage */ 646 647int 648compare_size(v1, v2) 649 650const void *v1, *v2; 651 652{ 653 register struct proc **pp1 = (struct proc **)v1; 654 register struct proc **pp2 = (struct proc **)v2; 655 register struct kinfo_proc *p1; 656 register struct kinfo_proc *p2; 657 register int result; 658 register pctcpu lresult; 659 660 /* remove one level of indirection */ 661 p1 = *(struct kinfo_proc **) pp1; 662 p2 = *(struct kinfo_proc **) pp2; 663 664 ORDERKEY_MEM 665 ORDERKEY_RSSIZE 666 ORDERKEY_PCTCPU 667 ORDERKEY_CPUTIME 668 ORDERKEY_STATE 669 ORDERKEY_PRIO 670 ; 671 672 return(result); 673} 674 675/* compare_res - the comparison function for sorting by resident set size */ 676 677int 678compare_res(v1, v2) 679 680const void *v1, *v2; 681 682{ 683 register struct proc **pp1 = (struct proc **)v1; 684 register struct proc **pp2 = (struct proc **)v2; 685 register struct kinfo_proc *p1; 686 register struct kinfo_proc *p2; 687 register int result; 688 register pctcpu lresult; 689 690 /* remove one level of indirection */ 691 p1 = *(struct kinfo_proc **) pp1; 692 p2 = *(struct kinfo_proc **) pp2; 693 694 ORDERKEY_RSSIZE 695 ORDERKEY_MEM 696 ORDERKEY_PCTCPU 697 ORDERKEY_CPUTIME 698 ORDERKEY_STATE 699 ORDERKEY_PRIO 700 ; 701 702 return(result); 703} 704 705/* compare_time - the comparison function for sorting by CPU time */ 706 707int 708compare_time(v1, v2) 709 710const void *v1, *v2; 711 712{ 713 register struct proc **pp1 = (struct proc **)v1; 714 register struct proc **pp2 = (struct proc **)v2; 715 register struct kinfo_proc *p1; 716 register struct kinfo_proc *p2; 717 register int result; 718 register pctcpu lresult; 719 720 /* remove one level of indirection */ 721 p1 = *(struct kinfo_proc **) pp1; 722 p2 = *(struct kinfo_proc **) pp2; 723 724 ORDERKEY_CPUTIME 725 ORDERKEY_PCTCPU 726 ORDERKEY_STATE 727 ORDERKEY_PRIO 728 ORDERKEY_MEM 729 ORDERKEY_RSSIZE 730 ; 731 732 return(result); 733} 734 735/* compare_prio - the comparison function for sorting by CPU time */ 736 737int 738compare_prio(v1, v2) 739 740const void *v1, *v2; 741 742{ 743 register struct proc **pp1 = (struct proc **)v1; 744 register struct proc **pp2 = (struct proc **)v2; 745 register struct kinfo_proc *p1; 746 register struct kinfo_proc *p2; 747 register int result; 748 register pctcpu lresult; 749 750 /* remove one level of indirection */ 751 p1 = *(struct kinfo_proc **) pp1; 752 p2 = *(struct kinfo_proc **) pp2; 753 754 ORDERKEY_PRIO 755 ORDERKEY_PCTCPU 756 ORDERKEY_CPUTIME 757 ORDERKEY_STATE 758 ORDERKEY_RSSIZE 759 ORDERKEY_MEM 760 ; 761 762 return(result); 763} 764 765int (*proc_compares[])() = { 766 compare_cpu, 767 compare_size, 768 compare_res, 769 compare_time, 770 compare_prio, 771 NULL 772}; 773#else 774/* 775 * proc_compare - comparison function for "qsort" 776 * Compares the resource consumption of two processes using five 777 * distinct keys. The keys (in descending order of importance) are: 778 * percent cpu, cpu ticks, state, resident set size, total virtual 779 * memory usage. The process states are ordered as follows (from least 780 * to most important): zombie, sleep, stop, start, run. The array 781 * declaration below maps a process state index into a number that 782 * reflects this ordering. 783 */ 784 785int 786proc_compare(v1, v2) 787 788const void *v1, *v2; 789 790{ 791 register struct proc **pp1 = (struct proc **)v1; 792 register struct proc **pp2 = (struct proc **)v2; 793 register struct kinfo_proc *p1; 794 register struct kinfo_proc *p2; 795 register int result; 796 register pctcpu lresult; 797 798 /* remove one level of indirection */ 799 p1 = *(struct kinfo_proc **) pp1; 800 p2 = *(struct kinfo_proc **) pp2; 801 802 /* compare percent cpu (pctcpu) */ 803 if ((lresult = PP(p2, p_pctcpu) - PP(p1, p_pctcpu)) == 0) 804 { 805 /* use CPU usage to break the tie */ 806 if ((result = PP(p2, p_rtime).tv_sec - PP(p1, p_rtime).tv_sec) == 0) 807 { 808 /* use process state to break the tie */ 809 if ((result = sorted_state[(unsigned char) PP(p2, p_stat)] - 810 sorted_state[(unsigned char) PP(p1, p_stat)]) == 0) 811 { 812 /* use priority to break the tie */ 813 if ((result = PP(p2, p_priority) - PP(p1, p_priority)) == 0) 814 { 815 /* use resident set size (rssize) to break the tie */ 816 if ((result = VP(p2, vm_rssize) - VP(p1, vm_rssize)) == 0) 817 { 818 /* use total memory to break the tie */ 819 result = PROCSIZE(p2) - PROCSIZE(p1); 820 } 821 } 822 } 823 } 824 } 825 else 826 { 827 result = lresult < 0 ? -1 : 1; 828 } 829 830 return(result); 831} 832#endif 833 834/* 835 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if 836 * the process does not exist. 837 * It is EXTREMLY IMPORTANT that this function work correctly. 838 * If top runs setuid root (as in SVR4), then this function 839 * is the only thing that stands in the way of a serious 840 * security problem. It validates requests for the "kill" 841 * and "renice" commands. 842 */ 843 844int proc_owner(pid) 845 846pid_t pid; 847 848{ 849 register int cnt; 850 register struct kinfo_proc **prefp; 851 register struct kinfo_proc *pp; 852 853 prefp = pref; 854 cnt = pref_len; 855 while (--cnt >= 0) 856 { 857 pp = *prefp++; 858 if (PP(pp, p_pid) == pid) 859 { 860 return((int)EP(pp, e_pcred.p_ruid)); 861 } 862 } 863 return(-1); 864} 865 866#ifdef DOSWAP 867/* 868 * swapmode is based on a program called swapinfo written 869 * by Kevin Lahey <kml@rokkaku.atl.ga.us>. 870 */ 871 872#define SVAR(var) __STRING(var) /* to force expansion */ 873#define KGET(idx, var) \ 874 KGET1(idx, &var, sizeof(var), SVAR(var)) 875#define KGET1(idx, p, s, msg) \ 876 KGET2(nlst[idx].n_value, p, s, msg) 877#define KGET2(addr, p, s, msg) \ 878 if (kvm_read(kd, (u_long)(addr), p, s) != s) \ 879 warnx("cannot read %s: %s", msg, kvm_geterr(kd)) 880 881static int 882swapmode(used, total) 883int *used; 884int *total; 885{ 886 int nswap, nswdev, dmmax, nswapmap, niswap, niswdev; 887 int s, e, i, l, nfree; 888 struct swdevt *sw; 889 long *perdev; 890 struct map *swapmap, *kswapmap; 891 struct mapent *mp, *freemp; 892 893 KGET(VM_NSWAP, nswap); 894 KGET(VM_NSWDEV, nswdev); 895 KGET(VM_DMMAX, dmmax); 896 KGET(VM_NSWAPMAP, nswapmap); 897 KGET(VM_SWAPMAP, kswapmap); /* kernel `swapmap' is a pointer */ 898 if (nswap == 0) { 899 *used = 0; 900 *total = 0; 901 return (1); 902 } 903 if ((sw = malloc(nswdev * sizeof(*sw))) == NULL || 904 (perdev = malloc(nswdev * sizeof(*perdev))) == NULL || 905 (freemp = mp = malloc(nswapmap * sizeof(*mp))) == NULL) 906 err(1, "malloc"); 907 KGET1(VM_SWDEVT, sw, nswdev * sizeof(*sw), "swdevt"); 908 KGET2((long)kswapmap, mp, nswapmap * sizeof(*mp), "swapmap"); 909 910 /* Supports sequential swap */ 911 if (nlst[VM_NISWAP].n_value != 0) { 912 KGET(VM_NISWAP, niswap); 913 KGET(VM_NISWDEV, niswdev); 914 } else { 915 niswap = nswap; 916 niswdev = nswdev; 917 } 918 919 /* First entry in map is `struct map'; rest are mapent's. */ 920 swapmap = (struct map *)mp; 921 if (nswapmap != swapmap->m_limit - (struct mapent *)kswapmap) 922 errx(1, "panic: nswapmap goof"); 923 924 /* Count up swap space. */ 925 nfree = 0; 926 memset(perdev, 0, nswdev * sizeof(*perdev)); 927 for (mp++; mp->m_addr != 0; mp++) { 928 s = mp->m_addr; /* start of swap region */ 929 e = mp->m_addr + mp->m_size; /* end of region */ 930 nfree += mp->m_size; 931 932 /* 933 * Swap space is split up among the configured disks. 934 * 935 * For interleaved swap devices, the first dmmax blocks 936 * of swap space some from the first disk, the next dmmax 937 * blocks from the next, and so on up to niswap blocks. 938 * 939 * Sequential swap devices follow the interleaved devices 940 * (i.e. blocks starting at niswap) in the order in which 941 * they appear in the swdev table. The size of each device 942 * will be a multiple of dmmax. 943 * 944 * The list of free space joins adjacent free blocks, 945 * ignoring device boundries. If we want to keep track 946 * of this information per device, we'll just have to 947 * extract it ourselves. We know that dmmax-sized chunks 948 * cannot span device boundaries (interleaved or sequential) 949 * so we loop over such chunks assigning them to devices. 950 */ 951 i = -1; 952 while (s < e) { /* XXX this is inefficient */ 953 int bound = roundup(s+1, dmmax); 954 955 if (bound > e) 956 bound = e; 957 if (bound <= niswap) { 958 /* Interleaved swap chunk. */ 959 if (i == -1) 960 i = (s / dmmax) % niswdev; 961 perdev[i] += bound - s; 962 if (++i >= niswdev) 963 i = 0; 964 } else { 965 /* Sequential swap chunk. */ 966 if (i < niswdev) { 967 i = niswdev; 968 l = niswap + sw[i].sw_nblks; 969 } 970 while (s >= l) { 971 /* XXX don't die on bogus blocks */ 972 if (i == nswdev-1) 973 break; 974 l += sw[++i].sw_nblks; 975 } 976 perdev[i] += bound - s; 977 } 978 s = bound; 979 } 980 } 981 982 *total = 0; 983 for (i = 0; i < nswdev; i++) { 984 int xsize, xfree; 985 986 xsize = sw[i].sw_nblks; 987 xfree = perdev[i]; 988 *total += xsize; 989 } 990 991 /* 992 * If only one partition has been set up via swapon(8), we don't 993 * need to bother with totals. 994 */ 995#if DEV_BSHIFT < 10 996 *used = (*total - nfree) >> (10 - DEV_BSHIFT); 997 *total >>= 10 - DEV_BSHIFT; 998#elif DEV_BSHIFT > 10 999 *used = (*total - nfree) >> (DEV_BSHIFT - 10); 1000 *total >>= DEV_BSHIFT - 10; 1001#endif 1002 free (sw); free (freemp); free (perdev); 1003 return 1; 1004} 1005#endif 1006