file-logger: Set owner/group of log file
[strongswan.git] / src / libcharon / bus / listeners / file_logger.c
1 /*
2 * Copyright (C) 2012-2015 Tobias Brunner
3 * Copyright (C) 2006 Martin Willi
4 * HSR Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include <stdio.h>
18 #include <string.h>
19 #include <time.h>
20 #include <errno.h>
21 #include <unistd.h>
22 #include <sys/types.h>
23
24 #include "file_logger.h"
25
26 #include <daemon.h>
27 #include <threading/mutex.h>
28 #include <threading/rwlock.h>
29
30 typedef struct private_file_logger_t private_file_logger_t;
31
32 /**
33 * Private data of a file_logger_t object
34 */
35 struct private_file_logger_t {
36
37 /**
38 * Public data.
39 */
40 file_logger_t public;
41
42 /**
43 * File name of the target
44 */
45 char *filename;
46
47 /**
48 * Current output file
49 */
50 FILE *out;
51
52 /**
53 * Flush after writing a line?
54 */
55 bool flush_line;
56
57 /**
58 * Maximum level to log, for each group
59 */
60 level_t levels[DBG_MAX];
61
62 /**
63 * strftime() format of time prefix, if any
64 */
65 char *time_format;
66
67 /**
68 * Add milliseconds after the time string
69 */
70 bool add_ms;
71
72 /**
73 * Print the name/# of the IKE_SA?
74 */
75 bool ike_name;
76
77 /**
78 * Mutex to ensure multi-line log messages are not torn apart
79 */
80 mutex_t *mutex;
81
82 /**
83 * Lock to read/write options (FD, levels, time_format, etc.)
84 */
85 rwlock_t *lock;
86 };
87
88 METHOD(logger_t, log_, void,
89 private_file_logger_t *this, debug_t group, level_t level, int thread,
90 ike_sa_t* ike_sa, const char *message)
91 {
92 char timestr[128], namestr[128] = "";
93 const char *current = message, *next;
94 struct tm tm;
95 timeval_t tv;
96 time_t s;
97 u_int ms = 0;
98
99 this->lock->read_lock(this->lock);
100 if (!this->out)
101 { /* file is not open */
102 this->lock->unlock(this->lock);
103 return;
104 }
105 if (this->time_format)
106 {
107 gettimeofday(&tv, NULL);
108 s = tv.tv_sec;
109 ms = tv.tv_usec / 1000;
110 localtime_r(&s, &tm);
111 strftime(timestr, sizeof(timestr), this->time_format, &tm);
112 }
113 if (this->ike_name && ike_sa)
114 {
115 if (ike_sa->get_peer_cfg(ike_sa))
116 {
117 snprintf(namestr, sizeof(namestr), " <%s|%d>",
118 ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa));
119 }
120 else
121 {
122 snprintf(namestr, sizeof(namestr), " <%d>",
123 ike_sa->get_unique_id(ike_sa));
124 }
125 }
126 else
127 {
128 namestr[0] = '\0';
129 }
130
131 /* prepend a prefix in front of every line */
132 this->mutex->lock(this->mutex);
133 while (TRUE)
134 {
135 next = strchr(current, '\n');
136 if (this->time_format)
137 {
138 if (this->add_ms)
139 {
140 fprintf(this->out, "%s.%03u %.2d[%N]%s ",
141 timestr, ms, thread, debug_names, group, namestr);
142 }
143 else
144 {
145 fprintf(this->out, "%s %.2d[%N]%s ",
146 timestr, thread, debug_names, group, namestr);
147 }
148 }
149 else
150 {
151 fprintf(this->out, "%.2d[%N]%s ",
152 thread, debug_names, group, namestr);
153 }
154 if (next == NULL)
155 {
156 fprintf(this->out, "%s\n", current);
157 break;
158 }
159 fprintf(this->out, "%.*s\n", (int)(next - current), current);
160 current = next + 1;
161 }
162 #ifndef HAVE_SETLINEBUF
163 if (this->flush_line)
164 {
165 fflush(this->out);
166 }
167 #endif /* !HAVE_SETLINEBUF */
168 this->mutex->unlock(this->mutex);
169 this->lock->unlock(this->lock);
170 }
171
172 METHOD(logger_t, get_level, level_t,
173 private_file_logger_t *this, debug_t group)
174 {
175 level_t level;
176
177 this->lock->read_lock(this->lock);
178 level = this->levels[group];
179 this->lock->unlock(this->lock);
180 return level;
181 }
182
183 METHOD(file_logger_t, set_level, void,
184 private_file_logger_t *this, debug_t group, level_t level)
185 {
186 this->lock->write_lock(this->lock);
187 if (group < DBG_ANY)
188 {
189 this->levels[group] = level;
190 }
191 else
192 {
193 for (group = 0; group < DBG_MAX; group++)
194 {
195 this->levels[group] = level;
196 }
197 }
198 this->lock->unlock(this->lock);
199 }
200
201 METHOD(file_logger_t, set_options, void,
202 private_file_logger_t *this, char *time_format, bool add_ms, bool ike_name)
203 {
204 this->lock->write_lock(this->lock);
205 free(this->time_format);
206 this->time_format = strdupnull(time_format);
207 this->add_ms = add_ms;
208 this->ike_name = ike_name;
209 this->lock->unlock(this->lock);
210 }
211
212 /**
213 * Close the current file, if any
214 */
215 static void close_file(private_file_logger_t *this)
216 {
217 if (this->out && this->out != stdout && this->out != stderr)
218 {
219 fclose(this->out);
220 this->out = NULL;
221 }
222 }
223
224 METHOD(file_logger_t, open_, void,
225 private_file_logger_t *this, bool flush_line, bool append)
226 {
227 FILE *file;
228
229 if (streq(this->filename, "stderr"))
230 {
231 file = stderr;
232 }
233 else if (streq(this->filename, "stdout"))
234 {
235 file = stdout;
236 }
237 else
238 {
239 file = fopen(this->filename, append ? "a" : "w");
240 if (file == NULL)
241 {
242 DBG1(DBG_DMN, "opening file %s for logging failed: %s",
243 this->filename, strerror(errno));
244 return;
245 }
246 #ifdef HAVE_CHOWN
247 if (lib->caps->check(lib->caps, CAP_CHOWN))
248 {
249 if (chown(this->filename, lib->caps->get_uid(lib->caps),
250 lib->caps->get_gid(lib->caps)) != 0)
251 {
252 DBG1(DBG_NET, "changing owner/group for '%s' failed: %s",
253 this->filename, strerror(errno));
254 }
255 }
256 else
257 {
258 if (chown(this->filename, -1, lib->caps->get_gid(lib->caps)) != 0)
259 {
260 DBG1(DBG_NET, "changing group for '%s' failed: %s",
261 this->filename, strerror(errno));
262 }
263 }
264 #endif /* HAVE_CHOWN */
265 #ifdef HAVE_SETLINEBUF
266 if (flush_line)
267 {
268 setlinebuf(file);
269 }
270 #endif /* HAVE_SETLINEBUF */
271 }
272 this->lock->write_lock(this->lock);
273 close_file(this);
274 this->out = file;
275 this->flush_line = flush_line;
276 this->lock->unlock(this->lock);
277 }
278
279 METHOD(file_logger_t, destroy, void,
280 private_file_logger_t *this)
281 {
282 this->lock->write_lock(this->lock);
283 close_file(this);
284 this->lock->unlock(this->lock);
285 this->mutex->destroy(this->mutex);
286 this->lock->destroy(this->lock);
287 free(this->time_format);
288 free(this->filename);
289 free(this);
290 }
291
292 /*
293 * Described in header.
294 */
295 file_logger_t *file_logger_create(char *filename)
296 {
297 private_file_logger_t *this;
298
299 INIT(this,
300 .public = {
301 .logger = {
302 .log = _log_,
303 .get_level = _get_level,
304 },
305 .set_level = _set_level,
306 .set_options = _set_options,
307 .open = _open_,
308 .destroy = _destroy,
309 },
310 .filename = strdup(filename),
311 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
312 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
313 );
314
315 set_level(this, DBG_ANY, LEVEL_SILENT);
316
317 return &this->public;
318 }