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> 11SM_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 35static void sm_debug_reset __P((void)); 36static 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 50static 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 62SM_FILE_T * 63sm_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 81void 82sm_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 101void 102sm_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 122SM_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 126void 127#if SM_VA_STD 128sm_dprintf(char *fmt, ...) 129#else /* SM_VA_STD */ 130sm_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 204void 205sm_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 221typedef struct sm_debug_setting SM_DEBUG_SETTING_T; 222struct sm_debug_setting 223{ 224 const char *ds_pattern; 225 unsigned int ds_level; 226 SM_DEBUG_SETTING_T *ds_next; 227}; 228SM_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 235SM_DEBUG_T *SmDebugInitialized = NULL; 236 237const 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 254static void 255sm_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 285void 286sm_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 319static const char * 320parse_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 380void 381sm_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 410int 411sm_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 449bool 450sm_debug_loadactive(debug, level) 451 SM_DEBUG_T *debug; 452 int level; 453{ 454 return sm_debug_loadlevel(debug) >= level; 455} 456