tool_cb_dbg.c (8624B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 #include "tool_setup.h" 25 26 #include "tool_cfgable.h" 27 #include "tool_msgs.h" 28 #include "tool_cb_dbg.h" 29 #include "tool_util.h" 30 31 #include "memdebug.h" /* keep this as LAST include */ 32 33 static void dump(const char *timebuf, const char *idsbuf, const char *text, 34 FILE *stream, const unsigned char *ptr, size_t size, 35 trace tracetype, curl_infotype infotype); 36 37 /* 38 * Return the formatted HH:MM:SS for the tv_sec given. 39 * NOT thread safe. 40 */ 41 static const char *hms_for_sec(time_t tv_sec) 42 { 43 static time_t cached_tv_sec; 44 static char hms_buf[12]; 45 46 if(tv_sec != cached_tv_sec) { 47 /* !checksrc! disable BANNEDFUNC 1 */ 48 struct tm *now = localtime(&tv_sec); /* not thread safe either */ 49 msnprintf(hms_buf, sizeof(hms_buf), "%02d:%02d:%02d", 50 now->tm_hour, now->tm_min, now->tm_sec); 51 cached_tv_sec = tv_sec; 52 } 53 return hms_buf; 54 } 55 56 static void log_line_start(FILE *log, const char *timebuf, 57 const char *idsbuf, curl_infotype type) 58 { 59 /* 60 * This is the trace look that is similar to what libcurl makes on its 61 * own. 62 */ 63 static const char * const s_infotype[] = { 64 "* ", "< ", "> ", "{ ", "} ", "{ ", "} " 65 }; 66 if((timebuf && *timebuf) || (idsbuf && *idsbuf)) 67 fprintf(log, "%s%s%s", timebuf, idsbuf, s_infotype[type]); 68 else 69 fputs(s_infotype[type], log); 70 } 71 72 #define TRC_IDS_FORMAT_IDS_1 "[%" CURL_FORMAT_CURL_OFF_T "-x] " 73 #define TRC_IDS_FORMAT_IDS_2 "[%" CURL_FORMAT_CURL_OFF_T "-%" \ 74 CURL_FORMAT_CURL_OFF_T "] " 75 /* 76 ** callback for CURLOPT_DEBUGFUNCTION 77 */ 78 int tool_debug_cb(CURL *handle, curl_infotype type, 79 char *data, size_t size, 80 void *userdata) 81 { 82 struct OperationConfig *operation = userdata; 83 struct GlobalConfig *global = operation->global; 84 FILE *output = tool_stderr; 85 const char *text; 86 struct timeval tv; 87 char timebuf[20]; 88 /* largest signed 64-bit is: 9,223,372,036,854,775,807 89 * max length in decimal: 1 + (6*3) = 19 90 * formatted via TRC_IDS_FORMAT_IDS_2 this becomes 2 + 19 + 1 + 19 + 2 = 43 91 * negative xfer-id are not printed, negative conn-ids use TRC_IDS_FORMAT_1 92 */ 93 char idsbuf[60]; 94 curl_off_t xfer_id, conn_id; 95 96 (void)handle; /* not used */ 97 98 if(global->tracetime) { 99 tv = tvrealnow(); 100 msnprintf(timebuf, sizeof(timebuf), "%s.%06ld ", 101 hms_for_sec(tv.tv_sec), (long)tv.tv_usec); 102 } 103 else 104 timebuf[0] = 0; 105 106 if(handle && global->traceids && 107 !curl_easy_getinfo(handle, CURLINFO_XFER_ID, &xfer_id) && xfer_id >= 0) { 108 if(!curl_easy_getinfo(handle, CURLINFO_CONN_ID, &conn_id) && 109 conn_id >= 0) { 110 msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_2, 111 xfer_id, conn_id); 112 } 113 else { 114 msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_1, xfer_id); 115 } 116 } 117 else 118 idsbuf[0] = 0; 119 120 if(!global->trace_stream) { 121 /* open for append */ 122 if(!strcmp("-", global->trace_dump)) 123 global->trace_stream = stdout; 124 else if(!strcmp("%", global->trace_dump)) 125 /* Ok, this is somewhat hackish but we do it undocumented for now */ 126 global->trace_stream = tool_stderr; 127 else { 128 global->trace_stream = fopen(global->trace_dump, FOPEN_WRITETEXT); 129 global->trace_fopened = TRUE; 130 } 131 } 132 133 if(global->trace_stream) 134 output = global->trace_stream; 135 136 if(!output) { 137 warnf(global, "Failed to create/open output"); 138 return 0; 139 } 140 141 if(global->tracetype == TRACE_PLAIN) { 142 static bool newl = FALSE; 143 static bool traced_data = FALSE; 144 145 switch(type) { 146 case CURLINFO_HEADER_OUT: 147 if(size > 0) { 148 size_t st = 0; 149 size_t i; 150 for(i = 0; i < size - 1; i++) { 151 if(data[i] == '\n') { /* LF */ 152 if(!newl) { 153 log_line_start(output, timebuf, idsbuf, type); 154 } 155 (void)fwrite(data + st, i - st + 1, 1, output); 156 st = i + 1; 157 newl = FALSE; 158 } 159 } 160 if(!newl) 161 log_line_start(output, timebuf, idsbuf, type); 162 (void)fwrite(data + st, i - st + 1, 1, output); 163 } 164 newl = (size && (data[size - 1] != '\n')); 165 traced_data = FALSE; 166 break; 167 case CURLINFO_TEXT: 168 case CURLINFO_HEADER_IN: 169 if(!newl) 170 log_line_start(output, timebuf, idsbuf, type); 171 (void)fwrite(data, size, 1, output); 172 newl = (size && (data[size - 1] != '\n')); 173 traced_data = FALSE; 174 break; 175 case CURLINFO_DATA_OUT: 176 case CURLINFO_DATA_IN: 177 case CURLINFO_SSL_DATA_IN: 178 case CURLINFO_SSL_DATA_OUT: 179 if(!traced_data) { 180 /* if the data is output to a tty and we are sending this debug trace 181 to stderr or stdout, we do not display the alert about the data not 182 being shown as the data _is_ shown then just not via this 183 function */ 184 if(!global->isatty || 185 ((output != tool_stderr) && (output != stdout))) { 186 if(!newl) 187 log_line_start(output, timebuf, idsbuf, type); 188 fprintf(output, "[%zu bytes data]\n", size); 189 newl = FALSE; 190 traced_data = TRUE; 191 } 192 } 193 break; 194 default: /* nada */ 195 newl = FALSE; 196 traced_data = FALSE; 197 break; 198 } 199 200 return 0; 201 } 202 203 switch(type) { 204 case CURLINFO_TEXT: 205 fprintf(output, "%s%s== Info: %.*s", timebuf, idsbuf, (int)size, data); 206 FALLTHROUGH(); 207 default: /* in case a new one is introduced to shock us */ 208 return 0; 209 210 case CURLINFO_HEADER_OUT: 211 text = "=> Send header"; 212 break; 213 case CURLINFO_DATA_OUT: 214 text = "=> Send data"; 215 break; 216 case CURLINFO_HEADER_IN: 217 text = "<= Recv header"; 218 break; 219 case CURLINFO_DATA_IN: 220 text = "<= Recv data"; 221 break; 222 case CURLINFO_SSL_DATA_IN: 223 text = "<= Recv SSL data"; 224 break; 225 case CURLINFO_SSL_DATA_OUT: 226 text = "=> Send SSL data"; 227 break; 228 } 229 230 dump(timebuf, idsbuf, text, output, (unsigned char *) data, size, 231 global->tracetype, type); 232 return 0; 233 } 234 235 static void dump(const char *timebuf, const char *idsbuf, const char *text, 236 FILE *stream, const unsigned char *ptr, size_t size, 237 trace tracetype, curl_infotype infotype) 238 { 239 size_t i; 240 size_t c; 241 242 unsigned int width = 0x10; 243 244 if(tracetype == TRACE_ASCII) 245 /* without the hex output, we can fit more on screen */ 246 width = 0x40; 247 248 fprintf(stream, "%s%s%s, %zu bytes (0x%zx)\n", timebuf, idsbuf, 249 text, size, size); 250 251 for(i = 0; i < size; i += width) { 252 253 fprintf(stream, "%04zx: ", i); 254 255 if(tracetype == TRACE_BIN) { 256 /* hex not disabled, show it */ 257 for(c = 0; c < width; c++) 258 if(i + c < size) 259 fprintf(stream, "%02x ", ptr[i + c]); 260 else 261 fputs(" ", stream); 262 } 263 264 for(c = 0; (c < width) && (i + c < size); c++) { 265 /* check for 0D0A; if found, skip past and start a new line of output */ 266 if((tracetype == TRACE_ASCII) && 267 (i + c + 1 < size) && (ptr[i + c] == 0x0D) && 268 (ptr[i + c + 1] == 0x0A)) { 269 i += (c + 2 - width); 270 break; 271 } 272 (void)infotype; 273 fprintf(stream, "%c", ((ptr[i + c] >= 0x20) && (ptr[i + c] < 0x7F)) ? 274 ptr[i + c] : UNPRINTABLE_CHAR); 275 /* check again for 0D0A, to avoid an extra \n if it is at width */ 276 if((tracetype == TRACE_ASCII) && 277 (i + c + 2 < size) && (ptr[i + c + 1] == 0x0D) && 278 (ptr[i + c + 2] == 0x0A)) { 279 i += (c + 3 - width); 280 break; 281 } 282 } 283 fputc('\n', stream); /* newline */ 284 } 285 fflush(stream); 286 }