1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 /* 26 * This file contains miscellaneous routines. 27 */ 28 #include "global.h" 29 30 #include <stdlib.h> 31 #include <signal.h> 32 #include <malloc.h> 33 #include <unistd.h> 34 #include <string.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <sys/ioctl.h> 38 #include <sys/fcntl.h> 39 #include <sys/time.h> 40 #include <ctype.h> 41 #include <termio.h> 42 #include "misc.h" 43 #include "analyze.h" 44 #include "label.h" 45 #include "startup.h" 46 47 static void cleanup(int sig); 48 49 struct env *current_env = NULL; /* ptr to current environment */ 50 static int stop_pending = 0; /* ctrl-Z is pending */ 51 struct ttystate ttystate; /* tty info */ 52 static int aborting = 0; /* in process of aborting */ 53 54 /* 55 * For 4.x, limit the choices of valid disk names to this set. 56 */ 57 static char *disk_4x_identifiers[] = { "sd", "id"}; 58 #define N_DISK_4X_IDS (sizeof (disk_4x_identifiers)/sizeof (char *)) 59 60 61 /* 62 * This is the list of legal inputs for all yes/no questions. 63 */ 64 char *confirm_list[] = { 65 "yes", 66 "no", 67 NULL, 68 }; 69 70 /* 71 * This routine is a wrapper for malloc. It allocates pre-zeroed space, 72 * and checks the return value so the caller doesn't have to. 73 */ 74 void * 75 zalloc(int count) 76 { 77 void *ptr; 78 79 if ((ptr = calloc(1, (unsigned)count)) == NULL) { 80 err_print("Error: unable to calloc more space.\n"); 81 fullabort(); 82 } 83 return (ptr); 84 } 85 86 /* 87 * This routine is a wrapper for realloc. It reallocates the given 88 * space, and checks the return value so the caller doesn't have to. 89 * Note that the any space added by this call is NOT necessarily 90 * zeroed. 91 */ 92 void * 93 rezalloc(void *ptr, int count) 94 { 95 void *new_ptr; 96 97 98 if ((new_ptr = realloc((char *)ptr, (unsigned)count)) == NULL) { 99 err_print("Error: unable to realloc more space.\n"); 100 fullabort(); 101 } 102 return (new_ptr); 103 } 104 105 /* 106 * This routine is a wrapper for free. 107 */ 108 void 109 destroy_data(char *data) 110 { 111 free(data); 112 } 113 114 #ifdef not 115 /* 116 * This routine takes the space number returned by an ioctl call and 117 * returns a mnemonic name for that space. 118 */ 119 char * 120 space2str(uint_t space) 121 { 122 char *name; 123 124 switch (space&SP_BUSMASK) { 125 case SP_VIRTUAL: 126 name = "virtual"; 127 break; 128 case SP_OBMEM: 129 name = "obmem"; 130 break; 131 case SP_OBIO: 132 name = "obio"; 133 break; 134 case SP_MBMEM: 135 name = "mbmem"; 136 break; 137 case SP_MBIO: 138 name = "mbio"; 139 break; 140 default: 141 err_print("Error: unknown address space type encountered.\n"); 142 fullabort(); 143 } 144 return (name); 145 } 146 #endif /* not */ 147 148 /* 149 * This routine asks the user the given yes/no question and returns 150 * the response. 151 */ 152 int 153 check(char *question) 154 { 155 int answer; 156 u_ioparam_t ioparam; 157 158 /* 159 * If we are running out of a command file, assume a yes answer. 160 */ 161 if (option_f) 162 return (0); 163 /* 164 * Ask the user. 165 */ 166 ioparam.io_charlist = confirm_list; 167 answer = input(FIO_MSTR, question, '?', &ioparam, NULL, DATA_INPUT); 168 return (answer); 169 } 170 171 /* 172 * This routine aborts the current command. It is called by a ctrl-C 173 * interrupt and also under certain error conditions. 174 */ 175 void 176 cmdabort(int sig __unused) 177 { 178 /* 179 * If there is no usable saved environment, gracefully exit. This 180 * allows the user to interrupt the program even when input is from 181 * a file, or if there is no current menu, like at the "Select disk:" 182 * prompt. 183 */ 184 if (current_env == NULL || !(current_env->flags & ENV_USE)) 185 fullabort(); 186 187 /* 188 * If we are in a critical zone, note the attempt and return. 189 */ 190 if (current_env->flags & ENV_CRITICAL) { 191 current_env->flags |= ENV_ABORT; 192 return; 193 } 194 /* 195 * All interruptions when we are running out of a command file 196 * cause the program to gracefully exit. 197 */ 198 if (option_f) 199 fullabort(); 200 fmt_print("\n"); 201 /* 202 * Clean up any state left by the interrupted command. 203 */ 204 cleanup(sig); 205 /* 206 * Jump to the saved environment. 207 */ 208 longjmp(current_env->env, 0); 209 } 210 211 /* 212 * This routine implements the ctrl-Z suspend mechanism. It is called 213 * when a suspend signal is received. 214 */ 215 void 216 onsusp(int sig __unused) 217 { 218 int fix_term; 219 #ifdef NOT_DEF 220 sigset_t sigmask; 221 #endif /* NOT_DEF */ 222 223 /* 224 * If we are in a critical zone, note the attempt and return. 225 */ 226 if (current_env != NULL && current_env->flags & ENV_CRITICAL) { 227 stop_pending = 1; 228 return; 229 } 230 /* 231 * If the terminal is mucked up, note that we will need to 232 * re-muck it when we start up again. 233 */ 234 fix_term = ttystate.ttyflags; 235 fmt_print("\n"); 236 /* 237 * Clean up any state left by the interrupted command. 238 */ 239 cleanup(sig); 240 #ifdef NOT_DEF 241 /* Investigate whether all this is necessary */ 242 /* 243 * Stop intercepting the suspend signal, then send ourselves one 244 * to cause us to stop. 245 */ 246 sigmask.sigbits[0] = (ulong_t)0xffffffff; 247 if (sigprocmask(SIG_SETMASK, &sigmask, NULL) == -1) 248 err_print("sigprocmask failed %d\n", errno); 249 #endif /* NOT_DEF */ 250 (void) signal(SIGTSTP, SIG_DFL); 251 (void) kill(0, SIGTSTP); 252 /* 253 * PC stops here 254 */ 255 /* 256 * We are started again. Set us up to intercept the suspend 257 * signal once again. 258 */ 259 (void) signal(SIGTSTP, onsusp); 260 /* 261 * Re-muck the terminal if necessary. 262 */ 263 if (fix_term & TTY_ECHO_OFF) 264 echo_off(); 265 if (fix_term & TTY_CBREAK_ON) 266 charmode_on(); 267 } 268 269 /* 270 * This routine implements the timing function used during long-term 271 * disk operations (e.g. formatting). It is called when an alarm signal 272 * is received. 273 */ 274 void 275 onalarm(int sig __unused) 276 { 277 } 278 279 280 /* 281 * This routine gracefully exits the program. 282 */ 283 void 284 fullabort(void) 285 { 286 287 fmt_print("\n"); 288 /* 289 * Clean up any state left by an interrupted command. 290 * Avoid infinite loops caused by a clean-up 291 * routine failing again... 292 */ 293 if (!aborting) { 294 aborting = 1; 295 cleanup(SIGKILL); 296 } 297 exit(1); 298 /*NOTREACHED*/ 299 } 300 301 /* 302 * This routine cleans up the state of the world. It is a hodge-podge 303 * of kludges to allow us to interrupt commands whenever possible. 304 * 305 * Some cleanup actions may depend on the type of signal. 306 */ 307 static void 308 cleanup(int sig) 309 { 310 311 /* 312 * Lock out interrupts to avoid recursion. 313 */ 314 enter_critical(); 315 /* 316 * Fix up the tty if necessary. 317 */ 318 if (ttystate.ttyflags & TTY_CBREAK_ON) { 319 charmode_off(); 320 } 321 if (ttystate.ttyflags & TTY_ECHO_OFF) { 322 echo_on(); 323 } 324 325 /* 326 * If the defect list is dirty, write it out. 327 */ 328 if (cur_list.flags & LIST_DIRTY) { 329 cur_list.flags = 0; 330 if (!EMBEDDED_SCSI) 331 write_deflist(&cur_list); 332 } 333 /* 334 * If the label is dirty, write it out. 335 */ 336 if (cur_flags & LABEL_DIRTY) { 337 cur_flags &= ~LABEL_DIRTY; 338 (void) write_label(); 339 } 340 /* 341 * If we are logging and just interrupted a scan, print out 342 * some summary info to the log file. 343 */ 344 if (log_file && scan_cur_block >= 0) { 345 pr_dblock(log_print, scan_cur_block); 346 log_print("\n"); 347 } 348 if (scan_blocks_fixed >= 0) 349 fmt_print("Total of %lld defective blocks repaired.\n", 350 scan_blocks_fixed); 351 if (sig != SIGSTOP) { /* Don't reset on suspend (converted to stop) */ 352 scan_cur_block = scan_blocks_fixed = -1; 353 } 354 exit_critical(); 355 } 356 357 /* 358 * This routine causes the program to enter a critical zone. Within the 359 * critical zone, no interrupts are allowed. Note that calls to this 360 * routine for the same environment do NOT nest, so there is not 361 * necessarily pairing between calls to enter_critical() and exit_critical(). 362 */ 363 void 364 enter_critical(void) 365 { 366 367 /* 368 * If there is no saved environment, interrupts will be ignored. 369 */ 370 if (current_env == NULL) 371 return; 372 /* 373 * Mark the environment to be in a critical zone. 374 */ 375 current_env->flags |= ENV_CRITICAL; 376 } 377 378 /* 379 * This routine causes the program to exit a critical zone. Note that 380 * calls to enter_critical() for the same environment do NOT nest, so 381 * one call to exit_critical() will erase any number of such calls. 382 */ 383 void 384 exit_critical(void) 385 { 386 387 /* 388 * If there is a saved environment, mark it to be non-critical. 389 */ 390 if (current_env != NULL) 391 current_env->flags &= ~ENV_CRITICAL; 392 /* 393 * If there is a stop pending, execute the stop. 394 */ 395 if (stop_pending) { 396 stop_pending = 0; 397 onsusp(SIGSTOP); 398 } 399 /* 400 * If there is an abort pending, execute the abort. 401 */ 402 if (current_env == NULL) 403 return; 404 if (current_env->flags & ENV_ABORT) { 405 current_env->flags &= ~ENV_ABORT; 406 cmdabort(SIGINT); 407 } 408 } 409 410 /* 411 * This routine turns off echoing on the controlling tty for the program. 412 */ 413 void 414 echo_off(void) 415 { 416 /* 417 * Open the tty and store the file pointer for later. 418 */ 419 if (ttystate.ttyflags == 0) { 420 if ((ttystate.ttyfile = open("/dev/tty", 421 O_RDWR | O_NDELAY)) < 0) { 422 err_print("Unable to open /dev/tty.\n"); 423 fullabort(); 424 } 425 } 426 /* 427 * Get the parameters for the tty, turn off echoing and set them. 428 */ 429 if (tcgetattr(ttystate.ttyfile, &ttystate.ttystate) < 0) { 430 err_print("Unable to get tty parameters.\n"); 431 fullabort(); 432 } 433 ttystate.ttystate.c_lflag &= ~ECHO; 434 if (tcsetattr(ttystate.ttyfile, TCSANOW, &ttystate.ttystate) < 0) { 435 err_print("Unable to set tty to echo off state.\n"); 436 fullabort(); 437 } 438 439 /* 440 * Remember that we've successfully turned 441 * ECHO mode off, so we know to fix it later. 442 */ 443 ttystate.ttyflags |= TTY_ECHO_OFF; 444 } 445 446 /* 447 * This routine turns on echoing on the controlling tty for the program. 448 */ 449 void 450 echo_on(void) 451 { 452 453 /* 454 * Using the saved parameters, turn echoing on and set them. 455 */ 456 ttystate.ttystate.c_lflag |= ECHO; 457 if (tcsetattr(ttystate.ttyfile, TCSANOW, &ttystate.ttystate) < 0) { 458 err_print("Unable to set tty to echo on state.\n"); 459 fullabort(); 460 } 461 /* 462 * Close the tty and mark it ok again. 463 */ 464 ttystate.ttyflags &= ~TTY_ECHO_OFF; 465 if (ttystate.ttyflags == 0) { 466 (void) close(ttystate.ttyfile); 467 } 468 } 469 470 /* 471 * This routine turns off single character entry mode for tty. 472 */ 473 void 474 charmode_on(void) 475 { 476 477 /* 478 * If tty unopened, open the tty and store the file pointer for later. 479 */ 480 if (ttystate.ttyflags == 0) { 481 if ((ttystate.ttyfile = open("/dev/tty", 482 O_RDWR | O_NDELAY)) < 0) { 483 err_print("Unable to open /dev/tty.\n"); 484 fullabort(); 485 } 486 } 487 /* 488 * Get the parameters for the tty, turn on char mode. 489 */ 490 if (tcgetattr(ttystate.ttyfile, &ttystate.ttystate) < 0) { 491 err_print("Unable to get tty parameters.\n"); 492 fullabort(); 493 } 494 ttystate.vmin = ttystate.ttystate.c_cc[VMIN]; 495 ttystate.vtime = ttystate.ttystate.c_cc[VTIME]; 496 497 ttystate.ttystate.c_lflag &= ~ICANON; 498 ttystate.ttystate.c_cc[VMIN] = 1; 499 ttystate.ttystate.c_cc[VTIME] = 0; 500 501 if (tcsetattr(ttystate.ttyfile, TCSANOW, &ttystate.ttystate) < 0) { 502 err_print("Unable to set tty to cbreak on state.\n"); 503 fullabort(); 504 } 505 506 /* 507 * Remember that we've successfully turned 508 * CBREAK mode on, so we know to fix it later. 509 */ 510 ttystate.ttyflags |= TTY_CBREAK_ON; 511 } 512 513 /* 514 * This routine turns on single character entry mode for tty. 515 * Note, this routine must be called before echo_on. 516 */ 517 void 518 charmode_off(void) 519 { 520 521 /* 522 * Using the saved parameters, turn char mode on. 523 */ 524 ttystate.ttystate.c_lflag |= ICANON; 525 ttystate.ttystate.c_cc[VMIN] = ttystate.vmin; 526 ttystate.ttystate.c_cc[VTIME] = ttystate.vtime; 527 if (tcsetattr(ttystate.ttyfile, TCSANOW, &ttystate.ttystate) < 0) { 528 err_print("Unable to set tty to cbreak off state.\n"); 529 fullabort(); 530 } 531 /* 532 * Close the tty and mark it ok again. 533 */ 534 ttystate.ttyflags &= ~TTY_CBREAK_ON; 535 if (ttystate.ttyflags == 0) { 536 (void) close(ttystate.ttyfile); 537 } 538 } 539 540 541 /* 542 * Allocate space for and return a pointer to a string 543 * on the stack. If the string is null, create 544 * an empty string. 545 * Use destroy_data() to free when no longer used. 546 */ 547 char * 548 alloc_string(char *s) 549 { 550 char *ns; 551 552 if (s == NULL) { 553 ns = zalloc(1); 554 } else { 555 ns = zalloc(strlen(s) + 1); 556 (void) strcpy(ns, s); 557 } 558 return (ns); 559 } 560 561 562 563 /* 564 * This function can be used to build up an array of strings 565 * dynamically, with a trailing NULL to terminate the list. 566 * 567 * Parameters: 568 * argvlist: a pointer to the base of the current list. 569 * does not have to be initialized. 570 * size: pointer to an integer, indicating the number 571 * of string installed in the list. Must be 572 * initialized to zero. 573 * alloc: pointer to an integer, indicating the amount 574 * of space allocated. Must be initialized to 575 * zero. For efficiency, we allocate the list 576 * in chunks and use it piece-by-piece. 577 * str: the string to be inserted in the list. 578 * A copy of the string is malloc'ed, and 579 * appended at the end of the list. 580 * Returns: 581 * a pointer to the possibly-moved argvlist. 582 * 583 * No attempt to made to free unused memory when the list is 584 * completed, although this would not be hard to do. For 585 * reasonably small lists, this should suffice. 586 */ 587 #define INITIAL_LISTSIZE 32 588 #define INCR_LISTSIZE 32 589 590 char ** 591 build_argvlist(char **argvlist, int *size, int *alloc, char *str) 592 { 593 if (*size + 2 > *alloc) { 594 if (*alloc == 0) { 595 *alloc = INITIAL_LISTSIZE; 596 argvlist = zalloc(sizeof (char *) * (*alloc)); 597 } else { 598 *alloc += INCR_LISTSIZE; 599 argvlist = rezalloc((void *) argvlist, 600 sizeof (char *) * (*alloc)); 601 } 602 } 603 604 argvlist[*size] = alloc_string(str); 605 *size += 1; 606 argvlist[*size] = NULL; 607 608 return (argvlist); 609 } 610 611 612 /* 613 * Useful parsing macros 614 */ 615 #define must_be(s, c) if (*s++ != c) return (0) 616 #define skip_digits(s) while (isdigit(*s)) s++ 617 /* Parsing macro below is created to handle fabric devices which contains */ 618 /* upper hex digits like c2t210000203708B8CEd0s0. */ 619 /* To get the target id(tid) the digit and hex upper digit need to */ 620 /* be processed. */ 621 #define skip_digit_or_hexupper(s) while (isdigit(*s) || \ 622 (isxdigit(*s) && isupper(*s))) s++ 623 624 /* 625 * Return true if a device name matches the conventions 626 * for the particular system. 627 */ 628 int 629 conventional_name(char *name) 630 { 631 must_be(name, 'c'); 632 skip_digits(name); 633 if (*name == 't') { 634 name++; 635 skip_digit_or_hexupper(name); 636 } 637 must_be(name, 'd'); 638 skip_digits(name); 639 must_be(name, 's'); 640 skip_digits(name); 641 return (*name == 0); 642 } 643 644 #ifdef i386 645 /* 646 * Return true if a device name match the emc powerpath name scheme: 647 * emcpowerN[a-p,p0,p1,p2,p3,p4] 648 */ 649 int 650 emcpower_name(char *name) 651 { 652 char *emcp = "emcpower"; 653 char *devp = "/dev/dsk"; 654 char *rdevp = "/dev/rdsk"; 655 656 if (strncmp(devp, name, strlen(devp)) == 0) { 657 name += strlen(devp) + 1; 658 } else if (strncmp(rdevp, name, strlen(rdevp)) == 0) { 659 name += strlen(rdevp) + 1; 660 } 661 if (strncmp(emcp, name, strlen(emcp)) == 0) { 662 name += strlen(emcp); 663 if (isdigit(*name)) { 664 skip_digits(name); 665 if ((*name >= 'a') && (*name <= 'p')) { 666 name ++; 667 if ((*name >= '0') && (*name <= '4')) { 668 name++; 669 } 670 } 671 return (*name == '\0'); 672 } 673 } 674 return (0); 675 } 676 #endif 677 678 /* 679 * Return true if a device name matches the intel physical name conventions 680 * for the particular system. 681 */ 682 int 683 fdisk_physical_name(char *name) 684 { 685 must_be(name, 'c'); 686 skip_digits(name); 687 if (*name == 't') { 688 name++; 689 skip_digit_or_hexupper(name); 690 } 691 must_be(name, 'd'); 692 skip_digits(name); 693 must_be(name, 'p'); 694 skip_digits(name); 695 return (*name == 0); 696 } 697 698 /* 699 * Return true if a device name matches the conventions 700 * for a "whole disk" name for the particular system. 701 * The name in this case must match exactly that which 702 * would appear in the device directory itself. 703 */ 704 int 705 whole_disk_name(char *name) 706 { 707 must_be(name, 'c'); 708 skip_digits(name); 709 if (*name == 't') { 710 name++; 711 skip_digit_or_hexupper(name); 712 } 713 must_be(name, 'd'); 714 skip_digits(name); 715 must_be(name, 's'); 716 must_be(name, '2'); 717 return (*name == 0); 718 } 719 720 721 /* 722 * Return true if a name is in the internal canonical form 723 */ 724 int 725 canonical_name(char *name) 726 { 727 must_be(name, 'c'); 728 skip_digits(name); 729 if (*name == 't') { 730 name++; 731 skip_digit_or_hexupper(name); 732 } 733 must_be(name, 'd'); 734 skip_digits(name); 735 return (*name == 0); 736 } 737 738 739 /* 740 * Return true if a name is in the internal canonical form for 4.x 741 * Used to support 4.x naming conventions under 5.0. 742 */ 743 int 744 canonical4x_name(char *name) 745 { 746 char **p; 747 int i; 748 749 p = disk_4x_identifiers; 750 for (i = N_DISK_4X_IDS; i > 0; i--, p++) { 751 if (match_substr(name, *p)) { 752 name += strlen(*p); 753 break; 754 } 755 } 756 if (i == 0) 757 return (0); 758 skip_digits(name); 759 return (*name == 0); 760 } 761 762 763 /* 764 * Map a conventional name into the internal canonical form: 765 * 766 * /dev/rdsk/c0t0d0s0 -> c0t0d0 767 */ 768 void 769 canonicalize_name(char *dst, char *src) 770 { 771 char *s; 772 773 /* 774 * Copy from the 'c' to the end to the destination string... 775 */ 776 s = strchr(src, 'c'); 777 if (s != NULL) { 778 (void) strcpy(dst, s); 779 /* 780 * Remove the trailing slice (partition) reference 781 */ 782 s = dst + strlen(dst) - 2; 783 if (*s == 's') { 784 *s = 0; 785 } 786 } else { 787 *dst = 0; /* be tolerant of garbage input */ 788 } 789 } 790 791 792 /* 793 * Return true if we find an occurance of s2 at the 794 * beginning of s1. We don't have to match all of 795 * s1, but we do have to match all of s2 796 */ 797 int 798 match_substr(char *s1, char *s2) 799 { 800 while (*s2 != 0) { 801 if (*s1++ != *s2++) 802 return (0); 803 } 804 805 return (1); 806 } 807 808 809 /* 810 * Dump a structure in hexadecimal, for diagnostic purposes 811 */ 812 #define BYTES_PER_LINE 16 813 814 void 815 dump(char *hdr, caddr_t src, int nbytes, int format) 816 { 817 int i; 818 int n; 819 char *p; 820 char s[256]; 821 822 assert(format == HEX_ONLY || format == HEX_ASCII); 823 824 (void) strcpy(s, hdr); 825 for (p = s; *p; p++) { 826 *p = ' '; 827 } 828 829 p = hdr; 830 while (nbytes > 0) { 831 err_print("%s", p); 832 p = s; 833 n = min(nbytes, BYTES_PER_LINE); 834 for (i = 0; i < n; i++) { 835 err_print("%02x ", src[i] & 0xff); 836 } 837 if (format == HEX_ASCII) { 838 for (i = BYTES_PER_LINE-n; i > 0; i--) { 839 err_print(" "); 840 } 841 err_print(" "); 842 for (i = 0; i < n; i++) { 843 err_print("%c", isprint(src[i]) ? src[i] : '.'); 844 } 845 } 846 err_print("\n"); 847 nbytes -= n; 848 src += n; 849 } 850 } 851 852 853 float 854 bn2mb(uint64_t nblks) 855 { 856 float n; 857 858 n = (float)nblks / 1024.0; 859 return ((n / 1024.0) * cur_blksz); 860 } 861 862 863 diskaddr_t 864 mb2bn(float mb) 865 { 866 diskaddr_t n; 867 868 n = (diskaddr_t)(mb * 1024.0 * (1024.0 / cur_blksz)); 869 return (n); 870 } 871 872 float 873 bn2gb(uint64_t nblks) 874 { 875 float n; 876 877 n = (float)nblks / (1024.0 * 1024.0); 878 return ((n/1024.0) * cur_blksz); 879 880 } 881 882 float 883 bn2tb(uint64_t nblks) 884 { 885 float n; 886 887 n = (float)nblks / (1024.0 * 1024.0 * 1024.0); 888 return ((n/1024.0) * cur_blksz); 889 } 890 891 diskaddr_t 892 gb2bn(float gb) 893 { 894 diskaddr_t n; 895 896 n = (diskaddr_t)(gb * 1024.0 * 1024.0 * (1024.0 / cur_blksz)); 897 return (n); 898 } 899 900 /* 901 * This routine finds out the number of lines (rows) in a terminal 902 * window. The default value of TTY_LINES is returned on error. 903 */ 904 int 905 get_tty_lines(void) 906 { 907 int tty_lines = TTY_LINES; 908 struct winsize winsize; 909 910 if ((option_f == NULL) && isatty(0) == 1 && isatty(1) == 1) { 911 /* 912 * We have a real terminal for std input and output 913 */ 914 winsize.ws_row = 0; 915 if (ioctl(1, TIOCGWINSZ, &winsize) == 0) { 916 if (winsize.ws_row > 2) { 917 /* 918 * Should be atleast 2 lines, for division 919 * by (tty_lines - 1, tty_lines - 2) to work. 920 */ 921 tty_lines = winsize.ws_row; 922 } 923 } 924 } 925 return (tty_lines); 926 } 927