1 | /*************************************** 2 | $Revision: 1.30 $ 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,2001,2002 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 "rip.h" 37 | 38 | #include <pthread.h> 39 | #include <time.h> 40 | 41 | #ifdef HAVE_SYS_TIME_H 42 | #include <sys/time.h> 43 | #endif 44 | 45 | #ifdef HAVE_UNISTD_H 46 | #include <unistd.h> 47 | #endif 48 | 49 | #include <sys/types.h> 50 | #include <sys/stat.h> 51 | #include <fcntl.h> 52 | #include <sys/wait.h> 53 | 54 | 55 | /*++++++++++++++++++++++++++++++++++++++ 56 | Simple check if the error code is a success (severity = 0). 57 | Detects (some) invalid codes - those whose facility part is 0. 58 | 59 | int NOERR returns 1 if the code is valid and is a success 60 | returns 0 if the code is invalid or is a real error 61 | (the severity is non-zero). 62 | 63 | er_ret_t a error code to be checked. 64 | ++++++++++++++++++++++++++++++++++++++*/ 65 | int NOERR(er_ret_t a) 66 | { 67 | return ( ((a & 0xFFFF) == 0 ) /* the error part is 0 */ 68 | && ((a & 0xFFFF0000) != 0) ); /* the facility is non-zero */ 69 | } 70 | 71 | 72 | /*++++++++++++++++++++++++++++++++++++++ 73 | 74 | Message selection routine. Checks if the message coming from the 75 | current thread, tagged with the given error code, facility and 76 | aspect matches the given filter. 77 | 78 | int er_msgsel returns 1 for a match, 0 otherwise. 79 | 80 | er_filter_t *filtptr filter to be matched 81 | 82 | er_fac_code_t facwhere message's facility of call 83 | 84 | er_mask_t asp message's aspect (if severity is INFO or DEBUG) 85 | 86 | er_ret_t errcode message's error code 87 | ++++++++++++++++++++++++++++++++++++++*/ 88 | 89 | int er_msgsel( er_filter_t *filtptr, 90 | er_fac_code_t facwhere, 91 | er_mask_t asp, 92 | er_ret_t errcode) 93 | { 94 | if( ! MA_isset( filtptr->fac_mask, facwhere) ) { 95 | return 0; 96 | } 97 | 98 | /* check aspect only for DEBUG and INFO messages */ 99 | if( ( errcode == ER_SEV_D || errcode == ER_SEV_I ) 100 | && ! (asp & filtptr->asp_mask) ) { 101 | return 0; 102 | } 103 | 104 | if( (errcode & 0xff000000) < filtptr->sev_min 105 | || (errcode & 0xff000000) > filtptr->sev_max ) { 106 | return 0; 107 | } 108 | 109 | if( filtptr->thr_id != 0 110 | && filtptr->thr_id != pthread_self() ) { 111 | return 0; 112 | } 113 | 114 | return 1; 115 | } 116 | 117 | 118 | 119 | 120 | 121 | /*++++++++++++++++++++++++++++++++++++++ 122 | Fork & exec a program specified with argv, the print msg 123 | on its stdin and exit. No redirection of stdout/stderr is done. 124 | 125 | MT-note: Solaris fork1() duplicates only the calling thread. 126 | So does Posix fork(). 127 | 128 | char **argv argv array for the exec call 129 | 130 | char *msg text payload to be printed on the stdin of forked process 131 | 132 | int usepath flag indicating if the PATH environmental variable 133 | should be used by the exec call. 134 | ++++++++++++++++++++++++++++++++++++++*/ 135 | void er_forkexec(char **argv, char *msg, int usepath) 136 | { 137 | int PipeEnds[2]; 138 | int status, cpid; 139 | 140 | pipe(PipeEnds); 141 | 142 | #define PIP_WR 1 143 | #define PIP_RD 0 144 | 145 | #ifdef _POSIX_PTHREAD_SEMANTICS 146 | #define fork1 fork 147 | #endif 148 | 149 | if((cpid=fork1()) == 0) /* child */ 150 | { 151 | dup2( PipeEnds[PIP_RD], 0 ); 152 | close( PipeEnds[PIP_WR] ); /* pipe input */ 153 | if( usepath ) { 154 | execvp(argv[0], argv); 155 | } 156 | else { 157 | execv(argv[0], argv); 158 | } 159 | perror("Exec failed: "); 160 | exit(-1); 161 | } 162 | close( PipeEnds[PIP_RD] ); 163 | 164 | write( PipeEnds[PIP_WR], msg, strlen(msg) ); 165 | close( PipeEnds[PIP_WR] ); 166 | 167 | wait(&status); 168 | } 169 | 170 | 171 | /*++++++++++++++++++++++++++++++++++++++ 172 | 173 | Main function logging a message to a path. The formatted message 174 | parts and the message text itself are given separately to avoid 175 | wasting time to remake them, because the message text may be the 176 | same while the path formats are different. 177 | 178 | er_path_t *pathptr pointer to the path structure where the message 179 | should go. 180 | 181 | char *form format part of the message 182 | 183 | char *msg payload part of the message 184 | ++++++++++++++++++++++++++++++++++++++*/ 185 | static 186 | void 187 | er_logtopath(er_path_t *pathptr, char *form, char *msg) 188 | { 189 | 190 | char fullline[ER_MSGLEN+ER_ERRLEN+4]; 191 | 192 | /* MUTEX : 193 | 194 | So, while the most of the work is done composing the message 195 | according to the format set in the path descriptor (mode), 196 | the output should also be locked. 197 | 198 | here the mutex associated with the path should be set. 199 | However, another mutex should be already used to protect other threads 200 | from reading the path description while it is modified by the master 201 | thread. An RW lock can be used for this. 202 | 203 | Fortunately, fputs is MT-Safe in Solaris. 204 | */ 205 | 206 | int fd; 207 | 208 | /* bound checking done already for form & msg */ 209 | strcpy(fullline, form); 210 | strcat(fullline, msg); 211 | strcat(fullline, "\n"); 212 | 213 | switch(pathptr->type) { 214 | case ER_PATH_SOCK: 215 | fd = pathptr->descr.sock.fd; 216 | /* XXX: write on a socket may write less than the requested number 217 | of bytes and not be an error - shane */ 218 | if( write(fd, fullline, strlen(fullline)) == -1 ) { 219 | perror("ER logging "); 220 | } 221 | break; 222 | case ER_PATH_NAME: 223 | { 224 | char *filename; 225 | char constructed[128], datestr[10]; 226 | struct timeval tval; 227 | struct tm tmstr; 228 | 229 | if( pathptr->descr.name.date == 0 ) { 230 | filename = pathptr->descr.name.filename; 231 | } 232 | else { 233 | /* construct the filename for the paths with DATE option */ 234 | strcpy( constructed, pathptr->descr.name.filename ); 235 | 236 | gettimeofday(&tval, NULL); 237 | localtime_r( & tval.tv_sec, &tmstr); 238 | strftime(datestr, 10, ".%Y%m%d", &tmstr); 239 | 240 | strcat( constructed, datestr ); 241 | filename = constructed; 242 | } 243 | fd=open(filename, O_WRONLY|O_APPEND|O_CREAT, 0666 ); 244 | if( fd > 0 ) { 245 | /* XXX lock ? According to SK, not needed as long as it's on one 246 | machine - the 'append' mode will make sure things are not garbled. 247 | Note: Two months of operation and no a single garbled line yet ;) SK 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) 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, 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, "%lu", (long int)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) 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) 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) 472 | { 473 | int i = 1; 474 | 475 | return i; 476 | } 477 | 478 | int er_get_printmode(er_path_t *pathstruct) 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, ...) 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, 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, ...) 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, ...) 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) 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 | }