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