## 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.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)
         /* [<][>][^][v][top][bottom][index][help] */
      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,
         /* [<][>][^][v][top][bottom][index][help] */
      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)
         /* [<][>][^][v][top][bottom][index][help] */
      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)
         /* [<][>][^][v][top][bottom][index][help] */
     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)
         /* [<][>][^][v][top][bottom][index][help] */
     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,
         /* [<][>][^][v][top][bottom][index][help] */
     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)
         /* [<][>][^][v][top][bottom][index][help] */
     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)
         /* [<][>][^][v][top][bottom][index][help] */
     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)
         /* [<][>][^][v][top][bottom][index][help] */
     370 {
     371 int i = 1;
     372
     373  return i;
     374 }
     375
     376 int er_get_printmode(er_path_t *pathstruct)
         /* [<][>][^][v][top][bottom][index][help] */
     377 {
     378   return pathstruct->format;
     379 }
     380
     381 void ER_perror(er_fac_code_t  facwhere, int errcode, char *format, ...)
         /* [<][>][^][v][top][bottom][index][help] */
     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,
         /* [<][>][^][v][top][bottom][index][help] */
     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, ...)
         /* [<][>][^][v][top][bottom][index][help] */
     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, ...)
         /* [<][>][^][v][top][bottom][index][help] */
     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)
         /* [<][>][^][v][top][bottom][index][help] */
     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 }

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