More include guards.
[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     struct tm *tp;
213     time_t atime = time (NULL);
214     
215     tp = localtime (&atime);
216     fprintf (errfp, "GPGME %04d-%02d-%02d %02d:%02d:%02d <0x%04llx>  ",
217              1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
218              tp->tm_hour, tp->tm_min, tp->tm_sec,
219              (unsigned long long) ath_self ());
220   }
221 #ifdef FRAME_NR
222   {
223     char spaces[] = "                                        ";
224     int nr_spaces = sizeof (spaces) - 1;
225     int nr_columns;
226
227     nr_columns = 2 * (frame_nr - 1);
228     if (nr_columns > nr_spaces)
229       nr_columns = nr_spaces;
230     if (nr_columns < 0)
231       nr_columns = 0;
232     spaces[nr_columns] = '\0';
233     fprintf (errfp, "%s", spaces);
234   }
235 #endif
236
237   vfprintf (errfp, format, arg_ptr);
238   va_end (arg_ptr);
239   if(format && *format && format[strlen (format) - 1] != '\n')
240     putc ('\n', errfp);
241   UNLOCK (debug_lock);
242   fflush (errfp);
243
244   gpg_err_set_errno (saved_errno);
245 }
246
247
248 /* Start a new debug line in *LINE, logged at level LEVEL or higher,
249    and starting with the formatted string FORMAT.  */
250 void
251 _gpgme_debug_begin (void **line, int level, const char *format, ...)
252 {
253   va_list arg_ptr;
254   int res;
255
256   if (debug_level < level)
257     {
258       /* Disable logging of this line.  */
259       *line = NULL;
260       return;
261     }
262
263   va_start (arg_ptr, format);
264   res = vasprintf ((char **) line, format, arg_ptr);
265   va_end (arg_ptr);
266   if (res < 0)
267     *line = NULL;
268 }
269
270
271 /* Add the formatted string FORMAT to the debug line *LINE.  */
272 void
273 _gpgme_debug_add (void **line, const char *format, ...)
274 {
275   va_list arg_ptr;
276   char *toadd;
277   char *result;
278   int res;
279
280   if (!*line)
281     return;
282
283   va_start (arg_ptr, format);
284   res = vasprintf (&toadd, format, arg_ptr);
285   va_end (arg_ptr);
286   if (res < 0)
287     {
288       free (*line);
289       *line = NULL;
290     }
291   res = asprintf (&result, "%s%s", *(char **) line, toadd);
292   free (toadd);
293   free (*line);
294   if (res < 0)
295     *line = NULL;
296   else
297     *line = result;
298 }
299
300
301 /* Finish construction of *LINE and send it to the debug output
302    stream.  */
303 void
304 _gpgme_debug_end (void **line)
305 {
306   if (!*line)
307     return;
308
309   /* The smallest possible level is 1, so force logging here by
310      using that.  */
311   _gpgme_debug (1, "%s", *line);
312   free (*line);
313   *line = NULL;
314 }
315
316
317 #define TOHEX(val) (((val) < 10) ? ((val) + '0') : ((val) - 10 + 'a'))
318
319 void
320 _gpgme_debug_buffer (int lvl, const char *const fmt,
321                      const char *const func, const char *const buffer,
322                      size_t len)
323 {
324   int idx = 0;
325   int j;
326
327   if (!_gpgme_debug_trace ())
328     return;
329
330   while (idx < len)
331     {
332       char str[51];
333       char *strp = str;
334       char *strp2 = &str[34];
335       
336       for (j = 0; j < 16; j++)
337         {
338           unsigned char val;
339           if (idx < len)
340             {
341               val = buffer[idx++];
342               *(strp++) = TOHEX (val >> 4);
343               *(strp++) = TOHEX (val % 16);
344               *(strp2++) = isprint (val) ? val : '.';
345             }
346           else
347             {
348               *(strp++) = ' ';
349               *(strp++) = ' ';
350             }
351           if (j == 7)
352             *(strp++) = ' ';
353         }
354       *(strp++) = ' ';
355       *(strp2) = '\0';
356
357       _gpgme_debug (lvl, fmt, func, str);
358     }
359 }