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