diff --git a/src/debug.c b/src/debug.c index a861b416..d5d11bd9 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,435 +1,467 @@ /* debug.c - helpful output in desperate situations * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009, 2019 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #ifndef HAVE_DOSISH_SYSTEM # ifdef HAVE_SYS_TYPES_H # include # endif # ifdef HAVE_SYS_STAT_H # include # endif # include #endif #include #include "util.h" #include "ath.h" #include "sema.h" #include "sys-util.h" #include "debug.h" /* The amount of detail requested by the user, per environment variable GPGME_DEBUG. */ static int debug_level; /* The output stream for the debug messages. */ static FILE *errfp; /* If not NULL, this malloced string is used instead of the GPGME_DEBUG envvar. It must have been set before the debug subsystem has been initialized. Using it later may or may not have any effect. */ static char *envvar_override; #ifdef HAVE_TLS #define FRAME_NR static __thread int frame_nr = 0; #endif void _gpgme_debug_frame_begin (void) { #ifdef FRAME_NR frame_nr++; #endif } int _gpgme_debug_frame_end (void) { #ifdef FRAME_NR frame_nr--; #endif return 0; } /* Remove leading and trailing white spaces. */ static char * trim_spaces (char *str) { char *string, *p, *mark; string = str; /* Find first non space character. */ for (p = string; *p && isspace (*(unsigned char *) p); p++) ; /* Move characters. */ for (mark = NULL; (*string = *p); string++, p++) if (isspace (*(unsigned char *) p)) { if (!mark) mark = string; } else mark = NULL; if (mark) *mark = '\0'; /* Remove trailing spaces. */ return str; } /* This is an internal function to set debug info. The caller must assure that this function is called only by one thread at a time. The function may have no effect if called after the debug system has been initialized. Returns 0 on success. */ int _gpgme_debug_set_debug_envvar (const char *value) { free (envvar_override); envvar_override = strdup (value); return !envvar_override; } static void debug_init (void) { static int initialized; if (!initialized) { gpgme_error_t err; char *e; const char *s1, *s2;; if (envvar_override) { e = strdup (envvar_override); free (envvar_override); envvar_override = NULL; } else { err = _gpgme_getenv ("GPGME_DEBUG", &e); if (err) return; } initialized = 1; errfp = stderr; if (e) { debug_level = atoi (e); s1 = strchr (e, PATHSEP_C); if (s1) { #ifndef HAVE_DOSISH_SYSTEM if (getuid () == geteuid () #if defined(HAVE_GETGID) && defined(HAVE_GETEGID) && getgid () == getegid () #endif ) { #endif char *p; FILE *fp; s1++; if (!(s2 = strchr (s1, PATHSEP_C))) s2 = s1 + strlen (s1); p = malloc (s2 - s1 + 1); if (p) { memcpy (p, s1, s2 - s1); p[s2-s1] = 0; trim_spaces (p); fp = fopen (p,"a"); if (fp) { setvbuf (fp, NULL, _IOLBF, 0); errfp = fp; } free (p); } #ifndef HAVE_DOSISH_SYSTEM } #endif } free (e); } } if (debug_level > 0) { _gpgme_debug (NULL, DEBUG_INIT, -1, NULL, NULL, NULL, "gpgme_debug: level=%d\n", debug_level); #ifdef HAVE_W32_SYSTEM { const char *name = _gpgme_get_inst_dir (); _gpgme_debug (NULL, DEBUG_INIT, -1, NULL, NULL, NULL, "gpgme_debug: gpgme='%s'\n", name? name: "?"); } #endif } } /* This should be called as soon as possible. It is required so that * the assuan logging gets connected to the gpgme log stream as early * as possible. */ void _gpgme_debug_subsystem_init (void) { debug_init (); } /* Log the formatted string FORMAT prefixed with additional info * depending on MODE: * * -1 = Do not print any additional args. * 0 = standalone (used by macro TRACE) * 1 = enter a function (used by macro TRACE_BEG) * 2 = debug a function (used by macro TRACE_LOG) * 3 = leave a function (used by macro TRACE_SUC) * * If LINE is not NULL the output will be stored in that variabale but * without a LF. _gpgme_debug_add can be used to add more and * _gpgme_debug_end to finally output it. * * Returns: 0 * * Note that we always return 0 because the old TRACE macro evaluated * to 0 which issues a warning with newer gcc version about an unused * values. By using a return value of this function this can be * avoided. Fixme: It might be useful to check whether the return * value from the TRACE macros are actually used somewhere. */ int _gpgme_debug (void **line, int level, int mode, const char *func, const char *tagname, const char *tagvalue, const char *format, ...) { va_list arg_ptr; int saved_errno; int need_lf; int indent; char *prefix, *stdinfo, *userinfo; const char *modestr; int no_userinfo = 0; if (debug_level < level) return 0; #ifdef FRAME_NR indent = frame_nr > 0? (2 * (frame_nr - 1)):0; #else indent = 0; #endif saved_errno = errno; va_start (arg_ptr, format); { struct tm *tp; time_t atime = time (NULL); tp = localtime (&atime); prefix = gpgrt_bsprintf ("GPGME %04d%02d%02dT%02d%02d%02d %04llX %*s", 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec, (unsigned long long) ath_self (), indent < 40? indent : 40, ""); } switch (mode) { case -1: modestr = NULL; break; /* Do nothing. */ case 0: modestr = "call"; break; case 1: modestr = "enter"; break; case 2: modestr = "check"; break; case 3: modestr = "leave"; break; default: modestr = "mode?"; break; } if (!modestr) stdinfo = NULL; else if (tagname && strcmp (tagname, XSTRINGIFY (NULL))) stdinfo = gpgrt_bsprintf ("%s: %s: %s=%p ", func,modestr,tagname,tagvalue); else stdinfo = gpgrt_bsprintf ("%s: %s: ", func, modestr); if (format && *format) userinfo = gpgrt_vbsprintf (format, arg_ptr); else { userinfo = NULL; no_userinfo = 1; } va_end (arg_ptr); if (mode != -1 && (!format || !*format)) need_lf = 1; else if (userinfo && *userinfo && userinfo[strlen (userinfo) - 1] != '\n') need_lf = 1; else need_lf = 0; if (line) *line = gpgrt_bsprintf ("%s%s%s", prefix? prefix : "GPGME out-of-core ", !modestr? "" : stdinfo? stdinfo : (!format || !*format)? "" :"out-of-core ", userinfo? userinfo : "out-of-core"); else { fprintf (errfp, "%s%s%s%s", prefix? prefix : "GPGME out-of-core ", !modestr? "" : stdinfo? stdinfo : (!format || !*format)? "" :"out-of-core ", userinfo? userinfo : no_userinfo? "" : "out-of-core", need_lf? "\n":""); fflush (errfp); } gpgrt_free (userinfo); gpgrt_free (stdinfo); gpgrt_free (prefix); gpg_err_set_errno (saved_errno); return 0; } /* Add the formatted string FORMAT to the debug line *LINE. */ void _gpgme_debug_add (void **line, const char *format, ...) { va_list arg_ptr; char *toadd; char *result; int res; if (!*line) return; va_start (arg_ptr, format); res = gpgrt_vasprintf (&toadd, format, arg_ptr); va_end (arg_ptr); if (res < 0) { gpgrt_free (*line); *line = NULL; } res = gpgrt_asprintf (&result, "%s%s", *(char **) line, toadd); gpgrt_free (toadd); gpgrt_free (*line); if (res < 0) *line = NULL; else *line = result; } /* Finish construction of *LINE and send it to the debug output stream. */ void _gpgme_debug_end (void **line) { const char *string; if (!*line) return; string = *line; fprintf (errfp, "%s%s", string, (*string && string[strlen (string)-1] != '\n')? "\n":""); fflush (errfp); gpgrt_free (*line); *line = NULL; } #define TOHEX(val) (((val) < 10) ? ((val) + '0') : ((val) - 10 + 'a')) void _gpgme_debug_buffer (int lvl, const char *const fmt, const char *const func, const char *const buffer, size_t len) { int idx = 0; int j; if (!_gpgme_debug_trace ()) return; if (!buffer) return; - while (idx < len) + if (lvl > 9) { - char str[51]; - char *strp = str; - char *strp2 = &str[34]; - - for (j = 0; j < 16; j++) - { - unsigned char val; - if (idx < len) - { - val = buffer[idx++]; - *(strp++) = TOHEX (val >> 4); - *(strp++) = TOHEX (val % 16); - *(strp2++) = isprint (val) ? val : '.'; - } - else - { - *(strp++) = ' '; - *(strp++) = ' '; - } - if (j == 7) - *(strp++) = ' '; - } - *(strp++) = ' '; - *(strp2) = '\0'; - - _gpgme_debug (NULL, lvl, -1, NULL, NULL, NULL, fmt, func, str); + while (idx < len) + { + char str[51]; + char *strp = str; + char *strp2 = &str[34]; + + for (j = 0; j < 16; j++) + { + unsigned char val; + if (idx < len) + { + val = buffer[idx++]; + *(strp++) = TOHEX (val >> 4); + *(strp++) = TOHEX (val % 16); + *(strp2++) = isprint (val)? val : '.'; + } + else + { + *(strp++) = ' '; + *(strp++) = ' '; + } + if (j == 7) + *(strp++) = ' '; + } + *(strp++) = ' '; + *(strp2) = '\0'; + + _gpgme_debug (NULL, lvl, -1, NULL, NULL, NULL, fmt, func, str); + } + } + else + { + while (idx < len) + { + char str[48+4+1]; + char *strp = str; + + for (j = 0; j < 48; j++) + { + unsigned char val; + if (idx < len) + { + val = buffer[idx++]; + if (val == '\n') + { + *strp++ = '<'; + *strp++ = 'L'; + *strp++ = 'F'; + *strp++ = '>'; + break; + } + *strp++ = (val > 31 && val < 127)? val : '.'; + } + } + *strp = 0; + + _gpgme_debug (NULL, lvl, -1, NULL, NULL, NULL, fmt, func, str); + } } }