added a configure option to enable the Vstr string library even if register_printf_fu...
[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(HAVE_PRINTF_HOOKS) && !defined(USE_VSTR)
79
80 /**
81 * Printf hook print function. This is actually of type "printf_function",
82 * however glibc does it typedef to function, but uclibc to a pointer.
83 * So we redefine it here.
84 */
85 static int custom_print(FILE *stream, const struct printf_info *info,
86 const void *const *args)
87 {
88 int written;
89 char buf[PRINTF_BUF_LEN];
90 printf_hook_spec_t spec;
91 printf_hook_handler_t *handler = printf_hooks[SPEC_TO_INDEX(info->spec)];
92
93 spec.hash = info->alt;
94 spec.minus = info->left;
95 spec.width = info->width;
96
97 written = handler->hook(buf, sizeof(buf), &spec, args);
98 if (written > 0)
99 {
100 ignore_result(fwrite(buf, 1, written, stream));
101 }
102 return written;
103 }
104
105 /**
106 * Printf hook arginfo function, which is actually of type
107 * "printf_arginfo_function".
108 */
109 static int custom_arginfo(const struct printf_info *info, size_t n, int *argtypes)
110 {
111 int i;
112 printf_hook_handler_t *handler = printf_hooks[SPEC_TO_INDEX(info->spec)];
113
114 if (handler->numargs <= n)
115 {
116 for (i = 0; i < handler->numargs; ++i)
117 {
118 argtypes[i] = handler->argtypes[i];
119 }
120 }
121 return handler->numargs;
122 }
123
124 #else
125
126 #include <errno.h>
127 #include <unistd.h> /* for STDOUT_FILENO */
128
129 /**
130 * Vstr custom format specifier callback function.
131 */
132 static int custom_fmt_cb(Vstr_base *base, size_t pos, Vstr_fmt_spec *fmt_spec)
133 {
134 int i, written;
135 char buf[PRINTF_BUF_LEN];
136 const void *args[ARGS_MAX];
137 printf_hook_spec_t spec;
138 printf_hook_handler_t *handler = printf_hooks[SPEC_TO_INDEX(fmt_spec->name[0])];
139
140 for (i = 0; i < handler->numargs; i++)
141 {
142 switch(handler->argtypes[i])
143 {
144 case PRINTF_HOOK_ARGTYPE_INT:
145 args[i] = VSTR_FMT_CB_ARG_PTR(fmt_spec, i);
146 break;
147 case PRINTF_HOOK_ARGTYPE_POINTER:
148 args[i] = &VSTR_FMT_CB_ARG_PTR(fmt_spec, i);
149 break;
150 }
151 }
152
153 spec.hash = fmt_spec->fmt_hash;
154 spec.minus = fmt_spec->fmt_minus;
155 spec.width = fmt_spec->fmt_field_width;
156
157 written = handler->hook(buf, sizeof(buf), &spec, args);
158 if (written > 0)
159 {
160 vstr_add_buf(base, pos, buf, written);
161 }
162 return TRUE;
163 }
164
165 /**
166 * Add a custom format handler to the given Vstr_conf object
167 */
168 static void vstr_fmt_add_handler(Vstr_conf *conf, printf_hook_handler_t *handler)
169 {
170 int *at = handler->argtypes;
171 switch(handler->numargs)
172 {
173 case 1:
174 vstr_fmt_add(conf, handler->name, custom_fmt_cb, at[0], VSTR_TYPE_FMT_END);
175 break;
176 case 2:
177 vstr_fmt_add(conf, handler->name, custom_fmt_cb, at[0], at[1], VSTR_TYPE_FMT_END);
178 break;
179 case 3:
180 vstr_fmt_add(conf, handler->name, custom_fmt_cb, at[0], at[1], at[2], VSTR_TYPE_FMT_END);
181 break;
182 }
183 }
184
185 /**
186 * Management of thread-specific Vstr_conf objects
187 */
188 #include <pthread.h>
189
190 static pthread_key_t vstr_conf_key;
191 static pthread_once_t vstr_conf_key_once = PTHREAD_ONCE_INIT;
192
193 static void init_vstr_conf_key(void)
194 {
195 pthread_key_create(&vstr_conf_key, (void*)vstr_free_conf);
196 }
197
198 static Vstr_conf *create_vstr_conf()
199 {
200 int i;
201 Vstr_conf *conf = vstr_make_conf();
202 vstr_cntl_conf(conf, VSTR_CNTL_CONF_SET_FMT_CHAR_ESC, '%');
203 vstr_cntl_conf(conf, VSTR_CNTL_CONF_SET_TYPE_GRPALLOC_CACHE,
204 VSTR_TYPE_CNTL_CONF_GRPALLOC_CSTR);
205 vstr_cntl_conf(conf, VSTR_CNTL_CONF_SET_NUM_BUF_SZ, PRINTF_BUF_LEN);
206 for (i = 0; i < NUM_HANDLERS; ++i)
207 {
208 printf_hook_handler_t *handler = printf_hooks[i];
209 if (handler)
210 {
211 vstr_fmt_add_handler(conf, handler);
212 }
213 }
214 return conf;
215 }
216
217 static inline Vstr_conf *get_vstr_conf()
218 {
219 Vstr_conf *conf;
220 pthread_once(&vstr_conf_key_once, init_vstr_conf_key);
221 conf = (Vstr_conf*)pthread_getspecific(vstr_conf_key);
222 if (!conf)
223 {
224 conf = create_vstr_conf();
225 pthread_setspecific(vstr_conf_key, conf);
226 }
227 return conf;
228 }
229
230 /**
231 * Wrapper functions for printf and alike
232 */
233 int vstr_wrapper_printf(const char *format, ...)
234 {
235 int written;
236 va_list args;
237 va_start(args, format);
238 written = vstr_wrapper_vprintf(format, args);
239 va_end(args);
240 return written;
241 }
242 int vstr_wrapper_fprintf(FILE *stream, const char *format, ...)
243 {
244 int written;
245 va_list args;
246 va_start(args, format);
247 written = vstr_wrapper_vfprintf(stream, format, args);
248 va_end(args);
249 return written;
250 }
251 int vstr_wrapper_sprintf(char *str, const char *format, ...)
252 {
253 int written;
254 va_list args;
255 va_start(args, format);
256 written = vstr_wrapper_vsprintf(str, format, args);
257 va_end(args);
258 return written;
259 }
260 int vstr_wrapper_snprintf(char *str, size_t size, const char *format, ...)
261 {
262 int written;
263 va_list args;
264 va_start(args, format);
265 written = vstr_wrapper_vsnprintf(str, size, format, args);
266 va_end(args);
267 return written;
268 }
269 static inline int vstr_wrapper_vprintf_internal(int fd, const char *format,
270 va_list args)
271 {
272 int written;
273 Vstr_conf *conf = get_vstr_conf();
274 Vstr_base *s = vstr_make_base(conf);
275 vstr_add_vfmt(s, 0, format, args);
276 written = s->len;
277 while (s->len)
278 {
279 if (!vstr_sc_write_fd(s, 1, s->len, fd, NULL))
280 {
281 if (errno != EAGAIN && errno != EINTR)
282 {
283 written -= s->len;
284 break;
285 }
286 }
287 }
288 vstr_free_base(s);
289 return written;
290 }
291 int vstr_wrapper_vprintf(const char *format, va_list args)
292 {
293 return vstr_wrapper_vprintf_internal(STDOUT_FILENO, format, args);
294 }
295 int vstr_wrapper_vfprintf(FILE *stream, const char *format, va_list args)
296 {
297 return vstr_wrapper_vprintf_internal(fileno(stream), format, args);
298 }
299 static inline int vstr_wrapper_vsnprintf_internal(char *str, size_t size,
300 const char *format,
301 va_list args)
302 {
303 int written;
304 Vstr_conf *conf = get_vstr_conf();
305 Vstr_base *s = vstr_make_base(conf);
306 vstr_add_vfmt(s, 0, format, args);
307 written = s->len;
308 vstr_export_cstr_buf(s, 1, s->len, str, (size > 0) ? size : s->len + 1);
309 vstr_free_base(s);
310 return written;
311 }
312 int vstr_wrapper_vsprintf(char *str, const char *format, va_list args)
313 {
314 return vstr_wrapper_vsnprintf_internal(str, 0, format, args);
315 }
316 int vstr_wrapper_vsnprintf(char *str, size_t size, const char *format,
317 va_list args)
318 {
319 return (size > 0) ? vstr_wrapper_vsnprintf_internal(str, size, format, args) : 0;
320 }
321
322 #endif
323
324 /**
325 * Implementation of printf_hook_t.add_handler.
326 */
327 static void add_handler(private_printf_hook_t *this, char spec,
328 printf_hook_function_t hook, ...)
329 {
330 int i = -1;
331 printf_hook_handler_t *handler;
332 printf_hook_argtype_t argtype;
333 va_list args;
334
335 if (!IS_VALID_SPEC(spec))
336 {
337 DBG1("'%c' is not a valid printf hook specifier, not registered!", spec);
338 return;
339 }
340
341 handler = malloc_thing(printf_hook_handler_t);
342 handler->hook = hook;
343
344 va_start(args, hook);
345 while ((argtype = va_arg(args, printf_hook_argtype_t)) != PRINTF_HOOK_ARGTYPE_END)
346 {
347 if (++i >= ARGS_MAX)
348 {
349 DBG1("Too many arguments for printf hook with specifier '%c', not registered!", spec);
350 va_end(args);
351 free(handler);
352 return;
353 }
354 handler->argtypes[i] = argtype;
355 }
356 va_end(args);
357
358 handler->numargs = i + 1;
359
360 if (handler->numargs > 0)
361 {
362 #if defined(HAVE_PRINTF_HOOKS) && !defined(USE_VSTR)
363 register_printf_function(spec, custom_print, custom_arginfo);
364 #else
365 Vstr_conf *conf = get_vstr_conf();
366 handler->name = malloc(2);
367 handler->name[0] = spec;
368 handler->name[1] = '\0';
369 vstr_fmt_add_handler(conf, handler);
370 #endif
371 printf_hooks[SPEC_TO_INDEX(spec)] = handler;
372 }
373 else
374 {
375 free(handler);
376 }
377 }
378
379 /**
380 * Implementation of printf_hook_t.destroy
381 */
382 static void destroy(private_printf_hook_t *this)
383 {
384 int i;
385 #ifdef USE_VSTR
386 Vstr_conf *conf = get_vstr_conf();
387 #endif
388
389 for (i = 0; i < NUM_HANDLERS; ++i)
390 {
391 printf_hook_handler_t *handler = printf_hooks[i];
392 if (handler)
393 {
394 #ifdef USE_VSTR
395 vstr_fmt_del(conf, handler->name);
396 free(handler->name);
397 #endif
398 free(handler);
399 }
400 }
401
402 #ifdef USE_VSTR
403 /* freeing the Vstr_conf of the main thread */
404 pthread_key_delete(vstr_conf_key);
405 vstr_free_conf(conf);
406 vstr_exit();
407 #endif
408 free(this);
409 }
410
411 /*
412 * see header file
413 */
414 printf_hook_t *printf_hook_create()
415 {
416 private_printf_hook_t *this = malloc_thing(private_printf_hook_t);
417
418 this->public.add_handler = (void(*)(printf_hook_t*, char, printf_hook_function_t, ...))add_handler;
419 this->public.destroy = (void(*)(printf_hook_t*))destroy;
420
421 memset(printf_hooks, 0, sizeof(printf_hooks));
422
423 #ifdef USE_VSTR
424 if (!vstr_init())
425 {
426 DBG1("failed to initialize Vstr library!");
427 free(this);
428 return NULL;
429 }
430 #endif
431
432 return &this->public;
433 }
434