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.27 $
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)
/* [<][>][^][v][top][bottom][index][help] */
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,
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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, 0666 );
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)
/* [<][>][^][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, "%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)
/* [<][>][^][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 }