Use register_printf_specifier instead of deprecated register_printf_function, if...
[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 <pthread.h>
195
196 static pthread_key_t vstr_conf_key;
197 static pthread_once_t vstr_conf_key_once = PTHREAD_ONCE_INIT;
198
199 static void init_vstr_conf_key(void)
200 {
201 pthread_key_create(&vstr_conf_key, (void*)vstr_free_conf);
202 }
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;
226 pthread_once(&vstr_conf_key_once, init_vstr_conf_key);
227 conf = (Vstr_conf*)pthread_getspecific(vstr_conf_key);
228 if (!conf)
229 {
230 conf = create_vstr_conf();
231 pthread_setspecific(vstr_conf_key, conf);
232 }
233 return conf;
234 }
235
236 /**
237 * Wrapper functions for printf and alike
238 */
239 int vstr_wrapper_printf(const char *format, ...)
240 {
241 int written;
242 va_list args;
243 va_start(args, format);
244 written = vstr_wrapper_vprintf(format, args);
245 va_end(args);
246 return written;
247 }
248 int vstr_wrapper_fprintf(FILE *stream, const char *format, ...)
249 {
250 int written;
251 va_list args;
252 va_start(args, format);
253 written = vstr_wrapper_vfprintf(stream, format, args);
254 va_end(args);
255 return written;
256 }
257 int vstr_wrapper_sprintf(char *str, const char *format, ...)
258 {
259 int written;
260 va_list args;
261 va_start(args, format);
262 written = vstr_wrapper_vsprintf(str, format, args);
263 va_end(args);
264 return written;
265 }
266 int vstr_wrapper_snprintf(char *str, size_t size, const char *format, ...)
267 {
268 int written;
269 va_list args;
270 va_start(args, format);
271 written = vstr_wrapper_vsnprintf(str, size, format, args);
272 va_end(args);
273 return written;
274 }
275 static inline int vstr_wrapper_vprintf_internal(int fd, const char *format,
276 va_list args)
277 {
278 int written;
279 Vstr_conf *conf = get_vstr_conf();
280 Vstr_base *s = vstr_make_base(conf);
281 vstr_add_vfmt(s, 0, format, args);
282 written = s->len;
283 while (s->len)
284 {
285 if (!vstr_sc_write_fd(s, 1, s->len, fd, NULL))
286 {
287 if (errno != EAGAIN && errno != EINTR)
288 {
289 written -= s->len;
290 break;
291 }
292 }
293 }
294 vstr_free_base(s);
295 return written;
296 }
297 int vstr_wrapper_vprintf(const char *format, va_list args)
298 {
299 return vstr_wrapper_vprintf_internal(STDOUT_FILENO, format, args);
300 }
301 int vstr_wrapper_vfprintf(FILE *stream, const char *format, va_list args)
302 {
303 return vstr_wrapper_vprintf_internal(fileno(stream), format, args);
304 }
305 static inline int vstr_wrapper_vsnprintf_internal(char *str, size_t size,
306 const char *format,
307 va_list args)
308 {
309 int written;
310 Vstr_conf *conf = get_vstr_conf();
311 Vstr_base *s = vstr_make_base(conf);
312 vstr_add_vfmt(s, 0, format, args);
313 written = s->len;
314 vstr_export_cstr_buf(s, 1, s->len, str, (size > 0) ? size : s->len + 1);
315 vstr_free_base(s);
316 return written;
317 }
318 int vstr_wrapper_vsprintf(char *str, const char *format, va_list args)
319 {
320 return vstr_wrapper_vsnprintf_internal(str, 0, format, args);
321 }
322 int vstr_wrapper_vsnprintf(char *str, size_t size, const char *format,
323 va_list args)
324 {
325 return (size > 0) ? vstr_wrapper_vsnprintf_internal(str, size, format, args) : 0;
326 }
327
328 #endif
329
330 /**
331 * Implementation of printf_hook_t.add_handler.
332 */
333 static void add_handler(private_printf_hook_t *this, char spec,
334 printf_hook_function_t hook, ...)
335 {
336 int i = -1;
337 printf_hook_handler_t *handler;
338 printf_hook_argtype_t argtype;
339 va_list args;
340
341 if (!IS_VALID_SPEC(spec))
342 {
343 DBG1("'%c' is not a valid printf hook specifier, not registered!", spec);
344 return;
345 }
346
347 handler = malloc_thing(printf_hook_handler_t);
348 handler->hook = hook;
349
350 va_start(args, hook);
351 while ((argtype = va_arg(args, printf_hook_argtype_t)) != PRINTF_HOOK_ARGTYPE_END)
352 {
353 if (++i >= ARGS_MAX)
354 {
355 DBG1("Too many arguments for printf hook with specifier '%c', not registered!", spec);
356 va_end(args);
357 free(handler);
358 return;
359 }
360 handler->argtypes[i] = argtype;
361 }
362 va_end(args);
363
364 handler->numargs = i + 1;
365
366 if (handler->numargs > 0)
367 {
368 #if !defined(USE_VSTR) && \
369 (defined(HAVE_PRINTF_FUNCTION) || defined(HAVE_PRINTF_SPECIFIER))
370 # ifdef HAVE_PRINTF_SPECIFIER
371 register_printf_specifier(spec, custom_print, custom_arginfo);
372 # else
373 register_printf_function(spec, custom_print, custom_arginfo);
374 # endif
375 #else
376 Vstr_conf *conf = get_vstr_conf();
377 handler->name = malloc(2);
378 handler->name[0] = spec;
379 handler->name[1] = '\0';
380 vstr_fmt_add_handler(conf, handler);
381 #endif
382 printf_hooks[SPEC_TO_INDEX(spec)] = handler;
383 }
384 else
385 {
386 free(handler);
387 }
388 }
389
390 /**
391 * Implementation of printf_hook_t.destroy
392 */
393 static void destroy(private_printf_hook_t *this)
394 {
395 int i;
396 #ifdef USE_VSTR
397 Vstr_conf *conf = get_vstr_conf();
398 #endif
399
400 for (i = 0; i < NUM_HANDLERS; ++i)
401 {
402 printf_hook_handler_t *handler = printf_hooks[i];
403 if (handler)
404 {
405 #ifdef USE_VSTR
406 vstr_fmt_del(conf, handler->name);
407 free(handler->name);
408 #endif
409 free(handler);
410 }
411 }
412
413 #ifdef USE_VSTR
414 /* freeing the Vstr_conf of the main thread */
415 pthread_key_delete(vstr_conf_key);
416 vstr_free_conf(conf);
417 vstr_exit();
418 #endif
419 free(this);
420 }
421
422 /*
423 * see header file
424 */
425 printf_hook_t *printf_hook_create()
426 {
427 private_printf_hook_t *this = malloc_thing(private_printf_hook_t);
428
429 this->public.add_handler = (void(*)(printf_hook_t*, char, printf_hook_function_t, ...))add_handler;
430 this->public.destroy = (void(*)(printf_hook_t*))destroy;
431
432 memset(printf_hooks, 0, sizeof(printf_hooks));
433
434 #ifdef USE_VSTR
435 if (!vstr_init())
436 {
437 DBG1("failed to initialize Vstr library!");
438 free(this);
439 return NULL;
440 }
441 #endif
442
443 return &this->public;
444 }
445