1 /* log.c - NeonServ v5.6
2 * Copyright (C) 2011-2012 Philipp Kreil (pk910)
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "IRCEvents.h"
21 #include "ClientSocket.h"
23 #include "IOHandler.h"
25 #include "ConfigParser.h"
27 #define MAXLOGLEN 1024
29 static void write_log(const char *module, const int section, const char *line, int len);
30 static void load_log_targets();
31 static void unload_log_targets();
32 static int reload_log_targets(int init);
35 static pthread_mutex_t log_sync;
38 #define LOG_TARGET_TYPE_FILE 1
39 #define LOG_TARGET_TYPE_STDOUT 2
40 #define LOG_TARGET_TYPE_STDERR 3
41 #define LOG_TARGET_TYPE_IRC 4
51 {LOG_OVERRIDE, "override"},
52 {LOG_WARNING, "warning"},
66 struct log_target *next;
69 static struct log_target *log_targets = NULL;
71 static char *get_section_name(int section_id) {
73 while(log_sections[++i].id) {
74 if(log_sections[i].id == section_id)
75 return log_sections[i].name;
80 static int get_section_id(char *section_name) {
82 while(log_sections[++i].id) {
83 if(!stricmp(log_sections[i].name, section_name))
84 return log_sections[i].id;
90 void printf_log(const char *module, const int section, const char *text, ...) {
92 char logBuf[MAXLOGLEN];
95 va_start(arg_list, text);
96 pos = vsnprintf(logBuf, MAXLOGLEN - 1, text, arg_list);
98 if (pos < 0 || pos > (MAXLOGLEN - 1)) pos = MAXLOGLEN - 1;
100 write_log(module, section, logBuf, pos);
103 static void write_log(const char *module, const int section, const char *line, int len) {
106 SYNCHRONIZE(log_sync);
107 struct log_target *target;
108 char lineBuf[MAXLOGLEN];
110 int lineBufPos, timeBufPos;
116 timeinfo = localtime(&rawtime);
117 timeBufPos = strftime(timeBuf, 80, "[%X %x] ", timeinfo);
119 lineBufPos = sprintf(lineBuf, "(%s:", module);
120 for(i = 0, j = 0; i < LOG_SECTIONS; i++) {
121 if((section & (1 << i))) {
122 char *section_name = get_section_name((1 << i));
126 lineBuf[lineBufPos++] = ',';
127 lineBufPos += sprintf(lineBuf + lineBufPos, "%s", section_name);
130 lineBufPos += sprintf(lineBuf + lineBufPos, ") %s", line);
132 while(lineBuf[lineBufPos-1] == '\r' || lineBuf[lineBufPos-1] == '\n') {
133 lineBuf[lineBufPos-1] = '\0';
138 for(target = log_targets; target; target = target->next) {
139 if(strcmp(target->module, "*") && stricmp(target->module, module))
141 if(!(target->section & section))
143 if(target->type == LOG_TARGET_TYPE_IRC) {
144 if(section == LOG_IRCRAW || !stricmp(module, "iohandler"))
145 continue; //endless loop ;)
146 struct ChanNode *channel = getChanByName(target->target.channel);
147 struct ClientSocket *client;
148 if(channel && (client = getChannelBot(channel, 0))) {
149 putsock(client, "PRIVMSG %s :%s", channel->name, lineBuf);
151 } else if(target->type == LOG_TARGET_TYPE_FILE) {
152 fwrite(timeBuf, 1, timeBufPos, target->target.fptr);
153 fwrite(lineBuf, 1, lineBufPos, target->target.fptr);
154 fwrite("\n", 1, 1, target->target.fptr);
155 } else if(target->type == LOG_TARGET_TYPE_STDOUT || target->type == LOG_TARGET_TYPE_STDERR) {
156 if(process_state.daemonized)
157 continue; //block stdout / stderr as daemon
158 fprintf((target->type == LOG_TARGET_TYPE_STDOUT ? stdout : stderr), "%s%s\n", timeBuf, lineBuf);
162 if((process_state.loglevel & section) && !process_state.daemonized && !j) {
163 fprintf(stdout, "%s%s\n", timeBuf, lineBuf);
166 DESYNCHRONIZE(log_sync);
169 static void load_log_targets() {
172 char **targetlist = get_all_fieldnames("logs");
173 if(!targetlist) return;
176 struct log_target *ctarget;
177 while(targetlist[i]) {
178 sprintf(tmp, "logs.%s", targetlist[i]);
179 char *type = get_string_field(tmp);
180 char *target = strchr(type, ':');
181 char *module = targetlist[i];
182 char *section = strchr(module, ':');
183 if(!target || !section) {
191 ctarget = malloc(sizeof(*ctarget));
192 if(!stricmp(type, "file")) {
193 ctarget->type = LOG_TARGET_TYPE_FILE;
194 ctarget->target.fptr = fopen(target, "a");
195 if(!ctarget->target.fptr) {
199 } else if(!stricmp(type, "std")) {
200 if(!stricmp(target, "out"))
201 ctarget->type = LOG_TARGET_TYPE_STDOUT;
202 else if(!stricmp(target, "err"))
203 ctarget->type = LOG_TARGET_TYPE_STDERR;
208 } else if(!stricmp(type, "irc")) {
209 if(is_valid_chan(target)) {
210 ctarget->type = LOG_TARGET_TYPE_IRC;
211 ctarget->target.channel = strdup(target);
221 ctarget->module = strdup(module);
222 ctarget->section = 0;
223 char *csection = section;
225 char *next_section = strchr(csection, ',');
227 *next_section = '\0';
228 if(!strcmp(csection, "*"))
229 ctarget->section |= LOG_ALL;
231 ctarget->section |= get_section_id(csection);
234 csection = next_section + 1;
238 ctarget->next = log_targets;
239 log_targets = ctarget;
249 static void unload_log_targets() {
250 struct log_target *target, *next_target;
251 for(target = log_targets; target; target = next_target) {
252 next_target = target->next;
253 if(target->type == LOG_TARGET_TYPE_IRC)
254 free(target->target.channel);
255 else if(target->type == LOG_TARGET_TYPE_FILE)
256 fclose(target->target.fptr);
262 static int reload_log_targets(int init) {
263 unload_log_targets();
268 static IOHANDLER_LOG_BACKEND(log_iohandler_backend) {
272 log_level = LOG_DEBUG;
275 log_level = LOG_WARNING;
278 log_level = LOG_ERROR;
281 log_level = LOG_FATAL;
286 write_log("iohandler", log_level, line, strlen(line));
291 THREAD_MUTEX_INIT(log_sync);
294 bind_reload(reload_log_targets, 0);
295 iolog_backend = log_iohandler_backend;
296 printf_log("log", LOG_INFO, "initialized log system.");