008-11-03 Marcus Brinkmann <marcus@g10code.com>
[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 (errfp);
149       assuan_set_assuan_log_level (debug_level >= 0? debug_level:0);
150 #endif /* HAVE_ASSUAN_H*/
151     }
152   UNLOCK (debug_lock);
153 }
154
155
156
157 /* This should be called as soon as the locks are intialized.  It is
158    required so that the assuan logging gets conncted to the gpgme log
159    stream as early as possible.  */
160 void
161 _gpgme_debug_subsystem_init (void)
162 {
163   debug_init ();
164 }
165
166
167
168 \f
169 /* Log the formatted string FORMAT at debug level LEVEL or higher.  */
170 void
171 _gpgme_debug (int level, const char *format, ...)
172 {
173   va_list arg_ptr;
174   int saved_errno;
175
176   saved_errno = errno;
177
178   debug_init ();
179   if (debug_level < level)
180     return;
181     
182   va_start (arg_ptr, format);
183   LOCK (debug_lock);
184   vfprintf (errfp, format, arg_ptr);
185   va_end (arg_ptr);
186   if(format && *format && format[strlen (format) - 1] != '\n')
187     putc ('\n', errfp);
188   UNLOCK (debug_lock);
189   fflush (errfp);
190
191   errno = saved_errno;
192 }
193
194
195 /* Start a new debug line in *LINE, logged at level LEVEL or higher,
196    and starting with the formatted string FORMAT.  */
197 void
198 _gpgme_debug_begin (void **line, int level, const char *format, ...)
199 {
200   va_list arg_ptr;
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   vasprintf ((char **) line, format, arg_ptr);
212   va_end (arg_ptr);
213 }
214
215
216 /* Add the formatted string FORMAT to the debug line *LINE.  */
217 void
218 _gpgme_debug_add (void **line, const char *format, ...)
219 {
220   va_list arg_ptr;
221   char *toadd;
222   char *result;
223
224   if (!*line)
225     return;
226
227   va_start (arg_ptr, format);
228   vasprintf (&toadd, format, arg_ptr);
229   va_end (arg_ptr);
230   asprintf (&result, "%s%s", *(char **) line, toadd);
231   free (*line);
232   free (toadd);
233   *line = result;
234 }
235
236
237 /* Finish construction of *LINE and send it to the debug output
238    stream.  */
239 void
240 _gpgme_debug_end (void **line)
241 {
242   if (!*line)
243     return;
244
245   /* The smallest possible level is 1, so force logging here by
246      using that.  */
247   _gpgme_debug (1, "%s", *line);
248   free (*line);
249   *line = NULL;
250 }
251
252
253 #define TOHEX(val) (((val) < 10) ? ((val) + '0') : ((val) - 10 + 'a'))
254
255 void
256 _gpgme_debug_buffer (int lvl, const char *const fmt,
257                      const char *const func, const char *const tagname,
258                      void *tag, const char *const buffer, size_t len)
259 {
260   int idx = 0;
261   int j;
262
263   if (!_gpgme_debug_trace ())
264     return;
265
266   while (idx < len)
267     {
268       char str[51];
269       char *strp = str;
270       char *strp2 = &str[34];
271       
272       for (j = 0; j < 16; j++)
273         {
274           unsigned char val;
275           if (idx < len)
276             {
277               val = buffer[idx++];
278               *(strp++) = TOHEX (val >> 4);
279               *(strp++) = TOHEX (val % 16);
280               *(strp2++) = isprint (val) ? val : '.';
281             }
282           else
283             {
284               *(strp++) = ' ';
285               *(strp++) = ' ';
286             }
287           if (j == 7)
288             *(strp++) = ' ';
289         }
290       *(strp++) = ' ';
291       *(strp2) = '\0';
292
293       _gpgme_debug (lvl, fmt, func, tagname, tag, str);
294     }
295 }