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