mirror of
https://github.com/unrealircd/unrealircd.git
synced 2026-06-12 17:14:46 +02:00
New module 'whowasdb': persistent WHOWAS history (preserved between reboots)
This commit is contained in:
@@ -430,6 +430,7 @@ DLL_FILES=\
|
||||
src/modules/whois.dll \
|
||||
src/modules/who_old.dll \
|
||||
src/modules/whowas.dll \
|
||||
src/modules/whowasdb.dll \
|
||||
src/modules/whox.dll
|
||||
|
||||
|
||||
@@ -1391,6 +1392,9 @@ src/modules/who_old.dll: src/modules/who_old.c $(INCLUDES)
|
||||
src/modules/whowas.dll: src/modules/whowas.c $(INCLUDES)
|
||||
$(CC) $(MODCFLAGS) src/modules/whowas.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/whowas.pdb $(MODLFLAGS)
|
||||
|
||||
src/modules/whowasdb.dll: src/modules/whowasdb.c $(INCLUDES)
|
||||
$(CC) $(MODCFLAGS) src/modules/whowasdb.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/whowasdb.pdb $(MODLFLAGS)
|
||||
|
||||
src/modules/whox.dll: src/modules/whox.c $(INCLUDES)
|
||||
$(CC) $(MODCFLAGS) src/modules/whox.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/whox.pdb $(MODLFLAGS)
|
||||
|
||||
|
||||
+6
-1
@@ -412,7 +412,12 @@ extern uint64_t siphash_raw(const char *in, size_t len, const char *k);
|
||||
extern uint64_t siphash_nocase(const char *in, const char *k);
|
||||
extern void siphash_generate_key(char *k);
|
||||
extern void init_hash(void);
|
||||
uint64_t hash_whowas_name(const char *name);
|
||||
extern void add_whowas_to_clist(WhoWas **, WhoWas *);
|
||||
extern void del_whowas_from_clist(WhoWas **, WhoWas *);
|
||||
extern void add_whowas_to_list(WhoWas **, WhoWas *);
|
||||
extern void del_whowas_from_list(WhoWas **, WhoWas *);
|
||||
extern uint64_t hash_whowas_name(const char *name);
|
||||
extern void free_whowas(WhoWas *e);
|
||||
extern int add_to_client_hash_table(const char *, Client *);
|
||||
extern int del_from_client_hash_table(const char *, Client *);
|
||||
extern int add_to_id_hash_table(const char *, Client *);
|
||||
|
||||
@@ -66,7 +66,7 @@ MODULES= \
|
||||
ircops.so staff.so nocodes.so \
|
||||
charsys.so antimixedutf8.so authprompt.so sinfo.so \
|
||||
reputation.so connthrottle.so history_backend_mem.so \
|
||||
history_backend_null.so tkldb.so channeldb.so \
|
||||
history_backend_null.so tkldb.so channeldb.so whowasdb.so \
|
||||
restrict-commands.so rmtkl.so require-module.so \
|
||||
account-notify.so \
|
||||
message-tags.so batch.so \
|
||||
|
||||
@@ -0,0 +1,579 @@
|
||||
/*
|
||||
* Stores WHOWAS history in a .db file
|
||||
* (C) Copyright 2023 Syzop
|
||||
* License: GPLv2 or later
|
||||
*/
|
||||
|
||||
#include "unrealircd.h"
|
||||
|
||||
ModuleHeader MOD_HEADER = {
|
||||
"whowasdb",
|
||||
"1.0",
|
||||
"Stores and retrieves WHOWAS history",
|
||||
"UnrealIRCd Team",
|
||||
"unrealircd-6",
|
||||
};
|
||||
|
||||
/* Our header */
|
||||
#define WHOWASDB_HEADER 0x57484F57
|
||||
/* Database version */
|
||||
#define WHOWASDB_VERSION 100
|
||||
/* Save whowas of users to file every <this> seconds */
|
||||
#define WHOWASDB_SAVE_EVERY 300
|
||||
/* The very first save after boot, apply this delta, this
|
||||
* so we don't coincide with other (potentially) expensive
|
||||
* I/O events like saving tkldb.
|
||||
*/
|
||||
#define WHOWASDB_SAVE_EVERY_DELTA -60
|
||||
|
||||
#define MAGIC_WHOWASDB_START 0x11111111
|
||||
#define MAGIC_WHOWASDB_END 0x22222222
|
||||
|
||||
// #undef BENCHMARK
|
||||
|
||||
#define WARN_WRITE_ERROR(fname) \
|
||||
do { \
|
||||
unreal_log(ULOG_ERROR, "whowasdb", "WHOWASDB_FILE_WRITE_ERROR", NULL, \
|
||||
"[whowasdb] Error writing to temporary database file $filename: $system_error", \
|
||||
log_data_string("filename", fname), \
|
||||
log_data_string("system_error", unrealdb_get_error_string())); \
|
||||
} while(0)
|
||||
|
||||
#define W_SAFE(x) \
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
WARN_WRITE_ERROR(tmpfname); \
|
||||
unrealdb_close(db); \
|
||||
return 0; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define W_SAFE_PROPERTY(db, x, y) \
|
||||
do { \
|
||||
if (x && y && (!unrealdb_write_str(db, x) || !unrealdb_write_str(db, y))) \
|
||||
{ \
|
||||
WARN_WRITE_ERROR(tmpfname); \
|
||||
unrealdb_close(db); \
|
||||
return 0; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define IsMDErr(x, y, z) \
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
config_error("A critical error occurred when registering ModData for %s: %s", MOD_HEADER.name, ModuleGetErrorStr((z)->handle)); \
|
||||
return MOD_FAILED; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/* Structs */
|
||||
struct cfgstruct {
|
||||
char *database;
|
||||
char *db_secret;
|
||||
};
|
||||
|
||||
/* Forward declarations */
|
||||
void whowasdb_moddata_free(ModData *md);
|
||||
void setcfg(struct cfgstruct *cfg);
|
||||
void freecfg(struct cfgstruct *cfg);
|
||||
int whowasdb_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
|
||||
int whowasdb_config_posttest(int *errs);
|
||||
int whowasdb_config_run(ConfigFile *cf, ConfigEntry *ce, int type);
|
||||
EVENT(write_whowasdb_evt);
|
||||
int write_whowasdb(void);
|
||||
int write_whowas_entry(UnrealDB *db, const char *tmpfname, WhoWas *e);
|
||||
int read_whowasdb(void);
|
||||
void whowasdb_terminating(void);
|
||||
|
||||
/* External variables */
|
||||
extern WhoWas MODVAR WHOWAS[NICKNAMEHISTORYLENGTH];
|
||||
extern WhoWas MODVAR *WHOWASHASH[WHOWAS_HASH_TABLE_SIZE];
|
||||
extern MODVAR int whowas_next;
|
||||
|
||||
/* Global variables */
|
||||
static uint32_t whowasdb_version = WHOWASDB_VERSION;
|
||||
static struct cfgstruct cfg;
|
||||
static struct cfgstruct test;
|
||||
|
||||
static long whowasdb_next_event = 0;
|
||||
|
||||
MOD_TEST()
|
||||
{
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
memset(&test, 0, sizeof(test));
|
||||
setcfg(&test);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, whowasdb_config_test);
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, whowasdb_config_posttest);
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_INIT()
|
||||
{
|
||||
MARK_AS_OFFICIAL_MODULE(modinfo);
|
||||
|
||||
LoadPersistentLong(modinfo, whowasdb_next_event);
|
||||
|
||||
setcfg(&cfg);
|
||||
|
||||
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, whowasdb_config_run);
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_LOAD()
|
||||
{
|
||||
if (!whowasdb_next_event)
|
||||
{
|
||||
/* If this is the first time that our module is loaded, then read the database. */
|
||||
if (!read_whowasdb())
|
||||
{
|
||||
char fname[512];
|
||||
snprintf(fname, sizeof(fname), "%s.corrupt", cfg.database);
|
||||
if (rename(cfg.database, fname) == 0)
|
||||
config_warn("[whowasdb] Existing database renamed to %s and starting a new one...", fname);
|
||||
else
|
||||
config_warn("[whowasdb] Failed to rename database from %s to %s: %s", cfg.database, fname, strerror(errno));
|
||||
}
|
||||
whowasdb_next_event = TStime() + WHOWASDB_SAVE_EVERY + WHOWASDB_SAVE_EVERY_DELTA;
|
||||
}
|
||||
EventAdd(modinfo->handle, "whowasdb_write_whowasdb", write_whowasdb_evt, NULL, 1000, 0);
|
||||
if (ModuleGetError(modinfo->handle) != MODERR_NOERROR)
|
||||
{
|
||||
config_error("A critical error occurred when loading module %s: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle));
|
||||
return MOD_FAILED;
|
||||
}
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
MOD_UNLOAD()
|
||||
{
|
||||
if (loop.terminating)
|
||||
whowasdb_terminating();
|
||||
freecfg(&test);
|
||||
freecfg(&cfg);
|
||||
SavePersistentLong(modinfo, whowasdb_next_event);
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
void whowasdb_moddata_free(ModData *md)
|
||||
{
|
||||
if (md->i)
|
||||
md->i = 0;
|
||||
}
|
||||
|
||||
void setcfg(struct cfgstruct *cfg)
|
||||
{
|
||||
// Default: data/whowas.db
|
||||
safe_strdup(cfg->database, "whowas.db");
|
||||
convert_to_absolute_path(&cfg->database, PERMDATADIR);
|
||||
}
|
||||
|
||||
void freecfg(struct cfgstruct *cfg)
|
||||
{
|
||||
safe_free(cfg->database);
|
||||
safe_free(cfg->db_secret);
|
||||
}
|
||||
|
||||
int whowasdb_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
||||
{
|
||||
int errors = 0;
|
||||
ConfigEntry *cep;
|
||||
|
||||
// We are only interested in set::whowasdb::database
|
||||
if (type != CONFIG_SET)
|
||||
return 0;
|
||||
|
||||
if (!ce || strcmp(ce->name, "whowasdb"))
|
||||
return 0;
|
||||
|
||||
for (cep = ce->items; cep; cep = cep->next)
|
||||
{
|
||||
if (!cep->value)
|
||||
{
|
||||
config_error("%s:%i: blank set::whowasdb::%s without value", cep->file->filename, cep->line_number, cep->name);
|
||||
errors++;
|
||||
} else
|
||||
if (!strcmp(cep->name, "database"))
|
||||
{
|
||||
convert_to_absolute_path(&cep->value, PERMDATADIR);
|
||||
safe_strdup(test.database, cep->value);
|
||||
} else
|
||||
if (!strcmp(cep->name, "db-secret"))
|
||||
{
|
||||
const char *err;
|
||||
if ((err = unrealdb_test_secret(cep->value)))
|
||||
{
|
||||
config_error("%s:%i: set::whowasdb::db-secret: %s", cep->file->filename, cep->line_number, err);
|
||||
errors++;
|
||||
continue;
|
||||
}
|
||||
safe_strdup(test.db_secret, cep->value);
|
||||
} else
|
||||
{
|
||||
config_error("%s:%i: unknown directive set::whowasdb::%s", cep->file->filename, cep->line_number, cep->name);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
*errs = errors;
|
||||
return errors ? -1 : 1;
|
||||
}
|
||||
|
||||
int whowasdb_config_posttest(int *errs)
|
||||
{
|
||||
int errors = 0;
|
||||
char *errstr;
|
||||
|
||||
if (test.database && ((errstr = unrealdb_test_db(test.database, test.db_secret))))
|
||||
{
|
||||
config_error("[whowasdb] %s", errstr);
|
||||
errors++;
|
||||
}
|
||||
|
||||
*errs = errors;
|
||||
return errors ? -1 : 1;
|
||||
}
|
||||
|
||||
int whowasdb_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
|
||||
{
|
||||
ConfigEntry *cep;
|
||||
|
||||
// We are only interested in set::whowasdb::database
|
||||
if (type != CONFIG_SET)
|
||||
return 0;
|
||||
|
||||
if (!ce || strcmp(ce->name, "whowasdb"))
|
||||
return 0;
|
||||
|
||||
for (cep = ce->items; cep; cep = cep->next)
|
||||
{
|
||||
if (!strcmp(cep->name, "database"))
|
||||
safe_strdup(cfg.database, cep->value);
|
||||
else if (!strcmp(cep->name, "db-secret"))
|
||||
safe_strdup(cfg.db_secret, cep->value);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
EVENT(write_whowasdb_evt)
|
||||
{
|
||||
if (whowasdb_next_event > TStime())
|
||||
return;
|
||||
whowasdb_next_event = TStime() + WHOWASDB_SAVE_EVERY;
|
||||
write_whowasdb();
|
||||
}
|
||||
|
||||
int count_whowas_entries(void)
|
||||
{
|
||||
int i;
|
||||
int cnt = 0;
|
||||
|
||||
for (i=0; i < NICKNAMEHISTORYLENGTH; i++)
|
||||
{
|
||||
WhoWas *e = &WHOWAS[i];
|
||||
if (e->name)
|
||||
cnt++;
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
int write_whowasdb(void)
|
||||
{
|
||||
char tmpfname[512];
|
||||
UnrealDB *db;
|
||||
WhoWas *e;
|
||||
int cnt, i;
|
||||
#ifdef BENCHMARK
|
||||
struct timeval tv_alpha, tv_beta;
|
||||
|
||||
gettimeofday(&tv_alpha, NULL);
|
||||
#endif
|
||||
|
||||
// Write to a tempfile first, then rename it if everything succeeded
|
||||
snprintf(tmpfname, sizeof(tmpfname), "%s.%x.tmp", cfg.database, getrandom32());
|
||||
db = unrealdb_open(tmpfname, UNREALDB_MODE_WRITE, cfg.db_secret);
|
||||
if (!db)
|
||||
{
|
||||
WARN_WRITE_ERROR(tmpfname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
W_SAFE(unrealdb_write_int32(db, WHOWASDB_HEADER));
|
||||
W_SAFE(unrealdb_write_int32(db, whowasdb_version));
|
||||
|
||||
cnt = count_whowas_entries();
|
||||
W_SAFE(unrealdb_write_int64(db, cnt));
|
||||
|
||||
for (i=0; i < NICKNAMEHISTORYLENGTH; i++)
|
||||
{
|
||||
WhoWas *e = &WHOWAS[i];
|
||||
if (e->name)
|
||||
{
|
||||
if (!write_whowas_entry(db, tmpfname, e))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Everything seems to have gone well, attempt to close and rename the tempfile
|
||||
if (!unrealdb_close(db))
|
||||
{
|
||||
WARN_WRITE_ERROR(tmpfname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
/* The rename operation cannot be atomic on Windows as it will cause a "file exists" error */
|
||||
unlink(cfg.database);
|
||||
#endif
|
||||
if (rename(tmpfname, cfg.database) < 0)
|
||||
{
|
||||
config_error("[whowasdb] Error renaming '%s' to '%s': %s (DATABASE NOT SAVED)", tmpfname, cfg.database, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
#ifdef BENCHMARK
|
||||
gettimeofday(&tv_beta, NULL);
|
||||
config_status("[whowasdb] Benchmark: SAVE DB: %ld microseconds",
|
||||
((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec));
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
int write_whowas_entry(UnrealDB *db, const char *tmpfname, WhoWas *e)
|
||||
{
|
||||
char logofftime[64];
|
||||
|
||||
snprintf(logofftime, sizeof(logofftime), "%lld", (long long)e->logoff);
|
||||
|
||||
W_SAFE(unrealdb_write_int32(db, MAGIC_WHOWASDB_START));
|
||||
W_SAFE_PROPERTY(db, "nick", e->name);
|
||||
W_SAFE_PROPERTY(db, "logofftime", logofftime);
|
||||
W_SAFE_PROPERTY(db, "username", e->username);
|
||||
W_SAFE_PROPERTY(db, "hostname", e->hostname);
|
||||
W_SAFE_PROPERTY(db, "ip", e->ip);
|
||||
W_SAFE_PROPERTY(db, "realname", e->realname);
|
||||
W_SAFE_PROPERTY(db, "server", e->servername);
|
||||
W_SAFE_PROPERTY(db, "virthost", e->virthost);
|
||||
W_SAFE_PROPERTY(db, "account", e->account);
|
||||
W_SAFE_PROPERTY(db, "end", "");
|
||||
W_SAFE(unrealdb_write_int32(db, MAGIC_WHOWASDB_END));
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define FreeWhowasEntry() \
|
||||
do { \
|
||||
/* Some of these might be NULL */ \
|
||||
safe_free(key); \
|
||||
safe_free(value); \
|
||||
safe_free(nick); \
|
||||
safe_free(username); \
|
||||
safe_free(hostname); \
|
||||
safe_free(ip); \
|
||||
safe_free(realname); \
|
||||
logofftime = 0; \
|
||||
safe_free(server); \
|
||||
safe_free(virthost); \
|
||||
safe_free(account); \
|
||||
} while(0)
|
||||
|
||||
#define R_SAFE(x) \
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
config_warn("[whowasdb] Read error from database file '%s' (possible corruption): %s", cfg.database, unrealdb_get_error_string()); \
|
||||
unrealdb_close(db); \
|
||||
FreeWhowasEntry(); \
|
||||
return 0; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
int read_whowasdb(void)
|
||||
{
|
||||
UnrealDB *db;
|
||||
uint32_t version;
|
||||
int added = 0;
|
||||
int i;
|
||||
uint64_t count = 0;
|
||||
uint32_t magic;
|
||||
char *key = NULL;
|
||||
char *value = NULL;
|
||||
char *nick = NULL;
|
||||
char *username = NULL;
|
||||
char *hostname = NULL;
|
||||
char *ip = NULL;
|
||||
char *realname = NULL;
|
||||
long long logofftime = 0;
|
||||
char *server = NULL;
|
||||
char *virthost = NULL;
|
||||
char *account = NULL;
|
||||
#ifdef BENCHMARK
|
||||
struct timeval tv_alpha, tv_beta;
|
||||
|
||||
gettimeofday(&tv_alpha, NULL);
|
||||
#endif
|
||||
|
||||
db = unrealdb_open(cfg.database, UNREALDB_MODE_READ, cfg.db_secret);
|
||||
if (!db)
|
||||
{
|
||||
if (unrealdb_get_error_code() == UNREALDB_ERROR_FILENOTFOUND)
|
||||
{
|
||||
/* Database does not exist. Could be first boot */
|
||||
config_warn("[whowasdb] No database present at '%s', will start a new one", cfg.database);
|
||||
return 1;
|
||||
} else
|
||||
if (unrealdb_get_error_code() == UNREALDB_ERROR_NOTCRYPTED)
|
||||
{
|
||||
/* Re-open as unencrypted */
|
||||
db = unrealdb_open(cfg.database, UNREALDB_MODE_READ, NULL);
|
||||
if (!db)
|
||||
{
|
||||
/* This should actually never happen, unless some weird I/O error */
|
||||
config_warn("[whowasdb] Unable to open the database file '%s': %s", cfg.database, unrealdb_get_error_string());
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
{
|
||||
config_warn("[whowasdb] Unable to open the database file '%s' for reading: %s", cfg.database, unrealdb_get_error_string());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
R_SAFE(unrealdb_read_int32(db, &version));
|
||||
if (version != WHOWASDB_HEADER)
|
||||
{
|
||||
config_warn("[whowasdb] Database '%s' is not a whowas db (incorrect header)", cfg.database);
|
||||
unrealdb_close(db);
|
||||
return 0;
|
||||
}
|
||||
R_SAFE(unrealdb_read_int32(db, &version));
|
||||
if (version > whowasdb_version)
|
||||
{
|
||||
config_warn("[whowasdb] Database '%s' has a wrong version: expected it to be <= %u but got %u instead", cfg.database, whowasdb_version, version);
|
||||
unrealdb_close(db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
R_SAFE(unrealdb_read_int64(db, &count));
|
||||
|
||||
for (i=1; i <= count; i++)
|
||||
{
|
||||
// Variables
|
||||
key = value = NULL;
|
||||
nick = username = hostname = ip = realname = virthost = account = server = NULL;
|
||||
logofftime = 0;
|
||||
|
||||
R_SAFE(unrealdb_read_int32(db, &magic));
|
||||
if (magic != MAGIC_WHOWASDB_START)
|
||||
{
|
||||
config_error("[whowasdb] Corrupt database (%s) - whowasdb magic start is 0x%x. Further reading aborted.", cfg.database, magic);
|
||||
break;
|
||||
}
|
||||
while(1)
|
||||
{
|
||||
R_SAFE(unrealdb_read_str(db, &key));
|
||||
R_SAFE(unrealdb_read_str(db, &value));
|
||||
if (!strcmp(key, "nick"))
|
||||
nick = value;
|
||||
else if (!strcmp(key, "username"))
|
||||
username = value;
|
||||
else if (!strcmp(key, "hostname"))
|
||||
hostname = value;
|
||||
else if (!strcmp(key, "ip"))
|
||||
ip = value;
|
||||
else if (!strcmp(key, "realname"))
|
||||
realname = value;
|
||||
else if (!strcmp(key, "logofftime"))
|
||||
{
|
||||
logofftime = atoll(value);
|
||||
safe_free(value);
|
||||
} else if (!strcmp(key, "server"))
|
||||
server = value;
|
||||
else if (!strcmp(key, "virthost"))
|
||||
virthost = value;
|
||||
else if (!strcmp(key, "account"))
|
||||
account = value;
|
||||
else if (!strcmp(key, "end"))
|
||||
{
|
||||
safe_free(key);
|
||||
safe_free(value);
|
||||
break; /* DONE! */
|
||||
} else
|
||||
{
|
||||
safe_free(value);
|
||||
/* just don't do anything with it -- ignored (future compatible).
|
||||
* FALLTHROUGH...
|
||||
*/
|
||||
}
|
||||
safe_free(key);
|
||||
}
|
||||
R_SAFE(unrealdb_read_int32(db, &magic));
|
||||
if (magic != MAGIC_WHOWASDB_END)
|
||||
{
|
||||
config_error("[whowasdb] Corrupt database (%s) - whowasdb magic end is 0x%x. Further reading aborted.", cfg.database, magic);
|
||||
FreeWhowasEntry();
|
||||
break;
|
||||
}
|
||||
|
||||
if (nick && username && hostname && realname)
|
||||
{
|
||||
WhoWas *e = &WHOWAS[whowas_next];
|
||||
if (e->hashv != -1)
|
||||
free_whowas(e);
|
||||
/* Set values */
|
||||
e->hashv = hash_whowas_name(nick);
|
||||
e->logoff = logofftime;
|
||||
safe_strdup(e->name, nick);
|
||||
safe_strdup(e->username, username);
|
||||
safe_strdup(e->hostname, hostname);
|
||||
safe_strdup(e->ip, ip);
|
||||
if (virthost)
|
||||
safe_strdup(e->virthost, virthost);
|
||||
else
|
||||
safe_strdup(e->virthost, "");
|
||||
e->servername = find_or_add(server); /* scache */
|
||||
safe_strdup(e->realname, realname);
|
||||
safe_strdup(e->account, account);
|
||||
e->online = NULL;
|
||||
/* Server is special - scache shit */
|
||||
/* Add to hash table */
|
||||
add_whowas_to_list(&WHOWASHASH[e->hashv], e);
|
||||
/* And advance pointer (well, integer) */
|
||||
whowas_next++;
|
||||
if (whowas_next == NICKNAMEHISTORYLENGTH)
|
||||
whowas_next = 0;
|
||||
}
|
||||
|
||||
FreeWhowasEntry();
|
||||
added++;
|
||||
}
|
||||
|
||||
unrealdb_close(db);
|
||||
|
||||
if (added)
|
||||
config_status("[whowasdb] Added %d WHOWAS items", added);
|
||||
#ifdef BENCHMARK
|
||||
gettimeofday(&tv_beta, NULL);
|
||||
unreal_log(ULOG_DEBUG, "whowasdb", "WHOWASDB_BENCHMARK", NULL,
|
||||
"[whowasdb] Benchmark: LOAD DB: $time_msec microseconds",
|
||||
log_data_integer("time_msec", ((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec)));
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
#undef FreeWhowasEntry
|
||||
#undef R_SAFE
|
||||
|
||||
void whowasdb_terminating(void)
|
||||
{
|
||||
Client *client;
|
||||
|
||||
/* Add all the currently connected users to WHOWAS history: */
|
||||
list_for_each_entry(client, &client_list, client_node)
|
||||
{
|
||||
if (IsUser(client))
|
||||
{
|
||||
add_history(client, 0);
|
||||
off_history(client);
|
||||
}
|
||||
}
|
||||
|
||||
/* And write the database... */
|
||||
write_whowasdb();
|
||||
}
|
||||
+23
-21
@@ -23,17 +23,30 @@
|
||||
// Consider making add_history an efunc? Or via a hook?
|
||||
// Some users may not want to load cmd_whowas at all.
|
||||
|
||||
/* internally defined function */
|
||||
static void add_whowas_to_clist(WhoWas **, WhoWas *);
|
||||
static void del_whowas_from_clist(WhoWas **, WhoWas *);
|
||||
static void add_whowas_to_list(WhoWas **, WhoWas *);
|
||||
static void del_whowas_from_list(WhoWas **, WhoWas *);
|
||||
void add_whowas_to_clist(WhoWas **, WhoWas *);
|
||||
void del_whowas_from_clist(WhoWas **, WhoWas *);
|
||||
void add_whowas_to_list(WhoWas **, WhoWas *);
|
||||
void del_whowas_from_list(WhoWas **, WhoWas *);
|
||||
|
||||
WhoWas MODVAR WHOWAS[NICKNAMEHISTORYLENGTH];
|
||||
WhoWas MODVAR *WHOWASHASH[WHOWAS_HASH_TABLE_SIZE];
|
||||
|
||||
MODVAR int whowas_next = 0;
|
||||
|
||||
void free_whowas(WhoWas *e)
|
||||
{
|
||||
safe_free(e->name);
|
||||
safe_free(e->hostname);
|
||||
safe_free(e->virthost);
|
||||
safe_free(e->realname);
|
||||
safe_free(e->username);
|
||||
safe_free(e->account);
|
||||
e->servername = NULL;
|
||||
|
||||
if (e->online)
|
||||
del_whowas_from_clist(&(e->online->user->whowas), e);
|
||||
del_whowas_from_list(&WHOWASHASH[e->hashv], e);
|
||||
}
|
||||
void add_history(Client *client, int online)
|
||||
{
|
||||
WhoWas *new;
|
||||
@@ -41,19 +54,8 @@ void add_history(Client *client, int online)
|
||||
new = &WHOWAS[whowas_next];
|
||||
|
||||
if (new->hashv != -1)
|
||||
{
|
||||
safe_free(new->name);
|
||||
safe_free(new->hostname);
|
||||
safe_free(new->virthost);
|
||||
safe_free(new->realname);
|
||||
safe_free(new->username);
|
||||
safe_free(new->account);
|
||||
new->servername = NULL;
|
||||
free_whowas(new);
|
||||
|
||||
if (new->online)
|
||||
del_whowas_from_clist(&(new->online->user->whowas), new);
|
||||
del_whowas_from_list(&WHOWASHASH[new->hashv], new);
|
||||
}
|
||||
new->hashv = hash_whowas_name(client->name);
|
||||
new->logoff = TStime();
|
||||
new->umodes = client->umodes;
|
||||
@@ -153,7 +155,7 @@ void initwhowas()
|
||||
WHOWASHASH[i] = NULL;
|
||||
}
|
||||
|
||||
static void add_whowas_to_clist(WhoWas ** bucket, WhoWas * whowas)
|
||||
void add_whowas_to_clist(WhoWas ** bucket, WhoWas * whowas)
|
||||
{
|
||||
whowas->cprev = NULL;
|
||||
if ((whowas->cnext = *bucket) != NULL)
|
||||
@@ -161,7 +163,7 @@ static void add_whowas_to_clist(WhoWas ** bucket, WhoWas * whowas)
|
||||
*bucket = whowas;
|
||||
}
|
||||
|
||||
static void del_whowas_from_clist(WhoWas ** bucket, WhoWas * whowas)
|
||||
void del_whowas_from_clist(WhoWas ** bucket, WhoWas * whowas)
|
||||
{
|
||||
if (whowas->cprev)
|
||||
whowas->cprev->cnext = whowas->cnext;
|
||||
@@ -171,7 +173,7 @@ static void del_whowas_from_clist(WhoWas ** bucket, WhoWas * whowas)
|
||||
whowas->cnext->cprev = whowas->cprev;
|
||||
}
|
||||
|
||||
static void add_whowas_to_list(WhoWas ** bucket, WhoWas * whowas)
|
||||
void add_whowas_to_list(WhoWas ** bucket, WhoWas * whowas)
|
||||
{
|
||||
whowas->prev = NULL;
|
||||
if ((whowas->next = *bucket) != NULL)
|
||||
@@ -179,7 +181,7 @@ static void add_whowas_to_list(WhoWas ** bucket, WhoWas * whowas)
|
||||
*bucket = whowas;
|
||||
}
|
||||
|
||||
static void del_whowas_from_list(WhoWas ** bucket, WhoWas * whowas)
|
||||
void del_whowas_from_list(WhoWas ** bucket, WhoWas * whowas)
|
||||
{
|
||||
if (whowas->prev)
|
||||
whowas->prev->next = whowas->next;
|
||||
|
||||
Reference in New Issue
Block a user