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