Enable debugging feature for W32CE.
[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 #include <unistd.h>
30 #include <ctype.h>
31 #include <errno.h>
32 #include <time.h>
33 #ifndef HAVE_DOSISH_SYSTEM
34 #  include <sys/types.h>
35 #  include <sys/stat.h>
36 #  include <fcntl.h>
37 #endif
38 #include <assert.h>
39
40 #include "util.h"
41 #include "ath.h"
42 #include "sema.h"
43 #include "debug.h"
44
45 \f
46 /* Lock to serialize initialization of the debug output subsystem and
47    output of actual debug messages.  */
48 DEFINE_STATIC_LOCK (debug_lock);
49
50 /* The amount of detail requested by the user, per environment
51    variable GPGME_DEBUG.  */
52 static int debug_level;
53
54 /* The output stream for the debug messages.  */
55 static FILE *errfp;
56
57 \f
58 #ifdef HAVE_TLS
59 #define FRAME_NR
60 static __thread int frame_nr = 0;
61 #endif
62
63 void
64 _gpgme_debug_frame_begin (void)
65 {
66 #ifdef FRAME_NR
67   frame_nr++;
68 #endif
69 }
70
71 void _gpgme_debug_frame_end (void)
72 {
73 #ifdef FRAME_NR
74   frame_nr--;
75 #endif
76 }
77
78
79 \f
80 /* Remove leading and trailing white spaces.  */
81 static char *
82 trim_spaces (char *str)
83 {
84   char *string, *p, *mark;
85
86   string = str;
87   /* Find first non space character.  */
88   for (p = string; *p && isspace (*(unsigned char *) p); p++)
89     ;
90   /* Move characters.  */
91   for (mark = NULL; (*string = *p); string++, p++)
92     if (isspace (*(unsigned char *) p))
93       {
94         if (!mark)
95           mark = string;
96       }
97     else
98       mark = NULL;
99   if (mark)
100     *mark = '\0';       /* Remove trailing spaces.  */
101
102   return str;
103 }
104
105
106 static void
107 debug_init (void)
108 {
109   static int initialized;
110
111   LOCK (debug_lock);
112   if (!initialized)
113     {
114       gpgme_error_t err;
115       char *e;
116       const char *s1, *s2;;
117
118 #ifdef HAVE_W32CE_SYSTEM
119       e = _gpgme_w32ce_get_debug_envvar ();
120 #else /*!HAVE_W32CE_SYSTEM*/
121       err = _gpgme_getenv ("GPGME_DEBUG", &e);
122       if (err)
123         {
124           UNLOCK (debug_lock);
125           return;
126         }
127 #endif /*!HAVE_W32CE_SYSTEM*/
128
129       initialized = 1;
130       errfp = stderr;
131       if (e)
132         {
133           debug_level = atoi (e);
134           s1 = strchr (e, PATHSEP_C);
135           if (s1)
136             {
137 #ifndef HAVE_DOSISH_SYSTEM
138               if (getuid () == geteuid ()
139 #if defined(HAVE_GETGID) && defined(HAVE_GETEGID)
140                   && getgid () == getegid ()
141 #endif
142                   )
143                 {
144 #endif
145                   char *p;
146                   FILE *fp;
147
148                   s1++;
149                   if (!(s2 = strchr (s1, PATHSEP_C)))
150                     s2 = s1 + strlen (s1);
151                   p = malloc (s2 - s1 + 1);
152                   if (p)
153                     {
154                       memcpy (p, s1, s2 - s1);
155                       p[s2-s1] = 0;
156                       trim_spaces (p);
157                       fp = fopen (p,"a");
158                       if (fp)
159                         {
160                           setvbuf (fp, NULL, _IOLBF, 0);
161                           errfp = fp;
162                         }
163                       free (p);
164                     }
165 #ifndef HAVE_DOSISH_SYSTEM
166                 }
167 #endif
168             }
169           free (e);
170         }
171     }
172   UNLOCK (debug_lock);
173
174   if (debug_level > 0)
175     _gpgme_debug (DEBUG_INIT, "gpgme_debug: level=%d\n", debug_level);
176 }
177
178
179
180 /* This should be called as soon as the locks are intialized.  It is
181    required so that the assuan logging gets conncted to the gpgme log
182    stream as early as possible.  */
183 void
184 _gpgme_debug_subsystem_init (void)
185 {
186   debug_init ();
187 }
188
189
190
191 \f
192 /* Log the formatted string FORMAT at debug level LEVEL or higher.  */
193 void
194 _gpgme_debug (int level, const char *format, ...)
195 {
196   va_list arg_ptr;
197   int saved_errno;
198
199   saved_errno = errno;
200   if (debug_level < level)
201     return;
202     
203   va_start (arg_ptr, format);
204   LOCK (debug_lock);
205   {
206     struct tm *tp;
207     time_t atime = time (NULL);
208     
209     tp = localtime (&atime);
210     fprintf (errfp, "GPGME %04d-%02d-%02d %02d:%02d:%02d <0x%04llx>  ",
211              1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
212              tp->tm_hour, tp->tm_min, tp->tm_sec,
213              (unsigned long long) ath_self ());
214   }
215 #ifdef FRAME_NR
216   {
217     char spaces[] = "                                        ";
218     int nr_spaces = sizeof (spaces) - 1;
219     int nr_columns;
220
221     nr_columns = 2 * (frame_nr - 1);
222     if (nr_columns > nr_spaces)
223       nr_columns = nr_spaces;
224     if (nr_columns < 0)
225       nr_columns = 0;
226     spaces[nr_columns] = '\0';
227     fprintf (errfp, "%s", spaces);
228   }
229 #endif
230
231   vfprintf (errfp, format, arg_ptr);
232   va_end (arg_ptr);
233   if(format && *format && format[strlen (format) - 1] != '\n')
234     putc ('\n', errfp);
235   UNLOCK (debug_lock);
236   fflush (errfp);
237
238   gpg_err_set_errno (saved_errno);
239 }
240
241
242 /* Start a new debug line in *LINE, logged at level LEVEL or higher,
243    and starting with the formatted string FORMAT.  */
244 void
245 _gpgme_debug_begin (void **line, int level, const char *format, ...)
246 {
247   va_list arg_ptr;
248   int res;
249
250   if (debug_level < level)
251     {
252       /* Disable logging of this line.  */
253       *line = NULL;
254       return;
255     }
256
257   va_start (arg_ptr, format);
258   res = vasprintf ((char **) line, format, arg_ptr);
259   va_end (arg_ptr);
260   if (res < 0)
261     *line = NULL;
262 }
263
264
265 /* Add the formatted string FORMAT to the debug line *LINE.  */
266 void
267 _gpgme_debug_add (void **line, const char *format, ...)
268 {
269   va_list arg_ptr;
270   char *toadd;
271   char *result;
272   int res;
273
274   if (!*line)
275     return;
276
277   va_start (arg_ptr, format);
278   res = vasprintf (&toadd, format, arg_ptr);
279   va_end (arg_ptr);
280   if (res < 0)
281     {
282       free (*line);
283       *line = NULL;
284     }
285   res = asprintf (&result, "%s%s", *(char **) line, toadd);
286   free (toadd);
287   free (*line);
288   if (res < 0)
289     *line = NULL;
290   else
291     *line = result;
292 }
293
294
295 /* Finish construction of *LINE and send it to the debug output
296    stream.  */
297 void
298 _gpgme_debug_end (void **line)
299 {
300   if (!*line)
301     return;
302
303   /* The smallest possible level is 1, so force logging here by
304      using that.  */
305   _gpgme_debug (1, "%s", *line);
306   free (*line);
307   *line = NULL;
308 }
309
310
311 #define TOHEX(val) (((val) < 10) ? ((val) + '0') : ((val) - 10 + 'a'))
312
313 void
314 _gpgme_debug_buffer (int lvl, const char *const fmt,
315                      const char *const func, const char *const buffer,
316                      size_t len)
317 {
318   int idx = 0;
319   int j;
320
321   if (!_gpgme_debug_trace ())
322     return;
323
324   while (idx < len)
325     {
326       char str[51];
327       char *strp = str;
328       char *strp2 = &str[34];
329       
330       for (j = 0; j < 16; j++)
331         {
332           unsigned char val;
333           if (idx < len)
334             {
335               val = buffer[idx++];
336               *(strp++) = TOHEX (val >> 4);
337               *(strp++) = TOHEX (val % 16);
338               *(strp2++) = isprint (val) ? val : '.';
339             }
340           else
341             {
342               *(strp++) = ' ';
343               *(strp++) = ' ';
344             }
345           if (j == 7)
346             *(strp++) = ' ';
347         }
348       *(strp++) = ' ';
349       *(strp2) = '\0';
350
351       _gpgme_debug (lvl, fmt, func, str);
352     }
353 }