anope- supernets anope source code & configuration |
git clone git://git.acid.vegas/anope.git |
Log | Files | Refs | Archive | README |
db_flatfile.cpp (9787B)
1 /* 2 * 3 * (C) 2003-2022 Anope Team 4 * Contact us at team@anope.org 5 * 6 * Please read COPYING and README for further details. 7 * 8 * Based on the original code of Epona by Lara. 9 * Based on the original code of Services by Andy Church. 10 */ 11 12 #include "module.h" 13 14 #ifndef _WIN32 15 #include <sys/wait.h> 16 #endif 17 18 class SaveData : public Serialize::Data 19 { 20 public: 21 Anope::string last; 22 std::fstream *fs; 23 24 SaveData() : fs(NULL) { } 25 26 std::iostream& operator[](const Anope::string &key) anope_override 27 { 28 if (key != last) 29 { 30 *fs << "\nDATA " << key << " "; 31 last = key; 32 } 33 34 return *fs; 35 } 36 }; 37 38 class LoadData : public Serialize::Data 39 { 40 public: 41 std::fstream *fs; 42 unsigned int id; 43 std::map<Anope::string, Anope::string> data; 44 std::stringstream ss; 45 bool read; 46 47 LoadData() : fs(NULL), id(0), read(false) { } 48 49 std::iostream& operator[](const Anope::string &key) anope_override 50 { 51 if (!read) 52 { 53 for (Anope::string token; std::getline(*this->fs, token.str());) 54 { 55 if (token.find("ID ") == 0) 56 { 57 try 58 { 59 this->id = convertTo<unsigned int>(token.substr(3)); 60 } 61 catch (const ConvertException &) { } 62 63 continue; 64 } 65 else if (token.find("DATA ") != 0) 66 break; 67 68 size_t sp = token.find(' ', 5); // Skip DATA 69 if (sp != Anope::string::npos) 70 data[token.substr(5, sp - 5)] = token.substr(sp + 1); 71 } 72 73 read = true; 74 } 75 76 ss.clear(); 77 this->ss << this->data[key]; 78 return this->ss; 79 } 80 81 std::set<Anope::string> KeySet() const anope_override 82 { 83 std::set<Anope::string> keys; 84 for (std::map<Anope::string, Anope::string>::const_iterator it = this->data.begin(), it_end = this->data.end(); it != it_end; ++it) 85 keys.insert(it->first); 86 return keys; 87 } 88 89 size_t Hash() const anope_override 90 { 91 size_t hash = 0; 92 for (std::map<Anope::string, Anope::string>::const_iterator it = this->data.begin(), it_end = this->data.end(); it != it_end; ++it) 93 if (!it->second.empty()) 94 hash ^= Anope::hash_cs()(it->second); 95 return hash; 96 } 97 98 void Reset() 99 { 100 id = 0; 101 read = false; 102 data.clear(); 103 } 104 }; 105 106 class DBFlatFile : public Module, public Pipe 107 { 108 /* Day the last backup was on */ 109 int last_day; 110 /* Backup file names */ 111 std::map<Anope::string, std::list<Anope::string> > backups; 112 bool loaded; 113 114 int child_pid; 115 116 void BackupDatabase() 117 { 118 tm *tm = localtime(&Anope::CurTime); 119 120 if (tm->tm_mday != last_day) 121 { 122 last_day = tm->tm_mday; 123 124 const std::vector<Anope::string> &type_order = Serialize::Type::GetTypeOrder(); 125 126 std::set<Anope::string> dbs; 127 dbs.insert(Config->GetModule(this)->Get<const Anope::string>("database", "anope.db")); 128 129 for (unsigned i = 0; i < type_order.size(); ++i) 130 { 131 Serialize::Type *stype = Serialize::Type::Find(type_order[i]); 132 133 if (stype && stype->GetOwner()) 134 dbs.insert("module_" + stype->GetOwner()->name + ".db"); 135 } 136 137 138 for (std::set<Anope::string>::const_iterator it = dbs.begin(), it_end = dbs.end(); it != it_end; ++it) 139 { 140 const Anope::string &oldname = Anope::DataDir + "/" + *it; 141 Anope::string newname = Anope::DataDir + "/backups/" + *it + "-" + stringify(tm->tm_year + 1900) + Anope::printf("-%02i-", tm->tm_mon + 1) + Anope::printf("%02i", tm->tm_mday); 142 143 /* Backup already exists or no database to backup */ 144 if (Anope::IsFile(newname) || !Anope::IsFile(oldname)) 145 continue; 146 147 Log(LOG_DEBUG) << "db_flatfile: Attempting to rename " << *it << " to " << newname; 148 if (rename(oldname.c_str(), newname.c_str())) 149 { 150 Anope::string err = Anope::LastError(); 151 Log(this) << "Unable to back up database " << *it << " (" << err << ")!"; 152 153 if (!Config->GetModule(this)->Get<bool>("nobackupokay")) 154 { 155 Anope::Quitting = true; 156 Anope::QuitReason = "Unable to back up database " + *it + " (" + err + ")"; 157 } 158 159 continue; 160 } 161 162 backups[*it].push_back(newname); 163 164 unsigned keepbackups = Config->GetModule(this)->Get<unsigned>("keepbackups"); 165 if (keepbackups > 0 && backups[*it].size() > keepbackups) 166 { 167 unlink(backups[*it].front().c_str()); 168 backups[*it].pop_front(); 169 } 170 } 171 } 172 } 173 174 public: 175 DBFlatFile(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR), last_day(0), loaded(false), child_pid(-1) 176 { 177 178 } 179 180 #ifndef _WIN32 181 void OnRestart() anope_override 182 { 183 OnShutdown(); 184 } 185 186 void OnShutdown() anope_override 187 { 188 if (child_pid > -1) 189 { 190 Log(this) << "Waiting for child to exit..."; 191 192 int status; 193 waitpid(child_pid, &status, 0); 194 195 Log(this) << "Done"; 196 } 197 } 198 #endif 199 200 void OnNotify() anope_override 201 { 202 char buf[512]; 203 int i = this->Read(buf, sizeof(buf) - 1); 204 if (i <= 0) 205 return; 206 buf[i] = 0; 207 208 child_pid = -1; 209 210 if (!*buf) 211 { 212 Log(this) << "Finished saving databases"; 213 return; 214 } 215 216 Log(this) << "Error saving databases: " << buf; 217 218 if (!Config->GetModule(this)->Get<bool>("nobackupokay")) 219 Anope::Quitting = true; 220 } 221 222 EventReturn OnLoadDatabase() anope_override 223 { 224 const std::vector<Anope::string> &type_order = Serialize::Type::GetTypeOrder(); 225 std::set<Anope::string> tried_dbs; 226 227 const Anope::string &db_name = Anope::DataDir + "/" + Config->GetModule(this)->Get<const Anope::string>("database", "anope.db"); 228 229 std::fstream fd(db_name.c_str(), std::ios_base::in | std::ios_base::binary); 230 if (!fd.is_open()) 231 { 232 Log(this) << "Unable to open " << db_name << " for reading!"; 233 return EVENT_STOP; 234 } 235 236 std::map<Anope::string, std::vector<std::streampos> > positions; 237 238 for (Anope::string buf; std::getline(fd, buf.str());) 239 if (buf.find("OBJECT ") == 0) 240 positions[buf.substr(7)].push_back(fd.tellg()); 241 242 LoadData ld; 243 ld.fs = &fd; 244 245 for (unsigned i = 0; i < type_order.size(); ++i) 246 { 247 Serialize::Type *stype = Serialize::Type::Find(type_order[i]); 248 if (!stype || stype->GetOwner()) 249 continue; 250 251 std::vector<std::streampos> &pos = positions[stype->GetName()]; 252 253 for (unsigned j = 0; j < pos.size(); ++j) 254 { 255 fd.clear(); 256 fd.seekg(pos[j]); 257 258 Serializable *obj = stype->Unserialize(NULL, ld); 259 if (obj != NULL) 260 obj->id = ld.id; 261 ld.Reset(); 262 } 263 } 264 265 fd.close(); 266 267 loaded = true; 268 return EVENT_STOP; 269 } 270 271 272 void OnSaveDatabase() anope_override 273 { 274 if (child_pid > -1) 275 { 276 Log(this) << "Database save is already in progress!"; 277 return; 278 } 279 280 BackupDatabase(); 281 282 int i = -1; 283 #ifndef _WIN32 284 if (!Anope::Quitting && Config->GetModule(this)->Get<bool>("fork")) 285 { 286 i = fork(); 287 if (i > 0) 288 { 289 child_pid = i; 290 return; 291 } 292 else if (i < 0) 293 Log(this) << "Unable to fork for database save"; 294 } 295 #endif 296 297 try 298 { 299 std::map<Module *, std::fstream *> databases; 300 301 /* First open the databases of all of the registered types. This way, if we have a type with 0 objects, that database will be properly cleared */ 302 for (std::map<Anope::string, Serialize::Type *>::const_iterator it = Serialize::Type::GetTypes().begin(), it_end = Serialize::Type::GetTypes().end(); it != it_end; ++it) 303 { 304 Serialize::Type *s_type = it->second; 305 306 if (databases[s_type->GetOwner()]) 307 continue; 308 309 Anope::string db_name; 310 if (s_type->GetOwner()) 311 db_name = Anope::DataDir + "/module_" + s_type->GetOwner()->name + ".db"; 312 else 313 db_name = Anope::DataDir + "/" + Config->GetModule(this)->Get<const Anope::string>("database", "anope.db"); 314 315 std::fstream *fs = databases[s_type->GetOwner()] = new std::fstream((db_name + ".tmp").c_str(), std::ios_base::out | std::ios_base::trunc | std::ios_base::binary); 316 317 if (!fs->is_open()) 318 Log(this) << "Unable to open " << db_name << " for writing"; 319 } 320 321 SaveData data; 322 const std::list<Serializable *> &items = Serializable::GetItems(); 323 for (std::list<Serializable *>::const_iterator it = items.begin(), it_end = items.end(); it != it_end; ++it) 324 { 325 Serializable *base = *it; 326 Serialize::Type *s_type = base->GetSerializableType(); 327 328 data.fs = databases[s_type->GetOwner()]; 329 if (!data.fs || !data.fs->is_open()) 330 continue; 331 332 *data.fs << "OBJECT " << s_type->GetName(); 333 if (base->id) 334 *data.fs << "\nID " << base->id; 335 base->Serialize(data); 336 *data.fs << "\nEND\n"; 337 } 338 339 for (std::map<Module *, std::fstream *>::iterator it = databases.begin(), it_end = databases.end(); it != it_end; ++it) 340 { 341 std::fstream *f = it->second; 342 const Anope::string &db_name = Anope::DataDir + "/" + (it->first ? (it->first->name + ".db") : Config->GetModule(this)->Get<const Anope::string>("database", "anope.db")); 343 344 if (!f->is_open() || !f->good()) 345 { 346 this->Write("Unable to write database " + db_name); 347 348 f->close(); 349 } 350 else 351 { 352 f->close(); 353 #ifdef _WIN32 354 /* Windows rename() fails if the file already exists. */ 355 remove(db_name.c_str()); 356 #endif 357 rename((db_name + ".tmp").c_str(), db_name.c_str()); 358 } 359 360 delete f; 361 } 362 } 363 catch (...) 364 { 365 if (i) 366 throw; 367 } 368 369 if (!i) 370 { 371 this->Notify(); 372 exit(0); 373 } 374 } 375 376 /* Load just one type. Done if a module is reloaded during runtime */ 377 void OnSerializeTypeCreate(Serialize::Type *stype) anope_override 378 { 379 if (!loaded) 380 return; 381 382 Anope::string db_name; 383 if (stype->GetOwner()) 384 db_name = Anope::DataDir + "/module_" + stype->GetOwner()->name + ".db"; 385 else 386 db_name = Anope::DataDir + "/" + Config->GetModule(this)->Get<const Anope::string>("database", "anope.db"); 387 388 std::fstream fd(db_name.c_str(), std::ios_base::in | std::ios_base::binary); 389 if (!fd.is_open()) 390 { 391 Log(this) << "Unable to open " << db_name << " for reading!"; 392 return; 393 } 394 395 LoadData ld; 396 ld.fs = &fd; 397 398 for (Anope::string buf; std::getline(fd, buf.str());) 399 { 400 if (buf == "OBJECT " + stype->GetName()) 401 { 402 stype->Unserialize(NULL, ld); 403 ld.Reset(); 404 } 405 } 406 407 fd.close(); 408 } 409 }; 410 411 MODULE_INIT(DBFlatFile)