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