printf-hook: Write to output stream instead of the FD directly when using Vstr
[strongswan.git] / src / libstrongswan / utils / printf_hook.c
1 /*
2 * Copyright (C) 2009-2013 Tobias Brunner
3 * Copyright (C) 2006-2008 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "printf_hook.h"
18
19 #include "utils.h"
20 #include "debug.h"
21
22 #include <stdio.h>
23 #include <stdarg.h>
24 #include <string.h>
25 #include <sys/uio.h>
26
27 typedef struct private_printf_hook_t private_printf_hook_t;
28 typedef struct printf_hook_handler_t printf_hook_handler_t;
29
30 #define PRINTF_BUF_LEN 8192
31 #define ARGS_MAX 3
32
33 /**
34 * private data of printf_hook
35 */
36 struct private_printf_hook_t {
37
38 /**
39 * public functions
40 */
41 printf_hook_t public;
42 };
43
44 /**
45 * struct with information about a registered handler
46 */
47 struct printf_hook_handler_t {
48
49 /**
50 * callback function
51 */
52 printf_hook_function_t hook;
53
54 /**
55 * number of arguments
56 */
57 int numargs;
58
59 /**
60 * types of the arguments
61 */
62 int argtypes[ARGS_MAX];
63
64 #ifdef USE_VSTR
65 /**
66 * name required for Vstr
67 */
68 char *name;
69 #endif
70 };
71
72 /* A-Z | 6 other chars | a-z */
73 #define NUM_HANDLERS 58
74 static printf_hook_handler_t *printf_hooks[NUM_HANDLERS];
75
76 #define SPEC_TO_INDEX(spec) ((int)(spec) - (int)'A')
77 #define IS_VALID_SPEC(spec) (SPEC_TO_INDEX(spec) > -1 && SPEC_TO_INDEX(spec) < NUM_HANDLERS)
78
79 #if !defined(USE_VSTR) && \
80 (defined(HAVE_PRINTF_FUNCTION) || defined(HAVE_PRINTF_SPECIFIER))
81
82 /**
83 * Printf hook print function. This is actually of type "printf_function",
84 * however glibc does it typedef to function, but uclibc to a pointer.
85 * So we redefine it here.
86 */
87 static int custom_print(FILE *stream, const struct printf_info *info,
88 const void *const *args)
89 {
90 printf_hook_spec_t spec;
91 printf_hook_handler_t *handler = printf_hooks[SPEC_TO_INDEX(info->spec)];
92 printf_hook_data_t data = {
93 .stream = stream,
94 };
95
96 spec.hash = info->alt;
97 spec.plus = info->showsign;
98 spec.minus = info->left;
99 spec.width = info->width;
100
101 return handler->hook(&data, &spec, args);
102 }
103
104 /**
105 * Printf hook arginfo function, which is actually of type
106 * "printf_arginfo_[size_]function".
107 */
108 static int custom_arginfo(const struct printf_info *info, size_t n, int *argtypes
109 #ifdef HAVE_PRINTF_SPECIFIER
110 , int *size
111 #endif
112 )
113 {
114 int i;
115 printf_hook_handler_t *handler = printf_hooks[SPEC_TO_INDEX(info->spec)];
116
117 if (handler->numargs <= n)
118 {
119 for (i = 0; i < handler->numargs; ++i)
120 {
121 argtypes[i] = handler->argtypes[i];
122 }
123 }
124 /* we never set "size", as we have no user defined types */
125 return handler->numargs;
126 }
127
128 #else
129
130 #include <errno.h>
131 #include <unistd.h> /* for STDOUT_FILENO */
132
133 /**
134 * These are used below, whenever the public wrapper functions are called before
135 * initialization or after destruction.
136 */
137 #undef vprintf
138 #undef vfprintf
139 #undef vsnprintf
140
141 /**
142 * Vstr custom format specifier callback function.
143 */
144 static int custom_fmt_cb(Vstr_base *base, size_t pos, Vstr_fmt_spec *fmt_spec)
145 {
146 int i;
147 const void *args[ARGS_MAX];
148 printf_hook_spec_t spec;
149 printf_hook_handler_t *handler = printf_hooks[SPEC_TO_INDEX(fmt_spec->name[0])];
150 printf_hook_data_t data = {
151 .base = base,
152 .pos = pos,
153 };
154
155 for (i = 0; i < handler->numargs; i++)
156 {
157 switch(handler->argtypes[i])
158 {
159 case PRINTF_HOOK_ARGTYPE_INT:
160 args[i] = VSTR_FMT_CB_ARG_PTR(fmt_spec, i);
161 break;
162 case PRINTF_HOOK_ARGTYPE_POINTER:
163 args[i] = &VSTR_FMT_CB_ARG_PTR(fmt_spec, i);
164 break;
165 }
166 }
167
168 spec.hash = fmt_spec->fmt_hash;
169 spec.plus = fmt_spec->fmt_plus;
170 spec.minus = fmt_spec->fmt_minus;
171 spec.width = fmt_spec->fmt_field_width;
172
173 handler->hook(&data, &spec, args);
174 return 1;
175 }
176
177 /**
178 * Add a custom format handler to the given Vstr_conf object
179 */
180 static void vstr_fmt_add_handler(Vstr_conf *conf, printf_hook_handler_t *handler)
181 {
182 int *at = handler->argtypes;
183 switch(handler->numargs)
184 {
185 case 1:
186 vstr_fmt_add(conf, handler->name, custom_fmt_cb, at[0],
187 VSTR_TYPE_FMT_END);
188 break;
189 case 2:
190 vstr_fmt_add(conf, handler->name, custom_fmt_cb, at[0],
191 at[1], VSTR_TYPE_FMT_END);
192 break;
193 case 3:
194 vstr_fmt_add(conf, handler->name, custom_fmt_cb, at[0],
195 at[1], at[2], VSTR_TYPE_FMT_END);
196 break;
197 }
198 }
199
200 /**
201 * Management of thread-specific Vstr_conf objects
202 */
203 #include <threading/thread_value.h>
204
205 static thread_value_t *vstr_conf = NULL;
206
207 static Vstr_conf *create_vstr_conf()
208 {
209 int i;
210 Vstr_conf *conf = vstr_make_conf();
211 vstr_cntl_conf(conf, VSTR_CNTL_CONF_SET_FMT_CHAR_ESC, '%');
212 vstr_cntl_conf(conf, VSTR_CNTL_CONF_SET_TYPE_GRPALLOC_CACHE,
213 VSTR_TYPE_CNTL_CONF_GRPALLOC_CSTR);
214 vstr_cntl_conf(conf, VSTR_CNTL_CONF_SET_NUM_BUF_SZ, PRINTF_BUF_LEN);
215 for (i = 0; i < NUM_HANDLERS; ++i)
216 {
217 printf_hook_handler_t *handler = printf_hooks[i];
218 if (handler)
219 {
220 vstr_fmt_add_handler(conf, handler);
221 }
222 }
223 return conf;
224 }
225
226 static inline Vstr_conf *get_vstr_conf()
227 {
228 Vstr_conf *conf = NULL;
229 if (vstr_conf)
230 {
231 conf = (Vstr_conf*)vstr_conf->get(vstr_conf);
232 if (!conf)
233 {
234 conf = create_vstr_conf();
235 vstr_conf->set(vstr_conf, conf);
236 }
237 }
238 return conf;
239 }
240
241 /**
242 * Described in header
243 */
244 size_t vstr_print_in_hook(struct Vstr_base *base, size_t pos, const char *fmt,
245 ...)
246 {
247 va_list args;
248 int written;
249
250 va_start(args, fmt);
251 written = vstr_add_vfmt(base, pos, fmt, args);
252 va_end(args);
253 return written;
254 }
255
256 /**
257 * Wrapper functions for printf and alike
258 */
259 int vstr_wrapper_printf(const char *format, ...)
260 {
261 int written;
262 va_list args;
263 va_start(args, format);
264 written = vstr_wrapper_vprintf(format, args);
265 va_end(args);
266 return written;
267 }
268 int vstr_wrapper_fprintf(FILE *stream, const char *format, ...)
269 {
270 int written;
271 va_list args;
272 va_start(args, format);
273 written = vstr_wrapper_vfprintf(stream, format, args);
274 va_end(args);
275 return written;
276 }
277 int vstr_wrapper_sprintf(char *str, const char *format, ...)
278 {
279 int written;
280 va_list args;
281 va_start(args, format);
282 written = vstr_wrapper_vsprintf(str, format, args);
283 va_end(args);
284 return written;
285 }
286 int vstr_wrapper_snprintf(char *str, size_t size, const char *format, ...)
287 {
288 int written;
289 va_list args;
290 va_start(args, format);
291 written = vstr_wrapper_vsnprintf(str, size, format, args);
292 va_end(args);
293 return written;
294 }
295 int vstr_wrapper_asprintf(char **str, const char *format, ...)
296 {
297 int written;
298 va_list args;
299 va_start(args, format);
300 written = vstr_wrapper_vasprintf(str, format, args);
301 va_end(args);
302 return written;
303 }
304 static inline int vstr_wrapper_vprintf_internal(Vstr_conf *conf, FILE *stream,
305 const char *format,
306 va_list args)
307 {
308 struct iovec *iov;
309 int iovcnt, written = 0;
310 Vstr_base *s = vstr_make_base(conf);
311 vstr_add_vfmt(s, 0, format, args);
312 if (vstr_export_iovec_ptr_all(s, &iov, &iovcnt))
313 {
314 while (iovcnt--)
315 {
316 if (iov->iov_base)
317 {
318 written += fwrite(iov->iov_base, 1, iov->iov_len, stream);
319 }
320 iov++;
321 }
322 }
323 vstr_free_base(s);
324 return written;
325 }
326 int vstr_wrapper_vprintf(const char *format, va_list args)
327 {
328 Vstr_conf *conf = get_vstr_conf();
329 if (conf)
330 {
331 return vstr_wrapper_vprintf_internal(conf, stdout, format, args);
332 }
333 return vprintf(format, args);
334 }
335 int vstr_wrapper_vfprintf(FILE *stream, const char *format, va_list args)
336 {
337 Vstr_conf *conf = get_vstr_conf();
338 if (conf)
339 {
340 return vstr_wrapper_vprintf_internal(conf, stream, format, args);
341 }
342 return vfprintf(stream, format, args);
343 }
344 static inline int vstr_wrapper_vsnprintf_internal(char *str, size_t size,
345 const char *format,
346 va_list args)
347 {
348 Vstr_conf *conf = get_vstr_conf();
349 if (conf)
350 {
351 int written;
352 Vstr_base *s = vstr_make_base(conf);
353 vstr_add_vfmt(s, 0, format, args);
354 written = s->len;
355 vstr_export_cstr_buf(s, 1, s->len, str, (size > 0) ? size : s->len + 1);
356 vstr_free_base(s);
357 return written;
358 }
359 return vsnprintf(str, size, format, args);
360 }
361 int vstr_wrapper_vsprintf(char *str, const char *format, va_list args)
362 {
363 return vstr_wrapper_vsnprintf_internal(str, 0, format, args);
364 }
365 int vstr_wrapper_vsnprintf(char *str, size_t size, const char *format,
366 va_list args)
367 {
368 return (size > 0) ? vstr_wrapper_vsnprintf_internal(str, size, format, args) : 0;
369 }
370 int vstr_wrapper_vasprintf(char **str, const char *format, va_list args)
371 {
372 size_t len = 100;
373 int written;
374 *str = malloc(len);
375 while (TRUE)
376 {
377 va_list ac;
378 va_copy(ac, args);
379 written = vstr_wrapper_vsnprintf_internal(*str, len, format, ac);
380 va_end(ac);
381 if (written < len)
382 {
383 break;
384 }
385 len = written + 1;
386 *str = realloc(*str, len);
387 }
388 return written;
389 }
390 #endif
391
392 METHOD(printf_hook_t, add_handler, void,
393 private_printf_hook_t *this, char spec,
394 printf_hook_function_t hook, ...)
395 {
396 int i = -1;
397 printf_hook_handler_t *handler;
398 printf_hook_argtype_t argtype;
399 va_list args;
400
401 if (!IS_VALID_SPEC(spec))
402 {
403 DBG1(DBG_LIB, "'%c' is not a valid printf hook specifier, "
404 "not registered!", spec);
405 return;
406 }
407
408 handler = malloc_thing(printf_hook_handler_t);
409 handler->hook = hook;
410
411 va_start(args, hook);
412 while ((argtype = va_arg(args, printf_hook_argtype_t)) != PRINTF_HOOK_ARGTYPE_END)
413 {
414 if (++i >= ARGS_MAX)
415 {
416 DBG1(DBG_LIB, "Too many arguments for printf hook with "
417 "specifier '%c', not registered!", spec);
418 va_end(args);
419 free(handler);
420 return;
421 }
422 handler->argtypes[i] = argtype;
423 }
424 va_end(args);
425
426 handler->numargs = i + 1;
427
428 if (handler->numargs > 0)
429 {
430 #if !defined(USE_VSTR) && \
431 (defined(HAVE_PRINTF_FUNCTION) || defined(HAVE_PRINTF_SPECIFIER))
432 # ifdef HAVE_PRINTF_SPECIFIER
433 register_printf_specifier(spec, custom_print, custom_arginfo);
434 # else
435 register_printf_function(spec, custom_print, custom_arginfo);
436 # endif
437 #else
438 Vstr_conf *conf = get_vstr_conf();
439 handler->name = malloc(2);
440 handler->name[0] = spec;
441 handler->name[1] = '\0';
442 vstr_fmt_add_handler(conf, handler);
443 #endif
444 printf_hooks[SPEC_TO_INDEX(spec)] = handler;
445 }
446 else
447 {
448 free(handler);
449 }
450 }
451
452 METHOD(printf_hook_t, destroy, void,
453 private_printf_hook_t *this)
454 {
455 int i;
456 #ifdef USE_VSTR
457 Vstr_conf *conf = get_vstr_conf();
458 #endif
459
460 for (i = 0; i < NUM_HANDLERS; ++i)
461 {
462 printf_hook_handler_t *handler = printf_hooks[i];
463 if (handler)
464 {
465 #ifdef USE_VSTR
466 vstr_fmt_del(conf, handler->name);
467 free(handler->name);
468 #endif
469 free(handler);
470 }
471 }
472
473 #ifdef USE_VSTR
474 /* freeing the Vstr_conf of the main thread */
475 vstr_conf->destroy(vstr_conf);
476 vstr_conf = NULL;
477 vstr_exit();
478 #endif
479 free(this);
480 }
481
482 /*
483 * see header file
484 */
485 printf_hook_t *printf_hook_create()
486 {
487 private_printf_hook_t *this;
488
489 INIT(this,
490 .public = {
491 .add_handler = _add_handler,
492 .destroy = _destroy,
493 },
494 );
495
496 memset(printf_hooks, 0, sizeof(printf_hooks));
497
498 #ifdef USE_VSTR
499 if (!vstr_init())
500 {
501 DBG1(DBG_LIB, "failed to initialize Vstr library!");
502 free(this);
503 return NULL;
504 }
505 vstr_conf = thread_value_create((thread_cleanup_t)vstr_free_conf);
506 #endif
507
508 return &this->public;
509 }
510