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