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) 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, 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) 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) 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) 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, "%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) 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 | }