Source File modules/er/er.c

    1    | /***************************************
    2    |   $Revision: 1.26 $
    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   | #include <pthread.h>
    36   | #include <time.h>
    37   |
    38   | #ifdef HAVE_SYS_TIME_H
    39   | #include <sys/time.h>
    40   | #endif
    41   |
    42   | #ifdef HAVE_UNISTD_H
    43   | #include <unistd.h>
    44   | #endif
    45   |
    46   | #include <sys/types.h>
    47   | #include <sys/stat.h>
    48   | #include <fcntl.h>
    49   | #include <sys/wait.h>
    50   |
    51   | #define ER_IMPL
    52   | #include "erroutines.h"
    53   |
    54   | #include "er_macro.h"
    55   | #include "er_paths.h"
    56   |
    57   |
    58   | /*++++++++++++++++++++++++++++++++++++++
    59   |   Simple check if the error code is a success (severity = 0).
    60   |   Detects (some) invalid codes - those whose facility part is 0.
    61   |
    62   |   int NOERR      returns 1 if the code is valid and is a success
    63   |                  returns 0 if the code is invalid or is a real error
    64   | 		 (the severity is non-zero).
    65   |
    66   |   er_ret_t a     error code to be checked.
    67   |   ++++++++++++++++++++++++++++++++++++++*/
    68   | int NOERR(er_ret_t a)
    69   | {
    70   |   return (    ((a & 0xFFFF) == 0 )          /* the error part is 0 */
    71   | 	      && ((a & 0xFFFF0000) != 0) ); /* the facility is non-zero */
    72   | }
    73   |
    74   |
    75   | /*++++++++++++++++++++++++++++++++++++++
    76   |
    77   |   Message selection routine. Checks if the message coming from the
    78   |   current thread, tagged with the given error code, facility and
    79   |   aspect matches the given filter.
    80   |
    81   |   int er_msgsel             returns 1 for a match, 0 otherwise.
    82   |
    83   |   er_filter_t *filtptr      filter to be matched
    84   |
    85   |   er_fac_code_t facwhere    message's facility of call
    86   |
    87   |   er_mask_t asp             message's aspect (if severity is INFO or DEBUG)
    88   |
    89   |   er_ret_t errcode          message's error code
    90   |   ++++++++++++++++++++++++++++++++++++++*/
    91   |
    92   | int er_msgsel( er_filter_t *filtptr,
    93   | 	       er_fac_code_t facwhere,
    94   | 	       er_mask_t asp,
    95   | 	       er_ret_t errcode)
    96   | {
    97   |   if( ! MA_isset( filtptr->fac_mask, facwhere) ) {
    98   |         return 0;
    99   |   }
    100  |
    101  |   /* check aspect only for DEBUG and INFO messages */
    102  |   if( ( errcode == ER_SEV_D || errcode == ER_SEV_I )
    103  |       && ! (asp & filtptr->asp_mask) ) {
    104  |     return 0;
    105  |   }
    106  |
    107  |   if(    (errcode & 0xff000000) < filtptr->sev_min
    108  |       || (errcode & 0xff000000) > filtptr->sev_max ) {
    109  |     return 0;
    110  |   }
    111  |
    112  |   if(    filtptr->thr_id != 0
    113  |       && filtptr->thr_id != pthread_self() ) {
    114  |     return 0;
    115  |   }
    116  |
    117  |   return 1;
    118  | }
    119  |
    120  |
    121  |
    122  |
    123  |
    124  | /*++++++++++++++++++++++++++++++++++++++
    125  |   Fork & exec a program specified with argv, the print msg
    126  |   on its stdin and exit. No redirection of stdout/stderr is done.
    127  |
    128  |   MT-note: Solaris fork1() duplicates only the calling thread.
    129  |   So does Posix fork().
    130  |
    131  |   char **argv       argv array for the exec call
    132  |
    133  |   char *msg         text payload to be printed on the stdin of forked process
    134  |
    135  |   int usepath       flag indicating if the PATH environmental variable
    136  |                     should be used by the exec call.
    137  |   ++++++++++++++++++++++++++++++++++++++*/
    138  | void er_forkexec(char **argv, char *msg, int usepath)
    139  | {
    140  |   int PipeEnds[2];
    141  |   int status, cpid;
    142  |
    143  |   pipe(PipeEnds);
    144  |
    145  | #define PIP_WR 1
    146  | #define PIP_RD 0
    147  |
    148  | #ifdef _POSIX_PTHREAD_SEMANTICS
    149  | #define fork1 fork
    150  | #endif
    151  |
    152  |   if((cpid=fork1()) == 0)	        /* child */
    153  |     {
    154  |       dup2( PipeEnds[PIP_RD], 0 );
    155  |       close( PipeEnds[PIP_WR] );	/* pipe input */
    156  |       if( usepath ) {
    157  | 	execvp(argv[0], argv);
    158  |       }
    159  |       else {
    160  | 	execv(argv[0], argv);
    161  |       }
    162  |       perror("Exec failed: ");
    163  |       exit(-1);
    164  |     }
    165  |   close( PipeEnds[PIP_RD] );
    166  |
    167  |   write( PipeEnds[PIP_WR], msg, strlen(msg) );
    168  |   close( PipeEnds[PIP_WR] );
    169  |
    170  |   wait(&status);
    171  | }
    172  |
    173  |
    174  | /*++++++++++++++++++++++++++++++++++++++
    175  |
    176  |   Main function logging a message to a path. The formatted message
    177  |   parts and the message text itself are given separately to avoid
    178  |   wasting time to remake them, because the message text may be the
    179  |   same while the path formats are different.
    180  |
    181  |   er_path_t *pathptr        pointer to the path structure where the message
    182  |                             should go.
    183  |
    184  |   char *form                format part of the message
    185  |
    186  |   char *msg                 payload part of the message
    187  |   ++++++++++++++++++++++++++++++++++++++*/
    188  | static
    189  | void
    190  | er_logtopath(er_path_t *pathptr, char *form, char *msg)
    191  | {
    192  |
    193  |   char fullline[ER_MSGLEN+ER_ERRLEN+4];
    194  |
    195  |   /* MUTEX :
    196  |
    197  |      So, while the most of the work is done composing the message
    198  |      according to the format set in the path descriptor (mode),
    199  |      the output should also be locked.
    200  |
    201  |      here the mutex associated with the path should be set.
    202  |      However, another mutex should be already used to protect other threads
    203  |      from reading the path description while it is modified by the master
    204  |      thread. An RW lock can be used for this.
    205  |
    206  |      Fortunately, fputs is MT-Safe in Solaris.
    207  |   */
    208  |
    209  |   int fd;
    210  |
    211  |   /* bound checking done already for form & msg */
    212  |   strcpy(fullline, form);
    213  |   strcat(fullline, msg);
    214  |   strcat(fullline, "\n");
    215  |
    216  |   switch(pathptr->type) {
    217  |   case ER_PATH_SOCK:
    218  |     fd = pathptr->descr.sock.fd;
    219  |     if( write(fd, fullline, strlen(fullline)) == -1 ) {
    220  |       perror("ER logging ");
    221  |     }
    222  |     break;
    223  |   case ER_PATH_NAME:
    224  |     {
    225  |       char *filename;
    226  |       char constructed[128], datestr[10];
    227  |       struct timeval tval;
    228  |       struct tm tmstr;
    229  |
    230  |       if( pathptr->descr.name.date == 0 ) {
    231  | 	filename = pathptr->descr.name.filename;
    232  |       }
    233  |       else {
    234  | 	/* construct the filename for the paths with DATE option */
    235  | 	strcpy( constructed, pathptr->descr.name.filename );
    236  |
    237  | 	gettimeofday(&tval, NULL);
    238  | 	localtime_r( & tval.tv_sec,   &tmstr);
    239  | 	strftime(datestr, 10, ".%Y%m%d", &tmstr);
    240  |
    241  | 	strcat( constructed, datestr );
    242  | 	filename = constructed;
    243  |       }
    244  |       fd=open(filename, O_WRONLY|O_APPEND|O_CREAT, 0644 );
    245  |       if( fd > 0 ) {
    246  | 	/* XXX lock ? According to SK, not needed as long as it's on one
    247  | 	 machine - the 'append' mode will make sure things are not garbled.
    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, "%d", 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  | }