mirror of
https://github.com/NishiOwO/ircservices5.git
synced 2025-04-21 00:34:38 +00:00
328 lines
9.3 KiB
C
328 lines
9.3 KiB
C
/* Database core routines.
|
|
*
|
|
* IRC Services is copyright (c) 1996-2009 Andrew Church.
|
|
* E-mail: <achurch@achurch.org>
|
|
* Parts written by Andrew Kempe and others.
|
|
* This program is free but copyrighted software; see the file GPL.txt for
|
|
* details.
|
|
*/
|
|
|
|
#include "services.h"
|
|
#include "databases.h"
|
|
#include "encrypt.h"
|
|
#include "modules.h"
|
|
|
|
/*************************************************************************/
|
|
|
|
/* List of registered tables */
|
|
typedef struct dbtablenode_ DBTableNode;
|
|
struct dbtablenode_ {
|
|
DBTableNode *next, *prev;
|
|
DBTable *table;
|
|
const Module *owner; /* Which module registered this table? */
|
|
int loaded; /* Has this table been loaded? */
|
|
};
|
|
static DBTableNode *tables = NULL;
|
|
|
|
/* Currently active database module */
|
|
static DBModule *dbmodule = NULL;
|
|
|
|
|
|
/* Local routines: */
|
|
static int do_unload_module(const Module *module);
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
/* Initialization/cleanup routines. */
|
|
|
|
int database_init(int ac, char **av)
|
|
{
|
|
if (!add_callback(NULL, "unload module", do_unload_module)) {
|
|
log("database_init: add_callback() failed");
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/************************************/
|
|
|
|
void database_cleanup(void)
|
|
{
|
|
remove_callback(NULL, "unload module", do_unload_module);
|
|
}
|
|
|
|
/************************************/
|
|
|
|
/* Check for tables that the module forgot to unload. */
|
|
|
|
static int do_unload_module(const Module *module)
|
|
{
|
|
DBTableNode *t, *t2;
|
|
|
|
LIST_FOREACH_SAFE (t, tables, t2) {
|
|
if (t->owner == module) {
|
|
log("database: Module `%s' forgot to unregister table `%s'",
|
|
get_module_name(module), t->table->name);
|
|
unregister_dbtable(t->table);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Register a new database table. Returns nonzero on success, zero on
|
|
* error.
|
|
*/
|
|
|
|
int _register_dbtable(DBTable *table, const Module *caller)
|
|
{
|
|
DBTableNode *t;
|
|
|
|
/* Sanity checks on parameter */
|
|
if (!table) {
|
|
log("BUG: register_dbtable() with NULL table!");
|
|
return 0;
|
|
}
|
|
if (!table->name) {
|
|
log("BUG: register_dbtable(): table->name is NULL!");
|
|
return 0;
|
|
}
|
|
if (!table->fields || !table->newrec || !table->freerec
|
|
|| !table->insert || !table->first || !table->next
|
|
) {
|
|
log("BUG: register_dbtable(%s): table->%s is NULL!", table->name,
|
|
!table->fields ? "fields" :
|
|
!table->newrec ? "newrec" :
|
|
!table->freerec ? "freerec" :
|
|
!table->insert ? "insert" :
|
|
!table->first ? "first" :
|
|
!table->next ? "next" :
|
|
"???");
|
|
return 0;
|
|
}
|
|
|
|
/* Sanity check: make sure it's not already registered */
|
|
LIST_FOREACH (t, tables) {
|
|
if (t->table == table) {
|
|
log("BUG: register_dbtable(%s): table already registered!",
|
|
table->name);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Allocate and append to list (make sure to preserve order, since
|
|
* later tables may depend on earlier ones); if a database module is
|
|
* available, load the table ammediately*/
|
|
t = smalloc(sizeof(*t));
|
|
t->table = table;
|
|
t->owner = caller;
|
|
t->loaded = 0;
|
|
LIST_APPEND(t, tables);
|
|
if (dbmodule)
|
|
t->loaded = (*dbmodule->load_table)(t->table);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Unregister a database table. Does nothing if the table was not
|
|
* registered in the first place.
|
|
*/
|
|
|
|
void unregister_dbtable(DBTable *table)
|
|
{
|
|
DBTableNode *t;
|
|
|
|
if (!table) {
|
|
log("BUG: unregister_dbtable() with NULL table!");
|
|
return;
|
|
}
|
|
/* Sanity check: make sure it was registered first */
|
|
LIST_FOREACH (t, tables) {
|
|
if (t->table == table) {
|
|
LIST_REMOVE(t, tables);
|
|
free(t);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Save all registered database tables to permanent storage. Returns 1 if
|
|
* all tables were successfully saved or no tables are registered, 0 if
|
|
* some tables were successfully saved (but some were not), or -1 if no
|
|
* tables were successfully saved.
|
|
*/
|
|
|
|
int save_all_dbtables(void)
|
|
{
|
|
DBTableNode *t;
|
|
int some_saved = 0;
|
|
int some_failed = 0;
|
|
|
|
if (!tables)
|
|
return 1;
|
|
if (!dbmodule) {
|
|
log("save_all_dbtables(): No database module registered!");
|
|
return -1;
|
|
}
|
|
LIST_FOREACH (t, tables) {
|
|
if ((*dbmodule->save_table)(t->table)) {
|
|
some_saved = 1;
|
|
} else {
|
|
log("save_all_dbtables(): Failed to save table `%s'",
|
|
t->table->name);
|
|
some_failed = 1;
|
|
}
|
|
}
|
|
return some_saved - some_failed;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
/* Register a database module. Returns nonzero on success, zero on error.
|
|
* On success, all registered tables which have not already been loaded
|
|
* will be loaded from permanent storage. Only one database module can be
|
|
* registered.
|
|
*/
|
|
|
|
int register_dbmodule(DBModule *module)
|
|
{
|
|
DBTableNode *t;
|
|
|
|
if (!module) {
|
|
log("BUG: register_dbmodule() with NULL module!");
|
|
return 0;
|
|
}
|
|
if (!module->load_table || !module->save_table) {
|
|
log("BUG: register_dbmodule(): module->%s is NULL!",
|
|
!module->load_table ? "load_table" : "save_table");
|
|
return 0;
|
|
}
|
|
if (dbmodule) {
|
|
if (module == dbmodule)
|
|
log("BUG: register_dbmodule(): attempt to re-register module!");
|
|
else
|
|
log("register_dbmodule(): a database module is already registered");
|
|
return 0;
|
|
}
|
|
|
|
dbmodule = module;
|
|
LIST_FOREACH (t, tables) {
|
|
if (!t->loaded)
|
|
t->loaded = (*dbmodule->load_table)(t->table);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Unregister a database module. Does nothing if the module was not
|
|
* registered in the first place.
|
|
*/
|
|
|
|
void unregister_dbmodule(DBModule *module)
|
|
{
|
|
if (!module) {
|
|
log("BUG: unregister_dbmodule() with NULL module!");
|
|
return;
|
|
}
|
|
if (dbmodule == module)
|
|
dbmodule = NULL;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Read a value from a database field. The value buffer is assumed to be
|
|
* large enough to hold the retrieved value.
|
|
*/
|
|
|
|
void get_dbfield(const void *record, const DBField *field, void *buffer)
|
|
{
|
|
int size;
|
|
|
|
if (!record || !field || !buffer) {
|
|
log("BUG: get_dbfield(): %s is NULL!",
|
|
!record ? "record" : !field ? "field" : "buffer");
|
|
return;
|
|
}
|
|
if (field->get) {
|
|
(*field->get)(record, buffer);
|
|
return;
|
|
}
|
|
switch (field->type) {
|
|
case DBTYPE_INT8: size = 1; break;
|
|
case DBTYPE_UINT8: size = 1; break;
|
|
case DBTYPE_INT16: size = 2; break;
|
|
case DBTYPE_UINT16: size = 2; break;
|
|
case DBTYPE_INT32: size = 4; break;
|
|
case DBTYPE_UINT32: size = 4; break;
|
|
case DBTYPE_TIME: size = sizeof(time_t); break;
|
|
case DBTYPE_STRING: size = sizeof(char *); break;
|
|
case DBTYPE_BUFFER: size = field->length; break;
|
|
case DBTYPE_PASSWORD: size = sizeof(Password); break;
|
|
default:
|
|
log("BUG: bad field type %d in get_dbfield()", field->type);
|
|
return;
|
|
}
|
|
if (!size) {
|
|
return;
|
|
}
|
|
memcpy(buffer, (const uint8 *)record + field->offset, size);
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Store a value to a database field. */
|
|
|
|
void put_dbfield(void *record, const DBField *field, const void *value)
|
|
{
|
|
int size;
|
|
|
|
if (!record || !field || !value) {
|
|
log("BUG: get_dbfield(): %s is NULL!",
|
|
!record ? "record" : !field ? "field" : "value");
|
|
return;
|
|
}
|
|
if (field->put) {
|
|
(*field->put)(record, value);
|
|
return;
|
|
}
|
|
switch (field->type) {
|
|
case DBTYPE_INT8: size = 1; break;
|
|
case DBTYPE_UINT8: size = 1; break;
|
|
case DBTYPE_INT16: size = 2; break;
|
|
case DBTYPE_UINT16: size = 2; break;
|
|
case DBTYPE_INT32: size = 4; break;
|
|
case DBTYPE_UINT32: size = 4; break;
|
|
case DBTYPE_TIME: size = sizeof(time_t); break;
|
|
case DBTYPE_STRING: size = sizeof(char *); break;
|
|
case DBTYPE_BUFFER: size = field->length; break;
|
|
case DBTYPE_PASSWORD: size = sizeof(Password); break;
|
|
default:
|
|
log("BUG: bad field type %d in get_dbfield()", field->type);
|
|
return;
|
|
}
|
|
if (!size) {
|
|
return;
|
|
}
|
|
memcpy((uint8 *)record + field->offset, value, size);
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/*
|
|
* Local variables:
|
|
* c-file-style: "stroustrup"
|
|
* c-file-offsets: ((case-label . *) (statement-case-intro . *))
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*
|
|
* vim: expandtab shiftwidth=4:
|
|
*/
|