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.1.1.1 2003-04-08 15:06:18 zacheiss 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 | } |
---|