irssi/src/fe-common/core/fe-log.c
Timo Sirainen bd6fe052bc Added time, size and level setting types. Breaks some settings - I'll add
automatic converter to these settings later. Meanwhile you CVS users can
fix your config files yourself :)

Time settings allow using "days", "hours", "minutes", "seconds" and
"milliseconds" or several of their abbreviations. For example "5d 4h
5msecs".

Size settings allow using "gbytes", "mbytes", "kbytes" and "bytes" or their
abbrevations. For example "5MB".

Level settings are currently handled pretty much the way they were before.


git-svn-id: http://svn.irssi.org/repos/irssi/trunk@3080 dbcabf3a-b0e7-0310-adc4-f8d773084564
2002-12-28 17:54:13 +00:00

763 lines
21 KiB
C

/*
fe-log.c : irssi
Copyright (C) 1999-2000 Timo Sirainen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "module.h"
#include "module-formats.h"
#include "signals.h"
#include "commands.h"
#include "chat-protocols.h"
#include "servers.h"
#include "levels.h"
#include "misc.h"
#include "log.h"
#include "special-vars.h"
#include "settings.h"
#include "lib-config/iconfig.h"
#include "fe-windows.h"
#include "window-items.h"
#include "formats.h"
#include "themes.h"
#include "printtext.h"
/* close autologs after 5 minutes of inactivity */
#define AUTOLOG_INACTIVITY_CLOSE (60*5)
static int autolog_level;
static int autoremove_tag;
static const char *autolog_path;
static THEME_REC *log_theme;
static int skip_next_printtext;
static const char *log_theme_name;
static int log_dir_create_mode;
static char *log_colorizer_strip(const char *str)
{
return strip_codes(str);
}
static void log_add_targets(LOG_REC *log, const char *targets, const char *tag)
{
char **tmp, **items;
g_return_if_fail(log != NULL);
g_return_if_fail(targets != NULL);
items = g_strsplit(targets, " ", -1);
for (tmp = items; *tmp != NULL; tmp++)
log_item_add(log, LOG_ITEM_TARGET, *tmp, tag);
g_strfreev(items);
}
/* SYNTAX: LOG OPEN [-noopen] [-autoopen] [-window] [-<server tag>]
[-targets <targets>] [-colors]
<fname> [<levels>] */
static void cmd_log_open(const char *data)
{
SERVER_REC *server;
GHashTable *optlist;
char *targetarg, *fname, *levels, *servertag;
void *free_arg;
char window[MAX_INT_STRLEN];
LOG_REC *log;
int level;
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_OPTIONS,
"log open", &optlist, &fname, &levels))
return;
if (*fname == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
level = level2bits(levels);
log = log_create_rec(fname, level != 0 ? level : MSGLEVEL_ALL);
/* -<server tag> */
server = cmd_options_get_server("log open", optlist, NULL);
servertag = server == NULL ? NULL : server->tag;
if (g_hash_table_lookup(optlist, "window")) {
/* log by window ref# */
targetarg = g_hash_table_lookup(optlist, "targets");
if (targetarg == NULL || !is_numeric(targetarg, '\0')) {
ltoa(window, active_win->refnum);
targetarg = window;
}
log_item_add(log, LOG_ITEM_WINDOW_REFNUM, targetarg,
servertag);
} else {
targetarg = g_hash_table_lookup(optlist, "targets");
if (targetarg != NULL && *targetarg != '\0')
log_add_targets(log, targetarg, servertag);
}
if (g_hash_table_lookup(optlist, "autoopen"))
log->autoopen = TRUE;
if (g_hash_table_lookup(optlist, "colors") == NULL)
log->colorizer = log_colorizer_strip;
log_update(log);
if (log->handle == -1 && g_hash_table_lookup(optlist, "noopen") == NULL) {
/* start logging */
if (log_start_logging(log)) {
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
TXT_LOG_OPENED, fname);
} else {
log_close(log);
}
}
cmd_params_free(free_arg);
}
static LOG_REC *log_find_from_data(const char *data)
{
GSList *tmp;
if (!is_numeric(data, ' '))
return log_find(data);
/* with index number */
tmp = g_slist_nth(logs, atoi(data)-1);
return tmp == NULL ? NULL : tmp->data;
}
/* SYNTAX: LOG CLOSE <id>|<file> */
static void cmd_log_close(const char *data)
{
LOG_REC *log;
log = log_find_from_data(data);
if (log == NULL)
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_LOG_NOT_OPEN, data);
else {
log_close(log);
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_CLOSED, data);
}
}
/* SYNTAX: LOG START <id>|<file> */
static void cmd_log_start(const char *data)
{
LOG_REC *log;
log = log_find_from_data(data);
if (log != NULL) {
log_start_logging(log);
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_OPENED, data);
}
}
/* SYNTAX: LOG STOP <id>|<file> */
static void cmd_log_stop(const char *data)
{
LOG_REC *log;
log = log_find_from_data(data);
if (log == NULL || log->handle == -1)
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_LOG_NOT_OPEN, data);
else {
log_stop_logging(log);
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_CLOSED, data);
}
}
static char *log_items_get_list(LOG_REC *log)
{
GSList *tmp;
GString *str;
char *ret;
g_return_val_if_fail(log != NULL, NULL);
g_return_val_if_fail(log->items != NULL, NULL);
str = g_string_new(NULL);
for (tmp = log->items; tmp != NULL; tmp = tmp->next) {
LOG_ITEM_REC *rec = tmp->data;
g_string_sprintfa(str, "%s, ", rec->name);
}
g_string_truncate(str, str->len-2);
ret = str->str;
g_string_free(str, FALSE);
return ret;
}
static void cmd_log_list(void)
{
GSList *tmp;
char *levelstr, *items;
int index;
printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_LOG_LIST_HEADER);
for (tmp = logs, index = 1; tmp != NULL; tmp = tmp->next, index++) {
LOG_REC *rec = tmp->data;
levelstr = bits2level(rec->level);
items = rec->items == NULL ? NULL :
log_items_get_list(rec);
printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_LOG_LIST,
index, rec->fname, items != NULL ? items : "",
levelstr, rec->autoopen ? " -autoopen" : "");
g_free_not_null(items);
g_free(levelstr);
}
printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_LOG_LIST_FOOTER);
}
static void cmd_log(const char *data, SERVER_REC *server, void *item)
{
if (*data == '\0')
cmd_log_list();
else
command_runsub("log", data, server, item);
}
static LOG_REC *logs_find_item(int type, const char *item,
const char *servertag, LOG_ITEM_REC **ret_item)
{
LOG_ITEM_REC *logitem;
GSList *tmp;
for (tmp = logs; tmp != NULL; tmp = tmp->next) {
LOG_REC *log = tmp->data;
logitem = log_item_find(log, type, item, servertag);
if (logitem != NULL) {
if (ret_item != NULL) *ret_item = logitem;
return log;
}
}
return NULL;
}
/* SYNTAX: WINDOW LOG on|off|toggle [<filename>] */
static void cmd_window_log(const char *data)
{
LOG_REC *log;
char *set, *fname, window[MAX_INT_STRLEN];
void *free_arg;
int open_log, close_log;
if (!cmd_get_params(data, &free_arg, 2, &set, &fname))
return;
ltoa(window, active_win->refnum);
log = logs_find_item(LOG_ITEM_WINDOW_REFNUM, window, NULL, NULL);
open_log = close_log = FALSE;
if (g_strcasecmp(set, "ON") == 0)
open_log = TRUE;
else if (g_strcasecmp(set, "OFF") == 0) {
close_log = TRUE;
} else if (g_strcasecmp(set, "TOGGLE") == 0) {
open_log = log == NULL;
close_log = log != NULL;
} else {
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_NOT_TOGGLE);
cmd_params_free(free_arg);
return;
}
if (open_log && log == NULL) {
/* irc.log.<windowname> or irc.log.Window<ref#> */
fname = *fname != '\0' ? g_strdup(fname) :
g_strdup_printf("~/irc.log.%s%s",
active_win->name != NULL ? active_win->name : "Window",
active_win->name != NULL ? "" : window);
log = log_create_rec(fname, MSGLEVEL_ALL);
log->colorizer = log_colorizer_strip;
log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window, NULL);
log_update(log);
g_free(fname);
}
if (open_log && log != NULL) {
log_start_logging(log);
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_OPENED, log->fname);
} else if (close_log && log != NULL && log->handle != -1) {
log_stop_logging(log);
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_CLOSED, log->fname);
}
cmd_params_free(free_arg);
}
/* Create log file entry to window, but don't start logging */
/* SYNTAX: WINDOW LOGFILE <file> */
static void cmd_window_logfile(const char *data)
{
LOG_REC *log;
char window[MAX_INT_STRLEN];
ltoa(window, active_win->refnum);
log = logs_find_item(LOG_ITEM_WINDOW_REFNUM, window, NULL, NULL);
if (log != NULL) {
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_WINDOWLOG_FILE_LOGGING);
return;
}
log = log_create_rec(data, MSGLEVEL_ALL);
log->colorizer = log_colorizer_strip;
log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window, NULL);
log_update(log);
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_WINDOWLOG_FILE, data);
}
/* window's refnum changed - update the logs to log the new window refnum */
static void sig_window_refnum_changed(WINDOW_REC *window, gpointer old_refnum)
{
char winnum[MAX_INT_STRLEN];
LOG_REC *log;
LOG_ITEM_REC *item;
ltoa(winnum, GPOINTER_TO_INT(old_refnum));
log = logs_find_item(LOG_ITEM_WINDOW_REFNUM, winnum, NULL, &item);
if (log != NULL) {
ltoa(winnum, window->refnum);
g_free(item->name);
item->name = g_strdup(winnum);
}
}
static void sig_server_disconnected(SERVER_REC *server)
{
LOG_ITEM_REC *logitem;
GSList *tmp, *next;
for (tmp = logs; tmp != NULL; tmp = next) {
LOG_REC *log = tmp->data;
next = tmp->next;
if (!log->temp || log->items == NULL)
continue;
logitem = log->items->data;
if (logitem->type == LOG_ITEM_TARGET &&
logitem->servertag != NULL &&
g_strcasecmp(logitem->servertag, server->tag) == 0 &&
server_ischannel(server, logitem->name)) /* kludge again.. so we won't close dcc chats */
log_close(log);
}
}
static void autologs_close_all(void)
{
GSList *tmp, *next;
for (tmp = logs; tmp != NULL; tmp = next) {
LOG_REC *rec = tmp->data;
next = tmp->next;
if (rec->temp) log_close(rec);
}
}
/* '%' -> '%%', '/' -> '_' */
static char *escape_target(const char *target)
{
char *str, *p;
p = str = g_malloc(strlen(target)*2+1);
while (*target != '\0') {
if (*target == '/')
*p++ = '_';
else {
if (*target == '%')
*p++ = '%';
*p++ = *target;
}
target++;
}
*p = '\0';
return str;
}
static void autolog_open(SERVER_REC *server, const char *server_tag,
const char *target)
{
LOG_REC *log;
char *fname, *dir, *fixed_target, *params;
log = logs_find_item(LOG_ITEM_TARGET, target, server_tag, NULL);
if (log != NULL && !log->failed) {
log_start_logging(log);
return;
}
/* '/' -> '_' - don't even accidentally try to log to
#../../../file if you happen to join to such channel..
'%' -> '%%' - so strftime() won't mess with them */
fixed_target = escape_target(target);
if (CHAT_PROTOCOL(server)->case_insensitive)
g_strdown(fixed_target);
/* $0 = target, $1 = server tag */
params = g_strconcat(fixed_target, " ", server_tag, NULL);
g_free(fixed_target);
fname = parse_special_string(autolog_path, server, NULL,
params, NULL, 0);
g_free(params);
if (log_find(fname) == NULL) {
log = log_create_rec(fname, autolog_level);
if (!settings_get_bool("autolog_colors"))
log->colorizer = log_colorizer_strip;
log_item_add(log, LOG_ITEM_TARGET, target, server_tag);
dir = g_dirname(log->real_fname);
mkpath(dir, log_dir_create_mode);
g_free(dir);
log->temp = TRUE;
log_update(log);
log_start_logging(log);
}
g_free(fname);
}
static void autolog_open_check(SERVER_REC *server, const char *server_tag,
const char *target, int level)
{
char **targets, **tmp;
/* FIXME: kind of a kludge, but we don't want to reopen logs when
we're parting the channel with /WINDOW CLOSE.. Maybe a small
timeout would be nice instead of immediately closing the log file
after "window item destroyed" */
if (level == MSGLEVEL_PARTS ||
(autolog_level & level) == 0 || target == NULL || *target == '\0')
return;
/* there can be multiple targets separated with comma */
targets = g_strsplit(target, ",", -1);
for (tmp = targets; *tmp != NULL; tmp++)
autolog_open(server, server_tag, *tmp);
g_strfreev(targets);
}
static void log_single_line(WINDOW_REC *window, const char *server_tag,
const char *target, int level, const char *text)
{
char windownum[MAX_INT_STRLEN];
char **targets, **tmp;
LOG_REC *log;
if (window != NULL) {
/* save to log created with /WINDOW LOG */
ltoa(windownum, window->refnum);
log = logs_find_item(LOG_ITEM_WINDOW_REFNUM,
windownum, NULL, NULL);
if (log != NULL)
log_write_rec(log, text, level);
}
if (target == NULL)
log_file_write(server_tag, NULL, level, text, FALSE);
else {
/* there can be multiple items separated with comma */
targets = g_strsplit(target, ",", -1);
for (tmp = targets; *tmp != NULL; tmp++)
log_file_write(server_tag, *tmp, level, text, FALSE);
g_strfreev(targets);
}
}
static void log_line(TEXT_DEST_REC *dest, const char *text)
{
char **lines, **tmp;
if (dest->level == MSGLEVEL_NEVER)
return;
/* let autolog open the log records */
autolog_open_check(dest->server, dest->server_tag,
dest->target, dest->level);
if (logs == NULL)
return;
/* text may contain one or more lines, log wants to eat them one
line at a time */
lines = g_strsplit(text, "\n", -1);
for (tmp = lines; *tmp != NULL; tmp++)
log_single_line(dest->window, dest->server_tag,
dest->target, dest->level, *tmp);
g_strfreev(lines);
}
static void sig_printtext(TEXT_DEST_REC *dest, const char *text,
const char *stripped)
{
if (skip_next_printtext) {
skip_next_printtext = FALSE;
return;
}
log_line(dest, text);
}
static void sig_print_format(THEME_REC *theme, const char *module,
TEXT_DEST_REC *dest, void *formatnum, char **args)
{
char *str, *linestart, *tmp;
if (log_theme == NULL) {
/* theme isn't loaded for some reason (/reload destroys it),
reload it. */
log_theme = theme_load(log_theme_name);
if (log_theme == NULL) return;
}
if (theme == log_theme)
return;
str = format_get_text_theme_charargs(log_theme, module, dest,
GPOINTER_TO_INT(formatnum), args);
skip_next_printtext = TRUE;
if (*str != '\0') {
/* add the line start format */
linestart = format_get_level_tag(log_theme, dest);
tmp = str;
str = format_add_linestart(tmp, linestart);
g_free_not_null(linestart);
g_free(tmp);
/* strip colors from text, log it. */
log_line(dest, str);
}
g_free(str);
}
static int sig_autoremove(void)
{
SERVER_REC *server;
LOG_ITEM_REC *logitem;
GSList *tmp, *next;
time_t removetime;
removetime = time(NULL)-AUTOLOG_INACTIVITY_CLOSE;
for (tmp = logs; tmp != NULL; tmp = next) {
LOG_REC *log = tmp->data;
next = tmp->next;
if (!log->temp || log->last > removetime || log->items == NULL)
continue;
/* Close only logs with private messages */
logitem = log->items->data;
if (logitem->servertag == NULL)
continue;
server = server_find_tag(logitem->servertag);
if (logitem->type == LOG_ITEM_TARGET &&
server != NULL && !server_ischannel(server, logitem->name))
log_close(log);
}
return 1;
}
static void sig_window_item_remove(WINDOW_REC *window, WI_ITEM_REC *item)
{
LOG_REC *log;
log = logs_find_item(LOG_ITEM_TARGET, item->visible_name,
item->server == NULL ? NULL :
item->server->tag, NULL);
if (log != NULL && log->temp)
log_close(log);
}
static void sig_log_locked(LOG_REC *log)
{
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
TXT_LOG_LOCKED, log->fname);
}
static void sig_log_create_failed(LOG_REC *log)
{
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
TXT_LOG_CREATE_FAILED,
log->real_fname, g_strerror(errno));
}
static void sig_log_new(LOG_REC *log)
{
if (!settings_get_bool("awaylog_colors") &&
strcmp(log->fname, settings_get_str("awaylog_file")) == 0)
log->colorizer = log_colorizer_strip;
}
static void sig_log_config_read(LOG_REC *log, CONFIG_NODE *node)
{
if (!config_node_get_bool(node, "colors", FALSE))
log->colorizer = log_colorizer_strip;
}
static void sig_log_config_save(LOG_REC *log, CONFIG_NODE *node)
{
if (log->colorizer == NULL)
iconfig_node_set_bool(node, "colors", TRUE);
else
iconfig_node_set_str(node, "colors", NULL);
}
static void sig_awaylog_show(LOG_REC *log, gpointer pmsgs, gpointer pfilepos)
{
char *str;
int msgs, filepos;
msgs = GPOINTER_TO_INT(pmsgs);
filepos = GPOINTER_TO_INT(pfilepos);
if (msgs == 0)
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_NO_AWAY_MSGS, log->fname);
else {
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_AWAY_MSGS, log->fname, msgs);
str = g_strdup_printf("\"%s\" %d", log->fname, filepos);
signal_emit("command cat", 1, str);
g_free(str);
}
}
static void sig_theme_destroyed(THEME_REC *theme)
{
if (theme == log_theme)
log_theme = NULL;
}
static void read_settings(void)
{
int old_autolog = autolog_level;
int log_file_create_mode;
autolog_path = settings_get_str("autolog_path");
autolog_level = !settings_get_bool("autolog") ? 0 :
settings_get_level("autolog_level");
if (old_autolog && !autolog_level)
autologs_close_all();
/* write to log files with different theme? */
if (log_theme_name != NULL)
signal_remove("print format", (SIGNAL_FUNC) sig_print_format);
log_theme_name = settings_get_str("log_theme");
if (*log_theme_name == '\0')
log_theme_name = NULL;
else
signal_add("print format", (SIGNAL_FUNC) sig_print_format);
log_theme = log_theme_name == NULL ? NULL :
theme_load(log_theme_name);
log_file_create_mode = octal2dec(settings_get_int("log_create_mode"));
log_dir_create_mode = log_file_create_mode;
if (log_file_create_mode & 0400) log_dir_create_mode |= 0100;
if (log_file_create_mode & 0040) log_dir_create_mode |= 0010;
if (log_file_create_mode & 0004) log_dir_create_mode |= 0001;
}
void fe_log_init(void)
{
autoremove_tag = g_timeout_add(60000, (GSourceFunc) sig_autoremove, NULL);
skip_next_printtext = FALSE;
settings_add_bool("log", "awaylog_colors", TRUE);
settings_add_bool("log", "autolog", FALSE);
settings_add_bool("log", "autolog_colors", FALSE);
settings_add_str("log", "autolog_path", "~/irclogs/$tag/$0.log");
settings_add_level("log", "autolog_level", "all -crap -clientcrap -ctcps");
settings_add_str("log", "log_theme", "");
autolog_level = 0;
log_theme_name = NULL;
read_settings();
command_bind("log", NULL, (SIGNAL_FUNC) cmd_log);
command_bind("log open", NULL, (SIGNAL_FUNC) cmd_log_open);
command_bind("log close", NULL, (SIGNAL_FUNC) cmd_log_close);
command_bind("log start", NULL, (SIGNAL_FUNC) cmd_log_start);
command_bind("log stop", NULL, (SIGNAL_FUNC) cmd_log_stop);
command_bind("window log", NULL, (SIGNAL_FUNC) cmd_window_log);
command_bind("window logfile", NULL, (SIGNAL_FUNC) cmd_window_logfile);
signal_add_first("print text", (SIGNAL_FUNC) sig_printtext);
signal_add("window item remove", (SIGNAL_FUNC) sig_window_item_remove);
signal_add("window refnum changed", (SIGNAL_FUNC) sig_window_refnum_changed);
signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
signal_add("log locked", (SIGNAL_FUNC) sig_log_locked);
signal_add("log create failed", (SIGNAL_FUNC) sig_log_create_failed);
signal_add("log new", (SIGNAL_FUNC) sig_log_new);
signal_add("log config read", (SIGNAL_FUNC) sig_log_config_read);
signal_add("log config save", (SIGNAL_FUNC) sig_log_config_save);
signal_add("awaylog show", (SIGNAL_FUNC) sig_awaylog_show);
signal_add("theme destroyed", (SIGNAL_FUNC) sig_theme_destroyed);
signal_add("setup changed", (SIGNAL_FUNC) read_settings);
command_set_options("log open", "noopen autoopen -targets window colors");
}
void fe_log_deinit(void)
{
g_source_remove(autoremove_tag);
if (log_theme_name != NULL)
signal_remove("print format", (SIGNAL_FUNC) sig_print_format);
command_unbind("log", (SIGNAL_FUNC) cmd_log);
command_unbind("log open", (SIGNAL_FUNC) cmd_log_open);
command_unbind("log close", (SIGNAL_FUNC) cmd_log_close);
command_unbind("log start", (SIGNAL_FUNC) cmd_log_start);
command_unbind("log stop", (SIGNAL_FUNC) cmd_log_stop);
command_unbind("window log", (SIGNAL_FUNC) cmd_window_log);
command_unbind("window logfile", (SIGNAL_FUNC) cmd_window_logfile);
signal_remove("print text", (SIGNAL_FUNC) sig_printtext);
signal_remove("window item remove", (SIGNAL_FUNC) sig_window_item_remove);
signal_remove("window refnum changed", (SIGNAL_FUNC) sig_window_refnum_changed);
signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
signal_remove("log locked", (SIGNAL_FUNC) sig_log_locked);
signal_remove("log create failed", (SIGNAL_FUNC) sig_log_create_failed);
signal_remove("log new", (SIGNAL_FUNC) sig_log_new);
signal_remove("log config read", (SIGNAL_FUNC) sig_log_config_read);
signal_remove("log config save", (SIGNAL_FUNC) sig_log_config_save);
signal_remove("awaylog show", (SIGNAL_FUNC) sig_awaylog_show);
signal_remove("theme destroyed", (SIGNAL_FUNC) sig_theme_destroyed);
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
}