4 Copyright (C) 1999-2000 Timo Sirainen
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.
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.
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
28 #include "write-buffer.h"
30 #include "lib-config/iconfig.h"
33 #define DEFAULT_LOG_FILE_CREATE_MODE 600
36 static struct flock lock;
41 static const char *log_item_types[] = {
48 const char *log_timestamp;
49 static int log_file_create_mode;
50 static int log_dir_create_mode;
51 static int rotate_tag;
53 static int log_item_str2type(const char *type)
57 for (n = 0; log_item_types[n] != NULL; n++) {
58 if (g_strcasecmp(log_item_types[n], type) == 0)
65 static void log_write_timestamp(int handle, const char *format,
66 const char *text, time_t stamp)
71 g_return_if_fail(format != NULL);
72 if (*format == '\0') return;
74 tm = localtime(&stamp);
75 if (strftime(str, sizeof(str), format, tm) > 0)
76 write_buffer(handle, str, strlen(str));
77 if (text != NULL) write_buffer(handle, text, strlen(text));
80 static char *log_filename(LOG_REC *log)
82 char *str, fname[1024];
90 str = convert_home(log->fname);
91 ret = strftime(fname, sizeof(fname), str, tm);
95 g_warning("log_filename() : strftime() failed");
99 return g_strdup(fname);
102 int log_start_logging(LOG_REC *log)
106 g_return_val_if_fail(log != NULL, FALSE);
108 if (log->handle != -1)
111 /* Append/create log file */
112 g_free_not_null(log->real_fname);
113 log->real_fname = log_filename(log);
115 if (log->real_fname != NULL &&
116 strcmp(log->real_fname, log->fname) != 0) {
117 /* path may contain variables (%time, $vars),
118 make sure the directory is created */
119 dir = g_dirname(log->real_fname);
120 mkpath(dir, log_dir_create_mode);
124 log->handle = log->real_fname == NULL ? -1 :
125 open(log->real_fname, O_WRONLY | O_APPEND | O_CREAT,
126 log_file_create_mode);
127 if (log->handle == -1) {
128 signal_emit("log create failed", 1, log);
133 memset(&lock, 0, sizeof(lock));
134 lock.l_type = F_WRLCK;
135 if (fcntl(log->handle, F_SETLK, &lock) == -1 && errno == EACCES) {
138 signal_emit("log locked", 1, log);
143 lseek(log->handle, 0, SEEK_END);
145 log->opened = log->last = time(NULL);
146 log_write_timestamp(log->handle,
147 settings_get_str("log_open_string"),
150 signal_emit("log started", 1, log);
155 void log_stop_logging(LOG_REC *log)
157 g_return_if_fail(log != NULL);
159 if (log->handle == -1)
162 signal_emit("log stopped", 1, log);
164 log_write_timestamp(log->handle,
165 settings_get_str("log_close_string"),
169 memset(&lock, 0, sizeof(lock));
170 lock.l_type = F_UNLCK;
171 fcntl(log->handle, F_SETLK, &lock);
174 write_buffer_flush();
179 static void log_rotate_check(LOG_REC *log)
183 g_return_if_fail(log != NULL);
185 if (log->handle == -1 || log->real_fname == NULL)
188 new_fname = log_filename(log);
189 if (strcmp(new_fname, log->real_fname) != 0) {
191 log_stop_logging(log);
192 signal_emit("log rotated", 1, log);
194 log_start_logging(log);
199 void log_write_rec(LOG_REC *log, const char *str, int level)
206 g_return_if_fail(log != NULL);
207 g_return_if_fail(str != NULL);
209 if (log->handle == -1)
213 tm = localtime(&now);
217 tm = localtime(&log->last);
218 day -= tm->tm_mday; /* tm breaks in log_rotate_check() .. */
219 if (tm->tm_hour != hour) {
220 /* hour changed, check if we need to rotate log file */
221 log_rotate_check(log);
226 log_write_timestamp(log->handle,
227 settings_get_str("log_day_changed"),
233 if (log->colorizer == NULL)
236 str = colorstr = log->colorizer(str);
238 if ((level & MSGLEVEL_LASTLOG) == 0)
239 log_write_timestamp(log->handle, log_timestamp, str, now);
241 write_buffer(log->handle, str, strlen(str));
242 write_buffer(log->handle, "\n", 1);
244 signal_emit("log written", 2, log, str);
246 g_free_not_null(colorstr);
249 static int itemcmp(const char *patt, const char *item)
251 /* returns 0 on match, nonzero otherwise */
254 return g_strcasecmp(patt, "*") != 0;
257 return g_strcasecmp(patt, item);
260 LOG_ITEM_REC *log_item_find(LOG_REC *log, int type, const char *item,
261 const char *servertag)
265 g_return_val_if_fail(log != NULL, NULL);
267 for (tmp = log->items; tmp != NULL; tmp = tmp->next) {
268 LOG_ITEM_REC *rec = tmp->data;
270 if (rec->type == type && itemcmp(rec->name, item) == 0 &&
271 (rec->servertag == NULL || (servertag != NULL &&
272 g_strcasecmp(rec->servertag, servertag) == 0)))
279 void log_file_write(const char *server_tag, const char *item, int level,
280 const char *str, int no_fallbacks)
282 GSList *tmp, *fallbacks;
286 g_return_if_fail(str != NULL);
291 fallbacks = NULL; found = FALSE;
293 for (tmp = logs; tmp != NULL; tmp = tmp->next) {
294 LOG_REC *rec = tmp->data;
296 if (rec->handle == -1)
297 continue; /* log not opened yet */
299 if ((level & rec->level) == 0)
302 if (rec->items == NULL)
303 fallbacks = g_slist_append(fallbacks, rec);
304 else if (item != NULL &&
305 log_item_find(rec, LOG_ITEM_TARGET, item,
307 log_write_rec(rec, str, level);
310 if (!found && !no_fallbacks && fallbacks != NULL) {
311 /* not found from any items, so write it to all main logs */
312 tmpstr = (level & MSGLEVEL_PUBLIC) && item != NULL ?
313 g_strconcat(item, ": ", str, NULL) :
316 for (tmp = fallbacks; tmp != NULL; tmp = tmp->next)
317 log_write_rec(tmp->data, tmpstr, level);
321 g_slist_free(fallbacks);
324 LOG_REC *log_find(const char *fname)
328 for (tmp = logs; tmp != NULL; tmp = tmp->next) {
329 LOG_REC *rec = tmp->data;
331 if (strcmp(rec->fname, fname) == 0)
338 static void log_items_update_config(LOG_REC *log, CONFIG_NODE *parent)
343 parent = config_node_section(parent, "items", NODE_TYPE_LIST);
344 for (tmp = log->items; tmp != NULL; tmp = tmp->next) {
345 LOG_ITEM_REC *rec = tmp->data;
347 node = config_node_section(parent, NULL, NODE_TYPE_BLOCK);
348 iconfig_node_set_str(node, "type", log_item_types[rec->type]);
349 iconfig_node_set_str(node, "name", rec->name);
350 iconfig_node_set_str(node, "server", rec->servertag);
354 static void log_update_config(LOG_REC *log)
362 node = iconfig_node_traverse("logs", TRUE);
363 node = config_node_section(node, log->fname, NODE_TYPE_BLOCK);
366 iconfig_node_set_bool(node, "auto_open", TRUE);
368 iconfig_node_set_str(node, "auto_open", NULL);
370 levelstr = bits2level(log->level);
371 iconfig_node_set_str(node, "level", levelstr);
374 iconfig_node_set_str(node, "items", NULL);
376 if (log->items != NULL)
377 log_items_update_config(log, node);
379 signal_emit("log config save", 2, log, node);
382 static void log_remove_config(LOG_REC *log)
384 iconfig_set_str("logs", log->fname, NULL);
387 LOG_REC *log_create_rec(const char *fname, int level)
391 g_return_val_if_fail(fname != NULL, NULL);
393 rec = log_find(fname);
395 rec = g_new0(LOG_REC, 1);
396 rec->fname = g_strdup(fname);
397 rec->real_fname = log_filename(rec);
405 void log_item_add(LOG_REC *log, int type, const char *name,
406 const char *servertag)
410 g_return_if_fail(log != NULL);
411 g_return_if_fail(name != NULL);
413 if (log_item_find(log, type, name, servertag))
416 rec = g_new0(LOG_ITEM_REC, 1);
418 rec->name = g_strdup(name);
419 rec->servertag = g_strdup(servertag);
421 log->items = g_slist_append(log->items, rec);
424 void log_update(LOG_REC *log)
426 g_return_if_fail(log != NULL);
428 if (log_find(log->fname) == NULL) {
429 logs = g_slist_append(logs, log);
433 log_update_config(log);
434 signal_emit("log new", 1, log);
437 void log_item_destroy(LOG_REC *log, LOG_ITEM_REC *item)
439 log->items = g_slist_remove(log->items, item);
442 g_free_not_null(item->servertag);
446 static void log_destroy(LOG_REC *log)
448 g_return_if_fail(log != NULL);
450 if (log->handle != -1)
451 log_stop_logging(log);
453 logs = g_slist_remove(logs, log);
454 signal_emit("log remove", 1, log);
456 while (log->items != NULL)
457 log_item_destroy(log, log->items->data);
459 g_free_not_null(log->real_fname);
463 void log_close(LOG_REC *log)
465 g_return_if_fail(log != NULL);
467 log_remove_config(log);
471 static int sig_rotate_check(void)
473 static int last_hour = -1;
477 /* don't do anything until hour is changed */
479 memcpy(&tm, localtime(&now), sizeof(tm));
480 if (tm.tm_hour != last_hour) {
481 last_hour = tm.tm_hour;
482 g_slist_foreach(logs, (GFunc) log_rotate_check, NULL);
487 static void log_items_read_config(CONFIG_NODE *node, LOG_REC *log)
494 tmp = config_node_first(node->value);
495 for (; tmp != NULL; tmp = config_node_next(tmp)) {
498 if (node->type != NODE_TYPE_BLOCK)
501 item = config_node_get_str(node, "name", NULL);
502 type = log_item_str2type(config_node_get_str(node, "type", NULL));
503 if (item == NULL || type == -1)
506 rec = g_new0(LOG_ITEM_REC, 1);
508 rec->name = g_strdup(item);
509 rec->servertag = g_strdup(config_node_get_str(node, "server", NULL));
511 log->items = g_slist_append(log->items, rec);
515 static void log_read_config(void)
519 GSList *tmp, *next, *fnames;
521 /* close old logs, save list of open logs */
523 for (tmp = logs; tmp != NULL; tmp = next) {
530 if (log->handle != -1)
531 fnames = g_slist_append(fnames, g_strdup(log->fname));
535 node = iconfig_node_traverse("logs", FALSE);
536 if (node == NULL) return;
538 tmp = config_node_first(node->value);
539 for (; tmp != NULL; tmp = config_node_next(tmp)) {
542 if (node->type != NODE_TYPE_BLOCK)
545 log = g_new0(LOG_REC, 1);
546 logs = g_slist_append(logs, log);
549 log->fname = g_strdup(node->key);
550 log->autoopen = config_node_get_bool(node, "auto_open", FALSE);
551 log->level = level2bits(config_node_get_str(node, "level", 0));
553 signal_emit("log config read", 2, log, node);
555 node = config_node_section(node, "items", -1);
557 log_items_read_config(node, log);
559 if (log->autoopen || gslist_find_string(fnames, log->fname))
560 log_start_logging(log);
563 g_slist_foreach(fnames, (GFunc) g_free, NULL);
564 g_slist_free(fnames);
567 static void read_settings(void)
569 log_timestamp = settings_get_str("log_timestamp");
570 log_file_create_mode = octal2dec(settings_get_int("log_create_mode"));
572 log_dir_create_mode = log_file_create_mode;
573 if (log_file_create_mode & 0400) log_dir_create_mode |= 0100;
574 if (log_file_create_mode & 0040) log_dir_create_mode |= 0010;
575 if (log_file_create_mode & 0004) log_dir_create_mode |= 0001;
580 rotate_tag = g_timeout_add(60000, (GSourceFunc) sig_rotate_check, NULL);
583 settings_add_int("log", "log_create_mode",
584 DEFAULT_LOG_FILE_CREATE_MODE);
585 settings_add_str("log", "log_timestamp", "%H:%M ");
586 settings_add_str("log", "log_open_string",
587 "--- Log opened %a %b %d %H:%M:%S %Y");
588 settings_add_str("log", "log_close_string",
589 "--- Log closed %a %b %d %H:%M:%S %Y");
590 settings_add_str("log", "log_day_changed",
591 "--- Day changed %a %b %d %Y");
594 signal_add("setup changed", (SIGNAL_FUNC) read_settings);
595 signal_add("setup reread", (SIGNAL_FUNC) log_read_config);
596 signal_add("irssi init finished", (SIGNAL_FUNC) log_read_config);
599 void log_deinit(void)
601 g_source_remove(rotate_tag);
604 log_close(logs->data);
606 signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
607 signal_remove("setup reread", (SIGNAL_FUNC) log_read_config);
608 signal_remove("irssi init finished", (SIGNAL_FUNC) log_read_config);