(no commit message)
[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 <string.h>
26 #include <stdio.h>
27 #include <time.h>
28 #include <pthread.h>
29
30 #include "logger.h"
31
32 /**
33 * Maximum length of a log entry (only used for logger_s.log).
34 */
35 #define MAX_LOG 8192
36
37 /**
38 * Maximum number of logged bytes per line
39 */
40 #define MAX_BYTES 16
41
42 typedef struct private_logger_t private_logger_t;
43
44 /**
45 * @brief Private data of a logger_t object.
46 */
47 struct private_logger_t {
48 /**
49 * Public data.
50 */
51 logger_t public;
52 /**
53 * Detail-level of logger.
54 */
55 log_level_t level;
56 /**
57 * Name of logger.
58 */
59 char *name;
60 /**
61 * File to write log output to.
62 * NULL for syslog.
63 */
64 FILE *output;
65
66 /**
67 * Should a thread_id be included in the log?
68 */
69 bool log_thread_id;
70 };
71
72 /**
73 * thread local storage for get_thread_number
74 */
75 static pthread_key_t thread_ids;
76 static void make_key(void)
77 {
78 pthread_key_create(&thread_ids, NULL);
79 }
80
81 /**
82 * Get a unique thread number for a calling thread. Since
83 * pthread_self returns large and ugly numbers, use this function
84 * for logging; these numbers are incremental starting at 1
85 */
86 static int get_thread_number(void)
87 {
88 static int current_num = 0;
89 static pthread_once_t key_once = PTHREAD_ONCE_INIT;
90 int stored_num;
91
92 pthread_once(&key_once, make_key);
93 stored_num = (int)pthread_getspecific(thread_ids);
94 if (stored_num == 0)
95 {
96 pthread_setspecific(thread_ids, (void*)++current_num);
97 return current_num;
98 }
99 else
100 {
101 return stored_num;
102 }
103 }
104
105 /**
106 * prepend the logging prefix to string and store it in buffer
107 */
108 static void prepend_prefix(private_logger_t *this, log_level_t loglevel, const char *string, char *buffer)
109 {
110 char thread_id[3] = "";
111 char log_type, log_details;
112 char *separator = (strlen(this->name) == 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 snprintf(thread_id, sizeof(thread_id), "%02d", get_thread_number());
159 }
160 snprintf(buffer, MAX_LOG, "%s[%c%c%s%s] %s",
161 thread_id, log_type, log_details, separator, this->name, string);
162 }
163
164 /**
165 * Convert a charon-loglevel to a syslog priority
166 */
167 static int get_priority(log_level_t loglevel)
168 {
169 if (loglevel & ERROR)
170 {
171 return LOG_AUTHPRIV|LOG_ERR;
172 }
173 if (loglevel & AUDIT)
174 {
175 return LOG_AUTHPRIV|LOG_INFO;
176 }
177 return LOG_AUTHPRIV|LOG_DEBUG;
178 }
179
180 /**
181 * Implementation of logger_t.logv.
182 */
183 static void logv(private_logger_t *this, log_level_t loglevel, const char *format, va_list args)
184 {
185 if ((this->level & loglevel) == loglevel)
186 {
187 char buffer[MAX_LOG];
188
189 if (this->output == NULL)
190 {
191 /* syslog */
192 prepend_prefix(this, loglevel, format, buffer);
193 vsyslog(get_priority(loglevel), buffer, args);
194 }
195 else
196 {
197 /* File output */
198 prepend_prefix(this, loglevel, format, buffer);
199 vfprintf(this->output, buffer, args);
200 fprintf(this->output, "\n");
201 }
202 }
203 }
204
205 /**
206 * Implementation of logger_t.log.
207 */
208 static void logg(private_logger_t *this, log_level_t loglevel, const char *format, ...)
209 {
210 va_list args;
211
212 va_start(args, format);
213 logv(this, loglevel, format, args);
214 va_end(args);
215 }
216
217 /**
218 * Implementation of logger_t.log_bytes.
219 */
220 static void log_bytes(private_logger_t *this, log_level_t loglevel, const char *label, const char *bytes, size_t len)
221 {
222 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
223
224 if ((this->level & loglevel) == loglevel)
225 {
226 char thread_id[3] = "";
227 char buffer[MAX_LOG];
228 char ascii_buffer[MAX_BYTES+1];
229
230 char *buffer_pos = buffer;
231 const char format[] = "%s %d bytes @ %p";
232 const char *bytes_pos = bytes;
233 const char *bytes_roof = bytes + len;
234
235 int line_start = 0;
236 int i = 0;
237
238 /* since me can't do multi-line output to syslog,
239 * we must do multiple syslogs. To avoid
240 * problems in output order, lock this by a mutex.
241 */
242 pthread_mutex_lock(&mutex);
243
244 prepend_prefix(this, loglevel, format, buffer);
245
246 if (this->log_thread_id)
247 {
248 snprintf(thread_id, sizeof(thread_id), "%02d", get_thread_number());
249 }
250
251 if (this->output == NULL)
252 {
253 syslog(get_priority(loglevel), buffer, label, len, bytes);
254 }
255 else
256 {
257 fprintf(this->output, buffer, label, len, bytes);
258 fprintf(this->output, "\n");
259 }
260
261 while (bytes_pos < bytes_roof)
262 {
263 static char hexdig[] = "0123456789ABCDEF";
264
265 *buffer_pos++ = hexdig[(*bytes_pos >> 4) & 0xF];
266 *buffer_pos++ = hexdig[ *bytes_pos & 0xF];
267
268 ascii_buffer[i++] = (*bytes_pos > 31 && *bytes_pos < 127)
269 ? *bytes_pos : '.';
270
271 if (++bytes_pos == bytes_roof || i == MAX_BYTES)
272 {
273 int padding = 3 * (MAX_BYTES - i);
274
275 while (padding--)
276 {
277 *buffer_pos++ = ' ';
278 }
279 *buffer_pos++ = '\0';
280 ascii_buffer[i] = '\0';
281
282 if (this->output == NULL)
283 {
284 syslog(get_priority(loglevel), "%s[ :%5d] %s %s", thread_id, line_start, buffer, ascii_buffer);
285 }
286 else
287 {
288 fprintf(this->output, "%s[ :%5d] %s %s\n", thread_id, line_start, buffer, ascii_buffer);
289 }
290 buffer_pos = buffer;
291 line_start += MAX_BYTES;
292 i = 0;
293 }
294 else
295 {
296 *buffer_pos++ = ' ';
297 }
298 }
299 pthread_mutex_unlock(&mutex);
300 }
301 }
302
303 /**
304 * Implementation of logger_t.log_chunk.
305 */
306 static void log_chunk(logger_t *this, log_level_t loglevel, const char *label, chunk_t chunk)
307 {
308 this->log_bytes(this, loglevel, label, chunk.ptr, chunk.len);
309 }
310
311 /**
312 * Implementation of logger_t.enable_level.
313 */
314 static void enable_level(private_logger_t *this, log_level_t log_level)
315 {
316 this->level |= log_level;
317 }
318
319 /**
320 * Implementation of logger_t.disable_level.
321 */
322 static void disable_level(private_logger_t *this, log_level_t log_level)
323 {
324 this->level &= ~log_level;
325 }
326
327 /**
328 * Implementation of logger_t.set_output.
329 */
330 static void set_output(private_logger_t *this, FILE * output)
331 {
332 this->output = output;
333 }
334
335 /**
336 * Implementation of logger_t.get_level.
337 */
338 static log_level_t get_level(private_logger_t *this)
339 {
340 return this->level;
341 }
342
343 /**
344 * Implementation of logger_t.destroy.
345 */
346 static void destroy(private_logger_t *this)
347 {
348 free(this->name);
349 free(this);
350 }
351
352 /*
353 * Described in header.
354 */
355 logger_t *logger_create(char *logger_name, log_level_t log_level, bool log_thread_id, FILE * output)
356 {
357 private_logger_t *this = malloc_thing(private_logger_t);
358
359 /* public functions */
360 this->public.log = (void(*)(logger_t*,log_level_t,const char*,...))logg;
361 this->public.logv = (void(*)(logger_t*,log_level_t,const char*,va_list))logv;
362 this->public.log_bytes = (void(*)(logger_t*, log_level_t, const char*, const char*,size_t))log_bytes;
363 this->public.log_chunk = log_chunk;
364 this->public.enable_level = (void(*)(logger_t*,log_level_t))enable_level;
365 this->public.disable_level = (void(*)(logger_t*,log_level_t))disable_level;
366 this->public.get_level = (log_level_t(*)(logger_t*))get_level;
367 this->public.set_output = (void(*)(logger_t*,FILE*))set_output;
368 this->public.destroy = (void(*)(logger_t*))destroy;
369
370 if (logger_name == NULL)
371 {
372 logger_name = "";
373 }
374
375 /* private variables */
376 this->level = log_level;
377 this->log_thread_id = log_thread_id;
378 this->name = malloc(strlen(logger_name) + 1);
379
380 strcpy(this->name,logger_name);
381 this->output = output;
382
383 return (logger_t*)this;
384 }