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)
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,
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)
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)
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)
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,
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)
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)
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)
370 | {
371 | int i = 1;
372 |
373 | return i;
374 | }
375 |
376 | int er_get_printmode(er_path_t *pathstruct)
377 | {
378 | return pathstruct->format;
379 | }
380 |
381 | void ER_perror(er_fac_code_t facwhere, int errcode, char *format, ...)
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,
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, ...)
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, ...)
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)
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 | }