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