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
pushchar(c)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
checkeof()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 *
gettoken(inbuf)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
clean_token(cleantoken,token)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
istokenpresent()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
flushline()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
strcnt(s1,s2)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
geti(str,iptr,wild)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
geti64(str,iptr,wild)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
getbn(str,iptr)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
input(type,promptstr,delim,param,deflt,cmdflag)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
print_input_choices(type,param)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
find_value(slist,match_str,match_value)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 *
find_string(slist,match_value)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
slist_widest_str(slist)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
ljust_print(str,width)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
fmt_print(char * format,...)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
nolog_print(char * format,...)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
log_print(char * format,...)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
err_print(char * format,...)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
print_buf(buf,nbytes)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
pr_ctlrline(ctlr)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
pr_diskline(disk,num)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
pr_dblock(void (* func)(char *,...),diskaddr_t bn)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
sup_inputchar()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
sup_pushchar(c)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
sup_gettoken(buf)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
sup_get_token(buf)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
sup_pushtoken(token_buf,token_type)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
get_inputline(line,nbytes)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
execute_shell(s,buff_size)2428 execute_shell(s, buff_size)
2429 char *s;
2430 size_t buff_size;
2431 {
2432 struct termio termio;
2433 struct termios tty;
2434 int tty_flag, i, j;
2435 char *shell_name;
2436 static char *default_shell = "/bin/sh";
2437
2438 tty_flag = -1;
2439
2440 if (*s == NULL) {
2441 shell_name = getenv("SHELL");
2442
2443 if (shell_name == NULL) {
2444 shell_name = default_shell;
2445 }
2446 if (strlcpy(s, shell_name, buff_size) >=
2447 buff_size) {
2448 err_print("Error: Shell command ($SHELL) too long.\n");
2449 fullabort();
2450 }
2451 }
2452
2453 /* save tty information */
2454
2455 if (isatty(0)) {
2456 if (ioctl(0, TCGETS, &tty) == 0)
2457 tty_flag = 1;
2458 else {
2459 if (ioctl(0, TCGETA, &termio) == 0) {
2460 tty_flag = 0;
2461 tty.c_iflag = termio.c_iflag;
2462 tty.c_oflag = termio.c_oflag;
2463 tty.c_cflag = termio.c_cflag;
2464 tty.c_lflag = termio.c_lflag;
2465 for (i = 0; i < NCC; i++)
2466 tty.c_cc[i] = termio.c_cc[i];
2467 }
2468 }
2469 }
2470
2471 /* close the current file descriptor */
2472 if (cur_disk != NULL) {
2473 (void) close(cur_file);
2474 }
2475
2476 /* execute the shell escape */
2477 (void) system(s);
2478
2479 /* reopen file descriptor if one was open before */
2480 if (cur_disk != NULL) {
2481 if ((cur_file = open_disk(cur_disk->disk_path,
2482 O_RDWR | O_NDELAY)) < 0) {
2483 err_print("Error: can't reopen selected disk '%s'. \n",
2484 cur_disk->disk_name);
2485 fullabort();
2486 }
2487 }
2488
2489 /* Restore tty information */
2490
2491 if (isatty(0)) {
2492 if (tty_flag > 0)
2493 (void) ioctl(0, TCSETSW, &tty);
2494 else if (tty_flag == 0) {
2495 termio.c_iflag = tty.c_iflag;
2496 termio.c_oflag = tty.c_oflag;
2497 termio.c_cflag = tty.c_cflag;
2498 termio.c_lflag = tty.c_lflag;
2499 for (j = 0; j < NCC; j++)
2500 termio.c_cc[j] = tty.c_cc[j];
2501 (void) ioctl(0, TCSETAW, &termio);
2502 }
2503
2504 if (isatty(1)) {
2505 fmt_print("\n[Hit Return to continue] \n");
2506 (void) fflush(stdin);
2507 if (getchar() == EOF)
2508 fullabort();
2509 }
2510 }
2511 return (0);
2512 }
2513
2514 void
print_efi_string(char * vendor,char * product,char * revision,uint64_t capacity)2515 print_efi_string(char *vendor, char *product, char *revision,
2516 uint64_t capacity)
2517 {
2518 char *new_vendor;
2519 char *new_product;
2520 char *new_revision;
2521 char capacity_string[10];
2522 float scaled;
2523 int i;
2524
2525 /* Strip whitespace from the end of inquiry strings */
2526 new_vendor = strdup(vendor);
2527 if (new_vendor == NULL)
2528 return;
2529
2530 for (i = (strlen(new_vendor) - 1); i >= 0; i--) {
2531 if (new_vendor[i] != 0x20) {
2532 new_vendor[i+1] = '\0';
2533 break;
2534 }
2535 }
2536
2537 new_product = strdup(product);
2538 if (new_product == NULL) {
2539 free(new_vendor);
2540 return;
2541 }
2542
2543 for (i = (strlen(new_product) - 1); i >= 0; i--) {
2544 if (new_product[i] != 0x20) {
2545 new_product[i+1] = '\0';
2546 break;
2547 }
2548 }
2549
2550 new_revision = strdup(revision);
2551 if (new_product == NULL) {
2552 free(new_vendor);
2553 free(new_product);
2554 return;
2555 }
2556
2557 for (i = (strlen(new_revision) - 1); i >= 0; i--) {
2558 if (new_revision[i] != 0x20) {
2559 new_revision[i+1] = '\0';
2560 break;
2561 }
2562 }
2563
2564 /* Now build size string */
2565 scaled = bn2mb(capacity);
2566 if (scaled >= (float)1024.0 * 1024) {
2567 (void) snprintf(capacity_string, sizeof (capacity_string),
2568 "%.2fTB", scaled/((float)1024.0 * 1024));
2569 } else if (scaled >= (float)1024.0) {
2570 (void) snprintf(capacity_string, sizeof (capacity_string),
2571 "%.2fGB", scaled/(float)1024.0);
2572 } else {
2573 (void) snprintf(capacity_string, sizeof (capacity_string),
2574 "%.2fMB", scaled);
2575 }
2576
2577 fmt_print("<%s-%s-%s-%s>",
2578 new_vendor, new_product, new_revision, capacity_string);
2579
2580 free(new_revision);
2581 free(new_product);
2582 free(new_vendor);
2583 }
2584