- lovlevels specified for each context
[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 #include <unistd.h>
30
31 #include "logger.h"
32
33 #include <daemon.h>
34 #include <utils/allocator.h>
35
36 /**
37 * Maximum length of al log entry (only used for logger_s.log)
38 */
39 #define MAX_LOG 8192
40
41 typedef struct private_logger_t private_logger_t;
42
43 /**
44 * @brief The logger 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 pid be included in the log?
67 */
68 bool log_pid;
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
101 {
102 log_type = '-';
103 }
104
105 if (loglevel & (ALL - MOST))
106 {
107 log_details = '3';
108 }
109 else if (loglevel & (MOST - MORE))
110 {
111 log_details = '2';
112 }
113 else if (loglevel & MORE)
114 {
115 log_details = '1';
116 }
117 else
118 {
119 log_details = '0';
120 }
121
122
123 if (this->log_pid)
124 {
125 snprintf(buffer, MAX_LOG, "[%c%c] [%s] @%d %s", log_type, log_details, this->name, getpid(), string);
126 }
127 else
128 {
129 snprintf(buffer, MAX_LOG, "[%c%c] [%s] %s", log_type, log_details, this->name, string);
130 }
131
132 }
133
134 /**
135 * Implementation of logger_t.log.
136 *
137 * Yes, logg is wrong written :-).
138 */
139 static status_t logg(private_logger_t *this, logger_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 return SUCCESS;
167 }
168
169 /**
170 * Implementation of logger_t.log_bytes.
171 */
172 static status_t log_bytes(private_logger_t *this, logger_level_t loglevel, char *label, char *bytes, size_t len)
173 {
174 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
175
176 /* since me can't do multi-line output to syslog,
177 * we must do multiple syslogs. To avoid
178 * problems in output order, lock this by a mutex.
179 */
180 pthread_mutex_lock(&mutex);
181
182 if ((this->level & loglevel) == loglevel)
183 {
184 char buffer[MAX_LOG];
185 char *format;
186 char *buffer_pos;
187 char *bytes_pos, *bytes_roof;
188 int i;
189
190
191 format = "%s (%d bytes)";
192 this->prepend_prefix(this, loglevel, format, buffer);
193
194 if (this->output == NULL)
195 {
196 syslog(LOG_INFO, buffer, label, len);
197 }
198 else
199 {
200 fprintf(this->output, buffer, label, len);
201 fprintf(this->output, "\n");
202 }
203
204 bytes_pos = bytes;
205 bytes_roof = bytes + len;
206 buffer_pos = buffer;
207
208 for (i = 1; bytes_pos < bytes_roof; i++)
209 {
210 static char hexdig[] = "0123456789ABCDEF";
211 *buffer_pos++ = hexdig[(*bytes_pos >> 4) & 0xF];
212 *buffer_pos++ = hexdig[ *bytes_pos & 0xF];
213 if ((i % 16) == 0)
214 {
215 *buffer_pos++ = '\0';
216 buffer_pos = buffer;
217 if (this->output == NULL)
218 {
219 syslog(LOG_INFO, "| %s", buffer);
220 }
221 else
222 {
223 fprintf(this->output, "| %s\n", buffer);
224 }
225 }
226 else if ((i % 8) == 0)
227 {
228 *buffer_pos++ = ' ';
229 *buffer_pos++ = ' ';
230 *buffer_pos++ = ' ';
231 }
232 else if ((i % 4) == 0)
233 {
234 *buffer_pos++ = ' ';
235 *buffer_pos++ = ' ';
236 }
237 else
238 {
239 *buffer_pos++ = ' ';
240 }
241
242 bytes_pos++;
243 }
244
245 *buffer_pos++ = '\0';
246 if (buffer_pos > buffer + 1)
247 {
248 buffer_pos = buffer;
249 if (this->output == NULL)
250 {
251 syslog(LOG_INFO, "| %s", buffer);
252 }
253 else
254 {
255 fprintf(this->output, "| %s\n", buffer);
256 }
257 }
258 }
259
260 pthread_mutex_unlock(&mutex);
261 return SUCCESS;
262 }
263
264
265 /**
266 * Implementation of logger_t.log_chunk.
267 */
268 static status_t log_chunk(logger_t *this, logger_level_t loglevel, char *label, chunk_t *chunk)
269 {
270 this->log_bytes(this, loglevel, label, chunk->ptr, chunk->len);
271 return SUCCESS;
272 }
273
274
275 /**
276 * Implementation of logger_t.enable_level.
277 */
278 static status_t enable_level(private_logger_t *this, logger_level_t log_level)
279 {
280 this->level |= log_level;
281 return SUCCESS;
282 }
283
284 /**
285 * Implementation of logger_t.disable_level.
286 */
287 static status_t disable_level(private_logger_t *this, logger_level_t log_level)
288 {
289 this->level &= ~log_level;
290 return SUCCESS;
291 }
292
293 /**
294 * Implementation of logger_t.destroy.
295 */
296 static status_t destroy(private_logger_t *this)
297 {
298 allocator_free(this->name);
299 allocator_free(this);
300 return SUCCESS;
301 }
302
303 /*
304 * Described in header.
305 */
306 logger_t *logger_create(char *logger_name, logger_level_t log_level, bool log_pid, FILE * output)
307 {
308 private_logger_t *this = allocator_alloc_thing(private_logger_t);
309
310 if (this == NULL)
311 {
312 return NULL;
313 }
314
315 if (logger_name == NULL)
316 {
317 logger_name = "";
318 }
319
320 this->public.log = (status_t(*)(logger_t*,logger_level_t,char*,...))logg;
321 this->public.log_bytes = (status_t(*)(logger_t*, logger_level_t, char*,char*,size_t))log_bytes;
322 this->public.log_chunk = log_chunk;
323 this->public.enable_level = (status_t(*)(logger_t*,logger_level_t))enable_level;
324 this->public.disable_level = (status_t(*)(logger_t*,logger_level_t))disable_level;
325 this->public.destroy = (status_t(*)(logger_t*))destroy;
326
327 this->prepend_prefix = prepend_prefix;
328
329 /* private variables */
330 this->level = log_level;
331 this->log_pid = log_pid;
332 this->name = allocator_alloc(strlen(logger_name) + 1);
333 if (this->name == NULL)
334 {
335 allocator_free(this);
336 return NULL;
337 }
338 strcpy(this->name,logger_name);
339 this->output = output;
340
341
342 if (output == NULL)
343 {
344 openlog(DAEMON_NAME, 0, LOG_DAEMON);
345 }
346
347 return (logger_t*)this;
348 }