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