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