]> git.ipfire.org Git - people/ms/strongswan.git/blob - programs/charon/lib/utils/logger.c
- import of strongswan-2.7.0
[people/ms/strongswan.git] / programs / charon / 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 * 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), " @%d", (int)pthread_self());
126 }
127 snprintf(buffer, MAX_LOG, "[%c%c:%s]%s %s", log_type, log_details, this->name, thread_id, 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 & AUDIT)
136 {
137 return LOG_AUTHPRIV|LOG_INFO;
138 }
139 return LOG_DAEMON|LOG_DEBUG;
140 }
141
142 /**
143 * Implementation of logger_t.log.
144 *
145 * Yes, logg is written wrong :-).
146 */
147 static void logg(private_logger_t *this, log_level_t loglevel, const char *format, ...)
148 {
149 if ((this->level & loglevel) == loglevel)
150 {
151 char buffer[MAX_LOG];
152 va_list args;
153
154
155 if (this->output == NULL)
156 {
157 /* syslog */
158 prepend_prefix(this, loglevel, format, buffer);
159 va_start(args, format);
160 vsyslog(get_priority(loglevel), buffer, args);
161 va_end(args);
162 }
163 else
164 {
165 /* File output */
166 prepend_prefix(this, loglevel, format, buffer);
167 va_start(args, format);
168 vfprintf(this->output, buffer, args);
169 va_end(args);
170 fprintf(this->output, "\n");
171 }
172
173 }
174 }
175
176 /**
177 * Implementation of logger_t.log_bytes.
178 */
179 static void log_bytes(private_logger_t *this, log_level_t loglevel, const char *label, const char *bytes, size_t len)
180 {
181 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
182
183 if ((this->level & loglevel) == loglevel)
184 {
185 char thread_id[10] = "";
186 char buffer[MAX_LOG];
187 char ascii_buffer[MAX_BYTES+1];
188
189 char *buffer_pos = buffer;
190 const char format[] = "%s %d bytes @ %p";
191 const char *bytes_pos = bytes;
192 const char *bytes_roof = bytes + len;
193
194 int line_start = 0;
195 int i = 0;
196
197 if (this->log_thread_id)
198 {
199 snprintf(thread_id, sizeof(thread_id), " @%d", (int)pthread_self());
200 }
201
202 /* since me can't do multi-line output to syslog,
203 * we must do multiple syslogs. To avoid
204 * problems in output order, lock this by a mutex.
205 */
206 pthread_mutex_lock(&mutex);
207
208 prepend_prefix(this, loglevel, format, buffer);
209
210 if (this->output == NULL)
211 {
212 syslog(get_priority(loglevel), buffer, label, len, bytes);
213 }
214 else
215 {
216 fprintf(this->output, buffer, label, len, bytes);
217 fprintf(this->output, "\n");
218 }
219
220 while (bytes_pos < bytes_roof)
221 {
222 static char hexdig[] = "0123456789ABCDEF";
223
224 *buffer_pos++ = hexdig[(*bytes_pos >> 4) & 0xF];
225 *buffer_pos++ = hexdig[ *bytes_pos & 0xF];
226
227 ascii_buffer[i++] = (*bytes_pos > 31 && *bytes_pos < 127)
228 ? *bytes_pos : '.';
229
230 if (++bytes_pos == bytes_roof || i == MAX_BYTES)
231 {
232 int padding = 3 * (MAX_BYTES - i);
233
234 while (padding--)
235 {
236 *buffer_pos++ = ' ';
237 }
238 *buffer_pos++ = '\0';
239 ascii_buffer[i] = '\0';
240
241 if (this->output == NULL)
242 {
243 syslog(get_priority(loglevel), "[ :%5d]%s %s %s", line_start, thread_id, buffer, ascii_buffer);
244 }
245 else
246 {
247 fprintf(this->output, "[ :%5d]%s %s %s\n", line_start, thread_id, buffer, ascii_buffer);
248 }
249 buffer_pos = buffer;
250 line_start += MAX_BYTES;
251 i = 0;
252 }
253 else
254 {
255 *buffer_pos++ = ' ';
256 }
257 }
258 pthread_mutex_unlock(&mutex);
259 }
260 }
261
262 /**
263 * Implementation of logger_t.log_chunk.
264 */
265 static void log_chunk(logger_t *this, log_level_t loglevel, const char *label, chunk_t chunk)
266 {
267 this->log_bytes(this, loglevel, label, chunk.ptr, chunk.len);
268 }
269
270 /**
271 * Implementation of logger_t.enable_level.
272 */
273 static void enable_level(private_logger_t *this, log_level_t log_level)
274 {
275 this->level |= log_level;
276 }
277
278 /**
279 * Implementation of logger_t.disable_level.
280 */
281 static void disable_level(private_logger_t *this, log_level_t log_level)
282 {
283 this->level &= ~log_level;
284 }
285
286 /**
287 * Implementation of logger_t.set_output.
288 */
289 static void set_output(private_logger_t *this, FILE * output)
290 {
291 this->output = output;
292 }
293
294 /**
295 * Implementation of logger_t.get_level.
296 */
297 static log_level_t get_level(private_logger_t *this)
298 {
299 return this->level;
300 }
301
302 /**
303 * Implementation of logger_t.destroy.
304 */
305 static void destroy(private_logger_t *this)
306 {
307 free(this->name);
308 free(this);
309 }
310
311 /*
312 * Described in header.
313 */
314 logger_t *logger_create(char *logger_name, log_level_t log_level, bool log_thread_id, FILE * output)
315 {
316 private_logger_t *this = malloc_thing(private_logger_t);
317
318 /* public functions */
319 this->public.log = (void(*)(logger_t*,log_level_t,const char*,...))logg;
320 this->public.log_bytes = (void(*)(logger_t*, log_level_t, const char*, const char*,size_t))log_bytes;
321 this->public.log_chunk = log_chunk;
322 this->public.enable_level = (void(*)(logger_t*,log_level_t))enable_level;
323 this->public.disable_level = (void(*)(logger_t*,log_level_t))disable_level;
324 this->public.get_level = (log_level_t(*)(logger_t*))get_level;
325 this->public.set_output = (void(*)(logger_t*,FILE*))set_output;
326 this->public.destroy = (void(*)(logger_t*))destroy;
327
328 if (logger_name == NULL)
329 {
330 logger_name = "";
331 }
332
333 /* private variables */
334 this->level = log_level;
335 this->log_thread_id = log_thread_id;
336 this->name = malloc(strlen(logger_name) + 1);
337
338 strcpy(this->name,logger_name);
339 this->output = output;
340
341 return (logger_t*)this;
342 }