gpgme-tool: Use membuf functions to build up strings.
[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     char spaces[] = "                                        ";
262     int nr_spaces = sizeof (spaces) - 1;
263     int nr_columns;
264
265     nr_columns = 2 * (frame_nr - 1);
266     if (nr_columns > nr_spaces)
267       nr_columns = nr_spaces;
268     if (nr_columns < 0)
269       nr_columns = 0;
270     spaces[nr_columns] = '\0';
271     fprintf (errfp, "%s", spaces);
272   }
273 #endif
274
275   vfprintf (errfp, format, arg_ptr);
276   va_end (arg_ptr);
277   if(format && *format && format[strlen (format) - 1] != '\n')
278     putc ('\n', errfp);
279   UNLOCK (debug_lock);
280   fflush (errfp);
281
282   gpg_err_set_errno (saved_errno);
283 }
284
285
286 /* Start a new debug line in *LINE, logged at level LEVEL or higher,
287    and starting with the formatted string FORMAT.  */
288 void
289 _gpgme_debug_begin (void **line, int level, const char *format, ...)
290 {
291   va_list arg_ptr;
292   int res;
293
294   if (debug_level < level)
295     {
296       /* Disable logging of this line.  */
297       *line = NULL;
298       return;
299     }
300
301   va_start (arg_ptr, format);
302   res = vasprintf ((char **) line, format, arg_ptr);
303   va_end (arg_ptr);
304   if (res < 0)
305     *line = NULL;
306 }
307
308
309 /* Add the formatted string FORMAT to the debug line *LINE.  */
310 void
311 _gpgme_debug_add (void **line, const char *format, ...)
312 {
313   va_list arg_ptr;
314   char *toadd;
315   char *result;
316   int res;
317
318   if (!*line)
319     return;
320
321   va_start (arg_ptr, format);
322   res = vasprintf (&toadd, format, arg_ptr);
323   va_end (arg_ptr);
324   if (res < 0)
325     {
326       free (*line);
327       *line = NULL;
328     }
329   res = asprintf (&result, "%s%s", *(char **) line, toadd);
330   free (toadd);
331   free (*line);
332   if (res < 0)
333     *line = NULL;
334   else
335     *line = result;
336 }
337
338
339 /* Finish construction of *LINE and send it to the debug output
340    stream.  */
341 void
342 _gpgme_debug_end (void **line)
343 {
344   if (!*line)
345     return;
346
347   /* The smallest possible level is 1, so force logging here by
348      using that.  */
349   _gpgme_debug (1, "%s", *line);
350   free (*line);
351   *line = NULL;
352 }
353
354
355 #define TOHEX(val) (((val) < 10) ? ((val) + '0') : ((val) - 10 + 'a'))
356
357 void
358 _gpgme_debug_buffer (int lvl, const char *const fmt,
359                      const char *const func, const char *const buffer,
360                      size_t len)
361 {
362   int idx = 0;
363   int j;
364
365   if (!_gpgme_debug_trace ())
366     return;
367
368   while (idx < len)
369     {
370       char str[51];
371       char *strp = str;
372       char *strp2 = &str[34];
373
374       for (j = 0; j < 16; j++)
375         {
376           unsigned char val;
377           if (idx < len)
378             {
379               val = buffer[idx++];
380               *(strp++) = TOHEX (val >> 4);
381               *(strp++) = TOHEX (val % 16);
382               *(strp2++) = isprint (val) ? val : '.';
383             }
384           else
385             {
386               *(strp++) = ' ';
387               *(strp++) = ' ';
388             }
389           if (j == 7)
390             *(strp++) = ' ';
391         }
392       *(strp++) = ' ';
393       *(strp2) = '\0';
394
395       _gpgme_debug (lvl, fmt, func, str);
396     }
397 }