2ddf22907a083e6a9c31b091898e3d11c098b738
[exim.git] / src / src / debug.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge 1995 - 2018 */
6 /* See the file NOTICE for conditions of use and distribution. */
7
8
9 #include "exim.h"
10
11 static uschar  debug_buffer[2048];
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
33   pos        amount of indenting & vertical bars to print
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 if (p->right) tree_printsub(p->right, pos+2, 1);
43 for (int i = 0; i <= pos-1; i++) debug_printf("%c", tree_printline[i]);
44 debug_printf("-->%s [%d]\n", p->name, p->balance);
45 tree_printline[pos] = barswitch? '|' : ' ';
46 if (p->left)
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 {
58 for (int i = 0; i < tree_printlinesize; i++) tree_printline[i] = ' ';
59 if (!p) debug_printf("Empty Tree\n"); else tree_printsub(p, 0, 0);
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
76 debug_print_argv(const uschar ** argv)
77 {
78 debug_printf("exec");
79 while (*argv) debug_printf(" %.256s", *argv++);
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 {
99 if (!debug_string) return;
100 HDEBUG(D_any|D_v)
101   {
102   uschar *s = expand_string(debug_string);
103   if (!s)
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
138 function with a variable argument list, one for prepending an indent.
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,
144 we save up the text until we do get the newline.
145 Take care to not disturb errno. */
146
147
148 /* Debug printf indented by ACL nest depth */
149 void
150 debug_printf_indent(const char * format, ...)
151 {
152 va_list ap;
153 va_start(ap, format);
154 debug_vprintf(acl_level + expand_level, format, ap);
155 va_end(ap);
156 }
157
158 void
159 debug_printf(const char *format, ...)
160 {
161 va_list ap;
162 va_start(ap, format);
163 debug_vprintf(0, format, ap);
164 va_end(ap);
165 }
166
167 void
168 debug_vprintf(int indent, const char *format, va_list ap)
169 {
170 int save_errno = errno;
171
172 if (!debug_file) return;
173
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.) */
178
179 if (debug_ptr == debug_buffer)
180   {
181   DEBUG(D_timestamp)
182     {
183     struct timeval now;
184     time_t tmp;
185     struct tm * t;
186
187     gettimeofday(&now, NULL);
188     tmp = now.tv_sec;
189     t = f.timestamps_utc ? gmtime(&tmp) : localtime(&tmp);
190     debug_ptr += sprintf(CS debug_ptr,
191       LOGGING(millisec) ? "%02d:%02d:%02d.%03d " : "%02d:%02d:%02d ",
192       t->tm_hour, t->tm_min, t->tm_sec, (int)(now.tv_usec/1000));
193     }
194
195   DEBUG(D_pid)
196     debug_ptr += sprintf(CS debug_ptr, "%5d ", (int)getpid());
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
209 if (indent > 0)
210   {
211   for (int i = indent >> 2; i > 0; i--)
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
225   Ustrncpy(debug_ptr, "   ", indent &= 3);
226   debug_ptr += indent;
227   debug_prefix_length += indent;
228   }
229
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
233   {
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     }
252   }
253
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   }
281 errno = save_errno;
282 }
283
284 /* End of debug.c */