Blame src/debug.c

Packit d7e8d0
/* debug.c - helpful output in desperate situations
Packit d7e8d0
   Copyright (C) 2000 Werner Koch (dd9jn)
Packit d7e8d0
   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 g10 Code GmbH
Packit d7e8d0
Packit d7e8d0
   This file is part of GPGME.
Packit d7e8d0
Packit d7e8d0
   GPGME is free software; you can redistribute it and/or modify it
Packit d7e8d0
   under the terms of the GNU Lesser General Public License as
Packit d7e8d0
   published by the Free Software Foundation; either version 2.1 of
Packit d7e8d0
   the License, or (at your option) any later version.
Packit d7e8d0
Packit d7e8d0
   GPGME is distributed in the hope that it will be useful, but
Packit d7e8d0
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit d7e8d0
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit d7e8d0
   Lesser General Public License for more details.
Packit d7e8d0
Packit d7e8d0
   You should have received a copy of the GNU Lesser General Public
Packit d7e8d0
   License along with this program; if not, write to the Free Software
Packit d7e8d0
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
Packit d7e8d0
   MA 02110-1301, USA.  */
Packit d7e8d0
Packit d7e8d0
#if HAVE_CONFIG_H
Packit d7e8d0
#include <config.h>
Packit d7e8d0
#endif
Packit d7e8d0
#include <stdio.h>
Packit d7e8d0
#include <stdlib.h>
Packit d7e8d0
#include <string.h>
Packit d7e8d0
#include <stdarg.h>
Packit d7e8d0
#ifdef HAVE_UNISTD_H
Packit d7e8d0
# include <unistd.h>
Packit d7e8d0
#endif
Packit d7e8d0
#include <ctype.h>
Packit d7e8d0
#include <errno.h>
Packit d7e8d0
#include <time.h>
Packit d7e8d0
#ifndef HAVE_DOSISH_SYSTEM
Packit d7e8d0
# ifdef HAVE_SYS_TYPES_H
Packit d7e8d0
#  include <sys/types.h>
Packit d7e8d0
# endif
Packit d7e8d0
# ifdef HAVE_SYS_STAT_H
Packit d7e8d0
#  include <sys/stat.h>
Packit d7e8d0
# endif
Packit d7e8d0
# include <fcntl.h>
Packit d7e8d0
#endif
Packit d7e8d0
#include <assert.h>
Packit d7e8d0
Packit d7e8d0
#include "util.h"
Packit d7e8d0
#include "ath.h"
Packit d7e8d0
#include "sema.h"
Packit d7e8d0
#include "sys-util.h"
Packit d7e8d0
#include "debug.h"
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
/* Lock to serialize initialization of the debug output subsystem and
Packit d7e8d0
   output of actual debug messages.  */
Packit d7e8d0
DEFINE_STATIC_LOCK (debug_lock);
Packit d7e8d0
Packit d7e8d0
/* The amount of detail requested by the user, per environment
Packit d7e8d0
   variable GPGME_DEBUG.  */
Packit d7e8d0
static int debug_level;
Packit d7e8d0
Packit d7e8d0
/* The output stream for the debug messages.  */
Packit d7e8d0
static FILE *errfp;
Packit d7e8d0
Packit d7e8d0
/* If not NULL, this malloced string is used instead of the
Packit d7e8d0
   GPGME_DEBUG envvar.  It must have been set before the debug
Packit d7e8d0
   subsystem has been initialized.  Using it later may or may not have
Packit d7e8d0
   any effect.  */
Packit d7e8d0
static char *envvar_override;
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
#ifdef HAVE_TLS
Packit d7e8d0
#define FRAME_NR
Packit d7e8d0
static __thread int frame_nr = 0;
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
void
Packit d7e8d0
_gpgme_debug_frame_begin (void)
Packit d7e8d0
{
Packit d7e8d0
#ifdef FRAME_NR
Packit d7e8d0
  frame_nr++;
Packit d7e8d0
#endif
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
int _gpgme_debug_frame_end (void)
Packit d7e8d0
{
Packit d7e8d0
#ifdef FRAME_NR
Packit d7e8d0
  frame_nr--;
Packit d7e8d0
#endif
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
/* Remove leading and trailing white spaces.  */
Packit d7e8d0
static char *
Packit d7e8d0
trim_spaces (char *str)
Packit d7e8d0
{
Packit d7e8d0
  char *string, *p, *mark;
Packit d7e8d0
Packit d7e8d0
  string = str;
Packit d7e8d0
  /* Find first non space character.  */
Packit d7e8d0
  for (p = string; *p && isspace (*(unsigned char *) p); p++)
Packit d7e8d0
    ;
Packit d7e8d0
  /* Move characters.  */
Packit d7e8d0
  for (mark = NULL; (*string = *p); string++, p++)
Packit d7e8d0
    if (isspace (*(unsigned char *) p))
Packit d7e8d0
      {
Packit d7e8d0
	if (!mark)
Packit d7e8d0
	  mark = string;
Packit d7e8d0
      }
Packit d7e8d0
    else
Packit d7e8d0
      mark = NULL;
Packit d7e8d0
  if (mark)
Packit d7e8d0
    *mark = '\0';	/* Remove trailing spaces.  */
Packit d7e8d0
Packit d7e8d0
  return str;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* This is an internal function to set debug info.  The caller must
Packit d7e8d0
   assure that this function is called only by one thread at a time.
Packit d7e8d0
   The function may have no effect if called after the debug system
Packit d7e8d0
   has been initialized.  Returns 0 on success.  */
Packit d7e8d0
int
Packit d7e8d0
_gpgme_debug_set_debug_envvar (const char *value)
Packit d7e8d0
{
Packit d7e8d0
  free (envvar_override);
Packit d7e8d0
  envvar_override = strdup (value);
Packit d7e8d0
  return !envvar_override;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static void
Packit d7e8d0
debug_init (void)
Packit d7e8d0
{
Packit d7e8d0
  static int initialized;
Packit d7e8d0
Packit d7e8d0
  LOCK (debug_lock);
Packit d7e8d0
  if (!initialized)
Packit d7e8d0
    {
Packit d7e8d0
      gpgme_error_t err;
Packit d7e8d0
      char *e;
Packit d7e8d0
      const char *s1, *s2;;
Packit d7e8d0
Packit d7e8d0
      if (envvar_override)
Packit d7e8d0
        {
Packit d7e8d0
          e = strdup (envvar_override);
Packit d7e8d0
          free (envvar_override);
Packit d7e8d0
          envvar_override = NULL;
Packit d7e8d0
        }
Packit d7e8d0
      else
Packit d7e8d0
        {
Packit d7e8d0
#ifdef HAVE_W32CE_SYSTEM
Packit d7e8d0
          e = _gpgme_w32ce_get_debug_envvar ();
Packit d7e8d0
#else /*!HAVE_W32CE_SYSTEM*/
Packit d7e8d0
          err = _gpgme_getenv ("GPGME_DEBUG", &e);
Packit d7e8d0
          if (err)
Packit d7e8d0
            {
Packit d7e8d0
              UNLOCK (debug_lock);
Packit d7e8d0
              return;
Packit d7e8d0
            }
Packit d7e8d0
#endif /*!HAVE_W32CE_SYSTEM*/
Packit d7e8d0
        }
Packit d7e8d0
Packit d7e8d0
      initialized = 1;
Packit d7e8d0
      errfp = stderr;
Packit d7e8d0
      if (e)
Packit d7e8d0
	{
Packit d7e8d0
	  debug_level = atoi (e);
Packit d7e8d0
	  s1 = strchr (e, PATHSEP_C);
Packit d7e8d0
	  if (s1)
Packit d7e8d0
	    {
Packit d7e8d0
#ifndef HAVE_DOSISH_SYSTEM
Packit d7e8d0
	      if (getuid () == geteuid ()
Packit d7e8d0
#if defined(HAVE_GETGID) && defined(HAVE_GETEGID)
Packit d7e8d0
                  && getgid () == getegid ()
Packit d7e8d0
#endif
Packit d7e8d0
                  )
Packit d7e8d0
		{
Packit d7e8d0
#endif
Packit d7e8d0
		  char *p;
Packit d7e8d0
		  FILE *fp;
Packit d7e8d0
Packit d7e8d0
		  s1++;
Packit d7e8d0
		  if (!(s2 = strchr (s1, PATHSEP_C)))
Packit d7e8d0
		    s2 = s1 + strlen (s1);
Packit d7e8d0
		  p = malloc (s2 - s1 + 1);
Packit d7e8d0
		  if (p)
Packit d7e8d0
		    {
Packit d7e8d0
		      memcpy (p, s1, s2 - s1);
Packit d7e8d0
		      p[s2-s1] = 0;
Packit d7e8d0
		      trim_spaces (p);
Packit d7e8d0
		      fp = fopen (p,"a");
Packit d7e8d0
		      if (fp)
Packit d7e8d0
			{
Packit d7e8d0
			  setvbuf (fp, NULL, _IOLBF, 0);
Packit d7e8d0
			  errfp = fp;
Packit d7e8d0
			}
Packit d7e8d0
		      free (p);
Packit d7e8d0
		    }
Packit d7e8d0
#ifndef HAVE_DOSISH_SYSTEM
Packit d7e8d0
		}
Packit d7e8d0
#endif
Packit d7e8d0
	    }
Packit d7e8d0
	  free (e);
Packit d7e8d0
        }
Packit d7e8d0
    }
Packit d7e8d0
  UNLOCK (debug_lock);
Packit d7e8d0
Packit d7e8d0
  if (debug_level > 0)
Packit d7e8d0
    {
Packit d7e8d0
      _gpgme_debug (DEBUG_INIT, "gpgme_debug: level=%d\n", debug_level);
Packit d7e8d0
#ifdef HAVE_W32_SYSTEM
Packit d7e8d0
      {
Packit d7e8d0
        const char *name = _gpgme_get_inst_dir ();
Packit d7e8d0
        _gpgme_debug (DEBUG_INIT, "gpgme_debug: gpgme='%s'\n",
Packit d7e8d0
                      name? name: "?");
Packit d7e8d0
      }
Packit d7e8d0
#endif
Packit d7e8d0
    }
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* This should be called as soon as the locks are initialized.  It is
Packit d7e8d0
   required so that the assuan logging gets conncted to the gpgme log
Packit d7e8d0
   stream as early as possible.  */
Packit d7e8d0
void
Packit d7e8d0
_gpgme_debug_subsystem_init (void)
Packit d7e8d0
{
Packit d7e8d0
  debug_init ();
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
/* Log the formatted string FORMAT at debug level LEVEL or higher.
Packit d7e8d0
 *
Packit d7e8d0
 * Returns: 0
Packit d7e8d0
 *
Packit d7e8d0
 * Note that we always return 0 because the old TRACE macro evaluated
Packit d7e8d0
 * to 0 which issues a warning with newer gcc version about an unused
Packit d7e8d0
 * values.  By using a return value of this function this can be
Packit d7e8d0
 * avoided.  Fixme: It might be useful to check whether the return
Packit d7e8d0
 * value from the TRACE macros are actually used somewhere.
Packit d7e8d0
 */
Packit d7e8d0
int
Packit d7e8d0
_gpgme_debug (int level, const char *format, ...)
Packit d7e8d0
{
Packit d7e8d0
  va_list arg_ptr;
Packit d7e8d0
  int saved_errno;
Packit d7e8d0
Packit d7e8d0
  saved_errno = errno;
Packit d7e8d0
  if (debug_level < level)
Packit d7e8d0
    return 0;
Packit d7e8d0
Packit d7e8d0
  va_start (arg_ptr, format);
Packit d7e8d0
  LOCK (debug_lock);
Packit d7e8d0
  {
Packit d7e8d0
#ifdef HAVE_W32CE_SYSTEM
Packit d7e8d0
    SYSTEMTIME t;
Packit d7e8d0
Packit d7e8d0
    GetLocalTime (&t);
Packit d7e8d0
    fprintf (errfp, "GPGME %04d-%02d-%02d %02d:%02d:%02d <0x%04llx>  ",
Packit d7e8d0
	     t.wYear, t.wMonth, t.wDay,
Packit d7e8d0
	     t.wHour, t.wMinute, t.wSecond,
Packit d7e8d0
	     (unsigned long long) ath_self ());
Packit d7e8d0
#else
Packit d7e8d0
    struct tm *tp;
Packit d7e8d0
    time_t atime = time (NULL);
Packit d7e8d0
Packit d7e8d0
    tp = localtime (&atime);
Packit d7e8d0
    fprintf (errfp, "GPGME %04d-%02d-%02d %02d:%02d:%02d <0x%04llx>  ",
Packit d7e8d0
	     1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
Packit d7e8d0
	     tp->tm_hour, tp->tm_min, tp->tm_sec,
Packit d7e8d0
	     (unsigned long long) ath_self ());
Packit d7e8d0
#endif
Packit d7e8d0
  }
Packit d7e8d0
#ifdef FRAME_NR
Packit d7e8d0
  {
Packit d7e8d0
    int indent;
Packit d7e8d0
Packit d7e8d0
    indent = frame_nr > 0? (2 * (frame_nr - 1)):0;
Packit d7e8d0
    fprintf (errfp, "%*s", indent < 40? indent : 40, "");
Packit d7e8d0
  }
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
  vfprintf (errfp, format, arg_ptr);
Packit d7e8d0
  va_end (arg_ptr);
Packit d7e8d0
  if(format && *format && format[strlen (format) - 1] != '\n')
Packit d7e8d0
    putc ('\n', errfp);
Packit d7e8d0
  UNLOCK (debug_lock);
Packit d7e8d0
  fflush (errfp);
Packit d7e8d0
Packit d7e8d0
  gpg_err_set_errno (saved_errno);
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Start a new debug line in *LINE, logged at level LEVEL or higher,
Packit d7e8d0
   and starting with the formatted string FORMAT.  */
Packit d7e8d0
void
Packit d7e8d0
_gpgme_debug_begin (void **line, int level, const char *format, ...)
Packit d7e8d0
{
Packit d7e8d0
  va_list arg_ptr;
Packit d7e8d0
  int res;
Packit d7e8d0
Packit d7e8d0
  if (debug_level < level)
Packit d7e8d0
    {
Packit d7e8d0
      /* Disable logging of this line.  */
Packit d7e8d0
      *line = NULL;
Packit d7e8d0
      return;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  va_start (arg_ptr, format);
Packit d7e8d0
  res = gpgrt_vasprintf ((char **) line, format, arg_ptr);
Packit d7e8d0
  va_end (arg_ptr);
Packit d7e8d0
  if (res < 0)
Packit d7e8d0
    *line = NULL;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Add the formatted string FORMAT to the debug line *LINE.  */
Packit d7e8d0
void
Packit d7e8d0
_gpgme_debug_add (void **line, const char *format, ...)
Packit d7e8d0
{
Packit d7e8d0
  va_list arg_ptr;
Packit d7e8d0
  char *toadd;
Packit d7e8d0
  char *result;
Packit d7e8d0
  int res;
Packit d7e8d0
Packit d7e8d0
  if (!*line)
Packit d7e8d0
    return;
Packit d7e8d0
Packit d7e8d0
  va_start (arg_ptr, format);
Packit d7e8d0
  res = gpgrt_vasprintf (&toadd, format, arg_ptr);
Packit d7e8d0
  va_end (arg_ptr);
Packit d7e8d0
  if (res < 0)
Packit d7e8d0
    {
Packit d7e8d0
      gpgrt_free (*line);
Packit d7e8d0
      *line = NULL;
Packit d7e8d0
    }
Packit d7e8d0
  res = gpgrt_asprintf (&result, "%s%s", *(char **) line, toadd);
Packit d7e8d0
  gpgrt_free (toadd);
Packit d7e8d0
  gpgrt_free (*line);
Packit d7e8d0
  if (res < 0)
Packit d7e8d0
    *line = NULL;
Packit d7e8d0
  else
Packit d7e8d0
    *line = result;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Finish construction of *LINE and send it to the debug output
Packit d7e8d0
   stream.  */
Packit d7e8d0
void
Packit d7e8d0
_gpgme_debug_end (void **line)
Packit d7e8d0
{
Packit d7e8d0
  if (!*line)
Packit d7e8d0
    return;
Packit d7e8d0
Packit d7e8d0
  /* The smallest possible level is 1, so force logging here by
Packit d7e8d0
     using that.  */
Packit d7e8d0
  _gpgme_debug (1, "%s", *line);
Packit d7e8d0
  gpgrt_free (*line);
Packit d7e8d0
  *line = NULL;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
#define TOHEX(val) (((val) < 10) ? ((val) + '0') : ((val) - 10 + 'a'))
Packit d7e8d0
Packit d7e8d0
void
Packit d7e8d0
_gpgme_debug_buffer (int lvl, const char *const fmt,
Packit d7e8d0
		     const char *const func, const char *const buffer,
Packit d7e8d0
		     size_t len)
Packit d7e8d0
{
Packit d7e8d0
  int idx = 0;
Packit d7e8d0
  int j;
Packit d7e8d0
Packit d7e8d0
  if (!_gpgme_debug_trace ())
Packit d7e8d0
    return;
Packit d7e8d0
Packit d7e8d0
  while (idx < len)
Packit d7e8d0
    {
Packit d7e8d0
      char str[51];
Packit d7e8d0
      char *strp = str;
Packit d7e8d0
      char *strp2 = &str[34];
Packit d7e8d0
Packit d7e8d0
      for (j = 0; j < 16; j++)
Packit d7e8d0
	{
Packit d7e8d0
	  unsigned char val;
Packit d7e8d0
	  if (idx < len)
Packit d7e8d0
	    {
Packit d7e8d0
	      val = buffer[idx++];
Packit d7e8d0
	      *(strp++) = TOHEX (val >> 4);
Packit d7e8d0
	      *(strp++) = TOHEX (val % 16);
Packit d7e8d0
	      *(strp2++) = isprint (val) ? val : '.';
Packit d7e8d0
	    }
Packit d7e8d0
	  else
Packit d7e8d0
	    {
Packit d7e8d0
	      *(strp++) = ' ';
Packit d7e8d0
	      *(strp++) = ' ';
Packit d7e8d0
	    }
Packit d7e8d0
	  if (j == 7)
Packit d7e8d0
	    *(strp++) = ' ';
Packit d7e8d0
	}
Packit d7e8d0
      *(strp++) = ' ';
Packit d7e8d0
      *(strp2) = '\0';
Packit d7e8d0
Packit d7e8d0
      _gpgme_debug (lvl, fmt, func, str);
Packit d7e8d0
    }
Packit d7e8d0
}