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