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