1 | /*************************************** 2 | $Revision: 1.12 $ 3 | 4 | Error reporting (er) er.c - library of functions to uniformly report errors. 5 | 6 | Status: NOT REVUED, TESTED, PROVISIONAL 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 | #include <pthread.h> 34 | #include <time.h> 35 | 36 | 37 | int NOERR(er_ret_t a) 38 | { 39 | return ( ((a & 0xFFFF) == 0 ) /* the error part is 0 */ 40 | && ((a & 0xFFFF0000) != 0) ); /* the facility is non-zero */ 41 | } 42 | 43 | char *er_getsev( int sev, int mode ) 44 | { 45 | int i; 46 | 47 | for(i=0; er_level_a[i].sev != 0; i++) { 48 | if (er_level_a[i].sev == sev) { 49 | break; 50 | } 51 | } 52 | 53 | switch( mode & 0x03 ) { 54 | case ER_M_SEVCHAR: /* one-letter severity indication */ 55 | return er_level_a[i].chr; 56 | case ER_M_SEVLONG: /* long severity indication */ 57 | return er_level_a[i].txt; 58 | } 59 | 60 | /* no severity indication */ 61 | return ""; /* "" goes to program text, so returning a 62 | pointer to it is OK */ 63 | } 64 | 65 | char *er_getfacsym(int faccode) 66 | { 67 | int facidx; 68 | 69 | if( faccode != FAC_NONE ) { 70 | for (facidx=0; facidx<FAC_LAST; facidx++) { 71 | if( er_main_err[facidx].code == faccode ) { 72 | break; 73 | } 74 | } 75 | return er_main_err[facidx].name; 76 | } 77 | else return ""; 78 | } 79 | 80 | /* TWO CONSTANTS DEFINE THE LENGTH OF STRINGS HERE: 81 | ER_MSGLEN - max length of the line to be logged 82 | ER_ERRLEN - max length of the error message 83 | */ 84 | char *er_getmsg_parts(int facwhere, int errcode, int mode, 85 | char *buf, char *fmttxt, va_list args) 86 | { 87 | int fac, err, sev; 88 | int facidx, erridx; 89 | char erbuf[ER_ERRLEN], thr_str[10], *ermne, *txtlong=""; 90 | 91 | /* init to "" */ 92 | erbuf[0] = 0; 93 | ermne = ""; 94 | 95 | sev = ( errcode & 0xff000000 ); /* not shifted */ 96 | fac = ( errcode & 0x00ff0000 ) >> 16; 97 | err = ( errcode & 0x0000ffff ); /* not shifted */ 98 | 99 | for (facidx=0; facidx<FAC_LAST; facidx++) { 100 | if( er_main_err[facidx].code == fac ) { 101 | break; 102 | } 103 | } 104 | 105 | /* now, if we got to the last one and it's not the right one, 106 | the system is not configured properly */ 107 | if(facidx==FAC_LAST) { 108 | assert( er_main_err[facidx].code == fac ); /* just bail out. */ 109 | } 110 | 111 | /* still alive ? OK, build the message ...*/ 112 | 113 | /* ... using facidx/erridx if it's not a DEBUG or INFO */ 114 | switch( sev ) { 115 | case ER_SEV_D: 116 | ermne = "DEBUG"; 117 | break; 118 | case ER_SEV_I: 119 | ermne = "INFO"; 120 | break; 121 | default: 122 | /* OK, go to the module table. bail out if not initialized */ 123 | assert( er_main_err[facidx].errs != NULL ); 124 | 125 | for(erridx=0; er_main_err[facidx].errs[erridx].code != -1; erridx++) { 126 | if( er_main_err[facidx].errs[erridx].code == errcode ) { 127 | /* FOUND! now set the error message format using facidx and erridx */ 128 | 129 | /* long error message without arguments */ 130 | txtlong = er_main_err[facidx].errs[erridx].text; 131 | 132 | /* set the mnemonic pointer if necessary */ 133 | if( mode & ER_M_MNEMONIC ) { 134 | ermne = er_main_err[facidx].errs[erridx].mnem; 135 | } 136 | break; 137 | } 138 | } 139 | /* return ""; */ 140 | /* no, do not return: bail out if the code is not defined */ 141 | assert( er_main_err[facidx].errs[erridx].code != -1 ); 142 | } 143 | 144 | /* build the error message using vsnprintf */ 145 | vsnprintf(erbuf, ER_ERRLEN, fmttxt, args); 146 | 147 | sprintf(thr_str, "%d", pthread_self() ); 148 | 149 | /* build the actual log message */ 150 | snprintf(buf, ER_MSGLEN, "%s-%s/%s %s-%s-%s %s %s", 151 | (mode & ER_M_PROGNAME) ? er_progname : "", 152 | (mode & ER_M_PIDFULL) ? er_pid : "", 153 | (mode & ER_M_THR_ID ) ? thr_str : "", 154 | (mode & ER_M_FACSYMB) ? er_getfacsym(facwhere) : "", 155 | er_getsev(sev, mode), 156 | (mode & ER_M_MNEMONIC) ? ermne : "", 157 | (mode & ER_M_TEXTLONG) ? txtlong : "", 158 | erbuf 159 | ); 160 | return buf; 161 | } 162 | 163 | void ER_setpath(er_path_t *newset) 164 | { 165 | /* initialise the mutex if not yet initialised */ 166 | 167 | if( er_pathlist_mutex_initialised == 0 ) { 168 | pthread_mutex_init( &er_pathlist_mutex, NULL ); 169 | } 170 | 171 | pthread_mutex_lock( &er_pathlist_mutex ); 172 | memcpy( & er_provisional_struct, newset, sizeof(er_path_t)); 173 | pthread_mutex_unlock( &er_pathlist_mutex ); 174 | } 175 | 176 | void er_logit(int facwhere, er_mask_t asp, int mode, int errcode, char *msg) 177 | { 178 | char buf[ER_MSGLEN], tmbuf[32]; 179 | struct timeval tval; 180 | struct tm tmstr; 181 | 182 | if ( mode & ER_M_DATETIME ) { 183 | gettimeofday(&tval, NULL); 184 | localtime_r( & tval.tv_sec, & tmstr); 185 | 186 | /* strcpy(tmbuf, ctime(&tm)+11); */ 187 | sprintf(tmbuf, "%02d:%02d:%02d", 188 | tmstr.tm_hour, tmstr.tm_min, tmstr.tm_sec); 189 | } else { 190 | tmbuf[0]=0; 191 | } 192 | 193 | snprintf(buf, ER_MSGLEN, "%s %s\n", tmbuf, msg ); 194 | /* OK, now dispatch the message to all different paths */ 195 | 196 | /* MUTEX : 197 | 198 | So, while the most of the work is done composing the message 199 | according to the format set in the path descriptor (mode), 200 | the output should also be locked. 201 | 202 | here the mutex associated with the path should be set. 203 | However, another mutex should be already used to protect other threads 204 | from reading the path description while it is modified by the master 205 | thread. An RW lock can be used for this. 206 | 207 | Fortunately, fputs is MT-Safe in Solaris. 208 | */ 209 | 210 | 211 | /* for now we have at most one :-) */ 212 | if( er_provisional_struct.fdes == NULL ) { 213 | fputs(buf,stderr); 214 | } 215 | else { 216 | /* someone has really set something! */ 217 | if( errcode >= er_provisional_struct.sev 218 | || ER_is_traced(facwhere, asp) ) { 219 | 220 | fputs(buf, er_provisional_struct.fdes); 221 | } 222 | } 223 | 224 | 225 | 226 | } 227 | 228 | 229 | int ER_is_traced(int facwhere, er_mask_t asp) 230 | { 231 | int ik = 0; 232 | 233 | if( er_provisional_struct.fac == 0 234 | || er_provisional_struct.fac == facwhere ) { 235 | /* pthread_mutex_lock( &er_pathlist_mutex ); */ 236 | ik = er_provisional_struct.asp & asp; 237 | /* pthread_mutex_unlock( &er_pathlist_mutex ); */ 238 | } 239 | 240 | return (ik); 241 | } 242 | 243 | int ER_anybody_wants( int facwhere, int errcode, er_mask_t asp ) 244 | { 245 | int i; 246 | 247 | pthread_mutex_lock( &er_pathlist_mutex ); 248 | i = ( errcode >= er_provisional_struct.sev ); 249 | pthread_mutex_unlock( &er_pathlist_mutex ); 250 | 251 | return i; 252 | } 253 | 254 | int er_get_printmode(er_path_t *pathstruct) 255 | { 256 | int i; 257 | 258 | pthread_mutex_lock( &er_pathlist_mutex ); 259 | if( pathstruct->fdes == NULL ) { 260 | /* default mode */ 261 | i = ER_M_DEFAULT; 262 | } 263 | else { 264 | i = pathstruct->mode; 265 | } 266 | pthread_mutex_unlock( &er_pathlist_mutex ); 267 | 268 | return i; 269 | } 270 | 271 | void ER_perror(int facwhere, int errcode, char *format, ...) 272 | { 273 | char erbuf[ER_MSGLEN]; 274 | int pmode; 275 | va_list ap; 276 | 277 | if( ER_anybody_wants( facwhere, errcode, 0 ) ) { /* uses pathlist mutex */ 278 | 279 | pmode = er_get_printmode( & er_provisional_struct );/* uses pathlist mutex */ 280 | 281 | /* now, this takes most time: */ 282 | va_start(ap, format); 283 | er_getmsg_parts(facwhere, errcode, pmode, erbuf, format, ap ); 284 | va_end(ap); 285 | 286 | /* actually, here will be a loop once there are more paths possible. */ 287 | er_logit(facwhere, 288 | 0, /* empty aspect mask for errors */ 289 | pmode, 290 | errcode, 291 | erbuf); /* empty debug message */ 292 | } 293 | } 294 | 295 | 296 | void ER_asp_va( int facwhere, int sev, er_mask_t asp, char *txt, 297 | va_list args) 298 | { 299 | int pmode; 300 | char erbuf[ER_MSGLEN]; 301 | 302 | pmode = er_get_printmode( & er_provisional_struct ); 303 | er_getmsg_parts(facwhere, sev, pmode, erbuf, txt, args ); 304 | er_logit(facwhere, asp, pmode, sev, erbuf); 305 | } 306 | 307 | void ER_inf_va( int facwhere, er_mask_t asp, char *txt, ...) 308 | { 309 | va_list ap; 310 | va_start(ap, txt); 311 | ER_asp_va( facwhere, ER_SEV_I, asp, txt, ap ); 312 | va_end(ap); 313 | } 314 | 315 | 316 | void ER_dbg_va( int facwhere, er_mask_t asp, char *txt, ...) 317 | { 318 | char erbuf[ER_MSGLEN]; 319 | int pmode; 320 | va_list ap; 321 | 322 | if( ER_is_traced( facwhere, asp ) ) { 323 | 324 | pmode = er_get_printmode( & er_provisional_struct ); 325 | 326 | va_start(ap, txt); 327 | er_getmsg_parts(facwhere, ER_SEV_D, pmode, erbuf, txt, ap ); 328 | va_end(ap); 329 | 330 | er_logit(facwhere, asp, pmode, ER_SEV_D, erbuf); 331 | } 332 | } 333 | 334 | 335 | /* Set GLOBAL VARIABLES == can be done only by the master thread */ 336 | void ER_init(int argc, char **argv) 337 | { 338 | char *er_slash; 339 | 340 | er_slash = rindex(argv[0],'/'); 341 | strncpy(er_progname, (er_slash != NULL) ? er_slash+1 : argv[0], 31); 342 | er_progname[31] = 0; 343 | 344 | snprintf(er_pid, 10, "%d", getpid()); 345 | 346 | }