- identification_t supports now almost all id types
[strongswan.git] / Source / lib / 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 typedef struct private_logger_t private_logger_t;
40
41 /**
42 * @brief Private data of a logger_t object.
43 */
44 struct private_logger_t {
45 /**
46 * Public data.
47 */
48 logger_t public;
49 /**
50 * Detail-level of logger.
51 */
52 log_level_t level;
53 /**
54 * Name of logger.
55 */
56 char *name;
57 /**
58 * File to write log output to.
59 * NULL for syslog.
60 */
61 FILE *output;
62
63 /**
64 * Should a thread_id be included in the log?
65 */
66 bool log_thread_id;
67
68 /**
69 * Applies a prefix to string and stores it in buffer.
70 *
71 * @warning: buffer must be at least have MAX_LOG size.
72 */
73 void (*prepend_prefix) (private_logger_t *this, log_level_t loglevel, char *string, char *buffer);
74 };
75
76 /**
77 * Implementation of private_logger_t.prepend_prefix.
78 */
79 static void prepend_prefix(private_logger_t *this, log_level_t loglevel, char *string, char *buffer)
80 {
81 char log_type, log_details;
82 if (loglevel & CONTROL)
83 {
84 log_type = '~';
85 }
86 else if (loglevel & ERROR)
87 {
88 log_type = '!';
89 }
90 else if (loglevel & RAW)
91 {
92 log_type = '#';
93 }
94 else if (loglevel & PRIVATE)
95 {
96 log_type = '?';
97 }
98 else if (loglevel & AUDIT)
99 {
100 log_type = '>';
101 }
102 else
103 {
104 log_type = '-';
105 }
106
107 if (loglevel & (LEVEL3 - LEVEL2))
108 {
109 log_details = '3';
110 }
111 else if (loglevel & (LEVEL2 - LEVEL1))
112 {
113 log_details = '2';
114 }
115 else if (loglevel & LEVEL1)
116 {
117 log_details = '1';
118 }
119 else
120 {
121 log_details = '0';
122 }
123
124 if (this->log_thread_id)
125 {
126 snprintf(buffer, MAX_LOG, "[%c%c:%s] @%u %s", log_type, log_details, this->name, (int)pthread_self(), string);
127 }
128 else
129 {
130 snprintf(buffer, MAX_LOG, "[%c%c:%s] %s", log_type, log_details, this->name, string);
131 }
132 }
133
134 /**
135 * Implementation of logger_t.log.
136 *
137 * Yes, logg is wrong written :-).
138 */
139 static void logg(private_logger_t *this, log_level_t loglevel, char *format, ...)
140 {
141 if ((this->level & loglevel) == loglevel)
142 {
143 char buffer[MAX_LOG];
144 va_list args;
145
146
147 if (this->output == NULL)
148 {
149 /* syslog */
150 this->prepend_prefix(this, loglevel, format, buffer);
151 va_start(args, format);
152 vsyslog(LOG_INFO, buffer, args);
153 va_end(args);
154 }
155 else
156 {
157 /* File output */
158 this->prepend_prefix(this, loglevel, format, buffer);
159 va_start(args, format);
160 vfprintf(this->output, buffer, args);
161 va_end(args);
162 fprintf(this->output, "\n");
163 }
164
165 }
166 }
167
168 /**
169 * Implementation of logger_t.log_bytes.
170 */
171 static void log_bytes(private_logger_t *this, log_level_t loglevel, char *label, char *bytes, size_t len)
172 {
173 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
174
175
176 if ((this->level & loglevel) == loglevel)
177 {
178 char buffer[MAX_LOG];
179 char ascii_buffer[17];
180 char *format;
181 char *buffer_pos;
182 char *bytes_pos, *bytes_roof;
183 int i;
184 int line_start = 0;
185
186 /* since me can't do multi-line output to syslog,
187 * we must do multiple syslogs. To avoid
188 * problems in output order, lock this by a mutex.
189 */
190 pthread_mutex_lock(&mutex);
191
192
193 format = "%s (%d bytes @%p)";
194 this->prepend_prefix(this, loglevel, format, buffer);
195
196 if (this->output == NULL)
197 {
198 syslog(LOG_INFO, buffer, label, len);
199 }
200 else
201 {
202 fprintf(this->output, buffer, label, len, bytes);
203 fprintf(this->output, "\n");
204 }
205
206 bytes_pos = bytes;
207 bytes_roof = bytes + len;
208 buffer_pos = buffer;
209 memset(ascii_buffer, 0, 17);
210
211 for (i = 1; bytes_pos < bytes_roof; i++)
212 {
213 static char hexdig[] = "0123456789ABCDEF";
214 *buffer_pos++ = hexdig[(*bytes_pos >> 4) & 0xF];
215 *buffer_pos++ = hexdig[ *bytes_pos & 0xF];
216 if ((i % 16) == 0)
217 {
218 *buffer_pos++ = '\0';
219 buffer_pos = buffer;
220 if (this->output == NULL)
221 {
222 syslog(LOG_INFO, "[ :%5d] %s %s", line_start, buffer, ascii_buffer);
223 }
224 else
225 {
226 fprintf(this->output, "[ :%5d] %s %s\n", line_start, buffer, ascii_buffer);
227 }
228 memset(ascii_buffer, 0, 16);
229 line_start += 16;
230 }
231 else if ((i % 4) == 0)
232 {
233 *buffer_pos++ = ' ';
234 // *buffer_pos++ = ' ';
235 }
236 else
237 {
238 *buffer_pos++ = ' ';
239 }
240
241 if (*bytes_pos > 31 && *bytes_pos < 127)
242 {
243 ascii_buffer[(i % 16)] = *bytes_pos;
244 }
245 else
246 {
247 ascii_buffer[(i % 16)] = '*';
248 }
249
250 bytes_pos++;
251 }
252
253 *buffer_pos++ = '\0';
254 if (buffer_pos > buffer + 1)
255 {
256 buffer_pos = buffer;
257 if (this->output == NULL)
258 {
259 syslog(LOG_INFO, "[ :%5d] %s %16s", line_start, buffer, ascii_buffer);
260 }
261 else
262 {
263 fprintf(this->output, "[ :%5d] %s %16s\n", line_start, buffer, ascii_buffer);
264 }
265 }
266 pthread_mutex_unlock(&mutex);
267 }
268 }
269
270 /**
271 * Implementation of logger_t.log_chunk.
272 */
273 static void log_chunk(logger_t *this, log_level_t loglevel, char *label, chunk_t chunk)
274 {
275 this->log_bytes(this, loglevel, label, chunk.ptr, chunk.len);
276 }
277
278 /**
279 * Implementation of logger_t.enable_level.
280 */
281 static void enable_level(private_logger_t *this, log_level_t log_level)
282 {
283 this->level |= log_level;
284 }
285
286 /**
287 * Implementation of logger_t.disable_level.
288 */
289 static void disable_level(private_logger_t *this, log_level_t log_level)
290 {
291 this->level &= ~log_level;
292 }
293
294 /**
295 * Implementation of logger_t.set_output.
296 */
297 static void set_output(private_logger_t *this, FILE * output)
298 {
299 this->output = output;
300 }
301
302 /**
303 * Implementation of logger_t.get_level.
304 */
305 static log_level_t get_level(private_logger_t *this)
306 {
307 return this->level;
308 }
309
310 /**
311 * Implementation of logger_t.destroy.
312 */
313 static void destroy(private_logger_t *this)
314 {
315 free(this->name);
316 free(this);
317 }
318
319 /*
320 * Described in header.
321 */
322 logger_t *logger_create(char *logger_name, log_level_t log_level, bool log_thread_id, FILE * output)
323 {
324 private_logger_t *this = malloc_thing(private_logger_t);
325
326 /* public functions */
327 this->public.log = (void(*)(logger_t*,log_level_t,char*,...))logg;
328 this->public.log_bytes = (void(*)(logger_t*, log_level_t, char*,char*,size_t))log_bytes;
329 this->public.log_chunk = log_chunk;
330 this->public.enable_level = (void(*)(logger_t*,log_level_t))enable_level;
331 this->public.disable_level = (void(*)(logger_t*,log_level_t))disable_level;
332 this->public.get_level = (log_level_t(*)(logger_t*))get_level;
333 this->public.set_output = (void(*)(logger_t*,FILE*))set_output;
334 this->public.destroy = (void(*)(logger_t*))destroy;
335
336 /* private functions */
337 this->prepend_prefix = prepend_prefix;
338
339 if (logger_name == NULL)
340 {
341 logger_name = "";
342 }
343
344 /* private variables */
345 this->level = log_level;
346 this->log_thread_id = log_thread_id;
347 this->name = malloc(strlen(logger_name) + 1);
348
349 strcpy(this->name,logger_name);
350 this->output = output;
351
352 if (output == NULL)
353 {
354 //openlog(DAEMON_NAME, 0, LOG_DAEMON);
355 }
356
357 return (logger_t*)this;
358 }