1    | /***************************************
2    |   $Revision: 1.13 $
3    | 
4    |   Error reporting (er) er.c - library of functions to uniformly report errors.
5    | 
6    |   Status: NOT REVUED, TESTED, PROVISIONAL 
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   | #include <pthread.h>
34   | #include <time.h>
35   | 
36   | 
37   | int NOERR(er_ret_t a) 
38   | {
39   |   return (    ((a & 0xFFFF) == 0 )          /* the error part is 0 */
40   | 	      && ((a & 0xFFFF0000) != 0) ); /* the facility is non-zero */
41   | }
42   | 
43   | char *er_getsev( int sev, int mode )
44   | {
45   | int i;
46   | 
47   |   for(i=0; er_level_a[i].sev != 0; i++) {
48   | 	if (er_level_a[i].sev == sev)  {
49   | 	    break;
50   | 	}
51   |   }
52   | 
53   |   switch( mode & 0x03 ) {
54   |   case ER_M_SEVCHAR:	/* one-letter severity indication */
55   |     return er_level_a[i].chr;
56   |   case ER_M_SEVLONG: 	/* long severity indication */
57   |     return er_level_a[i].txt;
58   |   }
59   |   
60   |   /* no severity indication */
61   |   return "";	/* "" goes to program text, so returning a
62   | 		   pointer to it is OK */
63   | }
64   | 
65   | char *er_getfacsym(int faccode)
66   | {
67   | int facidx;
68   | 
69   |   if( faccode != FAC_NONE  )  {
70   |     for (facidx=0; facidx<FAC_LAST; facidx++) {
71   |       if( er_main_err[facidx].code == faccode ) {
72   |         break;
73   |       }
74   |     }
75   |     return  er_main_err[facidx].name;
76   |   } 
77   |   else	return "";
78   | }
79   | 
80   | /* TWO CONSTANTS DEFINE THE LENGTH OF STRINGS HERE:
81   |    ER_MSGLEN - max length of the line to be logged
82   |    ER_ERRLEN - max length of the error message
83   | */
84   | char *er_getmsg_parts(int facwhere, int errcode, int mode, 
85   | 			char *buf, char *fmttxt, va_list args)
86   | {
87   | int fac, err, sev;
88   | int facidx, erridx;
89   | char erbuf[ER_ERRLEN], thr_str[10], *ermne, *txtlong="";
90   | 
91   | /* init to "" */
92   | erbuf[0] = 0;
93   | ermne = "";
94   | 
95   | sev = ( errcode & 0xff000000 );		/* not shifted */
96   | fac = ( errcode & 0x00ff0000 ) >> 16;
97   | err = ( errcode & 0x0000ffff );		/* not shifted */
98   | 
99   |   for (facidx=0; facidx<FAC_LAST; facidx++) {
100  |     if( er_main_err[facidx].code == fac ) {
101  |       break;
102  |     }
103  |   }
104  | 
105  |   /* now, if we got to the last one and it's not the right one, 
106  |      the system is not configured properly */
107  |   if(facidx==FAC_LAST) {
108  |      assert( er_main_err[facidx].code == fac );	/* just bail out. */
109  |   }
110  | 
111  |   /* still alive ? OK, build the message ...*/
112  | 
113  |   /* ... using facidx/erridx if it's not a DEBUG or INFO */
114  |   switch( sev ) {
115  |     case ER_SEV_D:
116  | 	ermne = "DEBUG";
117  | 	break;
118  |     case ER_SEV_I:
119  | 	ermne = "INFO";
120  | 	break;
121  |     default:
122  |     /* OK, go to the module table. bail out if not initialized */
123  |     assert( er_main_err[facidx].errs != NULL );
124  | 
125  |     for(erridx=0; er_main_err[facidx].errs[erridx].code != -1; erridx++) {
126  |       if( er_main_err[facidx].errs[erridx].code == errcode ) {
127  |        	/* FOUND! now set the error message format using facidx and erridx */
128  | 
129  |     	/* long error message without arguments */
130  | 	txtlong = er_main_err[facidx].errs[erridx].text;
131  |     	
132  | 	/* set the mnemonic pointer if necessary */ 
133  | 	if( mode & ER_M_MNEMONIC ) {
134  | 	  ermne = er_main_err[facidx].errs[erridx].mnem;
135  |         }
136  | 	break;
137  |       }
138  |     }
139  |     /*	return ""; */
140  |     /* no, do not return: bail out if the code is not defined */
141  |     assert( er_main_err[facidx].errs[erridx].code != -1 );
142  |   }
143  | 
144  |   /* build the error message using vsnprintf */  
145  |   vsnprintf(erbuf, ER_ERRLEN, fmttxt, args);
146  |   
147  |   sprintf(thr_str, "%d", pthread_self() );
148  | 
149  |   /* build the actual log message */
150  |   snprintf(buf, ER_MSGLEN, "%s-%s/%s %s-%s-%s %s %s",
151  | 	   (mode & ER_M_PROGNAME) ? er_progname : "",
152  | 	   (mode & ER_M_PIDFULL)  ? er_pid : "",
153  | 	   (mode & ER_M_THR_ID )  ? thr_str : "",
154  | 	   (mode & ER_M_FACSYMB)  ? er_getfacsym(facwhere) : "",
155  | 	   er_getsev(sev, mode),
156  | 	   (mode & ER_M_MNEMONIC) ? ermne : "",
157  | 	   (mode & ER_M_TEXTLONG) ? txtlong : "",
158  | 	   erbuf
159  | 	   );	
160  |   return buf;
161  | }
162  | 
163  | void ER_setpath(er_path_t *newset)
164  | {
165  |   /* initialise the mutex if not yet initialised */
166  | 
167  |   if( er_pathlist_mutex_initialised == 0 ) {
168  |     pthread_mutex_init( &er_pathlist_mutex, NULL );
169  |   }
170  |   
171  |   pthread_mutex_lock( &er_pathlist_mutex );
172  |   memcpy( & er_provisional_struct, newset, sizeof(er_path_t));  
173  |   pthread_mutex_unlock( &er_pathlist_mutex );
174  | }
175  | 
176  | void er_logit(int facwhere, er_mask_t asp, int mode, int errcode, char *msg)
177  | {
178  |   char 	buf[ER_MSGLEN], tmbuf[32];
179  |   struct timeval tval;
180  |   struct tm tmstr;
181  | 
182  |   if ( mode & ER_M_DATETIME ) {
183  |     gettimeofday(&tval, NULL);
184  |     
185  |     ctime_r(& tval.tv_sec, tmbuf);
186  |     /* truncate before 2000 */
187  |     tmbuf[19]=0;
188  |     /* localtime_r( & tval.tv_sec, & tmstr);
189  |        sprintf(tmbuf, "%02d:%02d:%02d", 
190  |        tmstr.tm_hour, tmstr.tm_min, tmstr.tm_sec); */
191  |     
192  |   } else {
193  |     tmbuf[0]=0;
194  |   }
195  | 
196  |   snprintf(buf, ER_MSGLEN, "%s %s\n", tmbuf, msg );
197  |   /* OK, now dispatch the message to all different paths */
198  |   
199  |   /* MUTEX :
200  | 
201  |      So, while the most of the work is done composing the message
202  |      according to the format set in the path descriptor (mode),
203  |      the output should also be locked.
204  | 
205  |      here the mutex associated with the path should be set.
206  |      However, another mutex should be already used to protect other threads 
207  |      from reading the path description while it is modified by the master
208  |      thread. An RW lock can be used for this.
209  |           
210  |      Fortunately, fputs is MT-Safe in Solaris.
211  |   */
212  |  
213  | 
214  |   /* for now we have at most one :-) */ 
215  |   if(  er_provisional_struct.fdes == NULL ) {
216  |     fputs(buf,stderr);
217  |   }
218  |   else {
219  |     /* someone has really set something! */
220  |     if( errcode >= er_provisional_struct.sev
221  | 	|| ER_is_traced(facwhere, asp) ) {
222  | 
223  | 	fputs(buf, er_provisional_struct.fdes);
224  |       }
225  |   }
226  |   
227  |   
228  |   
229  | }
230  | 
231  | 
232  | int ER_is_traced(int facwhere, er_mask_t asp) 
233  | {
234  | int ik = 0;
235  | 
236  |  if( er_provisional_struct.fac == 0 
237  |      || er_provisional_struct.fac == facwhere ) {
238  |   /* pthread_mutex_lock( &er_pathlist_mutex ); */
239  |      ik =  er_provisional_struct.asp & asp;
240  |   /* pthread_mutex_unlock( &er_pathlist_mutex ); */
241  |  }
242  |  
243  |   return (ik);
244  | }
245  | 
246  | int ER_anybody_wants( int facwhere, int errcode, er_mask_t asp )
247  | {
248  | int i;
249  | 
250  |   pthread_mutex_lock( &er_pathlist_mutex );
251  |   i = ( errcode >= er_provisional_struct.sev );
252  |   pthread_mutex_unlock( &er_pathlist_mutex );
253  | 
254  |   return i;
255  | }
256  | 
257  | int er_get_printmode(er_path_t *pathstruct) 
258  | {
259  | int i;
260  | 
261  |   pthread_mutex_lock( &er_pathlist_mutex );
262  |   if(  pathstruct->fdes == NULL ) {
263  |     /* default mode */
264  |     i = ER_M_DEFAULT;
265  |   }
266  |   else {
267  |     i = pathstruct->mode;
268  |   }
269  |   pthread_mutex_unlock( &er_pathlist_mutex );
270  | 
271  |   return i;
272  | }
273  | 
274  | void ER_perror(int facwhere, int errcode, char *format, ...)
275  | {
276  |   char 	erbuf[ER_MSGLEN];
277  |   int     pmode;
278  |   va_list ap;
279  | 
280  |   if( ER_anybody_wants( facwhere, errcode, 0 ) ) {      /* uses pathlist mutex */
281  | 
282  |     pmode = er_get_printmode( & er_provisional_struct );/* uses pathlist mutex */
283  | 
284  |     /* now, this takes most time: */
285  |     va_start(ap, format);
286  |     er_getmsg_parts(facwhere, errcode, pmode, erbuf, format, ap );
287  |     va_end(ap);
288  |     
289  |     /* actually, here will be a loop once there are more paths possible. */
290  |     er_logit(facwhere, 
291  | 	   0,			       /* empty aspect mask for errors */
292  | 	   pmode,
293  | 	   errcode, 
294  | 	   erbuf);				/* empty debug message */
295  |   }
296  | }
297  | 
298  | 
299  | void ER_asp_va( int facwhere, int sev,  er_mask_t asp, char *txt, 
300  | 		va_list args)
301  | {
302  |     int pmode;
303  |     char    erbuf[ER_MSGLEN];
304  | 
305  |     pmode = er_get_printmode( & er_provisional_struct );
306  |     er_getmsg_parts(facwhere, sev, pmode, erbuf, txt, args );
307  |     er_logit(facwhere, asp, pmode, sev, erbuf);
308  | }
309  | 
310  | void ER_inf_va( int facwhere, er_mask_t asp, char *txt, ...)
311  | {
312  |     va_list   ap;
313  |     va_start(ap, txt);
314  |     ER_asp_va( facwhere, ER_SEV_I, asp, txt, ap );
315  |     va_end(ap);
316  | }
317  | 
318  | 
319  | void ER_dbg_va( int facwhere, er_mask_t asp, char *txt, ...)
320  | {
321  |   char    erbuf[ER_MSGLEN];
322  |   int pmode;
323  |   va_list   ap;
324  | 
325  |   if( ER_is_traced( facwhere, asp ) ) {
326  |     
327  |     pmode = er_get_printmode( & er_provisional_struct );
328  |     
329  |     va_start(ap, txt);
330  |     er_getmsg_parts(facwhere, ER_SEV_D, pmode, erbuf, txt, ap );
331  |     va_end(ap);
332  |     
333  |     er_logit(facwhere, asp, pmode, ER_SEV_D, erbuf);
334  |   }
335  | }
336  | 
337  | 
338  | /* Set GLOBAL VARIABLES == can be done only by the master thread */
339  | void ER_init(int argc, char **argv)
340  | {
341  | char *er_slash;	
342  | 
343  |   er_slash = rindex(argv[0],'/'); 			       
344  |   strncpy(er_progname, (er_slash != NULL) ? er_slash+1 : argv[0], 31);
345  |   er_progname[31] = 0;
346  | 
347  |   snprintf(er_pid, 10, "%d", getpid());
348  | 
349  | }