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