Commit | Line | Data |
---|---|---|
059ec3d9 PH |
1 | /************************************************* |
2 | * Exim - an Internet mail transport agent * | |
3 | *************************************************/ | |
4 | ||
3386088d | 5 | /* Copyright (c) University of Cambridge 1995 - 2015 */ |
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 | { | |
42 | int i; | |
43 | if (p->right != NULL) tree_printsub(p->right, pos+2, 1); | |
44 | for (i = 0; i <= pos-1; i++) debug_printf("%c", tree_printline[i]); | |
45 | debug_printf("-->%s [%d]\n", p->name, p->balance); | |
46 | tree_printline[pos] = barswitch? '|' : ' '; | |
47 | if (p->left != NULL) | |
48 | { | |
49 | tree_printline[pos+2] = '|'; | |
50 | tree_printsub(p->left, pos+2, 0); | |
51 | } | |
52 | } | |
53 | ||
54 | /* The external function, with just a tree node argument. */ | |
55 | ||
56 | void | |
57 | debug_print_tree(tree_node *p) | |
58 | { | |
59 | int i; | |
60 | for (i = 0; i < tree_printlinesize; i++) tree_printline[i] = ' '; | |
61 | if (p == NULL) debug_printf("Empty Tree\n"); else tree_printsub(p, 0, 0); | |
62 | debug_printf("---- End of tree ----\n"); | |
63 | } | |
64 | ||
65 | ||
66 | ||
67 | /************************************************* | |
68 | * Print an argv vector * | |
69 | *************************************************/ | |
70 | ||
71 | /* Called when about to obey execv(). | |
72 | ||
73 | Argument: the argv vector | |
74 | Returns: nothing | |
75 | */ | |
76 | ||
77 | void | |
55414b25 | 78 | debug_print_argv(const uschar ** argv) |
059ec3d9 PH |
79 | { |
80 | debug_printf("exec"); | |
81 | while (*argv != NULL) debug_printf(" %.256s", *argv++); | |
82 | debug_printf("\n"); | |
83 | } | |
84 | ||
85 | ||
86 | ||
87 | /************************************************* | |
88 | * Expand and print debugging string * | |
89 | *************************************************/ | |
90 | ||
91 | /* The string is expanded and written as debugging output. If | |
92 | expansion fails, a message is written instead. | |
93 | ||
94 | Argument: the string | |
95 | Returns: nothing | |
96 | */ | |
97 | ||
98 | void | |
99 | debug_print_string(uschar *debug_string) | |
100 | { | |
101 | if (debug_string == NULL) return; | |
102 | HDEBUG(D_any|D_v) | |
103 | { | |
104 | uschar *s = expand_string(debug_string); | |
105 | if (s == NULL) | |
106 | debug_printf("failed to expand debug_output \"%s\": %s\n", debug_string, | |
107 | expand_string_message); | |
108 | else if (s[0] != 0) | |
109 | debug_printf("%s%s", s, (s[Ustrlen(s)-1] == '\n')? "" : "\n"); | |
110 | } | |
111 | } | |
112 | ||
113 | ||
114 | ||
115 | /************************************************* | |
116 | * Print current uids and gids * | |
117 | *************************************************/ | |
118 | ||
119 | /* | |
120 | Argument: an introductory string | |
121 | Returns: nothing | |
122 | */ | |
123 | ||
124 | void | |
125 | debug_print_ids(uschar *s) | |
126 | { | |
127 | debug_printf("%s uid=%ld gid=%ld euid=%ld egid=%ld\n", s, | |
128 | (long int)getuid(), (long int)getgid(), (long int)geteuid(), | |
129 | (long int)getegid()); | |
130 | } | |
131 | ||
132 | ||
133 | ||
134 | ||
135 | /************************************************* | |
136 | * Print debugging message * | |
137 | *************************************************/ | |
138 | ||
139 | /* There are two entries, one for use when being called directly from a | |
e1d04f48 | 140 | function with a variable argument list, one for prepending an indent. |
059ec3d9 PH |
141 | |
142 | If debug_pid is nonzero, print the pid at the start of each line. This is for | |
143 | tidier output when running parallel remote deliveries with debugging turned on. | |
144 | Must do the whole thing with a single printf and flush, as otherwise output may | |
145 | get interleaved. Since some calls to debug_printf() don't end with newline, | |
829dd842 JH |
146 | we save up the text until we do get the newline. |
147 | Take care to not disturb errno. */ | |
059ec3d9 | 148 | |
e1d04f48 JH |
149 | |
150 | /* Debug printf indented by ACL nest depth */ | |
151 | void | |
152 | debug_printf_indent(const char * format, ...) | |
153 | { | |
154 | va_list ap; | |
e1d04f48 | 155 | va_start(ap, format); |
398f9af3 | 156 | debug_vprintf(acl_level + expand_level, format, ap); |
e1d04f48 JH |
157 | va_end(ap); |
158 | } | |
159 | ||
059ec3d9 | 160 | void |
1ba28e2b | 161 | debug_printf(const char *format, ...) |
059ec3d9 PH |
162 | { |
163 | va_list ap; | |
164 | va_start(ap, format); | |
398f9af3 | 165 | debug_vprintf(0, format, ap); |
059ec3d9 PH |
166 | va_end(ap); |
167 | } | |
168 | ||
169 | void | |
398f9af3 | 170 | debug_vprintf(int indent, const char *format, va_list ap) |
059ec3d9 | 171 | { |
829dd842 JH |
172 | int save_errno = errno; |
173 | ||
174 | if (!debug_file) return; | |
059ec3d9 | 175 | |
d7ffbc12 PH |
176 | /* Various things can be inserted at the start of a line. Don't use the |
177 | tod_stamp() function for the timestamp, because that will overwrite the | |
178 | timestamp buffer, which may contain something useful. (This was a bug fix: the | |
179 | +memory debugging with +timestamp did cause a problem.) */ | |
059ec3d9 PH |
180 | |
181 | if (debug_ptr == debug_buffer) | |
182 | { | |
183 | DEBUG(D_timestamp) | |
184 | { | |
d7ffbc12 PH |
185 | time_t now = time(NULL); |
186 | struct tm *t = timestamps_utc? gmtime(&now) : localtime(&now); | |
187 | (void) sprintf(CS debug_ptr, "%02d:%02d:%02d ", t->tm_hour, t->tm_min, | |
188 | t->tm_sec); | |
059ec3d9 PH |
189 | while(*debug_ptr != 0) debug_ptr++; |
190 | } | |
191 | ||
192 | DEBUG(D_pid) | |
193 | { | |
194 | sprintf(CS debug_ptr, "%5d ", (int)getpid()); | |
195 | while(*debug_ptr != 0) debug_ptr++; | |
196 | } | |
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 | { | |
211 | int i; | |
212 | for (i = indent >> 2; i > 0; i--) | |
213 | { | |
f9913671 JH |
214 | Ustrcpy(debug_ptr, " " UTF8_VERT_2DASH); |
215 | debug_ptr += 6; /* 3 spaces + 3 UTF-8 octets */ | |
216 | debug_prefix_length += 6; | |
398f9af3 | 217 | } |
f9913671 JH |
218 | Ustrncpy(debug_ptr, " ", indent &= 3); |
219 | debug_ptr += indent; | |
220 | debug_prefix_length += indent; | |
398f9af3 JH |
221 | } |
222 | ||
059ec3d9 PH |
223 | /* Use the checked formatting routine to ensure that the buffer |
224 | does not overflow. Ensure there's space for a newline at the end. */ | |
225 | ||
226 | if (!string_vformat(debug_ptr, | |
227 | sizeof(debug_buffer) - (debug_ptr - debug_buffer) - 1, format, ap)) | |
228 | { | |
229 | uschar *s = US"**** debug string too long - truncated ****\n"; | |
230 | uschar *p = debug_buffer + Ustrlen(debug_buffer); | |
231 | int maxlen = sizeof(debug_buffer) - Ustrlen(s) - 3; | |
232 | if (p > debug_buffer + maxlen) p = debug_buffer + maxlen; | |
233 | if (p > debug_buffer && p[-1] != '\n') *p++ = '\n'; | |
234 | Ustrcpy(p, s); | |
235 | } | |
236 | ||
237 | while(*debug_ptr != 0) debug_ptr++; | |
238 | ||
239 | /* Output the line if it is complete. If we added any prefix data and there | |
240 | are internal newlines, make sure the prefix is on the continuation lines, | |
241 | as long as there is room in the buffer. We want to do just a single fprintf() | |
242 | so as to avoid interleaving. */ | |
243 | ||
244 | if (debug_ptr[-1] == '\n') | |
245 | { | |
246 | if (debug_prefix_length > 0) | |
247 | { | |
248 | uschar *p = debug_buffer; | |
249 | int left = sizeof(debug_buffer) - (debug_ptr - debug_buffer) - 1; | |
250 | while ((p = Ustrchr(p, '\n') + 1) != debug_ptr && | |
251 | left >= debug_prefix_length) | |
252 | { | |
253 | int len = debug_ptr - p; | |
254 | memmove(p + debug_prefix_length, p, len + 1); | |
255 | memmove(p, debug_buffer, debug_prefix_length); | |
256 | debug_ptr += debug_prefix_length; | |
257 | left -= debug_prefix_length; | |
258 | } | |
259 | } | |
260 | ||
261 | fprintf(debug_file, "%s", CS debug_buffer); | |
262 | fflush(debug_file); | |
263 | debug_ptr = debug_buffer; | |
264 | debug_prefix_length = 0; | |
265 | } | |
829dd842 | 266 | errno = save_errno; |
059ec3d9 PH |
267 | } |
268 | ||
269 | /* End of debug.c */ |