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.22 $
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 #define ER_IMPL
36 #include "erroutines.h"
37 #include <pthread.h>
38 #include <time.h>
39
40 #ifdef _LINUX
41 #include <sys/time.h>
42 #include <unistd.h>
43 #endif
44
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <fcntl.h>
48
49 #include "er_macro.h"
50
51 int NOERR(er_ret_t a)
/* [<][>][^][v][top][bottom][index][help] */
52 {
53 return ( ((a & 0xFFFF) == 0 ) /* the error part is 0 */
54 && ((a & 0xFFFF0000) != 0) ); /* the facility is non-zero */
55 }
56
57
58 int er_msgsel( er_filter_t *filtptr,
/* [<][>][^][v][top][bottom][index][help] */
59 er_fac_code_t facwhere,
60 er_mask_t asp,
61 er_ret_t errcode)
62 {
63 if( ! MA_isset( filtptr->fac_mask, facwhere) ) {
64 return 0;
65 }
66
67 /* check aspect only for DEBUG and INFO messages */
68 if( ( errcode == ER_SEV_D || errcode == ER_SEV_I )
69 && ! (asp & filtptr->asp_mask) ) {
70 return 0;
71 }
72
73 if( (errcode & 0xff000000) < filtptr->sev_min
74 || (errcode & 0xff000000) > filtptr->sev_max ) {
75 return 0;
76 }
77
78 if( filtptr->thr_id != 0
79 && filtptr->thr_id != pthread_self() ) {
80 return 0;
81 }
82
83 return 1;
84 }
85
86
87 /* fork & exec a program specified with argv, the print msg
88 on its stdin and exit. No redirection of stdout/stderr is done.
89
90 MT-note: Solaris fork1() duplicates only the calling thread.
91 So does Posix fork().
92 */
93 er_forkexec(char **argv, char *msg, int usepath)
/* [<][>][^][v][top][bottom][index][help] */
94 {
95 int PipeEnds[2];
96 int status, cpid;
97
98 pipe(PipeEnds);
99
100 #define PIP_WR 1
101 #define PIP_RD 0
102
103 #ifdef _POSIX_PTHREAD_SEMANTICS
104 #define fork1 fork
105 #endif
106
107 if((cpid=fork1()) == 0) /* child */
108 {
109 dup2( PipeEnds[PIP_RD], 0 );
110 close( PipeEnds[PIP_WR] ); /* pipe input */
111 if( usepath ) {
112 execvp(argv[0], argv);
113 }
114 else {
115 execv(argv[0], argv);
116 }
117 perror("Exec failed: ");
118 exit(-1);
119 }
120 close( PipeEnds[PIP_RD] );
121
122 write( PipeEnds[PIP_WR], msg, strlen(msg) );
123 close( PipeEnds[PIP_WR] );
124
125 wait(&status);
126 }
127
128 static
129 void
130 er_logtopath(er_path_t *pathptr, char *form, char *msg)
/* [<][>][^][v][top][bottom][index][help] */
131 {
132
133 char fullline[ER_MSGLEN+ER_ERRLEN+4];
134
135 /* MUTEX :
136
137 So, while the most of the work is done composing the message
138 according to the format set in the path descriptor (mode),
139 the output should also be locked.
140
141 here the mutex associated with the path should be set.
142 However, another mutex should be already used to protect other threads
143 from reading the path description while it is modified by the master
144 thread. An RW lock can be used for this.
145
146 Fortunately, fputs is MT-Safe in Solaris.
147 */
148
149 int fd;
150
151 /* bound checking done already for form & msg */
152 strcpy(fullline, form);
153 strcat(fullline, msg);
154 strcat(fullline, "\n");
155
156 switch(pathptr->type) {
157 case ER_PATH_SOCK:
158 fd = pathptr->descr.sock.fd;
159 if( write(fd, fullline, strlen(fullline)) == -1 ) {
160 perror("ER logging ");
161 }
162 break;
163 case ER_PATH_NAME:
164 {
165 char *filename;
166 char constructed[128], datestr[10];
167 struct timeval tval;
168 struct tm tmstr;
169
170 if( pathptr->descr.name.date == 0 ) {
171 filename = pathptr->descr.name.filename;
172 }
173 else {
174 /* construct the filename for the paths with DATE option */
175 strcpy( constructed, pathptr->descr.name.filename );
176
177 gettimeofday(&tval, NULL);
178 localtime_r( & tval.tv_sec, &tmstr);
179 strftime(datestr, 10, ".%Y%m%d", &tmstr);
180
181 strcat( constructed, datestr );
182 filename = constructed;
183 }
184 fd=open(filename, O_WRONLY|O_APPEND|O_CREAT, 0755 );
185 if( fd > 0 ) {
186 /* XXX lock ? According to SK, not needed as long as it's on one
187 machine - the 'append' mode will make sure things are not garbled.
188 */
189 if( write(fd, fullline, strlen(fullline)) == -1 ) {
190 perror("ER logging ");
191 }
192 /* XXX unlock ? */
193 close(fd);
194 }
195 else {
196 fprintf(stderr, "ER: cannot open log file %s ",
197 pathptr->descr.name.filename);
198 perror("");
199 }
200 }
201 break;
202
203 case ER_PATH_EXEC:
204 er_forkexec(pathptr->descr.exec.argv,
205 fullline,
206 pathptr->descr.exec.usepath );
207 break;
208 default:
209 die; /* not implemented */
210 }
211 }
212
213 void
214 er_getmsg_parts(char *buf, int buflen, char *fmttxt, va_list args)
/* [<][>][^][v][top][bottom][index][help] */
215 {
216 /* build the error message using vsnprintf */
217 vsnprintf(buf, buflen, fmttxt, args);
218 }
219
220
221
222 /* TWO CONSTANTS DEFINE THE LENGTH OF STRINGS HERE:
223 ER_MSGLEN - max length of the line to be logged
224 ER_ERRLEN - max length of the error message
225 */
226 char *er_format_line(char *erbuf, er_fac_code_t facwhere,
/* [<][>][^][v][top][bottom][index][help] */
227 er_mask_t asp, int mode, int errcode,
228 char *tmbuf)
229 {
230 int fac, err, sev;
231 int facidx, erridx;
232 char thr_str[10], *ermne, *txtlong="";
233
234 /* init to "" */
235 erbuf[0] = 0;
236 ermne = "";
237
238 sev = ( errcode & 0xff000000 ); /* not shifted */
239 fac = ( errcode & 0x00ff0000 ) >> 16;
240 err = ( errcode & 0x0000ffff ); /* not shifted */
241
242 /* take the overridden value (facwhere) in case of doubt */
243 if(facwhere != fac) {
244 fac = facwhere;
245 }
246
247 for (facidx=0; facidx<FAC_LAST; facidx++) {
248 if( er_fac_err[facidx].code == fac ) {
249 break;
250 }
251 }
252
253 /* now, if we got to the last one and it's not the right one,
254 the system is not configured properly */
255 if(facidx==FAC_LAST) {
256 assert( er_fac_err[facidx].code == fac ); /* just bail out. */
257 }
258
259 /* still alive ? OK, build the message ...*/
260
261 /* ... using facidx/erridx if it's not a DEBUG or INFO */
262 switch( sev ) {
263 case ER_SEV_D:
264 ermne = "DEBUG";
265 break;
266 case ER_SEV_I:
267 ermne = "INFO";
268 break;
269 default:
270 /* OK, go to the module table. bail out if not initialized */
271 assert( er_fac_err[facidx].errs != NULL );
272
273 for(erridx=0; er_fac_err[facidx].errs[erridx].code != -1; erridx++) {
274 if( er_fac_err[facidx].errs[erridx].code == errcode ) {
275 /* FOUND! now set the error message format using facidx and erridx */
276
277 /* long error message without arguments */
278 txtlong = er_fac_err[facidx].errs[erridx].text;
279
280 /* set the mnemonic pointer if necessary */
281 if( mode & ER_M_MNEMONIC ) {
282 ermne = er_fac_err[facidx].errs[erridx].mnem;
283 }
284 break;
285 }
286 }
287 /* return ""; */
288 /* no, do not return: bail out if the code is not defined */
289 assert( er_fac_err[facidx].errs[erridx].code != -1 );
290 }
291
292
293
294 sprintf(thr_str, "%d", pthread_self() );
295
296 /* build the actual log message */
297 snprintf(erbuf, ER_MSGLEN, "%s %s-%s/%s %s-%s-%s %s ",
298 ( mode & ER_M_DATETIME ) ? tmbuf : "",
299 (mode & ER_M_PROGNAME) ? er_progname : "",
300 (mode & ER_M_PIDFULL) ? er_pid : "",
301 (mode & ER_M_THR_ID ) ? thr_str : "",
302 (mode & ER_M_FACSYMB) ? er_getfacsym(facwhere) : "",
303 er_getsevsym(sev, mode),
304 (mode & ER_M_MNEMONIC) ? ermne : "",
305 (mode & ER_M_TEXTLONG) ? txtlong : ""
306 );
307 return erbuf;
308 }
309
310 void er_logit(er_fac_code_t facwhere, er_mask_t asp, int errcode, char *msg)
/* [<][>][^][v][top][bottom][index][help] */
311 {
312 char formbuf[ER_MSGLEN], tmbuf[32];
313 struct timeval tval;
314 struct tm tmstr;
315
316
317
318 /*er_pathlist_mutex;*/
319
320 {
321 GList *pitem, *fitem;
322
323 for( pitem = g_list_first(er_pathlist);
324 pitem != NULL;
325 pitem = g_list_next(pitem)) {
326
327 er_path_t *pathptr = (er_path_t *)pitem->data;
328
329
330 if( pathptr->active ) {
331
332 for( fitem = g_list_first(pathptr->filters);
333 fitem != NULL;
334 fitem = g_list_next(fitem)) {
335
336 er_filter_t *filtptr = (er_filter_t *) fitem->data;
337
338
339 if( er_msgsel( filtptr, facwhere, asp, errcode) ) {
340 if ( pathptr->format & ER_M_DATETIME ) {
341 gettimeofday(&tval, NULL);
342
343 localtime_r( & tval.tv_sec, & tmstr);
344
345 strftime(tmbuf, sizeof(tmbuf), "%Y%m%d %H:%M:%S", &tmstr);
346 } else {
347 tmbuf[0]=0;
348 }
349
350 er_format_line( formbuf,
351 facwhere, asp, pathptr->format, errcode, tmbuf);
352
353 er_logtopath( pathptr, formbuf, msg );
354 break; /* go to next path */
355 }
356 }
357 }
358 }
359 }
360 }
361
362 /* check if anyone traces this particular aspect for this facility,
363 whether on DEBUG or INFO level */
364 int ER_is_traced(er_fac_code_t facwhere, er_mask_t asp)
/* [<][>][^][v][top][bottom][index][help] */
365 {
366 return (er_asparray[facwhere] & asp );
367 }
368 /* check if anyone traces this particular error for this facility */
369 int ER_is_errorlogged(er_fac_code_t facwhere, int errcode)
/* [<][>][^][v][top][bottom][index][help] */
370 {
371 int i = 1;
372
373 return i;
374 }
375
376 int er_get_printmode(er_path_t *pathstruct)
/* [<][>][^][v][top][bottom][index][help] */
377 {
378 return pathstruct->format;
379 }
380
381 void ER_perror(er_fac_code_t facwhere, int errcode, char *format, ...)
/* [<][>][^][v][top][bottom][index][help] */
382 {
383 char erbuf[ER_MSGLEN];
384 va_list ap;
385
386 if( ER_is_errorlogged( facwhere, errcode ) ) { /* uses pathlist mutex */
387
388 /* now, this takes most time: */
389 va_start(ap, format);
390 er_getmsg_parts(erbuf, sizeof(erbuf), format, ap );
391 va_end(ap);
392
393 /* actually, here will be a loop once there are more paths possible. */
394 er_logit(facwhere,
395 0, /* empty aspect mask for errors */
396 errcode,
397 erbuf); /* empty debug message */
398 }
399 }
400
401 void ER_asp_va(er_fac_code_t facwhere, int sev, er_mask_t asp, char *txt,
/* [<][>][^][v][top][bottom][index][help] */
402 va_list args)
403 {
404 char erbuf[ER_MSGLEN];
405
406 er_getmsg_parts(erbuf, sizeof(erbuf), txt, args );
407 er_logit(facwhere, asp, sev, erbuf);
408 }
409
410 void ER_inf_va(er_fac_code_t facwhere, er_mask_t asp, char *txt, ...)
/* [<][>][^][v][top][bottom][index][help] */
411 {
412 va_list ap;
413 va_start(ap, txt);
414 ER_asp_va( facwhere, ER_SEV_I, asp, txt, ap );
415 va_end(ap);
416 }
417
418
419 void ER_dbg_va(er_fac_code_t facwhere, er_mask_t asp, char *txt, ...)
/* [<][>][^][v][top][bottom][index][help] */
420 {
421 char erbuf[ER_MSGLEN];
422 va_list ap;
423
424 if( ER_is_traced( facwhere, asp ) ) {
425
426 va_start(ap, txt);
427 er_getmsg_parts(erbuf, sizeof(erbuf), txt, ap );
428 va_end(ap);
429
430 er_logit(facwhere, asp, ER_SEV_D, erbuf);
431 }
432 }
433
434
435 /* Set GLOBAL VARIABLES == can be done only by the master thread */
436 void ER_init(char *progname, int processdefs)
/* [<][>][^][v][top][bottom][index][help] */
437 {
438
439 strncpy(er_progname, progname, 31);
440 er_progname[31] = 0;
441
442 snprintf(er_pid, 10, "%d", getpid());
443
444 /* now error definitions: first predefine macros */
445 ER_macro_predef();
446 /* then override them */
447 ER_proc_ca_macro();
448
449 if( processdefs ) {
450 /* now process the definitions if allowed */
451 ER_proc_ca_err();
452 }
453
454 }