Debug: config file name & line number for each ACL verb
[exim.git] / src / src / debug.c
CommitLineData
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 11static uschar debug_buffer[2048];
059ec3d9
PH
12static uschar *debug_ptr = debug_buffer;
13static 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
22hold the line-drawing characters that need to be printed on every line as it
23moves down the page. This function is used only in debugging circumstances. The
24output is done via debug_printf(). */
25
26#define tree_printlinesize 132 /* line size for printing */
27static uschar tree_printline[tree_printlinesize];
28
29/* Internal recursive subroutine.
30
31Arguments:
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
36Returns: nothing
37*/
38
39static void
40tree_printsub(tree_node *p, int pos, int barswitch)
41{
acec9514 42if (p->right) tree_printsub(p->right, pos+2, 1);
d7978c0f 43for (int i = 0; i <= pos-1; i++) debug_printf("%c", tree_printline[i]);
059ec3d9
PH
44debug_printf("-->%s [%d]\n", p->name, p->balance);
45tree_printline[pos] = barswitch? '|' : ' ';
acec9514 46if (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
55void
56debug_print_tree(tree_node *p)
57{
d7978c0f 58for (int i = 0; i < tree_printlinesize; i++) tree_printline[i] = ' ';
acec9514 59if (!p) debug_printf("Empty Tree\n"); else tree_printsub(p, 0, 0);
059ec3d9
PH
60debug_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
71Argument: the argv vector
72Returns: nothing
73*/
74
75void
55414b25 76debug_print_argv(const uschar ** argv)
059ec3d9
PH
77{
78debug_printf("exec");
acec9514 79while (*argv) debug_printf(" %.256s", *argv++);
059ec3d9
PH
80debug_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
90expansion fails, a message is written instead.
91
92Argument: the string
93Returns: nothing
94*/
95
96void
97debug_print_string(uschar *debug_string)
98{
acec9514 99if (!debug_string) return;
059ec3d9
PH
100HDEBUG(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/*
118Argument: an introductory string
119Returns: nothing
120*/
121
122void
123debug_print_ids(uschar *s)
124{
125debug_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 138function with a variable argument list, one for prepending an indent.
059ec3d9
PH
139
140If debug_pid is nonzero, print the pid at the start of each line. This is for
141tidier output when running parallel remote deliveries with debugging turned on.
142Must do the whole thing with a single printf and flush, as otherwise output may
143get interleaved. Since some calls to debug_printf() don't end with newline,
829dd842
JH
144we save up the text until we do get the newline.
145Take care to not disturb errno. */
059ec3d9 146
e1d04f48
JH
147
148/* Debug printf indented by ACL nest depth */
149void
150debug_printf_indent(const char * format, ...)
151{
152va_list ap;
e1d04f48 153va_start(ap, format);
398f9af3 154debug_vprintf(acl_level + expand_level, format, ap);
e1d04f48
JH
155va_end(ap);
156}
157
059ec3d9 158void
1ba28e2b 159debug_printf(const char *format, ...)
059ec3d9
PH
160{
161va_list ap;
162va_start(ap, format);
398f9af3 163debug_vprintf(0, format, ap);
059ec3d9
PH
164va_end(ap);
165}
166
167void
398f9af3 168debug_vprintf(int indent, const char *format, va_list ap)
059ec3d9 169{
829dd842
JH
170int save_errno = errno;
171
172if (!debug_file) return;
059ec3d9 173
d7ffbc12
PH
174/* Various things can be inserted at the start of a line. Don't use the
175tod_stamp() function for the timestamp, because that will overwrite the
176timestamp buffer, which may contain something useful. (This was a bug fix: the
177+memory debugging with +timestamp did cause a problem.) */
059ec3d9
PH
178
179if (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
209if (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
231does 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
255are internal newlines, make sure the prefix is on the continuation lines,
256as long as there is room in the buffer. We want to do just a single fprintf()
257so as to avoid interleaving. */
258
259if (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 281errno = save_errno;
059ec3d9
PH
282}
283
284/* End of debug.c */