| 1 | /* $Cambridge: exim/src/src/debug.c,v 1.3 2005/01/04 10:00:42 ph10 Exp $ */ |
| 2 | |
| 3 | /************************************************* |
| 4 | * Exim - an Internet mail transport agent * |
| 5 | *************************************************/ |
| 6 | |
| 7 | /* Copyright (c) University of Cambridge 1995 - 2005 */ |
| 8 | /* See the file NOTICE for conditions of use and distribution. */ |
| 9 | |
| 10 | |
| 11 | #include "exim.h" |
| 12 | |
| 13 | static uschar debug_buffer[2048]; |
| 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 */ |