Release 1.4.3.
[gpgme.git] / src / debug.c
1 /* debug.c - helpful output in desperate situations
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 g10 Code GmbH
4
5    This file is part of GPGME.
6
7    GPGME is free software; you can redistribute it and/or modify it
8    under the terms of the GNU Lesser General Public License as
9    published by the Free Software Foundation; either version 2.1 of
10    the License, or (at your option) any later version.
11
12    GPGME is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16
17    You should have received a copy of the GNU Lesser General Public
18    License along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20    MA 02110-1301, USA.  */
21
22 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdarg.h>
29 #ifdef HAVE_UNISTD_H
30 # include <unistd.h>
31 #endif
32 #include <ctype.h>
33 #include <errno.h>
34 #include <time.h>
35 #ifndef HAVE_DOSISH_SYSTEM
36 # ifdef HAVE_SYS_TYPES_H
37 #  include <sys/types.h>
38 # endif
39 # ifdef HAVE_SYS_STAT_H
40 #  include <sys/stat.h>
41 # endif
42 # include <fcntl.h>
43 #endif
44 #include <assert.h>
45
46 #include "util.h"
47 #include "ath.h"
48 #include "sema.h"
49 #include "debug.h"
50
51 \f
52 /* Lock to serialize initialization of the debug output subsystem and
53    output of actual debug messages.  */
54 DEFINE_STATIC_LOCK (debug_lock);
55
56 /* The amount of detail requested by the user, per environment
57    variable GPGME_DEBUG.  */
58 static int debug_level;
59
60 /* The output stream for the debug messages.  */
61 static FILE *errfp;
62
63 /* If not NULL, this malloced string is used instead of the
64    GPGME_DEBUG envvar.  It must have been set before the debug
65    subsystem has been initialized.  Using it later may or may not have
66    any effect.  */
67 static char *envvar_override;
68
69 \f
70 #ifdef HAVE_TLS
71 #define FRAME_NR
72 static __thread int frame_nr = 0;
73 #endif
74
75 void
76 _gpgme_debug_frame_begin (void)
77 {
78 #ifdef FRAME_NR
79   frame_nr++;
80 #endif
81 }
82
83 void _gpgme_debug_frame_end (void)
84 {
85 #ifdef FRAME_NR
86   frame_nr--;
87 #endif
88 }
89
90
91 \f
92 /* Remove leading and trailing white spaces.  */
93 static char *
94 trim_spaces (char *str)
95 {
96   char *string, *p, *mark;
97
98   string = str;
99   /* Find first non space character.  */
100   for (p = string; *p && isspace (*(unsigned char *) p); p++)
101     ;
102   /* Move characters.  */
103   for (mark = NULL; (*string = *p); string++, p++)
104     if (isspace (*(unsigned char *) p))
105       {
106         if (!mark)
107           mark = string;
108       }
109     else
110       mark = NULL;
111   if (mark)
112     *mark = '\0';       /* Remove trailing spaces.  */
113
114   return str;
115 }
116
117
118 /* This is an internal function to set debug info.  The caller must
119    assure that this function is called only by one thread at a time.
120    The function may have no effect if called after the debug system
121    has been initialized.  Returns 0 on success.  */
122 int
123 _gpgme_debug_set_debug_envvar (const char *value)
124 {
125   free (envvar_override);
126   envvar_override = strdup (value);
127   return !envvar_override;
128 }
129
130
131 static void
132 debug_init (void)
133 {
134   static int initialized;
135
136   LOCK (debug_lock);
137   if (!initialized)
138     {
139       gpgme_error_t err;
140       char *e;
141       const char *s1, *s2;;
142
143       if (envvar_override)
144         {
145           e = strdup (envvar_override);
146           free (envvar_override);
147           envvar_override = NULL;
148         }
149       else
150         {
151 #ifdef HAVE_W32CE_SYSTEM
152           e = _gpgme_w32ce_get_debug_envvar ();
153 #else /*!HAVE_W32CE_SYSTEM*/
154           err = _gpgme_getenv ("GPGME_DEBUG", &e);
155           if (err)
156             {
157               UNLOCK (debug_lock);
158               return;
159             }
160 #endif /*!HAVE_W32CE_SYSTEM*/
161         }
162
163       initialized = 1;
164       errfp = stderr;
165       if (e)
166         {
167           debug_level = atoi (e);
168           s1 = strchr (e, PATHSEP_C);
169           if (s1)
170             {
171 #ifndef HAVE_DOSISH_SYSTEM
172               if (getuid () == geteuid ()
173 #if defined(HAVE_GETGID) && defined(HAVE_GETEGID)
174                   && getgid () == getegid ()
175 #endif
176                   )
177                 {
178 #endif
179                   char *p;
180                   FILE *fp;
181
182                   s1++;
183                   if (!(s2 = strchr (s1, PATHSEP_C)))
184                     s2 = s1 + strlen (s1);
185                   p = malloc (s2 - s1 + 1);
186                   if (p)
187                     {
188                       memcpy (p, s1, s2 - s1);
189                       p[s2-s1] = 0;
190                       trim_spaces (p);
191                       fp = fopen (p,"a");
192                       if (fp)
193                         {
194                           setvbuf (fp, NULL, _IOLBF, 0);
195                           errfp = fp;
196                         }
197                       free (p);
198                     }
199 #ifndef HAVE_DOSISH_SYSTEM
200                 }
201 #endif
202             }
203           free (e);
204         }
205     }
206   UNLOCK (debug_lock);
207
208   if (debug_level > 0)
209     _gpgme_debug (DEBUG_INIT, "gpgme_debug: level=%d\n", debug_level);
210 }
211
212
213
214 /* This should be called as soon as the locks are intialized.  It is
215    required so that the assuan logging gets conncted to the gpgme log
216    stream as early as possible.  */
217 void
218 _gpgme_debug_subsystem_init (void)
219 {
220   debug_init ();
221 }
222
223
224
225 \f
226 /* Log the formatted string FORMAT at debug level LEVEL or higher.  */
227 void
228 _gpgme_debug (int level, const char *format, ...)
229 {
230   va_list arg_ptr;
231   int saved_errno;
232
233   saved_errno = errno;
234   if (debug_level < level)
235     return;
236
237   va_start (arg_ptr, format);
238   LOCK (debug_lock);
239   {
240 #ifdef HAVE_W32CE_SYSTEM
241     SYSTEMTIME t;
242
243     GetLocalTime (&t);
244     fprintf (errfp, "GPGME %04d-%02d-%02d %02d:%02d:%02d <0x%04llx>  ",
245              t.wYear, t.wMonth, t.wDay,
246              t.wHour, t.wMinute, t.wSecond,
247              (unsigned long long) ath_self ());
248 #else
249     struct tm *tp;
250     time_t atime = time (NULL);
251
252     tp = localtime (&atime);
253     fprintf (errfp, "GPGME %04d-%02d-%02d %02d:%02d:%02d <0x%04llx>  ",
254              1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
255              tp->tm_hour, tp->tm_min, tp->tm_sec,
256              (unsigned long long) ath_self ());
257 #endif
258   }
259 #ifdef FRAME_NR
260   {
261     int indent;
262
263     indent = frame_nr > 0? (2 * (frame_nr - 1)):0;
264     fprintf (errfp, "%*s", indent < 40? indent : 40, "");
265   }
266 #endif
267
268   vfprintf (errfp, format, arg_ptr);
269   va_end (arg_ptr);
270   if(format && *format && format[strlen (format) - 1] != '\n')
271     putc ('\n', errfp);
272   UNLOCK (debug_lock);
273   fflush (errfp);
274
275   gpg_err_set_errno (saved_errno);
276 }
277
278
279 /* Start a new debug line in *LINE, logged at level LEVEL or higher,
280    and starting with the formatted string FORMAT.  */
281 void
282 _gpgme_debug_begin (void **line, int level, const char *format, ...)
283 {
284   va_list arg_ptr;
285   int res;
286
287   if (debug_level < level)
288     {
289       /* Disable logging of this line.  */
290       *line = NULL;
291       return;
292     }
293
294   va_start (arg_ptr, format);
295   res = vasprintf ((char **) line, format, arg_ptr);
296   va_end (arg_ptr);
297   if (res < 0)
298     *line = NULL;
299 }
300
301
302 /* Add the formatted string FORMAT to the debug line *LINE.  */
303 void
304 _gpgme_debug_add (void **line, const char *format, ...)
305 {
306   va_list arg_ptr;
307   char *toadd;
308   char *result;
309   int res;
310
311   if (!*line)
312     return;
313
314   va_start (arg_ptr, format);
315   res = vasprintf (&toadd, format, arg_ptr);
316   va_end (arg_ptr);
317   if (res < 0)
318     {
319       free (*line);
320       *line = NULL;
321     }
322   res = asprintf (&result, "%s%s", *(char **) line, toadd);
323   free (toadd);
324   free (*line);
325   if (res < 0)
326     *line = NULL;
327   else
328     *line = result;
329 }
330
331
332 /* Finish construction of *LINE and send it to the debug output
333    stream.  */
334 void
335 _gpgme_debug_end (void **line)
336 {
337   if (!*line)
338     return;
339
340   /* The smallest possible level is 1, so force logging here by
341      using that.  */
342   _gpgme_debug (1, "%s", *line);
343   free (*line);
344   *line = NULL;
345 }
346
347
348 #define TOHEX(val) (((val) < 10) ? ((val) + '0') : ((val) - 10 + 'a'))
349
350 void
351 _gpgme_debug_buffer (int lvl, const char *const fmt,
352                      const char *const func, const char *const buffer,
353                      size_t len)
354 {
355   int idx = 0;
356   int j;
357
358   if (!_gpgme_debug_trace ())
359     return;
360
361   while (idx < len)
362     {
363       char str[51];
364       char *strp = str;
365       char *strp2 = &str[34];
366
367       for (j = 0; j < 16; j++)
368         {
369           unsigned char val;
370           if (idx < len)
371             {
372               val = buffer[idx++];
373               *(strp++) = TOHEX (val >> 4);
374               *(strp++) = TOHEX (val % 16);
375               *(strp2++) = isprint (val) ? val : '.';
376             }
377           else
378             {
379               *(strp++) = ' ';
380               *(strp++) = ' ';
381             }
382           if (j == 7)
383             *(strp++) = ' ';
384         }
385       *(strp++) = ' ';
386       *(strp2) = '\0';
387
388       _gpgme_debug (lvl, fmt, func, str);
389     }
390 }