main.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* 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:
* <stdio.h> C Standard I/O function definitions
* <string.h> C string-handling definitions
* <errno.h> UNIX error-code "errno" definitions
* <fmtmsg.h> Standard Message definitions
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fmtmsg.h>
/*
* 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 <str>. 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 <str> 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
* <kywd>,<sev>,<printstring> 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 <kywd>,... */
if (*tokend == ',') {
kywd = current;
*tokend = '\0';
/* Look for <kywd>,<sev>,... */
current = tokend + 1;
if (*(tokend = exttok(current, ":,")) == ',') {
valstr = current;
*tokend = '\0';
current = tokend+1;
prstr = current;
/* Make sure <sev> > 4 */
val = (int) strtol(noesc(valstr), &p, 0);
if ((val > 4) && (p == tokend)) {
/*
* Found <kywd>,<sev>,<printstring>.
* 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:
*/
main(argc, argv)
int argc; /* Argument count */
char *argv[]; /* Pointers to arguments */
{
/* 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);
}
} /* <severity> is not one of the standard severities */
} /* <severity> 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;
}
exit(exitval);
return(exitval);
}