1 /* $Id: chanlog.c 341 2006-01-12 21:56:48Z tsaviran $
2 * -------------------------------------------------------
3 * Copyright (C) 2002-2006 Tommi Saviranta <wnd@iki.fi>
4 * (C) 2002 Lee Hardy <lee@leeh.co.uk>
5 * -------------------------------------------------------
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17 #ifdef HAVE_CONFIG_H
18 #include <config.h>
19 #endif /* ifdef HAVE_CONFIG_H */
20
21 #ifdef CHANLOG
22
23 #include "chanlog.h"
24 #include "llist.h"
25 #include "common.h"
26 #include "channels.h"
27 #include "client.h"
28 #include "miau.h"
29 #include "tools.h"
30 #include "messages.h"
31 #include "etc.h"
32 #include "log.h"
33
34 #include <stdio.h>
35 #include <stdarg.h>
36 #include <string.h>
37
38
39
40 int global_logtype;
41 llist_list chanlog_list;
42
43
44
45 /*
46 * Creates a logging entry in log_list.
47 */
48 void
chanlog_add_rule(char * channels,char * file,int type)49 chanlog_add_rule(char *channels, char *file, int type)
50 {
51 char *chan;
52 int multi = 0;
53
54 /* Nothing to log. */
55 if (channels == NULL || channels[0] == '\0') {
56 return;
57 }
58
59 /*
60 * The "global" logentry, mark the logtype and then open logfiles
61 * for channels we dont have specific entries for.
62 */
63 if (xstrcmp(channels, "*") == 0) {
64 global_logtype = type;
65 return;
66 }
67
68 /*
69 * If we're doing multi channels, mark as such so we don't use a
70 * specified logfile, revert to #channel.log.
71 */
72 if (strchr(channels, ',') != NULL) {
73 multi = 1;
74 }
75
76 for (chan = strtok(channels, ","); chan != NULL;
77 chan = strtok(NULL, ",")) {
78 llist_node *node;
79 struct chanlogentry *logptr;
80
81 /* Check we haven't already done this channel */
82 for (node = chanlog_list.head; node != NULL;
83 node = node->next) {
84 logptr = (struct chanlogentry *) node->data;
85 if (xstrcasecmp(chan, logptr->channel) == 0) {
86 return;
87 }
88 }
89
90 logptr = (struct chanlogentry *)
91 xcalloc(sizeof(struct chanlogentry), 1);
92 node = llist_create(logptr);
93 llist_add(node, &chanlog_list);
94
95 logptr->channel = xstrdup(chan);
96 logptr->type = type;
97
98 /*
99 * If we were not given a logfilename or we're creating more
100 * than one entry at once, create a logfilename.
101 */
102 if (file == NULL || multi) {
103 char *file;
104 size_t size;
105 /* termination and validity guaranteed */
106 size = strlen(LOGDIR) + strlen(chan)
107 + strlen(cfg.logsuffix) + 3;
108 file = (char *) xmalloc(size);
109 snprintf(file, size, LOGDIR"/%s%s",
110 chan, cfg.logsuffix);
111 file[size - 1] = '\0';
112 logptr->filename = file;
113 } else { /* basically "if (file != NULL)" */
114 size_t size;
115 /* If filename is relative, add LOGDIR. */
116 /* termination and validity guaranteed */
117 size = strlen(LOGDIR) + strlen(file) + 3;
118 logptr->filename = (char *) xmalloc(size);
119 snprintf(logptr->filename, size, LOGDIR"/%s",
120 file);
121 logptr->filename[size - 1] = '\0';
122 }
123 }
124 } /* void chanlog_add_rule(char *channels, char *file, int type) */
125
126
127
128 /*
129 * Delete all logging rules.
130 */
131 void
chanlog_del_rules(void)132 chanlog_del_rules(void)
133 {
134 LLIST_WALK_H(chanlog_list.head, struct chanlogentry *);
135 xfree(data->channel);
136 xfree(data->filename);
137 xfree(data);
138
139 llist_delete(node, &chanlog_list);
140 LLIST_WALK_F;
141
142 } /* void chanlog_del_rules(void) */
143
144
145
146 /*
147 * Open a logfile.
148 */
149 void
chanlog_open(channel_type * channel)150 chanlog_open(channel_type *channel)
151 {
152 /*
153 * Should have no need for this.
154 *
155 if (channel->log->logfile != NULL) {
156 return;
157 }
158 */
159
160 /* See if a rule applies directly to this channel. */
161 LLIST_WALK_H(chanlog_list.head, struct chanlogentry *);
162 if (xstrcasecmp(channel->simple_name, data->channel) == 0) {
163 channel->log = (struct channel_log *)
164 xcalloc(sizeof(struct channel_log), 1);
165 /* filename termination and validity guaranteed */
166 channel->log->file = fopen(data->filename, "a");
167
168 if (channel->log->file == NULL) {
169 log_cannot_write(data->filename);
170 return;
171 }
172
173 channel->log->type = data->type;
174 break;
175 }
176 LLIST_WALK_F;
177
178 /* Didn't find a direct match. Use global logtype. */
179 if (channel->log == NULL && global_logtype) {
180 size_t plen;
181 char *p;
182 char *lowchan;
183
184 /* convert simple channel name to lowercase */
185 lowchan = xstrdup(channel->simple_name);
186 lowcase(lowchan);
187
188 channel->log = (struct channel_log *)
189 xcalloc(sizeof(struct channel_log), 1);
190
191 /* termination and validity guaranteed */
192 plen = strlen(LOGDIR) + strlen(lowchan)
193 + strlen(cfg.logsuffix) + 3;
194 p = (char *) xmalloc(plen);
195 snprintf(p, plen, LOGDIR"/%s%s", lowchan, cfg.logsuffix);
196 p[plen - 1] = '\0';
197 channel->log->file = fopen(p, "a");
198 xfree(p);
199 xfree(lowchan);
200
201 if (channel->log->file == NULL) {
202 return;
203 }
204
205 channel->log->type = global_logtype;
206 }
207
208 /* Still unaware where to log ? */
209 if (channel->log == NULL) {
210 return;
211 }
212
213 /* ...and start logging. */
214 chanlog_write_entry(channel, LOGM_LOGOPEN,
215 get_timestamp(NULL, TIMESTAMP_LONG));
216 } /* void chanlog_open(channel_type *channel) */
217
218
219
220 /*
221 * Close logfile and free resources.
222 */
223 void
chanlog_close(channel_type * channel)224 chanlog_close(channel_type *channel)
225 {
226 if (channel->log != NULL) {
227 chanlog_write_entry(channel, LOGM_LOGCLOSE,
228 get_timestamp(NULL, TIMESTAMP_LONG));
229 if (channel->log->file != NULL) {
230 fclose(channel->log->file);
231 }
232
233 FREE(channel->log);
234 }
235 } /* void chanlog_close(channel_type *channel) */
236
237
238
239 /*
240 * Writes a logging entry to the logfile.
241 */
242 void
chanlog_write_entry(channel_type * chptr,char * format,...)243 chanlog_write_entry(channel_type *chptr, char *format, ...)
244 {
245 char buffer[1024];
246 va_list va;
247
248 /* No logfile for this channel. */
249 if (chptr->log == NULL || chptr->log->file == NULL) {
250 return;
251 }
252
253 /* Probably not logging when client is attached/detached. */
254 if ((c_clients.connected == 0 &&
255 ! (chptr->log->type & LOG_DETACHED)) ||
256 (c_clients.connected > 0 &&
257 ! (chptr->log->type & LOG_ATTACHED))) {
258 return;
259 }
260
261 va_start(va, format);
262 vsnprintf(buffer, 1024, format, va);
263 va_end(va);
264 buffer[1023] = '\0';
265
266 fprintf(chptr->log->file, "%s", buffer);
267 fflush(chptr->log->file);
268 } /* void chanlog_write_entry(channel_type *chptr, char *format, ...) */
269
270
271
272 /*
273 * Writes a logging entry to all matching logfiles.
274 */
275 void
chanlog_write_entry_all(int type,char * format,...)276 chanlog_write_entry_all(int type, char *format, ...)
277 {
278 char buffer[1024];
279 va_list va;
280
281 va_start(va, format);
282 vsnprintf(buffer, 1024, format, va);
283 va_end(va);
284 buffer[1023] = '\0';
285
286 LLIST_WALK_H(active_channels.head, channel_type *);
287 /*
288 * We could have it this way...
289 *
290 if (data->log == NULL || data->log->file == NULL) {
291 LLIST_WALK_CONTINUE;
292 }
293
294 if (data->log->type & type) {
295 log_write_entry(data, "%s", buffer);
296 }
297 *
298 * ...but this should compile shorter. ;-)
299 */
300 if (! (data->log == NULL || data->log->file == NULL)
301 && data->log->type & type) {
302 chanlog_write_entry(data, "%s", buffer);
303 }
304 LLIST_WALK_F;
305 } /* void chanlog_write_entry_all(int type, char *format, ...) */
306
307
308
309 int
chanlog_has_log(const channel_type * chan,int type)310 chanlog_has_log(const channel_type *chan, int type)
311 {
312 if (chan == NULL || chan->log == NULL) {
313 return 0;
314 } else {
315 return (chan->log->type & type) == type;
316 }
317 } /* int chanlog_has_log(const channel_type *chan, int type) */
318
319
320
321 #endif /* ifdef CHANLOG */
322