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