From c1077faa281c5635f85b892e605e23bd6c8fcc3b Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 13 Dec 2012 06:12:56 -0500 Subject: Optimize much of the database code and serialize code. --- CMakeLists.txt | 7 - cmake/eventfd_test.cpp | 8 - data/example.conf | 12 +- include/access.h | 2 +- include/account.h | 4 +- include/anope.h | 11 + include/base.h | 2 +- include/bots.h | 2 +- include/extensible.h | 46 +-- include/memo.h | 2 +- include/module.h | 1 - include/regchannel.h | 10 +- include/serialize.h | 57 ++-- include/signals.h | 48 --- include/socketengine.h | 2 +- include/sockets.h | 33 +- include/xline.h | 2 +- modules/commands/cs_entrymsg.cpp | 18 +- modules/commands/cs_fantasy_stats.cpp | 32 +- modules/commands/cs_fantasy_top.cpp | 32 +- modules/commands/cs_seen.cpp | 14 +- modules/commands/cs_set_misc.cpp | 18 +- modules/commands/cs_suspend.cpp | 12 +- modules/commands/hs_request.cpp | 13 +- modules/commands/ns_ajoin.cpp | 14 +- modules/commands/ns_set_misc.cpp | 18 +- modules/commands/ns_suspend.cpp | 12 +- modules/commands/os_dns.cpp | 12 +- modules/commands/os_forbid.h | 8 +- modules/commands/os_ignore.h | 15 +- modules/commands/os_news.h | 8 +- modules/commands/os_oper.cpp | 15 +- modules/commands/os_session.h | 8 +- modules/commands/os_stats.cpp | 6 +- modules/database/db_flatfile.cpp | 354 ++++++++++++++-------- modules/database/db_sql.cpp | 62 ++-- modules/database/db_sql_live.cpp | 48 +-- modules/extra/m_chanstats.cpp | 58 ++-- modules/extra/m_mysql.cpp | 96 +++--- modules/extra/m_sql_authentication.cpp | 28 +- modules/extra/m_sql_oper.cpp | 22 +- modules/extra/m_sqlite.cpp | 76 ++--- modules/extra/sql.h | 280 +++++++++++------ modules/extra/webcpanel/pages/chanserv/access.cpp | 2 +- src/CMakeLists.txt | 8 - src/access.cpp | 24 +- src/base.cpp | 22 +- src/bots.cpp | 22 +- src/init.cpp | 181 +++++------ src/main.cpp | 39 +-- src/memos.cpp | 17 +- src/modulemanager.cpp | 5 +- src/nickalias.cpp | 34 ++- src/nickcore.cpp | 17 +- src/pipeengine.cpp | 81 +++++ src/regchannel.cpp | 180 ++++++----- src/serialize.cpp | 35 +-- src/socketengines/pipeengine_eventfd.cpp | 39 --- src/socketengines/pipeengine_pipe.cpp | 62 ---- src/sockets.cpp | 17 +- src/xline.cpp | 21 +- 61 files changed, 1240 insertions(+), 1094 deletions(-) delete mode 100644 cmake/eventfd_test.cpp delete mode 100644 include/signals.h create mode 100644 src/pipeengine.cpp delete mode 100644 src/socketengines/pipeengine_eventfd.cpp delete mode 100644 src/socketengines/pipeengine_pipe.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index addc01424..ad3b9ca06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -353,13 +353,6 @@ check_function_exists(epoll_wait HAVE_EPOLL) check_function_exists(poll HAVE_POLL) check_function_exists(kqueue HAVE_KQUEUE) -# Check if eventfd works -try_run(EVENTFD_TEST_RUN_RESULT EVENTFD_TEST_COMPILE_RESULT ${CMAKE_CURRENT_BINARY_DIR} ${Anope_SOURCE_DIR}/cmake/eventfd_test.cpp) -set(HAVE_EVENTFD FALSE) -if (EVENTFD_TEST_COMPILE_RESULT AND EVENTFD_TEST_RUN_RESULT EQUAL 1) - set(HAVE_EVENTFD TRUE) -endif(EVENTFD_TEST_COMPILE_RESULT AND EVENTFD_TEST_RUN_RESULT EQUAL 1) - # Strip the leading and trailing spaces from the compile flags if(CXXFLAGS) strip_string(${CXXFLAGS} CXXFLAGS) diff --git a/cmake/eventfd_test.cpp b/cmake/eventfd_test.cpp deleted file mode 100644 index ab45b653f..000000000 --- a/cmake/eventfd_test.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include - -int main() -{ - int i = eventfd(0, EFD_NONBLOCK); - return i >= 0 ? 1 : 0; -} - diff --git a/data/example.conf b/data/example.conf index 8396ffce2..205a0ead9 100644 --- a/data/example.conf +++ b/data/example.conf @@ -1135,6 +1135,15 @@ db_flatfile * The database name db_flatfile should use */ database = "anope.db" + + /* + * If enabled, services will fork a child process to save databases. + * + * This is only useful with very large databases, with hundreds + * of thousands of objects, that have a noticable delay from + * writing databases. + */ + fork = no } /* @@ -1154,7 +1163,8 @@ db_flatfile * This module allows saving and loading databases using one of the SQL engines. * This module reads and writes to SQL in real time. Changes to the SQL tables * will be immediately reflected into Anope. This module should not be loaded - * in conjunction with db_sql. + * in conjunction with db_sql, except during the initial import of existing + * databases to SQL. */ #module { name = "db_sql_live" } diff --git a/include/access.h b/include/access.h index 918dd7be6..f011ed438 100644 --- a/include/access.h +++ b/include/access.h @@ -87,7 +87,7 @@ class CoreExport ChanAccess : public Serializable ChanAccess(AccessProvider *p); virtual ~ChanAccess(); - Serialize::Data Serialize() const anope_override; + void Serialize(Serialize::Data &data) const anope_override; static Serializable* Unserialize(Serializable *obj, Serialize::Data &); /** Check if this access entry matches the given user or account diff --git a/include/account.h b/include/account.h index a45bf8c90..da855480c 100644 --- a/include/account.h +++ b/include/account.h @@ -122,7 +122,7 @@ class CoreExport NickAlias : public Serializable, public Extensible, public Flag NickAlias(const Anope::string &nickname, NickCore *nickcore); ~NickAlias(); - Serialize::Data Serialize() const anope_override; + void Serialize(Serialize::Data &data) const anope_override; static Serializable* Unserialize(Serializable *obj, Serialize::Data &); /** Release a nick @@ -227,7 +227,7 @@ class CoreExport NickCore : public Serializable, public Extensible, public Flags NickCore(const Anope::string &nickdisplay); ~NickCore(); - Serialize::Data Serialize() const anope_override; + void Serialize(Serialize::Data &data) const anope_override; static Serializable* Unserialize(Serializable *obj, Serialize::Data &); /** Changes the display for this account diff --git a/include/anope.h b/include/anope.h index 30ca48e69..c241524ab 100644 --- a/include/anope.h +++ b/include/anope.h @@ -13,6 +13,8 @@ #ifndef ANOPE_H #define ANOPE_H +#include + #include "hashcomp.h" namespace Anope @@ -282,9 +284,12 @@ namespace Anope * Stream insertion operator, must be friend because they cannot be inside the class. */ friend std::ostream &operator<<(std::ostream &os, const string &_str); + friend std::istream &operator>>(std::istream &is, string &_str); }; inline std::ostream &operator<<(std::ostream &os, const string &_str) { return os << _str._string; } + /* This is not standard to make operator>> behave like operator<< in that it will allow extracting a whole line, not just one word */ + inline std::istream &operator>>(std::istream &is, string &_str) { return std::getline(is, _str._string); } inline const string operator+(char chr, const string &str) { string tmp(chr); tmp += str; return tmp; } inline const string operator+(const char *_str, const string &str) { string tmp(_str); tmp += str; return tmp; } @@ -318,6 +323,8 @@ namespace Anope /** The value to return from main() */ extern int ReturnValue; + + extern sig_atomic_t Signal; extern bool Quitting; extern bool Restarting; extern Anope::string QuitReason; @@ -376,6 +383,10 @@ namespace Anope */ extern void Fork(); + /** Does something with the signal in Anope::Signal + */ + extern void HandleSignal(); + /** One of the first functions called, does general initialization such as reading * command line args, loading the configuration, doing the initial fork() if necessary, * initializating language support, loading modules, and loading databases. diff --git a/include/base.h b/include/base.h index 178bb1937..6fb974ec4 100644 --- a/include/base.h +++ b/include/base.h @@ -17,7 +17,7 @@ class CoreExport Base { /* References to this base class */ - std::set references; + std::set *references; public: Base(); virtual ~Base(); diff --git a/include/bots.h b/include/bots.h index 9b613715b..01c752124 100644 --- a/include/bots.h +++ b/include/bots.h @@ -63,7 +63,7 @@ class CoreExport BotInfo : public User, public Flags, public Serializab */ virtual ~BotInfo(); - Serialize::Data Serialize() const; + void Serialize(Serialize::Data &data) const; static Serializable* Unserialize(Serializable *obj, Serialize::Data &); void GenerateUID(); diff --git a/include/extensible.h b/include/extensible.h index bd7c8c76d..c1de409ed 100644 --- a/include/extensible.h +++ b/include/extensible.h @@ -38,22 +38,25 @@ class CoreExport Extensible { private: typedef std::map extensible_map; - extensible_map extension_items; + extensible_map *extension_items; public: - /** Default constructor, does nothing + /** Default constructor */ - Extensible() { } + Extensible() : extension_items(NULL) { } /** Destructor, deletes all of the extensible items in this object * then clears the map */ virtual ~Extensible() { - for (extensible_map::iterator it = extension_items.begin(), it_end = extension_items.end(); it != it_end; ++it) - if (it->second) - it->second->OnDelete(); - extension_items.clear(); + if (extension_items) + { + for (extensible_map::iterator it = extension_items->begin(), it_end = extension_items->end(); it != it_end; ++it) + if (it->second) + it->second->OnDelete(); + delete extension_items; + } } /** Extend an Extensible class. @@ -70,7 +73,9 @@ class CoreExport Extensible void Extend(const Anope::string &key, ExtensibleItem *p) { this->Shrink(key); - this->extension_items[key] = p; + if (!extension_items) + extension_items = new extensible_map(); + (*this->extension_items)[key] = p; } /** Shrink an Extensible class. @@ -83,8 +88,11 @@ class CoreExport Extensible */ bool Shrink(const Anope::string &key) { - extensible_map::iterator it = this->extension_items.find(key); - if (it != this->extension_items.end()) + if (!extension_items) + return false; + + extensible_map::iterator it = this->extension_items->find(key); + if (it != this->extension_items->end()) { if (it->second != NULL) it->second->OnDelete(); @@ -92,7 +100,7 @@ class CoreExport Extensible * returns the number of elements removed, std::map * is single-associative so this should only be 0 or 1 */ - return this->extension_items.erase(key) > 0; + return this->extension_items->erase(key) > 0; } return false; @@ -105,9 +113,12 @@ class CoreExport Extensible */ template T GetExt(const Anope::string &key) const { - extensible_map::const_iterator it = this->extension_items.find(key); - if (it != this->extension_items.end()) - return anope_dynamic_static_cast(it->second); + if (this->extension_items) + { + extensible_map::const_iterator it = this->extension_items->find(key); + if (it != this->extension_items->end()) + return anope_dynamic_static_cast(it->second); + } return NULL; } @@ -119,7 +130,7 @@ class CoreExport Extensible */ bool HasExt(const Anope::string &key) const { - return this->extension_items.count(key) > 0; + return this->extension_items != NULL && this->extension_items->count(key) > 0; } /** Get a list of all extension items names. @@ -129,8 +140,9 @@ class CoreExport Extensible */ void GetExtList(std::deque &list) const { - for (extensible_map::const_iterator it = extension_items.begin(), it_end = extension_items.end(); it != it_end; ++it) - list.push_back(it->first); + if (extension_items) + for (extensible_map::const_iterator it = extension_items->begin(), it_end = extension_items->end(); it != it_end; ++it) + list.push_back(it->first); } }; diff --git a/include/memo.h b/include/memo.h index 9fdbb3cab..9a7acceb9 100644 --- a/include/memo.h +++ b/include/memo.h @@ -31,7 +31,7 @@ class CoreExport Memo : public Flags, public Serializable public: Memo(); - Serialize::Data Serialize() const anope_override; + void Serialize(Serialize::Data &data) const anope_override; static Serializable* Unserialize(Serializable *obj, Serialize::Data &); Anope::string owner; diff --git a/include/module.h b/include/module.h index e6b63bdfb..197899ca8 100644 --- a/include/module.h +++ b/include/module.h @@ -40,7 +40,6 @@ #include "servers.h" #include "service.h" #include "services.h" -#include "signals.h" #include "socketengine.h" #include "sockets.h" #include "threadengine.h" diff --git a/include/regchannel.h b/include/regchannel.h index 9e01963b8..1cefe27dd 100644 --- a/include/regchannel.h +++ b/include/regchannel.h @@ -143,7 +143,7 @@ struct CoreExport BadWord : Serializable BadWordType type; BadWord() : Serializable("BadWord") { } - Serialize::Data Serialize() const anope_override; + void Serialize(Serialize::Data &data) const anope_override; static Serializable* Unserialize(Serializable *obj, Serialize::Data &); }; @@ -172,7 +172,7 @@ class CoreExport AutoKick : public Flags, public Serializable time_t last_used; AutoKick(); - Serialize::Data Serialize() const anope_override; + void Serialize(Serialize::Data &data) const anope_override; static Serializable* Unserialize(Serializable *obj, Serialize::Data &); }; @@ -188,7 +188,7 @@ struct CoreExport ModeLock : Serializable ModeLock(ChannelInfo *ch, bool s, ChannelModeName n, const Anope::string &p, const Anope::string &se = "", time_t c = Anope::CurTime); - Serialize::Data Serialize() const anope_override; + void Serialize(Serialize::Data &data) const anope_override; static Serializable* Unserialize(Serializable *obj, Serialize::Data &); }; @@ -206,7 +206,7 @@ struct CoreExport LogSetting : Serializable time_t created; LogSetting() : Serializable("LogSetting") { } - Serialize::Data Serialize() const anope_override; + void Serialize(Serialize::Data &data) const anope_override; static Serializable* Unserialize(Serializable *obj, Serialize::Data &); }; @@ -263,7 +263,7 @@ class CoreExport ChannelInfo : public Serializable, public Extensible, public Fl ~ChannelInfo(); - Serialize::Data Serialize() const anope_override; + void Serialize(Serialize::Data &data) const anope_override; static Serializable* Unserialize(Serializable *obj, Serialize::Data &); /** Change the founder of the channek diff --git a/include/serialize.h b/include/serialize.h index 8b8497c40..1a16e5b35 100644 --- a/include/serialize.h +++ b/include/serialize.h @@ -20,41 +20,24 @@ namespace Serialize { - enum DataType + class Data { - DT_TEXT, - DT_INT - }; - - class CoreExport stringstream : public std::stringstream - { - private: - Serialize::DataType type; - unsigned _max; - public: - stringstream(); - stringstream(const stringstream &ss); - Anope::string astr() const; - - template std::istream &operator>>(T &val) + enum Type { - std::istringstream is(this->str()); - is >> val; - return *this; - } - std::istream &operator>>(Anope::string &val); + DT_TEXT, + DT_INT + }; - bool operator==(const stringstream &other) const; - bool operator!=(const stringstream &other) const; + virtual ~Data() { } - stringstream &SetType(Serialize::DataType t); - Serialize::DataType GetType() const; - stringstream &SetMax(unsigned m); - unsigned GetMax() const; - }; + virtual std::iostream& operator[](const Anope::string &key) = 0; - typedef std::map Data; + virtual bool IsEqual(Data *other) { throw CoreException("Not supported"); } + + virtual void SetType(const Anope::string &key, Type t) { } + virtual Type GetType(const Anope::string &key) const { return DT_TEXT; } + }; extern void RegisterTypes(); @@ -64,7 +47,7 @@ namespace Serialize } /** A serialziable object. Serializable objects can be serialized into - * a map of stringstreams (Serialize::Data), and then reconstructed or + * abstract data types (Serialize::Data), and then reconstructed or * updated later at any time. */ class CoreExport Serializable : public virtual Base @@ -82,7 +65,7 @@ class CoreExport Serializable : public virtual Base /* Iterator into serializable_items */ std::list::iterator s_iter; /* The last serialized form of this object commited to the database */ - Serialize::Data last_commit; + Serialize::Data *last_commit; /* The last time this object was commited to the database */ time_t last_commit_time; @@ -108,8 +91,8 @@ class CoreExport Serializable : public virtual Base */ void QueueUpdate(); - bool IsCached(); - void UpdateCache(); + bool IsCached(Serialize::Data *); + void UpdateCache(Serialize::Data *); bool IsTSCached(); void UpdateTS(); @@ -117,9 +100,9 @@ class CoreExport Serializable : public virtual Base /** Get the type of serializable object this is * @return The serializable object type */ - Serialize::Type* GetSerializableType() const; + Serialize::Type* GetSerializableType() const { return this->s_type; } - virtual Serialize::Data Serialize() const = 0; + virtual void Serialize(Serialize::Data &data) const = 0; static const std::list &GetItems(); }; @@ -164,7 +147,7 @@ class CoreExport Serialize::Type /** Gets the name for this type * @return The name, eg "NickAlias" */ - const Anope::string &GetName(); + const Anope::string &GetName() { return this->name; } /** Unserialized an object. * @param obj NULL if this object doesn't yet exist. If this isn't NULL, instead @@ -187,7 +170,7 @@ class CoreExport Serialize::Type */ void UpdateTimestamp(); - Module* GetOwner() const; + Module* GetOwner() const { return this->owner; } static Serialize::Type *Find(const Anope::string &name); diff --git a/include/signals.h b/include/signals.h deleted file mode 100644 index 771404f52..000000000 --- a/include/signals.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * (C) 2003-2012 Anope Team - * Contact us at team@anope.org - * - * Please read COPYING and README for further details. - * - * Based on the original code of Epona by Lara. - * Based on the original code of Services by Andy Church. - * - */ - -#ifndef SIGNAL_H -#define SIGNAL_H - -#include - -#include "sockets.h" - -/** Represents a signal handler - */ -class Signal : public Pipe -{ - static std::vector SignalHandlers; - static void SignalHandler(int signal); - - struct sigaction action, old; - public: - int signal; - - /** Constructor - * @param s The signal to listen for - */ - Signal(int s); - ~Signal(); - - /** - * Called when the signal is received. - * Note this is not *immediatly* called when the signal is received, - * but it is saved and called at a later time when we are not doing something - * important. This is always called on the main thread, even on systems that - * spawn threads for signals, like Windows. - */ - virtual void OnNotify() anope_override = 0; -}; - -#endif - diff --git a/include/socketengine.h b/include/socketengine.h index 5f4bc5980..696f69c56 100644 --- a/include/socketengine.h +++ b/include/socketengine.h @@ -18,7 +18,7 @@ class CoreExport SocketEngine { - static const int DefaultSize = 8; // Uplink, DNS, Signal handlers, Mode stacker + static const int DefaultSize = 2; // Uplink, mode stacker public: /* Map of sockets */ static std::map Sockets; diff --git a/include/sockets.h b/include/sockets.h index 1ef10d0c8..ae7d6e798 100644 --- a/include/sockets.h +++ b/include/sockets.h @@ -225,15 +225,11 @@ class CoreExport Socket : public Flags */ bool IsIPv6() const; - /** Mark a socket as blocking + /** Mark a socket as (non)blocking + * @param state true to enable blocking, false to disable blocking * @return true if the socket is now blocking */ - bool SetBlocking(); - - /** Mark a socket as non-blocking - * @return true if the socket is now non-blocking - */ - bool SetNonBlocking(); + bool SetBlocking(bool state); /** Bind the socket to an ip and port * @param ip The ip @@ -456,7 +452,7 @@ class CoreExport ClientSocket : public virtual Socket class CoreExport Pipe : public Socket { public: - /** The FD of the write pipe (if this isn't evenfd) + /** The FD of the write pipe * this->sock is the readfd */ int write_pipe; @@ -468,7 +464,28 @@ class CoreExport Pipe : public Socket */ bool ProcessRead() anope_override; + /** Write data to this pipe + * @param data The data to write + * @param sz The amount of data to wirite + */ + void Write(const char *data, size_t sz); + inline void Write(const Anope::string &data) { this->Write(data.c_str(), data.length() + 1); } + + /** Read data from this pipe + * @param data A buffer to read data into + * @param sz The size of the buffer + * @return The amount of data read + */ + int Read(char *data, size_t sz); + + /** Mark the write end of this pipe (non)blocking + * @param state true to enable blocking, false to disable blocking + * @return true if the socket is now blocking + */ + bool SetWriteBlocking(bool state); + /** Called when this pipe needs to be woken up + * Is the same as Write("\0", 1) */ void Notify(); diff --git a/include/xline.h b/include/xline.h index 5d407d4ba..374b75a6a 100644 --- a/include/xline.h +++ b/include/xline.h @@ -42,7 +42,7 @@ class CoreExport XLine : public Serializable bool HasNickOrReal() const; bool IsRegex() const; - Serialize::Data Serialize() const anope_override; + void Serialize(Serialize::Data &data) const anope_override; static Serializable* Unserialize(Serializable *obj, Serialize::Data &data); }; diff --git a/modules/commands/cs_entrymsg.cpp b/modules/commands/cs_entrymsg.cpp index 85e428cc1..ab9c4d0c4 100644 --- a/modules/commands/cs_entrymsg.cpp +++ b/modules/commands/cs_entrymsg.cpp @@ -28,16 +28,12 @@ struct EntryMsg : Serializable this->when = ct; } - Serialize::Data Serialize() const anope_override + void Serialize(Serialize::Data &data) const anope_override { - Serialize::Data data; - data["ci"] << this->ci->name; data["creator"] << this->creator; data["message"] << this->message; - data["when"].SetType(Serialize::DT_INT) << this->when; - - return data; + data.SetType("when", Serialize::Data::DT_INT); data["when"] << this->when; } static Serializable* Unserialize(Serializable *obj, Serialize::Data &data); @@ -52,7 +48,13 @@ struct EntryMessageList : Serialize::Checker >, Extensib Serializable* EntryMsg::Unserialize(Serializable *obj, Serialize::Data &data) { - ChannelInfo *ci = ChannelInfo::Find(data["ci"].astr()); + Anope::string sci, screator, smessage; + + data["ci"] >> sci; + data["creator"] >> screator; + data["message"] >> smessage; + + ChannelInfo *ci = ChannelInfo::Find(sci); if (!ci) return NULL; @@ -73,7 +75,7 @@ Serializable* EntryMsg::Unserialize(Serializable *obj, Serialize::Data &data) ci->Extend("cs_entrymsg", messages); } - EntryMsg *m = new EntryMsg(ci, data["creator"].astr(), data["message"].astr()); + EntryMsg *m = new EntryMsg(ci, screator, smessage); (*messages)->push_back(m); return m; } diff --git a/modules/commands/cs_fantasy_stats.cpp b/modules/commands/cs_fantasy_stats.cpp index bbe135298..5b25cccce 100644 --- a/modules/commands/cs_fantasy_stats.cpp +++ b/modules/commands/cs_fantasy_stats.cpp @@ -14,16 +14,16 @@ #include "module.h" #include "../extra/sql.h" -class MySQLInterface : public SQLInterface +class MySQLInterface : public SQL::Interface { public: - MySQLInterface(Module *o) : SQLInterface(o) { } + MySQLInterface(Module *o) : SQL::Interface(o) { } - void OnResult(const SQLResult &r) anope_override + void OnResult(const SQL::Result &r) anope_override { } - void OnError(const SQLResult &r) anope_override + void OnError(const SQL::Result &r) anope_override { if (!r.GetQuery().query.empty()) Log(LOG_DEBUG) << "Chanstats: Error executing query " << r.finished_query << ": " << r.GetError(); @@ -66,7 +66,7 @@ class CSStats : public Module { CommandCSStats commandcsstats; CommandCSGStats commandcsgstats; - ServiceReference sql; + ServiceReference sql; MySQLInterface sqlinterface; Anope::string prefix; public: @@ -86,17 +86,17 @@ class CSStats : public Module ConfigReader config; prefix = config.ReadValue("chanstats", "prefix", "anope_", 0); Anope::string engine = config.ReadValue("chanstats", "engine", "", 0); - this->sql = ServiceReference("SQLProvider", engine); + this->sql = ServiceReference("SQL::Provider", engine); } - SQLResult RunQuery(const SQLQuery &query) + SQL::Result RunQuery(const SQL::Query &query) { if (!this->sql) - throw SQLException("Unable to locate SQL reference, is m_mysql loaded and configured correctly?"); + throw SQL::Exception("Unable to locate SQL reference, is m_mysql loaded and configured correctly?"); - SQLResult res = this->sql->RunQuery(query); + SQL::Result res = this->sql->RunQuery(query); if (!res.GetError().empty()) - throw SQLException(res.GetError()); + throw SQL::Exception(res.GetError()); return res; } @@ -118,16 +118,16 @@ class CSStats : public Module try { - SQLQuery query; + SQL::Query query; query = "SELECT letters, words, line, smileys_happy+smileys_sad+smileys_other as smileys," "actions FROM `" + prefix + "chanstats` " "WHERE `nick` = @nick@ AND `chan` = @channel@ AND `type` = 'total';"; if (is_global) - query.setValue("channel", ""); + query.SetValue("channel", ""); else - query.setValue("channel", source.c->ci->name); - query.setValue("nick", display); - SQLResult res = this->RunQuery(query); + query.SetValue("channel", source.c->ci->name); + query.SetValue("nick", display); + SQL::Result res = this->RunQuery(query); if (res.Rows() > 0) { @@ -144,7 +144,7 @@ class CSStats : public Module else source.Reply(_("No stats for %s"), display.c_str()); } - catch (const SQLException &ex) + catch (const SQL::Exception &ex) { Log(LOG_DEBUG) << ex.GetReason(); } diff --git a/modules/commands/cs_fantasy_top.cpp b/modules/commands/cs_fantasy_top.cpp index 86e25f122..ca9572039 100644 --- a/modules/commands/cs_fantasy_top.cpp +++ b/modules/commands/cs_fantasy_top.cpp @@ -14,16 +14,16 @@ #include "module.h" #include "../extra/sql.h" -class MySQLInterface : public SQLInterface +class MySQLInterface : public SQL::Interface { public: - MySQLInterface(Module *o) : SQLInterface(o) { } + MySQLInterface(Module *o) : SQL::Interface(o) { } - void OnResult(const SQLResult &r) anope_override + void OnResult(const SQL::Result &r) anope_override { } - void OnError(const SQLResult &r) anope_override + void OnError(const SQL::Result &r) anope_override { if (!r.GetQuery().query.empty()) Log(LOG_DEBUG) << "Chanstats: Error executing query " << r.finished_query << ": " << r.GetError(); @@ -93,7 +93,7 @@ class CSTop : public Module CommandCSGTop commandcsgtop; CommandCSTop10 commandcstop10; CommandCSGTop10 commandcsgtop10; - ServiceReference sql; + ServiceReference sql; MySQLInterface sqlinterface; Anope::string prefix; @@ -115,17 +115,17 @@ class CSTop : public Module ConfigReader config; prefix = config.ReadValue("chanstats", "prefix", "anope_", 0); Anope::string engine = config.ReadValue("chanstats", "engine", "", 0); - this->sql = ServiceReference("SQLProvider", engine); + this->sql = ServiceReference("SQL::Provider", engine); } - SQLResult RunQuery(const SQLQuery &query) + SQL::Result RunQuery(const SQL::Query &query) { if (!this->sql) - throw SQLException("Unable to locate SQL reference, is m_mysql loaded and configured correctly?"); + throw SQL::Exception("Unable to locate SQL reference, is m_mysql loaded and configured correctly?"); - SQLResult res = sql->RunQuery(query); + SQL::Result res = sql->RunQuery(query); if (!res.GetError().empty()) - throw SQLException(res.GetError()); + throw SQL::Exception(res.GetError()); return res; } @@ -147,20 +147,20 @@ class CSTop : public Module try { - SQLQuery query; + SQL::Query query; query = "SELECT nick, letters, words, line, actions," "smileys_happy+smileys_sad+smileys_other as smileys " "FROM `" + prefix + "chanstats` " "WHERE `nick` != '' AND `chan` = @channel@ AND `type` = 'total' " "ORDER BY `letters` DESC LIMIT @limit@;"; - query.setValue("limit", limit, false); + query.SetValue("limit", limit, false); if (is_global) - query.setValue("channel", ""); + query.SetValue("channel", ""); else - query.setValue("channel", channel.c_str()); + query.SetValue("channel", channel.c_str()); - SQLResult res = this->RunQuery(query); + SQL::Result res = this->RunQuery(query); if (res.Rows() > 0) { @@ -176,7 +176,7 @@ class CSTop : public Module else source.Reply(_("No stats for %s"), is_global ? "Network" : channel.c_str()); } - catch (const SQLException &ex) + catch (const SQL::Exception &ex) { Log(LOG_DEBUG) << ex.GetReason(); } diff --git a/modules/commands/cs_seen.cpp b/modules/commands/cs_seen.cpp index 18a21d35e..8f90e2f08 100644 --- a/modules/commands/cs_seen.cpp +++ b/modules/commands/cs_seen.cpp @@ -38,30 +38,30 @@ struct SeenInfo : Serializable { } - Serialize::Data Serialize() const anope_override + void Serialize(Serialize::Data &data) const anope_override { - Serialize::Data data; - data["nick"] << nick; data["vhost"] << vhost; data["type"] << type; data["nick2"] << nick2; data["channel"] << channel; data["message"] << message; - data["last"].SetType(Serialize::DT_INT) << last; - - return data; + data.SetType("last", Serialize::Data::DT_INT); data["last"] << last; } static Serializable* Unserialize(Serializable *obj, Serialize::Data &data) { + Anope::string snick; + + data["nick"] >> snick; + SeenInfo *s; if (obj) s = anope_dynamic_static_cast(obj); else { /* ignore duplicate entries in the db, created by an old bug */ - s = FindInfo(data["nick"].str()); + s = FindInfo(snick); if (!s) s = new SeenInfo(); } diff --git a/modules/commands/cs_set_misc.cpp b/modules/commands/cs_set_misc.cpp index 3bf0e9bbd..1fb25c79a 100644 --- a/modules/commands/cs_set_misc.cpp +++ b/modules/commands/cs_set_misc.cpp @@ -22,20 +22,22 @@ struct CSMiscData : ExtensibleItem, Serializable { } - Serialize::Data Serialize() const anope_override + void Serialize(Serialize::Data &sdata) const anope_override { - Serialize::Data sdata; - sdata["ci"] << this->ci->name; sdata["name"] << this->name; sdata["data"] << this->data; - - return sdata; } static Serializable* Unserialize(Serializable *obj, Serialize::Data &data) { - ChannelInfo *ci = ChannelInfo::Find(data["ci"].astr()); + Anope::string sci, sname, sdata; + + data["ci"] >> sci; + data["name"] >> sname; + data["data"] >> sdata; + + ChannelInfo *ci = ChannelInfo::Find(sci); if (ci == NULL) return NULL; @@ -49,8 +51,8 @@ struct CSMiscData : ExtensibleItem, Serializable } else { - d = new CSMiscData(ci, data["name"].astr(), data["data"].astr()); - ci->Extend(data["name"].astr(), d); + d = new CSMiscData(ci, sname, sdata); + ci->Extend(sname, d); } return d; diff --git a/modules/commands/cs_suspend.cpp b/modules/commands/cs_suspend.cpp index b380d4c97..097de1823 100644 --- a/modules/commands/cs_suspend.cpp +++ b/modules/commands/cs_suspend.cpp @@ -22,19 +22,19 @@ struct ChanSuspend : ExtensibleItem, Serializable { } - Serialize::Data Serialize() const anope_override + void Serialize(Serialize::Data &sd) const anope_override { - Serialize::Data sd; - sd["chan"] << this->chan; sd["when"] << this->when; - - return sd; } static Serializable* Unserialize(Serializable *obj, Serialize::Data &sd) { - ChannelInfo *ci = ChannelInfo::Find(sd["chan"].astr()); + Anope::string schan; + + sd["chan"] >> schan; + + ChannelInfo *ci = ChannelInfo::Find(schan); if (ci == NULL) return NULL; diff --git a/modules/commands/hs_request.cpp b/modules/commands/hs_request.cpp index c7cd75c11..6cd2e6811 100644 --- a/modules/commands/hs_request.cpp +++ b/modules/commands/hs_request.cpp @@ -32,21 +32,20 @@ struct HostRequest : ExtensibleItem, Serializable HostRequest() : Serializable("HostRequest") { } - Serialize::Data Serialize() const anope_override + void Serialize(Serialize::Data &data) const anope_override { - Serialize::Data data; - data["nick"] << this->nick; data["ident"] << this->ident; data["host"] << this->host; - data["time"].SetType(Serialize::DT_INT) << this->time; - - return data; + data.SetType("time", Serialize::Data::DT_INT); data["time"] << this->time; } static Serializable* Unserialize(Serializable *obj, Serialize::Data &data) { - NickAlias *na = NickAlias::Find(data["nick"].astr()); + Anope::string snick; + data["nick"] >> snick; + + NickAlias *na = NickAlias::Find(snick); if (na == NULL) return NULL; diff --git a/modules/commands/ns_ajoin.cpp b/modules/commands/ns_ajoin.cpp index ce92582f6..3a73772c1 100644 --- a/modules/commands/ns_ajoin.cpp +++ b/modules/commands/ns_ajoin.cpp @@ -28,23 +28,23 @@ struct AJoinEntry : Serializable AJoinEntry() : Serializable("AJoinEntry") { } - Serialize::Data Serialize() const anope_override + void Serialize(Serialize::Data &sd) const anope_override { - Serialize::Data sd; - if (!this->owner) - return sd; + return; sd["owner"] << this->owner->display; sd["channel"] << this->channel; sd["key"] << this->key; - - return sd; } static Serializable* Unserialize(Serializable *obj, Serialize::Data &sd) { - NickCore *nc = NickCore::Find(sd["owner"].astr()); + Anope::string sowner; + + sd["owner"] >> sowner; + + NickCore *nc = NickCore::Find(sowner); if (nc == NULL) return NULL; diff --git a/modules/commands/ns_set_misc.cpp b/modules/commands/ns_set_misc.cpp index 1e088a425..b6dd672a3 100644 --- a/modules/commands/ns_set_misc.cpp +++ b/modules/commands/ns_set_misc.cpp @@ -23,20 +23,22 @@ struct NSMiscData : ExtensibleItem, Serializable { } - Serialize::Data Serialize() const anope_override + void Serialize(Serialize::Data &sdata) const anope_override { - Serialize::Data sdata; - sdata["nc"] << this->nc->display; sdata["name"] << this->name; sdata["data"] << this->data; - - return sdata; } static Serializable* Unserialize(Serializable *obj, Serialize::Data &data) { - NickCore *nc = NickCore::Find(data["nc"].astr()); + Anope::string snc, sname, sdata; + + data["nc"] >> snc; + data["name"] >> sname; + data["data"] >> sdata; + + NickCore *nc = NickCore::Find(snc); if (nc == NULL) return NULL; @@ -50,8 +52,8 @@ struct NSMiscData : ExtensibleItem, Serializable } else { - d = new NSMiscData(nc, data["name"].astr(), data["data"].astr()); - nc->Extend(data["name"].astr(), d); + d = new NSMiscData(nc, sname, sdata); + nc->Extend(sname, d); } return d; diff --git a/modules/commands/ns_suspend.cpp b/modules/commands/ns_suspend.cpp index 6ff355437..63710ccca 100644 --- a/modules/commands/ns_suspend.cpp +++ b/modules/commands/ns_suspend.cpp @@ -22,19 +22,19 @@ struct NickSuspend : ExtensibleItem, Serializable { } - Serialize::Data Serialize() const anope_override + void Serialize(Serialize::Data &sd) const anope_override { - Serialize::Data sd; - sd["nick"] << this->nick; sd["when"] << this->when; - - return sd; } static Serializable* Unserialize(Serializable *obj, Serialize::Data &sd) { - const NickAlias *na = NickAlias::Find(sd["nick"].astr()); + Anope::string snick; + + sd["nick"] >> snick; + + const NickAlias *na = NickAlias::Find(snick); if (na == NULL) return NULL; diff --git a/modules/commands/os_dns.cpp b/modules/commands/os_dns.cpp index 769a42f26..6e5689396 100644 --- a/modules/commands/os_dns.cpp +++ b/modules/commands/os_dns.cpp @@ -49,17 +49,13 @@ class DNSServer : public Serializable } - Serialize::Data Serialize() const anope_override + void Serialize(Serialize::Data &data) const anope_override { - Serialize::Data data; - data["server_name"] << server_name; for (unsigned i = 0; i < ips.size(); ++i) data["ip" + stringify(i)] << ips[i]; data["limit"] << limit; data["pooled"] << pooled; - - return data; } static Serializable* Unserialize(Serializable *obj, Serialize::Data &data) @@ -72,12 +68,16 @@ class DNSServer : public Serializable req = new DNSServer(); data["server_name"] >> req->server_name; - for (unsigned i = 0; data.count("ip" + stringify(i)); ++i) + + for (unsigned i = 0; true; ++i) { Anope::string ip_str; data["ip" + stringify(i)] >> ip_str; + if (ip_str.empty()) + break; req->ips.push_back(ip_str); } + data["limit"] >> req->limit; data["pooled"] >> req->pooled; diff --git a/modules/commands/os_forbid.h b/modules/commands/os_forbid.h index 1fce928db..07619f65e 100644 --- a/modules/commands/os_forbid.h +++ b/modules/commands/os_forbid.h @@ -19,7 +19,7 @@ struct ForbidData : Serializable ForbidType type; ForbidData() : Serializable("ForbidData") { } - Serialize::Data Serialize() const anope_override; + void Serialize(Serialize::Data &data) const anope_override; static Serializable* Unserialize(Serializable *obj, Serialize::Data &data); }; @@ -39,18 +39,14 @@ class ForbidService : public Service static ServiceReference forbid_service("ForbidService", "forbid"); -Serialize::Data ForbidData::Serialize() const +void ForbidData::Serialize(Serialize::Data &data) const { - Serialize::Data data; - data["mask"] << this->mask; data["creator"] << this->creator; data["reason"] << this->reason; data["created"] << this->created; data["expires"] << this->expires; data["type"] << this->type; - - return data; } Serializable* ForbidData::Unserialize(Serializable *obj, Serialize::Data &data) diff --git a/modules/commands/os_ignore.h b/modules/commands/os_ignore.h index 003d88b5b..1990d95c4 100644 --- a/modules/commands/os_ignore.h +++ b/modules/commands/os_ignore.h @@ -18,7 +18,7 @@ struct IgnoreData : Serializable time_t time; /* When do we stop ignoring them? */ IgnoreData() : Serializable("IgnoreData") { } - Serialize::Data Serialize() const anope_override; + void Serialize(Serialize::Data &data) const anope_override; static Serializable* Unserialize(Serializable *obj, Serialize::Data &data); }; @@ -43,16 +43,12 @@ class IgnoreService : public Service static ServiceReference ignore_service("IgnoreService", "ignore"); -Serialize::Data IgnoreData::Serialize() const +void IgnoreData::Serialize(Serialize::Data &data) const { - Serialize::Data data; - data["mask"] << this->mask; data["creator"] << this->creator; data["reason"] << this->reason; data["time"] << this->time; - - return data; } Serializable* IgnoreData::Unserialize(Serializable *obj, Serialize::Data &data) @@ -70,9 +66,14 @@ Serializable* IgnoreData::Unserialize(Serializable *obj, Serialize::Data &data) return ign; } + Anope::string smask, screator, sreason; time_t t; + + data["mask"] >> smask; + data["creator"] >> screator; + data["reason"] >> sreason; data["time"] >> t; - return ignore_service->AddIgnore(data["mask"].astr(), data["creator"].astr(), data["reason"].astr(), t); + return ignore_service->AddIgnore(smask, screator, sreason, t); } diff --git a/modules/commands/os_news.h b/modules/commands/os_news.h index 44525b963..b1e717455 100644 --- a/modules/commands/os_news.h +++ b/modules/commands/os_news.h @@ -23,7 +23,7 @@ struct NewsItem : Serializable time_t time; NewsItem() : Serializable("NewsItem") { } - Serialize::Data Serialize() const anope_override; + void Serialize(Serialize::Data &data) const anope_override; static Serializable* Unserialize(Serializable *obj, Serialize::Data &data); }; @@ -41,16 +41,12 @@ class NewsService : public Service static ServiceReference news_service("NewsService", "news"); -Serialize::Data NewsItem::Serialize() const +void NewsItem::Serialize(Serialize::Data &data) const { - Serialize::Data data; - data["type"] << this->type; data["text"] << this->text; data["who"] << this->who; data["time"] << this->time; - - return data; } Serializable* NewsItem::Unserialize(Serializable *obj, Serialize::Data &data) diff --git a/modules/commands/os_oper.cpp b/modules/commands/os_oper.cpp index 882455898..b014a1271 100644 --- a/modules/commands/os_oper.cpp +++ b/modules/commands/os_oper.cpp @@ -17,22 +17,23 @@ struct MyOper : Oper, Serializable { MyOper(const Anope::string &n, OperType *o) : Oper(n, o), Serializable("Oper") { } - Serialize::Data Serialize() const anope_override + void Serialize(Serialize::Data &data) const anope_override { - Serialize::Data data; - data["name"] << this->name; data["type"] << this->ot->GetName(); - - return data; } static Serializable* Unserialize(Serializable *obj, Serialize::Data &data) { - OperType *ot = OperType::Find(data["type"].astr()); + Anope::string stype, sname; + + data["type"] >> stype; + data["name"] >> sname; + + OperType *ot = OperType::Find(stype); if (ot == NULL) return NULL; - NickCore *nc = NickCore::Find(data["name"].astr()); + NickCore *nc = NickCore::Find(sname); if (nc == NULL) return NULL; diff --git a/modules/commands/os_session.h b/modules/commands/os_session.h index 7304b21ae..6d1dd917d 100644 --- a/modules/commands/os_session.h +++ b/modules/commands/os_session.h @@ -20,7 +20,7 @@ struct Exception : Serializable time_t expires; /* Time when it expires. 0 == no expiry */ Exception() : Serializable("Exception") { } - Serialize::Data Serialize() const anope_override; + void Serialize(Serialize::Data &data) const anope_override; static Serializable* Unserialize(Serializable *obj, Serialize::Data &data); }; @@ -53,18 +53,14 @@ class SessionService : public Service static ServiceReference session_service("SessionService", "session"); -Serialize::Data Exception::Serialize() const +void Exception::Serialize(Serialize::Data &data) const { - Serialize::Data data; - data["mask"] << this->mask; data["limit"] << this->limit; data["who"] << this->who; data["reason"] << this->reason; data["time"] << this->time; data["expires"] << this->expires; - - return data; } Serializable* Exception::Unserialize(Serializable *obj, Serialize::Data &data) diff --git a/modules/commands/os_stats.cpp b/modules/commands/os_stats.cpp index bb029eed9..18e2e89fd 100644 --- a/modules/commands/os_stats.cpp +++ b/modules/commands/os_stats.cpp @@ -18,14 +18,10 @@ struct Stats : Serializable { Stats() : Serializable("Stats") { } - Serialize::Data Serialize() const anope_override + void Serialize(Serialize::Data &data) const anope_override { - Serialize::Data data; - data["maxusercnt"] << MaxUserCount; data["maxusertime"] << MaxUserTime; - - return data; } static Serializable* Unserialize(Serializable *obj, Serialize::Data &data) diff --git a/modules/database/db_flatfile.cpp b/modules/database/db_flatfile.cpp index acb287cee..66715b9f8 100644 --- a/modules/database/db_flatfile.cpp +++ b/modules/database/db_flatfile.cpp @@ -12,145 +12,202 @@ #include "module.h" -class DBFlatFile : public Module +class SaveData : public Serialize::Data { - Anope::string DatabaseFile; - Anope::string BackupFile; - /* Day the last backup was on */ - int LastDay; - /* Backup file names */ - std::list Backups; + public: + std::fstream *fs; + + SaveData() : fs(NULL) { } + + std::iostream& operator[](const Anope::string &key) anope_override + { + *fs << "\nDATA " << key << " "; + return *fs; + } +}; +class LoadData : public Serialize::Data +{ public: - DBFlatFile(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE) + std::fstream *fs; + std::map data; + std::stringstream ss; + bool read; + + LoadData() : fs(NULL), read(false) { } + + std::iostream& operator[](const Anope::string &key) anope_override { - this->SetAuthor("Anope"); + if (!read) + { + for (Anope::string token; std::getline(*this->fs, token.str());) + { + if (token.find("DATA ") != 0) + break; - Implementation i[] = { I_OnReload, I_OnLoadDatabase, I_OnSaveDatabase }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + size_t sp = token.find(' ', 5); // Skip DATA + if (sp != Anope::string::npos) + data[token.substr(5, sp - 5)] = token.substr(sp + 1); + } - OnReload(); + read = true; + } - LastDay = 0; + ss.clear(); + this->ss << this->data[key]; + return this->ss; } + + void Reset() + { + read = false; + data.clear(); + } +}; + +class DBFlatFile : public Module, public Pipe +{ + Anope::string database_file; + /* Day the last backup was on */ + int last_day; + /* Backup file names */ + std::map > backups; + bool use_fork; void BackupDatabase() { - /* Do not backup a database that doesn't exist */ - if (!Anope::IsFile(DatabaseFile)) - return; - - time_t now = Anope::CurTime; - tm *tm = localtime(&now); + tm *tm = localtime(&Anope::CurTime); - if (tm->tm_mday != LastDay) + if (tm->tm_mday != last_day) { - LastDay = tm->tm_mday; - Anope::string newname = BackupFile + "." + stringify(tm->tm_year) + "." + stringify(tm->tm_mon) + "." + stringify(tm->tm_mday); + last_day = tm->tm_mday; - /* Backup already exists */ - if (IsFile(newname)) - return; + const std::vector &type_order = Serialize::Type::GetTypeOrder(); - Log(LOG_DEBUG) << "db_flatfile: Attemping to rename " << DatabaseFile << " to " << newname; - if (rename(DatabaseFile.c_str(), newname.c_str())) - { - Log(this) << "Unable to back up database!"; + std::set dbs; + dbs.insert(database_file); - if (!Config->NoBackupOkay) - Anope::Quitting = true; + for (unsigned i = 0; i < type_order.size(); ++i) + { + Serialize::Type *stype = Serialize::Type::Find(type_order[i]); - return; + if (stype && stype->GetOwner()) + dbs.insert("module_" + stype->GetOwner()->name + ".db"); } - Backups.push_back(newname); - if (Config->KeepBackups > 0 && Backups.size() > static_cast(Config->KeepBackups)) + for (std::set::const_iterator it = dbs.begin(), it_end = dbs.end(); it != it_end; ++it) { - unlink(Backups.front().c_str()); - Backups.pop_front(); + const Anope::string &oldname = Anope::DataDir + "/" + *it; + Anope::string newname = Anope::DataDir + "/backups/" + *it + "." + stringify(tm->tm_year) + "." + stringify(tm->tm_mon) + "." + stringify(tm->tm_mday); + + /* Backup already exists */ + if (Anope::IsFile(newname)) + continue; + + Log(LOG_DEBUG) << "db_flatfile: Attemping to rename " << *it << " to " << newname; + if (rename(oldname.c_str(), newname.c_str())) + { + Log(this) << "Unable to back up database " << *it << "!"; + + if (!Config->NoBackupOkay) + Anope::Quitting = true; + + continue; + } + + backups[*it].push_back(newname); + + if (Config->KeepBackups > 0 && backups[*it].size() > static_cast(Config->KeepBackups)) + { + unlink(backups[*it].front().c_str()); + backups[*it].pop_front(); + } } } } - void OnReload() anope_override + public: + DBFlatFile(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE), last_day(0), use_fork(false) { - ConfigReader config; - DatabaseFile = Anope::DataDir + "/" + config.ReadValue("db_flatfile", "database", "anope.db", 0); - BackupFile = Anope::DataDir + "/backups/" + config.ReadValue("db_flatfile", "database", "anope.db", 0); + this->SetAuthor("Anope"); + + Implementation i[] = { I_OnReload, I_OnLoadDatabase, I_OnSaveDatabase }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + OnReload(); } - EventReturn OnLoadDatabase() anope_override + void OnNotify() anope_override { - std::map databases; - databases[NULL] = new std::fstream(DatabaseFile.c_str(), std::ios_base::in); + char buf[512]; + int i = this->Read(buf, sizeof(buf) - 1); + if (i <= 0) + return; + buf[i] = 0; - if (!databases[NULL]->is_open()) + if (!*buf) { - delete databases[NULL]; - Log(this) << "Unable to open " << DatabaseFile << " for reading!"; - return EVENT_CONTINUE; + Log(this) << "Finished saving databases"; + return; } - const std::vector type_order = Serialize::Type::GetTypeOrder(); + Log(this) << "Error saving databases: " << buf; - for (unsigned i = 0; i < type_order.size(); ++i) - { - Serialize::Type *stype = Serialize::Type::Find(type_order[i]); - if (stype && !databases.count(stype->GetOwner())) - { - Anope::string db_name = Anope::DataDir + "/module_" + stype->GetOwner()->name + ".db"; - databases[stype->GetOwner()] = new std::fstream(db_name.c_str(), std::ios_base::in); - } - } + if (!Config->NoBackupOkay) + Anope::Quitting = true; + } - std::multimap objects; - for (std::map::iterator it = databases.begin(), it_end = databases.end(); it != it_end; ++it) - { - std::fstream *db = it->second; - Serialize::Type *st = NULL; - Serialize::Data data; - for (Anope::string buf, token; std::getline(*db, buf.str());) - { - spacesepstream sep(buf); + void OnReload() anope_override + { + ConfigReader config; + database_file = config.ReadValue("db_flatfile", "database", "anope.db", 0); + use_fork = config.ReadFlag("db_flatfile", "fork", "no", 0); + } - if (!sep.GetToken(token)) - continue; + EventReturn OnLoadDatabase() anope_override + { + const std::vector &type_order = Serialize::Type::GetTypeOrder(); + std::set tried_dbs; - if (token == "OBJECT" && sep.GetToken(token)) - { - st = Serialize::Type::Find(token); - data.clear(); - } - else if (token == "DATA" && st != NULL && sep.GetToken(token)) - data[token] << sep.GetRemaining(); - else if (token == "END" && st != NULL) - { - objects.insert(std::make_pair(st, data)); + const Anope::string &db_name = Anope::DataDir + "/" + database_file; - st = NULL; - data.clear(); - } - } + std::fstream fd(db_name.c_str(), std::ios_base::in); + if (!fd.is_open()) + { + Log(this) << "Unable to open " << db_name << " for reading!"; + return EVENT_STOP; } + std::map > positions; + + for (Anope::string buf; std::getline(fd, buf.str());) + if (buf.find("OBJECT ") == 0) + positions[buf.substr(7)].push_back(fd.tellg()); + + LoadData ld; + ld.fs = &fd; + for (unsigned i = 0; i < type_order.size(); ++i) { Serialize::Type *stype = Serialize::Type::Find(type_order[i]); - - std::multimap::iterator it = objects.find(stype), it_end = objects.upper_bound(stype); - if (it == objects.end()) + if (!stype || stype->GetOwner()) continue; - for (; it != it_end; ++it) - it->first->Unserialize(NULL, it->second); - } + + std::vector &pos = positions[stype->GetName()]; - for (std::map::iterator it = databases.begin(), it_end = databases.end(); it != it_end; ++it) - { - it->second->close(); - delete it->second; + for (unsigned j = 0; j < pos.size(); ++j) + { + fd.clear(); + fd.seekg(pos[j]); + + stype->Unserialize(NULL, ld); + ld.Reset(); + } } + fd.close(); + return EVENT_STOP; } @@ -159,61 +216,92 @@ class DBFlatFile : public Module { BackupDatabase(); - Anope::string tmp_db = DatabaseFile + ".tmp"; - if (IsFile(DatabaseFile)) - rename(DatabaseFile.c_str(), tmp_db.c_str()); - - std::map databases; - databases[NULL] = new std::fstream(DatabaseFile.c_str(), std::ios_base::out | std::ios_base::trunc); - if (!databases[NULL]->is_open()) + int i = -1; + if (use_fork) { - delete databases[NULL]; - Log(this) << "Unable to open " << DatabaseFile << " for writing"; - if (IsFile(tmp_db)) - rename(tmp_db.c_str(), DatabaseFile.c_str()); - return EVENT_CONTINUE; + i = fork(); + if (i > 0) + return EVENT_CONTINUE; + else if (i < 0) + Log(this) << "Unable to fork for database save"; } - const std::list &items = Serializable::GetItems(); - for (std::list::const_iterator it = items.begin(), it_end = items.end(); it != it_end; ++it) + try { - Serializable *base = *it; - Serialize::Type *s_type = base->GetSerializableType(); + std::map databases; - if (!s_type) - continue; + SaveData data; + const std::list &items = Serializable::GetItems(); + for (std::list::const_iterator it = items.begin(), it_end = items.end(); it != it_end; ++it) + { + Serializable *base = *it; + Serialize::Type *s_type = base->GetSerializableType(); + + if (!s_type) + continue; - Serialize::Data data = base->Serialize(); + data.fs = databases[s_type->GetOwner()]; - if (!databases.count(s_type->GetOwner())) + if (!data.fs) + { + Anope::string db_name; + if (s_type->GetOwner()) + db_name = Anope::DataDir + "/module_" + s_type->GetOwner()->name + ".db"; + else + db_name = Anope::DataDir + "/" + database_file; + + if (Anope::IsFile(db_name)) + rename(db_name.c_str(), (db_name + ".tmp").c_str()); + + data.fs = databases[s_type->GetOwner()] = new std::fstream(db_name.c_str(), std::ios_base::out | std::ios_base::trunc); + + if (!data.fs->is_open()) + { + Log(this) << "Unable to open " << db_name << " for writing"; + continue; + } + } + else if (!data.fs->is_open()) + continue; + + *data.fs << "OBJECT " << s_type->GetName(); + base->Serialize(data); + *data.fs << "\nEND\n"; + } + + for (std::map::iterator it = databases.begin(), it_end = databases.end(); it != it_end; ++it) { - Anope::string db_name = Anope::DataDir + "/module_" + s_type->GetOwner()->name + ".db"; - databases[s_type->GetOwner()] = new std::fstream(db_name.c_str(), std::ios_base::out | std::ios_base::trunc); + std::fstream *f = it->second; + const Anope::string &db_name = Anope::DataDir + "/" + (it->first ? (it->first->name + ".db") : database_file); + + if (!f->is_open() || !f->good()) + { + this->Write("Unable to write database " + db_name); + + f->close(); + + if (Anope::IsFile((db_name + ".tmp").c_str())) + rename((db_name + ".tmp").c_str(), db_name.c_str()); + } + else + { + f->close(); + unlink((db_name + ".tmp").c_str()); + } + + delete f; } - std::fstream *fd = databases[s_type->GetOwner()]; - - *fd << "OBJECT " << s_type->GetName() << "\n"; - for (Serialize::Data::iterator dit = data.begin(), dit_end = data.end(); dit != dit_end; ++dit) - *fd << "DATA " << dit->first << " " << dit->second.astr() << "\n"; - *fd << "END\n"; } - - if (databases[NULL]->good() == false) + catch (...) { - Log(this) << "Unable to write database"; - databases[NULL]->close(); - if (!Config->NoBackupOkay) - Anope::Quitting = true; - if (IsFile(tmp_db)) - rename(tmp_db.c_str(), DatabaseFile.c_str()); + if (i) + throw; } - else - unlink(tmp_db.c_str()); - for (std::map::iterator it = databases.begin(), it_end = databases.end(); it != it_end; ++it) + if (!i) { - it->second->close(); - delete it->second; + this->Notify(); + exit(0); } return EVENT_CONTINUE; diff --git a/modules/database/db_sql.cpp b/modules/database/db_sql.cpp index 60db11c0d..56db7b8a0 100644 --- a/modules/database/db_sql.cpp +++ b/modules/database/db_sql.cpp @@ -11,18 +11,19 @@ #include "module.h" #include "../extra/sql.h" -class SQLSQLInterface : public SQLInterface +using namespace SQL; + +class SQLSQLInterface : public Interface { public: - SQLSQLInterface(Module *o) : SQLInterface(o) { } - virtual ~SQLSQLInterface() { } + SQLSQLInterface(Module *o) : Interface(o) { } - void OnResult(const SQLResult &r) anope_override + void OnResult(const Result &r) anope_override { Log(LOG_DEBUG) << "SQL successfully executed query: " << r.finished_query; } - void OnError(const SQLResult &r) anope_override + void OnError(const Result &r) anope_override { if (!r.GetQuery().query.empty()) Log(LOG_DEBUG) << "Error executing query " << r.finished_query << ": " << r.GetError(); @@ -38,7 +39,7 @@ class ResultSQLSQLInterface : public SQLSQLInterface public: ResultSQLSQLInterface(Module *o, Serializable *ob) : SQLSQLInterface(o), obj(ob) { } - void OnResult(const SQLResult &r) anope_override + void OnResult(const Result &r) anope_override { SQLSQLInterface::OnResult(r); if (r.GetID() > 0 && this->obj) @@ -46,7 +47,7 @@ public: delete this; } - void OnError(const SQLResult &r) anope_override + void OnError(const Result &r) anope_override { SQLSQLInterface::OnError(r); delete this; @@ -55,13 +56,14 @@ public: class DBSQL : public Module, public Pipe { - ServiceReference sql; + ServiceReference sql; SQLSQLInterface sqlinterface; Anope::string prefix; std::set > updated_items; bool shutting_down; + bool loading_databases; - void RunBackground(const SQLQuery &q, SQLInterface *iface = NULL) + void RunBackground(const Query &q, Interface *iface = NULL) { if (!this->sql) { @@ -83,7 +85,7 @@ class DBSQL : public Module, public Pipe } public: - DBSQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE), sql("", ""), sqlinterface(this), shutting_down(false) + DBSQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE), sql("", ""), sqlinterface(this), shutting_down(false), loading_databases(false) { this->SetAuthor("Anope"); @@ -101,21 +103,26 @@ class DBSQL : public Module, public Pipe if (obj && this->sql) { - if (obj->IsCached()) + Data *data = new Data(); + obj->Serialize(*data); + + if (obj->IsCached(data)) + { + delete data; continue; - obj->UpdateCache(); + } + + obj->UpdateCache(data); Serialize::Type *s_type = obj->GetSerializableType(); if (!s_type) continue; - Serialize::Data data = obj->Serialize(); - - std::vector create = this->sql->CreateTable(this->prefix + s_type->GetName(), data); + std::vector create = this->sql->CreateTable(this->prefix + s_type->GetName(), *data); for (unsigned i = 0; i < create.size(); ++i) this->RunBackground(create[i]); - SQLQuery insert = this->sql->BuildInsert(this->prefix + s_type->GetName(), obj->id, data); + Query insert = this->sql->BuildInsert(this->prefix + s_type->GetName(), obj->id, *data); this->RunBackground(insert, new ResultSQLSQLInterface(this, obj)); } } @@ -127,7 +134,7 @@ class DBSQL : public Module, public Pipe { ConfigReader config; Anope::string engine = config.ReadValue("db_sql", "engine", "", 0); - this->sql = ServiceReference("SQLProvider", engine); + this->sql = ServiceReference("SQL::Provider", engine); this->prefix = config.ReadValue("db_sql", "prefix", "anope_db_", 0); } @@ -150,23 +157,25 @@ class DBSQL : public Module, public Pipe return EVENT_CONTINUE; } + this->loading_databases = true; + const std::vector type_order = Serialize::Type::GetTypeOrder(); for (unsigned i = 0; i < type_order.size(); ++i) { Serialize::Type *sb = Serialize::Type::Find(type_order[i]); - SQLQuery query("SELECT * FROM `" + this->prefix + sb->GetName() + "`"); - SQLResult res = this->sql->RunQuery(query); + Query query("SELECT * FROM `" + this->prefix + sb->GetName() + "`"); + Result res = this->sql->RunQuery(query); for (int j = 0; j < res.Rows(); ++j) { - Serialize::Data data; + Data *data = new Data(); const std::map &row = res.Row(j); for (std::map::const_iterator rit = row.begin(), rit_end = row.end(); rit != rit_end; ++rit) - data[rit->first] << rit->second; + (*data)[rit->first] << rit->second; - Serializable *obj = sb->Unserialize(NULL, data); + Serializable *obj = sb->Unserialize(NULL, *data); try { if (obj) @@ -176,15 +185,22 @@ class DBSQL : public Module, public Pipe { Log(this) << "Unable to convert id for object #" << j << " of type " << sb->GetName(); } + + if (obj) + obj->UpdateCache(data); /* We know this is the most up to date copy */ + else + delete data; } } + this->loading_databases = false; + return EVENT_STOP; } void OnSerializableConstruct(Serializable *obj) anope_override { - if (this->shutting_down) + if (this->shutting_down || this->loading_databases) return; this->updated_items.insert(obj); this->Notify(); diff --git a/modules/database/db_sql_live.cpp b/modules/database/db_sql_live.cpp index 00561592f..8d8836055 100644 --- a/modules/database/db_sql_live.cpp +++ b/modules/database/db_sql_live.cpp @@ -2,12 +2,14 @@ #include "../extra/sql.h" #include "../commands/os_session.h" +using namespace SQL; + class DBMySQL : public Module, public Pipe { private: Anope::string engine; Anope::string prefix; - ServiceReference SQL; + ServiceReference SQL; time_t lastwarn; bool ro; bool init; @@ -43,24 +45,24 @@ class DBMySQL : public Module, public Pipe return init && SQL; } - void RunQuery(const SQLQuery &query) + void RunQuery(const Query &query) { /* Can this be threaded? */ this->RunQueryResult(query); } - SQLResult RunQueryResult(const SQLQuery &query) + Result RunQueryResult(const Query &query) { if (this->CheckSQL()) { - SQLResult res = SQL->RunQuery(query); + Result res = SQL->RunQuery(query); if (!res.GetError().empty()) Log(LOG_DEBUG) << "SQL-live got error " << res.GetError() << " for " + res.finished_query; else Log(LOG_DEBUG) << "SQL-live got " << res.Rows() << " rows for " << res.finished_query; return res; } - throw SQLException("No SQL!"); + throw SQL::Exception("No SQL!"); } public: @@ -87,21 +89,26 @@ class DBMySQL : public Module, public Pipe if (obj && this->SQL) { - if (obj->IsCached()) + Data *data = new Data(); + obj->Serialize(*data); + + if (obj->IsCached(data)) + { + delete data; continue; - obj->UpdateCache(); + } + + obj->UpdateCache(data); Serialize::Type *s_type = obj->GetSerializableType(); if (!s_type) continue; - Serialize::Data data = obj->Serialize(); - -