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