modules/er/er.c

/* [<][>]
[^][v][top][bottom][index][help] */

FUNCTIONS

This source file includes following functions.
  1. NOERR
  2. er_msgsel
  3. er_forkexec
  4. er_logtopath
  5. er_getmsg_parts
  6. er_format_line
  7. er_logit
  8. ER_is_traced
  9. ER_is_errorlogged
  10. er_get_printmode
  11. ER_perror
  12. er_asp_va
  13. ER_inf_va
  14. ER_dbg_va
  15. ER_init

   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) 
     /* [<][>][^][v][top][bottom][index][help] */
  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, 
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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, 
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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) 
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 472 {
 473 int i = 1;
 474 
 475  return i;
 476 }
 477 
 478 int er_get_printmode(er_path_t *pathstruct) 
     /* [<][>][^][v][top][bottom][index][help] */
 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, ...)
     /* [<][>][^][v][top][bottom][index][help] */
 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, 
     /* [<][>][^][v][top][bottom][index][help] */
 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, ...)
     /* [<][>][^][v][top][bottom][index][help] */
 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, ...)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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 }

/* [<][>][^][v][top][bottom][index][help] */