1 /* 2 * Copyright (c) 2000, 2001, 2003 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * 5 * By using this file, you agree to the terms and conditions set 6 * forth in the LICENSE file which can be found at the top level of 7 * the sendmail distribution. 8 */ 9 10 #include <sm/gen.h> 11 SM_RCSID("@(#)$Id: debug.c,v 1.29 2003/01/10 00:26:06 ca Exp $") 12 13 /* 14 ** libsm debugging and tracing 15 ** For documentation, see debug.html. 16 */ 17 18 #include <ctype.h> 19 #include <stdlib.h> 20 #include <setjmp.h> 21 #include <sm/io.h> 22 #include <sm/assert.h> 23 #include <sm/conf.h> 24 #include <sm/debug.h> 25 #include <sm/string.h> 26 #include <sm/varargs.h> 27 #include <sm/heap.h> 28 29 /* 30 ** Abstractions for printing trace messages. 31 */ 32 33 /* 34 ** The output file to which trace output is directed. 35 ** There is a controversy over whether this variable 36 ** should be process global or thread local. 37 ** To make the interface more abstract, we've hidden the 38 ** variable behind access functions. 39 */ 40 41 static SM_FILE_T *SmDebugOutput = smioout; 42 43 /* 44 ** SM_DEBUG_FILE -- Returns current debug file pointer. 45 ** 46 ** Parameters: 47 ** none. 48 ** 49 ** Returns: 50 ** current debug file pointer. 51 */ 52 53 SM_FILE_T * 54 sm_debug_file() 55 { 56 return SmDebugOutput; 57 } 58 59 /* 60 ** SM_DEBUG_SETFILE -- Sets debug file pointer. 61 ** 62 ** Parameters: 63 ** fp -- new debug file pointer. 64 ** 65 ** Returns: 66 ** none. 67 ** 68 ** Side Effects: 69 ** Sets SmDebugOutput. 70 */ 71 72 void 73 sm_debug_setfile(fp) 74 SM_FILE_T *fp; 75 { 76 SmDebugOutput = fp; 77 } 78 79 /* 80 ** SM_DEBUG_CLOSE -- Close debug file pointer. 81 ** 82 ** Parameters: 83 ** none. 84 ** 85 ** Returns: 86 ** none. 87 ** 88 ** Side Effects: 89 ** Closes SmDebugOutput. 90 */ 91 92 void 93 sm_debug_close() 94 { 95 if (SmDebugOutput != NULL && SmDebugOutput != smioout) 96 { 97 sm_io_close(SmDebugOutput, SM_TIME_DEFAULT); 98 SmDebugOutput = NULL; 99 } 100 } 101 102 /* 103 ** SM_DPRINTF -- printf() for debug output. 104 ** 105 ** Parameters: 106 ** fmt -- format for printf() 107 ** 108 ** Returns: 109 ** none. 110 */ 111 112 void 113 #if SM_VA_STD 114 sm_dprintf(char *fmt, ...) 115 #else /* SM_VA_STD */ 116 sm_dprintf(fmt, va_alist) 117 char *fmt; 118 va_dcl 119 #endif /* SM_VA_STD */ 120 { 121 SM_VA_LOCAL_DECL 122 123 if (SmDebugOutput == NULL) 124 return; 125 SM_VA_START(ap, fmt); 126 sm_io_vfprintf(SmDebugOutput, SmDebugOutput->f_timeout, fmt, ap); 127 SM_VA_END(ap); 128 } 129 130 /* 131 ** SM_DFLUSH -- Flush debug output. 132 ** 133 ** Parameters: 134 ** none. 135 ** 136 ** Returns: 137 ** none. 138 */ 139 140 void 141 sm_dflush() 142 { 143 sm_io_flush(SmDebugOutput, SM_TIME_DEFAULT); 144 } 145 146 /* 147 ** This is the internal database of debug settings. 148 ** The semantics of looking up a setting in the settings database 149 ** are that the *last* setting specified in a -d option on the sendmail 150 ** command line that matches a given SM_DEBUG structure is the one that is 151 ** used. That is necessary to conform to the existing semantics of 152 ** the sendmail -d option. We store the settings as a linked list in 153 ** reverse order, so when we do a lookup, we take the *first* entry 154 ** that matches. 155 */ 156 157 typedef struct sm_debug_setting SM_DEBUG_SETTING_T; 158 struct sm_debug_setting 159 { 160 const char *ds_pattern; 161 unsigned int ds_level; 162 SM_DEBUG_SETTING_T *ds_next; 163 }; 164 SM_DEBUG_SETTING_T *SmDebugSettings = NULL; 165 166 /* 167 ** We keep a linked list of SM_DEBUG structures that have been initialized, 168 ** for use by sm_debug_reset. 169 */ 170 171 SM_DEBUG_T *SmDebugInitialized = NULL; 172 173 const char SmDebugMagic[] = "sm_debug"; 174 175 /* 176 ** SM_DEBUG_RESET -- Reset SM_DEBUG structures. 177 ** 178 ** Reset all SM_DEBUG structures back to the uninitialized state. 179 ** This is used by sm_debug_addsetting to ensure that references to 180 ** SM_DEBUG structures that occur before sendmail processes its -d flags 181 ** do not cause those structures to be permanently forced to level 0. 182 ** 183 ** Parameters: 184 ** none. 185 ** 186 ** Returns: 187 ** none. 188 */ 189 190 void 191 sm_debug_reset() 192 { 193 SM_DEBUG_T *debug; 194 195 for (debug = SmDebugInitialized; 196 debug != NULL; 197 debug = debug->debug_next) 198 { 199 debug->debug_level = SM_DEBUG_UNKNOWN; 200 } 201 SmDebugInitialized = NULL; 202 } 203 204 /* 205 ** SM_DEBUG_ADDSETTING_X -- add an entry to the database of debug settings 206 ** 207 ** Parameters: 208 ** pattern -- a shell-style glob pattern (see sm_match). 209 ** WARNING: the storage for 'pattern' will be owned by 210 ** the debug package, so it should either be a string 211 ** literal or the result of a call to sm_strdup_x. 212 ** level -- a non-negative integer. 213 ** 214 ** Returns: 215 ** none. 216 ** 217 ** Exceptions: 218 ** F:sm_heap -- out of memory 219 */ 220 221 void 222 sm_debug_addsetting_x(pattern, level) 223 const char *pattern; 224 int level; 225 { 226 SM_DEBUG_SETTING_T *s; 227 228 SM_REQUIRE(pattern != NULL); 229 SM_REQUIRE(level >= 0); 230 s = sm_malloc_x(sizeof(SM_DEBUG_SETTING_T)); 231 s->ds_pattern = pattern; 232 s->ds_level = (unsigned int) level; 233 s->ds_next = SmDebugSettings; 234 SmDebugSettings = s; 235 sm_debug_reset(); 236 } 237 238 /* 239 ** PARSE_NAMED_SETTING_X -- process a symbolic debug setting 240 ** 241 ** Parameters: 242 ** s -- Points to a non-empty \0 or , terminated string, 243 ** of which the initial character is not a digit. 244 ** 245 ** Returns: 246 ** pointer to terminating \0 or , character. 247 ** 248 ** Exceptions: 249 ** F:sm.heap -- out of memory. 250 ** 251 ** Side Effects: 252 ** adds the setting to the database. 253 */ 254 255 static const char * 256 parse_named_setting_x(s) 257 register const char *s; 258 { 259 const char *pat, *endpat; 260 int level; 261 262 pat = s; 263 while (*s != '\0' && *s != ',' && *s != '.') 264 ++s; 265 endpat = s; 266 if (*s == '.') 267 { 268 ++s; 269 level = 0; 270 while (isascii(*s) && isdigit(*s)) 271 { 272 level = level * 10 + (*s - '0'); 273 ++s; 274 } 275 if (level < 0) 276 level = 0; 277 } 278 else 279 level = 1; 280 281 sm_debug_addsetting_x(sm_strndup_x(pat, endpat - pat), level); 282 283 /* skip trailing junk */ 284 while (*s != '\0' && *s != ',') 285 ++s; 286 287 return s; 288 } 289 290 /* 291 ** SM_DEBUG_ADDSETTINGS_X -- process a list of debug options 292 ** 293 ** Parameters: 294 ** s -- a list of debug settings, eg the argument to the 295 ** sendmail -d option. 296 ** 297 ** The syntax of the string s is as follows: 298 ** 299 ** <settings> ::= <setting> | <settings> "," <setting> 300 ** <setting> ::= <categories> | <categories> "." <level> 301 ** <categories> ::= [a-zA-Z_*?][a-zA-Z0-9_*?]* 302 ** 303 ** However, note that we skip over anything we don't 304 ** understand, rather than report an error. 305 ** 306 ** Returns: 307 ** none. 308 ** 309 ** Exceptions: 310 ** F:sm.heap -- out of memory 311 ** 312 ** Side Effects: 313 ** updates the database of debug settings. 314 */ 315 316 void 317 sm_debug_addsettings_x(s) 318 register const char *s; 319 { 320 for (;;) 321 { 322 if (*s == '\0') 323 return; 324 if (*s == ',') 325 { 326 ++s; 327 continue; 328 } 329 s = parse_named_setting_x(s); 330 } 331 } 332 333 /* 334 ** SM_DEBUG_LOADLEVEL -- Get activation level of the specified debug object. 335 ** 336 ** Parameters: 337 ** debug -- debug object. 338 ** 339 ** Returns: 340 ** Activation level of the specified debug object. 341 ** 342 ** Side Effects: 343 ** Ensures that the debug object is initialized. 344 */ 345 346 int 347 sm_debug_loadlevel(debug) 348 SM_DEBUG_T *debug; 349 { 350 if (debug->debug_level == SM_DEBUG_UNKNOWN) 351 { 352 SM_DEBUG_SETTING_T *s; 353 354 for (s = SmDebugSettings; s != NULL; s = s->ds_next) 355 { 356 if (sm_match(debug->debug_name, s->ds_pattern)) 357 { 358 debug->debug_level = s->ds_level; 359 goto initialized; 360 } 361 } 362 debug->debug_level = 0; 363 initialized: 364 debug->debug_next = SmDebugInitialized; 365 SmDebugInitialized = debug; 366 } 367 return (int) debug->debug_level; 368 } 369 370 /* 371 ** SM_DEBUG_LOADACTIVE -- Activation level reached? 372 ** 373 ** Parameters: 374 ** debug -- debug object. 375 ** level -- level to check. 376 ** 377 ** Returns: 378 ** true iff the activation level of the specified debug 379 ** object >= level. 380 ** 381 ** Side Effects: 382 ** Ensures that the debug object is initialized. 383 */ 384 385 bool 386 sm_debug_loadactive(debug, level) 387 SM_DEBUG_T *debug; 388 int level; 389 { 390 return sm_debug_loadlevel(debug) >= level; 391 } 392