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