Line data Source code
1 : /* assuan-logging.c - Default logging function.
2 : Copyright (C) 2002, 2003, 2004, 2007, 2009,
3 : 2010 Free Software Foundation, Inc.
4 :
5 : This file is part of Assuan.
6 :
7 : Assuan 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 : Assuan 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 <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #ifdef HAVE_CONFIG_H
22 : #include <config.h>
23 : #endif
24 : #include <stdio.h>
25 : #include <stdlib.h>
26 : #include <string.h>
27 : #include <stdarg.h>
28 : #ifdef HAVE_W32_SYSTEM
29 : # ifdef HAVE_WINSOCK2_H
30 : # include <winsock2.h>
31 : # endif
32 : # include <windows.h>
33 : #endif /*HAVE_W32_SYSTEM*/
34 : #include <errno.h>
35 : #include <ctype.h>
36 :
37 : #include "assuan-defs.h"
38 :
39 :
40 : /* The default log handler is useful for global logging, but it should
41 : only be used by one user of libassuan at a time. Libraries that
42 : use libassuan can register their own log handler. */
43 :
44 : /* A common prefix for all log messages. */
45 : static char prefix_buffer[80];
46 :
47 : /* A global flag read from the environment to check if to enable full
48 : logging of buffer data. This is also used by custom log
49 : handlers. */
50 : static int full_logging;
51 :
52 : /* A bitfield that specifies the categories to log. */
53 : static int log_cats;
54 : #define TEST_LOG_CAT(x) (!! (log_cats & (1 << (x - 1))))
55 :
56 : static FILE *_assuan_log;
57 :
58 :
59 : void
60 1 : _assuan_init_log_envvars (void)
61 : {
62 : char *flagstr;
63 :
64 1 : full_logging = !!getenv ("ASSUAN_FULL_LOGGING");
65 1 : flagstr = getenv ("ASSUAN_DEBUG");
66 1 : if (flagstr)
67 0 : log_cats = atoi (flagstr);
68 : else /* Default to log the control channel. */
69 1 : log_cats = (1 << (ASSUAN_LOG_CONTROL - 1));
70 :
71 1 : _assuan_sysutils_blurb (); /* Make sure this code gets linked in. */
72 1 : }
73 :
74 :
75 : void
76 1 : assuan_set_assuan_log_stream (FILE *fp)
77 : {
78 1 : _assuan_log = fp;
79 :
80 1 : _assuan_init_log_envvars ();
81 1 : }
82 :
83 :
84 : /* Set the per context log stream. Also enable the default log stream
85 : if it has not been set. */
86 : void
87 1 : assuan_set_log_stream (assuan_context_t ctx, FILE *fp)
88 : {
89 1 : if (ctx)
90 : {
91 1 : if (ctx->log_fp)
92 0 : fflush (ctx->log_fp);
93 1 : ctx->log_fp = fp;
94 1 : if (! _assuan_log)
95 1 : assuan_set_assuan_log_stream (fp);
96 : }
97 1 : }
98 :
99 :
100 : /* Set the prefix to be used for logging to TEXT or resets it to the
101 : default if TEXT is NULL. */
102 : void
103 4 : assuan_set_assuan_log_prefix (const char *text)
104 : {
105 4 : if (text)
106 : {
107 4 : strncpy (prefix_buffer, text, sizeof (prefix_buffer)-1);
108 4 : prefix_buffer[sizeof (prefix_buffer)-1] = 0;
109 : }
110 : else
111 0 : *prefix_buffer = 0;
112 4 : }
113 :
114 :
115 : /* Get the prefix to be used for logging. */
116 : const char *
117 33 : assuan_get_assuan_log_prefix (void)
118 : {
119 33 : return prefix_buffer;
120 : }
121 :
122 :
123 : /* Default log handler. */
124 : int
125 166 : _assuan_log_handler (assuan_context_t ctx, void *hook, unsigned int cat,
126 : const char *msg)
127 : {
128 : FILE *fp;
129 : const char *prf;
130 166 : int saved_errno = errno;
131 :
132 : /* For now. */
133 166 : if (msg == NULL)
134 133 : return TEST_LOG_CAT (cat);
135 :
136 33 : if (! TEST_LOG_CAT (cat))
137 0 : return 0;
138 :
139 33 : fp = ctx->log_fp ? ctx->log_fp : _assuan_log;
140 33 : if (!fp)
141 0 : return 0;
142 :
143 33 : prf = assuan_get_assuan_log_prefix ();
144 33 : if (*prf)
145 33 : fprintf (fp, "%s[%u]: ", prf, (unsigned int)getpid ());
146 :
147 33 : fprintf (fp, "%s", msg);
148 : /* If the log stream is a file, the output would be buffered. This
149 : is bad for debugging, thus we flush the stream if FORMAT ends
150 : with a LF. */
151 33 : if (msg && *msg && msg[strlen (msg) - 1] == '\n')
152 33 : fflush (fp);
153 33 : gpg_err_set_errno (saved_errno);
154 :
155 33 : return 0;
156 : }
157 :
158 :
159 :
160 : /* Log a control channel message. This is either a STRING with a
161 : diagnostic or actual data in (BUFFER1,LENGTH1) and
162 : (BUFFER2,LENGTH2). If OUTBOUND is true the data is intended for
163 : the peer. */
164 : void
165 72 : _assuan_log_control_channel (assuan_context_t ctx, int outbound,
166 : const char *string,
167 : const void *buffer1, size_t length1,
168 : const void *buffer2, size_t length2)
169 : {
170 : int res;
171 : char *outbuf;
172 : int saved_errno;
173 :
174 : /* Check whether logging is enabled and do a quick check to see
175 : whether the callback supports our category. */
176 72 : if (!ctx
177 72 : || !ctx->log_cb
178 72 : || ctx->flags.no_logging
179 72 : || !(*ctx->log_cb) (ctx, ctx->log_cb_data, ASSUAN_LOG_CONTROL, NULL))
180 111 : return;
181 :
182 33 : saved_errno = errno;
183 :
184 : /* Note that we use the inbound channel fd as the printed channel
185 : number for both directions. */
186 : #ifdef HAVE_W32_SYSTEM
187 : # define CHANNEL_FMT "%p"
188 : #else
189 : # define CHANNEL_FMT "%d"
190 : #endif
191 : #define TOHEX(val) (((val) < 10) ? ((val) + '0') : ((val) - 10 + 'a'))
192 :
193 33 : if (!buffer1 && buffer2)
194 : {
195 14 : buffer1 = buffer2;
196 14 : length1 = length2;
197 14 : buffer2 = NULL;
198 14 : length2 = 0;
199 : }
200 :
201 33 : if (ctx->flags.confidential && !string && buffer1)
202 0 : string = "[Confidential data not shown]";
203 :
204 33 : if (string)
205 : {
206 : /* Print the diagnostic. */
207 0 : res = gpgrt_asprintf (&outbuf, "chan_" CHANNEL_FMT " %s [%s]\n",
208 : ctx->inbound.fd, outbound? "->":"<-", string);
209 : }
210 33 : else if (buffer1)
211 : {
212 : /* Print the control channel data. */
213 : const unsigned char *s;
214 : unsigned int n, x;
215 :
216 351 : for (n = length1, s = buffer1; n; n--, s++)
217 318 : if ((!isascii (*s) || iscntrl (*s) || !isprint (*s) || !*s)
218 0 : && !(*s >= 0x80))
219 0 : break;
220 33 : if (!n && buffer2)
221 : {
222 0 : for (n = length2, s = buffer2; n; n--, s++)
223 0 : if ((!isascii (*s) || iscntrl (*s) || !isprint (*s) || !*s)
224 0 : && !(*s >= 0x80))
225 0 : break;
226 : }
227 33 : if (!buffer2)
228 33 : length2 = 0;
229 :
230 33 : if (!n && (length1 && *(const char*)buffer1 != '['))
231 : {
232 : /* No control characters and not starting with our error
233 : message indicator. Log it verbatim. */
234 33 : res = gpgrt_asprintf (&outbuf, "chan_" CHANNEL_FMT " %s %.*s%.*s\n",
235 : ctx->inbound.fd, outbound? "->":"<-",
236 : (int)length1, (const char*)buffer1,
237 : (int)length2, buffer2? (const char*)buffer2:"");
238 : }
239 : else
240 : {
241 : /* The buffer contains control characters - do a hex dump.
242 : Even in full logging mode we limit the line length -
243 : however this is no real limit because the provided
244 : buffers will never be larger than the maximum assuan line
245 : length. */
246 : char *hp;
247 : unsigned int nbytes;
248 0 : unsigned int maxbytes = full_logging? (2*LINELENGTH) : 16;
249 :
250 0 : nbytes = length1 + length2;
251 0 : if (nbytes > maxbytes)
252 0 : nbytes = maxbytes;
253 :
254 0 : if (!(outbuf = malloc (50 + 3*nbytes + 60 + 3 + 1)))
255 0 : res = -1;
256 : else
257 : {
258 0 : res = 0;
259 0 : hp = outbuf;
260 0 : snprintf (hp, 50, "chan_" CHANNEL_FMT " %s [",
261 : ctx->inbound.fd, outbound? "->":"<-");
262 0 : hp += strlen (hp);
263 0 : n = 0;
264 0 : for (s = buffer1, x = 0; x < length1 && n < nbytes; x++, n++)
265 : {
266 0 : *hp++ = ' ';
267 0 : *hp++ = TOHEX (*s >> 4);
268 0 : *hp++ = TOHEX (*s & 0x0f);
269 0 : s++;
270 : }
271 0 : for (s = buffer2, x = 0; x < length2 && n < nbytes; x++, n++)
272 : {
273 0 : *hp++ = ' ';
274 0 : *hp++ = TOHEX (*s >> 4);
275 0 : *hp++ = TOHEX (*s & 0x0f);
276 0 : s++;
277 : }
278 0 : if (nbytes < length1 + length2)
279 : {
280 0 : snprintf (hp, 60, " ...(%u byte(s) skipped)",
281 : (unsigned int)((length1+length2) - nbytes));
282 0 : hp += strlen (hp);
283 : }
284 0 : strcpy (hp, " ]\n");
285 : }
286 : }
287 : }
288 : else
289 : {
290 0 : res = 0;
291 0 : outbuf = NULL;
292 : }
293 33 : if (res < 0)
294 0 : ctx->log_cb (ctx, ctx->log_cb_data, ASSUAN_LOG_CONTROL,
295 : "[libassuan failed to format the log message]");
296 33 : else if (outbuf)
297 : {
298 33 : ctx->log_cb (ctx, ctx->log_cb_data, ASSUAN_LOG_CONTROL, outbuf);
299 33 : free (outbuf);
300 : }
301 : #undef TOHEX
302 : #undef CHANNEL_FMT
303 33 : gpg_err_set_errno (saved_errno);
304 : }
|