| 1 | /************************************************* |
| 2 | * Exim - an Internet mail transport agent * |
| 3 | *************************************************/ |
| 4 | |
| 5 | /* Copyright (c) University of Cambridge 1995 - 2015 */ |
| 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 | 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 |
| 78 | debug_print_argv(const uschar ** argv) |
| 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 |
| 140 | function with a variable argument list, one for prepending an indent. |
| 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, |
| 146 | we save up the text until we do get the newline. |
| 147 | Take care to not disturb errno. */ |
| 148 | |
| 149 | |
| 150 | /* Debug printf indented by ACL nest depth */ |
| 151 | void |
| 152 | debug_printf_indent(const char * format, ...) |
| 153 | { |
| 154 | va_list ap; |
| 155 | unsigned depth = acl_level + expand_level, i; |
| 156 | |
| 157 | if (!debug_file) return; |
| 158 | if (depth > 0) |
| 159 | { |
| 160 | for (i = depth >> 2; i > 0; i--) |
| 161 | fprintf(debug_file, " ."); |
| 162 | fprintf(debug_file, "%*s", depth & 3, ""); |
| 163 | } |
| 164 | |
| 165 | va_start(ap, format); |
| 166 | debug_vprintf(format, ap); |
| 167 | va_end(ap); |
| 168 | } |
| 169 | |
| 170 | |
| 171 | void |
| 172 | debug_printf(const char *format, ...) |
| 173 | { |
| 174 | va_list ap; |
| 175 | va_start(ap, format); |
| 176 | debug_vprintf(format, ap); |
| 177 | va_end(ap); |
| 178 | } |
| 179 | |
| 180 | void |
| 181 | debug_vprintf(const char *format, va_list ap) |
| 182 | { |
| 183 | int save_errno = errno; |
| 184 | |
| 185 | if (!debug_file) return; |
| 186 | |
| 187 | /* Various things can be inserted at the start of a line. Don't use the |
| 188 | tod_stamp() function for the timestamp, because that will overwrite the |
| 189 | timestamp buffer, which may contain something useful. (This was a bug fix: the |
| 190 | +memory debugging with +timestamp did cause a problem.) */ |
| 191 | |
| 192 | if (debug_ptr == debug_buffer) |
| 193 | { |
| 194 | DEBUG(D_timestamp) |
| 195 | { |
| 196 | time_t now = time(NULL); |
| 197 | struct tm *t = timestamps_utc? gmtime(&now) : localtime(&now); |
| 198 | (void) sprintf(CS debug_ptr, "%02d:%02d:%02d ", t->tm_hour, t->tm_min, |
| 199 | t->tm_sec); |
| 200 | while(*debug_ptr != 0) debug_ptr++; |
| 201 | } |
| 202 | |
| 203 | DEBUG(D_pid) |
| 204 | { |
| 205 | sprintf(CS debug_ptr, "%5d ", (int)getpid()); |
| 206 | while(*debug_ptr != 0) debug_ptr++; |
| 207 | } |
| 208 | |
| 209 | /* Set up prefix if outputting for host checking and not debugging */ |
| 210 | |
| 211 | if (host_checking && debug_selector == 0) |
| 212 | { |
| 213 | Ustrcpy(debug_ptr, ">>> "); |
| 214 | debug_ptr += 4; |
| 215 | } |
| 216 | |
| 217 | debug_prefix_length = debug_ptr - debug_buffer; |
| 218 | } |
| 219 | |
| 220 | /* Use the checked formatting routine to ensure that the buffer |
| 221 | does not overflow. Ensure there's space for a newline at the end. */ |
| 222 | |
| 223 | if (!string_vformat(debug_ptr, |
| 224 | sizeof(debug_buffer) - (debug_ptr - debug_buffer) - 1, format, ap)) |
| 225 | { |
| 226 | uschar *s = US"**** debug string too long - truncated ****\n"; |
| 227 | uschar *p = debug_buffer + Ustrlen(debug_buffer); |
| 228 | int maxlen = sizeof(debug_buffer) - Ustrlen(s) - 3; |
| 229 | if (p > debug_buffer + maxlen) p = debug_buffer + maxlen; |
| 230 | if (p > debug_buffer && p[-1] != '\n') *p++ = '\n'; |
| 231 | Ustrcpy(p, s); |
| 232 | } |
| 233 | |
| 234 | while(*debug_ptr != 0) debug_ptr++; |
| 235 | |
| 236 | /* Output the line if it is complete. If we added any prefix data and there |
| 237 | are internal newlines, make sure the prefix is on the continuation lines, |
| 238 | as long as there is room in the buffer. We want to do just a single fprintf() |
| 239 | so as to avoid interleaving. */ |
| 240 | |
| 241 | if (debug_ptr[-1] == '\n') |
| 242 | { |
| 243 | if (debug_prefix_length > 0) |
| 244 | { |
| 245 | uschar *p = debug_buffer; |
| 246 | int left = sizeof(debug_buffer) - (debug_ptr - debug_buffer) - 1; |
| 247 | while ((p = Ustrchr(p, '\n') + 1) != debug_ptr && |
| 248 | left >= debug_prefix_length) |
| 249 | { |
| 250 | int len = debug_ptr - p; |
| 251 | memmove(p + debug_prefix_length, p, len + 1); |
| 252 | memmove(p, debug_buffer, debug_prefix_length); |
| 253 | debug_ptr += debug_prefix_length; |
| 254 | left -= debug_prefix_length; |
| 255 | } |
| 256 | } |
| 257 | |
| 258 | fprintf(debug_file, "%s", CS debug_buffer); |
| 259 | fflush(debug_file); |
| 260 | debug_ptr = debug_buffer; |
| 261 | debug_prefix_length = 0; |
| 262 | } |
| 263 | errno = save_errno; |
| 264 | } |
| 265 | |
| 266 | /* End of debug.c */ |