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