1 | /***************************************
2 | $Revision: 1.7 $
3 |
4 | Error reporting (er) er.c - library of functions to uniformly report errors.
5 |
6 | Status: NOT REVUED, TESTED,
7 |
8 | Design and implementation by: Marek Bukowy
9 |
10 | ******************/ /******************
11 | Copyright (c) 1999 RIPE NCC
12 |
13 | All Rights Reserved
14 |
15 | Permission to use, copy, modify, and distribute this software and its
16 | documentation for any purpose and without fee is hereby granted,
17 | provided that the above copyright notice appear in all copies and that
18 | both that copyright notice and this permission notice appear in
19 | supporting documentation, and that the name of the author not be
20 | used in advertising or publicity pertaining to distribution of the
21 | software without specific, written prior permission.
22 |
23 | THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
25 | AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
26 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
27 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
28 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
29 | ***************************************/
30 |
31 | #define ER_IMPL
32 | #include "erroutines.h"
33 |
34 | char *er_getsev( int sev, int mode )
35 | {
36 | int i;
37 |
38 | for(i=0; er_level_a[i].sev != 0; i++) {
39 | if (er_level_a[i].sev == sev) {
40 | break;
41 | }
42 | }
43 |
44 | switch( mode & 0x03 ) {
45 | case ER_M_SEVNONE: /* no severity indication */
46 | return ""; /* "" goes to program text, so returning a
47 | pointer to it is OK */
48 | case ER_M_SEVCHAR: /* one-letter severity indication */
49 | return er_level_a[i].chr;
50 | case ER_M_SEVLONG: /* long severity indication */
51 | return er_level_a[i].txt;
52 | }
53 | return "";
54 | }
55 |
56 | char *er_getfacsym(int faccode)
57 | {
58 | int facidx;
59 |
60 | if( faccode != FAC_NONE ) {
61 | for (facidx=0; facidx<FAC_LAST; facidx++) {
62 | if( er_main_err[facidx].code == faccode ) {
63 | break;
64 | }
65 | }
66 | return er_main_err[facidx].name;
67 | }
68 | else return "";
69 | }
70 |
71 | /* TWO CONSTANTS DEFINE THE LENGTH OF STRINGS HERE:
72 | ER_MSGLEN - max length of the line to be logged
73 | ER_ERRLEN - max length of the error message
74 | */
75 | char *er_getmsg_parts(int facwhere, int errcode, int mode,
76 | char *buf, char *fmttxt, va_list args)
77 | {
78 | int fac, err, sev;
79 | int facidx, erridx;
80 | char erbuf[ER_ERRLEN], thr_str[10], *ermne;
81 |
82 | /* init to "" */
83 | erbuf[0] = 0;
84 | ermne = "";
85 |
86 | sev = ( errcode & 0xff000000 ); /* not shifted */
87 | fac = ( errcode & 0x00ff0000 ) >> 16;
88 | err = ( errcode & 0x0000ffff ); /* not shifted */
89 |
90 | for (facidx=0; facidx<FAC_LAST; facidx++) {
91 | if( er_main_err[facidx].code == fac ) {
92 | break;
93 | }
94 | }
95 |
96 | /* now, if we got to the last one and it's not the right one,
97 | the system is not configured properly */
98 | if(facidx==FAC_LAST) {
99 | assert( er_main_err[facidx].code == fac ); /* just bail out. */
100 | }
101 |
102 | /* still alive ? OK, build the message ...*/
103 |
104 | /* ... using facidx/erridx if it's not a DEBUG */
105 | if( sev != ER_SEV_D ) {
106 | /* OK, go to the module table. bail out if not initialized */
107 | assert( er_main_err[facidx].errs != NULL );
108 |
109 | for(erridx=0; er_main_err[facidx].errs[erridx].code != -1; erridx++) {
110 | if( er_main_err[facidx].errs[erridx].code == errcode ) {
111 | /* FOUND! now set the error message format using facidx and erridx */
112 |
113 | /* build error message with arguments */
114 | if( mode & ER_M_TEXTLONG ) {
115 | fmttxt = er_main_err[facidx].errs[erridx].text;
116 | }
117 | /* set the mnemonic pointer if necessary */
118 | if( mode & ER_M_MNEMONIC ) {
119 | ermne = er_main_err[facidx].errs[erridx].mnem;
120 | }
121 | break;
122 | }
123 | }
124 | /* return ""; */
125 | /* no, do not return: bail out if the code is not defined */
126 | assert( er_main_err[facidx].errs[erridx].code != -1 );
127 | }
128 | else {
129 | ermne = "DEBUG";
130 | }
131 |
132 | /* build the error message using vsnprintf */
133 | vsnprintf(erbuf, ER_ERRLEN, fmttxt, args);
134 |
135 | sprintf(thr_str, "%d", pthread_self() );
136 |
137 | /* build the actual log message */
138 | snprintf(buf, ER_MSGLEN, "%s-%s/%s %s-%s-%s %s",
139 | (mode & ER_M_PROGNAME) ? er_progname : "",
140 | (mode & ER_M_PIDFULL) ? er_pid : "",
141 | (mode & ER_M_THR_ID ) ? thr_str : "",
142 | (mode & ER_M_FACSYMB) ? er_getfacsym(facwhere) : "",
143 | er_getsev(sev, mode),
144 | ermne,
145 | erbuf
146 | );
147 | return buf;
148 | }
149 |
150 | void ER_setpath(er_path_t *newset)
151 | {
152 | /* initialise the mutex if not yet initialised */
153 |
154 | if( er_pathlist_mutex_initialised == 0 ) {
155 | pthread_mutex_init( &er_pathlist_mutex, NULL );
156 | }
157 |
158 | pthread_mutex_lock( &er_pathlist_mutex );
159 | memcpy( & er_provisional_struct, newset, sizeof(er_path_t));
160 | pthread_mutex_unlock( &er_pathlist_mutex );
161 | }
162 |
163 | void er_logit(int facwhere, er_mask_t asp, int mode, int errcode, char *msg)
164 | {
165 | char buf[ER_MSGLEN], tmbuf[32];
166 | struct timeval tval;
167 | struct tm tmstr;
168 |
169 | if ( mode & ER_M_DATETIME ) {
170 | gettimeofday(&tval, NULL);
171 | localtime_r( & tval.tv_sec, & tmstr);
172 |
173 | // strcpy(tmbuf, ctime(&tm)+11);
174 | sprintf(tmbuf, "%02d:%02d:%02d",
175 | tmstr.tm_hour, tmstr.tm_min, tmstr.tm_sec);
176 | } else {
177 | tmbuf[0]=0;
178 | }
179 |
180 | snprintf(buf, ER_MSGLEN, "%s %s\n", tmbuf, msg );
181 | /* OK, now dispatch the message to all different paths */
182 |
183 | /* MUTEX :
184 |
185 | So, while the most of the work is done composing the message
186 | according to the format set in the path descriptor (mode),
187 | the output should also be locked.
188 |
189 | here the mutex associated with the path should be set.
190 | However, another mutex should be already used to protect other threads
191 | from reading the path description while it is modified by the master
192 | thread. An RW lock can be used for this.
193 |
194 | Fortunately, fputs is MT-Safe in Solaris.
195 | */
196 |
197 |
198 | /* for now we have at most one :-) */
199 | if( er_provisional_struct.fdes == NULL ) {
200 | fputs(buf,stderr);
201 | }
202 | else {
203 | // someone has really set something!
204 | if( errcode >= er_provisional_struct.sev
205 | || asp & er_provisional_struct.asp
206 | )
207 | {
208 | fputs(buf, er_provisional_struct.fdes);
209 | }
210 | }
211 |
212 |
213 |
214 | }
215 |
216 |
217 | int ER_is_traced(int facwhere, er_mask_t asp)
218 | {
219 | int i;
220 |
221 | // pthread_mutex_lock( &er_pathlist_mutex );
222 | i = er_provisional_struct.asp;
223 | // pthread_mutex_unlock( &er_pathlist_mutex );
224 |
225 | i &= asp;
226 | return i;
227 | }
228 |
229 | int ER_anybody_wants( int facwhere, int errcode, er_mask_t asp )
230 | {
231 | int i;
232 |
233 | pthread_mutex_lock( &er_pathlist_mutex );
234 | i = ( errcode >= er_provisional_struct.sev );
235 | pthread_mutex_unlock( &er_pathlist_mutex );
236 |
237 | return i;
238 | }
239 |
240 | int er_get_printmode(er_path_t *pathstruct)
241 | {
242 | int i;
243 |
244 | pthread_mutex_lock( &er_pathlist_mutex );
245 | if( pathstruct->fdes == NULL ) {
246 | // default mode
247 | i = ER_M_DEFAULT;
248 | }
249 | else {
250 | i = pathstruct->mode;
251 | }
252 | pthread_mutex_unlock( &er_pathlist_mutex );
253 |
254 | return i;
255 | }
256 |
257 | void ER_perror(int facwhere, int errcode, ...)
258 | {
259 | char erbuf[ER_MSGLEN];
260 | int pmode;
261 | va_list ap;
262 |
263 | if( ER_anybody_wants( facwhere, errcode, 0 ) ) { // uses pathlist mutex
264 |
265 | pmode = er_get_printmode( & er_provisional_struct );// uses pathlist mutex
266 |
267 | // now, this takes most time:
268 | va_start(ap, errcode);
269 | er_getmsg_parts(facwhere, errcode, pmode, erbuf, NULL, ap );
270 | va_end(ap);
271 |
272 | // actually, here will be a loop once there are more paths possible.
273 | er_logit(facwhere,
274 | 0, /* empty aspect mask for errors */
275 | pmode,
276 | errcode,
277 | erbuf); /* empty debug message */
278 | }
279 | }
280 |
281 | void ER_dbg_va( int facwhere, er_mask_t asp, char *txt, ...)
282 | {
283 | char erbuf[ER_MSGLEN];
284 | int pmode;
285 | va_list ap;
286 |
287 | if( ER_is_traced( facwhere, asp ) ) {
288 |
289 | pmode = er_get_printmode( & er_provisional_struct );
290 |
291 | va_start(ap, txt);
292 | er_getmsg_parts(facwhere, ER_SEV_D, pmode, erbuf, txt, ap );
293 | va_end(ap);
294 |
295 | er_logit(facwhere, asp, pmode, ER_SEV_D, erbuf);
296 | }
297 | }
298 |
299 |
300 | /* Set GLOBAL VARIABLES == can be done only by the master thread */
301 | void ER_init(int argc, char **argv)
302 | {
303 | char *er_slash;
304 |
305 | er_slash = rindex(argv[0],'/');
306 | strncpy(er_progname, (er_slash != NULL) ? er_slash+1 : argv[0], 31);
307 | er_progname[31] = 0;
308 |
309 | snprintf(er_pid, 10, "%d", getpid());
310 |
311 | }