1/*
2 * Copyright (c) 2000, 2001, 2003, 2004 Sendmail, 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.32 2009/09/20 05:38:46 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