Source File modules/er/er.c

    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  | }