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