1    | /***************************************
2    |   $Revision: 1.30 $
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,2001,2002                   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 "rip.h"
37   | 
38   | #include <pthread.h>
39   | #include <time.h>
40   | 
41   | #ifdef HAVE_SYS_TIME_H
42   | #include <sys/time.h>
43   | #endif
44   | 
45   | #ifdef HAVE_UNISTD_H
46   | #include <unistd.h>
47   | #endif
48   | 
49   | #include <sys/types.h>
50   | #include <sys/stat.h>
51   | #include <fcntl.h>
52   | #include <sys/wait.h>
53   | 
54   | 
55   | /*++++++++++++++++++++++++++++++++++++++
56   |   Simple check if the error code is a success (severity = 0).
57   |   Detects (some) invalid codes - those whose facility part is 0.
58   | 
59   |   int NOERR      returns 1 if the code is valid and is a success
60   |                  returns 0 if the code is invalid or is a real error
61   | 		 (the severity is non-zero).
62   | 
63   |   er_ret_t a     error code to be checked.
64   |   ++++++++++++++++++++++++++++++++++++++*/
65   | int NOERR(er_ret_t a) 
66   | {
67   |   return (    ((a & 0xFFFF) == 0 )          /* the error part is 0 */
68   | 	      && ((a & 0xFFFF0000) != 0) ); /* the facility is non-zero */
69   | }
70   | 
71   | 
72   | /*++++++++++++++++++++++++++++++++++++++
73   |   
74   |   Message selection routine. Checks if the message coming from the
75   |   current thread, tagged with the given error code, facility and
76   |   aspect matches the given filter.
77   | 
78   |   int er_msgsel             returns 1 for a match, 0 otherwise.
79   | 
80   |   er_filter_t *filtptr      filter to be matched
81   | 
82   |   er_fac_code_t facwhere    message's facility of call
83   | 
84   |   er_mask_t asp             message's aspect (if severity is INFO or DEBUG)
85   | 
86   |   er_ret_t errcode          message's error code
87   |   ++++++++++++++++++++++++++++++++++++++*/
88   | 
89   | int er_msgsel( er_filter_t *filtptr, 
90   | 	       er_fac_code_t facwhere, 
91   | 	       er_mask_t asp, 
92   | 	       er_ret_t errcode)
93   | {
94   |   if( ! MA_isset( filtptr->fac_mask, facwhere) ) {
95   |         return 0;
96   |   }
97   |   
98   |   /* check aspect only for DEBUG and INFO messages */
99   |   if( ( errcode == ER_SEV_D || errcode == ER_SEV_I )
100  |       && ! (asp & filtptr->asp_mask) ) {
101  |     return 0;
102  |   }
103  |   
104  |   if(    (errcode & 0xff000000) < filtptr->sev_min 
105  |       || (errcode & 0xff000000) > filtptr->sev_max ) {
106  |     return 0;
107  |   }
108  | 
109  |   if(    filtptr->thr_id != 0 
110  |       && filtptr->thr_id != pthread_self() ) {
111  |     return 0;
112  |   }
113  | 
114  |   return 1;
115  | }
116  | 
117  | 
118  | 
119  | 
120  | 
121  | /*++++++++++++++++++++++++++++++++++++++
122  |   Fork & exec a program specified with argv, the print msg
123  |   on its stdin and exit. No redirection of stdout/stderr is done.
124  |   
125  |   MT-note: Solaris fork1() duplicates only the calling thread.
126  |   So does Posix fork().
127  |   
128  |   char **argv       argv array for the exec call
129  | 
130  |   char *msg         text payload to be printed on the stdin of forked process
131  | 
132  |   int usepath       flag indicating if the PATH environmental variable 
133  |                     should be used by the exec call.
134  |   ++++++++++++++++++++++++++++++++++++++*/
135  | void er_forkexec(char **argv, char *msg, int usepath)
136  | {
137  |   int PipeEnds[2];
138  |   int status, cpid;
139  |  
140  |   pipe(PipeEnds);	       
141  |   
142  | #define PIP_WR 1
143  | #define PIP_RD 0
144  | 
145  | #ifdef _POSIX_PTHREAD_SEMANTICS 
146  | #define fork1 fork
147  | #endif
148  | 
149  |   if((cpid=fork1()) == 0)	        /* child */
150  |     {       
151  |       dup2( PipeEnds[PIP_RD], 0 ); 
152  |       close( PipeEnds[PIP_WR] );	/* pipe input */
153  |       if( usepath ) {
154  | 	execvp(argv[0], argv);
155  |       }
156  |       else {
157  | 	execv(argv[0], argv);
158  |       }
159  |       perror("Exec failed: ");
160  |       exit(-1);
161  |     }
162  |   close( PipeEnds[PIP_RD] );
163  |   
164  |   write( PipeEnds[PIP_WR], msg, strlen(msg) );
165  |   close( PipeEnds[PIP_WR] );
166  | 
167  |   wait(&status);
168  | }
169  | 
170  | 
171  | /*++++++++++++++++++++++++++++++++++++++
172  |   
173  |   Main function logging a message to a path. The formatted message
174  |   parts and the message text itself are given separately to avoid
175  |   wasting time to remake them, because the message text may be the
176  |   same while the path formats are different.
177  |   
178  |   er_path_t *pathptr        pointer to the path structure where the message
179  |                             should go.
180  |   
181  |   char *form                format part of the message
182  |   
183  |   char *msg                 payload part of the message
184  |   ++++++++++++++++++++++++++++++++++++++*/
185  | static
186  | void
187  | er_logtopath(er_path_t *pathptr, char *form, char *msg)
188  | { 
189  |   
190  |   char fullline[ER_MSGLEN+ER_ERRLEN+4];
191  | 
192  |   /* MUTEX :
193  | 
194  |      So, while the most of the work is done composing the message
195  |      according to the format set in the path descriptor (mode),
196  |      the output should also be locked.
197  | 
198  |      here the mutex associated with the path should be set.
199  |      However, another mutex should be already used to protect other threads 
200  |      from reading the path description while it is modified by the master
201  |      thread. An RW lock can be used for this.
202  |           
203  |      Fortunately, fputs is MT-Safe in Solaris.
204  |   */
205  |  
206  |   int fd;
207  | 
208  |   /* bound checking done already for form & msg */
209  |   strcpy(fullline, form);
210  |   strcat(fullline, msg);
211  |   strcat(fullline, "\n");
212  | 
213  |   switch(pathptr->type) {
214  |   case ER_PATH_SOCK:
215  |     fd = pathptr->descr.sock.fd;
216  |     /* XXX: write on a socket may write less than the requested number
217  |             of bytes and not be an error - shane */
218  |     if( write(fd, fullline, strlen(fullline)) == -1 ) {
219  |       perror("ER logging ");
220  |     }    
221  |     break;
222  |   case ER_PATH_NAME:
223  |     {
224  |       char *filename;
225  |       char constructed[128], datestr[10];
226  |       struct timeval tval;
227  |       struct tm tmstr;
228  | 
229  |       if( pathptr->descr.name.date == 0 ) {
230  | 	filename = pathptr->descr.name.filename;
231  |       }
232  |       else {  
233  | 	/* construct the filename for the paths with DATE option */
234  | 	strcpy( constructed, pathptr->descr.name.filename );
235  | 
236  | 	gettimeofday(&tval, NULL);
237  | 	localtime_r( & tval.tv_sec,   &tmstr);
238  | 	strftime(datestr, 10, ".%Y%m%d", &tmstr);
239  | 
240  | 	strcat( constructed, datestr );
241  | 	filename = constructed;
242  |       } 
243  |       fd=open(filename, O_WRONLY|O_APPEND|O_CREAT, 0666 );
244  |       if( fd > 0 ) {
245  | 	/* XXX lock ? According to SK, not needed as long as it's on one
246  | 	 machine - the 'append' mode will make sure things are not garbled.
247  |          Note: Two months of operation and no a single garbled line yet ;) SK
248  | 	*/
249  | 	if(   write(fd, fullline, strlen(fullline)) == -1 ) {
250  | 	  perror("ER logging ");
251  | 	}    
252  | 	/* XXX unlock ? */
253  | 	close(fd);
254  |       }
255  |       else {
256  | 	fprintf(stderr, "ER: cannot open log file %s ",  
257  | 		pathptr->descr.name.filename);
258  | 	perror("");
259  |       }
260  |     }
261  |     break;
262  |     
263  |   case ER_PATH_EXEC:
264  |       er_forkexec(pathptr->descr.exec.argv, 
265  | 		  fullline, 
266  | 		  pathptr->descr.exec.usepath );
267  |       break;
268  |   default:
269  |     die; /* not implemented */
270  |   }   
271  | }
272  | 
273  | 
274  | /*++++++++++++++++++++++++++++++++++++++
275  |   
276  |   Internal message construction. Message parts are gathered according to the
277  |   specified format bitmask. The resulting message consisting of those parts
278  |   as well as the printf-style constructed message text itself is printed into 
279  |   the given buffer.
280  | 
281  |   char *buf         pointer to the predefined buffer
282  |   
283  |   unsigned buflen   buffer length
284  |   
285  |   char *fmttxt      printf-style format 
286  |   
287  |   va_list args      printf-style arguments, if any
288  | 
289  |   ++++++++++++++++++++++++++++++++++++++*/
290  | void
291  | er_getmsg_parts(char *buf, unsigned buflen, char *fmttxt, va_list args)
292  | {
293  |   /* build the error message using vsnprintf */  
294  |   vsnprintf(buf, buflen, fmttxt, args);
295  | }
296  | 
297  | 
298  | 
299  | /* TWO CONSTANTS DEFINE THE LENGTH OF STRINGS HERE:
300  |    ER_MSGLEN - max length of the line to be logged
301  |    ER_ERRLEN - max length of the error message
302  | */
303  | char *er_format_line(char *erbuf, er_fac_code_t  facwhere, 
304  | 		     er_mask_t asp, int mode,  int errcode, 
305  | 		     char *tmbuf)
306  | {
307  | int fac, err, sev;
308  | int facidx, erridx;
309  | char thr_str[10], *ermne, *txtlong="";
310  | 
311  | /* init to "" */
312  | erbuf[0] = 0;
313  | ermne = "";
314  | 
315  | sev = ( errcode & 0xff000000 );		/* not shifted */
316  | fac = ( errcode & 0x00ff0000 ) >> 16;
317  | err = ( errcode & 0x0000ffff );		/* not shifted */
318  | 
319  | /* take the overridden value (facwhere) in case of doubt */ 
320  |  if(facwhere != fac) {
321  |    fac = facwhere;
322  |  }
323  | 
324  |   for (facidx=0; facidx<FAC_LAST; facidx++) {
325  |     if( er_fac_err[facidx].code == fac ) {
326  |       break;
327  |     }
328  |   }
329  | 
330  |   /* now, if we got to the last one and it's not the right one, 
331  |      the system is not configured properly */
332  |   if(facidx==FAC_LAST) {
333  |      assert( er_fac_err[facidx].code == fac );	/* just bail out. */
334  |   }
335  | 
336  |   /* still alive ? OK, build the message ...*/
337  | 
338  |   /* ... using facidx/erridx if it's not a DEBUG or INFO */
339  |   switch( sev ) {
340  |     case ER_SEV_D:
341  | 	ermne = "DEBUG";
342  | 	break;
343  |     case ER_SEV_I:
344  | 	ermne = "INFO";
345  | 	break;
346  |     default:
347  |     /* OK, go to the module table. bail out if not initialized */
348  |     assert( er_fac_err[facidx].errs != NULL );
349  | 
350  |     for(erridx=0; er_fac_err[facidx].errs[erridx].code != -1; erridx++) {
351  |       if( er_fac_err[facidx].errs[erridx].code == errcode ) {
352  |        	/* FOUND! now set the error message format using facidx and erridx */
353  | 
354  |     	/* long error message without arguments */
355  | 	txtlong = er_fac_err[facidx].errs[erridx].text;
356  |     	
357  | 	/* set the mnemonic pointer if necessary */ 
358  | 	if( mode & ER_M_MNEMONIC ) {
359  | 	  ermne = er_fac_err[facidx].errs[erridx].mnem;
360  |         }
361  | 	break;
362  |       }
363  |     }
364  |     /*	return ""; */
365  |     /* no, do not return: bail out if the code is not defined */
366  |     assert( er_fac_err[facidx].errs[erridx].code != -1 );
367  |   }
368  | 
369  |   
370  |   
371  |   sprintf(thr_str, "%lu", (long int)pthread_self() );
372  | 
373  |   /* build the actual log message */
374  |   snprintf(erbuf, ER_MSGLEN, "%s %s-%s/%s %s-%s-%s %s ",
375  | 	   ( mode & ER_M_DATETIME ) ? tmbuf : "",
376  | 	   (mode & ER_M_PROGNAME) ? er_progname : "",
377  | 	   (mode & ER_M_PIDFULL)  ? er_pid : "",
378  | 	   (mode & ER_M_THR_ID )  ? thr_str : "",
379  | 	   (mode & ER_M_FACSYMB)  ? er_getfacsym(facwhere) : "",
380  | 	   er_getsevsym(sev, mode),
381  | 	   (mode & ER_M_MNEMONIC) ? ermne : "",
382  | 	   (mode & ER_M_TEXTLONG) ? txtlong : ""
383  | 	   );	
384  |   return erbuf;
385  | }
386  | 
387  | 
388  | /*++++++++++++++++++++++++++++++++++++++
389  |   
390  |   Browses the list of available paths, runs the filter check on every
391  |   path and logs the given message to all suitable paths.
392  | 
393  |   er_fac_code_t  facwhere     message's facility of call
394  | 
395  |   er_mask_t asp               message's aspect (if severity is INFO or DEBUG)
396  | 
397  |   int errcode                 message's error code
398  | 
399  |   char *msg                   text payload of the message
400  |   ++++++++++++++++++++++++++++++++++++++*/
401  | void er_logit(er_fac_code_t  facwhere, er_mask_t asp, int errcode, char *msg)
402  | {
403  |   char 	formbuf[ER_MSGLEN], tmbuf[32];
404  |   struct timeval tval;
405  |   struct tm tmstr;
406  | 
407  |   
408  | 
409  | 
410  |   TH_acquire_read_lock( &er_paths_lock );
411  |   {
412  |     GList *pitem, *fitem;
413  |       
414  |     for( pitem = g_list_first(er_pathlist);
415  | 	 pitem != NULL;
416  | 	 pitem = g_list_next(pitem)) {
417  | 	
418  |       er_path_t *pathptr = (er_path_t *)pitem->data;
419  | 
420  | 	
421  |       if( pathptr->active ) {
422  | 
423  | 	for( fitem = g_list_first(pathptr->filters);
424  | 	     fitem != NULL;
425  | 	     fitem = g_list_next(fitem)) {
426  | 	  
427  | 	  er_filter_t *filtptr = (er_filter_t *) fitem->data;
428  | 
429  | 	  
430  | 	  if( er_msgsel( filtptr, facwhere, asp, errcode) ) {
431  | 	    if ( pathptr->format & ER_M_DATETIME ) {
432  | 	      gettimeofday(&tval, NULL);
433  | 	      
434  | 	      localtime_r( & tval.tv_sec, & tmstr);
435  | 
436  | 	      strftime(tmbuf, sizeof(tmbuf), "%Y%m%d %H:%M:%S", &tmstr);
437  | 	    } else {
438  | 	      tmbuf[0]=0;
439  | 	    }
440  | 	    
441  | 	    er_format_line( formbuf, 
442  | 			    facwhere, asp, pathptr->format, errcode, tmbuf);
443  | 	    
444  | 	    er_logtopath( pathptr, formbuf, msg );
445  | 	    break; /* go to next path */
446  | 	  }
447  | 	}
448  |       }
449  |     }
450  |   } 
451  |   TH_release_read_lock( &er_paths_lock );
452  | }
453  | 
454  | /* check if anyone traces this particular aspect for this facility,
455  |    whether on DEBUG or INFO level */
456  | int ER_is_traced(er_fac_code_t  facwhere, er_mask_t asp) 
457  | {
458  |   int res;
459  | 
460  |   TH_acquire_read_lock( &er_paths_lock );
461  |   res = (er_asparray[facwhere] & asp );
462  |   TH_release_read_lock( &er_paths_lock );
463  | 
464  |   return res;
465  | }
466  | 
467  | 
468  | /* check if anyone traces this particular error for this facility.
469  |    For the moment, hardcoded to "always true".
470  |  */
471  | int ER_is_errorlogged(er_fac_code_t  facwhere, int errcode)
472  | {
473  | int i = 1;
474  | 
475  |  return i;
476  | }
477  | 
478  | int er_get_printmode(er_path_t *pathstruct) 
479  | {
480  |   return pathstruct->format;
481  | }
482  | 
483  | 
484  | /*++++++++++++++++++++++++++++++++++++++
485  |   
486  |   Entry point for predefined errors - this function will display a
487  |   predefined message for this error along with additional text.
488  | 
489  |   er_fac_code_t facwhere code of facility from which the error is reported
490  | 
491  |   int errcode            error code
492  | 
493  |   char *format           printf-style format for additional text, or ""
494  | 
495  |   ...                    printf-style arguments, if any
496  | 
497  |   ++++++++++++++++++++++++++++++++++++++*/
498  | void ER_perror(er_fac_code_t  facwhere, int errcode, char *format, ...)
499  | {
500  |   char 	erbuf[ER_MSGLEN];
501  |   va_list ap;
502  | 
503  |   if( ER_is_errorlogged( facwhere, errcode ) ) {      /* uses pathlist mutex */
504  | 
505  |     /* now, this takes most time: */
506  |     va_start(ap, format);
507  |     er_getmsg_parts(erbuf, sizeof(erbuf), format, ap );
508  |     va_end(ap);
509  |     
510  |     /* actually, here will be a loop once there are more paths possible. */
511  |     er_logit(facwhere, 
512  | 	   0,			       /* empty aspect mask for errors */
513  | 	   errcode, 
514  | 	   erbuf);				/* empty debug message */
515  |   }
516  | }
517  | 
518  | 
519  | /*++++++++++++++++++++++++++++++++++++++
520  |   
521  |   Internal function for message collection.
522  | 
523  |   er_fac_code_t facwhere code of facility from which the error is reported
524  | 
525  |   int sev                severity of the message
526  | 
527  |   er_mask_t asp          aspect of the message
528  | 
529  |   char *txt              printf-style format 
530  | 
531  |   va_list args           printf-style arguments, if any
532  |   ++++++++++++++++++++++++++++++++++++++*/
533  | static
534  | void er_asp_va(er_fac_code_t  facwhere, int sev,  er_mask_t asp, char *txt, 
535  | 		va_list args)
536  | {
537  |     char    erbuf[ER_MSGLEN];
538  | 
539  |     er_getmsg_parts(erbuf, sizeof(erbuf), txt, args );
540  |     er_logit(facwhere, asp, sev, erbuf);
541  | }
542  | 
543  | /*++++++++++++++++++++++++++++++++++++++
544  |   
545  |   Entry point for informational messages - this function will display a
546  |   message tagged with the INFO severity.
547  | 
548  |   er_fac_code_t facwhere code of facility from which the error is reported
549  | 
550  |   int sev                severity of the message
551  | 
552  |   er_mask_t asp          aspect of the message
553  | 
554  |   char *txt              printf-style format 
555  | 
556  |   ...                    printf-style arguments, if any
557  |   ++++++++++++++++++++++++++++++++++++++*/
558  | void ER_inf_va(er_fac_code_t  facwhere, er_mask_t asp, char *txt, ...)
559  | {
560  |     va_list   ap;
561  |     va_start(ap, txt);
562  |     er_asp_va( facwhere, ER_SEV_I, asp, txt, ap );
563  |     va_end(ap);
564  | }
565  | 
566  | /*++++++++++++++++++++++++++++++++++++++
567  |   
568  |   Entry point for debug/trace messages - this function will display a
569  |   message tagged with the DEBUG severity.
570  | 
571  |   er_fac_code_t facwhere code of facility from which the error is reported
572  | 
573  |   int sev                severity of the message
574  | 
575  |   er_mask_t asp          aspect of the message
576  | 
577  |   char *txt              printf-style format 
578  | 
579  |   ...                    printf-style arguments, if any
580  |   ++++++++++++++++++++++++++++++++++++++*/
581  | void ER_dbg_va(er_fac_code_t  facwhere, er_mask_t asp, char *txt, ...)
582  | {
583  |   char    erbuf[ER_MSGLEN];
584  |   va_list   ap;
585  | 
586  |   if( ER_is_traced( facwhere, asp ) ) {
587  |     
588  |     va_start(ap, txt);
589  |     er_getmsg_parts(erbuf, sizeof(erbuf), txt, ap );
590  |     va_end(ap);
591  |     
592  |     er_logit(facwhere, asp,  ER_SEV_D, erbuf);
593  |   }
594  | }
595  | 
596  | 
597  | /*++++++++++++++++++++++++++++++++++++++
598  |   
599  |   Initialisation function - setting global variables.
600  |   Can be done only by the master thread 
601  | 
602  |   char *progname    program name to be used in the log entries
603  | 
604  |   int processdefs   flag if the error definitions of the CA config file
605  |                     should be processed.
606  |   ++++++++++++++++++++++++++++++++++++++*/
607  | void ER_init(char *progname, int processdefs)
608  | {
609  |   /* create the hash with hashing and equality testing functions
610  |      specific for strings 
611  |   */ 
612  |   er_macro_hash = g_hash_table_new(g_str_hash, g_str_equal);
613  |   
614  |   TH_init_read_write_lock( &er_paths_lock ); /* created in open state */
615  | 
616  |   strncpy(er_progname, progname, 31);
617  |   er_progname[31] = 0;
618  | 
619  |   snprintf(er_pid, 10, "%d", getpid());
620  | 
621  |   /* now error definitions: first predefine macros */
622  |   ER_macro_predef();
623  |   /* then override them */
624  |   ER_proc_ca_macro();
625  | 
626  |   if( processdefs ) {
627  |     /* now process the definitions if allowed */
628  |     ER_proc_ca_err();
629  |   }  
630  | }