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