2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
5 * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
7 * This file is part of InspIRCd. InspIRCd is free software: you can
8 * redistribute it and/or modify it under the terms of the GNU General Public
9 * License as published by the Free Software Foundation, version 2.
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 /* $ModConfig: <xlinedb filename="data/xline.db">
25 * Specify the filename for the xline database here*/
26 /* $ModDesc: Keeps a dynamic log of all XLines created, and stores them in a seperate conf file (xline.db). */
28 class ModuleXLineDB : public Module
30 std::vector<XLine *> xlines;
31 bool reading_db; // If this is true, addlines are as a result of db reading, so don't bother flushing the db to disk.
32 // DO REMEMBER TO SET IT, otherwise it's annoying :P
33 std::string xlinedbpath;
36 Implementation eventlist[] = { I_OnAddLine, I_OnDelLine, I_OnExpireLine };
37 ServerInstance->Modules->Attach(eventlist, this, 3);
39 /* Load the configuration
41 * this is on purpose not in the OnRehash() method. It would be non-trivial to change the database on-the-fly.
42 * Imagine a scenario where the new file already exists. Merging the current XLines with the existing database is likely a bad idea
43 * ...and so is discarding all current in-memory XLines for the ones in the database.
45 ConfigTag* Conf = ServerInstance->Config->ConfValue("xlinedb");
46 xlinedbpath = Conf->getString("filename", DATA_PATH "/xline.db");
53 virtual ~ModuleXLineDB()
57 /** Called whenever an xline is added by a local user.
58 * This method is triggered after the line is added.
59 * @param source The sender of the line or NULL for local server
60 * @param line The xline being added
62 void OnAddLine(User* source, XLine* line)
64 ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Adding a line");
65 xlines.push_back(line);
73 /** Called whenever an xline is deleted.
74 * This method is triggered after the line is deleted.
75 * @param source The user removing the line or NULL for local server
76 * @param line the line being deleted
78 void OnDelLine(User* source, XLine* line)
83 void OnExpireLine(XLine *line)
88 void RemoveLine(XLine *line)
90 ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Removing a line");
91 for (std::vector<XLine *>::iterator i = xlines.begin(); i != xlines.end(); i++)
108 * We need to perform an atomic write so as not to fuck things up.
109 * So, let's write to a temporary file, flush and sync the FD, then rename the file..
110 * Technically, that means that this can block, but I have *never* seen that.
113 ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Opening temporary database");
114 std::string xlinenewdbpath = xlinedbpath + ".new";
115 f = fopen(xlinenewdbpath.c_str(), "w");
118 ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Cannot create database! %s (%d)", strerror(errno), errno);
119 ServerInstance->SNO->WriteToSnoMask('a', "database: cannot create new db: %s (%d)", strerror(errno), errno);
123 ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Opened. Writing..");
126 * Now, much as I hate writing semi-unportable formats, additional
127 * xline types may not have a conf tag, so let's just write them.
128 * In addition, let's use a file version, so we can maintain some
129 * semblance of backwards compatibility for reading on startup..
132 fprintf(f, "VERSION 1\n");
136 for (std::vector<XLine *>::iterator i = xlines.begin(); i != xlines.end(); i++)
139 fprintf(f, "LINE %s %s %s %lu %lu :%s\n", line->type.c_str(), line->Displayable(),
140 ServerInstance->Config->ServerName.c_str(), (unsigned long)line->set_time, (unsigned long)line->duration, line->reason.c_str());
143 ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Finished writing XLines. Checking for error..");
146 write_error = ferror(f);
147 write_error |= fclose(f);
150 ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Cannot write to new database! %s (%d)", strerror(errno), errno);
151 ServerInstance->SNO->WriteToSnoMask('a', "database: cannot write to new db: %s (%d)", strerror(errno), errno);
156 if (remove(xlinedbpath.c_str()))
158 ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Cannot remove old database! %s (%d)", strerror(errno), errno);
159 ServerInstance->SNO->WriteToSnoMask('a', "database: cannot remove old database: %s (%d)", strerror(errno), errno);
163 // Use rename to move temporary to new db - this is guarenteed not to fuck up, even in case of a crash.
164 if (rename(xlinenewdbpath.c_str(), xlinedbpath.c_str()) < 0)
166 ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Cannot move new to old database! %s (%d)", strerror(errno), errno);
167 ServerInstance->SNO->WriteToSnoMask('a', "database: cannot replace old with new db: %s (%d)", strerror(errno), errno);
177 char linebuf[MAXBUF];
178 unsigned int lineno = 0;
180 f = fopen(xlinedbpath.c_str(), "r");
185 /* xline.db doesn't exist, fake good return value (we don't care about this) */
190 /* this might be slightly more problematic. */
191 ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Cannot read database! %s (%d)", strerror(errno), errno);
192 ServerInstance->SNO->WriteToSnoMask('a', "database: cannot read db: %s (%d)", strerror(errno), errno);
197 while (fgets(linebuf, MAXBUF, f))
210 // Smart man might think of initing to 1, and moving this to the bottom. Don't. We use continue in this loop.
213 // Inspired by the command parser. :)
214 irc::tokenstream tokens(linebuf);
216 std::string command_p[MAXPARAMETERS];
219 while (tokens.GetToken(tmp) && (items < MAXPARAMETERS))
221 command_p[items] = tmp;
225 ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Processing %s", linebuf);
227 if (command_p[0] == "VERSION")
229 if (command_p[1] == "1")
231 ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Reading db version %s", command_p[1].c_str());
236 ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: I got database version %s - I don't understand it", command_p[1].c_str());
237 ServerInstance->SNO->WriteToSnoMask('a', "database: I got a database version (%s) I don't understand", command_p[1].c_str());
241 else if (command_p[0] == "LINE")
243 // Mercilessly stolen from spanningtree
244 XLineFactory* xlf = ServerInstance->XLines->GetFactory(command_p[1]);
248 ServerInstance->SNO->WriteToSnoMask('a', "database: Unknown line type (%s).", command_p[1].c_str());
252 XLine* xl = xlf->Generate(ServerInstance->Time(), atoi(command_p[5].c_str()), command_p[3], command_p[6], command_p[2]);
253 xl->SetCreateTime(atoi(command_p[4].c_str()));
255 if (ServerInstance->XLines->AddLine(xl, NULL))
257 ServerInstance->SNO->WriteToSnoMask('x', "database: Added a line of type %s", command_p[1].c_str());
270 virtual Version GetVersion()
272 return Version("Keeps a dynamic log of all XLines created, and stores them in a separate conf file (xline.db).", VF_VENDOR);
276 MODULE_INIT(ModuleXLineDB)