190792Sgshapiro/* 2261370Sgshapiro * Copyright (c) 2000, 2001, 2003, 2004 Proofpoint, Inc. and its suppliers. 390792Sgshapiro * All rights reserved. 490792Sgshapiro * 590792Sgshapiro * By using this file, you agree to the terms and conditions set 690792Sgshapiro * forth in the LICENSE file which can be found at the top level of 790792Sgshapiro * the sendmail distribution. 890792Sgshapiro */ 990792Sgshapiro 1090792Sgshapiro#include <sm/gen.h> 11266711SgshapiroSM_RCSID("@(#)$Id: debug.c,v 1.33 2013-11-22 20:51:42 ca Exp $") 1290792Sgshapiro 1390792Sgshapiro/* 1490792Sgshapiro** libsm debugging and tracing 1590792Sgshapiro** For documentation, see debug.html. 1690792Sgshapiro*/ 1790792Sgshapiro 1890792Sgshapiro#include <ctype.h> 1990792Sgshapiro#include <stdlib.h> 20203004Sgshapiro#if _FFR_DEBUG_PID_TIME 21203004Sgshapiro#include <unistd.h> 22203004Sgshapiro#include <time.h> 23203004Sgshapiro#endif /* _FFR_DEBUG_PID_TIME */ 2490792Sgshapiro#include <setjmp.h> 2590792Sgshapiro#include <sm/io.h> 2690792Sgshapiro#include <sm/assert.h> 2790792Sgshapiro#include <sm/conf.h> 2890792Sgshapiro#include <sm/debug.h> 2990792Sgshapiro#include <sm/string.h> 3090792Sgshapiro#include <sm/varargs.h> 3190792Sgshapiro#include <sm/heap.h> 3290792Sgshapiro 33141858Sgshapirostatic void sm_debug_reset __P((void)); 34141858Sgshapirostatic const char *parse_named_setting_x __P((const char *)); 35141858Sgshapiro 3690792Sgshapiro/* 3790792Sgshapiro** Abstractions for printing trace messages. 3890792Sgshapiro*/ 3990792Sgshapiro 4090792Sgshapiro/* 4190792Sgshapiro** The output file to which trace output is directed. 4290792Sgshapiro** There is a controversy over whether this variable 4390792Sgshapiro** should be process global or thread local. 4490792Sgshapiro** To make the interface more abstract, we've hidden the 4590792Sgshapiro** variable behind access functions. 4690792Sgshapiro*/ 4790792Sgshapiro 4890792Sgshapirostatic SM_FILE_T *SmDebugOutput = smioout; 4990792Sgshapiro 5090792Sgshapiro/* 5190792Sgshapiro** SM_DEBUG_FILE -- Returns current debug file pointer. 5290792Sgshapiro** 5390792Sgshapiro** Parameters: 5490792Sgshapiro** none. 5590792Sgshapiro** 5690792Sgshapiro** Returns: 5790792Sgshapiro** current debug file pointer. 5890792Sgshapiro*/ 5990792Sgshapiro 6090792SgshapiroSM_FILE_T * 6190792Sgshapirosm_debug_file() 6290792Sgshapiro{ 6390792Sgshapiro return SmDebugOutput; 6490792Sgshapiro} 6590792Sgshapiro 6690792Sgshapiro/* 6790792Sgshapiro** SM_DEBUG_SETFILE -- Sets debug file pointer. 6890792Sgshapiro** 6990792Sgshapiro** Parameters: 7090792Sgshapiro** fp -- new debug file pointer. 7190792Sgshapiro** 7290792Sgshapiro** Returns: 7390792Sgshapiro** none. 7490792Sgshapiro** 7590792Sgshapiro** Side Effects: 7690792Sgshapiro** Sets SmDebugOutput. 7790792Sgshapiro*/ 7890792Sgshapiro 7990792Sgshapirovoid 8090792Sgshapirosm_debug_setfile(fp) 8190792Sgshapiro SM_FILE_T *fp; 8290792Sgshapiro{ 8390792Sgshapiro SmDebugOutput = fp; 8490792Sgshapiro} 8590792Sgshapiro 8690792Sgshapiro/* 87132943Sgshapiro** SM_DEBUG_CLOSE -- Close debug file pointer. 88132943Sgshapiro** 89132943Sgshapiro** Parameters: 90132943Sgshapiro** none. 91132943Sgshapiro** 92132943Sgshapiro** Returns: 93132943Sgshapiro** none. 94132943Sgshapiro** 95132943Sgshapiro** Side Effects: 96132943Sgshapiro** Closes SmDebugOutput. 97132943Sgshapiro*/ 98132943Sgshapiro 99132943Sgshapirovoid 100132943Sgshapirosm_debug_close() 101132943Sgshapiro{ 102132943Sgshapiro if (SmDebugOutput != NULL && SmDebugOutput != smioout) 103132943Sgshapiro { 104132943Sgshapiro sm_io_close(SmDebugOutput, SM_TIME_DEFAULT); 105132943Sgshapiro SmDebugOutput = NULL; 106132943Sgshapiro } 107132943Sgshapiro} 108132943Sgshapiro 109132943Sgshapiro/* 11090792Sgshapiro** SM_DPRINTF -- printf() for debug output. 11190792Sgshapiro** 11290792Sgshapiro** Parameters: 11390792Sgshapiro** fmt -- format for printf() 11490792Sgshapiro** 11590792Sgshapiro** Returns: 11690792Sgshapiro** none. 11790792Sgshapiro*/ 11890792Sgshapiro 119203004Sgshapiro#if _FFR_DEBUG_PID_TIME 120203004SgshapiroSM_DEBUG_T SmDBGPidTime = SM_DEBUG_INITIALIZER("sm_trace_pid_time", 121203004Sgshapiro "@(#)$Debug: sm_trace_pid_time - print pid and time in debug $"); 122203004Sgshapiro#endif /* _FFR_DEBUG_PID_TIME */ 123203004Sgshapiro 12490792Sgshapirovoid 12590792Sgshapiro#if SM_VA_STD 12690792Sgshapirosm_dprintf(char *fmt, ...) 12790792Sgshapiro#else /* SM_VA_STD */ 12890792Sgshapirosm_dprintf(fmt, va_alist) 12990792Sgshapiro char *fmt; 13090792Sgshapiro va_dcl 13190792Sgshapiro#endif /* SM_VA_STD */ 13290792Sgshapiro{ 13390792Sgshapiro SM_VA_LOCAL_DECL 13490792Sgshapiro 13590792Sgshapiro if (SmDebugOutput == NULL) 13690792Sgshapiro return; 137203004Sgshapiro#if _FFR_DEBUG_PID_TIME 138203004Sgshapiro /* note: this is ugly if the output isn't a full line! */ 139203004Sgshapiro if (sm_debug_active(&SmDBGPidTime, 1)) 140203004Sgshapiro { 141203004Sgshapiro static char str[32] = "[1900-00-00/00:00:00] "; 142203004Sgshapiro struct tm *tmp; 143203004Sgshapiro time_t currt; 144203004Sgshapiro 145203004Sgshapiro currt = time((time_t *)0); 146203004Sgshapiro tmp = localtime(&currt); 147203004Sgshapiro snprintf(str, sizeof(str), "[%d-%02d-%02d/%02d:%02d:%02d] ", 148203004Sgshapiro 1900 + tmp->tm_year, /* HACK */ 149203004Sgshapiro tmp->tm_mon + 1, 150203004Sgshapiro tmp->tm_mday, 151203004Sgshapiro tmp->tm_hour, tmp->tm_min, tmp->tm_sec); 152203004Sgshapiro sm_io_fprintf(SmDebugOutput, SmDebugOutput->f_timeout, 153203004Sgshapiro "%ld: %s ", (long) getpid(), str); 154203004Sgshapiro } 155203004Sgshapiro#endif /* _FFR_DEBUG_PID_TIME */ 156203004Sgshapiro 15790792Sgshapiro SM_VA_START(ap, fmt); 15890792Sgshapiro sm_io_vfprintf(SmDebugOutput, SmDebugOutput->f_timeout, fmt, ap); 15990792Sgshapiro SM_VA_END(ap); 16090792Sgshapiro} 16190792Sgshapiro 16290792Sgshapiro/* 16390792Sgshapiro** SM_DFLUSH -- Flush debug output. 16490792Sgshapiro** 16590792Sgshapiro** Parameters: 16690792Sgshapiro** none. 16790792Sgshapiro** 16890792Sgshapiro** Returns: 16990792Sgshapiro** none. 17090792Sgshapiro*/ 17190792Sgshapiro 17290792Sgshapirovoid 17390792Sgshapirosm_dflush() 17490792Sgshapiro{ 17590792Sgshapiro sm_io_flush(SmDebugOutput, SM_TIME_DEFAULT); 17690792Sgshapiro} 17790792Sgshapiro 17890792Sgshapiro/* 17990792Sgshapiro** This is the internal database of debug settings. 18090792Sgshapiro** The semantics of looking up a setting in the settings database 18190792Sgshapiro** are that the *last* setting specified in a -d option on the sendmail 18290792Sgshapiro** command line that matches a given SM_DEBUG structure is the one that is 18390792Sgshapiro** used. That is necessary to conform to the existing semantics of 18490792Sgshapiro** the sendmail -d option. We store the settings as a linked list in 18590792Sgshapiro** reverse order, so when we do a lookup, we take the *first* entry 18690792Sgshapiro** that matches. 18790792Sgshapiro*/ 18890792Sgshapiro 18990792Sgshapirotypedef struct sm_debug_setting SM_DEBUG_SETTING_T; 19090792Sgshapirostruct sm_debug_setting 19190792Sgshapiro{ 19290792Sgshapiro const char *ds_pattern; 19390792Sgshapiro unsigned int ds_level; 19490792Sgshapiro SM_DEBUG_SETTING_T *ds_next; 19590792Sgshapiro}; 19690792SgshapiroSM_DEBUG_SETTING_T *SmDebugSettings = NULL; 19790792Sgshapiro 19890792Sgshapiro/* 19990792Sgshapiro** We keep a linked list of SM_DEBUG structures that have been initialized, 20090792Sgshapiro** for use by sm_debug_reset. 20190792Sgshapiro*/ 20290792Sgshapiro 20390792SgshapiroSM_DEBUG_T *SmDebugInitialized = NULL; 20490792Sgshapiro 20590792Sgshapiroconst char SmDebugMagic[] = "sm_debug"; 20690792Sgshapiro 20790792Sgshapiro/* 20890792Sgshapiro** SM_DEBUG_RESET -- Reset SM_DEBUG structures. 20990792Sgshapiro** 21090792Sgshapiro** Reset all SM_DEBUG structures back to the uninitialized state. 21190792Sgshapiro** This is used by sm_debug_addsetting to ensure that references to 21290792Sgshapiro** SM_DEBUG structures that occur before sendmail processes its -d flags 21390792Sgshapiro** do not cause those structures to be permanently forced to level 0. 21490792Sgshapiro** 21590792Sgshapiro** Parameters: 21690792Sgshapiro** none. 21790792Sgshapiro** 21890792Sgshapiro** Returns: 21990792Sgshapiro** none. 22090792Sgshapiro*/ 22190792Sgshapiro 222141858Sgshapirostatic void 22390792Sgshapirosm_debug_reset() 22490792Sgshapiro{ 22590792Sgshapiro SM_DEBUG_T *debug; 22690792Sgshapiro 22790792Sgshapiro for (debug = SmDebugInitialized; 22890792Sgshapiro debug != NULL; 22990792Sgshapiro debug = debug->debug_next) 23090792Sgshapiro { 23190792Sgshapiro debug->debug_level = SM_DEBUG_UNKNOWN; 23290792Sgshapiro } 23390792Sgshapiro SmDebugInitialized = NULL; 23490792Sgshapiro} 23590792Sgshapiro 23690792Sgshapiro/* 23790792Sgshapiro** SM_DEBUG_ADDSETTING_X -- add an entry to the database of debug settings 23890792Sgshapiro** 23990792Sgshapiro** Parameters: 24090792Sgshapiro** pattern -- a shell-style glob pattern (see sm_match). 24190792Sgshapiro** WARNING: the storage for 'pattern' will be owned by 24290792Sgshapiro** the debug package, so it should either be a string 24390792Sgshapiro** literal or the result of a call to sm_strdup_x. 24490792Sgshapiro** level -- a non-negative integer. 24590792Sgshapiro** 24690792Sgshapiro** Returns: 24790792Sgshapiro** none. 24890792Sgshapiro** 24990792Sgshapiro** Exceptions: 25090792Sgshapiro** F:sm_heap -- out of memory 25190792Sgshapiro*/ 25290792Sgshapiro 25390792Sgshapirovoid 25490792Sgshapirosm_debug_addsetting_x(pattern, level) 25590792Sgshapiro const char *pattern; 25690792Sgshapiro int level; 25790792Sgshapiro{ 25890792Sgshapiro SM_DEBUG_SETTING_T *s; 25990792Sgshapiro 26090792Sgshapiro SM_REQUIRE(pattern != NULL); 26190792Sgshapiro SM_REQUIRE(level >= 0); 26290792Sgshapiro s = sm_malloc_x(sizeof(SM_DEBUG_SETTING_T)); 26390792Sgshapiro s->ds_pattern = pattern; 26490792Sgshapiro s->ds_level = (unsigned int) level; 26590792Sgshapiro s->ds_next = SmDebugSettings; 26690792Sgshapiro SmDebugSettings = s; 26790792Sgshapiro sm_debug_reset(); 26890792Sgshapiro} 26990792Sgshapiro 27090792Sgshapiro/* 27190792Sgshapiro** PARSE_NAMED_SETTING_X -- process a symbolic debug setting 27290792Sgshapiro** 27390792Sgshapiro** Parameters: 27490792Sgshapiro** s -- Points to a non-empty \0 or , terminated string, 27590792Sgshapiro** of which the initial character is not a digit. 27690792Sgshapiro** 27790792Sgshapiro** Returns: 27890792Sgshapiro** pointer to terminating \0 or , character. 27990792Sgshapiro** 28090792Sgshapiro** Exceptions: 28190792Sgshapiro** F:sm.heap -- out of memory. 28290792Sgshapiro** 28390792Sgshapiro** Side Effects: 28490792Sgshapiro** adds the setting to the database. 28590792Sgshapiro*/ 28690792Sgshapiro 28790792Sgshapirostatic const char * 28890792Sgshapiroparse_named_setting_x(s) 289141858Sgshapiro const char *s; 29090792Sgshapiro{ 29190792Sgshapiro const char *pat, *endpat; 29290792Sgshapiro int level; 29390792Sgshapiro 29490792Sgshapiro pat = s; 29590792Sgshapiro while (*s != '\0' && *s != ',' && *s != '.') 29690792Sgshapiro ++s; 29790792Sgshapiro endpat = s; 29890792Sgshapiro if (*s == '.') 29990792Sgshapiro { 30090792Sgshapiro ++s; 30190792Sgshapiro level = 0; 30290792Sgshapiro while (isascii(*s) && isdigit(*s)) 30390792Sgshapiro { 30490792Sgshapiro level = level * 10 + (*s - '0'); 30590792Sgshapiro ++s; 30690792Sgshapiro } 30790792Sgshapiro if (level < 0) 30890792Sgshapiro level = 0; 30990792Sgshapiro } 31090792Sgshapiro else 31190792Sgshapiro level = 1; 31290792Sgshapiro 31390792Sgshapiro sm_debug_addsetting_x(sm_strndup_x(pat, endpat - pat), level); 31490792Sgshapiro 31590792Sgshapiro /* skip trailing junk */ 31690792Sgshapiro while (*s != '\0' && *s != ',') 31790792Sgshapiro ++s; 31890792Sgshapiro 31990792Sgshapiro return s; 32090792Sgshapiro} 32190792Sgshapiro 32290792Sgshapiro/* 32390792Sgshapiro** SM_DEBUG_ADDSETTINGS_X -- process a list of debug options 32490792Sgshapiro** 32590792Sgshapiro** Parameters: 32690792Sgshapiro** s -- a list of debug settings, eg the argument to the 32790792Sgshapiro** sendmail -d option. 32890792Sgshapiro** 32990792Sgshapiro** The syntax of the string s is as follows: 33090792Sgshapiro** 33190792Sgshapiro** <settings> ::= <setting> | <settings> "," <setting> 33290792Sgshapiro** <setting> ::= <categories> | <categories> "." <level> 33390792Sgshapiro** <categories> ::= [a-zA-Z_*?][a-zA-Z0-9_*?]* 33490792Sgshapiro** 33590792Sgshapiro** However, note that we skip over anything we don't 33690792Sgshapiro** understand, rather than report an error. 33790792Sgshapiro** 33890792Sgshapiro** Returns: 33990792Sgshapiro** none. 34090792Sgshapiro** 34190792Sgshapiro** Exceptions: 34290792Sgshapiro** F:sm.heap -- out of memory 34390792Sgshapiro** 34490792Sgshapiro** Side Effects: 34590792Sgshapiro** updates the database of debug settings. 34690792Sgshapiro*/ 34790792Sgshapiro 34890792Sgshapirovoid 34990792Sgshapirosm_debug_addsettings_x(s) 350141858Sgshapiro const char *s; 35190792Sgshapiro{ 35290792Sgshapiro for (;;) 35390792Sgshapiro { 35490792Sgshapiro if (*s == '\0') 35590792Sgshapiro return; 35690792Sgshapiro if (*s == ',') 35790792Sgshapiro { 35890792Sgshapiro ++s; 35990792Sgshapiro continue; 36090792Sgshapiro } 36190792Sgshapiro s = parse_named_setting_x(s); 36290792Sgshapiro } 36390792Sgshapiro} 36490792Sgshapiro 36590792Sgshapiro/* 36690792Sgshapiro** SM_DEBUG_LOADLEVEL -- Get activation level of the specified debug object. 36790792Sgshapiro** 36890792Sgshapiro** Parameters: 36990792Sgshapiro** debug -- debug object. 37090792Sgshapiro** 37190792Sgshapiro** Returns: 37290792Sgshapiro** Activation level of the specified debug object. 37390792Sgshapiro** 37490792Sgshapiro** Side Effects: 37590792Sgshapiro** Ensures that the debug object is initialized. 37690792Sgshapiro*/ 37790792Sgshapiro 37890792Sgshapiroint 37990792Sgshapirosm_debug_loadlevel(debug) 38090792Sgshapiro SM_DEBUG_T *debug; 38190792Sgshapiro{ 38290792Sgshapiro if (debug->debug_level == SM_DEBUG_UNKNOWN) 38390792Sgshapiro { 38490792Sgshapiro SM_DEBUG_SETTING_T *s; 38590792Sgshapiro 38690792Sgshapiro for (s = SmDebugSettings; s != NULL; s = s->ds_next) 38790792Sgshapiro { 38890792Sgshapiro if (sm_match(debug->debug_name, s->ds_pattern)) 38990792Sgshapiro { 39090792Sgshapiro debug->debug_level = s->ds_level; 39190792Sgshapiro goto initialized; 39290792Sgshapiro } 39390792Sgshapiro } 39490792Sgshapiro debug->debug_level = 0; 39590792Sgshapiro initialized: 39690792Sgshapiro debug->debug_next = SmDebugInitialized; 39790792Sgshapiro SmDebugInitialized = debug; 39890792Sgshapiro } 39990792Sgshapiro return (int) debug->debug_level; 40090792Sgshapiro} 40190792Sgshapiro 40290792Sgshapiro/* 40390792Sgshapiro** SM_DEBUG_LOADACTIVE -- Activation level reached? 40490792Sgshapiro** 40590792Sgshapiro** Parameters: 40690792Sgshapiro** debug -- debug object. 40790792Sgshapiro** level -- level to check. 40890792Sgshapiro** 40990792Sgshapiro** Returns: 41090792Sgshapiro** true iff the activation level of the specified debug 41190792Sgshapiro** object >= level. 41290792Sgshapiro** 41390792Sgshapiro** Side Effects: 41490792Sgshapiro** Ensures that the debug object is initialized. 41590792Sgshapiro*/ 41690792Sgshapiro 41790792Sgshapirobool 41890792Sgshapirosm_debug_loadactive(debug, level) 41990792Sgshapiro SM_DEBUG_T *debug; 42090792Sgshapiro int level; 42190792Sgshapiro{ 42290792Sgshapiro return sm_debug_loadlevel(debug) >= level; 42390792Sgshapiro} 424