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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 23 /* 24 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 29 /* All Rights Reserved */ 30 31 #pragma ident "%Z%%M% %I% %E% SMI" 32 33 34 /* 35 * fmtmsg.c 36 * 37 * Contains: 38 * fmtmsg Command that writes a message in the standard 39 * message format. May in future make these 40 * messages available for logging. 41 */ 42 43 44 /* 45 * Header files used: 46 * <stdio.h> C Standard I/O function definitions 47 * <string.h> C string-handling definitions 48 * <errno.h> UNIX error-code "errno" definitions 49 * <fmtmsg.h> Standard Message definitions 50 */ 51 52 #include <stdio.h> 53 #include <string.h> 54 #include <errno.h> 55 #include <fmtmsg.h> 56 57 58 /* 59 * Externals referenced: 60 * strtol Function that converts char strings to "long" 61 * fmtmsg Function that writes a message in standard format 62 * getenv Function that extracts an environment variable's 63 * value 64 * malloc Allocate memory from the memory pool 65 * free Frees allocated memory 66 * getopt Function that extracts arguments from the command- 67 * optarg Points to option's argument (from getopt()) 68 * optind Option's argument index (from getopt()) 69 * opterr FLAG, write error if invalid option (for getopt()) 70 * line. 71 * exit Exits the command 72 */ 73 74 extern long strtol(); 75 extern int fmtmsg(); 76 extern char *getenv(); 77 extern void *malloc(); 78 extern void free(); 79 extern int getopt(); 80 extern char *optarg; 81 extern int optind; 82 extern int opterr; 83 extern void exit(); 84 85 /* 86 * Local definitions 87 */ 88 89 /* 90 * Local constants 91 */ 92 93 94 /* 95 * Boolean constants 96 * TRUE Boolean value for "true" (any bits on) 97 * FALSE Boolean value for "false" (all bits off) 98 */ 99 100 #ifndef FALSE 101 #define FALSE (0) 102 #endif 103 104 #ifndef TRUE 105 #define TRUE (1) 106 #endif 107 108 109 #define CLASS (MM_PRINT|MM_SOFT|MM_NRECOV|MM_UTIL) 110 #define BIGUSAGE "fmtmsg [-a action] [-c class] [-l label] [-s severity] [-t tag]\n [-u subclass[,subclass[,...]]] [text]\n" 111 112 113 /* 114 * Local data-type definitions 115 */ 116 117 /* 118 * Structure used for tables containing keywords and integer values 119 */ 120 121 struct sev_info { 122 char *keyword; 123 int value; 124 }; 125 126 127 /* 128 * Structure used for tables containing keywords, long values 129 */ 130 131 struct class_info { 132 char *keyword; 133 long value; 134 long conflict; 135 }; 136 137 138 /* 139 * Severity string structure 140 * 141 * struct sevstr 142 * sevvalue Value of the severity-level being defined 143 * sevkywd Keyword identifying the severity 144 * sevprptr Pointer to the string associated with the value 145 * sevnext Pointer to the next value in the list. 146 */ 147 148 struct sevstr { 149 int sevvalue; 150 char *sevkywd; 151 char *sevprstr; 152 struct sevstr *sevnext; 153 }; 154 155 156 /* 157 * Local static data 158 */ 159 160 161 /* 162 * Table contains the keywords for the classes of a message 163 */ 164 165 static struct class_info classes[] = { 166 167 {"hard", MM_HARD, MM_SOFT|MM_FIRM}, /* hardware */ 168 {"soft", MM_SOFT, MM_HARD|MM_FIRM}, /* software */ 169 {"firm", MM_FIRM, MM_SOFT|MM_FIRM}, /* firmware */ 170 171 {(char *) NULL, 0L, 0L} /* end of list */ 172 173 }; 174 175 176 /* 177 * Table contains the keywords for the subclasses for a message 178 */ 179 180 static struct class_info subclasses[] = { 181 182 {"appl", MM_APPL, MM_UTIL|MM_OPSYS}, /* Application */ 183 {"util", MM_UTIL, MM_APPL|MM_OPSYS}, /* Utility */ 184 {"opsys", MM_OPSYS, MM_APPL|MM_UTIL}, /* Operating System */ 185 186 {"recov", MM_RECOVER, MM_NRECOV}, /* Recoverable */ 187 {"nrecov", MM_NRECOV, MM_RECOVER}, /* Non-recoverable */ 188 189 {"print", MM_PRINT, 0L}, /* Write message to stderr */ 190 {"console", MM_CONSOLE, 0L}, /* Write message on /dev/console */ 191 {(char *) NULL, 0L, 0L} /* End of list */ 192 193 }; 194 195 196 /* 197 * Table contains the keywords for the standard severities of a message. 198 * User may supply more through the SEV_LEVEL environment variable. 199 */ 200 201 static struct sev_info severities[] = { 202 {"halt", MM_HALT}, /* halt */ 203 {"error", MM_ERROR}, /* error */ 204 {"warn", MM_WARNING}, /* warn */ 205 {"info", MM_INFO}, /* info */ 206 {(char *) NULL, 0} /* end of list */ 207 }; 208 209 210 /* 211 * Buffers used by the command 212 */ 213 214 static char labelbuf[128]; /* Buf for message label */ 215 static char msgbuf[256]; /* Buf for messages */ 216 217 /* 218 * static char *exttok(str, delims) 219 * char *str 220 * char *delims 221 * 222 * This function examines the string pointed to by "str", looking 223 * for the first occurrence of any of the characters in the string 224 * whose address is "delims". It returns the address of that 225 * character or (char *) NULL if there was nothing to search. 226 * 227 * Arguments: 228 * str Address of the string to search 229 * delims Address of the string containing delimiters 230 * 231 * Returns: char * 232 * Returns the address of the first occurrence of any of the characters 233 * in "delim" in the string "str" (incl '\0'). If there was nothing 234 * to search, the function returns (char *) NULL. 235 * 236 * Notes: 237 * - This function is needed because strtok() can't be used inside a 238 * function. Besides, strtok() is destructive in the string, which 239 * is undesirable in many circumstances. 240 * - This function understands escaped delimiters as non-delimiters. 241 * Delimiters are escaped by preceding them with '\' characters. 242 * The '\' character also must be escaped. 243 */ 244 245 static char * 246 exttok(tok, delims) 247 char *tok; /* Ptr to the token we're parsing */ 248 char *delims; /* Ptr to string with delimiters */ 249 { 250 251 /* Automatic Data */ 252 char *tokend; /* Ptr to the end of the token */ 253 char *p, *q; /* Temp pointers */ 254 255 256 /* Algorithm: 257 * 1. Get the starting address (new string or where we 258 * left off). If nothing to search, return (char *) NULL 259 * 2. Find the end of the string 260 * 3. Look for the first unescaped delimiter closest to the 261 * beginning of the string 262 * 4. Remember where we left off 263 * 5. Return a pointer to the delimiter we found 264 */ 265 266 /* Begin at the beginning, if any */ 267 if (tok == (char *) NULL) { 268 return ((char *) NULL); 269 } 270 271 /* Find end of the token string */ 272 tokend = tok + strlen(tok); 273 274 /* Look for the 1st occurrence of any delimiter */ 275 for (p = delims ; *p != '\0' ; p++) { 276 for (q = strchr(tok, *p) ; q && (q != tok) && (*(q-1) == '\\') ; q = strchr(q+1, *p)) ; 277 if (q && (q < tokend)) tokend = q; 278 } 279 280 /* Done */ 281 return(tokend); 282 } 283 284 /* 285 * char *noesc(str) 286 * 287 * This function squeezes out all of the escaped character sequences 288 * from the string <str>. It returns a pointer to that string. 289 * 290 * Arguments: 291 * str char * 292 * The string that is to have its escaped characters removed. 293 * 294 * Returns: char * 295 * This function returns its argument <str> always. 296 * 297 * Notes: 298 * This function potentially modifies the string it is given. 299 */ 300 301 char * 302 noesc(str) 303 char *str; /* String to remove escaped characters from */ 304 { 305 char *p; /* Temp string pointer */ 306 char *q; /* Temp string pointer */ 307 308 /* Look for an escaped character */ 309 p = str; 310 while (*p && (*p != '\\')) p++; 311 312 313 /* 314 * If there was at least one, squeeze them out 315 * Otherwise, don't touch the argument string 316 */ 317 318 if (*p) { 319 q = p++; 320 while (*q++ = *p++) if (*p == '\\') p++; 321 } 322 323 /* Finished. Return our argument */ 324 return(str); 325 } 326 327 /* 328 * struct sevstr *getauxsevs(ptr) 329 * 330 * Parses a string that is in the format of the severity definitions. 331 * Returns a pointer to a (malloc'd) structure that contains the 332 * definition, or (struct sevstr *) NULL if none was parsed. 333 * 334 * Arguments: 335 * ptr char * 336 * References the string from which data is to be extracted. 337 * If (char *) NULL, continue where we left off. Otherwise, 338 * start with the string referenced by ptr. 339 * 340 * Returns: struct sevstr * 341 * A pointer to a malloc'd structure containing the severity definition 342 * parsed from string, or (struct sevstr *) NULL if none. 343 * 344 * Notes: 345 * - This function is destructive to the string referenced by its argument. 346 */ 347 348 349 /* Static data */ 350 static char *leftoff = (char *) NULL; 351 352 static struct sevstr * 353 getauxsevs(ptr) 354 char *ptr; 355 { 356 357 /* Automatic data */ 358 char *current; /* Ptr to current sev def'n */ 359 char *tokend; /* Ptr to end of current sev def'n */ 360 char *kywd; /* Ptr to extracted kywd */ 361 char *valstr; /* Ptr to extracted sev value */ 362 char *prstr; /* Ptr to extracted print str */ 363 char *p; /* Temp pointer */ 364 int val; /* Converted severity value */ 365 int done; /* Flag, sev def'n found and ok? */ 366 struct sevstr *rtnval; /* Value to return */ 367 368 369 /* Start anew or start where we left off? */ 370 current = (ptr == (char *) NULL) ? leftoff : ptr; 371 372 373 /* If nothing to parse, return (char *) NULL */ 374 if (current == (char *) NULL) { 375 return ((struct sevstr *) NULL); 376 } 377 378 379 /* 380 * Look through the string "current" for a token of the form 381 * <kywd>,<sev>,<printstring> delimited by ':' or '\0' 382 */ 383 384 /* Loop initializations */ 385 done = FALSE; 386 rtnval = (struct sevstr *) NULL; 387 while (!done) { 388 389 /* Eat leading junk */ 390 while (*(tokend = exttok(current, ":,")) == ':') { 391 current = tokend + 1; 392 } 393 394 /* If we've found a <kywd>,... */ 395 if (*tokend == ',') { 396 kywd = current; 397 *tokend = '\0'; 398 399 /* Look for <kywd>,<sev>,... */ 400 current = tokend + 1; 401 if (*(tokend = exttok(current, ":,")) == ',') { 402 valstr = current; 403 *tokend = '\0'; 404 current = tokend+1; 405 prstr = current; 406 407 /* Make sure <sev> > 4 */ 408 val = (int) strtol(noesc(valstr), &p, 0); 409 if ((val > 4) && (p == tokend)) { 410 411 /* 412 * Found <kywd>,<sev>,<printstring>. 413 * remember where we left off 414 */ 415 416 if (*(tokend = exttok(current, ":")) == ':') { 417 *tokend = '\0'; 418 leftoff = tokend + 1; 419 } else leftoff = (char *) NULL; 420 421 /* Alloc structure to contain severity definition */ 422 if (rtnval = (struct sevstr *) malloc(sizeof(struct sevstr))) { 423 424 /* Fill in structure */ 425 rtnval->sevkywd = noesc(kywd); 426 rtnval->sevvalue = val; 427 rtnval->sevprstr = noesc(prstr); 428 rtnval->sevnext = (struct sevstr *) NULL; 429 } 430 431 done = TRUE; 432 433 } else { 434 435 /* Invalid severity value, eat thru end of token */ 436 current = tokend; 437 if (*(tokend = exttok(prstr, ":")) == ':') 438 current++; 439 } 440 441 } else { 442 443 /* Invalid severity definition, eat thru end of token */ 444 current = tokend; 445 if (*tokend == ':') 446 current++; 447 } 448 449 } else { 450 451 /* End of string found */ 452 done = TRUE; 453 leftoff = (char *) NULL; 454 } 455 456 } /* while (!done) */ 457 458 /* Finished */ 459 return(rtnval); 460 } 461 462 /* 463 * fmtmsg [-a action] [-c classification] [-l label] [-s severity] [-t tag] 464 * [-u subclass[,subclass[,...]]] [text] 465 * 466 * Function: 467 * Writes a message in the standard format. Typically used by shell 468 * scripts to write error messages to the user. 469 * 470 * Arguments: 471 * text String that is the text of the message 472 * 473 * Options: 474 * -a action String that describes user action to take to 475 * correct the situation 476 * -c classification Keyword that identifies the type of the message 477 * -l label String that identifies the source of the message 478 * -s severity Keyword that identifies the severity of the message 479 * -t tag String that identifies the message (use unclear) 480 * -u sub_classes Comma-list of keywords that refines the type of 481 * the message 482 * 483 * Environment Variables Used: 484 * MSGVERB Defines the pieces of a message the user expects 485 * to see. It is a list of keywords separated by 486 * colons (':'). 487 * SEV_LEVEL Defines a list of auxiliary severity keywords, values, 488 * and print-strings. It is a list of fields separated 489 * by colons (':'). Each field consists of three 490 * elements, keyword, value (in octal, hex, or decimal), 491 * and print-string, separated by commas (','). 492 * 493 * Needs: 494 * 495 * Open Issues: 496 */ 497 498 int 499 main(int argc, char **argv) 500 { 501 502 /* Local automatic data */ 503 504 long class; /* Classification (built) */ 505 506 int severity; /* User specified severity */ 507 int msgrtn; /* Value returned by fmtmsg() */ 508 int optchar; /* Opt char on cmdline */ 509 int exitval; /* Value to return */ 510 511 int found; /* FLAG, kywd found yet? */ 512 int errflg; /* FLAG, error seen in cmd */ 513 int a_seen; /* FLAG, -a option seen */ 514 int c_seen; /* FLAG, -c option seen */ 515 int l_seen; /* FLAG, -l option seen */ 516 int s_seen; /* FLAG, -s option seen */ 517 int t_seen; /* FLAG, -t option seen */ 518 int u_seen; /* FLAG, -u option seen */ 519 int text_seen; /* FLAG, text seen */ 520 521 char *text; /* Ptr to user's text */ 522 char *label; /* Ptr to user's label */ 523 char *tag; /* Ptr to user's tag */ 524 char *action; /* Ptr to user's action str */ 525 char *sstr; /* Ptr to -s (severity) arg */ 526 char *ustr; /* Ptr to -u (subclass) arg */ 527 char *cstr; /* Ptr to -c (class) arg */ 528 char *sevstrval; /* Ptr to SEV_LEVEL argument */ 529 char *sevval; /* Ptr to temp SEV_LEVEL arg */ 530 char *tokenptr; /* Ptr to current token */ 531 char *cmdname; /* Ptr to base command name */ 532 char *p; /* Multipurpose ptr */ 533 534 struct class_info *class_info; /* Ptr to class/subclass info structure */ 535 struct sev_info *sev_info; /* Ptr to severity info struct */ 536 struct sevstr *penvsev; /* Ptr to SEV_LEVEL values */ 537 538 539 540 /* 541 * fmtmsg 542 */ 543 544 545 /* Initializations */ 546 547 548 /* Extract the base command name from the command */ 549 if ((p = strrchr(argv[0], '/')) == (char *) NULL) 550 cmdname = argv[0]; 551 else 552 cmdname = p+1; 553 554 /* Build the label for messages from "fmtmsg" */ 555 (void) snprintf(labelbuf, sizeof (labelbuf), "UX:%s", cmdname); 556 557 558 /* 559 * Extract arguments from the command line 560 */ 561 562 /* Initializations */ 563 564 opterr = 0; /* Disable messages from getopt() */ 565 errflg = FALSE; /* No errors seen yet */ 566 567 a_seen = FALSE; /* No action (-a) text seen yet */ 568 c_seen = FALSE; /* No classification (-c) seen yet */ 569 l_seen = FALSE; /* No label (-l) seen yet */ 570 s_seen = FALSE; /* No severity (-s) seen yet */ 571 t_seen = FALSE; /* No tag (-t) seen yet */ 572 u_seen = FALSE; /* No subclass (-u) seen yet */ 573 text_seen = FALSE; /* No text seen yet */ 574 575 576 /* 577 * If only the command name was used, write out a usage string to 578 * the standard output file. 579 */ 580 581 if (argc == 1) { 582 (void) fputs(BIGUSAGE, stderr); 583 exit(0); 584 } 585 586 587 /* Parce command line */ 588 while (((optchar = getopt(argc, argv, "a:c:l:s:t:u:")) != EOF) && 589 !errflg) { 590 591 switch(optchar) { 592 593 case 'a': /* -a actiontext */ 594 if (!a_seen) { 595 action = optarg; 596 a_seen = TRUE; 597 } else errflg = TRUE; 598 break; 599 600 case 'c': /* -c classification */ 601 if (!c_seen) { 602 cstr = optarg; 603 c_seen = TRUE; 604 } else errflg = TRUE; 605 break; 606 607 case 'l': /* -l label */ 608 if (!l_seen) { 609 label = optarg; 610 l_seen = TRUE; 611 } else errflg = TRUE; 612 break; 613 614 case 's': /* -s severity */ 615 if (!s_seen) { 616 sstr = optarg; 617 s_seen = TRUE; 618 } else errflg = TRUE; 619 break; 620 621 case 't': /* -t tag */ 622 if (!t_seen) { 623 tag = optarg; 624 t_seen = TRUE; 625 } else errflg = TRUE; 626 break; 627 628 case 'u': /* -u subclasslist */ 629 if (!u_seen) { 630 ustr = optarg; 631 u_seen = TRUE; 632 } else errflg = TRUE; 633 break; 634 635 case '?': /* -? or unknown option */ 636 default: 637 errflg = TRUE; 638 break; 639 640 } /* esac */ 641 } 642 643 644 /* Get the text */ 645 if (!errflg) { 646 if (argc == (optind+1)) { 647 text = argv[optind]; 648 text_seen = TRUE; 649 } 650 else if (argc != optind) { 651 errflg = TRUE; 652 } 653 } 654 655 656 /* Report syntax errors */ 657 if (errflg) { 658 (void) fputs(BIGUSAGE, stderr); 659 exit(1); 660 } 661 662 663 /* 664 * Classification. 665 */ 666 667 class = 0L; 668 if (c_seen) { 669 670 /* Search for keyword in list */ 671 for (class_info = &classes[0] ; 672 (class_info->keyword != (char *) NULL) && 673 (strcmp(cstr, class_info->keyword)) ; 674 class_info++) ; 675 676 /* If invalid (keyword unknown), write a message and exit */ 677 if (class_info->keyword == (char *) NULL) { 678 (void) snprintf(msgbuf, sizeof (msgbuf), 679 "Invalid class: %s", cstr); 680 (void) fmtmsg(CLASS, labelbuf, MM_ERROR, msgbuf, 681 MM_NULLACT, MM_NULLTAG); 682 exit(1); 683 } 684 685 /* Save classification */ 686 class = class_info->value; 687 688 } 689 690 691 /* 692 * Subclassification. 693 */ 694 695 if (u_seen) { 696 697 errflg = FALSE; 698 p = strcpy(malloc((unsigned int) strlen(ustr)+1), ustr); 699 if ((tokenptr = strtok(p, ",")) == (char *) NULL) errflg = TRUE; 700 else do { 701 702 /* Got a keyword. Look for it in keyword list */ 703 for (class_info = subclasses ; 704 (class_info->keyword != (char *) NULL) && 705 (strcmp(tokenptr, class_info->keyword) != 0) ; 706 class_info++) ; 707 708 /* If found in list and no conflict, remember in class */ 709 if ((class_info->keyword != (char *) NULL) && ((class & class_info->conflict) == 0L)) 710 class |= class_info->value; 711 else 712 errflg = TRUE; 713 714 } while (!errflg && ((tokenptr = strtok((char *) NULL, ",")) != (char *) NULL)) ; 715 716 if (errflg) { 717 (void) snprintf(msgbuf, sizeof (msgbuf), 718 "Invalid subclass: %s", ustr); 719 (void) fmtmsg(CLASS, labelbuf, MM_ERROR, msgbuf, 720 MM_NULLACT, MM_NULLTAG); 721 exit(1); 722 } 723 724 } 725 726 if (!c_seen & !u_seen) class = MM_NULLMC; 727 728 729 730 /* 731 * Severity. 732 */ 733 734 if (s_seen) { 735 736 /* If the severity is specified as a number, use that value */ 737 severity = strtol(sstr, &p, 10); 738 if (*p || (strlen(sstr) == 0)) { 739 740 /* Look for the standard severities */ 741 for (sev_info = severities ; 742 (sev_info->keyword != (char *) NULL) && 743 (strcmp(sstr, sev_info->keyword)) ; 744 sev_info++) ; 745 746 /* 747 * If the "severity" argument is one of the standard keywords, 748 * remember it for fmtmsg(). Otherwise, look at the SEV_LEVEL 749 * environment variable for severity extensions. 750 */ 751 752 /* If the keyword is one of the standard ones, save severity */ 753 if (sev_info->keyword != (char *) NULL) severity = sev_info->value; 754 755 else { 756 757 /* 758 * Severity keyword may be one of the extended set, if any. 759 */ 760 761 /* Get the value of the SEV_LEVEL environment variable */ 762 found = FALSE; 763 if ((sevstrval = getenv(SEV_LEVEL)) != (char *) NULL) { 764 sevval = (char *) malloc((unsigned int) strlen(sevstrval)+1); 765 penvsev = getauxsevs(strcpy(sevval, sevstrval)); 766 if (penvsev != (struct sevstr *) NULL) do { 767 if (strcmp(penvsev->sevkywd, sstr) == 0) { 768 severity = penvsev->sevvalue; 769 found = TRUE; 770 } 771 else { 772 free(penvsev); 773 penvsev = getauxsevs((char *) NULL); 774 } 775 } while (!found && (penvsev != (struct sevstr *) NULL)); 776 777 if (found) free(penvsev); 778 free(sevval); 779 } 780 781 if (!found) { 782 (void) snprintf(msgbuf, sizeof (msgbuf), 783 "Invalid severity: %s", sstr); 784 (void) fmtmsg(CLASS, labelbuf, MM_ERROR, msgbuf, 785 MM_NULLACT, MM_NULLTAG); 786 exit(1); 787 } 788 789 } /* <severity> is not one of the standard severities */ 790 791 } /* <severity> is not numeric */ 792 793 } /* if (s_seen) */ 794 795 else severity = MM_NULLSEV; 796 797 798 /* 799 * Other options 800 */ 801 802 if (!a_seen) action = MM_NULLACT; 803 if (!l_seen) label = MM_NULLLBL; 804 if (!t_seen) tag = MM_NULLTAG; 805 if (!text_seen) text = MM_NULLTXT; 806 807 808 /* 809 * Write the message 810 */ 811 812 msgrtn = fmtmsg(class, label, severity, text, action ,tag); 813 814 815 /* 816 * Return appropriate value to the shell (or wherever) 817 */ 818 819 exitval = 0; 820 if (msgrtn == MM_NOTOK) exitval = 32; 821 else { 822 if (msgrtn & MM_NOMSG) exitval += 2; 823 if (msgrtn & MM_NOCON) exitval += 4; 824 } 825 826 return(exitval); 827 } 828