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