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