2009-06-18 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 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
201   debug_init ();
202   if (debug_level < level)
203     {
204       /* Disable logging of this line.  */
205       *line = NULL;
206       return;
207     }
208
209   va_start (arg_ptr, format);
210   vasprintf ((char **) line, format, arg_ptr);
211   va_end (arg_ptr);
212 }
213
214
215 /* Add the formatted string FORMAT to the debug line *LINE.  */
216 void
217 _gpgme_debug_add (void **line, const char *format, ...)
218 {
219   va_list arg_ptr;
220   char *toadd;
221   char *result;
222
223   if (!*line)
224     return;
225
226   va_start (arg_ptr, format);
227   vasprintf (&toadd, format, arg_ptr);
228   va_end (arg_ptr);
229   asprintf (&result, "%s%s", *(char **) line, toadd);
230   free (*line);
231   free (toadd);
232   *line = result;
233 }
234
235
236 /* Finish construction of *LINE and send it to the debug output
237    stream.  */
238 void
239 _gpgme_debug_end (void **line)
240 {
241   if (!*line)
242     return;
243
244   /* The smallest possible level is 1, so force logging here by
245      using that.  */
246   _gpgme_debug (1, "%s", *line);
247   free (*line);
248   *line = NULL;
249 }
250
251
252 #define TOHEX(val) (((val) < 10) ? ((val) + '0') : ((val) - 10 + 'a'))
253
254 void
255 _gpgme_debug_buffer (int lvl, const char *const fmt,
256                      const char *const func, const char *const tagname,
257                      void *tag, const char *const buffer, size_t len)
258 {
259   int idx = 0;
260   int j;
261
262   if (!_gpgme_debug_trace ())
263     return;
264
265   while (idx < len)
266     {
267       char str[51];
268       char *strp = str;
269       char *strp2 = &str[34];
270       
271       for (j = 0; j < 16; j++)
272         {
273           unsigned char val;
274           if (idx < len)
275             {
276               val = buffer[idx++];
277               *(strp++) = TOHEX (val >> 4);
278               *(strp++) = TOHEX (val % 16);
279               *(strp2++) = isprint (val) ? val : '.';
280             }
281           else
282             {
283               *(strp++) = ' ';
284               *(strp++) = ' ';
285             }
286           if (j == 7)
287             *(strp++) = ' ';
288         }
289       *(strp++) = ' ';
290       *(strp2) = '\0';
291
292       _gpgme_debug (lvl, fmt, func, tagname, tag, str);
293     }
294 }