core: Remove old debug helper function
[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, see <https://gnu.org/licenses/>.
19  * SPDX-License-Identifier: LGPL-2.1-or-later
20  */
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 "sys-util.h"
50 #include "debug.h"
51
52 \f
53 /* Lock to serialize initialization of the debug output subsystem and
54    output of actual debug messages.  */
55 DEFINE_STATIC_LOCK (debug_lock);
56
57 /* The amount of detail requested by the user, per environment
58    variable GPGME_DEBUG.  */
59 static int debug_level;
60
61 /* The output stream for the debug messages.  */
62 static FILE *errfp;
63
64 /* If not NULL, this malloced string is used instead of the
65    GPGME_DEBUG envvar.  It must have been set before the debug
66    subsystem has been initialized.  Using it later may or may not have
67    any effect.  */
68 static char *envvar_override;
69
70 \f
71 #ifdef HAVE_TLS
72 #define FRAME_NR
73 static __thread int frame_nr = 0;
74 #endif
75
76 void
77 _gpgme_debug_frame_begin (void)
78 {
79 #ifdef FRAME_NR
80   frame_nr++;
81 #endif
82 }
83
84 int _gpgme_debug_frame_end (void)
85 {
86 #ifdef FRAME_NR
87   frame_nr--;
88 #endif
89   return 0;
90 }
91
92
93 \f
94 /* Remove leading and trailing white spaces.  */
95 static char *
96 trim_spaces (char *str)
97 {
98   char *string, *p, *mark;
99
100   string = str;
101   /* Find first non space character.  */
102   for (p = string; *p && isspace (*(unsigned char *) p); p++)
103     ;
104   /* Move characters.  */
105   for (mark = NULL; (*string = *p); string++, p++)
106     if (isspace (*(unsigned char *) p))
107       {
108         if (!mark)
109           mark = string;
110       }
111     else
112       mark = NULL;
113   if (mark)
114     *mark = '\0';       /* Remove trailing spaces.  */
115
116   return str;
117 }
118
119
120 /* This is an internal function to set debug info.  The caller must
121    assure that this function is called only by one thread at a time.
122    The function may have no effect if called after the debug system
123    has been initialized.  Returns 0 on success.  */
124 int
125 _gpgme_debug_set_debug_envvar (const char *value)
126 {
127   free (envvar_override);
128   envvar_override = strdup (value);
129   return !envvar_override;
130 }
131
132
133 static void
134 debug_init (void)
135 {
136   static int initialized;
137
138   LOCK (debug_lock);
139   if (!initialized)
140     {
141       gpgme_error_t err;
142       char *e;
143       const char *s1, *s2;;
144
145       if (envvar_override)
146         {
147           e = strdup (envvar_override);
148           free (envvar_override);
149           envvar_override = NULL;
150         }
151       else
152         {
153           err = _gpgme_getenv ("GPGME_DEBUG", &e);
154           if (err)
155             {
156               UNLOCK (debug_lock);
157               return;
158             }
159         }
160
161       initialized = 1;
162       errfp = stderr;
163       if (e)
164         {
165           debug_level = atoi (e);
166           s1 = strchr (e, PATHSEP_C);
167           if (s1)
168             {
169 #ifndef HAVE_DOSISH_SYSTEM
170               if (getuid () == geteuid ()
171 #if defined(HAVE_GETGID) && defined(HAVE_GETEGID)
172                   && getgid () == getegid ()
173 #endif
174                   )
175                 {
176 #endif
177                   char *p;
178                   FILE *fp;
179
180                   s1++;
181                   if (!(s2 = strchr (s1, PATHSEP_C)))
182                     s2 = s1 + strlen (s1);
183                   p = malloc (s2 - s1 + 1);
184                   if (p)
185                     {
186                       memcpy (p, s1, s2 - s1);
187                       p[s2-s1] = 0;
188                       trim_spaces (p);
189                       fp = fopen (p,"a");
190                       if (fp)
191                         {
192                           setvbuf (fp, NULL, _IOLBF, 0);
193                           errfp = fp;
194                         }
195                       free (p);
196                     }
197 #ifndef HAVE_DOSISH_SYSTEM
198                 }
199 #endif
200             }
201           free (e);
202         }
203     }
204   UNLOCK (debug_lock);
205
206   if (debug_level > 0)
207     {
208       _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL,
209                     "gpgme_debug: level=%d\n", debug_level);
210 #ifdef HAVE_W32_SYSTEM
211       {
212         const char *name = _gpgme_get_inst_dir ();
213         _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL,
214                       "gpgme_debug: gpgme='%s'\n", name? name: "?");
215       }
216 #endif
217     }
218 }
219
220
221
222 /* This should be called as soon as the locks are initialized.  It is
223    required so that the assuan logging gets conncted to the gpgme log
224    stream as early as possible.  */
225 void
226 _gpgme_debug_subsystem_init (void)
227 {
228   debug_init ();
229 }
230
231
232
233 \f
234 /* Log the formatted string FORMAT prefixed with additional info
235  * depending on MODE:
236  *
237  * -1 = Do not print any additional args.
238  *  0 = standalone (used by macro TRACE)
239  *  1 = enter a function (used by macro TRACE_BEG)
240  *  2 = debug a function (used by macro TRACE_LOG)
241  *  3 = leave a function (used by macro TRACE_SUC)
242  *
243  * Returns: 0
244  *
245  * Note that we always return 0 because the old TRACE macro evaluated
246  * to 0 which issues a warning with newer gcc version about an unused
247  * values.  By using a return value of this function this can be
248  * avoided.  Fixme: It might be useful to check whether the return
249  * value from the TRACE macros are actually used somewhere.
250  */
251 int
252 _gpgme_debug (int level, int mode, const char *func, const char *tagname,
253               const char *tagvalue, const char *format, ...)
254 {
255   va_list arg_ptr;
256   int saved_errno;
257
258   saved_errno = errno;
259   if (debug_level < level)
260     return 0;
261
262   va_start (arg_ptr, format);
263   LOCK (debug_lock);
264   {
265     struct tm *tp;
266     time_t atime = time (NULL);
267
268     tp = localtime (&atime);
269     fprintf (errfp, "GPGME %04d-%02d-%02d %02d:%02d:%02d <0x%04llx>  ",
270              1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
271              tp->tm_hour, tp->tm_min, tp->tm_sec,
272              (unsigned long long) ath_self ());
273   }
274 #ifdef FRAME_NR
275   {
276     int indent;
277
278     indent = frame_nr > 0? (2 * (frame_nr - 1)):0;
279     fprintf (errfp, "%*s", indent < 40? indent : 40, "");
280   }
281 #endif
282
283   switch (mode)
284     {
285     case -1:  /* Do nothing.  */
286       break;
287     case 0:
288       fprintf (errfp, "%s: call: %s=%p ", func, tagname, tagvalue);
289       break;
290     case 1:
291       fprintf (errfp, "%s: enter: %s=%p ", func, tagname, tagvalue);
292       break;
293     case 2:
294       fprintf (errfp, "%s: check: %s=%p ", func, tagname, tagvalue);
295       break;
296     case 3:
297       if (tagname)
298         fprintf (errfp, "%s: leave: %s=%p ", func, tagname, tagvalue);
299       else
300         fprintf (errfp, "%s: leave: ", func);
301       break;
302     default:
303       fprintf (errfp, "%s: m=%d: %s=%p ", func, mode, tagname, tagvalue);
304       break;
305     }
306
307   vfprintf (errfp, format, arg_ptr);
308   va_end (arg_ptr);
309   if(format && *format && format[strlen (format) - 1] != '\n')
310     putc ('\n', errfp);
311   UNLOCK (debug_lock);
312   fflush (errfp);
313
314   gpg_err_set_errno (saved_errno);
315   return 0;
316 }
317
318
319 /* Start a new debug line in *LINE, logged at level LEVEL or higher,
320    and starting with the formatted string FORMAT.  */
321 void
322 _gpgme_debug_begin (void **line, int level, const char *format, ...)
323 {
324   va_list arg_ptr;
325   int res;
326
327   if (debug_level < level)
328     {
329       /* Disable logging of this line.  */
330       *line = NULL;
331       return;
332     }
333
334   va_start (arg_ptr, format);
335   res = gpgrt_vasprintf ((char **) line, format, arg_ptr);
336   va_end (arg_ptr);
337   if (res < 0)
338     *line = NULL;
339 }
340
341
342 /* Add the formatted string FORMAT to the debug line *LINE.  */
343 void
344 _gpgme_debug_add (void **line, const char *format, ...)
345 {
346   va_list arg_ptr;
347   char *toadd;
348   char *result;
349   int res;
350
351   if (!*line)
352     return;
353
354   va_start (arg_ptr, format);
355   res = gpgrt_vasprintf (&toadd, format, arg_ptr);
356   va_end (arg_ptr);
357   if (res < 0)
358     {
359       gpgrt_free (*line);
360       *line = NULL;
361     }
362   res = gpgrt_asprintf (&result, "%s%s", *(char **) line, toadd);
363   gpgrt_free (toadd);
364   gpgrt_free (*line);
365   if (res < 0)
366     *line = NULL;
367   else
368     *line = result;
369 }
370
371
372 /* Finish construction of *LINE and send it to the debug output
373    stream.  */
374 void
375 _gpgme_debug_end (void **line)
376 {
377   if (!*line)
378     return;
379
380   /* The smallest possible level is 1, so force logging here by
381      using that.  */
382   _gpgme_debug (1, -1, NULL, NULL, NULL, "%s", *line);
383   gpgrt_free (*line);
384   *line = NULL;
385 }
386
387
388 #define TOHEX(val) (((val) < 10) ? ((val) + '0') : ((val) - 10 + 'a'))
389
390 void
391 _gpgme_debug_buffer (int lvl, const char *const fmt,
392                      const char *const func, const char *const buffer,
393                      size_t len)
394 {
395   int idx = 0;
396   int j;
397
398   if (!_gpgme_debug_trace ())
399     return;
400
401   if (!buffer)
402     return;
403
404   while (idx < len)
405     {
406       char str[51];
407       char *strp = str;
408       char *strp2 = &str[34];
409
410       for (j = 0; j < 16; j++)
411         {
412           unsigned char val;
413           if (idx < len)
414             {
415               val = buffer[idx++];
416               *(strp++) = TOHEX (val >> 4);
417               *(strp++) = TOHEX (val % 16);
418               *(strp2++) = isprint (val) ? val : '.';
419             }
420           else
421             {
422               *(strp++) = ' ';
423               *(strp++) = ' ';
424             }
425           if (j == 7)
426             *(strp++) = ' ';
427         }
428       *(strp++) = ' ';
429       *(strp2) = '\0';
430
431       _gpgme_debug (lvl, -1, NULL, NULL, NULL, fmt, func, str);
432     }
433 }