Fix compiler warnings about unused value in TRACE macros.
[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 int _gpgme_debug_frame_end (void)
84 {
85 #ifdef FRAME_NR
86   frame_nr--;
87 #endif
88   return 0;
89 }
90
91
92 \f
93 /* Remove leading and trailing white spaces.  */
94 static char *
95 trim_spaces (char *str)
96 {
97   char *string, *p, *mark;
98
99   string = str;
100   /* Find first non space character.  */
101   for (p = string; *p && isspace (*(unsigned char *) p); p++)
102     ;
103   /* Move characters.  */
104   for (mark = NULL; (*string = *p); string++, p++)
105     if (isspace (*(unsigned char *) p))
106       {
107         if (!mark)
108           mark = string;
109       }
110     else
111       mark = NULL;
112   if (mark)
113     *mark = '\0';       /* Remove trailing spaces.  */
114
115   return str;
116 }
117
118
119 /* This is an internal function to set debug info.  The caller must
120    assure that this function is called only by one thread at a time.
121    The function may have no effect if called after the debug system
122    has been initialized.  Returns 0 on success.  */
123 int
124 _gpgme_debug_set_debug_envvar (const char *value)
125 {
126   free (envvar_override);
127   envvar_override = strdup (value);
128   return !envvar_override;
129 }
130
131
132 static void
133 debug_init (void)
134 {
135   static int initialized;
136
137   LOCK (debug_lock);
138   if (!initialized)
139     {
140       gpgme_error_t err;
141       char *e;
142       const char *s1, *s2;;
143
144       if (envvar_override)
145         {
146           e = strdup (envvar_override);
147           free (envvar_override);
148           envvar_override = NULL;
149         }
150       else
151         {
152 #ifdef HAVE_W32CE_SYSTEM
153           e = _gpgme_w32ce_get_debug_envvar ();
154 #else /*!HAVE_W32CE_SYSTEM*/
155           err = _gpgme_getenv ("GPGME_DEBUG", &e);
156           if (err)
157             {
158               UNLOCK (debug_lock);
159               return;
160             }
161 #endif /*!HAVE_W32CE_SYSTEM*/
162         }
163
164       initialized = 1;
165       errfp = stderr;
166       if (e)
167         {
168           debug_level = atoi (e);
169           s1 = strchr (e, PATHSEP_C);
170           if (s1)
171             {
172 #ifndef HAVE_DOSISH_SYSTEM
173               if (getuid () == geteuid ()
174 #if defined(HAVE_GETGID) && defined(HAVE_GETEGID)
175                   && getgid () == getegid ()
176 #endif
177                   )
178                 {
179 #endif
180                   char *p;
181                   FILE *fp;
182
183                   s1++;
184                   if (!(s2 = strchr (s1, PATHSEP_C)))
185                     s2 = s1 + strlen (s1);
186                   p = malloc (s2 - s1 + 1);
187                   if (p)
188                     {
189                       memcpy (p, s1, s2 - s1);
190                       p[s2-s1] = 0;
191                       trim_spaces (p);
192                       fp = fopen (p,"a");
193                       if (fp)
194                         {
195                           setvbuf (fp, NULL, _IOLBF, 0);
196                           errfp = fp;
197                         }
198                       free (p);
199                     }
200 #ifndef HAVE_DOSISH_SYSTEM
201                 }
202 #endif
203             }
204           free (e);
205         }
206     }
207   UNLOCK (debug_lock);
208
209   if (debug_level > 0)
210     _gpgme_debug (DEBUG_INIT, "gpgme_debug: level=%d\n", debug_level);
211 }
212
213
214
215 /* This should be called as soon as the locks are intialized.  It is
216    required so that the assuan logging gets conncted to the gpgme log
217    stream as early as possible.  */
218 void
219 _gpgme_debug_subsystem_init (void)
220 {
221   debug_init ();
222 }
223
224
225
226 \f
227 /* Log the formatted string FORMAT at debug level LEVEL or higher.
228  *
229  * Returns: 0
230  *
231  * Note that we always return 0 because the old TRACE macro evaluated
232  * to 0 which issues a warning with newer gcc version about an unused
233  * values.  By using a return value of this function this can be
234  * avoided.  Fixme: It might be useful to check whether the return
235  * value from the TRACE macros are actually used somewhere.
236  */
237 int
238 _gpgme_debug (int level, const char *format, ...)
239 {
240   va_list arg_ptr;
241   int saved_errno;
242
243   saved_errno = errno;
244   if (debug_level < level)
245     return 0;
246
247   va_start (arg_ptr, format);
248   LOCK (debug_lock);
249   {
250 #ifdef HAVE_W32CE_SYSTEM
251     SYSTEMTIME t;
252
253     GetLocalTime (&t);
254     fprintf (errfp, "GPGME %04d-%02d-%02d %02d:%02d:%02d <0x%04llx>  ",
255              t.wYear, t.wMonth, t.wDay,
256              t.wHour, t.wMinute, t.wSecond,
257              (unsigned long long) ath_self ());
258 #else
259     struct tm *tp;
260     time_t atime = time (NULL);
261
262     tp = localtime (&atime);
263     fprintf (errfp, "GPGME %04d-%02d-%02d %02d:%02d:%02d <0x%04llx>  ",
264              1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
265              tp->tm_hour, tp->tm_min, tp->tm_sec,
266              (unsigned long long) ath_self ());
267 #endif
268   }
269 #ifdef FRAME_NR
270   {
271     int indent;
272
273     indent = frame_nr > 0? (2 * (frame_nr - 1)):0;
274     fprintf (errfp, "%*s", indent < 40? indent : 40, "");
275   }
276 #endif
277
278   vfprintf (errfp, format, arg_ptr);
279   va_end (arg_ptr);
280   if(format && *format && format[strlen (format) - 1] != '\n')
281     putc ('\n', errfp);
282   UNLOCK (debug_lock);
283   fflush (errfp);
284
285   gpg_err_set_errno (saved_errno);
286   return 0;
287 }
288
289
290 /* Start a new debug line in *LINE, logged at level LEVEL or higher,
291    and starting with the formatted string FORMAT.  */
292 void
293 _gpgme_debug_begin (void **line, int level, const char *format, ...)
294 {
295   va_list arg_ptr;
296   int res;
297
298   if (debug_level < level)
299     {
300       /* Disable logging of this line.  */
301       *line = NULL;
302       return;
303     }
304
305   va_start (arg_ptr, format);
306   res = vasprintf ((char **) line, format, arg_ptr);
307   va_end (arg_ptr);
308   if (res < 0)
309     *line = NULL;
310 }
311
312
313 /* Add the formatted string FORMAT to the debug line *LINE.  */
314 void
315 _gpgme_debug_add (void **line, const char *format, ...)
316 {
317   va_list arg_ptr;
318   char *toadd;
319   char *result;
320   int res;
321
322   if (!*line)
323     return;
324
325   va_start (arg_ptr, format);
326   res = vasprintf (&toadd, format, arg_ptr);
327   va_end (arg_ptr);
328   if (res < 0)
329     {
330       free (*line);
331       *line = NULL;
332     }
333   res = asprintf (&result, "%s%s", *(char **) line, toadd);
334   free (toadd);
335   free (*line);
336   if (res < 0)
337     *line = NULL;
338   else
339     *line = result;
340 }
341
342
343 /* Finish construction of *LINE and send it to the debug output
344    stream.  */
345 void
346 _gpgme_debug_end (void **line)
347 {
348   if (!*line)
349     return;
350
351   /* The smallest possible level is 1, so force logging here by
352      using that.  */
353   _gpgme_debug (1, "%s", *line);
354   free (*line);
355   *line = NULL;
356 }
357
358
359 #define TOHEX(val) (((val) < 10) ? ((val) + '0') : ((val) - 10 + 'a'))
360
361 void
362 _gpgme_debug_buffer (int lvl, const char *const fmt,
363                      const char *const func, const char *const buffer,
364                      size_t len)
365 {
366   int idx = 0;
367   int j;
368
369   if (!_gpgme_debug_trace ())
370     return;
371
372   while (idx < len)
373     {
374       char str[51];
375       char *strp = str;
376       char *strp2 = &str[34];
377
378       for (j = 0; j < 16; j++)
379         {
380           unsigned char val;
381           if (idx < len)
382             {
383               val = buffer[idx++];
384               *(strp++) = TOHEX (val >> 4);
385               *(strp++) = TOHEX (val % 16);
386               *(strp2++) = isprint (val) ? val : '.';
387             }
388           else
389             {
390               *(strp++) = ' ';
391               *(strp++) = ' ';
392             }
393           if (j == 7)
394             *(strp++) = ' ';
395         }
396       *(strp++) = ' ';
397       *(strp2) = '\0';
398
399       _gpgme_debug (lvl, fmt, func, str);
400     }
401 }