## 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.27 $
       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, 0666 );
     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] */