1    | /***************************************
2    |   $Revision: 1.11 $
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;
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  |     	/* build error message with arguments */
130  |     	if( mode & ER_M_TEXTLONG ) {
131  | 	  fmttxt = er_main_err[facidx].errs[erridx].text;
132  |     	}
133  | 	/* set the mnemonic pointer if necessary */ 
134  | 	if( mode & ER_M_MNEMONIC ) {
135  | 	  ermne = er_main_err[facidx].errs[erridx].mnem;
136  |         }
137  | 	break;
138  |       }
139  |     }
140  |     /*	return ""; */
141  |     /* no, do not return: bail out if the code is not defined */
142  |     assert( er_main_err[facidx].errs[erridx].code != -1 );
143  |   }
144  | 
145  |   /* build the error message using vsnprintf */  
146  |   vsnprintf(erbuf, ER_ERRLEN, fmttxt, args);
147  |   
148  |   sprintf(thr_str, "%d", pthread_self() );
149  | 
150  |   /* build the actual log message */
151  |   snprintf(buf, ER_MSGLEN, "%s-%s/%s %s-%s-%s %s",
152  | 	   (mode & ER_M_PROGNAME) ? er_progname : "",
153  | 	   (mode & ER_M_PIDFULL)  ? er_pid : "",
154  | 	   (mode & ER_M_THR_ID )  ? thr_str : "",
155  | 	   (mode & ER_M_FACSYMB)  ? er_getfacsym(facwhere) : "",
156  | 	   er_getsev(sev, mode),
157  | 	   (mode & ER_M_MNEMONIC)     ? ermne : "",
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  |     localtime_r( & tval.tv_sec, & tmstr);
185  | 
186  |     /*    strcpy(tmbuf, ctime(&tm)+11); */
187  |     sprintf(tmbuf, "%02d:%02d:%02d", 
188  | 	    tmstr.tm_hour, tmstr.tm_min, tmstr.tm_sec);
189  |   } else {
190  |     tmbuf[0]=0;
191  |   }
192  | 
193  |   snprintf(buf, ER_MSGLEN, "%s %s\n", tmbuf, msg );
194  |   /* OK, now dispatch the message to all different paths */
195  |   
196  |   /* MUTEX :
197  | 
198  |      So, while the most of the work is done composing the message
199  |      according to the format set in the path descriptor (mode),
200  |      the output should also be locked.
201  | 
202  |      here the mutex associated with the path should be set.
203  |      However, another mutex should be already used to protect other threads 
204  |      from reading the path description while it is modified by the master
205  |      thread. An RW lock can be used for this.
206  |           
207  |      Fortunately, fputs is MT-Safe in Solaris.
208  |   */
209  |  
210  | 
211  |   /* for now we have at most one :-) */ 
212  |   if(  er_provisional_struct.fdes == NULL ) {
213  |     fputs(buf,stderr);
214  |   }
215  |   else {
216  |     /* someone has really set something! */
217  |     if( errcode >= er_provisional_struct.sev
218  | 	|| ER_is_traced(facwhere, asp) ) {
219  | 
220  | 	fputs(buf, er_provisional_struct.fdes);
221  |       }
222  |   }
223  |   
224  |   
225  |   
226  | }
227  | 
228  | 
229  | int ER_is_traced(int facwhere, er_mask_t asp) 
230  | {
231  | int ik = 0;
232  | 
233  |  if( er_provisional_struct.fac == 0 
234  |      || er_provisional_struct.fac == facwhere ) {
235  |   /* pthread_mutex_lock( &er_pathlist_mutex ); */
236  |      ik =  er_provisional_struct.asp & asp;
237  |   /* pthread_mutex_unlock( &er_pathlist_mutex ); */
238  |  }
239  |  
240  |   return (ik);
241  | }
242  | 
243  | int ER_anybody_wants( int facwhere, int errcode, er_mask_t asp )
244  | {
245  | int i;
246  | 
247  |   pthread_mutex_lock( &er_pathlist_mutex );
248  |   i = ( errcode >= er_provisional_struct.sev );
249  |   pthread_mutex_unlock( &er_pathlist_mutex );
250  | 
251  |   return i;
252  | }
253  | 
254  | int er_get_printmode(er_path_t *pathstruct) 
255  | {
256  | int i;
257  | 
258  |   pthread_mutex_lock( &er_pathlist_mutex );
259  |   if(  pathstruct->fdes == NULL ) {
260  |     /* default mode */
261  |     i = ER_M_DEFAULT;
262  |   }
263  |   else {
264  |     i = pathstruct->mode;
265  |   }
266  |   pthread_mutex_unlock( &er_pathlist_mutex );
267  | 
268  |   return i;
269  | }
270  | 
271  | void ER_perror(int facwhere, int errcode, ...)
272  | {
273  |   char 	erbuf[ER_MSGLEN];
274  |   int     pmode;
275  |   va_list ap;
276  | 
277  |   if( ER_anybody_wants( facwhere, errcode, 0 ) ) {      /* uses pathlist mutex */
278  | 
279  |     pmode = er_get_printmode( & er_provisional_struct );/* uses pathlist mutex */
280  | 
281  |     /* now, this takes most time: */
282  |     va_start(ap, errcode);
283  |     er_getmsg_parts(facwhere, errcode, pmode, erbuf, NULL, ap );
284  |     va_end(ap);
285  |     
286  |     /* actually, here will be a loop once there are more paths possible. */
287  |     er_logit(facwhere, 
288  | 	   0,			       /* empty aspect mask for errors */
289  | 	   pmode,
290  | 	   errcode, 
291  | 	   erbuf);				/* empty debug message */
292  |   }
293  | }
294  | 
295  | 
296  | void ER_asp_va( int facwhere, int sev,  er_mask_t asp, char *txt, 
297  | 		va_list args)
298  | {
299  |     int pmode;
300  |     char    erbuf[ER_MSGLEN];
301  | 
302  |     pmode = er_get_printmode( & er_provisional_struct );
303  |     er_getmsg_parts(facwhere, sev, pmode, erbuf, txt, args );
304  |     er_logit(facwhere, asp, pmode, sev, erbuf);
305  | }
306  | 
307  | void ER_inf_va( int facwhere, er_mask_t asp, char *txt, ...)
308  | {
309  |     va_list   ap;
310  |     va_start(ap, txt);
311  |     ER_asp_va( facwhere, ER_SEV_I, asp, txt, ap );
312  |     va_end(ap);
313  | }
314  | 
315  | 
316  | void ER_dbg_va( int facwhere, er_mask_t asp, char *txt, ...)
317  | {
318  |   char    erbuf[ER_MSGLEN];
319  |   int pmode;
320  |   va_list   ap;
321  | 
322  |   if( ER_is_traced( facwhere, asp ) ) {
323  |     
324  |     pmode = er_get_printmode( & er_provisional_struct );
325  |     
326  |     va_start(ap, txt);
327  |     er_getmsg_parts(facwhere, ER_SEV_D, pmode, erbuf, txt, ap );
328  |     va_end(ap);
329  |     
330  |     er_logit(facwhere, asp, pmode, ER_SEV_D, erbuf);
331  |   }
332  | }
333  | 
334  | 
335  | /* Set GLOBAL VARIABLES == can be done only by the master thread */
336  | void ER_init(int argc, char **argv)
337  | {
338  | char *er_slash;	
339  | 
340  |   er_slash = rindex(argv[0],'/'); 			       
341  |   strncpy(er_progname, (er_slash != NULL) ? er_slash+1 : argv[0], 31);
342  |   er_progname[31] = 0;
343  | 
344  |   snprintf(er_pid, 10, "%d", getpid());
345  | 
346  | }