1    | /***************************************
2    |   $Revision: 1.8 $
3    | 
4    |   Error reporting (er) er.c - library of functions to uniformly report errors.
5    | 
6    |   Status: NOT REVUED, TESTED, 
7    | 
8    |   Design and implementation by: Marek Bukowy
9    | 
10   |   ******************/ /******************
11   |   Copyright (c) 1999                              RIPE NCC
12   |  
13   |   All Rights Reserved
14   |   
15   |   Permission to use, copy, modify, and distribute this software and its
16   |   documentation for any purpose and without fee is hereby granted,
17   |   provided that the above copyright notice appear in all copies and that
18   |   both that copyright notice and this permission notice appear in
19   |   supporting documentation, and that the name of the author not be
20   |   used in advertising or publicity pertaining to distribution of the
21   |   software without specific, written prior permission.
22   |   
23   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
25   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
26   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
27   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
28   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
29   |   ***************************************/
30   | 
31   | #define ER_IMPL
32   | #include "erroutines.h"
33   | 
34   | char *er_getsev( int sev, int mode )
35   | {
36   | int i;
37   | 
38   |   for(i=0; er_level_a[i].sev != 0; i++) {
39   | 	if (er_level_a[i].sev == sev)  {
40   | 	    break;
41   | 	}
42   |   }
43   | 
44   |   switch( mode & 0x03 ) {
45   | 	case ER_M_SEVNONE:	/* no severity indication */
46   | 		return "";	/* "" goes to program text, so returning a
47   | 				  pointer to it is OK */
48   | 	case ER_M_SEVCHAR:	/* one-letter severity indication */
49   | 		return er_level_a[i].chr;
50   | 	case ER_M_SEVLONG: 	/* long severity indication */
51   | 		return er_level_a[i].txt;
52   |   }
53   |   return "";
54   | }
55   | 
56   | char *er_getfacsym(int faccode)
57   | {
58   | int facidx;
59   | 
60   |   if( faccode != FAC_NONE  )  {
61   |     for (facidx=0; facidx<FAC_LAST; facidx++) {
62   |       if( er_main_err[facidx].code == faccode ) {
63   |         break;
64   |       }
65   |     }
66   |     return  er_main_err[facidx].name;
67   |   } 
68   |   else	return "";
69   | }
70   | 
71   | /* TWO CONSTANTS DEFINE THE LENGTH OF STRINGS HERE:
72   |    ER_MSGLEN - max length of the line to be logged
73   |    ER_ERRLEN - max length of the error message
74   | */
75   | char *er_getmsg_parts(int facwhere, int errcode, int mode, 
76   | 			char *buf, char *fmttxt, va_list args)
77   | {
78   | int fac, err, sev;
79   | int facidx, erridx;
80   | char erbuf[ER_ERRLEN], thr_str[10], *ermne;
81   | 
82   | /* init to "" */
83   | erbuf[0] = 0;
84   | ermne = "";
85   | 
86   | sev = ( errcode & 0xff000000 );		/* not shifted */
87   | fac = ( errcode & 0x00ff0000 ) >> 16;
88   | err = ( errcode & 0x0000ffff );		/* not shifted */
89   | 
90   |   for (facidx=0; facidx<FAC_LAST; facidx++) {
91   |     if( er_main_err[facidx].code == fac ) {
92   |       break;
93   |     }
94   |   }
95   | 
96   |   /* now, if we got to the last one and it's not the right one, 
97   |      the system is not configured properly */
98   |   if(facidx==FAC_LAST) {
99   |      assert( er_main_err[facidx].code == fac );	/* just bail out. */
100  |   }
101  | 
102  |   /* still alive ? OK, build the message ...*/
103  | 
104  |   /* ... using facidx/erridx if it's not a DEBUG */
105  |   if( sev != ER_SEV_D ) {
106  |     /* OK, go to the module table. bail out if not initialized */
107  |     assert( er_main_err[facidx].errs != NULL );
108  | 
109  |     for(erridx=0; er_main_err[facidx].errs[erridx].code != -1; erridx++) {
110  |       if( er_main_err[facidx].errs[erridx].code == errcode ) {
111  |        	/* FOUND! now set the error message format using facidx and erridx */
112  | 
113  |     	/* build error message with arguments */
114  |     	if( mode & ER_M_TEXTLONG ) {
115  | 	  fmttxt = er_main_err[facidx].errs[erridx].text;
116  |     	}
117  | 	/* set the mnemonic pointer if necessary */ 
118  | 	if( mode & ER_M_MNEMONIC ) {
119  | 	  ermne = er_main_err[facidx].errs[erridx].mnem;
120  |         }
121  | 	break;
122  |       }
123  |     }
124  |     /*	return ""; */
125  |     /* no, do not return: bail out if the code is not defined */
126  |     assert( er_main_err[facidx].errs[erridx].code != -1 );
127  |   }
128  |   else {
129  |     ermne = "DEBUG";
130  |   } 
131  | 
132  |   /* build the error message using vsnprintf */  
133  |   vsnprintf(erbuf, ER_ERRLEN, fmttxt, args);
134  |   
135  |   sprintf(thr_str, "%d", pthread_self() );
136  | 
137  |   /* build the actual log message */
138  |   snprintf(buf, ER_MSGLEN, "%s-%s/%s %s-%s-%s %s",
139  | 	   (mode & ER_M_PROGNAME) ? er_progname : "",
140  | 	   (mode & ER_M_PIDFULL)  ? er_pid : "",
141  | 	   (mode & ER_M_THR_ID )  ? thr_str : "",
142  | 	   (mode & ER_M_FACSYMB)  ? er_getfacsym(facwhere) : "",
143  | 	   er_getsev(sev, mode),
144  | 	   ermne,
145  | 	   erbuf
146  | 	   );	
147  |   return buf;
148  | }
149  | 
150  | void ER_setpath(er_path_t *newset)
151  | {
152  |   /* initialise the mutex if not yet initialised */
153  | 
154  |   if( er_pathlist_mutex_initialised == 0 ) {
155  |     pthread_mutex_init( &er_pathlist_mutex, NULL );
156  |   }
157  |   
158  |   pthread_mutex_lock( &er_pathlist_mutex );
159  |   memcpy( & er_provisional_struct, newset, sizeof(er_path_t));  
160  |   pthread_mutex_unlock( &er_pathlist_mutex );
161  | }
162  | 
163  | void er_logit(int facwhere, er_mask_t asp, int mode, int errcode, char *msg)
164  | {
165  |   char 	buf[ER_MSGLEN], tmbuf[32];
166  |   struct timeval tval;
167  |   struct tm tmstr;
168  | 
169  |   if ( mode & ER_M_DATETIME ) {
170  |     gettimeofday(&tval, NULL);
171  |     localtime_r( & tval.tv_sec, & tmstr);
172  | 
173  |     /*    strcpy(tmbuf, ctime(&tm)+11); */
174  |     sprintf(tmbuf, "%02d:%02d:%02d", 
175  | 	    tmstr.tm_hour, tmstr.tm_min, tmstr.tm_sec);
176  |   } else {
177  |     tmbuf[0]=0;
178  |   }
179  | 
180  |   snprintf(buf, ER_MSGLEN, "%s %s\n", tmbuf, msg );
181  |   /* OK, now dispatch the message to all different paths */
182  |   
183  |   /* MUTEX :
184  | 
185  |      So, while the most of the work is done composing the message
186  |      according to the format set in the path descriptor (mode),
187  |      the output should also be locked.
188  | 
189  |      here the mutex associated with the path should be set.
190  |      However, another mutex should be already used to protect other threads 
191  |      from reading the path description while it is modified by the master
192  |      thread. An RW lock can be used for this.
193  |           
194  |      Fortunately, fputs is MT-Safe in Solaris.
195  |   */
196  |  
197  | 
198  |   /* for now we have at most one :-) */ 
199  |   if(  er_provisional_struct.fdes == NULL ) {
200  |     fputs(buf,stderr);
201  |   }
202  |   else {
203  |     /* someone has really set something! */
204  |     if( errcode >= er_provisional_struct.sev
205  | 	|| asp & er_provisional_struct.asp
206  | 	)
207  |       {
208  | 	fputs(buf, er_provisional_struct.fdes);
209  |       }
210  |   }
211  |   
212  |   
213  |   
214  | }
215  | 
216  | 
217  | int ER_is_traced(int facwhere, er_mask_t asp) 
218  | {
219  | int i;
220  | 
221  |   /* pthread_mutex_lock( &er_pathlist_mutex ); */
222  |   i = er_provisional_struct.asp;
223  |   /* pthread_mutex_unlock( &er_pathlist_mutex ); */
224  | 
225  |   i &= asp;
226  |   return i;
227  | }
228  | 
229  | int ER_anybody_wants( int facwhere, int errcode, er_mask_t asp )
230  | {
231  | int i;
232  | 
233  |   pthread_mutex_lock( &er_pathlist_mutex );
234  |   i = ( errcode >= er_provisional_struct.sev );
235  |   pthread_mutex_unlock( &er_pathlist_mutex );
236  | 
237  |   return i;
238  | }
239  | 
240  | int er_get_printmode(er_path_t *pathstruct) 
241  | {
242  | int i;
243  | 
244  |   pthread_mutex_lock( &er_pathlist_mutex );
245  |   if(  pathstruct->fdes == NULL ) {
246  |     /* default mode */
247  |     i = ER_M_DEFAULT;
248  |   }
249  |   else {
250  |     i = pathstruct->mode;
251  |   }
252  |   pthread_mutex_unlock( &er_pathlist_mutex );
253  | 
254  |   return i;
255  | }
256  | 
257  | void ER_perror(int facwhere, int errcode, ...)
258  | {
259  |   char 	erbuf[ER_MSGLEN];
260  |   int     pmode;
261  |   va_list ap;
262  | 
263  |   if( ER_anybody_wants( facwhere, errcode, 0 ) ) {      /* uses pathlist mutex */
264  | 
265  |     pmode = er_get_printmode( & er_provisional_struct );/* uses pathlist mutex */
266  | 
267  |     /* now, this takes most time: */
268  |     va_start(ap, errcode);
269  |     er_getmsg_parts(facwhere, errcode, pmode, erbuf, NULL, ap );
270  |     va_end(ap);
271  |     
272  |     /* actually, here will be a loop once there are more paths possible. */
273  |     er_logit(facwhere, 
274  | 	   0,			       /* empty aspect mask for errors */
275  | 	   pmode,
276  | 	   errcode, 
277  | 	   erbuf);				/* empty debug message */
278  |   }
279  | }
280  | 
281  | void ER_dbg_va( int facwhere, er_mask_t asp, char *txt, ...)
282  | {
283  |   char    erbuf[ER_MSGLEN];
284  |   int pmode;
285  |   va_list   ap;
286  | 
287  |   if( ER_is_traced( facwhere, asp ) ) {
288  |     
289  |     pmode = er_get_printmode( & er_provisional_struct );
290  |     
291  |     va_start(ap, txt);
292  |     er_getmsg_parts(facwhere, ER_SEV_D, pmode, erbuf, txt, ap );
293  |     va_end(ap);
294  |     
295  |     er_logit(facwhere, asp, pmode, ER_SEV_D, erbuf);
296  |   }
297  | }
298  | 
299  | 
300  | /* Set GLOBAL VARIABLES == can be done only by the master thread */
301  | void ER_init(int argc, char **argv)
302  | {
303  | char *er_slash;	
304  | 
305  |   er_slash = rindex(argv[0],'/'); 			       
306  |   strncpy(er_progname, (er_slash != NULL) ? er_slash+1 : argv[0], 31);
307  |   er_progname[31] = 0;
308  | 
309  |   snprintf(er_pid, 10, "%d", getpid());
310  | 
311  | }