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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* Copyright (c) 1988 AT&T */ 29 /* All Rights Reserved */ 30 31 32 /* 33 * fmtmsg.c 34 * 35 * Contains: 36 * fmtmsg() Writes a message in standard format. 37 * addseverity() Adds a severity definition to the list of known 38 * severity definitions. 39 * 40 * Notes: 41 * - None of these functions can use strtok(). 42 */ 43 44 /* 45 * Header Files Referenced: 46 * <stdio.h> C Standard I/O Definitions 47 * <string.h> C string handling definitions 48 * <fcntl.h> UNIX file control definitions 49 * <errno.h> UNIX error numbers and definitions 50 * <fmtmsg.h> Global definitions for fmtmsg() 51 * <stdlib.h> miscellaneous function declarations 52 */ 53 54 #pragma weak fmtmsg = _fmtmsg 55 #pragma weak addseverity = _addseverity 56 #include "synonyms.h" 57 #include "mtlib.h" 58 #include "libc.h" 59 #include <sys/types.h> 60 #include <stddef.h> 61 #include <stdio.h> 62 #include <string.h> 63 #include <fcntl.h> 64 #include <errno.h> 65 #include <fmtmsg.h> 66 #include <stdlib.h> 67 #include <thread.h> 68 #include <synch.h> 69 #include <alloca.h> 70 71 /* 72 * External functions referenced: 73 * (Others may be defined in header files above) 74 * 75 * getenv Extracts data from the environment 76 * libc_malloc Allocates space from main memory 77 * libc_free Frees space allocated via libc_malloc() 78 * strtol Convert string to "long" 79 * clearerr Clears an error on a stream (this is to make "lint" 80 * happy) 81 */ 82 83 84 /* 85 * Local Constant Definitions 86 */ 87 88 /* 89 * Boolean constants 90 * TRUE Boolean value for "true" (any bits on) 91 * FALSE Boolean value for "false" (all bits off) 92 */ 93 94 #ifndef FALSE 95 #define FALSE (0) 96 #endif 97 98 #ifndef TRUE 99 #define TRUE (1) 100 #endif 101 102 #define MAX_MSG_SIZE 1024 103 104 /* 105 * Keywords for fields named in the MSGVERB environment variable. 106 */ 107 108 #define ST_LBL "label" 109 #define ST_SEV "severity" 110 #define ST_TXT "text" 111 #define ST_TAG "tag" 112 #define ST_ACT "action" 113 114 115 /* 116 * The following constants define the value of the "msgverb" 117 * variable. This variable tells fmtmsg() which parts of the 118 * standard message it is to display. If !(msgverb&MV_SET), 119 * fmtmsg() will interrogate the "MSGVERB" environment variable 120 * and set "msgverb" accordingly. 121 * 122 * NOTE: This means that if MSGVERB changes after the first call 123 * to fmtmsg(), it will be ignored. 124 * 125 * Constants: 126 * MV_INV Check MSGVERB environment variable (invalidates value) 127 * MV_SET MSGVERB checked, msgverb value valid 128 * MV_LBL "label" selected 129 * MV_SEV "severity" selected 130 * MV_TXT "text" selected 131 * MV_TAG "messageID" selected 132 * MV_ACT "action" selected 133 * 134 * MV_ALL All components selected 135 * MV_DFLT Default value for MSGVERB 136 */ 137 138 #define MV_INV 0 139 #define MV_SET 0x0001 140 #define MV_LBL 0x0002 141 #define MV_SEV 0x0004 142 #define MV_TXT 0x0008 143 #define MV_TAG 0x0010 144 #define MV_ACT 0x0020 145 146 #define MV_ALL (MV_LBL|MV_SEV|MV_TXT|MV_TAG|MV_ACT) 147 #define MV_DFLT (MV_LBL|MV_SEV|MV_TXT|MV_TAG|MV_ACT) 148 149 150 151 /* 152 * Strings defining the different severities of a message. 153 * Internationalization may demand that these come from the message database 154 */ 155 156 #define SV_UNK "UNKNOWN" 157 #define SV_HALT "HALT" 158 #define SV_ERROR "ERROR" 159 #define SV_WARN "WARNING" 160 #define SV_INF "INFO" 161 162 163 /* 164 * Text string if none is provided: 165 */ 166 167 #define DEFLT_TEXT "No text provided with this message" 168 169 170 /* 171 * Text string introduction for "action". This may have to come from 172 * the message database because of internationalization. 173 */ 174 175 #define ACTINTRO "TO FIX: " 176 #define ACTINTROLN 8 177 178 179 /* 180 * SEPSTR is the string that separates the "label" from what follows it, 181 * and the severity from what follows it. 182 */ 183 184 #define SEPSTR ": " 185 #define SEPSTRLN 2 186 187 188 /* 189 * Miscellaneous constants: 190 * CONNAME Filesystem entry name for the system console 191 */ 192 193 #define CONNAME "/dev/console" 194 195 /* 196 * Local data type definitions 197 */ 198 199 /* 200 * Severity string structure 201 * 202 * struct sevstr 203 * sevvalue Value of the severity-level being defined 204 * sevkywd Keyword identifying the severity 205 * sevprptr Pointer to the string associated with the value 206 * sevnext Pointer to the next value in the list. 207 * 208 * Restrictions: 209 * sevvalue Must be a non-negative integer (>=0) 210 * 211 * There are three (possibly null) lists of these structures. 212 * 1) is the list of standard severities 213 * 2) is the list of severity-levels defined by SEV_LEVEL 214 * 3) is the list of severity-levels defined by calls to 215 * addseverity() 216 */ 217 218 struct sevstr { 219 int sevvalue; 220 const char *sevkywd; 221 const char *sevprstr; 222 struct sevstr *sevnext; 223 }; 224 225 /* 226 * Local Static Data 227 * msgverb int 228 * Contains the internal representation or the 229 * MSGVERB environment variable. 230 * sevlook TRUE if fmtmsg() has to look at SEV_LEVEL the 231 * next time it is called. 232 * paugsevs struct sevstr * 233 * Head of the linked list of structures that define 234 * severities that augment the standard severities, 235 * as defined by addseverity(). 236 * penvsevs struct sevstrs * 237 * Head of the linked list of structures that define 238 * severities that augment the standard severities, 239 * as defined by SEV_LEVEL. 240 * pstdsevs struct sevstrs * 241 * Head of the linked list of structures that define 242 * the standard severities. 243 */ 244 245 static mutex_t fmt_lock = DEFAULTMUTEX; 246 247 static int msgverb = 0; 248 static int sevlook = TRUE; 249 250 static struct sevstr *paugsevs = (struct sevstr *)NULL; 251 static struct sevstr *penvsevs = (struct sevstr *)NULL; 252 253 static struct sevstr sevstrs[] = { 254 { MM_HALT, "", SV_HALT, &sevstrs[1]}, 255 { MM_ERROR, "", SV_ERROR, &sevstrs[2]}, 256 { MM_WARNING, "", SV_WARN, &sevstrs[3]}, 257 { MM_INFO, "", SV_INF, (struct sevstr *)NULL}, 258 }; 259 static struct sevstr *pstdsevs = &sevstrs[0]; 260 261 /* 262 * static char *exttok(str, delims) 263 * const char *str 264 * const char *delims 265 * 266 * This function examines the string pointed to by "str", looking 267 * for the first occurrence of any of the characters in the string 268 * whose address is "delims". It returns the address of that 269 * character or (char *)NULL if there was nothing to search. 270 * 271 * Arguments: 272 * str Address of the string to search 273 * delims Address of the string containing delimiters 274 * 275 * Returns: char * 276 * Returns the address of the first occurrence of any of the characters 277 * in "delim" in the string "str" (incl '\0'). If there was nothing 278 * to search, the function returns (char *)NULL. 279 * 280 * Notes: 281 * - This function is needed because strtok() can't be used inside a 282 * function. Besides, strtok() is destructive in the string, which 283 * is undesirable in many circumstances. 284 * - This function understands escaped delimiters as non-delimiters. 285 * Delimiters are escaped by preceding them with '\' characters. 286 * The '\' character also must be escaped. 287 */ 288 289 static char * 290 exttok(const char *tok, const char *delims) 291 { 292 char *tokend; /* Ptr to the end of the token */ 293 char *p, *q; /* Temp pointers */ 294 295 /* 296 * Algorithm: 297 * 1. Get the starting address(new string or where we 298 * left off). If nothing to search, return(char *)NULL 299 * 2. Find the end of the string 300 * 3. Look for the first unescaped delimiter closest to the 301 * beginning of the string 302 * 4. Remember where we left off 303 * 5. Return a pointer to the delimiter we found 304 */ 305 306 /* Begin at the beginning, if any */ 307 if (tok == (char *)NULL) { 308 return ((char *)NULL); 309 } 310 311 /* Find end of the token string */ 312 tokend = (char *)tok + (ptrdiff_t)strlen(tok); 313 314 /* Look for the 1st occurrence of any delimiter */ 315 for (p = (char *)delims; *p != '\0'; p++) { 316 for (q = strchr(tok, (int)*p); 317 (q != 0) && (q != tok) && (*(q - (ptrdiff_t)1) == '\\'); 318 q = strchr(q + (ptrdiff_t)1, (int)*p)) 319 ; 320 if ((q != 0) && (q < tokend)) 321 tokend = q; 322 } 323 324 /* Done */ 325 return (tokend); 326 } 327 328 /* 329 * char *noesc(str) 330 * 331 * This function squeezes out all of the escaped character sequences 332 * from the string <str>. It returns a pointer to that string. 333 * 334 * Arguments: 335 * str char * 336 * The string that is to have its escaped characters removed. 337 * 338 * Returns: char * 339 * This function returns its argument <str> always. 340 * 341 * Notes: 342 * This function potentially modifies the string it is given. 343 */ 344 345 static char * 346 noesc(char *str) 347 { 348 char *p; /* Temp string pointer */ 349 char *q; /* Temp string pointer */ 350 351 /* Look for an escaped character */ 352 p = str; 353 while (*p && (*p != '\\')) p++; 354 355 356 /* 357 * If there was at least one, squeeze them out 358 * Otherwise, don't touch the argument string 359 */ 360 361 if (*p) { 362 q = p++; 363 while (*q++ = *p++) { 364 if (*p == '\\') 365 p++; 366 } 367 } 368 369 /* Finished. Return our argument */ 370 return (str); 371 } 372 373 /* 374 * struct sevstr *getauxsevs(ptr) 375 * 376 * Parses a string that is in the format of the severity definitions. 377 * Returns a pointer to a (malloc'd) structure that contains the 378 * definition, or (struct sevstr *)NULL if none was parsed. 379 * 380 * Arguments: 381 * ptr char * 382 * References the string from which data is to be extracted. 383 * If (char *)NULL, continue where we left off. Otherwise, 384 * start with the string referenced by ptr. 385 * 386 * Returns: struct sevstr * 387 * A pointer to a malloc'd structure containing the severity definition 388 * parsed from string, or (struct sevstr *)NULL if none. 389 * 390 * Notes: 391 * - This function is destructive to the string referenced by its argument. 392 */ 393 394 /* Static data */ 395 static char *leftoff = (char *)NULL; 396 397 static struct sevstr * 398 getauxsevs(char *ptr) 399 { 400 char *current; /* Ptr to current sev def'n */ 401 char *tokend; /* Ptr to end of current sev def'n */ 402 char *kywd; /* Ptr to extracted kywd */ 403 char *valstr; /* Ptr to extracted sev value */ 404 char *prstr; /* Ptr to extracted print str */ 405 char *p; /* Temp pointer */ 406 int val; /* Converted severity value */ 407 int done; /* Flag, sev def'n found and ok? */ 408 struct sevstr *rtnval; /* Value to return */ 409 410 411 /* Start anew or start where we left off? */ 412 current = (ptr == (char *)NULL) ? leftoff : ptr; 413 414 415 /* If nothing to parse, return (char *)NULL */ 416 if (current == (char *)NULL) { 417 return ((struct sevstr *)NULL); 418 } 419 420 421 /* 422 * Look through the string "current" for a token of the form 423 * <kywd>,<sev>,<printstring> delimited by ':' or '\0' 424 */ 425 426 /* Loop initializations */ 427 done = FALSE; 428 rtnval = (struct sevstr *)NULL; 429 while (!done) { 430 /* Eat leading junk */ 431 while (*(tokend = exttok(current, ":,")) == ':') { 432 current = tokend + (ptrdiff_t)1; 433 } 434 435 /* If we've found a <kywd>,... */ 436 if (*tokend == ',') { 437 kywd = current; 438 *tokend = '\0'; 439 440 /* Look for <kywd>,<sev>,... */ 441 current = tokend + (ptrdiff_t)1; 442 if (*(tokend = exttok(current, ":,")) == ',') { 443 valstr = current; 444 *tokend = '\0'; 445 446 current = tokend + (ptrdiff_t)1; 447 prstr = current; 448 449 /* Make sure <sev> > 4 */ 450 val = (int)strtol(noesc(valstr), &p, 0); 451 if ((val > 4) && (p == tokend)) { 452 453 /* 454 * Found <kywd>,<sev>,<printstring>. 455 * remember where we left off 456 */ 457 458 if (*(tokend = 459 exttok(current, ":")) == ':') { 460 *tokend = '\0'; 461 leftoff = tokend + 462 (ptrdiff_t)1; 463 } else { 464 leftoff = (char *)NULL; 465 } 466 467 /* 468 * Alloc structure to contain 469 * severity definition 470 */ 471 rtnval = libc_malloc( 472 sizeof (struct sevstr)); 473 if (rtnval != NULL) { 474 475 /* Fill in structure */ 476 rtnval->sevkywd = noesc(kywd); 477 rtnval->sevvalue = val; 478 rtnval->sevprstr = noesc(prstr); 479 rtnval->sevnext = 480 (struct sevstr *)NULL; 481 } 482 done = TRUE; 483 } else { 484 /* 485 * Invalid severity value, 486 * eat thru end of token 487 */ 488 current = tokend; 489 if (*(tokend = exttok(prstr, ":")) == 490 ':') { 491 current++; 492 } 493 } 494 } else { 495 /* 496 * Invalid severity definition, 497 * eat thru end of token 498 */ 499 current = tokend; 500 if (*tokend == ':') 501 current++; 502 } 503 } else { 504 /* End of string found */ 505 done = TRUE; 506 leftoff = (char *)NULL; 507 } 508 } /* while (!done) */ 509 510 /* Finished */ 511 return (rtnval); 512 } 513 514 /* 515 * void msgverbset() 516 * 517 * Parces the argument of the MSGVERB environment variable and places 518 * a representation of the value of that value in "msgverb" 519 * 520 * Arguments: 521 * None: 522 * 523 * Returns: void 524 * 525 * Notes: 526 */ 527 528 static void 529 msgverbset(void) 530 { 531 char *opts; /* Pointer to MSGVERB's value */ 532 char *alloced; /* Pointer to MSGVERB's value */ 533 char *tok; /* Pointer to current token */ 534 char *tokend; /* Pointer to end of current token */ 535 char *nexttok; /* Pointer to next token */ 536 537 538 /* Rid ourselves of junk in "msgverb" */ 539 msgverb = 0; 540 541 /* Get the value of MSGVERB. If none, use default value */ 542 if ((opts = getenv(MSGVERB)) == (char *)NULL) { 543 msgverb = MV_DFLT; 544 } else { /* MSGVERB has a value. Interpret it */ 545 if ((alloced = libc_malloc(strlen(opts) + 1)) == NULL) { 546 msgverb = MV_DFLT; 547 } else { 548 /* Make a copy of the value of MSGVERB */ 549 nexttok = strcpy(alloced, opts); 550 551 /* Parse the options given by the user */ 552 while ((tok = nexttok) != (char *)NULL) { 553 /* 554 * Find end of the next token and squeeze 555 * out escaped characters 556 */ 557 tokend = exttok(tok, ":"); 558 tok = noesc(tok); 559 560 /* Delimit token and mark next, if any */ 561 if (*tokend == ':') { 562 nexttok = tokend + (ptrdiff_t)1; 563 *tokend = '\0'; 564 } else { 565 nexttok = (char *)NULL; 566 } 567 568 /* Check for "text" */ 569 if (strcmp(tok, ST_TXT) == 0) { 570 msgverb |= MV_TXT; 571 572 /* Check for "label" */ 573 } else if (strcmp(tok, ST_LBL) == 0) { 574 msgverb |= MV_LBL; 575 576 /* Check for "action */ 577 } else if (strcmp(tok, ST_ACT) == 0) { 578 msgverb |= MV_ACT; 579 580 /* Check for "severity" */ 581 } else if (strcmp(tok, ST_SEV) == 0) { 582 msgverb |= MV_SEV; 583 584 /* Check for "tag" */ 585 } else if (strcmp(tok, ST_TAG) == 0) { 586 msgverb |= MV_TAG; 587 588 /* Unknown, ignore MSGVERB value */ 589 } else { 590 msgverb = MV_DFLT; 591 nexttok = (char *)NULL; 592 } 593 } /* do while */ 594 595 /* 596 * Use default if no keywords on MSGVERB 597 * environment variable 598 */ 599 if (msgverb == 0) 600 msgverb = MV_DFLT; 601 602 /* Free allocated space */ 603 libc_free(alloced); 604 } 605 } 606 /* Finished */ 607 /* return; */ 608 } 609 610 /* 611 * void sevstrset() 612 * 613 * This function builds a structure containing auxillary severity 614 * definitions. 615 * 616 * Arguments: None 617 * 618 * Returns: Void 619 */ 620 621 static char *sevspace = (char *)NULL; 622 623 static void 624 sevstrset(void) 625 { 626 struct sevstr *plast; 627 struct sevstr *psev; 628 char *value; 629 630 631 /* Look for SEV_LEVEL definition */ 632 if ((value = getenv(SEV_LEVEL)) != (char *)NULL) { 633 634 /* Allocate space and make a copy of the value of SEV_LEVEL */ 635 if ((sevspace = libc_malloc(strlen(value) + 1)) != NULL) { 636 (void) strcpy(sevspace, value); 637 638 /* Continue for all severity descriptions */ 639 psev = getauxsevs(sevspace); 640 plast = (struct sevstr *)NULL; 641 if (psev != (struct sevstr *)NULL) { 642 penvsevs = psev; 643 plast = psev; 644 while (psev = getauxsevs((char *)NULL)) { 645 plast->sevnext = psev; 646 plast = psev; 647 } 648 } 649 } /* if sevspace != (char *)NULL */ 650 } /* if value != (char *)NULL */ 651 } 652 653 /* 654 * int addseverity(value, string) 655 * int value Value of the severity 656 * const char *string Print-string for the severity 657 * 658 * Arguments: 659 * value int 660 * The integer value of the severity being added 661 * string char * 662 * A pointer to the character-string to be printed 663 * whenever a severity of "value" is printed 664 * 665 * Returns: int 666 * Zero if successful, -1 if failed. The function can fail under 667 * the following circumstances: 668 * - libc_malloc() fails 669 * - The "value" is one of the reserved values. 670 * 671 * This function permits C applications to define severity-levels 672 * that augment the standard levels and those defined by the 673 * SEV_LEVEL environment variable. 674 */ 675 676 int 677 addseverity(int value, const char *string) 678 { 679 struct sevstr *p; /* Temp ptr to severity structs */ 680 struct sevstr *q; /* Temp ptr(follower) to severity */ 681 int found; /* FLAG, element found in the list */ 682 int rtnval; /* Value to return to the caller */ 683 684 /* Make sure we're not trying to redefine one of the reserved values */ 685 if (value <= 4) { 686 errno = EINVAL; 687 return (-1); 688 } 689 690 lmutex_lock(&fmt_lock); 691 692 /* Make sure we've interpreted SEV_LEVEL */ 693 694 if (sevlook) { 695 sevstrset(); 696 sevlook = FALSE; 697 } 698 699 700 /* 701 * Leaf through the list. We may be redefining or removing a 702 * definition 703 */ 704 q = (struct sevstr *)NULL; 705 found = FALSE; 706 for (p = paugsevs; !found && (p != (struct sevstr *)NULL); 707 p = p->sevnext) { 708 if (p->sevvalue == value) { 709 /* We've a match. Remove or modify the entry */ 710 if (string == (char *)NULL) { 711 if (q == (struct sevstr *)NULL) { 712 paugsevs = p->sevnext; 713 } else { 714 q->sevnext = p->sevnext; 715 } 716 libc_free(p); 717 } else { 718 p->sevprstr = string; 719 } 720 found = TRUE; 721 } 722 q = p; 723 } 724 725 /* Adding a definition */ 726 if (!found && (string != (char *)NULL)) { 727 /* Allocate space for the severity structure */ 728 if ((p = libc_malloc(sizeof (struct sevstr))) == NULL) { 729 lmutex_unlock(&fmt_lock); 730 return (-1); 731 } 732 733 /* 734 * Fill in the new structure with the data supplied and add to 735 * the head of the augmented severity list. 736 */ 737 738 p->sevkywd = (char *)NULL; 739 p->sevprstr = string; 740 p->sevvalue = value; 741 p->sevnext = paugsevs; 742 paugsevs = p; 743 744 /* Successfully added a new severity */ 745 rtnval = 0; 746 } else if (string == (char *)NULL) { 747 /* Attempting to undefined a non-defined severity */ 748 rtnval = -1; 749 errno = EINVAL; 750 } else { 751 /* Successfully redefined a severity */ 752 rtnval = 0; 753 } 754 /* Finished, successful */ 755 lmutex_unlock(&fmt_lock); 756 return (rtnval); 757 } 758 759 /* 760 * Utility function for converting an integer to a string, avoiding stdio. 761 */ 762 static void 763 itoa(int n, char *s) 764 { 765 char buf[12]; /* 32 bits fits in 10 decimal digits */ 766 char *cp = buf; 767 uint_t un = (n < 0)? -n : n; 768 769 do { 770 *cp++ = "0123456789"[un % 10]; 771 un /= 10; 772 } while (un); 773 774 if (n < 0) 775 *s++ = '-'; 776 777 do { 778 *s++ = *--cp; 779 } while (cp > buf); 780 781 *s = '\0'; 782 } 783 784 /* 785 * void writemsg(buf, size, verbosity, label, severity, text, action, tag) 786 * 787 * Arguments: 788 * char *buf The buffer in which to format the message 789 * size_t size The size of the buffer 790 * int verbosity A bit-string that indicates which components 791 * are to be written 792 * const char *label The address of the label-component 793 * int severity The severity value of the message 794 * const char *text The address of the text-component 795 * const char *action The address of the action-component 796 * const char *tag The address of the tag-component 797 * 798 * This function formats the message consisting of the label-component, 799 * severity-component, text-component, action-component, and tag- 800 * component into the provided buffer. The "verbosity" argument 801 * tells which components can be selected. Any or all of the 802 * components can be their null-values. 803 * 804 * Returns: void 805 * 806 * Notes: 807 */ 808 809 static void 810 writemsg(char *buf, size_t size, 811 int verbosity, const char *label, int severity, 812 const char *text, const char *action, const char *tag) 813 { 814 struct sevstr *psev; /* Ptr for severity str list */ 815 char *p; /* General purpose pointer */ 816 char *sevpstr = NULL; /* Pointer to severity string */ 817 int l1indent; /* # chars to indent line 1 */ 818 int l2indent; /* # chars to indent line 2 */ 819 int textindent; /* # spaces to indent text */ 820 int actindent = 0; /* # spaces to indent action */ 821 int i; /* General purpose counter */ 822 int dolabel; /* TRUE if label to be written */ 823 int dotext; /* TRUE if text to be written */ 824 int dosev; /* TRUE if severity to be written */ 825 int doaction; /* TRUE if action to be written */ 826 int dotag; /* TRUE if tag to be written */ 827 char c; /* Temp, multiuse character */ 828 char sevpstrbuf[15]; /* Space for SV=%d */ 829 830 char lcllbl[MM_MXLABELLN+1]; /* Space for (possibly */ 831 /* truncated) label */ 832 char lcltag[MM_MXTAGLN+1]; /* Space for (possibly */ 833 /* truncated) tag */ 834 char *ebuf = buf + size - 2; 835 836 /* 837 * initialize variables. 838 */ 839 sevpstrbuf[0] = (char)0; 840 lcllbl[0] = (char)0; 841 lcltag[0] = (char)0; 842 843 /* 844 * Figure out what fields are to be written (all are optional) 845 */ 846 847 dolabel = (verbosity & MV_LBL) && (label != MM_NULLLBL); 848 dosev = (verbosity & MV_SEV) && (severity != MM_NULLSEV); 849 dotext = (verbosity & MV_TXT) && (text != MM_NULLTXT); 850 doaction = (verbosity & MV_ACT) && (action != MM_NULLACT); 851 dotag = (verbosity & MV_TAG) && (tag != MM_NULLTAG); 852 853 /* 854 * Figure out how much we'll need to indent the text of the message 855 */ 856 857 /* Count the label of the message, if requested */ 858 textindent = 0; 859 if (dolabel) { 860 (void) strncpy(lcllbl, label, (size_t)MM_MXLABELLN); 861 lcllbl[MM_MXLABELLN] = '\0'; 862 textindent = (int)strlen(lcllbl) + SEPSTRLN; 863 } 864 865 /* 866 * If severity req'd, determine the severity string and factor 867 * into indent count. Severity string generated by: 868 * 1. Search the standard list of severities. 869 * 2. Search the severities added by the application. 870 * 3. Search the severities added by the environment. 871 * 4. Use the default (SV=n where n is the value of the severity). 872 */ 873 874 if (dosev) { 875 /* Search the default severity definitions */ 876 psev = pstdsevs; 877 while (psev != (struct sevstr *)NULL) { 878 if (psev->sevvalue == severity) 879 break; 880 psev = psev->sevnext; 881 } 882 883 if (psev == (struct sevstr *)NULL) { 884 /* 885 * Search the severity definitions 886 * added by the application 887 */ 888 psev = paugsevs; 889 while (psev != (struct sevstr *)NULL) { 890 if (psev->sevvalue == severity) 891 break; 892 psev = psev->sevnext; 893 } 894 if (psev == (struct sevstr *)NULL) { 895 /* 896 * Search the severity definitions 897 * added by the environment 898 */ 899 psev = penvsevs; 900 while (psev != (struct sevstr *)NULL) { 901 if (psev->sevvalue == severity) 902 break; 903 psev = psev->sevnext; 904 } 905 if (psev == (struct sevstr *)NULL) { 906 /* Use default string, SV=severity */ 907 (void) strcpy(sevpstrbuf, "SV="); 908 itoa(severity, &sevpstrbuf[3]); 909 sevpstr = sevpstrbuf; 910 } else { 911 sevpstr = (char *)psev->sevprstr; 912 } 913 } else { 914 sevpstr = (char *)psev->sevprstr; 915 } 916 } else { 917 sevpstr = (char *)psev->sevprstr; 918 } 919 /* Factor into indent counts */ 920 textindent += (int)strlen(sevpstr) + SEPSTRLN; 921 } 922 923 /* 924 * Figure out the indents. 925 */ 926 927 if (doaction && dotext) { 928 if (textindent > ACTINTROLN) { 929 l1indent = 0; 930 l2indent = textindent - ACTINTROLN; 931 actindent = textindent; 932 } else { 933 l2indent = 0; 934 actindent = ACTINTROLN; 935 if (dosev || dolabel) { 936 l1indent = ACTINTROLN - textindent; 937 textindent = ACTINTROLN; 938 } else { 939 textindent = 0; 940 l1indent = 0; 941 } 942 } 943 } else { 944 l1indent = 0; 945 l2indent = 0; 946 if (doaction) { 947 actindent = textindent + ACTINTROLN; 948 } else if (dotext) { 949 actindent = 0; 950 } 951 } 952 953 /* 954 * Write the message. 955 */ 956 957 /* Write the LABEL, if requested */ 958 if (dolabel) { 959 /* Write spaces to align on the ':' char, if needed */ 960 while (--l1indent >= 0 && buf < ebuf) 961 *buf++ = ' '; 962 963 /* Write the label */ 964 buf += strlcpy(buf, lcllbl, ebuf - buf); 965 966 /* 967 * Write the separator string 968 * (if another component is to follow) 969 */ 970 if (dosev || dotext || doaction || dotag) 971 buf += strlcpy(buf, SEPSTR, ebuf - buf); 972 } 973 974 /* Write the SEVERITY, if requested */ 975 if (dosev) { 976 /* Write spaces to align on the ':' char, if needed */ 977 while (--l1indent >= 0 && buf < ebuf) 978 *buf++ = ' '; 979 980 /* Write the severity print-string */ 981 buf += strlcpy(buf, sevpstr, ebuf - buf); 982 983 /* 984 * Write the separator string 985 * (if another component is to follow) 986 */ 987 if (dotext || doaction || dotag) 988 buf += strlcpy(buf, SEPSTR, ebuf - buf); 989 } 990 991 /* Write the TEXT, if requested */ 992 if (dotext) { 993 p = (char *)text; 994 for (c = *p++; c != NULL && buf < ebuf; c = *p++) { 995 *buf++ = c; 996 if (c == '\n') { 997 for (i = 0; i < textindent && buf < ebuf; i++) 998 *buf++ = ' '; 999 } 1000 } 1001 } 1002 1003 /* 1004 * Write ACTION if requested. 1005 */ 1006 1007 if (doaction) { 1008 if (dotext && buf < ebuf) { 1009 *buf++ = '\n'; 1010 while (--l2indent >= 0 && buf < ebuf) 1011 *buf++ = ' '; 1012 } 1013 1014 /* Write the action-string's introduction */ 1015 buf += strlcpy(buf, ACTINTRO, ebuf - buf); 1016 1017 /* Write the "action" string */ 1018 p = (char *)action; 1019 for (c = *p++; c != NULL && buf < ebuf; c = *p++) { 1020 *buf++ = c; 1021 if (c == '\n') { 1022 for (i = 0; i < actindent && buf < ebuf; i++) 1023 *buf++ = ' '; 1024 } 1025 } 1026 } 1027 1028 /* 1029 * Write the TAG if requested 1030 */ 1031 1032 if (dotag) { 1033 if (doaction) 1034 buf += strlcpy(buf, " ", ebuf - buf); 1035 else if (dotext && buf < ebuf) 1036 *buf++ = '\n'; 1037 (void) strncpy(lcltag, tag, (size_t)MM_MXTAGLN); 1038 lcltag[MM_MXTAGLN] = '\0'; 1039 buf += strlcpy(buf, lcltag, ebuf - buf); 1040 } 1041 1042 /* 1043 * Write terminating newline and null byte. 1044 * We reserved space for these at the start. 1045 */ 1046 *buf++ = '\n'; 1047 *buf++ = '\0'; 1048 } 1049 1050 /* 1051 * int fmtmsg(class, label, severity, text, action, tag) 1052 * long class 1053 * const char *label 1054 * int severity 1055 * const char *text 1056 * const char *action 1057 * const char *tag 1058 * 1059 * If requested, the fmtmsg() function writes a message to the standard 1060 * error stream in the standard message format. Also if requested, it 1061 * will write a message to the system console. 1062 * 1063 * Arguments: 1064 * class Fields which classify the message for the system 1065 * logging facility 1066 * label A character-string that is printed as the "label" 1067 * of the message. Typically identifies the source 1068 * of the message 1069 * severity Identifies the severity of the message. Either one 1070 * of the standard severities, or possibly one of the 1071 * augmented severities 1072 * text Pointer to the text of the message 1073 * action Pointer to a char string that describes some type 1074 * of corrective action. 1075 * tag A character-string that is printed as the "tag" or 1076 * the message. Typically a pointer to documentation 1077 * 1078 * Returns: 1079 * -1 if nothing was generated, 0 if everything requested was 1080 * generated, or flags if partially generated. 1081 * 1082 * Needs: 1083 * - Nothing special for 4.0. 1084 */ 1085 1086 int 1087 fmtmsg(long class, const char *label, int severity, 1088 const char *text, const char *action, const char *tag) 1089 { 1090 int rtnval; /* Value to return */ 1091 FILE *console; /* Ptr to "console" stream */ 1092 char *message1; 1093 char *message2; 1094 1095 /* 1096 * Determine the "verbosity" of the message. If "msgverb" is 1097 * already set, don't interrogate the "MSGVERB" environment vbl. 1098 * If so, interrogate "MSGVERB" and do initialization stuff also. 1099 */ 1100 1101 lmutex_lock(&fmt_lock); 1102 1103 if (!(msgverb & MV_SET)) { 1104 msgverbset(); 1105 msgverb |= MV_SET; 1106 } 1107 1108 1109 /* 1110 * Extract the severity definitions from the SEV_LEVEL 1111 * environment variable and save away for later. 1112 */ 1113 1114 if (sevlook) { 1115 sevstrset(); 1116 sevlook = FALSE; 1117 } 1118 1119 1120 /* Set up the default text component [if text==(char *)NULL] */ 1121 if (text == (char *)NULL) 1122 text = DEFLT_TEXT; 1123 1124 /* Prepare the message for stderr if requested */ 1125 if (class & MM_PRINT) { 1126 message1 = alloca(MAX_MSG_SIZE); 1127 writemsg(message1, MAX_MSG_SIZE, 1128 msgverb, label, severity, text, action, tag); 1129 } 1130 1131 /* Prepare the message for the console if requested */ 1132 if (class & MM_CONSOLE) { 1133 message2 = alloca(MAX_MSG_SIZE); 1134 writemsg(message2, MAX_MSG_SIZE, 1135 MV_ALL, label, severity, text, action, tag); 1136 } 1137 1138 lmutex_unlock(&fmt_lock); 1139 1140 rtnval = MM_OK; 1141 1142 /* Write the message to stderr if requested */ 1143 if (class & MM_PRINT) { 1144 clearerr(stderr); 1145 (void) fputs(message1, stderr); 1146 if (ferror(stderr)) 1147 rtnval |= MM_NOMSG; 1148 } 1149 1150 /* Write the message to the console if requested */ 1151 if (class & MM_CONSOLE) { 1152 if ((console = fopen(CONNAME, "wF")) != NULL) { 1153 clearerr(console); 1154 (void) fputs(message2, console); 1155 if (ferror(console)) 1156 rtnval |= MM_NOCON; 1157 (void) fclose(console); 1158 } else { 1159 rtnval |= MM_NOCON; 1160 } 1161 } 1162 1163 if ((rtnval & (MM_NOCON | MM_NOMSG)) == (MM_NOCON | MM_NOMSG)) 1164 rtnval = MM_NOTOK; 1165 return (rtnval); 1166 } 1167