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