]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/logger.cpp
Close files opened by configreader
[user/henk/code/inspircd.git] / src / logger.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15
16 #include "filelogger.h"
17
18 /*
19  * Suggested implementation...
20  *      class LogManager
21  *              bool AddLogType(const std::string &type, enum loglevel, LogStream *)
22  *              bool DelLogType(const std::string &type, LogStream *)
23  *              Log(const std::string &type, enum loglevel, const std::string &msg)
24  *              std::map<std::string, std::vector<LogStream *> > logstreams (holds a 'chain' of logstreams for each type that are all notified when a log happens)
25  *
26  *  class LogStream
27  *              std::string type
28  *      virtual void OnLog(enum loglevel, const std::string &msg)
29  *
30  * How it works:
31  *  Modules create their own logstream types (core will create one for 'file logging' for example) and create instances of these logstream types
32  *  and register interest in a certain logtype. Globbing is not here, with the exception of * - for all events.. loglevel is used to drop
33  *  events that are of no interest to a logstream.
34  *
35  *  When Log is called, the vector of logstreams for that type is iterated (along with the special vector for "*"), and all registered logstreams
36  *  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.
37  *  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.
38  *
39  * NOTE: Somehow we have to let LogManager manage the non-blocking file streams and provide an interface to share them with various LogStreams,
40  *       as, for example, a user may want to let 'KILL' and 'XLINE' snotices go to /home/ircd/inspircd/logs/operactions.log, or whatever. How
41  *       can we accomplish this easily? I guess with a map of pre-loved logpaths, and a pointer of FILE *..
42  *
43  */
44
45 LogManager::LogManager()
46 {
47         noforkstream = NULL;
48         Logging = false;
49 }
50
51 LogManager::~LogManager()
52 {
53         if (noforkstream)
54         {
55                 ServerInstance->Logs = this;
56                 delete noforkstream;
57                 ServerInstance->Logs = NULL;
58         }
59 }
60
61 void LogManager::SetupNoFork()
62 {
63         if (!noforkstream)
64         {
65                 FileWriter* fw = new FileWriter(stdout);
66                 noforkstream = new FileLogStream(ServerInstance->Config->forcedebug ? DEBUG : DEFAULT, fw);
67         }
68         else
69         {
70                 noforkstream->ChangeLevel(ServerInstance->Config->forcedebug ? DEBUG : DEFAULT);
71         }
72         AddLogType("*", noforkstream, false);
73 }
74
75 void LogManager::OpenFileLogs()
76 {
77         /* Re-register the nofork stream if necessary. */
78         if (ServerInstance->Config->nofork)
79         {
80                 SetupNoFork();
81         }
82         /* Skip rest of logfile opening if we are running -nolog. */
83         if (!ServerInstance->Config->writelog)
84         {
85                 return;
86         }
87         ConfigReader Conf;
88         std::map<std::string, FileWriter*> logmap;
89         std::map<std::string, FileWriter*>::iterator i;
90         for (int index = 0;; ++index)
91         {
92                 ConfigTag* tag = ServerInstance->Config->ConfValue("log", index);
93                 if (!tag)
94                         break;
95                 std::string method = tag->getString("method");
96                 if (method != "file")
97                 {
98                         continue;
99                 }
100                 std::string type = tag->getString("type");
101                 std::string level = tag->getString("level");
102                 int loglevel = DEFAULT;
103                 if (level == "debug" || ServerInstance->Config->forcedebug)
104                 {
105                         loglevel = DEBUG;
106                         ServerInstance->Config->debugging = true;
107                 }
108                 else if (level == "verbose")
109                 {
110                         loglevel = VERBOSE;
111                 }
112                 else if (level == "default")
113                 {
114                         loglevel = DEFAULT;
115                 }
116                 else if (level == "sparse")
117                 {
118                         loglevel = SPARSE;
119                 }
120                 else if (level == "none")
121                 {
122                         loglevel = NONE;
123                 }
124                 FileWriter* fw;
125                 std::string target = Conf.ReadValue("log", "target", index);
126                 if ((i = logmap.find(target)) == logmap.end())
127                 {
128                         FILE* f = fopen(target.c_str(), "a");
129                         fw = new FileWriter(f);
130                         logmap.insert(std::make_pair(target, fw));
131                 }
132                 else
133                 {
134                         fw = i->second;
135                 }
136                 FileLogStream* fls = new FileLogStream(loglevel, fw);
137                 AddLogTypes(type, fls, true);
138         }
139 }
140
141 void LogManager::CloseLogs()
142 {
143         std::map<std::string, std::vector<LogStream*> >().swap(LogStreams); /* Clear it */
144         std::map<LogStream*, std::vector<std::string> >().swap(GlobalLogStreams); /* Clear it */
145         for (std::map<LogStream*, int>::iterator i = AllLogStreams.begin(); i != AllLogStreams.end(); ++i)
146         {
147                 delete i->first;
148         }
149         std::map<LogStream*, int>().swap(AllLogStreams); /* And clear it */
150 }
151
152 void LogManager::AddLogTypes(const std::string &types, LogStream* l, bool autoclose)
153 {
154         irc::spacesepstream css(types);
155         std::string tok;
156         std::vector<std::string> excludes;
157         while (css.GetToken(tok))
158         {
159                 if (tok.empty())
160                 {
161                         continue;
162                 }
163                 if (tok.at(0) == '-')
164                 {
165                         /* Exclude! */
166                         excludes.push_back(tok.substr(1));
167                 }
168                 else
169                 {
170                         AddLogType(tok, l, autoclose);
171                 }
172         }
173         // Handle doing things like: USERINPUT USEROUTPUT -USERINPUT should be the same as saying just USEROUTPUT.
174         // (This is so modules could, for example, inject exclusions for logtypes they can't handle.)
175         for (std::vector<std::string>::iterator i = excludes.begin(); i != excludes.end(); ++i)
176         {
177                 if (*i == "*")
178                 {
179                         /* -* == Exclude all. Why someone would do this, I dunno. */
180                         DelLogStream(l);
181                         return;
182                 }
183                 DelLogType(*i, l);
184         }
185         // Now if it's registered as a global, add the exclusions there too.
186         std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.find(l);
187         if (gi != GlobalLogStreams.end())
188         {
189                 gi->second.swap(excludes); // Swap with the vector in the hash.
190         }
191 }
192
193 bool LogManager::AddLogType(const std::string &type, LogStream *l, bool autoclose)
194 {
195         std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
196
197         if (i != LogStreams.end())
198         {
199                 i->second.push_back(l);
200         }
201         else
202         {
203                 std::vector<LogStream *> v;
204                 v.push_back(l);
205                 LogStreams[type] = v;
206         }
207
208         if (type == "*")
209         {
210                 GlobalLogStreams.insert(std::make_pair(l, std::vector<std::string>()));
211         }
212
213         if (autoclose)
214         {
215                 std::map<LogStream*, int>::iterator ai = AllLogStreams.find(l);
216                 if (ai == AllLogStreams.end())
217                 {
218                         AllLogStreams.insert(std::make_pair(l, 1));
219                 }
220                 else
221                 {
222                         ++ai->second;
223                 }
224         }
225
226         return true;
227 }
228
229 void LogManager::DelLogStream(LogStream* l)
230 {
231         for (std::map<std::string, std::vector<LogStream*> >::iterator i = LogStreams.begin(); i != LogStreams.end(); ++i)
232         {
233                 std::vector<LogStream*>::iterator it;
234                 while ((it = std::find(i->second.begin(), i->second.end(), l)) != i->second.end())
235                 {
236                         if (it == i->second.end())
237                                 continue;
238                         i->second.erase(it);
239                 }
240         }
241         std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.find(l);
242         if (gi != GlobalLogStreams.end())
243         {
244                 GlobalLogStreams.erase(gi);
245         }
246         std::map<LogStream*, int>::iterator ai = AllLogStreams.begin();
247         if (ai == AllLogStreams.end())
248         {
249                 return; /* Done. */
250         }
251         delete ai->first;
252         AllLogStreams.erase(ai);
253 }
254
255 bool LogManager::DelLogType(const std::string &type, LogStream *l)
256 {
257         std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
258         if (type == "*")
259         {
260                 std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.find(l);
261                 if (gi != GlobalLogStreams.end()) GlobalLogStreams.erase(gi);
262         }
263
264         if (i != LogStreams.end())
265         {
266                 std::vector<LogStream *>::iterator it = std::find(i->second.begin(), i->second.end(), l);
267
268                 if (it != i->second.end())
269                 {
270                         i->second.erase(it);
271                         if (i->second.size() == 0)
272                         {
273                                 LogStreams.erase(i);
274                         }
275                 }
276                 else
277                 {
278                         return false;
279                 }
280         }
281         else
282         {
283                 return false;
284         }
285
286         std::map<LogStream*, int>::iterator ai = AllLogStreams.find(l);
287         if (ai == AllLogStreams.end())
288         {
289                 return true;
290         }
291
292         if ((--ai->second) < 1)
293         {
294                 AllLogStreams.erase(ai);
295                 delete l;
296         }
297
298         return true;
299 }
300
301 void LogManager::Log(const std::string &type, int loglevel, const char *fmt, ...)
302 {
303         if (Logging)
304         {
305                 return;
306         }
307
308         va_list a;
309         static char buf[65536];
310
311         va_start(a, fmt);
312         vsnprintf(buf, 65536, fmt, a);
313         va_end(a);
314
315         this->Log(type, loglevel, std::string(buf));
316 }
317
318 void LogManager::Log(const std::string &type, int loglevel, const std::string &msg)
319 {
320         if (Logging)
321         {
322                 return;
323         }
324
325         Logging = true;
326
327         for (std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.begin(); gi != GlobalLogStreams.end(); ++gi)
328         {
329                 if (std::find(gi->second.begin(), gi->second.end(), type) != gi->second.end())
330                 {
331                         continue;
332                 }
333                 gi->first->OnLog(loglevel, type, msg);
334         }
335
336         std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
337
338         if (i != LogStreams.end())
339         {
340                 for (std::vector<LogStream *>::iterator it = i->second.begin(); it != i->second.end(); ++it)
341                 {
342                         (*it)->OnLog(loglevel, type, msg);
343                 }
344         }
345
346         Logging = false;
347 }
348
349
350 FileWriter::FileWriter(FILE* logfile)
351 : log(logfile), writeops(0)
352 {
353 }
354
355 void FileWriter::HandleEvent(EventType ev, int)
356 {
357 }
358
359 void FileWriter::WriteLogLine(const std::string &line)
360 {
361         if (log == NULL)
362                 return;
363 // XXX: For now, just return. Don't throw an exception. It'd be nice to find out if this is happening, but I'm terrified of breaking so close to final release. -- w00t
364 //              throw CoreException("FileWriter::WriteLogLine called with a closed logfile");
365
366         fprintf(log,"%s",line.c_str());
367         if (writeops++ % 20)
368         {
369                 fflush(log);
370         }
371 }
372
373 void FileWriter::Close()
374 {
375         if (log)
376         {
377                 fflush(log);
378                 fclose(log);
379                 log = NULL;
380         }
381 }
382
383 FileWriter::~FileWriter()
384 {
385         this->Close();
386 }