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