]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/logger.cpp
Made the launch script aware of --runasroot, so it does not drop privs if this is...
[user/henk/code/inspircd.git] / src / logger.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
6  *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
7  *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
8  *
9  * This file is part of InspIRCd.  InspIRCd is free software: you can
10  * redistribute it and/or modify it under the terms of the GNU General Public
11  * License as published by the Free Software Foundation, version 2.
12  *
13  * This program is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22
23 #include "inspircd.h"
24 #include "filelogger.h"
25
26 /*
27  * Suggested implementation...
28  *      class LogManager
29  *              bool AddLogType(const std::string &type, enum loglevel, LogStream *)
30  *              bool DelLogType(const std::string &type, LogStream *)
31  *              Log(const std::string &type, enum loglevel, const std::string &msg)
32  *              std::map<std::string, std::vector<LogStream *> > logstreams (holds a 'chain' of logstreams for each type that are all notified when a log happens)
33  *
34  *  class LogStream
35  *              std::string type
36  *      virtual void OnLog(enum loglevel, const std::string &msg)
37  *
38  * How it works:
39  *  Modules create their own logstream types (core will create one for 'file logging' for example) and create instances of these logstream types
40  *  and register interest in a certain logtype. Globbing is not here, with the exception of * - for all events.. loglevel is used to drop
41  *  events that are of no interest to a logstream.
42  *
43  *  When Log is called, the vector of logstreams for that type is iterated (along with the special vector for "*"), and all registered logstreams
44  *  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.
45  *  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.
46  *
47  * NOTE: Somehow we have to let LogManager manage the non-blocking file streams and provide an interface to share them with various LogStreams,
48  *       as, for example, a user may want to let 'KILL' and 'XLINE' snotices go to /home/ircd/inspircd/logs/operactions.log, or whatever. How
49  *       can we accomplish this easily? I guess with a map of pre-loved logpaths, and a pointer of FILE *..
50  *
51  */
52
53 const char LogStream::LogHeader[] =
54         "Log started for " VERSION " (" REVISION ", " MODULE_INIT_STR ")"
55         " - compiled on " SYSTEM;
56
57 LogManager::LogManager()
58         : Logging(false)
59 {
60 }
61
62 LogManager::~LogManager()
63 {
64         CloseLogs();
65 }
66
67 void LogManager::OpenFileLogs()
68 {
69         if (ServerInstance->Config->cmdline.forcedebug)
70         {
71                 ServerInstance->Config->RawLog = true;
72                 return;
73         }
74         /* Skip rest of logfile opening if we are running -nolog. */
75         if (!ServerInstance->Config->cmdline.writelog)
76                 return;
77         std::map<std::string, FileWriter*> logmap;
78         ConfigTagList tags = ServerInstance->Config->ConfTags("log");
79         for(ConfigIter i = tags.first; i != tags.second; ++i)
80         {
81                 ConfigTag* tag = i->second;
82                 std::string method = tag->getString("method");
83                 if (method != "file")
84                 {
85                         continue;
86                 }
87                 std::string type = tag->getString("type");
88                 std::string level = tag->getString("level");
89                 LogLevel loglevel = LOG_DEFAULT;
90                 if (level == "rawio")
91                 {
92                         loglevel = LOG_RAWIO;
93                         ServerInstance->Config->RawLog = true;
94                 }
95                 else if (level == "debug")
96                 {
97                         loglevel = LOG_DEBUG;
98                 }
99                 else if (level == "verbose")
100                 {
101                         loglevel = LOG_VERBOSE;
102                 }
103                 else if (level == "default")
104                 {
105                         loglevel = LOG_DEFAULT;
106                 }
107                 else if (level == "sparse")
108                 {
109                         loglevel = LOG_SPARSE;
110                 }
111                 else if (level == "none")
112                 {
113                         loglevel = LOG_NONE;
114                 }
115                 FileWriter* fw;
116                 std::string target = tag->getString("target");
117                 std::map<std::string, FileWriter*>::iterator fwi = logmap.find(target);
118                 if (fwi == logmap.end())
119                 {
120                         char realtarget[256];
121                         time_t time = ServerInstance->Time();
122                         struct tm *mytime = gmtime(&time);
123                         strftime(realtarget, sizeof(realtarget), target.c_str(), mytime);
124                         FILE* f = fopen(realtarget, "a");
125                         fw = new FileWriter(f);
126                         logmap.insert(std::make_pair(target, fw));
127                 }
128                 else
129                 {
130                         fw = fwi->second;
131                 }
132                 FileLogStream* fls = new FileLogStream(loglevel, fw);
133                 fls->OnLog(LOG_SPARSE, "HEADER", LogStream::LogHeader);
134                 AddLogTypes(type, fls, true);
135         }
136 }
137
138 void LogManager::CloseLogs()
139 {
140         if (ServerInstance->Config && ServerInstance->Config->cmdline.forcedebug)
141                 return;
142
143         LogStreams.clear();
144         GlobalLogStreams.clear();
145
146         for (std::map<LogStream*, int>::iterator i = AllLogStreams.begin(); i != AllLogStreams.end(); ++i)
147         {
148                 delete i->first;
149         }
150
151         AllLogStreams.clear();
152 }
153
154 void LogManager::AddLogTypes(const std::string &types, LogStream* l, bool autoclose)
155 {
156         irc::spacesepstream css(types);
157         std::string tok;
158         std::vector<std::string> excludes;
159         while (css.GetToken(tok))
160         {
161                 if (tok.empty())
162                 {
163                         continue;
164                 }
165                 if (tok.at(0) == '-')
166                 {
167                         /* Exclude! */
168                         excludes.push_back(tok.substr(1));
169                 }
170                 else
171                 {
172                         AddLogType(tok, l, autoclose);
173                 }
174         }
175         // Handle doing things like: USERINPUT USEROUTPUT -USERINPUT should be the same as saying just USEROUTPUT.
176         // (This is so modules could, for example, inject exclusions for logtypes they can't handle.)
177         for (std::vector<std::string>::iterator i = excludes.begin(); i != excludes.end(); ++i)
178         {
179                 if (*i == "*")
180                 {
181                         /* -* == Exclude all. Why someone would do this, I dunno. */
182                         DelLogStream(l);
183                         return;
184                 }
185                 DelLogType(*i, l);
186         }
187         // Now if it's registered as a global, add the exclusions there too.
188         std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.find(l);
189         if (gi != GlobalLogStreams.end())
190         {
191                 gi->second.swap(excludes); // Swap with the vector in the hash.
192         }
193 }
194
195 bool LogManager::AddLogType(const std::string &type, LogStream *l, bool autoclose)
196 {
197         LogStreams[type].push_back(l);
198
199         if (type == "*")
200                 GlobalLogStreams.insert(std::make_pair(l, std::vector<std::string>()));
201
202         if (autoclose)
203                 AllLogStreams[l]++;
204
205         return true;
206 }
207
208 void LogManager::DelLogStream(LogStream* l)
209 {
210         for (std::map<std::string, std::vector<LogStream*> >::iterator i = LogStreams.begin(); i != LogStreams.end(); ++i)
211         {
212                 std::vector<LogStream*>::iterator it;
213                 while ((it = std::find(i->second.begin(), i->second.end(), l)) != i->second.end())
214                 {
215                         i->second.erase(it);
216                 }
217         }
218
219         GlobalLogStreams.erase(l);
220
221         std::map<LogStream*, int>::iterator ai = AllLogStreams.begin();
222         if (ai == AllLogStreams.end())
223         {
224                 return; /* Done. */
225         }
226
227         delete ai->first;
228         AllLogStreams.erase(ai);
229 }
230
231 bool LogManager::DelLogType(const std::string &type, LogStream *l)
232 {
233         std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
234         if (type == "*")
235         {
236                 GlobalLogStreams.erase(l);
237         }
238
239         if (i != LogStreams.end())
240         {
241                 std::vector<LogStream *>::iterator it = std::find(i->second.begin(), i->second.end(), l);
242
243                 if (it != i->second.end())
244                 {
245                         i->second.erase(it);
246                         if (i->second.size() == 0)
247                         {
248                                 LogStreams.erase(i);
249                         }
250                 }
251                 else
252                 {
253                         return false;
254                 }
255         }
256         else
257         {
258                 return false;
259         }
260
261         std::map<LogStream*, int>::iterator ai = AllLogStreams.find(l);
262         if (ai == AllLogStreams.end())
263         {
264                 return true;
265         }
266
267         if ((--ai->second) < 1)
268         {
269                 AllLogStreams.erase(ai);
270                 delete l;
271         }
272
273         return true;
274 }
275
276 void LogManager::Log(const std::string &type, LogLevel loglevel, const char *fmt, ...)
277 {
278         if (Logging)
279                 return;
280
281         std::string buf;
282         VAFORMAT(buf, fmt, fmt);
283         this->Log(type, loglevel, buf);
284 }
285
286 void LogManager::Log(const std::string &type, LogLevel loglevel, const std::string &msg)
287 {
288         if (Logging)
289         {
290                 return;
291         }
292
293         Logging = true;
294
295         for (std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.begin(); gi != GlobalLogStreams.end(); ++gi)
296         {
297                 if (std::find(gi->second.begin(), gi->second.end(), type) != gi->second.end())
298                 {
299                         continue;
300                 }
301                 gi->first->OnLog(loglevel, type, msg);
302         }
303
304         std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
305
306         if (i != LogStreams.end())
307         {
308                 for (std::vector<LogStream *>::iterator it = i->second.begin(); it != i->second.end(); ++it)
309                 {
310                         (*it)->OnLog(loglevel, type, msg);
311                 }
312         }
313
314         Logging = false;
315 }
316
317
318 FileWriter::FileWriter(FILE* logfile)
319 : log(logfile), writeops(0)
320 {
321 }
322
323 void FileWriter::WriteLogLine(const std::string &line)
324 {
325         if (log == NULL)
326                 return;
327 // 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
328 //              throw CoreException("FileWriter::WriteLogLine called with a closed logfile");
329
330         fputs(line.c_str(), log);
331         if (++writeops % 20 == 0)
332         {
333                 fflush(log);
334         }
335 }
336
337 FileWriter::~FileWriter()
338 {
339         if (log)
340         {
341                 fflush(log);
342                 fclose(log);
343                 log = NULL;
344         }
345 }