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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * Copyright 2015 Nexenta Systems, Inc. All rights reserved. 25 */ 26 27 /* 28 * This file contains I/O related functions. 29 */ 30 #include "global.h" 31 32 #include <unistd.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <signal.h> 36 #include <ctype.h> 37 #include <stdarg.h> 38 #include <sys/tty.h> 39 #include <sys/termio.h> 40 #include <sys/termios.h> 41 42 #include "startup.h" 43 #include "misc.h" 44 #include "menu_partition.h" 45 #include "param.h" 46 #include "menu.h" 47 48 49 extern int data_lineno; 50 extern char *space2str(); 51 extern long strtol(); 52 53 /* 54 * This variable is used to determine whether a token is present in the pipe 55 * already. 56 */ 57 static char token_present = 0; 58 59 /* 60 * This variable always gives us access to the most recent token type 61 */ 62 int last_token_type = 0; 63 64 #ifdef __STDC__ 65 /* 66 * Prototypes for ANSI C compilers 67 */ 68 static int sup_get_token(char *); 69 static void pushchar(int c); 70 static int checkeof(void); 71 static void flushline(void); 72 static int strcnt(char *s1, char *s2); 73 static int getbn(char *str, diskaddr_t *iptr); 74 static void print_input_choices(int type, u_ioparam_t *param); 75 static int slist_widest_str(slist_t *slist); 76 static void ljust_print(char *str, int width); 77 static int sup_inputchar(void); 78 static void sup_pushchar(int c); 79 static int geti64(char *str, uint64_t *iptr, uint64_t *wild); 80 81 #else /* __STDC__ */ 82 /* 83 * Prototypes for non-ANSI C compilers 84 */ 85 86 static int sup_get_token(); 87 static void pushchar(int c); 88 static int checkeof(void); 89 static void flushline(void); 90 static int strcnt(char *s1, char *s2); 91 static int getbn(char *str, diskaddr_t *iptr); 92 static void print_input_choices(int type, u_ioparam_t *param); 93 static int slist_widest_str(slist_t *slist); 94 static void ljust_print(char *str, int width); 95 static int sup_inputchar(void); 96 static void sup_pushchar(int c); 97 static int geti64(char *str, uint64_t *iptr, uint64_t *wild); 98 99 #endif /* __STDC__ */ 100 101 102 /* 103 * This routine pushes the given character back onto the input stream. 104 */ 105 static void 106 pushchar(c) 107 int c; 108 { 109 (void) ungetc(c, stdin); 110 } 111 112 /* 113 * This routine checks the input stream for an eof condition. 114 */ 115 static int 116 checkeof() 117 { 118 return (feof(stdin)); 119 } 120 121 /* 122 * This routine gets the next token off the input stream. A token is 123 * basically any consecutive non-white characters. 124 */ 125 char * 126 gettoken(inbuf) 127 char *inbuf; 128 { 129 char *ptr = inbuf; 130 int c, quoted = 0; 131 132 retoke: 133 /* 134 * Remove any leading white-space. 135 */ 136 while ((isspace(c = getchar())) && (c != '\n')) 137 ; 138 /* 139 * If we are at the beginning of a line and hit the comment character, 140 * flush the line and start again. 141 */ 142 if (!token_present && c == COMMENT_CHAR) { 143 token_present = 1; 144 flushline(); 145 goto retoke; 146 } 147 /* 148 * Loop on each character until we hit unquoted white-space. 149 */ 150 while (!isspace(c) || quoted && (c != '\n')) { 151 /* 152 * If we hit eof, get out. 153 */ 154 if (checkeof()) 155 return (NULL); 156 /* 157 * If we hit a double quote, change the state of quotedness. 158 */ 159 if (c == '"') 160 quoted = !quoted; 161 /* 162 * If there's room in the buffer, add the character to the end. 163 */ 164 else if (ptr - inbuf < TOKEN_SIZE) 165 *ptr++ = (char)c; 166 /* 167 * Get the next character. 168 */ 169 c = getchar(); 170 } 171 /* 172 * Null terminate the token. 173 */ 174 *ptr = '\0'; 175 /* 176 * Peel off white-space still in the pipe. 177 */ 178 while (isspace(c) && (c != '\n')) 179 c = getchar(); 180 /* 181 * If we hit another token, push it back and set state. 182 */ 183 if (c != '\n') { 184 pushchar(c); 185 token_present = 1; 186 } else 187 token_present = 0; 188 /* 189 * Return the token. 190 */ 191 return (inbuf); 192 } 193 194 /* 195 * This routine removes the leading and trailing spaces from a token. 196 */ 197 void 198 clean_token(cleantoken, token) 199 char *cleantoken, *token; 200 { 201 char *ptr; 202 203 /* 204 * Strip off leading white-space. 205 */ 206 for (ptr = token; isspace(*ptr); ptr++) 207 ; 208 /* 209 * Copy it into the clean buffer. 210 */ 211 (void) strcpy(cleantoken, ptr); 212 /* 213 * Strip off trailing white-space. 214 */ 215 for (ptr = cleantoken + strlen(cleantoken) - 1; 216 isspace(*ptr) && (ptr >= cleantoken); ptr--) { 217 *ptr = '\0'; 218 } 219 } 220 221 /* 222 * This routine checks if a token is already present on the input line 223 */ 224 int 225 istokenpresent() 226 { 227 return (token_present); 228 } 229 230 /* 231 * This routine flushes the rest of an input line if there is known 232 * to be data in it. The flush has to be qualified because the newline 233 * may have already been swallowed by the last gettoken. 234 */ 235 static void 236 flushline() 237 { 238 if (token_present) { 239 /* 240 * Flush the pipe to eol or eof. 241 */ 242 while ((getchar() != '\n') && !checkeof()) 243 ; 244 /* 245 * Mark the pipe empty. 246 */ 247 token_present = 0; 248 } 249 } 250 251 /* 252 * This routine returns the number of characters that are identical 253 * between s1 and s2, stopping as soon as a mismatch is found. 254 */ 255 static int 256 strcnt(s1, s2) 257 char *s1, *s2; 258 { 259 int i = 0; 260 261 while ((*s1 != '\0') && (*s1++ == *s2++)) 262 i++; 263 return (i); 264 } 265 266 /* 267 * This routine converts the given token into an integer. The token 268 * must convert cleanly into an integer with no unknown characters. 269 * If the token is the wildcard string, and the wildcard parameter 270 * is present, the wildcard value will be returned. 271 */ 272 int 273 geti(str, iptr, wild) 274 char *str; 275 int *iptr, *wild; 276 { 277 char *str2; 278 279 /* 280 * If there's a wildcard value and the string is wild, return the 281 * wildcard value. 282 */ 283 if (wild != NULL && strcmp(str, WILD_STRING) == 0) 284 *iptr = *wild; 285 else { 286 /* 287 * Conver the string to an integer. 288 */ 289 *iptr = (int)strtol(str, &str2, 0); 290 /* 291 * If any characters didn't convert, it's an error. 292 */ 293 if (*str2 != '\0') { 294 err_print("`%s' is not an integer.\n", str); 295 return (-1); 296 } 297 } 298 return (0); 299 } 300 301 /* 302 * This routine converts the given token into a long long. The token 303 * must convert cleanly into a 64-bit integer with no unknown characters. 304 * If the token is the wildcard string, and the wildcard parameter 305 * is present, the wildcard value will be returned. 306 */ 307 static int 308 geti64(str, iptr, wild) 309 char *str; 310 uint64_t *iptr, *wild; 311 { 312 char *str2; 313 314 /* 315 * If there's a wildcard value and the string is wild, return the 316 * wildcard value. 317 */ 318 if ((wild != NULL) && (strcmp(str, WILD_STRING)) == 0) { 319 *iptr = *wild; 320 } else { 321 /* 322 * Conver the string to an integer. 323 */ 324 *iptr = (uint64_t)strtoll(str, &str2, 0); 325 /* 326 * If any characters didn't convert, it's an error. 327 */ 328 if (*str2 != '\0') { 329 err_print("`%s' is not an integer.\n", str); 330 return (-1); 331 } 332 } 333 return (0); 334 } 335 336 /* 337 * This routine converts the given string into a block number on the 338 * current disk. The format of a block number is either a self-based 339 * number, or a series of self-based numbers separated by slashes. 340 * Any number preceeding the first slash is considered a cylinder value. 341 * Any number succeeding the first slash but preceeding the second is 342 * considered a head value. Any number succeeding the second slash is 343 * considered a sector value. Any of these numbers can be wildcarded 344 * to the highest possible legal value. 345 */ 346 static int 347 getbn(str, iptr) 348 char *str; 349 diskaddr_t *iptr; 350 { 351 char *cptr, *hptr, *sptr; 352 int cyl, head, sect; 353 int wild; 354 diskaddr_t wild64; 355 TOKEN buf; 356 357 /* 358 * Set cylinder pointer to beginning of string. 359 */ 360 cptr = str; 361 /* 362 * Look for the first slash. 363 */ 364 while ((*str != '\0') && (*str != '/')) 365 str++; 366 /* 367 * If there wasn't one, convert string to an integer and return it. 368 */ 369 if (*str == '\0') { 370 wild64 = physsects() - 1; 371 if (geti64(cptr, iptr, &wild64)) 372 return (-1); 373 return (0); 374 } 375 /* 376 * Null out the slash and set head pointer just beyond it. 377 */ 378 *str++ = '\0'; 379 hptr = str; 380 /* 381 * Look for the second slash. 382 */ 383 while ((*str != '\0') && (*str != '/')) 384 str++; 385 /* 386 * If there wasn't one, sector pointer points to a . 387 */ 388 if (*str == '\0') 389 sptr = str; 390 /* 391 * If there was, null it out and set sector point just beyond it. 392 */ 393 else { 394 *str++ = '\0'; 395 sptr = str; 396 } 397 /* 398 * Convert the cylinder part to an integer and store it. 399 */ 400 clean_token(buf, cptr); 401 wild = ncyl + acyl - 1; 402 if (geti(buf, &cyl, &wild)) 403 return (-1); 404 if ((cyl < 0) || (cyl >= (ncyl + acyl))) { 405 err_print("`%d' is out of range.\n", cyl); 406 return (-1); 407 } 408 /* 409 * Convert the head part to an integer and store it. 410 */ 411 clean_token(buf, hptr); 412 wild = nhead - 1; 413 if (geti(buf, &head, &wild)) 414 return (-1); 415 if ((head < 0) || (head >= nhead)) { 416 err_print("`%d' is out of range.\n", head); 417 return (-1); 418 } 419 /* 420 * Convert the sector part to an integer and store it. 421 */ 422 clean_token(buf, sptr); 423 wild = sectors(head) - 1; 424 if (geti(buf, §, &wild)) 425 return (-1); 426 if ((sect < 0) || (sect >= sectors(head))) { 427 err_print("`%d' is out of range.\n", sect); 428 return (-1); 429 } 430 /* 431 * Combine the pieces into a block number and return it. 432 */ 433 *iptr = chs2bn(cyl, head, sect); 434 return (0); 435 } 436 437 /* 438 * This routine is the basis for all input into the program. It 439 * understands the semantics of a set of input types, and provides 440 * consistent error messages for all input. It allows for default 441 * values and prompt strings. 442 */ 443 uint64_t 444 input(type, promptstr, delim, param, deflt, cmdflag) 445 int type; 446 char *promptstr; 447 int delim; 448 u_ioparam_t *param; 449 int *deflt; 450 int cmdflag; 451 { 452 int interactive, help, i, length, index, tied; 453 blkaddr_t bn; 454 diskaddr_t bn64; 455 char **str, **strings; 456 TOKEN token, cleantoken; 457 TOKEN token2, cleantoken2; 458 char *arg; 459 struct bounds *bounds; 460 char *s; 461 int value; 462 int cyls, cylno; 463 uint64_t blokno; 464 float nmegs; 465 float ngigs; 466 char shell_argv[MAXPATHLEN]; 467 part_deflt_t *part_deflt; 468 efi_deflt_t *efi_deflt; 469 470 /* 471 * Optional integer input has been added as a hack. 472 * Function result is 1 if user typed anything. 473 * Whatever they typed is returned in *deflt. 474 * This permits us to distinguish between "no value", 475 * and actually entering in some value, for instance. 476 */ 477 if (type == FIO_OPINT) { 478 assert(deflt != NULL); 479 } 480 reprompt: 481 help = interactive = 0; 482 /* 483 * If we are inputting a command, flush any current input in the pipe. 484 */ 485 if (cmdflag == CMD_INPUT) 486 flushline(); 487 /* 488 * Note whether the token is already present. 489 */ 490 if (!token_present) 491 interactive = 1; 492 /* 493 * Print the prompt. 494 */ 495 fmt_print(promptstr); 496 /* 497 * If there is a default value, print it in a format appropriate 498 * for the input type. 499 */ 500 if (deflt != NULL) { 501 switch (type) { 502 case FIO_BN: 503 #if !defined(lint) /* caller has aligned the pointer specifying FIO_BN */ 504 fmt_print("[%llu, ", *(diskaddr_t *)deflt); 505 pr_dblock(fmt_print, *(diskaddr_t *)deflt); 506 fmt_print("]"); 507 #endif 508 break; 509 case FIO_INT: 510 fmt_print("[%d]", *deflt); 511 break; 512 case FIO_INT64: 513 #if defined(lint) 514 /* caller is longlong aligned specifying FIO_INT64 */ 515 efi_deflt = NULL; 516 #else 517 efi_deflt = (efi_deflt_t *)deflt; 518 #endif 519 fmt_print("[%llu]", efi_deflt->start_sector); 520 break; 521 case FIO_CSTR: 522 case FIO_MSTR: 523 strings = (char **)param->io_charlist; 524 for (i = 0, str = strings; i < *deflt; i++, str++) 525 ; 526 fmt_print("[%s]", *str); 527 break; 528 case FIO_OSTR: 529 fmt_print("[\"%s\"]", (char *)deflt); 530 break; 531 case FIO_SLIST: 532 /* 533 * Search for a string matching the default 534 * value. If found, use it. Otherwise 535 * assume the default value is actually 536 * an illegal choice, and default to 537 * the first item in the list. 538 */ 539 s = find_string(param->io_slist, *deflt); 540 if (s == (char *)NULL) { 541 s = (param->io_slist)->str; 542 } 543 fmt_print("[%s]", s); 544 break; 545 case FIO_CYL: 546 /* 547 * Old-style partition size input, used to 548 * modify complete partition tables 549 */ 550 blokno = *(blkaddr32_t *)deflt; 551 fmt_print("[%llub, %uc, %1.2fmb, %1.2fgb]", blokno, 552 bn2c(blokno), bn2mb(blokno), bn2gb(blokno)); 553 break; 554 case FIO_ECYL: 555 /* 556 * set up pointer to partition defaults 557 * structure 558 */ 559 part_deflt = (part_deflt_t *)deflt; 560 561 /* 562 * Build print format specifier. We use the 563 * starting cylinder number which was entered 564 * before this call to input(), in case the 565 * user has changed it from the value in the 566 * cur_parts->pinfo_map[].dkl_cylno 567 * field for the current parition 568 */ 569 570 /* 571 * Determine the proper default end cylinder: 572 * Start Cyl Default Size End Cylinder 573 * 0 0 0 574 * >0 0 Start Cyl 575 * 0 >0 Default Size 576 * (Cyls) - 1 577 * >0 >0 (Start + 578 * Default Size 579 * (Cyls)) -1 580 */ 581 582 if (part_deflt->deflt_size == 0) { 583 cylno = part_deflt->start_cyl; 584 } else if (part_deflt->start_cyl == 0) { 585 cylno = bn2c(part_deflt->deflt_size) - 1; 586 } else { 587 cylno = (bn2c(part_deflt->deflt_size) + 588 part_deflt->start_cyl) - 1; 589 } 590 591 fmt_print("[%ub, %uc, %de, %1.2fmb, %1.2fgb]", 592 part_deflt->deflt_size, 593 bn2c(part_deflt->deflt_size), 594 cylno, 595 bn2mb(part_deflt->deflt_size), 596 bn2gb(part_deflt->deflt_size)); 597 598 break; 599 case FIO_EFI: 600 #if defined(lint) 601 /* caller is longlong aligned when specifying FIO_EFI */ 602 efi_deflt = NULL; 603 #else 604 efi_deflt = (efi_deflt_t *)deflt; 605 #endif 606 607 fmt_print("[%llub, %llue, %llumb, %llugb, %llutb]", 608 efi_deflt->end_sector, 609 efi_deflt->start_sector + efi_deflt->end_sector - 1, 610 (efi_deflt->end_sector * cur_blksz) / 611 (1024 * 1024), 612 (efi_deflt->end_sector * cur_blksz) / 613 (1024 * 1024 * 1024), 614 (efi_deflt->end_sector * cur_blksz) / 615 ((uint64_t)1024 * 1024 * 1024 * 1024)); 616 break; 617 case FIO_OPINT: 618 /* no default value for optional input type */ 619 fmt_print("[default]"); 620 break; 621 default: 622 err_print("Error: unknown input type.\n"); 623 fullabort(); 624 } 625 } 626 /* 627 * Print the delimiter character. 628 */ 629 fmt_print("%c ", delim); 630 /* 631 * Get the token. If we hit eof, exit the program gracefully. 632 */ 633 if (gettoken(token) == NULL) 634 fullabort(); 635 636 /* 637 * check if the user has issued (!) , escape to shell 638 */ 639 if ((cmdflag == CMD_INPUT) && (token[0] == '!')) { 640 641 /* get the list of arguments to shell command */ 642 (void) memset(shell_argv, 0, sizeof (shell_argv)); 643 644 /* initialize to the first token... */ 645 arg = &token[1]; 646 647 /* 648 * ... and then collect all tokens until the end of 649 * the line as arguments 650 */ 651 do { 652 /* skip empty tokens. */ 653 if (*arg == '\0') 654 continue; 655 /* 656 * If either of the following two strlcat() 657 * operations overflows, report an error and 658 * exit gracefully. 659 */ 660 if ((strlcat(shell_argv, arg, sizeof (shell_argv)) >= 661 sizeof (shell_argv)) || 662 (strlcat(shell_argv, " ", sizeof (shell_argv)) >= 663 sizeof (shell_argv))) { 664 err_print("Error: Command line too long.\n"); 665 fullabort(); 666 } 667 } while (token_present && (arg = gettoken(token)) != NULL); 668 669 /* execute the shell command */ 670 (void) execute_shell(shell_argv, sizeof (shell_argv)); 671 redisplay_menu_list((char **)param->io_charlist); 672 if (interactive) { 673 goto reprompt; 674 } 675 } 676 677 /* 678 * Certain commands accept up to two tokens 679 * Unfortunately, this is kind of a hack. 680 */ 681 token2[0] = 0; 682 cleantoken2[0] = 0; 683 if (type == FIO_CYL || type == FIO_ECYL) { 684 if (token_present) { 685 if (gettoken(token2) == NULL) 686 fullabort(); 687 clean_token(cleantoken2, token2); 688 } 689 } 690 /* 691 * Echo the token back to the user if it was in the pipe or we 692 * are running out of a command file. 693 */ 694 if (!interactive || option_f) { 695 if (token2[0] == 0) { 696 fmt_print("%s\n", token); 697 } else { 698 fmt_print("%s %s\n", token, token2); 699 } 700 } 701 /* 702 * If we are logging, echo the token to the log file. The else 703 * is necessary here because the above printf will also put the 704 * token in the log file. 705 */ 706 else if (log_file) { 707 log_print("%s %s\n", token, token2); 708 } 709 /* 710 * If the token was not in the pipe and it wasn't a command, flush 711 * the rest of the line to keep things in sync. 712 */ 713 if (interactive && cmdflag != CMD_INPUT) 714 flushline(); 715 /* 716 * Scrub off the white-space. 717 */ 718 clean_token(cleantoken, token); 719 /* 720 * If the input was a blank line and we weren't prompting 721 * specifically for a blank line... 722 */ 723 if ((strcmp(cleantoken, "") == 0) && (type != FIO_BLNK)) { 724 /* 725 * If there's a default, return it. 726 */ 727 if (deflt != NULL) { 728 if (type == FIO_OSTR) { 729 /* 730 * Duplicate and return the default string 731 */ 732 return ((int)alloc_string((char *)deflt)); 733 } else if (type == FIO_SLIST) { 734 /* 735 * If we can find a match for the default 736 * value in the list, return the default 737 * value. If there's no match for the 738 * default value, it's an illegal 739 * choice. Return the first value in 740 * the list. 741 */ 742 s = find_string(param->io_slist, *deflt); 743 if ((cur_label == L_TYPE_EFI) && 744 (s == (char *)NULL)) { 745 return (*deflt); 746 } 747 if (s == (char *)NULL) { 748 return ((param->io_slist)->value); 749 } else { 750 return (*deflt); 751 } 752 } else if (type == FIO_OPINT) { 753 /* 754 * The user didn't enter anything 755 */ 756 return (0); 757 } else if (type == FIO_ECYL) { 758 return (part_deflt->deflt_size); 759 } else if (type == FIO_INT64) { 760 return (efi_deflt->start_sector); 761 } else if (type == FIO_EFI) { 762 return (efi_deflt->end_sector); 763 } else { 764 return (*deflt); 765 } 766 } 767 /* 768 * If the blank was not in the pipe, just reprompt. 769 */ 770 if (interactive) { 771 goto reprompt; 772 } 773 /* 774 * If the blank was in the pipe, it's an error. 775 */ 776 err_print("No default for this entry.\n"); 777 cmdabort(SIGINT); 778 } 779 /* 780 * If token is a '?' or a 'h', it is a request for help. 781 */ 782 if ((strcmp(cleantoken, "?") == 0) || 783 (strcmp(cleantoken, "h") == 0) || 784 (strcmp(cleantoken, "help") == 0)) { 785 help = 1; 786 } 787 /* 788 * Switch on the type of input expected. 789 */ 790 switch (type) { 791 /* 792 * Expecting a disk block number. 793 */ 794 case FIO_BN: 795 /* 796 * Parameter is the bounds of legal block numbers. 797 */ 798 bounds = (struct bounds *)¶m->io_bounds; 799 /* 800 * Print help message if required. 801 */ 802 if (help) { 803 fmt_print("Expecting a block number from %llu (", 804 bounds->lower); 805 pr_dblock(fmt_print, bounds->lower); 806 fmt_print(") to %llu (", bounds->upper); 807 pr_dblock(fmt_print, bounds->upper); 808 fmt_print(")\n"); 809 break; 810 } 811 /* 812 * Convert token to a disk block number. 813 */ 814 if (cur_label == L_TYPE_EFI) { 815 if (geti64(cleantoken, (uint64_t *)&bn64, 816 (uint64_t *)NULL)) 817 break; 818 } else { 819 if (getbn(cleantoken, &bn64)) 820 break; 821 } 822 /* 823 * Check to be sure it is within the legal bounds. 824 */ 825 if ((bn64 < bounds->lower) || (bn64 > bounds->upper)) { 826 err_print("`"); 827 pr_dblock(err_print, bn64); 828 err_print("' is out of range.\n"); 829 break; 830 } 831 /* 832 * It's ok, return it. 833 */ 834 return (bn64); 835 /* 836 * Expecting an integer. 837 */ 838 case FIO_INT: 839 /* 840 * Parameter is the bounds of legal integers. 841 */ 842 bounds = (struct bounds *)¶m->io_bounds; 843 /* 844 * Print help message if required. 845 */ 846 if (help) { 847 fmt_print("Expecting an integer from %llu", 848 bounds->lower); 849 fmt_print(" to %llu\n", bounds->upper); 850 break; 851 } 852 /* 853 * Convert the token into an integer. 854 */ 855 if (geti(cleantoken, (int *)&bn, (int *)NULL)) 856 break; 857 /* 858 * Check to be sure it is within the legal bounds. 859 */ 860 if ((bn < bounds->lower) || (bn > bounds->upper)) { 861 err_print("`%lu' is out of range.\n", bn); 862 break; 863 } 864 /* 865 * If it's ok, return it. 866 */ 867 return (bn); 868 case FIO_INT64: 869 /* 870 * Parameter is the bounds of legal integers. 871 */ 872 bounds = (struct bounds *)¶m->io_bounds; 873 /* 874 * Print help message if required. 875 */ 876 if (help) { 877 fmt_print("Expecting an integer from %llu", 878 bounds->lower); 879 fmt_print(" to %llu\n", bounds->upper); 880 break; 881 } 882 /* 883 * Convert the token into an integer. 884 */ 885 if (geti64(cleantoken, (uint64_t *)&bn64, (uint64_t *)NULL)) { 886 break; 887 } 888 /* 889 * Check to be sure it is within the legal bounds. 890 */ 891 if ((bn64 < bounds->lower) || (bn64 > bounds->upper)) { 892 err_print("`%llu' is out of range.\n", bn64); 893 break; 894 } 895 /* 896 * If it's ok, return it. 897 */ 898 return (bn64); 899 /* 900 * Expecting an integer, or no input. 901 */ 902 case FIO_OPINT: 903 /* 904 * Parameter is the bounds of legal integers. 905 */ 906 bounds = (struct bounds *)¶m->io_bounds; 907 /* 908 * Print help message if required. 909 */ 910 if (help) { 911 fmt_print("Expecting an integer from %llu", 912 bounds->lower); 913 fmt_print(" to %llu, or no input\n", bounds->upper); 914 break; 915 } 916 /* 917 * Convert the token into an integer. 918 */ 919 if (geti(cleantoken, (int *)&bn, (int *)NULL)) 920 break; 921 /* 922 * Check to be sure it is within the legal bounds. 923 */ 924 if ((bn < bounds->lower) || (bn > bounds->upper)) { 925 err_print("`%lu' is out of range.\n", bn); 926 break; 927 } 928 /* 929 * For optional case, return 1 indicating that 930 * the user actually did enter something. 931 */ 932 if (!deflt) 933 *deflt = bn; 934 return (1); 935 /* 936 * Expecting a closed string. This means that the input 937 * string must exactly match one of the strings passed in 938 * as the parameter. 939 */ 940 case FIO_CSTR: 941 /* 942 * The parameter is a null terminated array of character 943 * pointers, each one pointing to a legal input string. 944 */ 945 strings = (char **)param->io_charlist; 946 /* 947 * Walk through the legal strings, seeing if any of them 948 * match the token. If a match is made, return the index 949 * of the string that was matched. 950 */ 951 for (str = strings; *str != NULL; str++) 952 if (strcmp(cleantoken, *str) == 0) 953 return (str - strings); 954 /* 955 * Print help message if required. 956 */ 957 if (help) { 958 print_input_choices(type, param); 959 } else { 960 err_print("`%s' is not expected.\n", cleantoken); 961 } 962 break; 963 /* 964 * Expecting a matched string. This means that the input 965 * string must either match one of the strings passed in, 966 * or be a unique abbreviation of one of them. 967 */ 968 case FIO_MSTR: 969 /* 970 * The parameter is a null terminated array of character 971 * pointers, each one pointing to a legal input string. 972 */ 973 strings = (char **)param->io_charlist; 974 length = index = tied = 0; 975 /* 976 * Loop through the legal input strings. 977 */ 978 for (str = strings; *str != NULL; str++) { 979 /* 980 * See how many characters of the token match 981 * this legal string. 982 */ 983 i = strcnt(cleantoken, *str); 984 /* 985 * If it's not the whole token, then it's not a match. 986 */ 987 if ((uint_t)i < strlen(cleantoken)) 988 continue; 989 /* 990 * If it ties with another input, remember that. 991 */ 992 if (i == length) 993 tied = 1; 994 /* 995 * If it matches the most so far, record that. 996 */ 997 if (i > length) { 998 index = str - strings; 999 tied = 0; 1000 length = i; 1001 } 1002 } 1003 /* 1004 * Print help message if required. 1005 */ 1006 if (length == 0) { 1007 if (help) { 1008 print_input_choices(type, param); 1009 } else { 1010 err_print("`%s' is not expected.\n", 1011 cleantoken); 1012 } 1013 break; 1014 } 1015 /* 1016 * If the abbreviation was non-unique, it's an error. 1017 */ 1018 if (tied) { 1019 err_print("`%s' is ambiguous.\n", cleantoken); 1020 break; 1021 } 1022 /* 1023 * We matched one. Return the index of the string we matched. 1024 */ 1025 return (index); 1026 /* 1027 * Expecting an open string. This means that any string is legal. 1028 */ 1029 case FIO_OSTR: 1030 /* 1031 * Print a help message if required. 1032 */ 1033 if (help) { 1034 fmt_print("Expecting a string\n"); 1035 break; 1036 } 1037 /* 1038 * alloc a copy of the string and return it 1039 */ 1040 return ((int)alloc_string(token)); 1041 1042 /* 1043 * Expecting a blank line. 1044 */ 1045 case FIO_BLNK: 1046 /* 1047 * We are always in non-echo mode when we are inputting 1048 * this type. We echo the newline as a carriage return 1049 * only so the prompt string will be covered over. 1050 */ 1051 nolog_print("\015"); 1052 /* 1053 * If we are logging, send a newline to the log file. 1054 */ 1055 if (log_file) 1056 log_print("\n"); 1057 /* 1058 * There is no value returned for this type. 1059 */ 1060 return (0); 1061 1062 /* 1063 * Expecting one of the entries in a string list. 1064 * Accept unique abbreviations. 1065 * Return the value associated with the matched string. 1066 */ 1067 case FIO_SLIST: 1068 i = find_value((slist_t *)param->io_slist, 1069 cleantoken, &value); 1070 if (i == 1) { 1071 return (value); 1072 } else { 1073 /* 1074 * Print help message if required. 1075 */ 1076 1077 if (help) { 1078 print_input_choices(type, param); 1079 } else { 1080 if (i == 0) 1081 err_print("`%s' not expected.\n", 1082 cleantoken); 1083 else 1084 err_print("`%s' is ambiguous.\n", 1085 cleantoken); 1086 } 1087 } 1088 break; 1089 1090 /* 1091 * Cylinder size input when modifying a complete partition map 1092 */ 1093 case FIO_CYL: 1094 /* 1095 * Parameter is the bounds of legal block numbers. 1096 */ 1097 bounds = (struct bounds *)¶m->io_bounds; 1098 assert(bounds->lower == 0); 1099 /* 1100 * Print help message if required. 1101 */ 1102 if (help) { 1103 fmt_print("Expecting up to %llu blocks,", 1104 bounds->upper); 1105 fmt_print(" %u cylinders, ", bn2c(bounds->upper)); 1106 fmt_print(" %1.2f megabytes, ", bn2mb(bounds->upper)); 1107 fmt_print("or %1.2f gigabytes\n", bn2gb(bounds->upper)); 1108 break; 1109 } 1110 /* 1111 * Parse the first token: try to find 'b', 'c' or 'm' 1112 */ 1113 s = cleantoken; 1114 while (*s && (isdigit(*s) || (*s == '.') || (*s == '$'))) { 1115 s++; 1116 } 1117 /* 1118 * If we found a conversion specifier, second token is unused 1119 * Otherwise, the second token should supply it. 1120 */ 1121 if (*s != 0) { 1122 value = *s; 1123 *s = 0; 1124 } else { 1125 value = cleantoken2[0]; 1126 } 1127 /* 1128 * If the token is the wild card, simply supply the max 1129 * This order allows the user to specify the maximum in 1130 * either blocks/cyls/megabytes - a convenient fiction. 1131 */ 1132 if (strcmp(cleantoken, WILD_STRING) == 0) { 1133 return (bounds->upper); 1134 } 1135 /* 1136 * Allow the user to specify zero with no units, 1137 * by just defaulting to cylinders. 1138 */ 1139 if (strcmp(cleantoken, "0") == 0) { 1140 value = 'c'; 1141 } 1142 /* 1143 * If there's a decimal point, but no unit specification, 1144 * let's assume megabytes. 1145 */ 1146 if ((value == 0) && (strchr(cleantoken, '.') != NULL)) { 1147 value = 'm'; 1148 } 1149 /* 1150 * Handle each unit type we support 1151 */ 1152 switch (value) { 1153 case 'b': 1154 /* 1155 * Convert token to a disk block number. 1156 */ 1157 if (geti64(cleantoken, &bn64, &bounds->upper)) 1158 break; 1159 /* 1160 * Check to be sure it is within the legal bounds. 1161 */ 1162 if ((bn64 < bounds->lower) || (bn64 > bounds->upper)) { 1163 err_print( 1164 "`%llub' is out of the range %llu " 1165 "to %llu\n", 1166 bn64, bounds->lower, bounds->upper); 1167 break; 1168 } 1169 /* 1170 * Verify the block lies on a cylinder boundary 1171 */ 1172 if ((bn64 % spc()) != 0) { 1173 err_print( 1174 "partition size must be a multiple of " 1175 "%u blocks to lie on a cylinder boundary\n", 1176 spc()); 1177 err_print( 1178 "%llu blocks is approximately %u cylinders," 1179 " %1.2f megabytes or %1.2f gigabytes\n", 1180 bn64, bn2c(bn64), bn2mb(bn64), bn2gb(bn64)); 1181 break; 1182 } 1183 return (bn64); 1184 case 'c': 1185 /* 1186 * Convert token from a number of cylinders to 1187 * a number of blocks. 1188 */ 1189 i = bn2c(bounds->upper); 1190 if (geti(cleantoken, &cyls, &i)) 1191 break; 1192 /* 1193 * Check the bounds - cyls is number of cylinders 1194 */ 1195 if (cyls > (bounds->upper/spc())) { 1196 err_print("`%dc' is out of range\n", cyls); 1197 break; 1198 } 1199 /* 1200 * Convert cylinders to blocks and return 1201 */ 1202 return (cyls * spc()); 1203 case 'm': 1204 /* 1205 * Convert token from megabytes to a block number. 1206 */ 1207 if (sscanf(cleantoken, "%f2", &nmegs) != 1) { 1208 err_print("`%s' is not recognized\n", 1209 cleantoken); 1210 break; 1211 } 1212 /* 1213 * Check the bounds 1214 */ 1215 if (nmegs > bn2mb(bounds->upper)) { 1216 err_print("`%1.2fmb' is out of range\n", nmegs); 1217 break; 1218 } 1219 /* 1220 * Convert to blocks 1221 */ 1222 bn64 = mb2bn(nmegs); 1223 /* 1224 * Round value up to nearest cylinder 1225 */ 1226 i = spc(); 1227 bn64 = ((bn64 + (i-1)) / i) * i; 1228 return (bn64); 1229 case 'g': 1230 /* 1231 * Convert token from gigabytes to a block number. 1232 */ 1233 if (sscanf(cleantoken, "%f2", &ngigs) != 1) { 1234 err_print("`%s' is not recognized\n", 1235 cleantoken); 1236 break; 1237 } 1238 /* 1239 * Check the bounds 1240 */ 1241 if (ngigs > bn2gb(bounds->upper)) { 1242 err_print("`%1.2fgb' is out of range\n", ngigs); 1243 break; 1244 } 1245 /* 1246 * Convert to blocks 1247 */ 1248 bn64 = gb2bn(ngigs); 1249 /* 1250 * Round value up to nearest cylinder 1251 */ 1252 i = spc(); 1253 bn64 = ((bn64 + (i-1)) / i) * i; 1254 return (bn64); 1255 default: 1256 err_print( 1257 "Please specify units in either b(blocks), c(cylinders), m(megabytes) \ 1258 or g(gigabytes)\n"); 1259 break; 1260 } 1261 break; 1262 1263 case FIO_ECYL: 1264 /* 1265 * Parameter is the bounds of legal block numbers. 1266 */ 1267 bounds = (struct bounds *)¶m->io_bounds; 1268 assert(bounds->lower == 0); 1269 1270 /* 1271 * Print help message if required. 1272 */ 1273 if (help) { 1274 fmt_print("Expecting up to %llu blocks,", 1275 bounds->upper); 1276 fmt_print(" %u cylinders, ", 1277 bn2c(bounds->upper)); 1278 fmt_print(" %u end cylinder, ", 1279 (uint_t)(bounds->upper / spc())); 1280 fmt_print(" %1.2f megabytes, ", 1281 bn2mb(bounds->upper)); 1282 fmt_print("or %1.2f gigabytes\n", 1283 bn2gb(bounds->upper)); 1284 break; 1285 } 1286 1287 /* 1288 * Parse the first token: try to find 'b', 'c', 'e' 1289 * or 'm' 1290 */ 1291 s = cleantoken; 1292 while (*s && (isdigit(*s) || (*s == '.') || (*s == '$'))) { 1293 s++; 1294 } 1295 1296 /* 1297 * If we found a conversion specifier, second token is 1298 * unused Otherwise, the second token should supply it. 1299 */ 1300 if (*s != 0) { 1301 value = *s; 1302 *s = 0; 1303 } else { 1304 value = cleantoken2[0]; 1305 } 1306 1307 /* 1308 * If the token is the wild card, simply supply the max 1309 * This order allows the user to specify the maximum in 1310 * either blocks/cyls/megabytes - a convenient fiction. 1311 */ 1312 if (strcmp(cleantoken, WILD_STRING) == 0) { 1313 return (bounds->upper); 1314 } 1315 1316 /* 1317 * Allow the user to specify zero with no units, 1318 * by just defaulting to cylinders. 1319 */ 1320 1321 if (value != 'e' && strcmp(cleantoken, "0") == 0) { 1322 value = 'c'; 1323 } 1324 1325 1326 /* 1327 * If there's a decimal point, but no unit 1328 * specification, let's assume megabytes. 1329 */ 1330 if ((value == 0) && (strchr(cleantoken, '.') != NULL)) { 1331 value = 'm'; 1332 } 1333 1334 /* 1335 * Handle each unit type we support 1336 */ 1337 switch (value) { 1338 case 'b': 1339 /* 1340 * Convert token to a disk block number. 1341 */ 1342 if (geti64(cleantoken, &bn64, &bounds->upper)) 1343 break; 1344 /* 1345 * Check to be sure it is within the 1346 * legal bounds. 1347 */ 1348 if ((bn64 < bounds->lower) || (bn64 > bounds->upper)) { 1349 err_print( 1350 "`%llub' is out of the range %llu to %llu\n", 1351 bn64, bounds->lower, bounds->upper); 1352 break; 1353 } 1354 1355 /* 1356 * Verify the block lies on a cylinder 1357 * boundary 1358 */ 1359 if ((bn64 % spc()) != 0) { 1360 err_print( 1361 "partition size must be a multiple of %u " 1362 "blocks to lie on a cylinder boundary\n", 1363 spc()); 1364 err_print( 1365 "%llu blocks is approximately %u cylinders," 1366 " %1.2f megabytes or %1.2f gigabytes\n", 1367 bn64, bn2c(bn64), bn2mb(bn64), bn2gb(bn64)); 1368 break; 1369 } 1370 1371 return (bn64); 1372 1373 case 'e': 1374 /* 1375 * Token is ending cylinder 1376 */ 1377 1378 /* convert token to integer */ 1379 if (geti(cleantoken, &cylno, (int *)NULL)) { 1380 break; 1381 } 1382 1383 /* 1384 * check that input cylno isn't before the current 1385 * starting cylinder number. Note that we are NOT 1386 * using the starting cylinder from 1387 * cur_parts->pinfo_map[].dkl_cylno! 1388 */ 1389 if (cylno < part_deflt->start_cyl) { 1390 err_print( 1391 "End cylinder must fall on or after start cylinder %u\n", 1392 part_deflt->start_cyl); 1393 break; 1394 } 1395 1396 /* 1397 * calculate cylinder number of upper boundary, and 1398 * verify that our input is within range 1399 */ 1400 i = (bn2c(bounds->upper) + part_deflt->start_cyl - 1); 1401 1402 if (cylno > i) { 1403 err_print( 1404 "End cylinder %d is beyond max cylinder %d\n", 1405 cylno, i); 1406 break; 1407 } 1408 1409 /* 1410 * calculate number of cylinders based on input 1411 */ 1412 cyls = ((cylno - part_deflt->start_cyl) + 1); 1413 1414 return (cyls * spc()); 1415 1416 case 'c': 1417 /* 1418 * Convert token from a number of 1419 * cylinders to a number of blocks. 1420 */ 1421 i = bn2c(bounds->upper); 1422 if (geti(cleantoken, &cyls, &i)) 1423 break; 1424 1425 /* 1426 * Check the bounds - cyls is number of 1427 * cylinders 1428 */ 1429 if (cyls > (bounds->upper/spc())) { 1430 err_print("`%dc' is out of range\n", cyls); 1431 break; 1432 } 1433 1434 /* 1435 * Convert cylinders to blocks and 1436 * return 1437 */ 1438 return (cyls * spc()); 1439 1440 case 'm': 1441 /* 1442 * Convert token from megabytes to a 1443 * block number. 1444 */ 1445 if (sscanf(cleantoken, "%f2", &nmegs) != 1) { 1446 err_print("`%s' is not recognized\n", 1447 cleantoken); 1448 break; 1449 } 1450 1451 /* 1452 * Check the bounds 1453 */ 1454 if (nmegs > bn2mb(bounds->upper)) { 1455 err_print("`%1.2fmb' is out of range\n", nmegs); 1456 break; 1457 } 1458 1459 /* 1460 * Convert to blocks 1461 */ 1462 bn64 = mb2bn(nmegs); 1463 1464 /* 1465 * Round value up to nearest cylinder 1466 */ 1467 i = spc(); 1468 bn64 = ((bn64 + (i-1)) / i) * i; 1469 return (bn64); 1470 1471 case 'g': 1472 /* 1473 * Convert token from gigabytes to a 1474 * block number. 1475 */ 1476 if (sscanf(cleantoken, "%f2", &ngigs) != 1) { 1477 err_print("`%s' is not recognized\n", 1478 cleantoken); 1479 break; 1480 } 1481 1482 /* 1483 * Check the bounds 1484 */ 1485 if (ngigs > bn2gb(bounds->upper)) { 1486 err_print("`%1.2fgb' is out of range\n", ngigs); 1487 break; 1488 } 1489 1490 /* 1491 * Convert to blocks 1492 */ 1493 bn64 = gb2bn(ngigs); 1494 1495 /* 1496 * Round value up to nearest cylinder 1497 */ 1498 i = spc(); 1499 bn64 = ((bn64 + (i-1)) / i) * i; 1500 return (bn64); 1501 1502 default: 1503 err_print( 1504 "Please specify units in either b(blocks), c(cylinders), e(end cylinder),\n"); 1505 err_print("m(megabytes) or g(gigabytes)\n"); 1506 break; 1507 } 1508 break; 1509 case FIO_EFI: 1510 /* 1511 * Parameter is the bounds of legal block numbers. 1512 */ 1513 bounds = (struct bounds *)¶m->io_bounds; 1514 1515 /* 1516 * Print help message if required. 1517 */ 1518 if (help) { 1519 fmt_print("Expecting up to %llu sectors,", 1520 cur_parts->etoc->efi_last_u_lba); 1521 fmt_print("or %llu megabytes,", 1522 (cur_parts->etoc->efi_last_u_lba * cur_blksz)/ 1523 (1024 * 1024)); 1524 fmt_print("or %llu gigabytes\n", 1525 (cur_parts->etoc->efi_last_u_lba * cur_blksz)/ 1526 (1024 * 1024 * 1024)); 1527 fmt_print("or %llu terabytes\n", 1528 (cur_parts->etoc->efi_last_u_lba * cur_blksz)/ 1529 ((uint64_t)1024 * 1024 * 1024 * 1024)); 1530 break; 1531 } 1532 1533 /* 1534 * Parse the first token: try to find 'b', 'c', 'e' 1535 * or 'm' 1536 */ 1537 s = cleantoken; 1538 while (*s && (isdigit(*s) || (*s == '.') || (*s == '$'))) { 1539 s++; 1540 } 1541 1542 /* 1543 * If we found a conversion specifier, second token is 1544 * unused Otherwise, the second token should supply it. 1545 */ 1546 if (*s != 0) { 1547 value = *s; 1548 *s = 0; 1549 } else { 1550 value = cleantoken2[0]; 1551 } 1552 1553 /* 1554 * If the token is the wild card, simply supply the max 1555 * This order allows the user to specify the maximum in 1556 * either blocks/cyls/megabytes - a convenient fiction. 1557 */ 1558 if (strcmp(cleantoken, WILD_STRING) == 0) { 1559 return (bounds->upper - EFI_MIN_RESV_SIZE - 1560 efi_deflt->start_sector); 1561 } 1562 1563 /* 1564 * Allow the user to specify zero with no units, 1565 * by just defaulting to sectors. 1566 */ 1567 1568 if (value != 'e' && strcmp(cleantoken, "0") == 0) { 1569 value = 'm'; 1570 } 1571 1572 1573 /* 1574 * If there's a decimal point, but no unit 1575 * specification, let's assume megabytes. 1576 */ 1577 if ((value == 0) && (strchr(cleantoken, '.') != NULL)) { 1578 value = 'm'; 1579 } 1580 1581 /* 1582 * Handle each unit type we support 1583 */ 1584 switch (value) { 1585 case 'b': 1586 /* 1587 * Token is number of blocks 1588 */ 1589 if (geti64(cleantoken, &blokno, (uint64_t *)NULL)) { 1590 break; 1591 } 1592 if (blokno > bounds->upper) { 1593 err_print( 1594 "Number of blocks must be less that the total available blocks.\n"); 1595 break; 1596 } 1597 return (blokno); 1598 1599 case 'e': 1600 /* 1601 * Token is ending block number 1602 */ 1603 1604 /* convert token to integer */ 1605 if (geti64(cleantoken, &blokno, (uint64_t *)NULL)) { 1606 break; 1607 } 1608 1609 /* 1610 * Some sanity check 1611 */ 1612 if (blokno < efi_deflt->start_sector) { 1613 err_print( 1614 "End Sector must fall on or after start sector %llu\n", 1615 efi_deflt->start_sector); 1616 break; 1617 } 1618 1619 /* 1620 * verify that our input is within range 1621 */ 1622 if (blokno > cur_parts->etoc->efi_last_u_lba) { 1623 err_print( 1624 "End Sector %llu is beyond max Sector %llu\n", 1625 blokno, cur_parts->etoc->efi_last_u_lba); 1626 break; 1627 } 1628 1629 /* 1630 * calculate number of blocks based on input 1631 */ 1632 1633 return (blokno - efi_deflt->start_sector + 1); 1634 1635 case 'm': 1636 /* 1637 * Convert token from megabytes to a 1638 * block number. 1639 */ 1640 if (sscanf(cleantoken, "%f2", &nmegs) != 1) { 1641 err_print("`%s' is not recognized\n", 1642 cleantoken); 1643 break; 1644 } 1645 1646 /* 1647 * Check the bounds 1648 */ 1649 if (nmegs > bn2mb(bounds->upper - bounds->lower)) { 1650 err_print("`%1.2fmb' is out of range\n", nmegs); 1651 break; 1652 } 1653 1654 return (mb2bn(nmegs)); 1655 1656 case 'g': 1657 if (sscanf(cleantoken, "%f2", &nmegs) != 1) { 1658 err_print("`%s' is not recognized\n", 1659 cleantoken); 1660 break; 1661 } 1662 if (nmegs > bn2gb(bounds->upper - bounds->lower)) { 1663 err_print("`%1.2fgb' is out of range\n", nmegs); 1664 break; 1665 } 1666 1667 return (gb2bn(nmegs)); 1668 1669 case 't': 1670 if (sscanf(cleantoken, "%f2", &nmegs) != 1) { 1671 err_print("`%s' is not recognized\n", 1672 cleantoken); 1673 break; 1674 } 1675 if (nmegs > bn2tb(bounds->upper - bounds->lower)) { 1676 err_print("`%1.2ftb' is out of range\n", nmegs); 1677 break; 1678 } 1679 return (uint64_t)((float)nmegs * 1024.0 * 1680 1024.0 * 1024.0 * 1024.0 / cur_blksz); 1681 1682 default: 1683 err_print( 1684 "Please specify units in either b(number of blocks), e(end sector),\n"); 1685 err_print(" g(gigabytes), m(megabytes)"); 1686 err_print(" or t(terabytes)\n"); 1687 break; 1688 } 1689 break; 1690 1691 /* 1692 * If we don't recognize the input type, it's bad news. 1693 */ 1694 default: 1695 err_print("Error: unknown input type.\n"); 1696 fullabort(); 1697 } 1698 /* 1699 * If we get here, it's because some error kept us from accepting 1700 * the token. If we are running out of a command file, gracefully 1701 * leave the program. If we are interacting with the user, simply 1702 * reprompt. If the token was in the pipe, abort the current command. 1703 */ 1704 if (option_f) 1705 fullabort(); 1706 else if (interactive) 1707 goto reprompt; 1708 else 1709 cmdabort(SIGINT); 1710 /* 1711 * Never actually reached. 1712 */ 1713 return (-1); 1714 } 1715 1716 /* 1717 * Print input choices 1718 */ 1719 static void 1720 print_input_choices(type, param) 1721 int type; 1722 u_ioparam_t *param; 1723 { 1724 char **sp; 1725 slist_t *lp; 1726 int width; 1727 int col; 1728 int ncols; 1729 1730 switch (type) { 1731 case FIO_CSTR: 1732 fmt_print("Expecting one of the following:\n"); 1733 goto common; 1734 1735 case FIO_MSTR: 1736 fmt_print("Expecting one of the following: "); 1737 fmt_print("(abbreviations ok):\n"); 1738 common: 1739 for (sp = (char **)param->io_charlist; *sp != NULL; sp++) { 1740 fmt_print("\t%s\n", *sp); 1741 } 1742 break; 1743 1744 case FIO_SLIST: 1745 fmt_print("Expecting one of the following: "); 1746 fmt_print("(abbreviations ok):\n"); 1747 /* 1748 * Figure out the width of the widest string 1749 */ 1750 width = slist_widest_str((slist_t *)param->io_slist); 1751 width += 4; 1752 /* 1753 * If the help messages are empty, print the 1754 * possible choices in left-justified columns 1755 */ 1756 lp = (slist_t *)param->io_slist; 1757 if (*lp->help == 0) { 1758 col = 0; 1759 ncols = 60 / width; 1760 for (; lp->str != NULL; lp++) { 1761 if (col == 0) 1762 fmt_print("\t"); 1763 ljust_print(lp->str, 1764 (++col == ncols) ? 0 : width); 1765 if (col == ncols) { 1766 col = 0; 1767 fmt_print("\n"); 1768 } 1769 } 1770 if (col != 0) 1771 fmt_print("\n"); 1772 } else { 1773 /* 1774 * With help messages, print each choice, 1775 * and help message, on its own line. 1776 */ 1777 for (; lp->str != NULL; lp++) { 1778 fmt_print("\t"); 1779 ljust_print(lp->str, width); 1780 fmt_print("- %s\n", lp->help); 1781 } 1782 } 1783 break; 1784 1785 default: 1786 err_print("Error: unknown input type.\n"); 1787 fullabort(); 1788 } 1789 1790 fmt_print("\n"); 1791 } 1792 1793 1794 /* 1795 * Search a string list for a particular string. 1796 * Use minimum recognition, to accept unique abbreviations 1797 * Return the number of possible matches found. 1798 * If only one match was found, return the arbitrary value 1799 * associated with the matched string in match_value. 1800 */ 1801 int 1802 find_value(slist, match_str, match_value) 1803 slist_t *slist; 1804 char *match_str; 1805 int *match_value; 1806 { 1807 int i; 1808 int nmatches; 1809 int length; 1810 int match_length; 1811 1812 nmatches = 0; 1813 length = 0; 1814 1815 match_length = strlen(match_str); 1816 1817 for (; slist->str != NULL; slist++) { 1818 /* 1819 * See how many characters of the token match 1820 */ 1821 i = strcnt(match_str, slist->str); 1822 /* 1823 * If it's not the whole token, then it's not a match. 1824 */ 1825 if (i < match_length) 1826 continue; 1827 /* 1828 * If it ties with another input, remember that. 1829 */ 1830 if (i == length) 1831 nmatches++; 1832 /* 1833 * If it matches the most so far, record that. 1834 */ 1835 if (i > length) { 1836 *match_value = slist->value; 1837 nmatches = 1; 1838 length = i; 1839 } 1840 } 1841 1842 return (nmatches); 1843 } 1844 1845 /* 1846 * Search a string list for a particular value. 1847 * Return the string associated with that value. 1848 */ 1849 char * 1850 find_string(slist, match_value) 1851 slist_t *slist; 1852 int match_value; 1853 { 1854 for (; slist->str != NULL; slist++) { 1855 if (slist->value == match_value) { 1856 return (slist->str); 1857 } 1858 } 1859 1860 return ((char *)NULL); 1861 } 1862 1863 /* 1864 * Return the width of the widest string in an slist 1865 */ 1866 static int 1867 slist_widest_str(slist) 1868 slist_t *slist; 1869 { 1870 int i; 1871 int width; 1872 1873 width = 0; 1874 for (; slist->str != NULL; slist++) { 1875 if ((i = strlen(slist->str)) > width) 1876 width = i; 1877 } 1878 1879 return (width); 1880 } 1881 1882 /* 1883 * Print a string left-justified to a fixed width. 1884 */ 1885 static void 1886 ljust_print(str, width) 1887 char *str; 1888 int width; 1889 { 1890 int i; 1891 1892 fmt_print("%s", str); 1893 for (i = width - strlen(str); i > 0; i--) { 1894 fmt_print(" "); 1895 } 1896 } 1897 1898 /* 1899 * This routine is a modified version of printf. It handles the cases 1900 * of silent mode and logging; other than that it is identical to the 1901 * library version. 1902 */ 1903 /*PRINTFLIKE1*/ 1904 void 1905 fmt_print(char *format, ...) 1906 { 1907 va_list ap; 1908 1909 va_start(ap, format); 1910 1911 /* 1912 * If we are running silent, skip it. 1913 */ 1914 if (option_s == 0) { 1915 /* 1916 * Do the print to standard out. 1917 */ 1918 if (need_newline) { 1919 (void) printf("\n"); 1920 } 1921 (void) vprintf(format, ap); 1922 /* 1923 * If we are logging, also print to the log file. 1924 */ 1925 if (log_file) { 1926 if (need_newline) { 1927 (void) fprintf(log_file, "\n"); 1928 } 1929 (void) vfprintf(log_file, format, ap); 1930 (void) fflush(log_file); 1931 } 1932 } 1933 1934 need_newline = 0; 1935 1936 va_end(ap); 1937 } 1938 1939 /* 1940 * This routine is a modified version of printf. It handles the cases 1941 * of silent mode; other than that it is identical to the 1942 * library version. It differs from the above printf in that it does 1943 * not print the message to a log file. 1944 */ 1945 /*PRINTFLIKE1*/ 1946 void 1947 nolog_print(char *format, ...) 1948 { 1949 va_list ap; 1950 1951 va_start(ap, format); 1952 1953 /* 1954 * If we are running silent, skip it. 1955 */ 1956 if (option_s == 0) { 1957 /* 1958 * Do the print to standard out. 1959 */ 1960 if (need_newline) { 1961 (void) printf("\n"); 1962 } 1963 (void) vprintf(format, ap); 1964 } 1965 1966 va_end(ap); 1967 1968 need_newline = 0; 1969 } 1970 1971 /* 1972 * This routine is a modified version of printf. It handles the cases 1973 * of silent mode, and only prints the message to the log file, not 1974 * stdout. Other than that is identical to the library version. 1975 */ 1976 /*PRINTFLIKE1*/ 1977 void 1978 log_print(char *format, ...) 1979 { 1980 va_list ap; 1981 1982 va_start(ap, format); 1983 1984 /* 1985 * If we are running silent, skip it. 1986 */ 1987 if (option_s == 0) { 1988 /* 1989 * Do the print to the log file. 1990 */ 1991 if (need_newline) { 1992 (void) fprintf(log_file, "\n"); 1993 } 1994 (void) vfprintf(log_file, format, ap); 1995 (void) fflush(log_file); 1996 } 1997 1998 va_end(ap); 1999 2000 need_newline = 0; 2001 } 2002 2003 /* 2004 * This routine is a modified version of printf. It prints the message 2005 * to stderr, and to the log file is appropriate. 2006 * Other than that is identical to the library version. 2007 */ 2008 /*PRINTFLIKE1*/ 2009 void 2010 err_print(char *format, ...) 2011 { 2012 va_list ap; 2013 2014 va_start(ap, format); 2015 2016 /* 2017 * Flush anything pending to stdout 2018 */ 2019 if (need_newline) { 2020 (void) printf("\n"); 2021 } 2022 (void) fflush(stdout); 2023 /* 2024 * Do the print to stderr. 2025 */ 2026 (void) vfprintf(stderr, format, ap); 2027 /* 2028 * If we are logging, also print to the log file. 2029 */ 2030 if (log_file) { 2031 if (need_newline) { 2032 (void) fprintf(log_file, "\n"); 2033 } 2034 (void) vfprintf(log_file, format, ap); 2035 (void) fflush(log_file); 2036 } 2037 va_end(ap); 2038 2039 need_newline = 0; 2040 } 2041 2042 /* 2043 * Print a number of characters from a buffer. The buffer 2044 * does not need to be null-terminated. Since the data 2045 * may be coming from a device, we cannot be sure the 2046 * data is not crud, so be rather defensive. 2047 */ 2048 void 2049 print_buf(buf, nbytes) 2050 char *buf; 2051 int nbytes; 2052 { 2053 int c; 2054 2055 while (nbytes-- > 0) { 2056 c = *buf++; 2057 if (isascii(c) && isprint(c)) { 2058 fmt_print("%c", c); 2059 } else 2060 break; 2061 } 2062 } 2063 2064 #ifdef not 2065 /* 2066 * This routine prints out a message describing the given ctlr. 2067 * The message is identical to the one printed by the kernel during 2068 * booting. 2069 */ 2070 void 2071 pr_ctlrline(ctlr) 2072 register struct ctlr_info *ctlr; 2073 { 2074 2075 fmt_print(" %s%d at %s 0x%x ", 2076 ctlr->ctlr_cname, ctlr->ctlr_num, 2077 space2str(ctlr->ctlr_space), ctlr->ctlr_addr); 2078 if (ctlr->ctlr_vec != 0) 2079 fmt_print("vec 0x%x ", ctlr->ctlr_vec); 2080 else 2081 fmt_print("pri %d ", ctlr->ctlr_prio); 2082 fmt_print("\n"); 2083 } 2084 #endif /* not */ 2085 2086 /* 2087 * This routine prints out a message describing the given disk. 2088 * The message is identical to the one printed by the kernel during 2089 * booting. 2090 */ 2091 void 2092 pr_diskline(disk, num) 2093 register struct disk_info *disk; 2094 int num; 2095 { 2096 struct ctlr_info *ctlr = disk->disk_ctlr; 2097 struct disk_type *type = disk->disk_type; 2098 2099 fmt_print(" %4d. %s ", num, disk->disk_name); 2100 if ((type != NULL) && (disk->label_type == L_TYPE_SOLARIS)) { 2101 fmt_print("<%s cyl %u alt %u hd %u sec %u>", 2102 type->dtype_asciilabel, type->dtype_ncyl, 2103 type->dtype_acyl, type->dtype_nhead, 2104 type->dtype_nsect); 2105 } else if ((type != NULL) && (disk->label_type == L_TYPE_EFI)) { 2106 cur_blksz = disk->disk_lbasize; 2107 print_efi_string(type->vendor, type->product, 2108 type->revision, type->capacity); 2109 } else if (disk->disk_flags & DSK_RESERVED) { 2110 fmt_print("<drive not available: reserved>"); 2111 } else if (disk->disk_flags & DSK_UNAVAILABLE) { 2112 fmt_print("<drive not available>"); 2113 } else { 2114 fmt_print("<drive type unknown>"); 2115 } 2116 if (chk_volname(disk)) { 2117 fmt_print(" "); 2118 print_volname(disk); 2119 } 2120 fmt_print("\n"); 2121 2122 if (disk->devfs_name != NULL) { 2123 fmt_print(" %s\n", disk->devfs_name); 2124 } else { 2125 fmt_print(" %s%d at %s%d slave %d\n", 2126 ctlr->ctlr_dname, disk->disk_dkinfo.dki_unit, 2127 ctlr->ctlr_cname, ctlr->ctlr_num, 2128 disk->disk_dkinfo.dki_slave); 2129 } 2130 2131 #ifdef OLD 2132 fmt_print(" %4d. %s at %s%d slave %d", num, disk->disk_name, 2133 ctlr->ctlr_cname, ctlr->ctlr_num, disk->disk_dkinfo.dki_slave); 2134 if (chk_volname(disk)) { 2135 fmt_print(": "); 2136 print_volname(disk); 2137 } 2138 fmt_print("\n"); 2139 if (type != NULL) { 2140 fmt_print( 2141 " %s%d: <%s cyl %u alt %u hd %u sec %u>\n", 2142 ctlr->ctlr_dname, disk->disk_dkinfo.dki_unit, 2143 type->dtype_asciilabel, type->dtype_ncyl, 2144 type->dtype_acyl, type->dtype_nhead, 2145 type->dtype_nsect); 2146 } else { 2147 fmt_print(" %s%d: <drive type unknown>\n", 2148 ctlr->ctlr_dname, disk->disk_dkinfo.dki_unit); 2149 } 2150 #endif /* OLD */ 2151 } 2152 2153 /* 2154 * This routine prints out a given disk block number in cylinder/head/sector 2155 * format. It uses the printing routine passed in to do the actual output. 2156 */ 2157 void 2158 pr_dblock(void (*func)(char *, ...), diskaddr_t bn) 2159 { 2160 if (cur_label == L_TYPE_SOLARIS) { 2161 (*func)("%u/%u/%u", bn2c(bn), 2162 bn2h(bn), bn2s(bn)); 2163 } else { 2164 (*func)("%llu", bn); 2165 } 2166 } 2167 2168 /* 2169 * This routine inputs a character from the data file. It understands 2170 * the use of '\' to prevent interpretation of a newline. It also keeps 2171 * track of the current line in the data file via a global variable. 2172 */ 2173 static int 2174 sup_inputchar() 2175 { 2176 int c; 2177 2178 /* 2179 * Input the character. 2180 */ 2181 c = getc(data_file); 2182 /* 2183 * If it's not a backslash, return it. 2184 */ 2185 if (c != '\\') 2186 return (c); 2187 /* 2188 * It was a backslash. Get the next character. 2189 */ 2190 c = getc(data_file); 2191 /* 2192 * If it was a newline, update the line counter and get the next 2193 * character. 2194 */ 2195 if (c == '\n') { 2196 data_lineno++; 2197 c = getc(data_file); 2198 } 2199 /* 2200 * Return the character. 2201 */ 2202 return (c); 2203 } 2204 2205 /* 2206 * This routine pushes a character back onto the input pipe for the data file. 2207 */ 2208 static void 2209 sup_pushchar(c) 2210 int c; 2211 { 2212 (void) ungetc(c, data_file); 2213 } 2214 2215 /* 2216 * Variables to support pushing back tokens 2217 */ 2218 static int have_pushed_token = 0; 2219 static TOKEN pushed_buf; 2220 static int pushed_token; 2221 2222 /* 2223 * This routine inputs a token from the data file. A token is a series 2224 * of contiguous non-white characters or a recognized special delimiter 2225 * character. Use of the wrapper lets us always have the value of the 2226 * last token around, which is useful for error recovery. 2227 */ 2228 int 2229 sup_gettoken(buf) 2230 char *buf; 2231 { 2232 last_token_type = sup_get_token(buf); 2233 return (last_token_type); 2234 } 2235 2236 static int 2237 sup_get_token(buf) 2238 char *buf; 2239 { 2240 char *ptr = buf; 2241 int c, quoted = 0; 2242 2243 /* 2244 * First check for presence of push-backed token. 2245 * If so, return it. 2246 */ 2247 if (have_pushed_token) { 2248 have_pushed_token = 0; 2249 bcopy(pushed_buf, buf, TOKEN_SIZE+1); 2250 return (pushed_token); 2251 } 2252 /* 2253 * Zero out the returned token buffer 2254 */ 2255 bzero(buf, TOKEN_SIZE + 1); 2256 /* 2257 * Strip off leading white-space. 2258 */ 2259 while ((isspace(c = sup_inputchar())) && (c != '\n')) 2260 ; 2261 /* 2262 * Read in characters until we hit unquoted white-space. 2263 */ 2264 for (; !isspace(c) || quoted; c = sup_inputchar()) { 2265 /* 2266 * If we hit eof, that's a token. 2267 */ 2268 if (feof(data_file)) 2269 return (SUP_EOF); 2270 /* 2271 * If we hit a double quote, change the state of quoting. 2272 */ 2273 if (c == '"') { 2274 quoted = !quoted; 2275 continue; 2276 } 2277 /* 2278 * If we hit a newline, that delimits a token. 2279 */ 2280 if (c == '\n') 2281 break; 2282 /* 2283 * If we hit any nonquoted special delimiters, that delimits 2284 * a token. 2285 */ 2286 if (!quoted && (c == '=' || c == ',' || c == ':' || 2287 c == '#' || c == '|' || c == '&' || c == '~')) 2288 break; 2289 /* 2290 * Store the character if there's room left. 2291 */ 2292 if (ptr - buf < TOKEN_SIZE) 2293 *ptr++ = (char)c; 2294 } 2295 /* 2296 * If we stored characters in the buffer, then we inputted a string. 2297 * Push the delimiter back into the pipe and return the string. 2298 */ 2299 if (ptr - buf > 0) { 2300 sup_pushchar(c); 2301 return (SUP_STRING); 2302 } 2303 /* 2304 * We didn't input a string, so we must have inputted a known delimiter. 2305 * store the delimiter in the buffer, so it will get returned. 2306 */ 2307 buf[0] = c; 2308 /* 2309 * Switch on the delimiter. Return the appropriate value for each one. 2310 */ 2311 switch (c) { 2312 case '=': 2313 return (SUP_EQL); 2314 case ':': 2315 return (SUP_COLON); 2316 case ',': 2317 return (SUP_COMMA); 2318 case '\n': 2319 return (SUP_EOL); 2320 case '|': 2321 return (SUP_OR); 2322 case '&': 2323 return (SUP_AND); 2324 case '~': 2325 return (SUP_TILDE); 2326 case '#': 2327 /* 2328 * For comments, we flush out the rest of the line and return 2329 * an EOL. 2330 */ 2331 while ((c = sup_inputchar()) != '\n' && !feof(data_file)) 2332 ; 2333 if (feof(data_file)) 2334 return (SUP_EOF); 2335 else 2336 return (SUP_EOL); 2337 /* 2338 * Shouldn't ever get here. 2339 */ 2340 default: 2341 return (SUP_STRING); 2342 } 2343 } 2344 2345 /* 2346 * Push back a token 2347 */ 2348 void 2349 sup_pushtoken(token_buf, token_type) 2350 char *token_buf; 2351 int token_type; 2352 { 2353 /* 2354 * We can only push one token back at a time 2355 */ 2356 assert(have_pushed_token == 0); 2357 2358 have_pushed_token = 1; 2359 bcopy(token_buf, pushed_buf, TOKEN_SIZE+1); 2360 pushed_token = token_type; 2361 } 2362 2363 /* 2364 * Get an entire line of input. Handles logging, comments, 2365 * and EOF. 2366 */ 2367 void 2368 get_inputline(line, nbytes) 2369 char *line; 2370 int nbytes; 2371 { 2372 char *p = line; 2373 int c; 2374 2375 /* 2376 * Remove any leading white-space and comments 2377 */ 2378 do { 2379 while ((isspace(c = getchar())) && (c != '\n')) 2380 ; 2381 } while (c == COMMENT_CHAR); 2382 /* 2383 * Loop on each character until end of line 2384 */ 2385 while (c != '\n') { 2386 /* 2387 * If we hit eof, get out. 2388 */ 2389 if (checkeof()) { 2390 fullabort(); 2391 } 2392 /* 2393 * Add the character to the buffer. 2394 */ 2395 if (nbytes > 1) { 2396 *p++ = (char)c; 2397 nbytes --; 2398 } 2399 /* 2400 * Get the next character. 2401 */ 2402 c = getchar(); 2403 } 2404 /* 2405 * Null terminate the token. 2406 */ 2407 *p = 0; 2408 /* 2409 * Indicate that we've emptied the pipe 2410 */ 2411 token_present = 0; 2412 /* 2413 * If we're running out of a file, echo the line to 2414 * the user, otherwise if we're logging, copy the 2415 * input to the log file. 2416 */ 2417 if (option_f) { 2418 fmt_print("%s\n", line); 2419 } else if (log_file) { 2420 log_print("%s\n", line); 2421 } 2422 } 2423 2424 /* 2425 * execute the shell escape command 2426 */ 2427 int 2428 execute_shell(char *s, size_t buff_size) 2429 { 2430 struct termio termio; 2431 struct termios tty; 2432 int tty_flag, i, j; 2433 char *shell_name; 2434 static char *default_shell = "/bin/sh"; 2435 2436 tty_flag = -1; 2437 2438 if (*s == '\0') { 2439 shell_name = getenv("SHELL"); 2440 2441 if (shell_name == NULL) { 2442 shell_name = default_shell; 2443 } 2444 if (strlcpy(s, shell_name, buff_size) >= 2445 buff_size) { 2446 err_print("Error: Shell command ($SHELL) too long.\n"); 2447 fullabort(); 2448 } 2449 } 2450 2451 /* save tty information */ 2452 2453 if (isatty(0)) { 2454 if (ioctl(0, TCGETS, &tty) == 0) 2455 tty_flag = 1; 2456 else { 2457 if (ioctl(0, TCGETA, &termio) == 0) { 2458 tty_flag = 0; 2459 tty.c_iflag = termio.c_iflag; 2460 tty.c_oflag = termio.c_oflag; 2461 tty.c_cflag = termio.c_cflag; 2462 tty.c_lflag = termio.c_lflag; 2463 for (i = 0; i < NCC; i++) 2464 tty.c_cc[i] = termio.c_cc[i]; 2465 } 2466 } 2467 } 2468 2469 /* close the current file descriptor */ 2470 if (cur_disk != NULL) { 2471 (void) close(cur_file); 2472 } 2473 2474 /* execute the shell escape */ 2475 (void) system(s); 2476 2477 /* reopen file descriptor if one was open before */ 2478 if (cur_disk != NULL) { 2479 if ((cur_file = open_disk(cur_disk->disk_path, 2480 O_RDWR | O_NDELAY)) < 0) { 2481 err_print("Error: can't reopen selected disk '%s'. \n", 2482 cur_disk->disk_name); 2483 fullabort(); 2484 } 2485 } 2486 2487 /* Restore tty information */ 2488 2489 if (isatty(0)) { 2490 if (tty_flag > 0) 2491 (void) ioctl(0, TCSETSW, &tty); 2492 else if (tty_flag == 0) { 2493 termio.c_iflag = tty.c_iflag; 2494 termio.c_oflag = tty.c_oflag; 2495 termio.c_cflag = tty.c_cflag; 2496 termio.c_lflag = tty.c_lflag; 2497 for (j = 0; j < NCC; j++) 2498 termio.c_cc[j] = tty.c_cc[j]; 2499 (void) ioctl(0, TCSETAW, &termio); 2500 } 2501 2502 if (isatty(1)) { 2503 fmt_print("\n[Hit Return to continue] \n"); 2504 (void) fflush(stdin); 2505 if (getchar() == EOF) 2506 fullabort(); 2507 } 2508 } 2509 return (0); 2510 } 2511 2512 void 2513 print_efi_string(char *vendor, char *product, char *revision, 2514 uint64_t capacity) 2515 { 2516 char *new_vendor; 2517 char *new_product; 2518 char *new_revision; 2519 char capacity_string[10]; 2520 float scaled; 2521 int i; 2522 2523 /* Strip whitespace from the end of inquiry strings */ 2524 new_vendor = strdup(vendor); 2525 if (new_vendor == NULL) 2526 return; 2527 2528 for (i = (strlen(new_vendor) - 1); i >= 0; i--) { 2529 if (new_vendor[i] != 0x20) { 2530 new_vendor[i+1] = '\0'; 2531 break; 2532 } 2533 } 2534 2535 new_product = strdup(product); 2536 if (new_product == NULL) { 2537 free(new_vendor); 2538 return; 2539 } 2540 2541 for (i = (strlen(new_product) - 1); i >= 0; i--) { 2542 if (new_product[i] != 0x20) { 2543 new_product[i+1] = '\0'; 2544 break; 2545 } 2546 } 2547 2548 new_revision = strdup(revision); 2549 if (new_product == NULL) { 2550 free(new_vendor); 2551 free(new_product); 2552 return; 2553 } 2554 2555 for (i = (strlen(new_revision) - 1); i >= 0; i--) { 2556 if (new_revision[i] != 0x20) { 2557 new_revision[i+1] = '\0'; 2558 break; 2559 } 2560 } 2561 2562 /* Now build size string */ 2563 scaled = bn2mb(capacity); 2564 if (scaled >= (float)1024.0 * 1024) { 2565 (void) snprintf(capacity_string, sizeof (capacity_string), 2566 "%.2fTB", scaled/((float)1024.0 * 1024)); 2567 } else if (scaled >= (float)1024.0) { 2568 (void) snprintf(capacity_string, sizeof (capacity_string), 2569 "%.2fGB", scaled/(float)1024.0); 2570 } else { 2571 (void) snprintf(capacity_string, sizeof (capacity_string), 2572 "%.2fMB", scaled); 2573 } 2574 2575 fmt_print("<%s-%s-%s-%s>", 2576 new_vendor, new_product, new_revision, capacity_string); 2577 2578 free(new_revision); 2579 free(new_product); 2580 free(new_vendor); 2581 } 2582