de323bb9af3b808ba69a274c09035e7e2ce84751
[strongswan.git] / src / libstrongswan / utils / logger.c
1 /**
2 * @file logger.c
3 *
4 * @brief Implementation of logger_t.
5 *
6 */
7
8 /*
9 * Copyright (C) 2005-2006 Martin Willi
10 * Copyright (C) 2005 Jan Hutter
11 * Hochschule fuer Technik Rapperswil
12 *
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the
15 * Free Software Foundation; either version 2 of the License, or (at your
16 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 * for more details.
22 */
23
24 #include <syslog.h>
25 #include <stdarg.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <time.h>
29 #include <pthread.h>
30
31 #include "logger.h"
32
33
34 /**
35 * Maximum length of a log entry (only used for logger_s.log).
36 */
37 #define MAX_LOG 8192
38
39 /**
40 * Maximum number of logged bytes per line
41 */
42 #define MAX_BYTES 16
43
44 typedef struct private_logger_t private_logger_t;
45
46 /**
47 * @brief Private data of a logger_t object.
48 */
49 struct private_logger_t {
50 /**
51 * Public data.
52 */
53 logger_t public;
54 /**
55 * Detail-level of logger.
56 */
57 log_level_t level;
58 /**
59 * Name of logger.
60 */
61 char *name;
62 /**
63 * File to write log output to.
64 * NULL for syslog.
65 */
66 FILE *output;
67
68 /**
69 * Should a thread_id be included in the log?
70 */
71 bool log_thread_id;
72 };
73
74 /**
75 * thread local storage for get_thread_number
76 */
77 static pthread_key_t thread_ids;
78 static void make_key(void)
79 {
80 pthread_key_create(&thread_ids, NULL);
81 }
82
83 /**
84 * Get a unique thread number for a calling thread. Since
85 * pthread_self returns large and ugly numbers, use this function
86 * for logging; these numbers are incremental starting at 1
87 */
88 static int get_thread_number(void)
89 {
90 static int current_num = 0;
91 static pthread_once_t key_once = PTHREAD_ONCE_INIT;
92 int stored_num;
93
94 pthread_once(&key_once, make_key);
95 stored_num = (int)pthread_getspecific(thread_ids);
96 if (stored_num == 0)
97 {
98 pthread_setspecific(thread_ids, (void*)++current_num);
99 return current_num;
100 }
101 else
102 {
103 return stored_num;
104 }
105 }
106
107 /**
108 * prepend the logging prefix to string and store it in buffer
109 */
110 static void prepend_prefix(private_logger_t *this, log_level_t loglevel, const char *string, char *buffer)
111 {
112 char thread_id[3] = "";
113 char log_type, log_details;
114 char *separator = (strlen(this->name) == 0)? "" : ":";
115
116 if (loglevel & CONTROL)
117 {
118 log_type = 'C';
119 }
120 else if (loglevel & ERROR)
121 {
122 log_type = 'E';
123 }
124 else if (loglevel & RAW)
125 {
126 log_type = 'R';
127 }
128 else if (loglevel & PRIVATE)
129 {
130 log_type = 'P';
131 }
132 else if (loglevel & AUDIT)
133 {
134 log_type = 'A';
135 }
136 else
137 {
138 log_type = '-';
139 }
140
141 if (loglevel & (LEVEL3 - LEVEL2))
142 {
143 log_details = '3';
144 }
145 else if (loglevel & (LEVEL2 - LEVEL1))
146 {
147 log_details = '2';
148 }
149 else if (loglevel & LEVEL1)
150 {
151 log_details = '1';
152 }
153 else
154 {
155 log_details = '0';
156 }
157
158 if (this->log_thread_id)
159 {
160 snprintf(thread_id, sizeof(thread_id), "%02d", get_thread_number());
161 }
162 snprintf(buffer, MAX_LOG, "%s[%c%c%s%s] %s",
163 thread_id, log_type, log_details, separator, this->name, string);
164 }
165
166 /**
167 * Convert a charon-loglevel to a syslog priority
168 */
169 static int get_priority(log_level_t loglevel)
170 {
171 if (loglevel & ERROR)
172 {
173 return LOG_AUTHPRIV|LOG_ERR;
174 }
175 if (loglevel & AUDIT)
176 {
177 return LOG_AUTHPRIV|LOG_INFO;
178 }
179 return LOG_AUTHPRIV|LOG_DEBUG;
180 }
181
182 /**
183 * Implementation of logger_t.log.
184 *
185 * Yes, logg is written wrong :-).
186 */
187 static void logg(private_logger_t *this, log_level_t loglevel, const char *format, ...)
188 {
189 if ((this->level & loglevel) == loglevel)
190 {
191 char buffer[MAX_LOG];
192 va_list args;
193
194
195 if (this->output == NULL)
196 {
197 /* syslog */
198 prepend_prefix(this, loglevel, format, buffer);
199 va_start(args, format);
200 vsyslog(get_priority(loglevel), buffer, args);
201 va_end(args);
202 }
203 else
204 {
205 /* File output */
206 prepend_prefix(this, loglevel, format, buffer);
207 va_start(args, format);
208 vfprintf(this->output, buffer, args);
209 va_end(args);
210 fprintf(this->output, "\n");
211 }
212
213 }
214 }
215
216 /**
217 * Implementation of logger_t.log_bytes.
218 */
219 static void log_bytes(private_logger_t *this, log_level_t loglevel, const char *label, const char *bytes, size_t len)
220 {
221 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
222
223 if ((this->level & loglevel) == loglevel)
224 {
225 char thread_id[3] = "";
226 char buffer[MAX_LOG];
227 char ascii_buffer[MAX_BYTES+1];
228
229 char *buffer_pos = buffer;
230 const char format[] = "%s %d bytes @ %p";
231 const char *bytes_pos = bytes;
232 const char *bytes_roof = bytes + len;
233
234 int line_start = 0;
235 int i = 0;
236
237 /* since me can't do multi-line output to syslog,
238 * we must do multiple syslogs. To avoid
239 * problems in output order, lock this by a mutex.
240 */
241 pthread_mutex_lock(&mutex);
242
243 prepend_prefix(this, loglevel, format, buffer);
244
245 if (this->log_thread_id)
246 {
247 snprintf(thread_id, sizeof(thread_id), "%02d", get_thread_number());
248 }
249
250 if (this->output == NULL)
251 {
252 syslog(get_priority(loglevel), buffer, label, len, bytes);
253 }
254 else
255 {
256 fprintf(this->output, buffer, label, len, bytes);
257 fprintf(this->output, "\n");
258 }
259
260 while (bytes_pos < bytes_roof)
261 {
262 static char hexdig[] = "0123456789ABCDEF";
263
264 *buffer_pos++ = hexdig[(*bytes_pos >> 4) & 0xF];
265 *buffer_pos++ = hexdig[ *bytes_pos & 0xF];
266
267 ascii_buffer[i++] = (*bytes_pos > 31 && *bytes_pos < 127)
268 ? *bytes_pos : '.';
269
270 if (++bytes_pos == bytes_roof || i == MAX_BYTES)
271 {
272 int padding = 3 * (MAX_BYTES - i);
273
274 while (padding--)
275 {
276 *buffer_pos++ = ' ';
277 }
278 *buffer_pos++ = '\0';
279 ascii_buffer[i] = '\0';
280
281 if (this->output == NULL)
282 {
283 syslog(get_priority(loglevel), "%s[ :%5d] %s %s", thread_id, line_start, buffer, ascii_buffer);
284 }
285 else
286 {
287 fprintf(this->output, "%s[ :%5d] %s %s\n", thread_id, line_start, buffer, ascii_buffer);
288 }
289 buffer_pos = buffer;
290 line_start += MAX_BYTES;
291 i = 0;
292 }
293 else
294 {
295 *buffer_pos++ = ' ';
296 }
297 }
298 pthread_mutex_unlock(&mutex);
299 }
300 }
301
302 /**
303 * Implementation of logger_t.log_chunk.
304 */
305 static void log_chunk(logger_t *this, log_level_t loglevel, const char *label, chunk_t chunk)
306 {
307 this->log_bytes(this, loglevel, label, chunk.ptr, chunk.len);
308 }
309
310 /**
311 * Implementation of logger_t.enable_level.
312 */
313 static void enable_level(private_logger_t *this, log_level_t log_level)
314 {
315 this->level |= log_level;
316 }
317
318 /**
319 * Implementation of logger_t.disable_level.
320 */
321 static void disable_level(private_logger_t *this, log_level_t log_level)
322 {
323 this->level &= ~log_level;
324 }
325
326 /**
327 * Implementation of logger_t.set_output.
328 */
329 static void set_output(private_logger_t *this, FILE * output)
330 {
331 this->output = output;
332 }
333
334 /**
335 * Implementation of logger_t.get_level.
336 */
337 static log_level_t get_level(private_logger_t *this)
338 {
339 return this->level;
340 }
341
342 /**
343 * Implementation of logger_t.destroy.
344 */
345 static void destroy(private_logger_t *this)
346 {
347 free(this->name);
348 free(this);
349 }
350
351 /*
352 * Described in header.
353 */
354 logger_t *logger_create(char *logger_name, log_level_t log_level, bool log_thread_id, FILE * output)
355 {
356 private_logger_t *this = malloc_thing(private_logger_t);
357
358 /* public functions */
359 this->public.log = (void(*)(logger_t*,log_level_t,const char*,...))logg;
360 this->public.log_bytes = (void(*)(logger_t*, log_level_t, const char*, const char*,size_t))log_bytes;
361 this->public.log_chunk = log_chunk;
362 this->public.enable_level = (void(*)(logger_t*,log_level_t))enable_level;
363 this->public.disable_level = (void(*)(logger_t*,log_level_t))disable_level;
364 this->public.get_level = (log_level_t(*)(logger_t*))get_level;
365 this->public.set_output = (void(*)(logger_t*,FILE*))set_output;
366 this->public.destroy = (void(*)(logger_t*))destroy;
367
368 if (logger_name == NULL)
369 {
370 logger_name = "";
371 }
372
373 /* private variables */
374 this->level = log_level;
375 this->log_thread_id = log_thread_id;
376 this->name = malloc(strlen(logger_name) + 1);
377
378 strcpy(this->name,logger_name);
379 this->output = output;
380
381 return (logger_t*)this;
382 }