debug.c revision 266692
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 <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 33static void sm_debug_reset __P((void)); 34static 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 48static 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 60SM_FILE_T * 61sm_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 79void 80sm_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 99void 100sm_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 120SM_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 124void 125#if SM_VA_STD 126sm_dprintf(char *fmt, ...) 127#else /* SM_VA_STD */ 128sm_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 172void 173sm_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 189typedef struct sm_debug_setting SM_DEBUG_SETTING_T; 190struct sm_debug_setting 191{ 192 const char *ds_pattern; 193 unsigned int ds_level; 194 SM_DEBUG_SETTING_T *ds_next; 195}; 196SM_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 203SM_DEBUG_T *SmDebugInitialized = NULL; 204 205const 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 222static void 223sm_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 253void 254sm_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 287static const char * 288parse_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 348void 349sm_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 378int 379sm_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 417bool 418sm_debug_loadactive(debug, level) 419 SM_DEBUG_T *debug; 420 int level; 421{ 422 return sm_debug_loadlevel(debug) >= level; 423} 424