00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #include "module.h"
00014
00015 class SaveData : public Serialize::Data
00016 {
00017 public:
00018 Anope::string last;
00019 std::fstream *fs;
00020
00021 SaveData() : fs(NULL) { }
00022
00023 std::iostream& operator[](const Anope::string &key) anope_override
00024 {
00025 if (key != last)
00026 {
00027 *fs << "\nDATA " << key << " ";
00028 last = key;
00029 }
00030
00031 return *fs;
00032 }
00033 };
00034
00035 class LoadData : public Serialize::Data
00036 {
00037 public:
00038 std::fstream *fs;
00039 unsigned int id;
00040 std::map<Anope::string, Anope::string> data;
00041 std::stringstream ss;
00042 bool read;
00043
00044 LoadData() : fs(NULL), id(0), read(false) { }
00045
00046 std::iostream& operator[](const Anope::string &key) anope_override
00047 {
00048 if (!read)
00049 {
00050 for (Anope::string token; std::getline(*this->fs, token.str());)
00051 {
00052 if (token.find("ID ") == 0)
00053 {
00054 try
00055 {
00056 this->id = convertTo<unsigned int>(token.substr(3));
00057 }
00058 catch (const ConvertException &) { }
00059
00060 continue;
00061 }
00062 else if (token.find("DATA ") != 0)
00063 break;
00064
00065 size_t sp = token.find(' ', 5);
00066 if (sp != Anope::string::npos)
00067 data[token.substr(5, sp - 5)] = token.substr(sp + 1);
00068 }
00069
00070 read = true;
00071 }
00072
00073 ss.clear();
00074 this->ss << this->data[key];
00075 return this->ss;
00076 }
00077
00078 std::set<Anope::string> KeySet() const anope_override
00079 {
00080 std::set<Anope::string> keys;
00081 for (std::map<Anope::string, Anope::string>::const_iterator it = this->data.begin(), it_end = this->data.end(); it != it_end; ++it)
00082 keys.insert(it->first);
00083 return keys;
00084 }
00085
00086 size_t Hash() const anope_override
00087 {
00088 size_t hash = 0;
00089 for (std::map<Anope::string, Anope::string>::const_iterator it = this->data.begin(), it_end = this->data.end(); it != it_end; ++it)
00090 if (!it->second.empty())
00091 hash ^= Anope::hash_cs()(it->second);
00092 return hash;
00093 }
00094
00095 void Reset()
00096 {
00097 id = 0;
00098 read = false;
00099 data.clear();
00100 }
00101 };
00102
00103 class DBFlatFile : public Module, public Pipe
00104 {
00105 Anope::string database_file;
00106
00107 int last_day;
00108
00109 std::map<Anope::string, std::list<Anope::string> > backups;
00110 bool use_fork;
00111 bool loaded;
00112
00113 void BackupDatabase()
00114 {
00115 tm *tm = localtime(&Anope::CurTime);
00116
00117 if (tm->tm_mday != last_day)
00118 {
00119 last_day = tm->tm_mday;
00120
00121 const std::vector<Anope::string> &type_order = Serialize::Type::GetTypeOrder();
00122
00123 std::set<Anope::string> dbs;
00124 dbs.insert(database_file);
00125
00126 for (unsigned i = 0; i < type_order.size(); ++i)
00127 {
00128 Serialize::Type *stype = Serialize::Type::Find(type_order[i]);
00129
00130 if (stype && stype->GetOwner())
00131 dbs.insert("module_" + stype->GetOwner()->name + ".db");
00132 }
00133
00134
00135 for (std::set<Anope::string>::const_iterator it = dbs.begin(), it_end = dbs.end(); it != it_end; ++it)
00136 {
00137 const Anope::string &oldname = Anope::DataDir + "/" + *it;
00138 Anope::string newname = Anope::DataDir + "/backups/" + *it + "." + stringify(tm->tm_year) + "." + stringify(tm->tm_mon) + "." + stringify(tm->tm_mday);
00139
00140
00141 if (Anope::IsFile(newname) || !Anope::IsFile(oldname))
00142 continue;
00143
00144 Log(LOG_DEBUG) << "db_flatfile: Attemping to rename " << *it << " to " << newname;
00145 if (rename(oldname.c_str(), newname.c_str()))
00146 {
00147 Log(this) << "Unable to back up database " << *it << "!";
00148
00149 if (!Config->NoBackupOkay)
00150 Anope::Quitting = true;
00151
00152 continue;
00153 }
00154
00155 backups[*it].push_back(newname);
00156
00157 if (Config->KeepBackups > 0 && backups[*it].size() > static_cast<unsigned>(Config->KeepBackups))
00158 {
00159 unlink(backups[*it].front().c_str());
00160 backups[*it].pop_front();
00161 }
00162 }
00163 }
00164 }
00165
00166 public:
00167 DBFlatFile(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE), last_day(0), use_fork(false), loaded(false)
00168 {
00169 this->SetAuthor("Anope");
00170
00171 Implementation i[] = { I_OnReload, I_OnLoadDatabase, I_OnSaveDatabase, I_OnSerializeTypeCreate };
00172 ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation));
00173
00174 OnReload();
00175 }
00176
00177 void OnNotify() anope_override
00178 {
00179 char buf[512];
00180 int i = this->Read(buf, sizeof(buf) - 1);
00181 if (i <= 0)
00182 return;
00183 buf[i] = 0;
00184
00185 if (!*buf)
00186 {
00187 Log(this) << "Finished saving databases";
00188 return;
00189 }
00190
00191 Log(this) << "Error saving databases: " << buf;
00192
00193 if (!Config->NoBackupOkay)
00194 Anope::Quitting = true;
00195 }
00196
00197 void OnReload() anope_override
00198 {
00199 ConfigReader config;
00200 database_file = config.ReadValue("db_flatfile", "database", "anope.db", 0);
00201 use_fork = config.ReadFlag("db_flatfile", "fork", "no", 0);
00202 }
00203
00204 EventReturn OnLoadDatabase() anope_override
00205 {
00206 const std::vector<Anope::string> &type_order = Serialize::Type::GetTypeOrder();
00207 std::set<Anope::string> tried_dbs;
00208
00209 const Anope::string &db_name = Anope::DataDir + "/" + database_file;
00210
00211 std::fstream fd(db_name.c_str(), std::ios_base::in);
00212 if (!fd.is_open())
00213 {
00214 Log(this) << "Unable to open " << db_name << " for reading!";
00215 return EVENT_STOP;
00216 }
00217
00218 std::map<Anope::string, std::vector<std::streampos> > positions;
00219
00220 for (Anope::string buf; std::getline(fd, buf.str());)
00221 if (buf.find("OBJECT ") == 0)
00222 positions[buf.substr(7)].push_back(fd.tellg());
00223
00224 LoadData ld;
00225 ld.fs = &fd;
00226
00227 for (unsigned i = 0; i < type_order.size(); ++i)
00228 {
00229 Serialize::Type *stype = Serialize::Type::Find(type_order[i]);
00230 if (!stype || stype->GetOwner())
00231 continue;
00232
00233 std::vector<std::streampos> &pos = positions[stype->GetName()];
00234
00235 for (unsigned j = 0; j < pos.size(); ++j)
00236 {
00237 fd.clear();
00238 fd.seekg(pos[j]);
00239
00240 Serializable *obj = stype->Unserialize(NULL, ld);
00241 if (obj != NULL)
00242 obj->id = ld.id;
00243 ld.Reset();
00244 }
00245 }
00246
00247 fd.close();
00248
00249 loaded = true;
00250 return EVENT_STOP;
00251 }
00252
00253
00254 EventReturn OnSaveDatabase() anope_override
00255 {
00256 BackupDatabase();
00257
00258 int i = -1;
00259 #ifndef _WIN32
00260 if (use_fork)
00261 {
00262 i = fork();
00263 if (i > 0)
00264 return EVENT_CONTINUE;
00265 else if (i < 0)
00266 Log(this) << "Unable to fork for database save";
00267 }
00268 #endif
00269
00270 try
00271 {
00272 std::map<Module *, std::fstream *> databases;
00273
00274
00275 for (std::map<Anope::string, Serialize::Type *>::const_iterator it = Serialize::Type::GetTypes().begin(), it_end = Serialize::Type::GetTypes().end(); it != it_end; ++it)
00276 {
00277 Serialize::Type *s_type = it->second;
00278
00279 if (databases[s_type->GetOwner()])
00280 continue;
00281
00282 Anope::string db_name;
00283 if (s_type->GetOwner())
00284 db_name = Anope::DataDir + "/module_" + s_type->GetOwner()->name + ".db";
00285 else
00286 db_name = Anope::DataDir + "/" + database_file;
00287
00288 if (Anope::IsFile(db_name))
00289 rename(db_name.c_str(), (db_name + ".tmp").c_str());
00290
00291 std::fstream *fs = databases[s_type->GetOwner()] = new std::fstream(db_name.c_str(), std::ios_base::out | std::ios_base::trunc);
00292
00293 if (!fs->is_open())
00294 Log(this) << "Unable to open " << db_name << " for writing";
00295 }
00296
00297 SaveData data;
00298 const std::list<Serializable *> &items = Serializable::GetItems();
00299 for (std::list<Serializable *>::const_iterator it = items.begin(), it_end = items.end(); it != it_end; ++it)
00300 {
00301 Serializable *base = *it;
00302 Serialize::Type *s_type = base->GetSerializableType();
00303
00304 data.fs = databases[s_type->GetOwner()];
00305 if (!data.fs || !data.fs->is_open())
00306 continue;
00307
00308 *data.fs << "OBJECT " << s_type->GetName();
00309 if (base->id)
00310 *data.fs << "\nID " << base->id;
00311 base->Serialize(data);
00312 *data.fs << "\nEND\n";
00313 }
00314
00315 for (std::map<Module *, std::fstream *>::iterator it = databases.begin(), it_end = databases.end(); it != it_end; ++it)
00316 {
00317 std::fstream *f = it->second;
00318 const Anope::string &db_name = Anope::DataDir + "/" + (it->first ? (it->first->name + ".db") : database_file);
00319
00320 if (!f->is_open() || !f->good())
00321 {
00322 this->Write("Unable to write database " + db_name);
00323
00324 f->close();
00325
00326 if (Anope::IsFile((db_name + ".tmp").c_str()))
00327 rename((db_name + ".tmp").c_str(), db_name.c_str());
00328 }
00329 else
00330 {
00331 f->close();
00332 unlink((db_name + ".tmp").c_str());
00333 }
00334
00335 delete f;
00336 }
00337 }
00338 catch (...)
00339 {
00340 if (i)
00341 throw;
00342 }
00343
00344 if (!i)
00345 {
00346 this->Notify();
00347 exit(0);
00348 }
00349
00350 return EVENT_CONTINUE;
00351 }
00352
00353
00354 void OnSerializeTypeCreate(Serialize::Type *stype) anope_override
00355 {
00356 if (!loaded)
00357 return;
00358
00359 Anope::string db_name;
00360 if (stype->GetOwner())
00361 db_name = Anope::DataDir + "/module_" + stype->GetOwner()->name + ".db";
00362 else
00363 db_name = Anope::DataDir + "/" + database_file;
00364
00365 std::fstream fd(db_name.c_str(), std::ios_base::in);
00366 if (!fd.is_open())
00367 {
00368 Log(this) << "Unable to open " << db_name << " for reading!";
00369 return;
00370 }
00371
00372 LoadData ld;
00373 ld.fs = &fd;
00374
00375 for (Anope::string buf; std::getline(fd, buf.str());)
00376 {
00377 if (buf == "OBJECT " + stype->GetName())
00378 {
00379 stype->Unserialize(NULL, ld);
00380 ld.Reset();
00381 }
00382 }
00383
00384 fd.close();
00385 }
00386 };
00387
00388 MODULE_INIT(DBFlatFile)
00389
00390