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