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