xref: /freebsd/contrib/sendmail/libsm/debug.c (revision 0b3105a37d7adcadcb720112fed4dc4e8040be99)
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>
11 SM_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 <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 
33 static void		 sm_debug_reset __P((void));
34 static 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 
48 static 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 
60 SM_FILE_T *
61 sm_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 
79 void
80 sm_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 
99 void
100 sm_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
120 SM_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 
124 void
125 #if SM_VA_STD
126 sm_dprintf(char *fmt, ...)
127 #else /* SM_VA_STD */
128 sm_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 
172 void
173 sm_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 
189 typedef struct sm_debug_setting SM_DEBUG_SETTING_T;
190 struct sm_debug_setting
191 {
192 	const char		*ds_pattern;
193 	unsigned int		ds_level;
194 	SM_DEBUG_SETTING_T	*ds_next;
195 };
196 SM_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 
203 SM_DEBUG_T *SmDebugInitialized = NULL;
204 
205 const 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 
222 static void
223 sm_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 
253 void
254 sm_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 
287 static const char *
288 parse_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 
348 void
349 sm_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 
378 int
379 sm_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 
417 bool
418 sm_debug_loadactive(debug, level)
419 	SM_DEBUG_T *debug;
420 	int level;
421 {
422 	return sm_debug_loadlevel(debug) >= level;
423 }
424