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