Merge branch 'development'
[NeonServV5.git] / src / log.c
1 /* log.c - NeonServ v5.6
2  * Copyright (C) 2011-2012  Philipp Kreil (pk910)
3  * 
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.
8  * 
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.
13  * 
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/>. 
16  */
17
18 #include "log.h"
19 #include "ChanNode.h"
20 #include "IRCEvents.h"
21 #include "ClientSocket.h"
22 #include "bots.h"
23 #include "IOHandler.h"
24 #include "tools.h"
25 #include "ConfigParser.h"
26
27 #define MAXLOGLEN 1024
28
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);
33
34 #ifdef HAVE_THREADS
35 static pthread_mutex_t log_sync;
36 #endif
37
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
42
43 static const struct {
44     int id;
45     char *name;
46 } log_sections[] = {
47     {LOG_INFO, "info"},
48     {LOG_DEBUG, "debug"},
49     {LOG_IRCRAW, "raw"},
50     {LOG_MYSQL, "mysql"},
51     {LOG_OVERRIDE, "override"},
52     {LOG_WARNING, "warning"},
53     {LOG_ERROR, "error"},
54     {LOG_FATAL, "fatal"},
55     {0, NULL}
56 };
57
58 struct log_target {
59     char *module;
60     int section;
61     int type : 8;
62     union {
63         char *channel;
64         FILE *fptr;
65     } target;
66     struct log_target *next;
67 };
68
69 static struct log_target *log_targets = NULL;
70
71 static char *get_section_name(int section_id) {
72     int i = -1;
73     while(log_sections[++i].id) {
74         if(log_sections[i].id == section_id)
75             return log_sections[i].name;
76     }
77     return NULL;
78 }
79
80 static int get_section_id(char *section_name) {
81     int i = -1;
82     while(log_sections[++i].id) {
83         if(!stricmp(log_sections[i].name, section_name))
84             return log_sections[i].id;
85     }
86     return 0;
87 }
88
89
90 void printf_log(const char *module, const int section, const char *text, ...) {
91     va_list arg_list;
92     char logBuf[MAXLOGLEN];
93     int pos;
94     logBuf[0] = '\0';
95     va_start(arg_list, text);
96     pos = vsnprintf(logBuf, MAXLOGLEN - 1, text, arg_list);
97     va_end(arg_list);
98     if (pos < 0 || pos > (MAXLOGLEN - 1)) pos = MAXLOGLEN - 1;
99     logBuf[pos] = '\0';
100     write_log(module, section, logBuf, pos);
101 }
102
103 static void write_log(const char *module, const int section, const char *line, int len) {
104     if(!log_targets)
105         return;
106     SYNCHRONIZE(log_sync);
107     struct log_target *target;
108     char lineBuf[MAXLOGLEN];
109     char timeBuf[80];
110     int lineBufPos, timeBufPos;
111     time_t rawtime;
112     struct tm *timeinfo;
113     int i, j;
114     
115     time(&rawtime);
116     timeinfo = localtime(&rawtime);
117     timeBufPos = strftime(timeBuf, 80, "[%X %x] ", timeinfo);
118     
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));
123             if(!section_name)
124                 continue;
125             if(j++)
126                 lineBuf[lineBufPos++] = ',';
127             lineBufPos += sprintf(lineBuf + lineBufPos, "%s", section_name);
128         }
129     }
130     lineBufPos += sprintf(lineBuf + lineBufPos, ") %s", line);
131     //cut off \n \r
132     while(lineBuf[lineBufPos-1] == '\r' || lineBuf[lineBufPos-1] == '\n') {
133         lineBuf[lineBufPos-1] = '\0';
134         lineBufPos--;
135     }
136     
137     j = 0;
138     for(target = log_targets; target; target = target->next) {
139         if(strcmp(target->module, "*") && stricmp(target->module, module))
140             continue;
141         if(!(target->section & section))
142             continue;
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);
150             }
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);
159             j = 1;
160         }
161     }
162     if((process_state.loglevel & section) && !process_state.daemonized && !j) {
163         fprintf(stdout, "%s%s\n", timeBuf, lineBuf);
164     }
165     
166     DESYNCHRONIZE(log_sync);
167 }
168
169 static void load_log_targets() {
170     if(log_targets)
171         return;
172     char **targetlist = get_all_fieldnames("logs");
173     if(!targetlist) return;
174     int i = 0;
175     char tmp[MAXLEN];
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) {
184             i++;
185             continue;
186         }
187         *target = '\0';
188         target++;
189         *section = '\0';
190         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) {
196                 free(ctarget);
197                 ctarget = NULL;
198             }
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;
204             else {
205                 free(ctarget);
206                 ctarget = NULL;
207             }
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);
212             } else {
213                 free(ctarget);
214                 ctarget = NULL;
215             }
216         } else {
217             free(ctarget);
218             ctarget = NULL;
219         }
220         if(ctarget) {
221             ctarget->module = strdup(module);
222             ctarget->section = 0;
223             char *csection = section;
224             while(1) {
225                 char *next_section = strchr(csection, ',');
226                 if(next_section)
227                     *next_section = '\0';
228                 if(!strcmp(csection, "*"))
229                     ctarget->section |= LOG_ALL;
230                 else
231                     ctarget->section |= get_section_id(csection);
232                 if(next_section) {
233                     *next_section = ',';
234                     csection = next_section + 1;
235                 } else
236                     break;
237             }
238             ctarget->next = log_targets;
239             log_targets = ctarget;
240         }
241         target--;
242         *target = ':';
243         section--;
244         *section = ':';
245         i++;
246     }
247 }
248
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);
257         free(target);
258     }
259     log_targets = NULL;
260 }
261
262 static int reload_log_targets(int init) {
263     unload_log_targets();
264     load_log_targets();
265     return 1;
266 }
267
268 static IOHANDLER_LOG_BACKEND(log_iohandler_backend) {
269     int log_level;
270     switch(type) {
271         case IOLOG_DEBUG:
272             log_level = LOG_DEBUG;
273             break;
274         case IOLOG_WARNING:
275             log_level = LOG_WARNING;
276             break;
277         case IOLOG_ERROR:
278             log_level = LOG_ERROR;
279             break;
280         case IOLOG_FATAL:
281             log_level = LOG_FATAL;
282             break;
283         default:
284             log_level = 0;
285     }
286     write_log("iohandler", log_level, line, strlen(line));
287 }
288
289 void init_log() {
290     #if HAVE_THREADS
291     THREAD_MUTEX_INIT(log_sync);
292     #endif
293     load_log_targets();
294     bind_reload(reload_log_targets, 0);
295     iolog_backend = log_iohandler_backend;
296     printf_log("log", LOG_INFO, "initialized log system.");
297 }