]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/logger.cpp
Patch by dz avoiding <badchan:redirect> loops, and tidying up on-rehash checks.
[user/henk/code/inspircd.git] / src / logger.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 /* $Core: libIRCDlogger */
15
16 #include "inspircd.h"
17
18 #include "filelogger.h"
19
20 /*
21  * Suggested implementation...
22  *      class LogManager
23  *              bool AddLogType(const std::string &type, enum loglevel, LogStream *)
24  *              bool DelLogType(const std::string &type, LogStream *)
25  *              Log(const std::string &type, enum loglevel, const std::string &msg)
26  *              std::map<std::string, std::vector<LogStream *> > logstreams (holds a 'chain' of logstreams for each type that are all notified when a log happens)
27  *
28  *  class LogStream
29  *              std::string type
30  *      virtual void OnLog(enum loglevel, const std::string &msg)
31  *
32  * How it works:
33  *  Modules create their own logstream types (core will create one for 'file logging' for example) and create instances of these logstream types
34  *  and register interest in a certain logtype. Globbing is not here, with the exception of * - for all events.. loglevel is used to drop 
35  *  events that are of no interest to a logstream.
36  *
37  *  When Log is called, the vector of logstreams for that type is iterated (along with the special vector for "*"), and all registered logstreams
38  *  are called back ("OnLog" or whatever) to do whatever they like with the message. In the case of the core, this will write to a file.
39  *  In the case of the module I plan to write (m_logtochannel or something), it will log to the channel(s) for that logstream, etc.
40  *
41  * NOTE: Somehow we have to let LogManager manage the non-blocking file streams and provide an interface to share them with various LogStreams,
42  *       as, for example, a user may want to let 'KILL' and 'XLINE' snotices go to /home/ircd/inspircd/logs/operactions.log, or whatever. How
43  *       can we accomplish this easily? I guess with a map of pre-loved logpaths, and a pointer of FILE *..
44  * 
45  */
46
47 void LogManager::SetupNoFork()
48 {
49         if (!noforkstream)
50         {
51                 FileWriter* fw = new FileWriter(ServerInstance, stdout);
52                 noforkstream = new FileLogStream(ServerInstance, ServerInstance->Config->forcedebug ? DEBUG : ServerInstance->Config->LogLevel, fw);
53         }
54         else
55         {
56                 noforkstream->ChangeLevel(ServerInstance->Config->forcedebug ? DEBUG : ServerInstance->Config->LogLevel);
57         }
58         AddLogType("*", noforkstream, false);
59 }
60
61 void LogManager::OpenFileLogs()
62 {
63         if (ServerInstance->Config->nofork) SetupNoFork(); // Call this to reregister the nofork stream.
64         if (!ServerInstance->Config->writelog) return; // Skip rest of logfile opening if we are running -nolog.
65         ConfigReader* Conf = new ConfigReader(ServerInstance);
66         std::map<std::string, FileWriter*> logmap;
67         std::map<std::string, FileWriter*>::iterator i;
68         for (int index = 0; index < Conf->Enumerate("log"); ++index)
69         {
70                 std::string method = Conf->ReadValue("log", "method", index);
71                 if (method != "file") continue;
72                 std::string type = Conf->ReadValue("log", "type", index);
73                 std::string level = Conf->ReadValue("log", "level", index);
74                 int loglevel = DEFAULT;
75                 if (level == "debug" || ServerInstance->Config->forcedebug)
76                 {
77                         loglevel = DEBUG;
78                         ServerInstance->Config->debugging = true;
79                 }
80                 else if (level == "verbose")
81                 {
82                         loglevel = VERBOSE;
83                 }
84                 else if (level == "default")
85                 {
86                         loglevel = DEFAULT;
87                 }
88                 else if (level == "sparse")
89                 {
90                         loglevel = SPARSE;
91                 }
92                 else if (level == "none")
93                 {
94                         loglevel = NONE;
95                 }
96                 FileWriter* fw;
97                 std::string target = Conf->ReadValue("log", "target", index);
98                 if ((i = logmap.find(target)) == logmap.end())
99                 {
100                         FILE* f = fopen(target.c_str(), "a");
101                         fw = new FileWriter(ServerInstance, f);
102                         logmap.insert(std::make_pair(target, fw));
103                 }
104                 else
105                 {
106                         fw = i->second;
107                 }
108                 FileLogStream* fls = new FileLogStream(ServerInstance, loglevel, fw);
109                 irc::commasepstream css(type);
110                 std::string tok;
111                 while (css.GetToken(tok))
112                 {
113                         AddLogType(tok, fls, true);
114                 }
115         }
116 }
117
118 void LogManager::CloseLogs()
119 {
120         std::map<std::string, std::vector<LogStream*> >().swap(LogStreams); /* Clear it */
121         std::vector<LogStream*>().swap(GlobalLogStreams); /* Clear it */
122         for (std::map<LogStream*, int>::iterator i = AllLogStreams.begin(); i != AllLogStreams.end(); ++i)
123         {
124                 delete i->first;
125         }
126         std::map<LogStream*, int>().swap(AllLogStreams); /* And clear it */
127 }
128
129 bool LogManager::AddLogType(const std::string &type, LogStream *l, bool autoclose)
130 {
131         std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
132
133         if (i != LogStreams.end())
134                 i->second.push_back(l);
135         else
136         {
137                 std::vector<LogStream *> v;
138                 v.push_back(l);
139                 LogStreams[type] = v;
140         }
141
142         if (type == "*")
143                 GlobalLogStreams.push_back(l);
144
145         if (autoclose)
146         {
147                 std::map<LogStream*, int>::iterator ai = AllLogStreams.find(l);
148                 if (ai == AllLogStreams.end())
149                 {
150                         AllLogStreams.insert(std::make_pair(l, 1));
151                 }
152                 else
153                 {
154                         ++ai->second;
155                 }
156         }
157
158         return true;
159 }
160
161 void LogManager::DelLogStream(LogStream* l)
162 {
163         for (std::map<std::string, std::vector<LogStream*> >::iterator i = LogStreams.begin(); i != LogStreams.end(); ++i)
164         {
165                 std::vector<LogStream*>::iterator it;
166                 while ((it = std::find(i->second.begin(), i->second.end(), l)) != i->second.end())
167                 {
168                         if (it == i->second.end()) continue;
169                         i->second.erase(it);
170                 }
171         }
172         std::vector<LogStream *>::iterator gi = std::find(GlobalLogStreams.begin(), GlobalLogStreams.end(), l);
173         if (gi != GlobalLogStreams.end()) GlobalLogStreams.erase(gi);
174         std::map<LogStream*, int>::iterator ai = AllLogStreams.begin();
175         if (ai == AllLogStreams.end()) return; /* Done. */
176         delete ai->first;
177         AllLogStreams.erase(ai);
178 }
179
180 bool LogManager::DelLogType(const std::string &type, LogStream *l)
181 {
182         std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
183         if (type == "*")
184         {
185                 std::vector<LogStream *>::iterator gi = std::find(GlobalLogStreams.begin(), GlobalLogStreams.end(), l);
186                 if (gi != GlobalLogStreams.end()) GlobalLogStreams.erase(gi);
187         }
188
189         if (i != LogStreams.end())
190         {
191                 std::vector<LogStream *>::iterator it = std::find(i->second.begin(), i->second.end(), l);
192
193                 if (it != i->second.end())
194                 {
195                         i->second.erase(it);
196                         if (i->second.size() == 0)
197                         {
198                                 LogStreams.erase(i);
199                         }
200                 }
201                 else
202                 {
203                         return false;
204                 }
205         }
206         else
207         {
208                 return false;
209         }
210
211         std::map<LogStream*, int>::iterator ai = AllLogStreams.find(l);
212         if (ai == AllLogStreams.end()) return true;
213
214         if ((--ai->second) < 1)
215         {
216                 AllLogStreams.erase(ai);
217                 delete l;
218         }
219
220         return true;
221 }
222
223 void LogManager::Log(const std::string &type, int loglevel, const char *fmt, ...)
224 {
225         if (Logging)
226                 return;
227
228         va_list a;
229         static char buf[65536];
230
231         va_start(a, fmt);
232         vsnprintf(buf, 65536, fmt, a);
233         va_end(a);
234
235         this->Log(type, loglevel, std::string(buf));
236 }
237
238 void LogManager::Log(const std::string &type, int loglevel, const std::string &msg)
239 {
240         if (Logging)
241                 return;
242
243         Logging = true;
244
245         std::vector<LogStream *>::iterator gi = GlobalLogStreams.begin();
246
247         while (gi != GlobalLogStreams.end())
248         {
249                 (*gi)->OnLog(loglevel, type, msg);
250                 gi++;
251         }
252
253         std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
254
255         if (i != LogStreams.end())
256         {
257                 std::vector<LogStream *>::iterator it = i->second.begin();
258
259                 while (it != i->second.end())
260                 {
261                         (*it)->OnLog(loglevel, type, msg);
262                         it++;
263                 }
264         }
265
266         Logging = false;
267 }
268
269
270 FileWriter::FileWriter(InspIRCd* Instance, FILE* logfile)
271 : ServerInstance(Instance), log(logfile), writeops(0)
272 {
273         if (log)
274         {
275                 Instance->SE->NonBlocking(fileno(log));
276                 SetFd(fileno(log));
277                 buffer.clear();
278         }
279 }
280
281 bool FileWriter::Readable()
282 {
283         return false;
284 }
285     
286 void FileWriter::HandleEvent(EventType, int)
287 {
288         WriteLogLine("");
289         if (log)
290                 ServerInstance->SE->DelFd(this);
291 }
292
293 void FileWriter::WriteLogLine(const std::string &line)
294 {
295         if (line.length())
296                 buffer.append(line);
297
298         if (log)
299         {
300                 int written = fprintf(log,"%s",buffer.c_str());
301 #ifdef WINDOWS
302                 buffer.clear();
303 #else
304                 if ((written >= 0) && (written < (int)buffer.length()))
305                 {
306                         buffer.erase(0, buffer.length());
307                         ServerInstance->SE->AddFd(this);
308                 }
309                 else if (written == -1)
310                 {
311                         if (errno == EAGAIN)
312                                 ServerInstance->SE->AddFd(this);
313                 }
314                 else
315                 {
316                         /* Wrote the whole buffer, and no need for write callback */
317                         buffer.clear();
318                 }
319 #endif
320                 if (writeops++ % 20)
321                 {
322                         fflush(log);
323                 }
324         }
325 }
326
327 void FileWriter::Close()
328 {
329         if (log)
330         {
331                 ServerInstance->SE->Blocking(fileno(log));
332
333                 if (buffer.size())
334                         fprintf(log,"%s",buffer.c_str());
335
336 #ifndef WINDOWS
337                 ServerInstance->SE->DelFd(this);
338 #endif
339
340                 fflush(log);
341                 fclose(log);
342         }
343
344         buffer.clear();
345 }
346
347 FileWriter::~FileWriter()
348 {
349         this->Close();
350 }
351
352