Commit | Line | Data |
---|---|---|
c988f1f4 | 1 | /* $Cambridge: exim/src/src/debug.c,v 1.3 2005/01/04 10:00:42 ph10 Exp $ */ |
059ec3d9 PH |
2 | |
3 | /************************************************* | |
4 | * Exim - an Internet mail transport agent * | |
5 | *************************************************/ | |
6 | ||
c988f1f4 | 7 | /* Copyright (c) University of Cambridge 1995 - 2005 */ |
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 | |
151 | debug_printf(char *format, ...) | |
152 | { | |
153 | va_list ap; | |
154 | va_start(ap, format); | |
155 | debug_vprintf(format, ap); | |
156 | va_end(ap); | |
157 | } | |
158 | ||
159 | void | |
160 | debug_vprintf(char *format, va_list ap) | |
161 | { | |
162 | if (debug_file == NULL) return; | |
163 | ||
164 | /* Various things can be inserted at the start of a line. */ | |
165 | ||
166 | if (debug_ptr == debug_buffer) | |
167 | { | |
168 | DEBUG(D_timestamp) | |
169 | { | |
170 | uschar *ts = tod_stamp(tod_log_bare); | |
171 | sprintf(CS debug_ptr, "%s ", ts + 11); | |
172 | while(*debug_ptr != 0) debug_ptr++; | |
173 | } | |
174 | ||
175 | DEBUG(D_pid) | |
176 | { | |
177 | sprintf(CS debug_ptr, "%5d ", (int)getpid()); | |
178 | while(*debug_ptr != 0) debug_ptr++; | |
179 | } | |
180 | ||
181 | /* Set up prefix if outputting for host checking and not debugging */ | |
182 | ||
183 | if (host_checking && debug_selector == 0) | |
184 | { | |
185 | Ustrcpy(debug_ptr, ">>> "); | |
186 | debug_ptr += 4; | |
187 | } | |
188 | ||
189 | debug_prefix_length = debug_ptr - debug_buffer; | |
190 | } | |
191 | ||
192 | /* Use the checked formatting routine to ensure that the buffer | |
193 | does not overflow. Ensure there's space for a newline at the end. */ | |
194 | ||
195 | if (!string_vformat(debug_ptr, | |
196 | sizeof(debug_buffer) - (debug_ptr - debug_buffer) - 1, format, ap)) | |
197 | { | |
198 | uschar *s = US"**** debug string too long - truncated ****\n"; | |
199 | uschar *p = debug_buffer + Ustrlen(debug_buffer); | |
200 | int maxlen = sizeof(debug_buffer) - Ustrlen(s) - 3; | |
201 | if (p > debug_buffer + maxlen) p = debug_buffer + maxlen; | |
202 | if (p > debug_buffer && p[-1] != '\n') *p++ = '\n'; | |
203 | Ustrcpy(p, s); | |
204 | } | |
205 | ||
206 | while(*debug_ptr != 0) debug_ptr++; | |
207 | ||
208 | /* Output the line if it is complete. If we added any prefix data and there | |
209 | are internal newlines, make sure the prefix is on the continuation lines, | |
210 | as long as there is room in the buffer. We want to do just a single fprintf() | |
211 | so as to avoid interleaving. */ | |
212 | ||
213 | if (debug_ptr[-1] == '\n') | |
214 | { | |
215 | if (debug_prefix_length > 0) | |
216 | { | |
217 | uschar *p = debug_buffer; | |
218 | int left = sizeof(debug_buffer) - (debug_ptr - debug_buffer) - 1; | |
219 | while ((p = Ustrchr(p, '\n') + 1) != debug_ptr && | |
220 | left >= debug_prefix_length) | |
221 | { | |
222 | int len = debug_ptr - p; | |
223 | memmove(p + debug_prefix_length, p, len + 1); | |
224 | memmove(p, debug_buffer, debug_prefix_length); | |
225 | debug_ptr += debug_prefix_length; | |
226 | left -= debug_prefix_length; | |
227 | } | |
228 | } | |
229 | ||
230 | fprintf(debug_file, "%s", CS debug_buffer); | |
231 | fflush(debug_file); | |
232 | debug_ptr = debug_buffer; | |
233 | debug_prefix_length = 0; | |
234 | } | |
235 | } | |
236 | ||
237 | /* End of debug.c */ |