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