2009-06-22 Marcus Brinkmann <marcus@g10code.de>
[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 #ifndef HAVE_DOSISH_SYSTEM
33 #  include <sys/types.h>
34 #  include <sys/stat.h>
35 #  include <fcntl.h>
36 #endif
37 #include <assert.h>
38
39 #ifdef HAVE_ASSUAN_H
40 #include "assuan.h"
41 #endif
42
43 #include "util.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 /* Remove leading and trailing white spaces.  */
61 static char *
62 trim_spaces (char *str)
63 {
64   char *string, *p, *mark;
65
66   string = str;
67   /* Find first non space character.  */
68   for (p = string; *p && isspace (*(unsigned char *) p); p++)
69     ;
70   /* Move characters.  */
71   for (mark = NULL; (*string = *p); string++, p++)
72     if (isspace (*(unsigned char *) p))
73       {
74         if (!mark)
75           mark = string;
76       }
77     else
78       mark = NULL;
79   if (mark)
80     *mark = '\0';       /* Remove trailing spaces.  */
81
82   return str;
83 }
84
85
86 static void
87 debug_init (void)
88 {
89   static int initialized;
90
91   LOCK (debug_lock);
92   if (!initialized)
93     {
94       gpgme_error_t err;
95       char *e;
96       const char *s1, *s2;;
97
98       err = _gpgme_getenv ("GPGME_DEBUG", &e);
99       if (err)
100         {
101           UNLOCK (debug_lock);
102           return;
103         }
104
105       initialized = 1;
106       errfp = stderr;
107       if (e)
108         {
109           debug_level = atoi (e);
110           s1 = strchr (e, PATHSEP_C);
111           if (s1)
112             {
113 #ifndef HAVE_DOSISH_SYSTEM
114               if (getuid () == geteuid ())
115                 {
116 #endif
117                   char *p;
118                   FILE *fp;
119
120                   s1++;
121                   if (!(s2 = strchr (s1, PATHSEP_C)))
122                     s2 = s1 + strlen (s1);
123                   p = malloc (s2 - s1 + 1);
124                   if (p)
125                     {
126                       memcpy (p, s1, s2 - s1);
127                       p[s2-s1] = 0;
128                       trim_spaces (p);
129                       fp = fopen (p,"a");
130                       if (fp)
131                         {
132                           setvbuf (fp, NULL, _IOLBF, 0);
133                           errfp = fp;
134                         }
135                       free (p);
136                     }
137 #ifndef HAVE_DOSISH_SYSTEM
138                 }
139 #endif
140             }
141           free (e);
142         }
143
144       if (debug_level > 0)
145         fprintf (errfp, "gpgme_debug: level=%d\n", debug_level);
146 #ifdef HAVE_ASSUAN_H
147       assuan_set_assuan_log_prefix ("gpgme-assuan");
148       assuan_set_assuan_log_stream (debug_level > 0 ? errfp : NULL);
149 #endif /* HAVE_ASSUAN_H*/
150     }
151   UNLOCK (debug_lock);
152 }
153
154
155
156 /* This should be called as soon as the locks are intialized.  It is
157    required so that the assuan logging gets conncted to the gpgme log
158    stream as early as possible.  */
159 void
160 _gpgme_debug_subsystem_init (void)
161 {
162   debug_init ();
163 }
164
165
166
167 \f
168 /* Log the formatted string FORMAT at debug level LEVEL or higher.  */
169 void
170 _gpgme_debug (int level, const char *format, ...)
171 {
172   va_list arg_ptr;
173   int saved_errno;
174
175   saved_errno = errno;
176
177   debug_init ();
178   if (debug_level < level)
179     return;
180     
181   va_start (arg_ptr, format);
182   LOCK (debug_lock);
183   vfprintf (errfp, format, arg_ptr);
184   va_end (arg_ptr);
185   if(format && *format && format[strlen (format) - 1] != '\n')
186     putc ('\n', errfp);
187   UNLOCK (debug_lock);
188   fflush (errfp);
189
190   errno = saved_errno;
191 }
192
193
194 /* Start a new debug line in *LINE, logged at level LEVEL or higher,
195    and starting with the formatted string FORMAT.  */
196 void
197 _gpgme_debug_begin (void **line, int level, const char *format, ...)
198 {
199   va_list arg_ptr;
200   int res;
201
202   debug_init ();
203   if (debug_level < level)
204     {
205       /* Disable logging of this line.  */
206       *line = NULL;
207       return;
208     }
209
210   va_start (arg_ptr, format);
211   res = vasprintf ((char **) line, format, arg_ptr);
212   va_end (arg_ptr);
213   if (res < 0)
214     *line = NULL;
215 }
216
217
218 /* Add the formatted string FORMAT to the debug line *LINE.  */
219 void
220 _gpgme_debug_add (void **line, const char *format, ...)
221 {
222   va_list arg_ptr;
223   char *toadd;
224   char *result;
225   int res;
226
227   if (!*line)
228     return;
229
230   va_start (arg_ptr, format);
231   res = vasprintf (&toadd, format, arg_ptr);
232   va_end (arg_ptr);
233   if (res < 0)
234     {
235       free (*line);
236       *line = NULL;
237     }
238   res = asprintf (&result, "%s%s", *(char **) line, toadd);
239   free (toadd);
240   free (*line);
241   if (res < 0)
242     *line = NULL;
243   else
244     *line = result;
245 }
246
247
248 /* Finish construction of *LINE and send it to the debug output
249    stream.  */
250 void
251 _gpgme_debug_end (void **line)
252 {
253   if (!*line)
254     return;
255
256   /* The smallest possible level is 1, so force logging here by
257      using that.  */
258   _gpgme_debug (1, "%s", *line);
259   free (*line);
260   *line = NULL;
261 }
262
263
264 #define TOHEX(val) (((val) < 10) ? ((val) + '0') : ((val) - 10 + 'a'))
265
266 void
267 _gpgme_debug_buffer (int lvl, const char *const fmt,
268                      const char *const func, const char *const tagname,
269                      void *tag, const char *const buffer, size_t len)
270 {
271   int idx = 0;
272   int j;
273
274   if (!_gpgme_debug_trace ())
275     return;
276
277   while (idx < len)
278     {
279       char str[51];
280       char *strp = str;
281       char *strp2 = &str[34];
282       
283       for (j = 0; j < 16; j++)
284         {
285           unsigned char val;
286           if (idx < len)
287             {
288               val = buffer[idx++];
289               *(strp++) = TOHEX (val >> 4);
290               *(strp++) = TOHEX (val % 16);
291               *(strp2++) = isprint (val) ? val : '.';
292             }
293           else
294             {
295               *(strp++) = ' ';
296               *(strp++) = ' ';
297             }
298           if (j == 7)
299             *(strp++) = ' ';
300         }
301       *(strp++) = ' ';
302       *(strp2) = '\0';
303
304       _gpgme_debug (lvl, fmt, func, tagname, tag, str);
305     }
306 }