modules/er/er.c
/* [<][>][^][v][top][bottom][index][help] */
FUNCTIONS
This source file includes following functions.
- NOERR
- er_msgsel
- er_forkexec
- er_logtopath
- er_getmsg_parts
- er_format_line
- er_logit
- ER_is_traced
- ER_is_errorlogged
- er_get_printmode
- ER_perror
- er_asp_va
- ER_inf_va
- ER_dbg_va
- ER_init
1 /***************************************
2 $Revision: 1.29 $
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 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)
/* [<][>][^][v][top][bottom][index][help] */
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,
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][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, "%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)
/* [<][>][^][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 }