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 <sm/types.h>
23#include <sm/time.h>
24#include <time.h>
25#endif /* _FFR_DEBUG_PID_TIME */
26#include <setjmp.h>
27#include <sm/io.h>
28#include <sm/assert.h>
29#include <sm/conf.h>
30#include <sm/debug.h>
31#include <sm/string.h>
32#include <sm/varargs.h>
33#include <sm/heap.h>
34
35static void		 sm_debug_reset __P((void));
36static const char	*parse_named_setting_x __P((const char *));
37
38/*
39**  Abstractions for printing trace messages.
40*/
41
42/*
43**  The output file to which trace output is directed.
44**  There is a controversy over whether this variable
45**  should be process global or thread local.
46**  To make the interface more abstract, we've hidden the
47**  variable behind access functions.
48*/
49
50static SM_FILE_T *SmDebugOutput = smioout;
51
52/*
53**  SM_DEBUG_FILE -- Returns current debug file pointer.
54**
55**	Parameters:
56**		none.
57**
58**	Returns:
59**		current debug file pointer.
60*/
61
62SM_FILE_T *
63sm_debug_file()
64{
65	return SmDebugOutput;
66}
67
68/*
69**  SM_DEBUG_SETFILE -- Sets debug file pointer.
70**
71**	Parameters:
72**		fp -- new debug file pointer.
73**
74**	Returns:
75**		none.
76**
77**	Side Effects:
78**		Sets SmDebugOutput.
79*/
80
81void
82sm_debug_setfile(fp)
83	SM_FILE_T *fp;
84{
85	SmDebugOutput = fp;
86}
87
88/*
89**  SM_DEBUG_CLOSE -- Close debug file pointer.
90**
91**	Parameters:
92**		none.
93**
94**	Returns:
95**		none.
96**
97**	Side Effects:
98**		Closes SmDebugOutput.
99*/
100
101void
102sm_debug_close()
103{
104	if (SmDebugOutput != NULL && SmDebugOutput != smioout)
105	{
106		sm_io_close(SmDebugOutput, SM_TIME_DEFAULT);
107		SmDebugOutput = NULL;
108	}
109}
110
111/*
112**  SM_DPRINTF -- printf() for debug output.
113**
114**	Parameters:
115**		fmt -- format for printf()
116**
117**	Returns:
118**		none.
119*/
120
121#if _FFR_DEBUG_PID_TIME
122SM_DEBUG_T SmDBGPidTime = SM_DEBUG_INITIALIZER("sm_trace_pid_time",
123	"@(#)$Debug: sm_trace_pid_time - print pid and time in debug $");
124#endif
125
126void
127#if SM_VA_STD
128sm_dprintf(char *fmt, ...)
129#else /* SM_VA_STD */
130sm_dprintf(fmt, va_alist)
131	char *fmt;
132	va_dcl
133#endif /* SM_VA_STD */
134{
135	SM_VA_LOCAL_DECL
136#if _FFR_DEBUG_PID_TIME
137	static struct timeval lasttv;
138#endif
139
140	if (SmDebugOutput == NULL)
141		return;
142#if _FFR_DEBUG_PID_TIME
143	/* note: this is ugly if the output isn't a full line! */
144	if (sm_debug_active(&SmDBGPidTime, 3))
145	{
146		struct timeval tv, tvd;
147
148		gettimeofday(&tv, NULL);
149		if (timerisset(&lasttv))
150			timersub(&tv, &lasttv, &tvd);
151		else
152			timerclear(&tvd);
153		sm_io_fprintf(SmDebugOutput, SmDebugOutput->f_timeout,
154			"%ld: %ld.%06ld ",
155			(long) getpid(),
156			(long) tvd.tv_sec,
157			(long) tvd.tv_usec);
158		lasttv = tv;
159	}
160	else if (sm_debug_active(&SmDBGPidTime, 2))
161	{
162		struct timeval tv;
163
164		gettimeofday(&tv, NULL);
165		sm_io_fprintf(SmDebugOutput, SmDebugOutput->f_timeout,
166			"%ld: %ld.%06ld ",
167			(long) getpid(),
168			(long) tv.tv_sec,
169			(long) tv.tv_usec);
170	}
171	else if (sm_debug_active(&SmDBGPidTime, 1))
172	{
173		static char str[32] = "[1900-00-00/00:00:00] ";
174		struct tm *tmp;
175		time_t currt;
176
177		currt = time((time_t *)0);
178		tmp = localtime(&currt);
179		snprintf(str, sizeof(str), "[%d-%02d-%02d/%02d:%02d:%02d] ",
180			1900 + tmp->tm_year,	/* HACK */
181			tmp->tm_mon + 1,
182			tmp->tm_mday,
183			tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
184		sm_io_fprintf(SmDebugOutput, SmDebugOutput->f_timeout,
185			"%ld: %s ", (long) getpid(), str);
186	}
187#endif /* _FFR_DEBUG_PID_TIME */
188
189	SM_VA_START(ap, fmt);
190	sm_io_vfprintf(SmDebugOutput, SmDebugOutput->f_timeout, fmt, ap);
191	SM_VA_END(ap);
192}
193
194/*
195**  SM_DFLUSH -- Flush debug output.
196**
197**	Parameters:
198**		none.
199**
200**	Returns:
201**		none.
202*/
203
204void
205sm_dflush()
206{
207	sm_io_flush(SmDebugOutput, SM_TIME_DEFAULT);
208}
209
210/*
211**  This is the internal database of debug settings.
212**  The semantics of looking up a setting in the settings database
213**  are that the *last* setting specified in a -d option on the sendmail
214**  command line that matches a given SM_DEBUG structure is the one that is
215**  used.  That is necessary to conform to the existing semantics of
216**  the sendmail -d option.  We store the settings as a linked list in
217**  reverse order, so when we do a lookup, we take the *first* entry
218**  that matches.
219*/
220
221typedef struct sm_debug_setting SM_DEBUG_SETTING_T;
222struct sm_debug_setting
223{
224	const char		*ds_pattern;
225	unsigned int		ds_level;
226	SM_DEBUG_SETTING_T	*ds_next;
227};
228SM_DEBUG_SETTING_T *SmDebugSettings = NULL;
229
230/*
231**  We keep a linked list of SM_DEBUG structures that have been initialized,
232**  for use by sm_debug_reset.
233*/
234
235SM_DEBUG_T *SmDebugInitialized = NULL;
236
237const char SmDebugMagic[] = "sm_debug";
238
239/*
240**  SM_DEBUG_RESET -- Reset SM_DEBUG structures.
241**
242**	Reset all SM_DEBUG structures back to the uninitialized state.
243**	This is used by sm_debug_addsetting to ensure that references to
244**	SM_DEBUG structures that occur before sendmail processes its -d flags
245**	do not cause those structures to be permanently forced to level 0.
246**
247**	Parameters:
248**		none.
249**
250**	Returns:
251**		none.
252*/
253
254static void
255sm_debug_reset()
256{
257	SM_DEBUG_T *debug;
258
259	for (debug = SmDebugInitialized;
260	     debug != NULL;
261	     debug = debug->debug_next)
262	{
263		debug->debug_level = SM_DEBUG_UNKNOWN;
264	}
265	SmDebugInitialized = NULL;
266}
267
268/*
269**  SM_DEBUG_ADDSETTING_X -- add an entry to the database of debug settings
270**
271**	Parameters:
272**		pattern -- a shell-style glob pattern (see sm_match).
273**			WARNING: the storage for 'pattern' will be owned by
274**			the debug package, so it should either be a string
275**			literal or the result of a call to sm_strdup_x.
276**		level -- a non-negative integer.
277**
278**	Returns:
279**		none.
280**
281**	Exceptions:
282**		F:sm_heap -- out of memory
283*/
284
285void
286sm_debug_addsetting_x(pattern, level)
287	const char *pattern;
288	int level;
289{
290	SM_DEBUG_SETTING_T *s;
291
292	SM_REQUIRE(pattern != NULL);
293	SM_REQUIRE(level >= 0);
294	s = sm_malloc_x(sizeof(SM_DEBUG_SETTING_T));
295	s->ds_pattern = pattern;
296	s->ds_level = (unsigned int) level;
297	s->ds_next = SmDebugSettings;
298	SmDebugSettings = s;
299	sm_debug_reset();
300}
301
302/*
303**  PARSE_NAMED_SETTING_X -- process a symbolic debug setting
304**
305**	Parameters:
306**		s -- Points to a non-empty \0 or , terminated string,
307**		     of which the initial character is not a digit.
308**
309**	Returns:
310**		pointer to terminating \0 or , character.
311**
312**	Exceptions:
313**		F:sm.heap -- out of memory.
314**
315**	Side Effects:
316**		adds the setting to the database.
317*/
318
319static const char *
320parse_named_setting_x(s)
321	const char *s;
322{
323	const char *pat, *endpat;
324	int level;
325
326	pat = s;
327	while (*s != '\0' && *s != ',' && *s != '.')
328		++s;
329	endpat = s;
330	if (*s == '.')
331	{
332		++s;
333		level = 0;
334		while (isascii(*s) && isdigit(*s))
335		{
336			level = level * 10 + (*s - '0');
337			++s;
338		}
339		if (level < 0)
340			level = 0;
341	}
342	else
343		level = 1;
344
345	sm_debug_addsetting_x(sm_strndup_x(pat, endpat - pat), level);
346
347	/* skip trailing junk */
348	while (*s != '\0' && *s != ',')
349		++s;
350
351	return s;
352}
353
354/*
355**  SM_DEBUG_ADDSETTINGS_X -- process a list of debug options
356**
357**	Parameters:
358**		s -- a list of debug settings, eg the argument to the
359**		     sendmail -d option.
360**
361**		The syntax of the string s is as follows:
362**
363**		<settings> ::= <setting> | <settings> "," <setting>
364**		<setting> ::= <categories> | <categories> "." <level>
365**		<categories> ::= [a-zA-Z_*?][a-zA-Z0-9_*?]*
366**
367**		However, note that we skip over anything we don't
368**		understand, rather than report an error.
369**
370**	Returns:
371**		none.
372**
373**	Exceptions:
374**		F:sm.heap -- out of memory
375**
376**	Side Effects:
377**		updates the database of debug settings.
378*/
379
380void
381sm_debug_addsettings_x(s)
382	const char *s;
383{
384	for (;;)
385	{
386		if (*s == '\0')
387			return;
388		if (*s == ',')
389		{
390			++s;
391			continue;
392		}
393		s = parse_named_setting_x(s);
394	}
395}
396
397/*
398**  SM_DEBUG_LOADLEVEL -- Get activation level of the specified debug object.
399**
400**	Parameters:
401**		debug -- debug object.
402**
403**	Returns:
404**		Activation level of the specified debug object.
405**
406**	Side Effects:
407**		Ensures that the debug object is initialized.
408*/
409
410int
411sm_debug_loadlevel(debug)
412	SM_DEBUG_T *debug;
413{
414	if (debug->debug_level == SM_DEBUG_UNKNOWN)
415	{
416		SM_DEBUG_SETTING_T *s;
417
418		for (s = SmDebugSettings; s != NULL; s = s->ds_next)
419		{
420			if (sm_match(debug->debug_name, s->ds_pattern))
421			{
422				debug->debug_level = s->ds_level;
423				goto initialized;
424			}
425		}
426		debug->debug_level = 0;
427	initialized:
428		debug->debug_next = SmDebugInitialized;
429		SmDebugInitialized = debug;
430	}
431	return (int) debug->debug_level;
432}
433
434/*
435**  SM_DEBUG_LOADACTIVE -- Activation level reached?
436**
437**	Parameters:
438**		debug -- debug object.
439**		level -- level to check.
440**
441**	Returns:
442**		true iff the activation level of the specified debug
443**			object >= level.
444**
445**	Side Effects:
446**		Ensures that the debug object is initialized.
447*/
448
449bool
450sm_debug_loadactive(debug, level)
451	SM_DEBUG_T *debug;
452	int level;
453{
454	return sm_debug_loadlevel(debug) >= level;
455}
456