Commit | Line | Data |
---|---|---|
0a49a7a4 | 1 | /* $Cambridge: exim/src/src/debug.c,v 1.7 2009/11/16 19:50:36 nm4 Exp $ */ |
059ec3d9 PH |
2 | |
3 | /************************************************* | |
4 | * Exim - an Internet mail transport agent * | |
5 | *************************************************/ | |
6 | ||
0a49a7a4 | 7 | /* Copyright (c) University of Cambridge 1995 - 2009 */ |
059ec3d9 PH |
8 | /* See the file NOTICE for conditions of use and distribution. */ |
9 | ||
10 | ||
11 | #include "exim.h" | |
12 | ||
c2bcbe20 | 13 | static uschar debug_buffer[2048]; |
059ec3d9 PH |
14 | static uschar *debug_ptr = debug_buffer; |
15 | static int debug_prefix_length = 0; | |
16 | ||
17 | ||
18 | ||
19 | /************************************************* | |
20 | * Print tree * | |
21 | *************************************************/ | |
22 | ||
23 | /* Recursive tree-printing subroutine. It uses a static vector of uschar to | |
24 | hold the line-drawing characters that need to be printed on every line as it | |
25 | moves down the page. This function is used only in debugging circumstances. The | |
26 | output is done via debug_printf(). */ | |
27 | ||
28 | #define tree_printlinesize 132 /* line size for printing */ | |
29 | static uschar tree_printline[tree_printlinesize]; | |
30 | ||
31 | /* Internal recursive subroutine. | |
32 | ||
33 | Arguments: | |
34 | p tree node | |
35 | pos amount of indenting & vertical bars to pring | |
36 | barswitch if TRUE print | at the pos value | |
37 | ||
38 | Returns: nothing | |
39 | */ | |
40 | ||
41 | static void | |
42 | tree_printsub(tree_node *p, int pos, int barswitch) | |
43 | { | |
44 | int i; | |
45 | if (p->right != NULL) tree_printsub(p->right, pos+2, 1); | |
46 | for (i = 0; i <= pos-1; i++) debug_printf("%c", tree_printline[i]); | |
47 | debug_printf("-->%s [%d]\n", p->name, p->balance); | |
48 | tree_printline[pos] = barswitch? '|' : ' '; | |
49 | if (p->left != NULL) | |
50 | { | |
51 | tree_printline[pos+2] = '|'; | |
52 | tree_printsub(p->left, pos+2, 0); | |
53 | } | |
54 | } | |
55 | ||
56 | /* The external function, with just a tree node argument. */ | |
57 | ||
58 | void | |
59 | debug_print_tree(tree_node *p) | |
60 | { | |
61 | int i; | |
62 | for (i = 0; i < tree_printlinesize; i++) tree_printline[i] = ' '; | |
63 | if (p == NULL) debug_printf("Empty Tree\n"); else tree_printsub(p, 0, 0); | |
64 | debug_printf("---- End of tree ----\n"); | |
65 | } | |
66 | ||
67 | ||
68 | ||
69 | /************************************************* | |
70 | * Print an argv vector * | |
71 | *************************************************/ | |
72 | ||
73 | /* Called when about to obey execv(). | |
74 | ||
75 | Argument: the argv vector | |
76 | Returns: nothing | |
77 | */ | |
78 | ||
79 | void | |
80 | debug_print_argv(uschar **argv) | |
81 | { | |
82 | debug_printf("exec"); | |
83 | while (*argv != NULL) debug_printf(" %.256s", *argv++); | |
84 | debug_printf("\n"); | |
85 | } | |
86 | ||
87 | ||
88 | ||
89 | /************************************************* | |
90 | * Expand and print debugging string * | |
91 | *************************************************/ | |
92 | ||
93 | /* The string is expanded and written as debugging output. If | |
94 | expansion fails, a message is written instead. | |
95 | ||
96 | Argument: the string | |
97 | Returns: nothing | |
98 | */ | |
99 | ||
100 | void | |
101 | debug_print_string(uschar *debug_string) | |
102 | { | |
103 | if (debug_string == NULL) return; | |
104 | HDEBUG(D_any|D_v) | |
105 | { | |
106 | uschar *s = expand_string(debug_string); | |
107 | if (s == NULL) | |
108 | debug_printf("failed to expand debug_output \"%s\": %s\n", debug_string, | |
109 | expand_string_message); | |
110 | else if (s[0] != 0) | |
111 | debug_printf("%s%s", s, (s[Ustrlen(s)-1] == '\n')? "" : "\n"); | |
112 | } | |
113 | } | |
114 | ||
115 | ||
116 | ||
117 | /************************************************* | |
118 | * Print current uids and gids * | |
119 | *************************************************/ | |
120 | ||
121 | /* | |
122 | Argument: an introductory string | |
123 | Returns: nothing | |
124 | */ | |
125 | ||
126 | void | |
127 | debug_print_ids(uschar *s) | |
128 | { | |
129 | debug_printf("%s uid=%ld gid=%ld euid=%ld egid=%ld\n", s, | |
130 | (long int)getuid(), (long int)getgid(), (long int)geteuid(), | |
131 | (long int)getegid()); | |
132 | } | |
133 | ||
134 | ||
135 | ||
136 | ||
137 | /************************************************* | |
138 | * Print debugging message * | |
139 | *************************************************/ | |
140 | ||
141 | /* There are two entries, one for use when being called directly from a | |
142 | function with a variable argument list. | |
143 | ||
144 | If debug_pid is nonzero, print the pid at the start of each line. This is for | |
145 | tidier output when running parallel remote deliveries with debugging turned on. | |
146 | Must do the whole thing with a single printf and flush, as otherwise output may | |
147 | get interleaved. Since some calls to debug_printf() don't end with newline, | |
148 | we save up the text until we do get the newline. */ | |
149 | ||
150 | void | |
1ba28e2b | 151 | debug_printf(const char *format, ...) |
059ec3d9 PH |
152 | { |
153 | va_list ap; | |
154 | va_start(ap, format); | |
155 | debug_vprintf(format, ap); | |
156 | va_end(ap); | |
157 | } | |
158 | ||
159 | void | |
1ba28e2b | 160 | debug_vprintf(const char *format, va_list ap) |
059ec3d9 PH |
161 | { |
162 | if (debug_file == NULL) return; | |
163 | ||
d7ffbc12 PH |
164 | /* Various things can be inserted at the start of a line. Don't use the |
165 | tod_stamp() function for the timestamp, because that will overwrite the | |
166 | timestamp buffer, which may contain something useful. (This was a bug fix: the | |
167 | +memory debugging with +timestamp did cause a problem.) */ | |
059ec3d9 PH |
168 | |
169 | if (debug_ptr == debug_buffer) | |
170 | { | |
171 | DEBUG(D_timestamp) | |
172 | { | |
d7ffbc12 PH |
173 | time_t now = time(NULL); |
174 | struct tm *t = timestamps_utc? gmtime(&now) : localtime(&now); | |
175 | (void) sprintf(CS debug_ptr, "%02d:%02d:%02d ", t->tm_hour, t->tm_min, | |
176 | t->tm_sec); | |
059ec3d9 PH |
177 | while(*debug_ptr != 0) debug_ptr++; |
178 | } | |
179 | ||
180 | DEBUG(D_pid) | |
181 | { | |
182 | sprintf(CS debug_ptr, "%5d ", (int)getpid()); | |
183 | while(*debug_ptr != 0) debug_ptr++; | |
184 | } | |
185 | ||
186 | /* Set up prefix if outputting for host checking and not debugging */ | |
187 | ||
188 | if (host_checking && debug_selector == 0) | |
189 | { | |
190 | Ustrcpy(debug_ptr, ">>> "); | |
191 | debug_ptr += 4; | |
192 | } | |
193 | ||
194 | debug_prefix_length = debug_ptr - debug_buffer; | |
195 | } | |
196 | ||
197 | /* Use the checked formatting routine to ensure that the buffer | |
198 | does not overflow. Ensure there's space for a newline at the end. */ | |
199 | ||
200 | if (!string_vformat(debug_ptr, | |
201 | sizeof(debug_buffer) - (debug_ptr - debug_buffer) - 1, format, ap)) | |
202 | { | |
203 | uschar *s = US"**** debug string too long - truncated ****\n"; | |
204 | uschar *p = debug_buffer + Ustrlen(debug_buffer); | |
205 | int maxlen = sizeof(debug_buffer) - Ustrlen(s) - 3; | |
206 | if (p > debug_buffer + maxlen) p = debug_buffer + maxlen; | |
207 | if (p > debug_buffer && p[-1] != '\n') *p++ = '\n'; | |
208 | Ustrcpy(p, s); | |
209 | } | |
210 | ||
211 | while(*debug_ptr != 0) debug_ptr++; | |
212 | ||
213 | /* Output the line if it is complete. If we added any prefix data and there | |
214 | are internal newlines, make sure the prefix is on the continuation lines, | |
215 | as long as there is room in the buffer. We want to do just a single fprintf() | |
216 | so as to avoid interleaving. */ | |
217 | ||
218 | if (debug_ptr[-1] == '\n') | |
219 | { | |
220 | if (debug_prefix_length > 0) | |
221 | { | |
222 | uschar *p = debug_buffer; | |
223 | int left = sizeof(debug_buffer) - (debug_ptr - debug_buffer) - 1; | |
224 | while ((p = Ustrchr(p, '\n') + 1) != debug_ptr && | |
225 | left >= debug_prefix_length) | |
226 | { | |
227 | int len = debug_ptr - p; | |
228 | memmove(p + debug_prefix_length, p, len + 1); | |
229 | memmove(p, debug_buffer, debug_prefix_length); | |
230 | debug_ptr += debug_prefix_length; | |
231 | left -= debug_prefix_length; | |
232 | } | |
233 | } | |
234 | ||
235 | fprintf(debug_file, "%s", CS debug_buffer); | |
236 | fflush(debug_file); | |
237 | debug_ptr = debug_buffer; | |
238 | debug_prefix_length = 0; | |
239 | } | |
240 | } | |
241 | ||
242 | /* End of debug.c */ |