w32: Remove all support for WindowsCE
[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 "sys-util.h"
50 #include "debug.h"
51
52 \f
53 /* Lock to serialize initialization of the debug output subsystem and
54    output of actual debug messages.  */
55 DEFINE_STATIC_LOCK (debug_lock);
56
57 /* The amount of detail requested by the user, per environment
58    variable GPGME_DEBUG.  */
59 static int debug_level;
60
61 /* The output stream for the debug messages.  */
62 static FILE *errfp;
63
64 /* If not NULL, this malloced string is used instead of the
65    GPGME_DEBUG envvar.  It must have been set before the debug
66    subsystem has been initialized.  Using it later may or may not have
67    any effect.  */
68 static char *envvar_override;
69
70 \f
71 #ifdef HAVE_TLS
72 #define FRAME_NR
73 static __thread int frame_nr = 0;
74 #endif
75
76 void
77 _gpgme_debug_frame_begin (void)
78 {
79 #ifdef FRAME_NR
80   frame_nr++;
81 #endif
82 }
83
84 int _gpgme_debug_frame_end (void)
85 {
86 #ifdef FRAME_NR
87   frame_nr--;
88 #endif
89   return 0;
90 }
91
92
93 \f
94 /* Remove leading and trailing white spaces.  */
95 static char *
96 trim_spaces (char *str)
97 {
98   char *string, *p, *mark;
99
100   string = str;
101   /* Find first non space character.  */
102   for (p = string; *p && isspace (*(unsigned char *) p); p++)
103     ;
104   /* Move characters.  */
105   for (mark = NULL; (*string = *p); string++, p++)
106     if (isspace (*(unsigned char *) p))
107       {
108         if (!mark)
109           mark = string;
110       }
111     else
112       mark = NULL;
113   if (mark)
114     *mark = '\0';       /* Remove trailing spaces.  */
115
116   return str;
117 }
118
119
120 /* This is an internal function to set debug info.  The caller must
121    assure that this function is called only by one thread at a time.
122    The function may have no effect if called after the debug system
123    has been initialized.  Returns 0 on success.  */
124 int
125 _gpgme_debug_set_debug_envvar (const char *value)
126 {
127   free (envvar_override);
128   envvar_override = strdup (value);
129   return !envvar_override;
130 }
131
132
133 static void
134 debug_init (void)
135 {
136   static int initialized;
137
138   LOCK (debug_lock);
139   if (!initialized)
140     {
141       gpgme_error_t err;
142       char *e;
143       const char *s1, *s2;;
144
145       if (envvar_override)
146         {
147           e = strdup (envvar_override);
148           free (envvar_override);
149           envvar_override = NULL;
150         }
151       else
152         {
153           err = _gpgme_getenv ("GPGME_DEBUG", &e);
154           if (err)
155             {
156               UNLOCK (debug_lock);
157               return;
158             }
159         }
160
161       initialized = 1;
162       errfp = stderr;
163       if (e)
164         {
165           debug_level = atoi (e);
166           s1 = strchr (e, PATHSEP_C);
167           if (s1)
168             {
169 #ifndef HAVE_DOSISH_SYSTEM
170               if (getuid () == geteuid ()
171 #if defined(HAVE_GETGID) && defined(HAVE_GETEGID)
172                   && getgid () == getegid ()
173 #endif
174                   )
175                 {
176 #endif
177                   char *p;
178                   FILE *fp;
179
180                   s1++;
181                   if (!(s2 = strchr (s1, PATHSEP_C)))
182                     s2 = s1 + strlen (s1);
183                   p = malloc (s2 - s1 + 1);
184                   if (p)
185                     {
186                       memcpy (p, s1, s2 - s1);
187                       p[s2-s1] = 0;
188                       trim_spaces (p);
189                       fp = fopen (p,"a");
190                       if (fp)
191                         {
192                           setvbuf (fp, NULL, _IOLBF, 0);
193                           errfp = fp;
194                         }
195                       free (p);
196                     }
197 #ifndef HAVE_DOSISH_SYSTEM
198                 }
199 #endif
200             }
201           free (e);
202         }
203     }
204   UNLOCK (debug_lock);
205
206   if (debug_level > 0)
207     {
208       _gpgme_debug (DEBUG_INIT, "gpgme_debug: level=%d\n", debug_level);
209 #ifdef HAVE_W32_SYSTEM
210       {
211         const char *name = _gpgme_get_inst_dir ();
212         _gpgme_debug (DEBUG_INIT, "gpgme_debug: gpgme='%s'\n",
213                       name? name: "?");
214       }
215 #endif
216     }
217 }
218
219
220
221 /* This should be called as soon as the locks are initialized.  It is
222    required so that the assuan logging gets conncted to the gpgme log
223    stream as early as possible.  */
224 void
225 _gpgme_debug_subsystem_init (void)
226 {
227   debug_init ();
228 }
229
230
231
232 \f
233 /* Log the formatted string FORMAT at debug level LEVEL or higher.
234  *
235  * Returns: 0
236  *
237  * Note that we always return 0 because the old TRACE macro evaluated
238  * to 0 which issues a warning with newer gcc version about an unused
239  * values.  By using a return value of this function this can be
240  * avoided.  Fixme: It might be useful to check whether the return
241  * value from the TRACE macros are actually used somewhere.
242  */
243 int
244 _gpgme_debug (int level, const char *format, ...)
245 {
246   va_list arg_ptr;
247   int saved_errno;
248
249   saved_errno = errno;
250   if (debug_level < level)
251     return 0;
252
253   va_start (arg_ptr, format);
254   LOCK (debug_lock);
255   {
256     struct tm *tp;
257     time_t atime = time (NULL);
258
259     tp = localtime (&atime);
260     fprintf (errfp, "GPGME %04d-%02d-%02d %02d:%02d:%02d <0x%04llx>  ",
261              1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
262              tp->tm_hour, tp->tm_min, tp->tm_sec,
263              (unsigned long long) ath_self ());
264   }
265 #ifdef FRAME_NR
266   {
267     int indent;
268
269     indent = frame_nr > 0? (2 * (frame_nr - 1)):0;
270     fprintf (errfp, "%*s", indent < 40? indent : 40, "");
271   }
272 #endif
273
274   vfprintf (errfp, format, arg_ptr);
275   va_end (arg_ptr);
276   if(format && *format && format[strlen (format) - 1] != '\n')
277     putc ('\n', errfp);
278   UNLOCK (debug_lock);
279   fflush (errfp);
280
281   gpg_err_set_errno (saved_errno);
282   return 0;
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 = gpgrt_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 = gpgrt_vasprintf (&toadd, format, arg_ptr);
323   va_end (arg_ptr);
324   if (res < 0)
325     {
326       gpgrt_free (*line);
327       *line = NULL;
328     }
329   res = gpgrt_asprintf (&result, "%s%s", *(char **) line, toadd);
330   gpgrt_free (toadd);
331   gpgrt_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   gpgrt_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   if (!buffer)
369     return;
370
371   while (idx < len)
372     {
373       char str[51];
374       char *strp = str;
375       char *strp2 = &str[34];
376
377       for (j = 0; j < 16; j++)
378         {
379           unsigned char val;
380           if (idx < len)
381             {
382               val = buffer[idx++];
383               *(strp++) = TOHEX (val >> 4);
384               *(strp++) = TOHEX (val % 16);
385               *(strp2++) = isprint (val) ? val : '.';
386             }
387           else
388             {
389               *(strp++) = ' ';
390               *(strp++) = ' ';
391             }
392           if (j == 7)
393             *(strp++) = ' ';
394         }
395       *(strp++) = ' ';
396       *(strp2) = '\0';
397
398       _gpgme_debug (lvl, fmt, func, str);
399     }
400 }