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