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