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