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