machine.c revision 1.25
1/* $OpenBSD: machine.c,v 1.25 2001/07/12 05:17:26 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 * TERMCAP: -ltermlib 14 * 15 * CFLAGS: -DHAVE_GETOPT -DORDER 16 * 17 * AUTHOR: Thorsten Lockert <tholo@sigmasoft.com> 18 * Adapted from BSD4.4 by Christos Zoulas <christos@ee.cornell.edu> 19 * Patch for process wait display by Jarl F. Greipsland <jarle@idt.unit.no> 20 * Patch for -DORDER by Kenneth Stailey <kstailey@disclosure.com> 21 * Patch for new swapctl(2) by Tobias Weingartner <weingart@openbsd.org> 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 <math.h> 36#include <unistd.h> 37#include <sys/errno.h> 38#include <sys/sysctl.h> 39#include <sys/dir.h> 40#include <sys/dkstat.h> 41#include <sys/file.h> 42#include <sys/time.h> 43#include <sys/resource.h> 44 45#ifdef DOSWAP 46#include <sys/swap.h> 47#include <err.h> 48#endif 49 50static int swapmode __P((int *, int *)); 51 52#include "top.h" 53#include "display.h" 54#include "machine.h" 55#include "utils.h" 56 57/* get_process_info passes back a handle. This is what it looks like: */ 58 59struct handle { 60 struct kinfo_proc **next_proc; /* points to next valid proc pointer */ 61 int remaining; /* number of pointers remaining */ 62}; 63 64/* declarations for load_avg */ 65#include "loadavg.h" 66 67#define PP(pp, field) ((pp)->kp_proc . field) 68#define EP(pp, field) ((pp)->kp_eproc . field) 69#define VP(pp, field) ((pp)->kp_eproc.e_vm . field) 70 71/* what we consider to be process size: */ 72#define PROCSIZE(pp) (VP((pp), vm_tsize) + VP((pp), vm_dsize) + VP((pp), vm_ssize)) 73 74/* 75 * These definitions control the format of the per-process area 76 */ 77static char header[] = 78" PID X PRI NICE SIZE RES STATE WAIT TIME CPU COMMAND"; 79/* 0123456 -- field to fill in starts at header+6 */ 80#define UNAME_START 6 81 82#define Proc_format \ 83 "%5d %-8.8s %3d %4d %5s %5s %-5s %-6.6s %6s %5.2f%% %.14s" 84 85 86/* process state names for the "STATE" column of the display */ 87/* the extra nulls in the string "run" are for adding a slash and 88 the processor number when needed */ 89 90char *state_abbrev[] = { 91 "", "start", "run\0\0\0", "sleep", "stop", "zomb", 92}; 93 94 95static int stathz; 96 97/* these are for calculating cpu state percentages */ 98static long cp_time[CPUSTATES]; 99static long cp_old[CPUSTATES]; 100static long cp_diff[CPUSTATES]; 101 102/* these are for detailing the process states */ 103int process_states[7]; 104char *procstatenames[] = { 105 "", " starting, ", " running, ", " idle, ", " stopped, ", " zombie, ", 106 NULL 107}; 108 109/* these are for detailing the cpu states */ 110int cpu_states[CPUSTATES]; 111char *cpustatenames[] = { 112 "user", "nice", "system", "interrupt", "idle", NULL 113}; 114 115/* these are for detailing the memory statistics */ 116int memory_stats[8]; 117char *memorynames[] = { 118 "Real: ", "K/", "K act/tot ", "Free: ", "K ", 119#ifdef DOSWAP 120 "Swap: ", "K/", "K used/tot", 121#endif 122 NULL 123}; 124 125#ifdef ORDER 126/* these are names given to allowed sorting orders -- first is default */ 127char *ordernames[] = {"cpu", "size", "res", "time", "pri", NULL}; 128#endif 129 130/* these are for keeping track of the proc array */ 131static int nproc; 132static int onproc = -1; 133static int pref_len; 134static struct kinfo_proc *pbase; 135static struct kinfo_proc **pref; 136 137/* these are for getting the memory statistics */ 138static int pageshift; /* log base 2 of the pagesize */ 139 140/* define pagetok in terms of pageshift */ 141#define pagetok(size) ((size) << pageshift) 142 143int 144getstathz() 145{ 146 struct clockinfo cinf; 147 size_t size = sizeof(cinf); 148 int mib[2]; 149 150 mib[0] = CTL_KERN; 151 mib[1] = KERN_CLOCKRATE; 152 if (sysctl(mib, 2, &cinf, &size, NULL, 0) == -1) 153 return (-1); 154 return (cinf.stathz); 155} 156 157int 158machine_init(statics) 159 struct statics *statics; 160{ 161 int pagesize; 162 163 stathz = getstathz(); 164 if (stathz == -1) 165 return (-1); 166 167 pbase = NULL; 168 pref = NULL; 169 onproc = -1; 170 nproc = 0; 171 172 /* get the page size with "getpagesize" and calculate pageshift from 173 * it */ 174 pagesize = getpagesize(); 175 pageshift = 0; 176 while (pagesize > 1) { 177 pageshift++; 178 pagesize >>= 1; 179 } 180 181 /* we only need the amount of log(2)1024 for our conversion */ 182 pageshift -= LOG1024; 183 184 /* fill in the statics information */ 185 statics->procstate_names = procstatenames; 186 statics->cpustate_names = cpustatenames; 187 statics->memory_names = memorynames; 188#ifdef ORDER 189 statics->order_names = ordernames; 190#endif 191 return (0); 192} 193 194char * 195format_header(uname_field) 196 char *uname_field; 197{ 198 char *ptr; 199 200 ptr = header + UNAME_START; 201 while (*uname_field != '\0') { 202 *ptr++ = *uname_field++; 203 } 204 return (header); 205} 206 207void 208get_system_info(si) 209 struct system_info *si; 210{ 211 static int sysload_mib[] = {CTL_VM, VM_LOADAVG}; 212 static int vmtotal_mib[] = {CTL_VM, VM_METER}; 213 static int cp_time_mib[] = { CTL_KERN, KERN_CPTIME }; 214 struct loadavg sysload; 215 struct vmtotal vmtotal; 216 double *infoloadp; 217 int total, i; 218 size_t size; 219 220 size = sizeof(cp_time); 221 if (sysctl(cp_time_mib, 2, &cp_time, &size, NULL, 0) < 0) { 222 warn("sysctl kern.cp_time failed"); 223 total = 0; 224 } 225 226 size = sizeof(sysload); 227 if (sysctl(sysload_mib, 2, &sysload, &size, NULL, 0) < 0) { 228 warn("sysctl failed"); 229 total = 0; 230 } 231 infoloadp = si->load_avg; 232 for (i = 0; i < 3; i++) 233 *infoloadp++ = ((double) sysload.ldavg[i]) / sysload.fscale; 234 235 /* convert cp_time counts to percentages */ 236 total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); 237 238 /* get total -- systemwide main memory usage structure */ 239 size = sizeof(vmtotal); 240 if (sysctl(vmtotal_mib, 2, &vmtotal, &size, NULL, 0) < 0) { 241 warn("sysctl failed"); 242 bzero(&vmtotal, sizeof(vmtotal)); 243 } 244 /* convert memory stats to Kbytes */ 245 memory_stats[0] = -1; 246 memory_stats[1] = pagetok(vmtotal.t_arm); 247 memory_stats[2] = pagetok(vmtotal.t_rm); 248 memory_stats[3] = -1; 249 memory_stats[4] = pagetok(vmtotal.t_free); 250 memory_stats[5] = -1; 251#ifdef DOSWAP 252 if (!swapmode(&memory_stats[6], &memory_stats[7])) { 253 memory_stats[6] = 0; 254 memory_stats[7] = 0; 255 } 256#endif 257 258 /* set arrays and strings */ 259 si->cpustates = cpu_states; 260 si->memory = memory_stats; 261 si->last_pid = -1; 262} 263 264static struct handle handle; 265 266struct kinfo_proc * 267getprocs(op, arg, cnt) 268 int op, arg; 269 int *cnt; 270{ 271 size_t size = sizeof(int); 272 int mib[4] = {CTL_KERN, KERN_PROC, op, arg}; 273 int smib[2] = {CTL_KERN, KERN_NPROCS}; 274 static struct kinfo_proc *procbase; 275 int st; 276 277 st = sysctl(smib, 2, cnt, &size, NULL, 0); 278 if (st == -1) { 279 /* _kvm_syserr(kd, kd->program, "kvm_getprocs"); */ 280 return (0); 281 } 282 if (procbase) 283 free(procbase); 284 size = (6 * (*cnt) * sizeof(struct kinfo_proc)) / 5; 285 procbase = (struct kinfo_proc *)malloc(size); 286 if (procbase == NULL) 287 return (0); 288 st = sysctl(mib, 4, procbase, &size, NULL, 0); 289 if (st == -1) { 290 /* _kvm_syserr(kd, kd->program, "kvm_getprocs"); */ 291 return (0); 292 } 293 if (size % sizeof(struct kinfo_proc) != 0) { 294 /* _kvm_err(kd, kd->program, 295 "proc size mismatch (%d total, %d chunks)", 296 size, sizeof(struct kinfo_proc)); */ 297 return (0); 298 } 299 return (procbase); 300} 301 302caddr_t 303get_process_info(si, sel, compare) 304 struct system_info *si; 305 struct process_select *sel; 306 int (*compare) __P((const void *, const void *)); 307 308{ 309 int show_idle, show_system, show_uid, show_command; 310 int total_procs, active_procs, i; 311 struct kinfo_proc **prefp, *pp; 312 313 if ((pbase = getprocs(KERN_PROC_KTHREAD, 0, &nproc)) == NULL) { 314 /* warnx("%s", kvm_geterr(kd)); */ 315 quit(23); 316 } 317 if (nproc > onproc) 318 pref = (struct kinfo_proc **) realloc(pref, sizeof(struct kinfo_proc *) 319 * (onproc = nproc)); 320 if (pref == NULL) { 321 warnx("Out of memory."); 322 quit(23); 323 } 324 /* get a pointer to the states summary array */ 325 si->procstates = process_states; 326 327 /* set up flags which define what we are going to select */ 328 show_idle = sel->idle; 329 show_system = sel->system; 330 show_uid = sel->uid != -1; 331 show_command = sel->command != NULL; 332 333 /* count up process states and get pointers to interesting procs */ 334 total_procs = 0; 335 active_procs = 0; 336 memset((char *) process_states, 0, sizeof(process_states)); 337 prefp = pref; 338 for (pp = pbase, i = 0; i < nproc; pp++, i++) { 339 /* 340 * Place pointers to each valid proc structure in pref[]. 341 * Process slots that are actually in use have a non-zero 342 * status field. Processes with SSYS set are system 343 * processes---these get ignored unless show_sysprocs is set. 344 */ 345 if (PP(pp, p_stat) != 0 && 346 (show_system || ((PP(pp, p_flag) & P_SYSTEM) == 0))) { 347 total_procs++; 348 process_states[(unsigned char) PP(pp, p_stat)]++; 349 if ((PP(pp, p_stat) != SZOMB) && 350 (show_idle || (PP(pp, p_pctcpu) != 0) || 351 (PP(pp, p_stat) == SRUN)) && 352 (!show_uid || EP(pp, e_pcred.p_ruid) == (uid_t) sel->uid)) { 353 *prefp++ = pp; 354 active_procs++; 355 } 356 } 357 } 358 359 /* if requested, sort the "interesting" processes */ 360 if (compare != NULL) { 361 qsort((char *) pref, active_procs, sizeof(struct kinfo_proc *), compare); 362 } 363 /* remember active and total counts */ 364 si->p_total = total_procs; 365 si->p_active = pref_len = active_procs; 366 367 /* pass back a handle */ 368 handle.next_proc = pref; 369 handle.remaining = active_procs; 370 return ((caddr_t) & handle); 371} 372 373char fmt[MAX_COLS]; /* static area where result is built */ 374 375char * 376format_next_process(handle, get_userid) 377 caddr_t handle; 378 char *(*get_userid)(); 379 380{ 381 char waddr[sizeof(void *) * 2 + 3]; /* Hexify void pointer */ 382 struct kinfo_proc *pp; 383 struct handle *hp; 384 char *p_wait; 385 int cputime; 386 double pct; 387 388 /* find and remember the next proc structure */ 389 hp = (struct handle *) handle; 390 pp = *(hp->next_proc++); 391 hp->remaining--; 392 393 /* get the process's user struct and set cputime */ 394 if ((PP(pp, p_flag) & P_INMEM) == 0) { 395 /* 396 * Print swapped processes as <pname> 397 */ 398 char *comm = PP(pp, p_comm); 399#define COMSIZ sizeof(PP(pp, p_comm)) 400 char buf[COMSIZ]; 401 (void) strncpy(buf, comm, COMSIZ); 402 comm[0] = '<'; 403 (void) strncpy(&comm[1], buf, COMSIZ - 2); 404 comm[COMSIZ - 2] = '\0'; 405 (void) strncat(comm, ">", COMSIZ - 1); 406 comm[COMSIZ - 1] = '\0'; 407 } 408 cputime = (PP(pp, p_uticks) + PP(pp, p_sticks) + PP(pp, p_iticks)) / stathz; 409 410 /* calculate the base for cpu percentages */ 411 pct = pctdouble(PP(pp, p_pctcpu)); 412 413 if (PP(pp, p_wchan)) 414 if (PP(pp, p_wmesg)) 415 p_wait = EP(pp, e_wmesg); 416 else { 417 snprintf(waddr, sizeof(waddr), "%lx", 418 (unsigned long) (PP(pp, p_wchan)) & ~KERNBASE); 419 p_wait = waddr; 420 } 421 else 422 p_wait = "-"; 423 424 /* format this entry */ 425 snprintf(fmt, MAX_COLS, 426 Proc_format, 427 PP(pp, p_pid), 428 (*get_userid) (EP(pp, e_pcred.p_ruid)), 429 PP(pp, p_priority) - PZERO, 430 PP(pp, p_nice) - NZERO, 431 format_k(pagetok(PROCSIZE(pp))), 432 format_k(pagetok(VP(pp, vm_rssize))), 433 (PP(pp, p_stat) == SSLEEP && PP(pp, p_slptime) > MAXSLP) 434 ? "idle" : state_abbrev[(unsigned char) PP(pp, p_stat)], 435 p_wait, 436 format_time(cputime), 437 100.0 * pct, 438 printable(PP(pp, p_comm))); 439 440 /* return the result */ 441 return (fmt); 442} 443 444/* comparison routine for qsort */ 445static unsigned char sorted_state[] = 446{ 447 0, /* not used */ 448 4, /* start */ 449 5, /* run */ 450 2, /* sleep */ 451 3, /* stop */ 452 1 /* zombie */ 453}; 454#ifdef ORDER 455 456/* 457 * proc_compares - comparison functions for "qsort" 458 */ 459 460/* 461 * First, the possible comparison keys. These are defined in such a way 462 * that they can be merely listed in the source code to define the actual 463 * desired ordering. 464 */ 465 466 467#define ORDERKEY_PCTCPU \ 468 if (lresult = (pctcpu)PP(p2, p_pctcpu) - (pctcpu)PP(p1, p_pctcpu), \ 469 (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0) 470#define ORDERKEY_CPUTIME \ 471 if ((result = PP(p2, p_rtime.tv_sec) - PP(p1, p_rtime.tv_sec)) == 0) \ 472 if ((result = PP(p2, p_rtime.tv_usec) - \ 473 PP(p1, p_rtime.tv_usec)) == 0) 474#define ORDERKEY_STATE \ 475 if ((result = sorted_state[(unsigned char) PP(p2, p_stat)] - \ 476 sorted_state[(unsigned char) PP(p1, p_stat)]) == 0) 477#define ORDERKEY_PRIO \ 478 if ((result = PP(p2, p_priority) - PP(p1, p_priority)) == 0) 479#define ORDERKEY_RSSIZE \ 480 if ((result = VP(p2, vm_rssize) - VP(p1, vm_rssize)) == 0) 481#define ORDERKEY_MEM \ 482 if ((result = PROCSIZE(p2) - PROCSIZE(p1)) == 0) 483 484 485/* compare_cpu - the comparison function for sorting by cpu percentage */ 486int 487compare_cpu(v1, v2) 488 const void *v1, *v2; 489{ 490 struct proc **pp1 = (struct proc **) v1; 491 struct proc **pp2 = (struct proc **) v2; 492 struct kinfo_proc *p1; 493 struct kinfo_proc *p2; 494 int result; 495 pctcpu lresult; 496 497 /* remove one level of indirection */ 498 p1 = *(struct kinfo_proc **) pp1; 499 p2 = *(struct kinfo_proc **) pp2; 500 501 ORDERKEY_PCTCPU 502 ORDERKEY_CPUTIME 503 ORDERKEY_STATE 504 ORDERKEY_PRIO 505 ORDERKEY_RSSIZE 506 ORDERKEY_MEM 507 ; 508 return (result); 509} 510 511/* compare_size - the comparison function for sorting by total memory usage */ 512int 513compare_size(v1, v2) 514 const void *v1, *v2; 515{ 516 struct proc **pp1 = (struct proc **) v1; 517 struct proc **pp2 = (struct proc **) v2; 518 struct kinfo_proc *p1; 519 struct kinfo_proc *p2; 520 int result; 521 pctcpu lresult; 522 523 /* remove one level of indirection */ 524 p1 = *(struct kinfo_proc **) pp1; 525 p2 = *(struct kinfo_proc **) pp2; 526 527 ORDERKEY_MEM 528 ORDERKEY_RSSIZE 529 ORDERKEY_PCTCPU 530 ORDERKEY_CPUTIME 531 ORDERKEY_STATE 532 ORDERKEY_PRIO 533 ; 534 return (result); 535} 536 537/* compare_res - the comparison function for sorting by resident set size */ 538int 539compare_res(v1, v2) 540 const void *v1, *v2; 541{ 542 struct proc **pp1 = (struct proc **) v1; 543 struct proc **pp2 = (struct proc **) v2; 544 struct kinfo_proc *p1; 545 struct kinfo_proc *p2; 546 int result; 547 pctcpu lresult; 548 549 /* remove one level of indirection */ 550 p1 = *(struct kinfo_proc **) pp1; 551 p2 = *(struct kinfo_proc **) pp2; 552 553 ORDERKEY_RSSIZE 554 ORDERKEY_MEM 555 ORDERKEY_PCTCPU 556 ORDERKEY_CPUTIME 557 ORDERKEY_STATE 558 ORDERKEY_PRIO 559 ; 560 return (result); 561} 562 563/* compare_time - the comparison function for sorting by CPU time */ 564int 565compare_time(v1, v2) 566 const void *v1, *v2; 567{ 568 struct proc **pp1 = (struct proc **) v1; 569 struct proc **pp2 = (struct proc **) v2; 570 struct kinfo_proc *p1; 571 struct kinfo_proc *p2; 572 int result; 573 pctcpu lresult; 574 575 /* remove one level of indirection */ 576 p1 = *(struct kinfo_proc **) pp1; 577 p2 = *(struct kinfo_proc **) pp2; 578 579 ORDERKEY_CPUTIME 580 ORDERKEY_PCTCPU 581 ORDERKEY_STATE 582 ORDERKEY_PRIO 583 ORDERKEY_MEM 584 ORDERKEY_RSSIZE 585 ; 586 return (result); 587} 588 589/* compare_prio - the comparison function for sorting by CPU time */ 590int 591compare_prio(v1, v2) 592 const void *v1, *v2; 593{ 594 struct proc **pp1 = (struct proc **) v1; 595 struct proc **pp2 = (struct proc **) v2; 596 struct kinfo_proc *p1; 597 struct kinfo_proc *p2; 598 int result; 599 pctcpu lresult; 600 601 /* remove one level of indirection */ 602 p1 = *(struct kinfo_proc **) pp1; 603 p2 = *(struct kinfo_proc **) pp2; 604 605 ORDERKEY_PRIO 606 ORDERKEY_PCTCPU 607 ORDERKEY_CPUTIME 608 ORDERKEY_STATE 609 ORDERKEY_RSSIZE 610 ORDERKEY_MEM 611 ; 612 return (result); 613} 614 615int (*proc_compares[]) () = { 616 compare_cpu, 617 compare_size, 618 compare_res, 619 compare_time, 620 compare_prio, 621 NULL 622}; 623#else 624/* 625 * proc_compare - comparison function for "qsort" 626 * Compares the resource consumption of two processes using five 627 * distinct keys. The keys (in descending order of importance) are: 628 * percent cpu, cpu ticks, state, resident set size, total virtual 629 * memory usage. The process states are ordered as follows (from least 630 * to most important): zombie, sleep, stop, start, run. The array 631 * declaration below maps a process state index into a number that 632 * reflects this ordering. 633 */ 634int 635proc_compare(v1, v2) 636 const void *v1, *v2; 637{ 638 struct proc **pp1 = (struct proc **) v1; 639 struct proc **pp2 = (struct proc **) v2; 640 struct kinfo_proc *p1; 641 struct kinfo_proc *p2; 642 int result; 643 pctcpu lresult; 644 645 /* remove one level of indirection */ 646 p1 = *(struct kinfo_proc **) pp1; 647 p2 = *(struct kinfo_proc **) pp2; 648 649 /* compare percent cpu (pctcpu) */ 650 if ((lresult = PP(p2, p_pctcpu) - PP(p1, p_pctcpu)) == 0) { 651 /* use CPU usage to break the tie */ 652 if ((result = PP(p2, p_rtime).tv_sec - PP(p1, p_rtime).tv_sec) == 0) { 653 /* use process state to break the tie */ 654 if ((result = sorted_state[(unsigned char) PP(p2, p_stat)] - 655 sorted_state[(unsigned char) PP(p1, p_stat)]) == 0) { 656 /* use priority to break the tie */ 657 if ((result = PP(p2, p_priority) - 658 PP(p1, p_priority)) == 0) { 659 /* use resident set size (rssize) to 660 * break the tie */ 661 if ((result = VP(p2, vm_rssize) - 662 VP(p1, vm_rssize)) == 0) { 663 /* use total memory to break 664 * the tie */ 665 result = PROCSIZE(p2) - PROCSIZE(p1); 666 } 667 } 668 } 669 } 670 } else { 671 result = lresult < 0 ? -1 : 1; 672 } 673 return (result); 674} 675#endif 676 677/* 678 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if 679 * the process does not exist. 680 * It is EXTREMLY IMPORTANT that this function work correctly. 681 * If top runs setuid root (as in SVR4), then this function 682 * is the only thing that stands in the way of a serious 683 * security problem. It validates requests for the "kill" 684 * and "renice" commands. 685 */ 686int 687proc_owner(pid) 688 pid_t pid; 689{ 690 struct kinfo_proc **prefp, *pp; 691 int cnt; 692 693 prefp = pref; 694 cnt = pref_len; 695 while (--cnt >= 0) { 696 pp = *prefp++; 697 if (PP(pp, p_pid) == pid) { 698 return ((int) EP(pp, e_pcred.p_ruid)); 699 } 700 } 701 return (-1); 702} 703#ifdef DOSWAP 704/* 705 * swapmode is rewritten by Tobias Weingartner <weingart@openbsd.org> 706 * to be based on the new swapctl(2) system call. 707 */ 708static int 709swapmode(used, total) 710 int *used; 711 int *total; 712{ 713 int nswap, rnswap, i; 714 struct swapent *swdev; 715 716 nswap = swapctl(SWAP_NSWAP, 0, 0); 717 if (nswap == 0) 718 return 0; 719 720 swdev = malloc(nswap * sizeof(*swdev)); 721 if (swdev == NULL) 722 return 0; 723 724 rnswap = swapctl(SWAP_STATS, swdev, nswap); 725 if (rnswap == -1) 726 return 0; 727 728 /* if rnswap != nswap, then what? */ 729 730 /* Total things up */ 731 *total = *used = 0; 732 for (i = 0; i < nswap; i++) { 733 if (swdev[i].se_flags & SWF_ENABLE) { 734 *used += (swdev[i].se_inuse / (1024 / DEV_BSIZE)); 735 *total += (swdev[i].se_nblks / (1024 / DEV_BSIZE)); 736 } 737 } 738 739 free(swdev); 740 return 1; 741} 742#endif 743