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