8354 sync regcomp(3C) with upstream (fix make catalog)
[unleashed/tickless.git] / usr / src / cmd / format / misc.c
blob785bf642bf3a751052133a8f165fa5525cb460bd
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
26 * This file contains miscellaneous routines.
28 #include "global.h"
30 #include <stdlib.h>
31 #include <signal.h>
32 #include <malloc.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <sys/ioctl.h>
38 #include <sys/fcntl.h>
39 #include <sys/time.h>
40 #include <ctype.h>
41 #include <termio.h>
42 #include "misc.h"
43 #include "analyze.h"
44 #include "label.h"
45 #include "startup.h"
47 #ifdef __STDC__
49 /* Function prototypes for ANSI C Compilers */
50 static void cleanup(int sig);
52 #else /* __STDC__ */
54 /* Function prototypes for non-ANSI C Compilers */
55 static void cleanup();
57 #endif /* __STDC__ */
59 struct env *current_env = NULL; /* ptr to current environment */
60 static int stop_pending = 0; /* ctrl-Z is pending */
61 struct ttystate ttystate; /* tty info */
62 static int aborting = 0; /* in process of aborting */
65 * For 4.x, limit the choices of valid disk names to this set.
67 static char *disk_4x_identifiers[] = { "sd", "id"};
68 #define N_DISK_4X_IDS (sizeof (disk_4x_identifiers)/sizeof (char *))
72 * This is the list of legal inputs for all yes/no questions.
74 char *confirm_list[] = {
75 "yes",
76 "no",
77 NULL,
81 * This routine is a wrapper for malloc. It allocates pre-zeroed space,
82 * and checks the return value so the caller doesn't have to.
84 void *
85 zalloc(count)
86 int count;
88 void *ptr;
90 if ((ptr = (void *) calloc(1, (unsigned)count)) == NULL) {
91 err_print("Error: unable to calloc more space.\n");
92 fullabort();
94 return (ptr);
98 * This routine is a wrapper for realloc. It reallocates the given
99 * space, and checks the return value so the caller doesn't have to.
100 * Note that the any space added by this call is NOT necessarily
101 * zeroed.
103 void *
104 rezalloc(ptr, count)
105 void *ptr;
106 int count;
108 void *new_ptr;
111 if ((new_ptr = (void *) realloc((char *)ptr,
112 (unsigned)count)) == NULL) {
113 err_print("Error: unable to realloc more space.\n");
114 fullabort();
116 return (new_ptr);
120 * This routine is a wrapper for free.
122 void
123 destroy_data(data)
124 char *data;
126 free((char *)data);
129 #ifdef not
131 * This routine takes the space number returned by an ioctl call and
132 * returns a mnemonic name for that space.
134 char *
135 space2str(space)
136 uint_t space;
138 char *name;
140 switch (space&SP_BUSMASK) {
141 case SP_VIRTUAL:
142 name = "virtual";
143 break;
144 case SP_OBMEM:
145 name = "obmem";
146 break;
147 case SP_OBIO:
148 name = "obio";
149 break;
150 case SP_MBMEM:
151 name = "mbmem";
152 break;
153 case SP_MBIO:
154 name = "mbio";
155 break;
156 default:
157 err_print("Error: unknown address space type encountered.\n");
158 fullabort();
160 return (name);
162 #endif /* not */
165 * This routine asks the user the given yes/no question and returns
166 * the response.
169 check(question)
170 char *question;
172 int answer;
173 u_ioparam_t ioparam;
176 * If we are running out of a command file, assume a yes answer.
178 if (option_f)
179 return (0);
181 * Ask the user.
183 ioparam.io_charlist = confirm_list;
184 answer = input(FIO_MSTR, question, '?', &ioparam,
185 (int *)NULL, DATA_INPUT);
186 return (answer);
190 * This routine aborts the current command. It is called by a ctrl-C
191 * interrupt and also under certain error conditions.
193 /*ARGSUSED*/
194 void
195 cmdabort(sig)
196 int sig;
200 * If there is no usable saved environment, gracefully exit. This
201 * allows the user to interrupt the program even when input is from
202 * a file, or if there is no current menu, like at the "Select disk:"
203 * prompt.
205 if (current_env == NULL || !(current_env->flags & ENV_USE))
206 fullabort();
209 * If we are in a critical zone, note the attempt and return.
211 if (current_env->flags & ENV_CRITICAL) {
212 current_env->flags |= ENV_ABORT;
213 return;
216 * All interruptions when we are running out of a command file
217 * cause the program to gracefully exit.
219 if (option_f)
220 fullabort();
221 fmt_print("\n");
223 * Clean up any state left by the interrupted command.
225 cleanup(sig);
227 * Jump to the saved environment.
229 longjmp(current_env->env, 0);
233 * This routine implements the ctrl-Z suspend mechanism. It is called
234 * when a suspend signal is received.
236 /*ARGSUSED*/
237 void
238 onsusp(sig)
239 int sig;
241 int fix_term;
242 #ifdef NOT_DEF
243 sigset_t sigmask;
244 #endif /* NOT_DEF */
247 * If we are in a critical zone, note the attempt and return.
249 if (current_env != NULL && current_env->flags & ENV_CRITICAL) {
250 stop_pending = 1;
251 return;
254 * If the terminal is mucked up, note that we will need to
255 * re-muck it when we start up again.
257 fix_term = ttystate.ttyflags;
258 fmt_print("\n");
260 * Clean up any state left by the interrupted command.
262 cleanup(sig);
263 #ifdef NOT_DEF
264 /* Investigate whether all this is necessary */
266 * Stop intercepting the suspend signal, then send ourselves one
267 * to cause us to stop.
269 sigmask.sigbits[0] = (ulong_t)0xffffffff;
270 if (sigprocmask(SIG_SETMASK, &sigmask, (sigset_t *)NULL) == -1)
271 err_print("sigprocmask failed %d\n", errno);
272 #endif /* NOT_DEF */
273 (void) signal(SIGTSTP, SIG_DFL);
274 (void) kill(0, SIGTSTP);
276 * PC stops here
279 * We are started again. Set us up to intercept the suspend
280 * signal once again.
282 (void) signal(SIGTSTP, onsusp);
284 * Re-muck the terminal if necessary.
286 if (fix_term & TTY_ECHO_OFF)
287 echo_off();
288 if (fix_term & TTY_CBREAK_ON)
289 charmode_on();
293 * This routine implements the timing function used during long-term
294 * disk operations (e.g. formatting). It is called when an alarm signal
295 * is received.
297 /*ARGSUSED*/
298 void
299 onalarm(sig)
300 int sig;
306 * This routine gracefully exits the program.
308 void
309 fullabort()
312 fmt_print("\n");
314 * Clean up any state left by an interrupted command.
315 * Avoid infinite loops caused by a clean-up
316 * routine failing again...
318 if (!aborting) {
319 aborting = 1;
320 cleanup(SIGKILL);
322 exit(1);
323 /*NOTREACHED*/
327 * This routine cleans up the state of the world. It is a hodge-podge
328 * of kludges to allow us to interrupt commands whenever possible.
330 * Some cleanup actions may depend on the type of signal.
332 static void
333 cleanup(int sig)
337 * Lock out interrupts to avoid recursion.
339 enter_critical();
341 * Fix up the tty if necessary.
343 if (ttystate.ttyflags & TTY_CBREAK_ON) {
344 charmode_off();
346 if (ttystate.ttyflags & TTY_ECHO_OFF) {
347 echo_on();
351 * If the defect list is dirty, write it out.
353 if (cur_list.flags & LIST_DIRTY) {
354 cur_list.flags = 0;
355 if (!EMBEDDED_SCSI)
356 write_deflist(&cur_list);
359 * If the label is dirty, write it out.
361 if (cur_flags & LABEL_DIRTY) {
362 cur_flags &= ~LABEL_DIRTY;
363 (void) write_label();
366 * If we are logging and just interrupted a scan, print out
367 * some summary info to the log file.
369 if (log_file && scan_cur_block >= 0) {
370 pr_dblock(log_print, scan_cur_block);
371 log_print("\n");
373 if (scan_blocks_fixed >= 0)
374 fmt_print("Total of %lld defective blocks repaired.\n",
375 scan_blocks_fixed);
376 if (sig != SIGSTOP) { /* Don't reset on suspend (converted to stop) */
377 scan_cur_block = scan_blocks_fixed = -1;
379 exit_critical();
383 * This routine causes the program to enter a critical zone. Within the
384 * critical zone, no interrupts are allowed. Note that calls to this
385 * routine for the same environment do NOT nest, so there is not
386 * necessarily pairing between calls to enter_critical() and exit_critical().
388 void
389 enter_critical()
393 * If there is no saved environment, interrupts will be ignored.
395 if (current_env == NULL)
396 return;
398 * Mark the environment to be in a critical zone.
400 current_env->flags |= ENV_CRITICAL;
404 * This routine causes the program to exit a critical zone. Note that
405 * calls to enter_critical() for the same environment do NOT nest, so
406 * one call to exit_critical() will erase any number of such calls.
408 void
409 exit_critical()
413 * If there is a saved environment, mark it to be non-critical.
415 if (current_env != NULL)
416 current_env->flags &= ~ENV_CRITICAL;
418 * If there is a stop pending, execute the stop.
420 if (stop_pending) {
421 stop_pending = 0;
422 onsusp(SIGSTOP);
425 * If there is an abort pending, execute the abort.
427 if (current_env == NULL)
428 return;
429 if (current_env->flags & ENV_ABORT) {
430 current_env->flags &= ~ENV_ABORT;
431 cmdabort(SIGINT);
436 * This routine turns off echoing on the controlling tty for the program.
438 void
439 echo_off()
442 * Open the tty and store the file pointer for later.
444 if (ttystate.ttyflags == 0) {
445 if ((ttystate.ttyfile = open("/dev/tty",
446 O_RDWR | O_NDELAY)) < 0) {
447 err_print("Unable to open /dev/tty.\n");
448 fullabort();
452 * Get the parameters for the tty, turn off echoing and set them.
454 if (tcgetattr(ttystate.ttyfile, &ttystate.ttystate) < 0) {
455 err_print("Unable to get tty parameters.\n");
456 fullabort();
458 ttystate.ttystate.c_lflag &= ~ECHO;
459 if (tcsetattr(ttystate.ttyfile, TCSANOW, &ttystate.ttystate) < 0) {
460 err_print("Unable to set tty to echo off state.\n");
461 fullabort();
465 * Remember that we've successfully turned
466 * ECHO mode off, so we know to fix it later.
468 ttystate.ttyflags |= TTY_ECHO_OFF;
472 * This routine turns on echoing on the controlling tty for the program.
474 void
475 echo_on()
479 * Using the saved parameters, turn echoing on and set them.
481 ttystate.ttystate.c_lflag |= ECHO;
482 if (tcsetattr(ttystate.ttyfile, TCSANOW, &ttystate.ttystate) < 0) {
483 err_print("Unable to set tty to echo on state.\n");
484 fullabort();
487 * Close the tty and mark it ok again.
489 ttystate.ttyflags &= ~TTY_ECHO_OFF;
490 if (ttystate.ttyflags == 0) {
491 (void) close(ttystate.ttyfile);
496 * This routine turns off single character entry mode for tty.
498 void
499 charmode_on()
503 * If tty unopened, open the tty and store the file pointer for later.
505 if (ttystate.ttyflags == 0) {
506 if ((ttystate.ttyfile = open("/dev/tty",
507 O_RDWR | O_NDELAY)) < 0) {
508 err_print("Unable to open /dev/tty.\n");
509 fullabort();
513 * Get the parameters for the tty, turn on char mode.
515 if (tcgetattr(ttystate.ttyfile, &ttystate.ttystate) < 0) {
516 err_print("Unable to get tty parameters.\n");
517 fullabort();
519 ttystate.vmin = ttystate.ttystate.c_cc[VMIN];
520 ttystate.vtime = ttystate.ttystate.c_cc[VTIME];
522 ttystate.ttystate.c_lflag &= ~ICANON;
523 ttystate.ttystate.c_cc[VMIN] = 1;
524 ttystate.ttystate.c_cc[VTIME] = 0;
526 if (tcsetattr(ttystate.ttyfile, TCSANOW, &ttystate.ttystate) < 0) {
527 err_print("Unable to set tty to cbreak on state.\n");
528 fullabort();
532 * Remember that we've successfully turned
533 * CBREAK mode on, so we know to fix it later.
535 ttystate.ttyflags |= TTY_CBREAK_ON;
539 * This routine turns on single character entry mode for tty.
540 * Note, this routine must be called before echo_on.
542 void
543 charmode_off()
547 * Using the saved parameters, turn char mode on.
549 ttystate.ttystate.c_lflag |= ICANON;
550 ttystate.ttystate.c_cc[VMIN] = ttystate.vmin;
551 ttystate.ttystate.c_cc[VTIME] = ttystate.vtime;
552 if (tcsetattr(ttystate.ttyfile, TCSANOW, &ttystate.ttystate) < 0) {
553 err_print("Unable to set tty to cbreak off state.\n");
554 fullabort();
557 * Close the tty and mark it ok again.
559 ttystate.ttyflags &= ~TTY_CBREAK_ON;
560 if (ttystate.ttyflags == 0) {
561 (void) close(ttystate.ttyfile);
567 * Allocate space for and return a pointer to a string
568 * on the stack. If the string is null, create
569 * an empty string.
570 * Use destroy_data() to free when no longer used.
572 char *
573 alloc_string(s)
574 char *s;
576 char *ns;
578 if (s == (char *)NULL) {
579 ns = (char *)zalloc(1);
580 } else {
581 ns = (char *)zalloc(strlen(s) + 1);
582 (void) strcpy(ns, s);
584 return (ns);
590 * This function can be used to build up an array of strings
591 * dynamically, with a trailing NULL to terminate the list.
593 * Parameters:
594 * argvlist: a pointer to the base of the current list.
595 * does not have to be initialized.
596 * size: pointer to an integer, indicating the number
597 * of string installed in the list. Must be
598 * initialized to zero.
599 * alloc: pointer to an integer, indicating the amount
600 * of space allocated. Must be initialized to
601 * zero. For efficiency, we allocate the list
602 * in chunks and use it piece-by-piece.
603 * str: the string to be inserted in the list.
604 * A copy of the string is malloc'ed, and
605 * appended at the end of the list.
606 * Returns:
607 * a pointer to the possibly-moved argvlist.
609 * No attempt to made to free unused memory when the list is
610 * completed, although this would not be hard to do. For
611 * reasonably small lists, this should suffice.
613 #define INITIAL_LISTSIZE 32
614 #define INCR_LISTSIZE 32
616 char **
617 build_argvlist(argvlist, size, alloc, str)
618 char **argvlist;
619 int *size;
620 int *alloc;
621 char *str;
623 if (*size + 2 > *alloc) {
624 if (*alloc == 0) {
625 *alloc = INITIAL_LISTSIZE;
626 argvlist = (char **)
627 zalloc(sizeof (char *) * (*alloc));
628 } else {
629 *alloc += INCR_LISTSIZE;
630 argvlist = (char **)
631 rezalloc((void *) argvlist,
632 sizeof (char *) * (*alloc));
636 argvlist[*size] = alloc_string(str);
637 *size += 1;
638 argvlist[*size] = NULL;
640 return (argvlist);
645 * Useful parsing macros
647 #define must_be(s, c) if (*s++ != c) return (0)
648 #define skip_digits(s) while (isdigit(*s)) s++
649 /* Parsing macro below is created to handle fabric devices which contains */
650 /* upper hex digits like c2t210000203708B8CEd0s0. */
651 /* To get the target id(tid) the digit and hex upper digit need to */
652 /* be processed. */
653 #define skip_digit_or_hexupper(s) while (isdigit(*s) || \
654 (isxdigit(*s) && isupper(*s))) s++
657 * Return true if a device name matches the conventions
658 * for the particular system.
661 conventional_name(char *name)
663 must_be(name, 'c');
664 skip_digits(name);
665 if (*name == 't') {
666 name++;
667 skip_digit_or_hexupper(name);
669 must_be(name, 'd');
670 skip_digits(name);
671 must_be(name, 's');
672 skip_digits(name);
673 return (*name == 0);
676 #ifdef i386
678 * Return true if a device name match the emc powerpath name scheme:
679 * emcpowerN[a-p,p0,p1,p2,p3,p4]
682 emcpower_name(char *name)
684 char *emcp = "emcpower";
685 char *devp = "/dev/dsk";
686 char *rdevp = "/dev/rdsk";
688 if (strncmp(devp, name, strlen(devp)) == 0) {
689 name += strlen(devp) + 1;
690 } else if (strncmp(rdevp, name, strlen(rdevp)) == 0) {
691 name += strlen(rdevp) + 1;
693 if (strncmp(emcp, name, strlen(emcp)) == 0) {
694 name += strlen(emcp);
695 if (isdigit(*name)) {
696 skip_digits(name);
697 if ((*name >= 'a') && (*name <= 'p')) {
698 name ++;
699 if ((*name >= '0') && (*name <= '4')) {
700 name++;
703 return (*name == '\0');
706 return (0);
708 #endif
711 * Return true if a device name matches the intel physical name conventions
712 * for the particular system.
715 fdisk_physical_name(char *name)
717 must_be(name, 'c');
718 skip_digits(name);
719 if (*name == 't') {
720 name++;
721 skip_digit_or_hexupper(name);
723 must_be(name, 'd');
724 skip_digits(name);
725 must_be(name, 'p');
726 skip_digits(name);
727 return (*name == 0);
731 * Return true if a device name matches the conventions
732 * for a "whole disk" name for the particular system.
733 * The name in this case must match exactly that which
734 * would appear in the device directory itself.
737 whole_disk_name(name)
738 char *name;
740 must_be(name, 'c');
741 skip_digits(name);
742 if (*name == 't') {
743 name++;
744 skip_digit_or_hexupper(name);
746 must_be(name, 'd');
747 skip_digits(name);
748 must_be(name, 's');
749 must_be(name, '2');
750 return (*name == 0);
755 * Return true if a name is in the internal canonical form
758 canonical_name(name)
759 char *name;
761 must_be(name, 'c');
762 skip_digits(name);
763 if (*name == 't') {
764 name++;
765 skip_digit_or_hexupper(name);
767 must_be(name, 'd');
768 skip_digits(name);
769 return (*name == 0);
774 * Return true if a name is in the internal canonical form for 4.x
775 * Used to support 4.x naming conventions under 5.0.
778 canonical4x_name(name)
779 char *name;
781 char **p;
782 int i;
784 p = disk_4x_identifiers;
785 for (i = N_DISK_4X_IDS; i > 0; i--, p++) {
786 if (match_substr(name, *p)) {
787 name += strlen(*p);
788 break;
791 if (i == 0)
792 return (0);
793 skip_digits(name);
794 return (*name == 0);
799 * Map a conventional name into the internal canonical form:
801 * /dev/rdsk/c0t0d0s0 -> c0t0d0
803 void
804 canonicalize_name(dst, src)
805 char *dst;
806 char *src;
808 char *s;
811 * Copy from the 'c' to the end to the destination string...
813 s = strchr(src, 'c');
814 if (s != NULL) {
815 (void) strcpy(dst, s);
817 * Remove the trailing slice (partition) reference
819 s = dst + strlen(dst) - 2;
820 if (*s == 's') {
821 *s = 0;
823 } else {
824 *dst = 0; /* be tolerant of garbage input */
830 * Return true if we find an occurance of s2 at the
831 * beginning of s1. We don't have to match all of
832 * s1, but we do have to match all of s2
835 match_substr(s1, s2)
836 char *s1;
837 char *s2;
839 while (*s2 != 0) {
840 if (*s1++ != *s2++)
841 return (0);
844 return (1);
849 * Dump a structure in hexadecimal, for diagnostic purposes
851 #define BYTES_PER_LINE 16
853 void
854 dump(hdr, src, nbytes, format)
855 char *hdr;
856 caddr_t src;
857 int nbytes;
858 int format;
860 int i;
861 int n;
862 char *p;
863 char s[256];
865 assert(format == HEX_ONLY || format == HEX_ASCII);
867 (void) strcpy(s, hdr);
868 for (p = s; *p; p++) {
869 *p = ' ';
872 p = hdr;
873 while (nbytes > 0) {
874 err_print("%s", p);
875 p = s;
876 n = min(nbytes, BYTES_PER_LINE);
877 for (i = 0; i < n; i++) {
878 err_print("%02x ", src[i] & 0xff);
880 if (format == HEX_ASCII) {
881 for (i = BYTES_PER_LINE-n; i > 0; i--) {
882 err_print(" ");
884 err_print(" ");
885 for (i = 0; i < n; i++) {
886 err_print("%c",
887 isprint(src[i]) ? src[i] : '.');
890 err_print("\n");
891 nbytes -= n;
892 src += n;
897 float
898 bn2mb(uint64_t nblks)
900 float n;
902 n = (float)nblks / 1024.0;
903 return ((n / 1024.0) * cur_blksz);
907 diskaddr_t
908 mb2bn(float mb)
910 diskaddr_t n;
912 n = (diskaddr_t)(mb * 1024.0 * (1024.0 / cur_blksz));
913 return (n);
916 float
917 bn2gb(uint64_t nblks)
919 float n;
921 n = (float)nblks / (1024.0 * 1024.0);
922 return ((n/1024.0) * cur_blksz);
926 float
927 bn2tb(uint64_t nblks)
929 float n;
931 n = (float)nblks / (1024.0 * 1024.0 * 1024.0);
932 return ((n/1024.0) * cur_blksz);
935 diskaddr_t
936 gb2bn(float gb)
938 diskaddr_t n;
940 n = (diskaddr_t)(gb * 1024.0 * 1024.0 * (1024.0 / cur_blksz));
941 return (n);
945 * This routine finds out the number of lines (rows) in a terminal
946 * window. The default value of TTY_LINES is returned on error.
949 get_tty_lines()
951 int tty_lines = TTY_LINES;
952 struct winsize winsize;
954 if ((option_f == (char *)NULL) && isatty(0) == 1 && isatty(1) == 1) {
956 * We have a real terminal for std input and output
958 winsize.ws_row = 0;
959 if (ioctl(1, TIOCGWINSZ, &winsize) == 0) {
960 if (winsize.ws_row > 2) {
962 * Should be atleast 2 lines, for division
963 * by (tty_lines - 1, tty_lines - 2) to work.
965 tty_lines = winsize.ws_row;
969 return (tty_lines);