Commit | Line | Data |
---|---|---|
059ec3d9 PH |
1 | /************************************************* |
2 | * Exim - an Internet mail transport agent * | |
3 | *************************************************/ | |
4 | ||
f9ba5e22 | 5 | /* Copyright (c) University of Cambridge 1995 - 2018 */ |
059ec3d9 PH |
6 | /* See the file NOTICE for conditions of use and distribution. */ |
7 | ||
8 | ||
9 | #include "exim.h" | |
10 | ||
c2bcbe20 | 11 | static uschar debug_buffer[2048]; |
059ec3d9 PH |
12 | static uschar *debug_ptr = debug_buffer; |
13 | static int debug_prefix_length = 0; | |
14 | ||
15 | ||
16 | ||
aeb65e91 JH |
17 | const uschar * rc_names[] = { /* Mostly for debug output */ |
18 | [OK] = US"OK", | |
19 | [DEFER] = US"DEFER", | |
20 | [FAIL] = US"FAIL", | |
21 | [ERROR] = US"ERROR", | |
22 | [FAIL_FORCED] = US"FAIL_FORCED", | |
23 | [DECLINE] = US"DECLINE", | |
24 | [PASS] = US"PASS", | |
25 | [DISCARD] = US"DISCARD", | |
26 | [SKIP] = US"SKIP", | |
27 | [REROUTED] = US"REROUTED", | |
28 | [PANIC] = US"PANIC", | |
29 | [BAD64] = US"BAD64", | |
30 | [UNEXPECTED] = US"UNEXPECTED", | |
31 | [CANCELLED] = US"CANCELLED", | |
32 | [FAIL_SEND] = US"FAIL_SEND", | |
33 | [FAIL_DROP] = US"FAIL_DROP" | |
34 | }; | |
35 | ||
36 | ||
059ec3d9 PH |
37 | /************************************************* |
38 | * Print tree * | |
39 | *************************************************/ | |
40 | ||
41 | /* Recursive tree-printing subroutine. It uses a static vector of uschar to | |
42 | hold the line-drawing characters that need to be printed on every line as it | |
43 | moves down the page. This function is used only in debugging circumstances. The | |
44 | output is done via debug_printf(). */ | |
45 | ||
46 | #define tree_printlinesize 132 /* line size for printing */ | |
47 | static uschar tree_printline[tree_printlinesize]; | |
48 | ||
49 | /* Internal recursive subroutine. | |
50 | ||
51 | Arguments: | |
52 | p tree node | |
4c04137d | 53 | pos amount of indenting & vertical bars to print |
059ec3d9 PH |
54 | barswitch if TRUE print | at the pos value |
55 | ||
56 | Returns: nothing | |
57 | */ | |
58 | ||
59 | static void | |
60 | tree_printsub(tree_node *p, int pos, int barswitch) | |
61 | { | |
acec9514 | 62 | if (p->right) tree_printsub(p->right, pos+2, 1); |
d7978c0f | 63 | for (int i = 0; i <= pos-1; i++) debug_printf("%c", tree_printline[i]); |
059ec3d9 PH |
64 | debug_printf("-->%s [%d]\n", p->name, p->balance); |
65 | tree_printline[pos] = barswitch? '|' : ' '; | |
acec9514 | 66 | if (p->left) |
059ec3d9 PH |
67 | { |
68 | tree_printline[pos+2] = '|'; | |
69 | tree_printsub(p->left, pos+2, 0); | |
70 | } | |
71 | } | |
72 | ||
73 | /* The external function, with just a tree node argument. */ | |
74 | ||
75 | void | |
76 | debug_print_tree(tree_node *p) | |
77 | { | |
d7978c0f | 78 | for (int i = 0; i < tree_printlinesize; i++) tree_printline[i] = ' '; |
acec9514 | 79 | if (!p) debug_printf("Empty Tree\n"); else tree_printsub(p, 0, 0); |
059ec3d9 PH |
80 | debug_printf("---- End of tree ----\n"); |
81 | } | |
82 | ||
83 | ||
84 | ||
85 | /************************************************* | |
86 | * Print an argv vector * | |
87 | *************************************************/ | |
88 | ||
89 | /* Called when about to obey execv(). | |
90 | ||
91 | Argument: the argv vector | |
92 | Returns: nothing | |
93 | */ | |
94 | ||
95 | void | |
55414b25 | 96 | debug_print_argv(const uschar ** argv) |
059ec3d9 PH |
97 | { |
98 | debug_printf("exec"); | |
acec9514 | 99 | while (*argv) debug_printf(" %.256s", *argv++); |
059ec3d9 PH |
100 | debug_printf("\n"); |
101 | } | |
102 | ||
103 | ||
104 | ||
105 | /************************************************* | |
106 | * Expand and print debugging string * | |
107 | *************************************************/ | |
108 | ||
109 | /* The string is expanded and written as debugging output. If | |
110 | expansion fails, a message is written instead. | |
111 | ||
112 | Argument: the string | |
113 | Returns: nothing | |
114 | */ | |
115 | ||
116 | void | |
117 | debug_print_string(uschar *debug_string) | |
118 | { | |
acec9514 | 119 | if (!debug_string) return; |
059ec3d9 PH |
120 | HDEBUG(D_any|D_v) |
121 | { | |
122 | uschar *s = expand_string(debug_string); | |
acec9514 | 123 | if (!s) |
059ec3d9 PH |
124 | debug_printf("failed to expand debug_output \"%s\": %s\n", debug_string, |
125 | expand_string_message); | |
126 | else if (s[0] != 0) | |
127 | debug_printf("%s%s", s, (s[Ustrlen(s)-1] == '\n')? "" : "\n"); | |
128 | } | |
129 | } | |
130 | ||
131 | ||
132 | ||
133 | /************************************************* | |
134 | * Print current uids and gids * | |
135 | *************************************************/ | |
136 | ||
137 | /* | |
138 | Argument: an introductory string | |
139 | Returns: nothing | |
140 | */ | |
141 | ||
142 | void | |
143 | debug_print_ids(uschar *s) | |
144 | { | |
145 | debug_printf("%s uid=%ld gid=%ld euid=%ld egid=%ld\n", s, | |
146 | (long int)getuid(), (long int)getgid(), (long int)geteuid(), | |
147 | (long int)getegid()); | |
148 | } | |
149 | ||
aeb65e91 JH |
150 | /************************************************/ |
151 | ||
152 | /* Give a string for a return-code */ | |
153 | ||
154 | const uschar * | |
155 | rc_to_string(int rc) | |
156 | { | |
157 | return rc < 0 || rc >= nelem(rc_names) ? US"?" : rc_names[rc]; | |
158 | } | |
159 | ||
160 | ||
059ec3d9 PH |
161 | |
162 | ||
163 | ||
164 | /************************************************* | |
165 | * Print debugging message * | |
166 | *************************************************/ | |
167 | ||
168 | /* There are two entries, one for use when being called directly from a | |
e1d04f48 | 169 | function with a variable argument list, one for prepending an indent. |
059ec3d9 PH |
170 | |
171 | If debug_pid is nonzero, print the pid at the start of each line. This is for | |
172 | tidier output when running parallel remote deliveries with debugging turned on. | |
173 | Must do the whole thing with a single printf and flush, as otherwise output may | |
174 | get interleaved. Since some calls to debug_printf() don't end with newline, | |
829dd842 JH |
175 | we save up the text until we do get the newline. |
176 | Take care to not disturb errno. */ | |
059ec3d9 | 177 | |
e1d04f48 JH |
178 | |
179 | /* Debug printf indented by ACL nest depth */ | |
180 | void | |
181 | debug_printf_indent(const char * format, ...) | |
182 | { | |
183 | va_list ap; | |
e1d04f48 | 184 | va_start(ap, format); |
398f9af3 | 185 | debug_vprintf(acl_level + expand_level, format, ap); |
e1d04f48 JH |
186 | va_end(ap); |
187 | } | |
188 | ||
059ec3d9 | 189 | void |
1ba28e2b | 190 | debug_printf(const char *format, ...) |
059ec3d9 PH |
191 | { |
192 | va_list ap; | |
193 | va_start(ap, format); | |
398f9af3 | 194 | debug_vprintf(0, format, ap); |
059ec3d9 PH |
195 | va_end(ap); |
196 | } | |
197 | ||
198 | void | |
398f9af3 | 199 | debug_vprintf(int indent, const char *format, va_list ap) |
059ec3d9 | 200 | { |
829dd842 JH |
201 | int save_errno = errno; |
202 | ||
203 | if (!debug_file) return; | |
059ec3d9 | 204 | |
d7ffbc12 PH |
205 | /* Various things can be inserted at the start of a line. Don't use the |
206 | tod_stamp() function for the timestamp, because that will overwrite the | |
207 | timestamp buffer, which may contain something useful. (This was a bug fix: the | |
208 | +memory debugging with +timestamp did cause a problem.) */ | |
059ec3d9 PH |
209 | |
210 | if (debug_ptr == debug_buffer) | |
211 | { | |
212 | DEBUG(D_timestamp) | |
213 | { | |
fca5cb18 JH |
214 | struct timeval now; |
215 | time_t tmp; | |
216 | struct tm * t; | |
217 | ||
218 | gettimeofday(&now, NULL); | |
219 | tmp = now.tv_sec; | |
8768d548 | 220 | t = f.timestamps_utc ? gmtime(&tmp) : localtime(&tmp); |
fca5cb18 JH |
221 | debug_ptr += sprintf(CS debug_ptr, |
222 | LOGGING(millisec) ? "%02d:%02d:%02d.%03d " : "%02d:%02d:%02d ", | |
cab0c277 | 223 | t->tm_hour, t->tm_min, t->tm_sec, (int)(now.tv_usec/1000)); |
059ec3d9 PH |
224 | } |
225 | ||
226 | DEBUG(D_pid) | |
5976eb99 | 227 | debug_ptr += sprintf(CS debug_ptr, "%5d ", (int)getpid()); |
059ec3d9 PH |
228 | |
229 | /* Set up prefix if outputting for host checking and not debugging */ | |
230 | ||
231 | if (host_checking && debug_selector == 0) | |
232 | { | |
f3ebb786 | 233 | Ustrcpy(debug_ptr, US">>> "); |
059ec3d9 PH |
234 | debug_ptr += 4; |
235 | } | |
236 | ||
237 | debug_prefix_length = debug_ptr - debug_buffer; | |
238 | } | |
239 | ||
398f9af3 JH |
240 | if (indent > 0) |
241 | { | |
d7978c0f | 242 | for (int i = indent >> 2; i > 0; i--) |
ae8f9024 JH |
243 | DEBUG(D_noutf8) |
244 | { | |
f3ebb786 | 245 | Ustrcpy(debug_ptr, US" !"); |
ae8f9024 JH |
246 | debug_ptr += 4; /* 3 spaces + shriek */ |
247 | debug_prefix_length += 4; | |
248 | } | |
249 | else | |
250 | { | |
f3ebb786 | 251 | Ustrcpy(debug_ptr, US" " UTF8_VERT_2DASH); |
ae8f9024 JH |
252 | debug_ptr += 6; /* 3 spaces + 3 UTF-8 octets */ |
253 | debug_prefix_length += 6; | |
254 | } | |
255 | ||
f3ebb786 | 256 | Ustrncpy(debug_ptr, US" ", indent &= 3); |
f9913671 JH |
257 | debug_ptr += indent; |
258 | debug_prefix_length += indent; | |
398f9af3 JH |
259 | } |
260 | ||
f3ebb786 JH |
261 | /* Use the lengthchecked formatting routine to ensure that the buffer |
262 | does not overflow. Ensure there's space for a newline at the end. | |
263 | However, use taint-unchecked routines for writing into the buffer | |
264 | so that we can write tainted info into the static debug_buffer - | |
265 | we trust that we will never expand the results. */ | |
059ec3d9 | 266 | |
059ec3d9 | 267 | { |
d12746bc JH |
268 | gstring gs = { .size = (int)sizeof(debug_buffer) - 1, |
269 | .ptr = debug_ptr - debug_buffer, | |
270 | .s = debug_buffer }; | |
f3ebb786 | 271 | if (!string_vformat(&gs, SVFMT_TAINT_NOCHK, format, ap)) |
d12746bc JH |
272 | { |
273 | uschar * s = US"**** debug string too long - truncated ****\n"; | |
274 | uschar * p = gs.s + gs.ptr; | |
275 | int maxlen = gs.size - Ustrlen(s) - 2; | |
276 | if (p > gs.s + maxlen) p = gs.s + maxlen; | |
277 | if (p > gs.s && p[-1] != '\n') *p++ = '\n'; | |
278 | Ustrcpy(p, s); | |
279 | while(*debug_ptr) debug_ptr++; | |
280 | } | |
281 | else | |
282 | { | |
283 | string_from_gstring(&gs); | |
284 | debug_ptr = gs.s + gs.ptr; | |
285 | } | |
059ec3d9 PH |
286 | } |
287 | ||
059ec3d9 PH |
288 | /* Output the line if it is complete. If we added any prefix data and there |
289 | are internal newlines, make sure the prefix is on the continuation lines, | |
290 | as long as there is room in the buffer. We want to do just a single fprintf() | |
291 | so as to avoid interleaving. */ | |
292 | ||
293 | if (debug_ptr[-1] == '\n') | |
294 | { | |
295 | if (debug_prefix_length > 0) | |
296 | { | |
297 | uschar *p = debug_buffer; | |
298 | int left = sizeof(debug_buffer) - (debug_ptr - debug_buffer) - 1; | |
299 | while ((p = Ustrchr(p, '\n') + 1) != debug_ptr && | |
300 | left >= debug_prefix_length) | |
301 | { | |
302 | int len = debug_ptr - p; | |
303 | memmove(p + debug_prefix_length, p, len + 1); | |
304 | memmove(p, debug_buffer, debug_prefix_length); | |
305 | debug_ptr += debug_prefix_length; | |
306 | left -= debug_prefix_length; | |
307 | } | |
308 | } | |
309 | ||
310 | fprintf(debug_file, "%s", CS debug_buffer); | |
311 | fflush(debug_file); | |
312 | debug_ptr = debug_buffer; | |
313 | debug_prefix_length = 0; | |
314 | } | |
829dd842 | 315 | errno = save_errno; |
059ec3d9 PH |
316 | } |
317 | ||
318 | /* End of debug.c */ |