728892b17e8c3c04f5e0b11f9f121a82a8b86772
[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 * prepend the logging prefix to string and store it in buffer
75 */
76 static void prepend_prefix(private_logger_t *this, log_level_t loglevel, const char *string, char *buffer)
77 {
78 char log_type, log_details;
79 char thread_id[10] = "";
80
81 if (loglevel & CONTROL)
82 {
83 log_type = 'C';
84 }
85 else if (loglevel & ERROR)
86 {
87 log_type = 'E';
88 }
89 else if (loglevel & RAW)
90 {
91 log_type = 'R';
92 }
93 else if (loglevel & PRIVATE)
94 {
95 log_type = 'P';
96 }
97 else if (loglevel & AUDIT)
98 {
99 log_type = 'A';
100 }
101 else
102 {
103 log_type = '-';
104 }
105
106 if (loglevel & (LEVEL3 - LEVEL2))
107 {
108 log_details = '3';
109 }
110 else if (loglevel & (LEVEL2 - LEVEL1))
111 {
112 log_details = '2';
113 }
114 else if (loglevel & LEVEL1)
115 {
116 log_details = '1';
117 }
118 else
119 {
120 log_details = '0';
121 }
122
123 if (this->log_thread_id)
124 {
125 snprintf(thread_id, sizeof(thread_id), "%06d", (int)pthread_self());
126 }
127 snprintf(buffer, MAX_LOG, "%s[%c%c:%s] %s", thread_id, log_type, log_details, this->name, string);
128 }
129
130 /**
131 * Convert a charon-loglevel to a syslog priority
132 */
133 static int get_priority(log_level_t loglevel)
134 {
135 if (loglevel & ERROR)
136 {
137 return LOG_AUTHPRIV|LOG_ERR;
138 }
139 if (loglevel & AUDIT)
140 {
141 return LOG_AUTHPRIV|LOG_INFO;
142 }
143 return LOG_AUTHPRIV|LOG_DEBUG;
144 }
145
146 /**
147 * Implementation of logger_t.log.
148 *
149 * Yes, logg is written wrong :-).
150 */
151 static void logg(private_logger_t *this, log_level_t loglevel, const char *format, ...)
152 {
153 if ((this->level & loglevel) == loglevel)
154 {
155 char buffer[MAX_LOG];
156 va_list args;
157
158
159 if (this->output == NULL)
160 {
161 /* syslog */
162 prepend_prefix(this, loglevel, format, buffer);
163 va_start(args, format);
164 vsyslog(get_priority(loglevel), buffer, args);
165 va_end(args);
166 }
167 else
168 {
169 /* File output */
170 prepend_prefix(this, loglevel, format, buffer);
171 va_start(args, format);
172 vfprintf(this->output, buffer, args);
173 va_end(args);
174 fprintf(this->output, "\n");
175 }
176
177 }
178 }
179
180 /**
181 * Implementation of logger_t.log_bytes.
182 */
183 static void log_bytes(private_logger_t *this, log_level_t loglevel, const char *label, const char *bytes, size_t len)
184 {
185 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
186
187 if ((this->level & loglevel) == loglevel)
188 {
189 char thread_id[10] = "";
190 char buffer[MAX_LOG];
191 char ascii_buffer[MAX_BYTES+1];
192
193 char *buffer_pos = buffer;
194 const char format[] = "%s %d bytes @ %p";
195 const char *bytes_pos = bytes;
196 const char *bytes_roof = bytes + len;
197
198 int line_start = 0;
199 int i = 0;
200
201 if (this->log_thread_id)
202 {
203 snprintf(thread_id, sizeof(thread_id), "%06d", (int)pthread_self());
204 }
205
206 /* since me can't do multi-line output to syslog,
207 * we must do multiple syslogs. To avoid
208 * problems in output order, lock this by a mutex.
209 */
210 pthread_mutex_lock(&mutex);
211
212 prepend_prefix(this, loglevel, format, buffer);
213
214 if (this->output == NULL)
215 {
216 syslog(get_priority(loglevel), buffer, label, len, bytes);
217 }
218 else
219 {
220 fprintf(this->output, buffer, label, len, bytes);
221 fprintf(this->output, "\n");
222 }
223
224 while (bytes_pos < bytes_roof)
225 {
226 static char hexdig[] = "0123456789ABCDEF";
227
228 *buffer_pos++ = hexdig[(*bytes_pos >> 4) & 0xF];
229 *buffer_pos++ = hexdig[ *bytes_pos & 0xF];
230
231 ascii_buffer[i++] = (*bytes_pos > 31 && *bytes_pos < 127)
232 ? *bytes_pos : '.';
233
234 if (++bytes_pos == bytes_roof || i == MAX_BYTES)
235 {
236 int padding = 3 * (MAX_BYTES - i);
237
238 while (padding--)
239 {
240 *buffer_pos++ = ' ';
241 }
242 *buffer_pos++ = '\0';
243 ascii_buffer[i] = '\0';
244
245 if (this->output == NULL)
246 {
247 syslog(get_priority(loglevel), "%s[ :%5d] %s %s", thread_id, line_start, buffer, ascii_buffer);
248 }
249 else
250 {
251 fprintf(this->output, "%s[ :%5d] %s %s\n", thread_id, line_start, buffer, ascii_buffer);
252 }
253 buffer_pos = buffer;
254 line_start += MAX_BYTES;
255 i = 0;
256 }
257 else
258 {
259 *buffer_pos++ = ' ';
260 }
261 }
262 pthread_mutex_unlock(&mutex);
263 }
264 }
265
266 /**
267 * Implementation of logger_t.log_chunk.
268 */
269 static void log_chunk(logger_t *this, log_level_t loglevel, const char *label, chunk_t chunk)
270 {
271 this->log_bytes(this, loglevel, label, chunk.ptr, chunk.len);
272 }
273
274 /**
275 * Implementation of logger_t.enable_level.
276 */
277 static void enable_level(private_logger_t *this, log_level_t log_level)
278 {
279 this->level |= log_level;
280 }
281
282 /**
283 * Implementation of logger_t.disable_level.
284 */
285 static void disable_level(private_logger_t *this, log_level_t log_level)
286 {
287 this->level &= ~log_level;
288 }
289
290 /**
291 * Implementation of logger_t.set_output.
292 */
293 static void set_output(private_logger_t *this, FILE * output)
294 {
295 this->output = output;
296 }
297
298 /**
299 * Implementation of logger_t.get_level.
300 */
301 static log_level_t get_level(private_logger_t *this)
302 {
303 return this->level;
304 }
305
306 /**
307 * Implementation of logger_t.destroy.
308 */
309 static void destroy(private_logger_t *this)
310 {
311 free(this->name);
312 free(this);
313 }
314
315 /*
316 * Described in header.
317 */
318 logger_t *logger_create(char *logger_name, log_level_t log_level, bool log_thread_id, FILE * output)
319 {
320 private_logger_t *this = malloc_thing(private_logger_t);
321
322 /* public functions */
323 this->public.log = (void(*)(logger_t*,log_level_t,const char*,...))logg;
324 this->public.log_bytes = (void(*)(logger_t*, log_level_t, const char*, const char*,size_t))log_bytes;
325 this->public.log_chunk = log_chunk;
326 this->public.enable_level = (void(*)(logger_t*,log_level_t))enable_level;
327 this->public.disable_level = (void(*)(logger_t*,log_level_t))disable_level;
328 this->public.get_level = (log_level_t(*)(logger_t*))get_level;
329 this->public.set_output = (void(*)(logger_t*,FILE*))set_output;
330 this->public.destroy = (void(*)(logger_t*))destroy;
331
332 if (logger_name == NULL)
333 {
334 logger_name = "";
335 }
336
337 /* private variables */
338 this->level = log_level;
339 this->log_thread_id = log_thread_id;
340 this->name = malloc(strlen(logger_name) + 1);
341
342 strcpy(this->name,logger_name);
343 this->output = output;
344
345 return (logger_t*)this;
346 }