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