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