1    | /***************************************
2    |   $Revision: 1.22 $
3    | 
4    |   Error reporting (er) er.c - library of functions to uniformly report errors.
5    | 
6    |   Status: NOT REVUED, PARTLY TESTED
7    | 
8    |   NOTE: MALLOC ALERT!!! THE REPORTING FUNCTIONS MAY NOT USE DYNAMIC MEMORY!!!
9    |   for one: they wouldn't work if we run out of memory...
10   |   for two: the memory wrappers may have logging enabled, and it would loop.
11   | 
12   |   Design and implementation by: Marek Bukowy
13   | 
14   |   ******************/ /******************
15   |   Copyright (c) 1999,2000                             RIPE NCC
16   |  
17   |   All Rights Reserved
18   |   
19   |   Permission to use, copy, modify, and distribute this software and its
20   |   documentation for any purpose and without fee is hereby granted,
21   |   provided that the above copyright notice appear in all copies and that
22   |   both that copyright notice and this permission notice appear in
23   |   supporting documentation, and that the name of the author not be
24   |   used in advertising or publicity pertaining to distribution of the
25   |   software without specific, written prior permission.
26   |   
27   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
28   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
29   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
30   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
31   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
32   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
33   |   ***************************************/
34   | 
35   | #define ER_IMPL
36   | #include "erroutines.h"
37   | #include <pthread.h>
38   | #include <time.h>
39   | 
40   | #ifdef _LINUX
41   | #include <sys/time.h>
42   | #include <unistd.h>
43   | #endif
44   | 
45   | #include <sys/types.h>
46   | #include <sys/stat.h>
47   | #include <fcntl.h>
48   | 
49   | #include "er_macro.h"
50   | 
51   | int NOERR(er_ret_t a) 
52   | {
53   |   return (    ((a & 0xFFFF) == 0 )          /* the error part is 0 */
54   | 	      && ((a & 0xFFFF0000) != 0) ); /* the facility is non-zero */
55   | }
56   | 
57   | 
58   | int er_msgsel( er_filter_t *filtptr, 
59   | 	       er_fac_code_t facwhere, 
60   | 	       er_mask_t asp, 
61   | 	       er_ret_t errcode)
62   | {
63   |   if( ! MA_isset( filtptr->fac_mask, facwhere) ) {
64   |         return 0;
65   |   }
66   |   
67   |   /* check aspect only for DEBUG and INFO messages */
68   |   if( ( errcode == ER_SEV_D || errcode == ER_SEV_I )
69   |       && ! (asp & filtptr->asp_mask) ) {
70   |     return 0;
71   |   }
72   |   
73   |   if(    (errcode & 0xff000000) < filtptr->sev_min 
74   |       || (errcode & 0xff000000) > filtptr->sev_max ) {
75   |     return 0;
76   |   }
77   | 
78   |   if(    filtptr->thr_id != 0 
79   |       && filtptr->thr_id != pthread_self() ) {
80   |     return 0;
81   |   }
82   | 
83   |   return 1;
84   | }
85   | 
86   | 
87   | /* fork & exec a program specified with argv, the print msg
88   |    on its stdin and exit. No redirection of stdout/stderr is done.
89   | 
90   |    MT-note: Solaris fork1() duplicates only the calling thread.
91   |    So does Posix fork().
92   |  */
93   | er_forkexec(char **argv, char *msg, int usepath)
94   | {
95   |   int PipeEnds[2];
96   |   int status, cpid;
97   |  
98   |   pipe(PipeEnds);	       
99   |   
100  | #define PIP_WR 1
101  | #define PIP_RD 0
102  | 
103  | #ifdef _POSIX_PTHREAD_SEMANTICS 
104  | #define fork1 fork
105  | #endif
106  | 
107  |   if((cpid=fork1()) == 0)	        /* child */
108  |     {       
109  |       dup2( PipeEnds[PIP_RD], 0 ); 
110  |       close( PipeEnds[PIP_WR] );	/* pipe input */
111  |       if( usepath ) {
112  | 	execvp(argv[0], argv);
113  |       }
114  |       else {
115  | 	execv(argv[0], argv);
116  |       }
117  |       perror("Exec failed: ");
118  |       exit(-1);
119  |     }
120  |   close( PipeEnds[PIP_RD] );
121  |   
122  |   write( PipeEnds[PIP_WR], msg, strlen(msg) );
123  |   close( PipeEnds[PIP_WR] );
124  | 
125  |   wait(&status);
126  | }
127  | 
128  | static
129  | void
130  | er_logtopath(er_path_t *pathptr, char *form, char *msg)
131  | { 
132  |   
133  |   char fullline[ER_MSGLEN+ER_ERRLEN+4];
134  | 
135  |   /* MUTEX :
136  | 
137  |      So, while the most of the work is done composing the message
138  |      according to the format set in the path descriptor (mode),
139  |      the output should also be locked.
140  | 
141  |      here the mutex associated with the path should be set.
142  |      However, another mutex should be already used to protect other threads 
143  |      from reading the path description while it is modified by the master
144  |      thread. An RW lock can be used for this.
145  |           
146  |      Fortunately, fputs is MT-Safe in Solaris.
147  |   */
148  |  
149  |   int fd;
150  | 
151  |   /* bound checking done already for form & msg */
152  |   strcpy(fullline, form);
153  |   strcat(fullline, msg);
154  |   strcat(fullline, "\n");
155  | 
156  |   switch(pathptr->type) {
157  |   case ER_PATH_SOCK:
158  |     fd = pathptr->descr.sock.fd;
159  |     if( write(fd, fullline, strlen(fullline)) == -1 ) {
160  |       perror("ER logging ");
161  |     }    
162  |     break;
163  |   case ER_PATH_NAME:
164  |     {
165  |       char *filename;
166  |       char constructed[128], datestr[10];
167  |       struct timeval tval;
168  |       struct tm tmstr;
169  | 
170  |       if( pathptr->descr.name.date == 0 ) {
171  | 	filename = pathptr->descr.name.filename;
172  |       }
173  |       else {  
174  | 	/* construct the filename for the paths with DATE option */
175  | 	strcpy( constructed, pathptr->descr.name.filename );
176  | 
177  | 	gettimeofday(&tval, NULL);
178  | 	localtime_r( & tval.tv_sec,   &tmstr);
179  | 	strftime(datestr, 10, ".%Y%m%d", &tmstr);
180  | 
181  | 	strcat( constructed, datestr );
182  | 	filename = constructed;
183  |       } 
184  |       fd=open(filename, O_WRONLY|O_APPEND|O_CREAT, 0755 );
185  |       if( fd > 0 ) {
186  | 	/* XXX lock ? According to SK, not needed as long as it's on one
187  | 	 machine - the 'append' mode will make sure things are not garbled.
188  | 	*/
189  | 	if(   write(fd, fullline, strlen(fullline)) == -1 ) {
190  | 	  perror("ER logging ");
191  | 	}    
192  | 	/* XXX unlock ? */
193  | 	close(fd);
194  |       }
195  |       else {
196  | 	fprintf(stderr, "ER: cannot open log file %s ",  
197  | 		pathptr->descr.name.filename);
198  | 	perror("");
199  |       }
200  |     }
201  |     break;
202  |     
203  |   case ER_PATH_EXEC:
204  |       er_forkexec(pathptr->descr.exec.argv, 
205  | 		  fullline, 
206  | 		  pathptr->descr.exec.usepath );
207  |       break;
208  |   default:
209  |     die; /* not implemented */
210  |   }   
211  | }
212  | 
213  | void
214  | er_getmsg_parts(char *buf, int buflen, char *fmttxt, va_list args)
215  | {
216  |   /* build the error message using vsnprintf */  
217  |   vsnprintf(buf, buflen, fmttxt, args);
218  | }
219  | 
220  | 
221  | 
222  | /* TWO CONSTANTS DEFINE THE LENGTH OF STRINGS HERE:
223  |    ER_MSGLEN - max length of the line to be logged
224  |    ER_ERRLEN - max length of the error message
225  | */
226  | char *er_format_line(char *erbuf, er_fac_code_t  facwhere, 
227  | 		     er_mask_t asp, int mode,  int errcode, 
228  | 		     char *tmbuf)
229  | {
230  | int fac, err, sev;
231  | int facidx, erridx;
232  | char thr_str[10], *ermne, *txtlong="";
233  | 
234  | /* init to "" */
235  | erbuf[0] = 0;
236  | ermne = "";
237  | 
238  | sev = ( errcode & 0xff000000 );		/* not shifted */
239  | fac = ( errcode & 0x00ff0000 ) >> 16;
240  | err = ( errcode & 0x0000ffff );		/* not shifted */
241  | 
242  | /* take the overridden value (facwhere) in case of doubt */ 
243  |  if(facwhere != fac) {
244  |    fac = facwhere;
245  |  }
246  | 
247  |   for (facidx=0; facidx<FAC_LAST; facidx++) {
248  |     if( er_fac_err[facidx].code == fac ) {
249  |       break;
250  |     }
251  |   }
252  | 
253  |   /* now, if we got to the last one and it's not the right one, 
254  |      the system is not configured properly */
255  |   if(facidx==FAC_LAST) {
256  |      assert( er_fac_err[facidx].code == fac );	/* just bail out. */
257  |   }
258  | 
259  |   /* still alive ? OK, build the message ...*/
260  | 
261  |   /* ... using facidx/erridx if it's not a DEBUG or INFO */
262  |   switch( sev ) {
263  |     case ER_SEV_D:
264  | 	ermne = "DEBUG";
265  | 	break;
266  |     case ER_SEV_I:
267  | 	ermne = "INFO";
268  | 	break;
269  |     default:
270  |     /* OK, go to the module table. bail out if not initialized */
271  |     assert( er_fac_err[facidx].errs != NULL );
272  | 
273  |     for(erridx=0; er_fac_err[facidx].errs[erridx].code != -1; erridx++) {
274  |       if( er_fac_err[facidx].errs[erridx].code == errcode ) {
275  |        	/* FOUND! now set the error message format using facidx and erridx */
276  | 
277  |     	/* long error message without arguments */
278  | 	txtlong = er_fac_err[facidx].errs[erridx].text;
279  |     	
280  | 	/* set the mnemonic pointer if necessary */ 
281  | 	if( mode & ER_M_MNEMONIC ) {
282  | 	  ermne = er_fac_err[facidx].errs[erridx].mnem;
283  |         }
284  | 	break;
285  |       }
286  |     }
287  |     /*	return ""; */
288  |     /* no, do not return: bail out if the code is not defined */
289  |     assert( er_fac_err[facidx].errs[erridx].code != -1 );
290  |   }
291  | 
292  |   
293  |   
294  |   sprintf(thr_str, "%d", pthread_self() );
295  | 
296  |   /* build the actual log message */
297  |   snprintf(erbuf, ER_MSGLEN, "%s %s-%s/%s %s-%s-%s %s ",
298  | 	   ( mode & ER_M_DATETIME ) ? tmbuf : "",
299  | 	   (mode & ER_M_PROGNAME) ? er_progname : "",
300  | 	   (mode & ER_M_PIDFULL)  ? er_pid : "",
301  | 	   (mode & ER_M_THR_ID )  ? thr_str : "",
302  | 	   (mode & ER_M_FACSYMB)  ? er_getfacsym(facwhere) : "",
303  | 	   er_getsevsym(sev, mode),
304  | 	   (mode & ER_M_MNEMONIC) ? ermne : "",
305  | 	   (mode & ER_M_TEXTLONG) ? txtlong : ""
306  | 	   );	
307  |   return erbuf;
308  | }
309  | 
310  | void er_logit(er_fac_code_t  facwhere, er_mask_t asp, int errcode, char *msg)
311  | {
312  |   char 	formbuf[ER_MSGLEN], tmbuf[32];
313  |   struct timeval tval;
314  |   struct tm tmstr;
315  | 
316  |   
317  | 
318  |   /*er_pathlist_mutex;*/
319  | 
320  |   {
321  |     GList *pitem, *fitem;
322  |       
323  |     for( pitem = g_list_first(er_pathlist);
324  | 	 pitem != NULL;
325  | 	 pitem = g_list_next(pitem)) {
326  | 	
327  |       er_path_t *pathptr = (er_path_t *)pitem->data;
328  | 
329  | 	
330  |       if( pathptr->active ) {
331  | 
332  | 	for( fitem = g_list_first(pathptr->filters);
333  | 	     fitem != NULL;
334  | 	     fitem = g_list_next(fitem)) {
335  | 	  
336  | 	  er_filter_t *filtptr = (er_filter_t *) fitem->data;
337  | 
338  | 	  
339  | 	  if( er_msgsel( filtptr, facwhere, asp, errcode) ) {
340  | 	    if ( pathptr->format & ER_M_DATETIME ) {
341  | 	      gettimeofday(&tval, NULL);
342  | 	      
343  | 	      localtime_r( & tval.tv_sec, & tmstr);
344  | 
345  | 	      strftime(tmbuf, sizeof(tmbuf), "%Y%m%d %H:%M:%S", &tmstr);
346  | 	    } else {
347  | 	      tmbuf[0]=0;
348  | 	    }
349  | 	    
350  | 	    er_format_line( formbuf, 
351  | 			    facwhere, asp, pathptr->format, errcode, tmbuf);
352  | 	    
353  | 	    er_logtopath( pathptr, formbuf, msg );
354  | 	    break; /* go to next path */
355  | 	  }
356  | 	}
357  |       }
358  |     }
359  |   }
360  | }
361  | 
362  | /* check if anyone traces this particular aspect for this facility,
363  |    whether on DEBUG or INFO level */
364  | int ER_is_traced(er_fac_code_t  facwhere, er_mask_t asp) 
365  | {
366  |   return (er_asparray[facwhere] & asp );
367  | }
368  | /* check if anyone traces this particular error for this facility */
369  | int ER_is_errorlogged(er_fac_code_t  facwhere, int errcode)
370  | {
371  | int i = 1;
372  | 
373  |  return i;
374  | }
375  | 
376  | int er_get_printmode(er_path_t *pathstruct) 
377  | {
378  |   return pathstruct->format;
379  | }
380  | 
381  | void ER_perror(er_fac_code_t  facwhere, int errcode, char *format, ...)
382  | {
383  |   char 	erbuf[ER_MSGLEN];
384  |   va_list ap;
385  | 
386  |   if( ER_is_errorlogged( facwhere, errcode ) ) {      /* uses pathlist mutex */
387  | 
388  |     /* now, this takes most time: */
389  |     va_start(ap, format);
390  |     er_getmsg_parts(erbuf, sizeof(erbuf), format, ap );
391  |     va_end(ap);
392  |     
393  |     /* actually, here will be a loop once there are more paths possible. */
394  |     er_logit(facwhere, 
395  | 	   0,			       /* empty aspect mask for errors */
396  | 	   errcode, 
397  | 	   erbuf);				/* empty debug message */
398  |   }
399  | }
400  | 
401  | void ER_asp_va(er_fac_code_t  facwhere, int sev,  er_mask_t asp, char *txt, 
402  | 		va_list args)
403  | {
404  |     char    erbuf[ER_MSGLEN];
405  | 
406  |     er_getmsg_parts(erbuf, sizeof(erbuf), txt, args );
407  |     er_logit(facwhere, asp, sev, erbuf);
408  | }
409  | 
410  | void ER_inf_va(er_fac_code_t  facwhere, er_mask_t asp, char *txt, ...)
411  | {
412  |     va_list   ap;
413  |     va_start(ap, txt);
414  |     ER_asp_va( facwhere, ER_SEV_I, asp, txt, ap );
415  |     va_end(ap);
416  | }
417  | 
418  | 
419  | void ER_dbg_va(er_fac_code_t  facwhere, er_mask_t asp, char *txt, ...)
420  | {
421  |   char    erbuf[ER_MSGLEN];
422  |   va_list   ap;
423  | 
424  |   if( ER_is_traced( facwhere, asp ) ) {
425  |     
426  |     va_start(ap, txt);
427  |     er_getmsg_parts(erbuf, sizeof(erbuf), txt, ap );
428  |     va_end(ap);
429  |     
430  |     er_logit(facwhere, asp,  ER_SEV_D, erbuf);
431  |   }
432  | }
433  | 
434  | 
435  | /* Set GLOBAL VARIABLES == can be done only by the master thread */
436  | void ER_init(char *progname, int processdefs)
437  | {
438  | 		       
439  |   strncpy(er_progname, progname, 31);
440  |   er_progname[31] = 0;
441  | 
442  |   snprintf(er_pid, 10, "%d", getpid());
443  | 
444  |   /* now error definitions: first predefine macros */
445  |   ER_macro_predef();
446  |   /* then override them */
447  |   ER_proc_ca_macro();
448  | 
449  |   if( processdefs ) {
450  |     /* now process the definitions if allowed */
451  |     ER_proc_ca_err();
452  |   }
453  |     
454  | }