New silcconfig library and server parser. Merged silc-newconfig-final.patch.
[crypto.git] / apps / irssi / src / core / log.c
1 /*
2  log.c : irssi
3
4     Copyright (C) 1999-2000 Timo Sirainen
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     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "module.h"
22 #include "signals.h"
23 #include "commands.h"
24 #include "levels.h"
25 #include "misc.h"
26 #include "servers.h"
27 #include "log.h"
28 #include "write-buffer.h"
29
30 #include "lib-config/iconfig.h"
31 #include "settings.h"
32
33 #define DEFAULT_LOG_FILE_CREATE_MODE 600
34
35 #ifdef HAVE_FCNTL
36 static struct flock lock;
37 #endif
38
39 GSList *logs;
40
41 static const char *log_item_types[] = {
42         "target",
43         "window",
44
45         NULL
46 };
47
48 const char *log_timestamp;
49 static int log_file_create_mode;
50 static int rotate_tag;
51
52 static int log_item_str2type(const char *type)
53 {
54         int n;
55
56         for (n = 0; log_item_types[n] != NULL; n++) {
57                 if (g_strcasecmp(log_item_types[n], type) == 0)
58                         return n;
59         }
60
61         return -1;
62 }
63
64 static void log_write_timestamp(int handle, const char *format,
65                                 const char *text, time_t stamp)
66 {
67         struct tm *tm;
68         char str[256];
69
70         g_return_if_fail(format != NULL);
71         if (*format == '\0') return;
72
73         tm = localtime(&stamp);
74         if (strftime(str, sizeof(str), format, tm) > 0)
75                 write_buffer(handle, str, strlen(str));
76         if (text != NULL) write_buffer(handle, text, strlen(text));
77 }
78
79 static char *log_filename(LOG_REC *log)
80 {
81         char *str, fname[1024];
82         struct tm *tm;
83         size_t ret;
84         time_t now;
85
86         now = time(NULL);
87         tm = localtime(&now);
88
89         str = convert_home(log->fname);
90         ret = strftime(fname, sizeof(fname), str, tm);
91         g_free(str);
92
93         if (ret <= 0) {
94                 g_warning("log_filename() : strftime() failed");
95                 return NULL;
96         }
97
98         return g_strdup(fname);
99 }
100
101 int log_start_logging(LOG_REC *log)
102 {
103         g_return_val_if_fail(log != NULL, FALSE);
104
105         if (log->handle != -1)
106                 return TRUE;
107
108         /* Append/create log file */
109         g_free_not_null(log->real_fname);
110         log->real_fname = log_filename(log);
111         log->handle = log->real_fname == NULL ? -1 :
112                 open(log->real_fname, O_WRONLY | O_APPEND | O_CREAT,
113                      log_file_create_mode);
114         if (log->handle == -1) {
115                 signal_emit("log create failed", 1, log);
116                 log->failed = TRUE;
117                 return FALSE;
118         }
119 #ifdef HAVE_FCNTL
120         memset(&lock, 0, sizeof(lock));
121         lock.l_type = F_WRLCK;
122         if (fcntl(log->handle, F_SETLK, &lock) == -1 && errno == EACCES) {
123                 close(log->handle);
124                 log->handle = -1;
125                 signal_emit("log locked", 1, log);
126                 log->failed = TRUE;
127                 return FALSE;
128         }
129 #endif
130         lseek(log->handle, 0, SEEK_END);
131
132         log->opened = log->last = time(NULL);
133         log_write_timestamp(log->handle,
134                             settings_get_str("log_open_string"),
135                             "\n", log->last);
136
137         signal_emit("log started", 1, log);
138         log->failed = FALSE;
139         return TRUE;
140 }
141
142 void log_stop_logging(LOG_REC *log)
143 {
144         g_return_if_fail(log != NULL);
145
146         if (log->handle == -1)
147                 return;
148
149         signal_emit("log stopped", 1, log);
150
151         log_write_timestamp(log->handle,
152                             settings_get_str("log_close_string"),
153                             "\n", time(NULL));
154
155 #ifdef HAVE_FCNTL
156         memset(&lock, 0, sizeof(lock));
157         lock.l_type = F_UNLCK;
158         fcntl(log->handle, F_SETLK, &lock);
159 #endif
160
161         write_buffer_flush();
162         close(log->handle);
163         log->handle = -1;
164 }
165
166 static void log_rotate_check(LOG_REC *log)
167 {
168         char *new_fname, *dir;
169
170         g_return_if_fail(log != NULL);
171
172         if (log->handle == -1 || log->real_fname == NULL)
173                 return;
174
175         new_fname = log_filename(log);
176         if (strcmp(new_fname, log->real_fname) != 0) {
177                 /* rotate log */
178                 log_stop_logging(log);
179                 signal_emit("log rotated", 1, log);
180
181                 dir = g_dirname(new_fname);
182                 mkpath(dir, LOG_DIR_CREATE_MODE);
183                 g_free(dir);
184
185                 log_start_logging(log);
186         }
187         g_free(new_fname);
188 }
189
190 void log_write_rec(LOG_REC *log, const char *str, int level)
191 {
192         char *colorstr;
193         struct tm *tm;
194         time_t now;
195         int hour, day;
196
197         g_return_if_fail(log != NULL);
198         g_return_if_fail(str != NULL);
199
200         if (log->handle == -1)
201                 return;
202
203         now = time(NULL);
204         tm = localtime(&now);
205         hour = tm->tm_hour;
206         day = tm->tm_mday;
207
208         tm = localtime(&log->last);
209         day -= tm->tm_mday; /* tm breaks in log_rotate_check() .. */
210         if (tm->tm_hour != hour) {
211                 /* hour changed, check if we need to rotate log file */
212                 log_rotate_check(log);
213         }
214
215         if (day != 0) {
216                 /* day changed */
217                 log_write_timestamp(log->handle,
218                                     settings_get_str("log_day_changed"),
219                                     "\n", now);
220         }
221
222         log->last = now;
223
224         if (log->colorizer == NULL)
225                 colorstr = NULL;
226         else
227                 str = colorstr = log->colorizer(str);
228
229         if ((level & MSGLEVEL_LASTLOG) == 0)
230                 log_write_timestamp(log->handle, log_timestamp, str, now);
231         else
232                 write_buffer(log->handle, str, strlen(str));
233         write_buffer(log->handle, "\n", 1);
234
235         signal_emit("log written", 2, log, str);
236
237         g_free_not_null(colorstr);
238 }
239
240 LOG_ITEM_REC *log_item_find(LOG_REC *log, int type, const char *item,
241                             const char *servertag)
242 {
243         GSList *tmp;
244
245         g_return_val_if_fail(log != NULL, NULL);
246         g_return_val_if_fail(item != NULL, NULL);
247
248         for (tmp = log->items; tmp != NULL; tmp = tmp->next) {
249                 LOG_ITEM_REC *rec = tmp->data;
250
251                 if (rec->type == type && g_strcasecmp(rec->name, item) == 0 &&
252                     (rec->servertag == NULL || (servertag != NULL &&
253                         g_strcasecmp(rec->servertag, servertag) == 0)))
254                         return rec;
255         }
256
257         return NULL;
258 }
259
260 void log_file_write(const char *server_tag, const char *item, int level,
261                     const char *str, int no_fallbacks)
262 {
263         GSList *tmp, *fallbacks;
264         char *tmpstr;
265         int found;
266
267         g_return_if_fail(str != NULL);
268
269         if (logs == NULL)
270                 return;
271
272         fallbacks = NULL; found = FALSE;
273
274         for (tmp = logs; tmp != NULL; tmp = tmp->next) {
275                 LOG_REC *rec = tmp->data;
276
277                 if (rec->handle == -1)
278                         continue; /* log not opened yet */
279
280                 if ((level & rec->level) == 0)
281                         continue;
282
283                 if (rec->items == NULL)
284                         fallbacks = g_slist_append(fallbacks, rec);
285                 else if (item != NULL &&
286                          log_item_find(rec, LOG_ITEM_TARGET, item,
287                                        server_tag) != NULL)
288                         log_write_rec(rec, str, level);
289         }
290
291         if (!found && !no_fallbacks && fallbacks != NULL) {
292                 /* not found from any items, so write it to all main logs */
293                 tmpstr = (level & MSGLEVEL_PUBLIC) ?
294                         g_strconcat(item, ": ", str, NULL) :
295                         g_strdup(str);
296
297                 for (tmp = fallbacks; tmp != NULL; tmp = tmp->next)
298                         log_write_rec(tmp->data, tmpstr, level);
299
300                 g_free(tmpstr);
301         }
302         g_slist_free(fallbacks);
303 }
304
305 LOG_REC *log_find(const char *fname)
306 {
307         GSList *tmp;
308
309         for (tmp = logs; tmp != NULL; tmp = tmp->next) {
310                 LOG_REC *rec = tmp->data;
311
312                 if (strcmp(rec->fname, fname) == 0)
313                         return rec;
314         }
315
316         return NULL;
317 }
318
319 static void log_items_update_config(LOG_REC *log, CONFIG_NODE *parent)
320 {
321         GSList *tmp;
322         CONFIG_NODE *node;
323
324         parent = config_node_section(parent, "items", NODE_TYPE_LIST);
325         for (tmp = log->items; tmp != NULL; tmp = tmp->next) {
326                 LOG_ITEM_REC *rec = tmp->data;
327
328                 node = config_node_section(parent, NULL, NODE_TYPE_BLOCK);
329                 iconfig_node_set_str(node, "type", log_item_types[rec->type]);
330                 iconfig_node_set_str(node, "name", rec->name);
331                 iconfig_node_set_str(node, "server", rec->servertag);
332         }
333 }
334
335 static void log_update_config(LOG_REC *log)
336 {
337         CONFIG_NODE *node;
338         char *levelstr;
339
340         if (log->temp)
341                 return;
342
343         node = iconfig_node_traverse("logs", TRUE);
344         node = config_node_section(node, log->fname, NODE_TYPE_BLOCK);
345
346         if (log->autoopen)
347                 iconfig_node_set_bool(node, "auto_open", TRUE);
348         else
349                 iconfig_node_set_str(node, "auto_open", NULL);
350
351         levelstr = bits2level(log->level);
352         iconfig_node_set_str(node, "level", levelstr);
353         g_free(levelstr);
354
355         iconfig_node_set_str(node, "items", NULL);
356
357         if (log->items != NULL)
358                 log_items_update_config(log, node);
359
360         signal_emit("log config save", 2, log, node);
361 }
362
363 static void log_remove_config(LOG_REC *log)
364 {
365         iconfig_set_str("logs", log->fname, NULL);
366 }
367
368 LOG_REC *log_create_rec(const char *fname, int level)
369 {
370         LOG_REC *rec;
371
372         g_return_val_if_fail(fname != NULL, NULL);
373
374         rec = log_find(fname);
375         if (rec == NULL) {
376                 rec = g_new0(LOG_REC, 1);
377                 rec->fname = g_strdup(fname);
378                 rec->real_fname = log_filename(rec);
379                 rec->handle = -1;
380         }
381
382         rec->level = level;
383         return rec;
384 }
385
386 void log_item_add(LOG_REC *log, int type, const char *name,
387                   const char *servertag)
388 {
389         LOG_ITEM_REC *rec;
390
391         g_return_if_fail(log != NULL);
392         g_return_if_fail(name != NULL);
393
394         if (log_item_find(log, type, name, servertag))
395                 return;
396
397         rec = g_new0(LOG_ITEM_REC, 1);
398         rec->type = type;
399         rec->name = g_strdup(name);
400         rec->servertag = g_strdup(servertag);
401
402         log->items = g_slist_append(log->items, rec);
403 }
404
405 void log_update(LOG_REC *log)
406 {
407         g_return_if_fail(log != NULL);
408
409         if (log_find(log->fname) == NULL) {
410                 logs = g_slist_append(logs, log);
411                 log->handle = -1;
412         }
413
414         log_update_config(log);
415         signal_emit("log new", 1, log);
416 }
417
418 void log_item_destroy(LOG_REC *log, LOG_ITEM_REC *item)
419 {
420         log->items = g_slist_remove(log->items, item);
421
422         g_free(item->name);
423         g_free_not_null(item->servertag);
424         g_free(item);
425 }
426
427 static void log_destroy(LOG_REC *log)
428 {
429         g_return_if_fail(log != NULL);
430
431         if (log->handle != -1)
432                 log_stop_logging(log);
433
434         logs = g_slist_remove(logs, log);
435         signal_emit("log remove", 1, log);
436
437         while (log->items != NULL)
438                 log_item_destroy(log, log->items->data);
439         g_free(log->fname);
440         g_free_not_null(log->real_fname);
441         g_free(log);
442 }
443
444 void log_close(LOG_REC *log)
445 {
446         g_return_if_fail(log != NULL);
447
448         log_remove_config(log);
449         log_destroy(log);
450 }
451
452 static int sig_rotate_check(void)
453 {
454         static int last_hour = -1;
455         struct tm tm;
456         time_t now;
457
458         /* don't do anything until hour is changed */
459         now = time(NULL);
460         memcpy(&tm, localtime(&now), sizeof(tm));
461         if (tm.tm_hour != last_hour) {
462                 last_hour = tm.tm_hour;
463                 g_slist_foreach(logs, (GFunc) log_rotate_check, NULL);
464         }
465         return 1;
466 }
467
468 static void log_items_read_config(CONFIG_NODE *node, LOG_REC *log)
469 {
470         LOG_ITEM_REC *rec;
471         GSList *tmp;
472         char *item;
473         int type;
474
475         tmp = config_node_first(node->value);
476         for (; tmp != NULL; tmp = config_node_next(tmp)) {
477                 node = tmp->data;
478
479                 if (node->type != NODE_TYPE_BLOCK)
480                         continue;
481
482                 item = config_node_get_str(node, "name", NULL);
483                 type = log_item_str2type(config_node_get_str(node, "type", NULL));
484                 if (item == NULL || type == -1)
485                         continue;
486
487                 rec = g_new0(LOG_ITEM_REC, 1);
488                 rec->type = type;
489                 rec->name = g_strdup(item);
490                 rec->servertag = g_strdup(config_node_get_str(node, "server", NULL));
491
492                 log->items = g_slist_append(log->items, rec);
493         }
494 }
495
496 static void log_read_config(void)
497 {
498         CONFIG_NODE *node;
499         LOG_REC *log;
500         GSList *tmp, *next, *fnames;
501
502         /* close old logs, save list of open logs */
503         fnames = NULL;
504         for (tmp = logs; tmp != NULL; tmp = next) {
505                 log = tmp->data;
506
507                 next = tmp->next;
508                 if (log->temp)
509                         continue;
510
511                 if (log->handle != -1)
512                         fnames = g_slist_append(fnames, g_strdup(log->fname));
513                 log_destroy(log);
514         }
515
516         node = iconfig_node_traverse("logs", FALSE);
517         if (node == NULL) return;
518
519         tmp = config_node_first(node->value);
520         for (; tmp != NULL; tmp = config_node_next(tmp)) {
521                 node = tmp->data;
522
523                 if (node->type != NODE_TYPE_BLOCK)
524                         continue;
525
526                 log = g_new0(LOG_REC, 1);
527                 logs = g_slist_append(logs, log);
528
529                 log->handle = -1;
530                 log->fname = g_strdup(node->key);
531                 log->autoopen = config_node_get_bool(node, "auto_open", FALSE);
532                 log->level = level2bits(config_node_get_str(node, "level", 0));
533
534                 signal_emit("log config read", 2, log, node);
535
536                 node = config_node_section(node, "items", -1);
537                 if (node != NULL)
538                         log_items_read_config(node, log);
539
540                 if (log->autoopen || gslist_find_string(fnames, log->fname))
541                         log_start_logging(log);
542         }
543
544         g_slist_foreach(fnames, (GFunc) g_free, NULL);
545         g_slist_free(fnames);
546 }
547
548 static void read_settings(void)
549 {
550         log_timestamp = settings_get_str("log_timestamp");
551         log_file_create_mode = octal2dec(settings_get_int("log_create_mode"));
552 }
553
554 void log_init(void)
555 {
556         rotate_tag = g_timeout_add(60000, (GSourceFunc) sig_rotate_check, NULL);
557         logs = NULL;
558
559         settings_add_int("log", "log_create_mode",
560                          DEFAULT_LOG_FILE_CREATE_MODE);
561         settings_add_str("log", "log_timestamp", "%H:%M ");
562         settings_add_str("log", "log_open_string",
563                          "--- Log opened %a %b %d %H:%M:%S %Y");
564         settings_add_str("log", "log_close_string",
565                          "--- Log closed %a %b %d %H:%M:%S %Y");
566         settings_add_str("log", "log_day_changed",
567                          "--- Day changed %a %b %d %Y");
568
569         read_settings();
570         signal_add("setup changed", (SIGNAL_FUNC) read_settings);
571         signal_add("setup reread", (SIGNAL_FUNC) log_read_config);
572         signal_add("irssi init finished", (SIGNAL_FUNC) log_read_config);
573 }
574
575 void log_deinit(void)
576 {
577         g_source_remove(rotate_tag);
578
579         while (logs != NULL)
580                 log_close(logs->data);
581
582         signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
583         signal_remove("setup reread", (SIGNAL_FUNC) log_read_config);
584         signal_remove("irssi init finished", (SIGNAL_FUNC) log_read_config);
585 }