/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 2018, Joyent, Inc. */ /* * fmtmsg.c * * Contains: * fmtmsg Command that writes a message in the standard * message format. May in future make these * messages available for logging. */ /* * Header files used: * C Standard I/O function definitions * C string-handling definitions * UNIX error-code "errno" definitions * Standard Message definitions */ #include #include #include #include /* * Externals referenced: * strtol Function that converts char strings to "long" * fmtmsg Function that writes a message in standard format * getenv Function that extracts an environment variable's * value * malloc Allocate memory from the memory pool * free Frees allocated memory * getopt Function that extracts arguments from the command- * optarg Points to option's argument (from getopt()) * optind Option's argument index (from getopt()) * opterr FLAG, write error if invalid option (for getopt()) * line. * exit Exits the command */ extern long strtol(); extern int fmtmsg(); extern char *getenv(); extern void *malloc(); extern void free(); extern int getopt(); extern char *optarg; extern int optind; extern int opterr; extern void exit(); /* * Local definitions */ /* * Local constants */ /* * Boolean constants * TRUE Boolean value for "true" (any bits on) * FALSE Boolean value for "false" (all bits off) */ #ifndef FALSE #define FALSE (0) #endif #ifndef TRUE #define TRUE (1) #endif #define CLASS (MM_PRINT|MM_SOFT|MM_NRECOV|MM_UTIL) #define BIGUSAGE "fmtmsg [-a action] [-c class] [-l label] [-s severity] [-t tag]\n [-u subclass[,subclass[,...]]] [text]\n" /* * Local data-type definitions */ /* * Structure used for tables containing keywords and integer values */ struct sev_info { char *keyword; int value; }; /* * Structure used for tables containing keywords, long values */ struct class_info { char *keyword; long value; long conflict; }; /* * Severity string structure * * struct sevstr * sevvalue Value of the severity-level being defined * sevkywd Keyword identifying the severity * sevprptr Pointer to the string associated with the value * sevnext Pointer to the next value in the list. */ struct sevstr { int sevvalue; char *sevkywd; char *sevprstr; struct sevstr *sevnext; }; /* * Local static data */ /* * Table contains the keywords for the classes of a message */ static struct class_info classes[] = { {"hard", MM_HARD, MM_SOFT|MM_FIRM}, /* hardware */ {"soft", MM_SOFT, MM_HARD|MM_FIRM}, /* software */ {"firm", MM_FIRM, MM_SOFT|MM_FIRM}, /* firmware */ {(char *) NULL, 0L, 0L} /* end of list */ }; /* * Table contains the keywords for the subclasses for a message */ static struct class_info subclasses[] = { {"appl", MM_APPL, MM_UTIL|MM_OPSYS}, /* Application */ {"util", MM_UTIL, MM_APPL|MM_OPSYS}, /* Utility */ {"opsys", MM_OPSYS, MM_APPL|MM_UTIL}, /* Operating System */ {"recov", MM_RECOVER, MM_NRECOV}, /* Recoverable */ {"nrecov", MM_NRECOV, MM_RECOVER}, /* Non-recoverable */ {"print", MM_PRINT, 0L}, /* Write message to stderr */ {"console", MM_CONSOLE, 0L}, /* Write message on /dev/console */ {(char *) NULL, 0L, 0L} /* End of list */ }; /* * Table contains the keywords for the standard severities of a message. * User may supply more through the SEV_LEVEL environment variable. */ static struct sev_info severities[] = { {"halt", MM_HALT}, /* halt */ {"error", MM_ERROR}, /* error */ {"warn", MM_WARNING}, /* warn */ {"info", MM_INFO}, /* info */ {(char *) NULL, 0} /* end of list */ }; /* * Buffers used by the command */ static char labelbuf[128]; /* Buf for message label */ static char msgbuf[256]; /* Buf for messages */ /* * static char *exttok(str, delims) * char *str * char *delims * * This function examines the string pointed to by "str", looking * for the first occurrence of any of the characters in the string * whose address is "delims". It returns the address of that * character or (char *) NULL if there was nothing to search. * * Arguments: * str Address of the string to search * delims Address of the string containing delimiters * * Returns: char * * Returns the address of the first occurrence of any of the characters * in "delim" in the string "str" (incl '\0'). If there was nothing * to search, the function returns (char *) NULL. * * Notes: * - This function is needed because strtok() can't be used inside a * function. Besides, strtok() is destructive in the string, which * is undesirable in many circumstances. * - This function understands escaped delimiters as non-delimiters. * Delimiters are escaped by preceding them with '\' characters. * The '\' character also must be escaped. */ static char * exttok(tok, delims) char *tok; /* Ptr to the token we're parsing */ char *delims; /* Ptr to string with delimiters */ { /* Automatic Data */ char *tokend; /* Ptr to the end of the token */ char *p, *q; /* Temp pointers */ /* Algorithm: * 1. Get the starting address (new string or where we * left off). If nothing to search, return (char *) NULL * 2. Find the end of the string * 3. Look for the first unescaped delimiter closest to the * beginning of the string * 4. Remember where we left off * 5. Return a pointer to the delimiter we found */ /* Begin at the beginning, if any */ if (tok == (char *) NULL) { return ((char *) NULL); } /* Find end of the token string */ tokend = tok + strlen(tok); /* Look for the 1st occurrence of any delimiter */ for (p = delims ; *p != '\0' ; p++) { for (q = strchr(tok, *p) ; q && (q != tok) && (*(q-1) == '\\') ; q = strchr(q+1, *p)) ; if (q && (q < tokend)) tokend = q; } /* Done */ return(tokend); } /* * char *noesc(str) * * This function squeezes out all of the escaped character sequences * from the string . It returns a pointer to that string. * * Arguments: * str char * * The string that is to have its escaped characters removed. * * Returns: char * * This function returns its argument always. * * Notes: * This function potentially modifies the string it is given. */ char * noesc(str) char *str; /* String to remove escaped characters from */ { char *p; /* Temp string pointer */ char *q; /* Temp string pointer */ /* Look for an escaped character */ p = str; while (*p && (*p != '\\')) p++; /* * If there was at least one, squeeze them out * Otherwise, don't touch the argument string */ if (*p) { q = p++; while (*q++ = *p++) if (*p == '\\') p++; } /* Finished. Return our argument */ return(str); } /* * struct sevstr *getauxsevs(ptr) * * Parses a string that is in the format of the severity definitions. * Returns a pointer to a (malloc'd) structure that contains the * definition, or (struct sevstr *) NULL if none was parsed. * * Arguments: * ptr char * * References the string from which data is to be extracted. * If (char *) NULL, continue where we left off. Otherwise, * start with the string referenced by ptr. * * Returns: struct sevstr * * A pointer to a malloc'd structure containing the severity definition * parsed from string, or (struct sevstr *) NULL if none. * * Notes: * - This function is destructive to the string referenced by its argument. */ /* Static data */ static char *leftoff = (char *) NULL; static struct sevstr * getauxsevs(ptr) char *ptr; { /* Automatic data */ char *current; /* Ptr to current sev def'n */ char *tokend; /* Ptr to end of current sev def'n */ char *kywd; /* Ptr to extracted kywd */ char *valstr; /* Ptr to extracted sev value */ char *prstr; /* Ptr to extracted print str */ char *p; /* Temp pointer */ int val; /* Converted severity value */ int done; /* Flag, sev def'n found and ok? */ struct sevstr *rtnval; /* Value to return */ /* Start anew or start where we left off? */ current = (ptr == (char *) NULL) ? leftoff : ptr; /* If nothing to parse, return (char *) NULL */ if (current == (char *) NULL) { return ((struct sevstr *) NULL); } /* * Look through the string "current" for a token of the form * ,, delimited by ':' or '\0' */ /* Loop initializations */ done = FALSE; rtnval = (struct sevstr *) NULL; while (!done) { /* Eat leading junk */ while (*(tokend = exttok(current, ":,")) == ':') { current = tokend + 1; } /* If we've found a ,... */ if (*tokend == ',') { kywd = current; *tokend = '\0'; /* Look for ,,... */ current = tokend + 1; if (*(tokend = exttok(current, ":,")) == ',') { valstr = current; *tokend = '\0'; current = tokend+1; prstr = current; /* Make sure > 4 */ val = (int) strtol(noesc(valstr), &p, 0); if ((val > 4) && (p == tokend)) { /* * Found ,,. * remember where we left off */ if (*(tokend = exttok(current, ":")) == ':') { *tokend = '\0'; leftoff = tokend + 1; } else leftoff = (char *) NULL; /* Alloc structure to contain severity definition */ if (rtnval = (struct sevstr *) malloc(sizeof(struct sevstr))) { /* Fill in structure */ rtnval->sevkywd = noesc(kywd); rtnval->sevvalue = val; rtnval->sevprstr = noesc(prstr); rtnval->sevnext = (struct sevstr *) NULL; } done = TRUE; } else { /* Invalid severity value, eat thru end of token */ current = tokend; if (*(tokend = exttok(prstr, ":")) == ':') current++; } } else { /* Invalid severity definition, eat thru end of token */ current = tokend; if (*tokend == ':') current++; } } else { /* End of string found */ done = TRUE; leftoff = (char *) NULL; } } /* while (!done) */ /* Finished */ return(rtnval); } /* * fmtmsg [-a action] [-c classification] [-l label] [-s severity] [-t tag] * [-u subclass[,subclass[,...]]] [text] * * Function: * Writes a message in the standard format. Typically used by shell * scripts to write error messages to the user. * * Arguments: * text String that is the text of the message * * Options: * -a action String that describes user action to take to * correct the situation * -c classification Keyword that identifies the type of the message * -l label String that identifies the source of the message * -s severity Keyword that identifies the severity of the message * -t tag String that identifies the message (use unclear) * -u sub_classes Comma-list of keywords that refines the type of * the message * * Environment Variables Used: * MSGVERB Defines the pieces of a message the user expects * to see. It is a list of keywords separated by * colons (':'). * SEV_LEVEL Defines a list of auxiliary severity keywords, values, * and print-strings. It is a list of fields separated * by colons (':'). Each field consists of three * elements, keyword, value (in octal, hex, or decimal), * and print-string, separated by commas (','). * * Needs: * * Open Issues: */ int main(int argc, char **argv) { /* Local automatic data */ long class; /* Classification (built) */ int severity; /* User specified severity */ int msgrtn; /* Value returned by fmtmsg() */ int optchar; /* Opt char on cmdline */ int exitval; /* Value to return */ int found; /* FLAG, kywd found yet? */ int errflg; /* FLAG, error seen in cmd */ int a_seen; /* FLAG, -a option seen */ int c_seen; /* FLAG, -c option seen */ int l_seen; /* FLAG, -l option seen */ int s_seen; /* FLAG, -s option seen */ int t_seen; /* FLAG, -t option seen */ int u_seen; /* FLAG, -u option seen */ int text_seen; /* FLAG, text seen */ char *text; /* Ptr to user's text */ char *label; /* Ptr to user's label */ char *tag; /* Ptr to user's tag */ char *action; /* Ptr to user's action str */ char *sstr; /* Ptr to -s (severity) arg */ char *ustr; /* Ptr to -u (subclass) arg */ char *cstr; /* Ptr to -c (class) arg */ char *sevstrval; /* Ptr to SEV_LEVEL argument */ char *sevval; /* Ptr to temp SEV_LEVEL arg */ char *tokenptr; /* Ptr to current token */ char *cmdname; /* Ptr to base command name */ char *p; /* Multipurpose ptr */ struct class_info *class_info; /* Ptr to class/subclass info structure */ struct sev_info *sev_info; /* Ptr to severity info struct */ struct sevstr *penvsev; /* Ptr to SEV_LEVEL values */ /* * fmtmsg */ /* Initializations */ /* Extract the base command name from the command */ if ((p = strrchr(argv[0], '/')) == (char *) NULL) cmdname = argv[0]; else cmdname = p+1; /* Build the label for messages from "fmtmsg" */ (void) snprintf(labelbuf, sizeof (labelbuf), "UX:%s", cmdname); /* * Extract arguments from the command line */ /* Initializations */ opterr = 0; /* Disable messages from getopt() */ errflg = FALSE; /* No errors seen yet */ a_seen = FALSE; /* No action (-a) text seen yet */ c_seen = FALSE; /* No classification (-c) seen yet */ l_seen = FALSE; /* No label (-l) seen yet */ s_seen = FALSE; /* No severity (-s) seen yet */ t_seen = FALSE; /* No tag (-t) seen yet */ u_seen = FALSE; /* No subclass (-u) seen yet */ text_seen = FALSE; /* No text seen yet */ /* * If only the command name was used, write out a usage string to * the standard output file. */ if (argc == 1) { (void) fputs(BIGUSAGE, stderr); exit(0); } /* Parce command line */ while (((optchar = getopt(argc, argv, "a:c:l:s:t:u:")) != EOF) && !errflg) { switch(optchar) { case 'a': /* -a actiontext */ if (!a_seen) { action = optarg; a_seen = TRUE; } else errflg = TRUE; break; case 'c': /* -c classification */ if (!c_seen) { cstr = optarg; c_seen = TRUE; } else errflg = TRUE; break; case 'l': /* -l label */ if (!l_seen) { label = optarg; l_seen = TRUE; } else errflg = TRUE; break; case 's': /* -s severity */ if (!s_seen) { sstr = optarg; s_seen = TRUE; } else errflg = TRUE; break; case 't': /* -t tag */ if (!t_seen) { tag = optarg; t_seen = TRUE; } else errflg = TRUE; break; case 'u': /* -u subclasslist */ if (!u_seen) { ustr = optarg; u_seen = TRUE; } else errflg = TRUE; break; case '?': /* -? or unknown option */ default: errflg = TRUE; break; } /* esac */ } /* Get the text */ if (!errflg) { if (argc == (optind+1)) { text = argv[optind]; text_seen = TRUE; } else if (argc != optind) { errflg = TRUE; } } /* Report syntax errors */ if (errflg) { (void) fputs(BIGUSAGE, stderr); exit(1); } /* * Classification. */ class = 0L; if (c_seen) { /* Search for keyword in list */ for (class_info = &classes[0] ; (class_info->keyword != (char *) NULL) && (strcmp(cstr, class_info->keyword)) ; class_info++) ; /* If invalid (keyword unknown), write a message and exit */ if (class_info->keyword == (char *) NULL) { (void) snprintf(msgbuf, sizeof (msgbuf), "Invalid class: %s", cstr); (void) fmtmsg(CLASS, labelbuf, MM_ERROR, msgbuf, MM_NULLACT, MM_NULLTAG); exit(1); } /* Save classification */ class = class_info->value; } /* * Subclassification. */ if (u_seen) { errflg = FALSE; p = strcpy(malloc((unsigned int) strlen(ustr)+1), ustr); if ((tokenptr = strtok(p, ",")) == (char *) NULL) errflg = TRUE; else do { /* Got a keyword. Look for it in keyword list */ for (class_info = subclasses ; (class_info->keyword != (char *) NULL) && (strcmp(tokenptr, class_info->keyword) != 0) ; class_info++) ; /* If found in list and no conflict, remember in class */ if ((class_info->keyword != (char *) NULL) && ((class & class_info->conflict) == 0L)) class |= class_info->value; else errflg = TRUE; } while (!errflg && ((tokenptr = strtok((char *) NULL, ",")) != (char *) NULL)) ; if (errflg) { (void) snprintf(msgbuf, sizeof (msgbuf), "Invalid subclass: %s", ustr); (void) fmtmsg(CLASS, labelbuf, MM_ERROR, msgbuf, MM_NULLACT, MM_NULLTAG); exit(1); } } if (!c_seen && !u_seen) class = MM_NULLMC; /* * Severity. */ if (s_seen) { /* If the severity is specified as a number, use that value */ severity = strtol(sstr, &p, 10); if (*p || (strlen(sstr) == 0)) { /* Look for the standard severities */ for (sev_info = severities ; (sev_info->keyword != (char *) NULL) && (strcmp(sstr, sev_info->keyword)) ; sev_info++) ; /* * If the "severity" argument is one of the standard keywords, * remember it for fmtmsg(). Otherwise, look at the SEV_LEVEL * environment variable for severity extensions. */ /* If the keyword is one of the standard ones, save severity */ if (sev_info->keyword != (char *) NULL) severity = sev_info->value; else { /* * Severity keyword may be one of the extended set, if any. */ /* Get the value of the SEV_LEVEL environment variable */ found = FALSE; if ((sevstrval = getenv(SEV_LEVEL)) != (char *) NULL) { sevval = (char *) malloc((unsigned int) strlen(sevstrval)+1); penvsev = getauxsevs(strcpy(sevval, sevstrval)); if (penvsev != (struct sevstr *) NULL) do { if (strcmp(penvsev->sevkywd, sstr) == 0) { severity = penvsev->sevvalue; found = TRUE; } else { free(penvsev); penvsev = getauxsevs((char *) NULL); } } while (!found && (penvsev != (struct sevstr *) NULL)); if (found) free(penvsev); free(sevval); } if (!found) { (void) snprintf(msgbuf, sizeof (msgbuf), "Invalid severity: %s", sstr); (void) fmtmsg(CLASS, labelbuf, MM_ERROR, msgbuf, MM_NULLACT, MM_NULLTAG); exit(1); } } /* is not one of the standard severities */ } /* is not numeric */ } /* if (s_seen) */ else severity = MM_NULLSEV; /* * Other options */ if (!a_seen) action = MM_NULLACT; if (!l_seen) label = MM_NULLLBL; if (!t_seen) tag = MM_NULLTAG; if (!text_seen) text = MM_NULLTXT; /* * Write the message */ msgrtn = fmtmsg(class, label, severity, text, action ,tag); /* * Return appropriate value to the shell (or wherever) */ exitval = 0; if (msgrtn == MM_NOTOK) exitval = 32; else { if (msgrtn & MM_NOMSG) exitval += 2; if (msgrtn & MM_NOCON) exitval += 4; } return(exitval); }