diff options
author | sjaz <sjaz@5417fbe8-f217-4b02-8779-1006273d7864> | 2009-01-01 12:00:20 +0000 |
---|---|---|
committer | sjaz <sjaz@5417fbe8-f217-4b02-8779-1006273d7864> | 2009-01-01 12:00:20 +0000 |
commit | c777c8d9aa7cd5c2e9a399727a7fa9985a77fb1c (patch) | |
tree | 9e996ae4a1bbb833cec036c5cd4d87a590149e85 /src/datafiles.c |
Anope Stable Branch
git-svn-id: http://anope.svn.sourceforge.net/svnroot/anope/stable@1902 5417fbe8-f217-4b02-8779-1006273d7864
Diffstat (limited to 'src/datafiles.c')
-rw-r--r-- | src/datafiles.c | 788 |
1 files changed, 788 insertions, 0 deletions
diff --git a/src/datafiles.c b/src/datafiles.c new file mode 100644 index 000000000..3af10b1d4 --- /dev/null +++ b/src/datafiles.c @@ -0,0 +1,788 @@ +/* Database file handling routines. + * + * (C) 2003-2008 Anope Team + * Contact us at info@anope.org + * + * Please read COPYING and README for further details. + * + * Based on the original code of Epona by Lara. + * Based on the original code of Services by Andy Church. + * + * $Id$ + * + */ + +#include "services.h" +#include "datafiles.h" +#include <fcntl.h> + +static int curday = 0; +static time_t lastwarn = 0; + +/*************************************************************************/ + +/** + * Return the version number on the file. Return 0 if there is no version + * number or the number doesn't make sense (i.e. less than 1 or greater + * than FILE_VERSION). + * @param f dbFile Struct Member + * @return int 0 if failure, 1 > is the version number + */ +int get_file_version(dbFILE * f) +{ + FILE *fp = f->fp; + int version = + fgetc(fp) << 24 | fgetc(fp) << 16 | fgetc(fp) << 8 | fgetc(fp); + if (ferror(fp)) { +#ifndef NOT_MAIN + log_perror("Error reading version number on %s", f->filename); +#endif + return 0; + } else if (feof(fp)) { +#ifndef NOT_MAIN + alog("Error reading version number on %s: End of file detected", + f->filename); +#endif + return 0; + } else if (version < 1) { +#ifndef NOT_MAIN + alog("Invalid version number (%d) on %s", version, f->filename); +#endif + return 0; + } + return version; +} + +/*************************************************************************/ + +/** + * Write the current version number to the file. + * @param f dbFile Struct Member + * @return 0 on error, 1 on success. + */ +int write_file_version(dbFILE * f, uint32 version) +{ + FILE *fp = f->fp; + if (fputc(version >> 24 & 0xFF, fp) < 0 || + fputc(version >> 16 & 0xFF, fp) < 0 || + fputc(version >> 8 & 0xFF, fp) < 0 || + fputc(version & 0xFF, fp) < 0) { +#ifndef NOT_MAIN + log_perror("Error writing version number on %s", f->filename); +#endif + return 0; + } + return 1; +} + +/*************************************************************************/ + +/** + * Open the database for reading + * @param service If error whom to return the error as + * @param filename File to open as the database + * @return dbFile struct + */ +static dbFILE *open_db_read(const char *service, const char *filename) +{ + dbFILE *f; + FILE *fp; + + f = scalloc(sizeof(*f), 1); + if (!f) { +#ifndef NOT_MAIN + log_perror("Can't read %s database %s", service, filename); + if (time(NULL) - lastwarn > WarningTimeout) { + anope_cmd_global(NULL, + "Write error on %s: Memory allocation failed", + filename); + lastwarn = time(NULL); + } +#endif + return NULL; + } + strscpy(f->filename, filename, sizeof(f->filename)); + f->mode = 'r'; + fp = fopen(f->filename, "rb"); + if (!fp) { + int errno_save = errno; +#ifndef NOT_MAIN + if (errno != ENOENT) + log_perror("Can not read %s database %s", service, + f->filename); + if (time(NULL) - lastwarn > WarningTimeout) { + anope_cmd_global(NULL, "Write error on %s: %s", f->filename, + strerror(errno)); + lastwarn = time(NULL); + } +#endif + free(f); + errno = errno_save; + return NULL; + } + f->fp = fp; + f->backupfp = NULL; + return f; +} + +/*************************************************************************/ + +/** + * Open the database for writting + * @param service If error whom to return the error as + * @param filename File to open as the database + * @param version Database Version + * @return dbFile struct + */ +static dbFILE *open_db_write(const char *service, const char *filename, + uint32 version) +{ + dbFILE *f; + int fd; +#ifdef _WIN32 + char buffer[_MAX_PATH]; + char win32filename[MAXPATHLEN]; + + /* Get the current working directory: */ + if (_getcwd(buffer, _MAX_PATH) == NULL) { + alog("Warning: Unable to set Current working directory"); + } +#endif + + f = scalloc(sizeof(*f), 1); + if (!f) { +#ifndef NOT_MAIN + log_perror("Can not read %s database %s", service, filename); +#else + alog("Can not read %s database %s", service, filename); +#endif + return NULL; + } + strscpy(f->filename, filename, sizeof(f->filename)); +#ifndef _WIN32 + filename = f->filename; +#else + snprintf(win32filename, sizeof(win32filename), "%s\\%s", buffer, + f->filename); + filename = win32filename; +#endif + f->mode = 'w'; + + *f->backupname = 0; + snprintf(f->backupname, sizeof(f->backupname), "%s.save", filename); + if (!*f->backupname || strcmp(f->backupname, filename) == 0) { + int errno_save = errno; +#ifndef NOT_MAIN + alog("Opening %s database %s for write: Filename too long", + service, filename); +#endif + free(f); + errno = errno_save; + return NULL; + } +#ifndef _WIN32 + unlink(filename); +#else + DeleteFile(filename); +#endif + f->backupfp = fopen(filename, "rb"); +#ifdef _WIN32 + if (!MoveFileExA(filename, f->backupname, MOVEFILE_COPY_ALLOWED) + && GetLastError() != ENOENT) { + int errno_save = GetLastError(); +#else + if (rename(filename, f->backupname) < 0 && errno != ENOENT) { + int errno_save = errno; +#endif +#ifndef NOT_MAIN + static int walloped = 0; + if (!walloped) { + walloped++; + anope_cmd_global(NULL, "Can not back up %s database %s", + service, filename); + } +#ifdef _WIN32 + if (debug) { + if (errno == ENOENT) { + alog("debug: Error %d (ENOENT) : the file or directory does not exist", errno, filename); + } else if (errno == EACCES) { + alog("debug: Error %d (EACCES) : error while attempting to access file", errno); + } else { + alog("debug: Error %d", errno); + } + } +#else + if (debug) { + alog("debug: Error %d", errno); + } +#endif + errno = errno_save; + log_perror("Can not back up %s database %s", service, filename); + if (!NoBackupOkay) { +#endif + if (f->backupfp) + fclose(f->backupfp); + free(f); + errno = errno_save; + return NULL; +#ifndef NOT_MAIN + } +#endif + *f->backupname = 0; + } +#ifndef _WIN32 + unlink(filename); +#else + DeleteFile(filename); +#endif + /* Use open() to avoid people sneaking a new file in under us */ +#ifndef _WIN32 + fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0666); +#else + fd = open(filename, O_WRONLY | O_CREAT | O_EXCL | _O_BINARY, 0666); +#endif + f->fp = fdopen(fd, "wb"); /* will fail and return NULL if fd < 0 */ + if (!f->fp || !write_file_version(f, version)) { + int errno_save = errno; +#ifndef NOT_MAIN + static int walloped = 0; + if (!walloped) { + walloped++; + anope_cmd_global(NULL, "Can't write to %s database %s", + service, filename); + } + errno = errno_save; + log_perror("Can't write to %s database %s", service, filename); +#endif + if (f->fp) { + fclose(f->fp); +#ifndef _WIN32 + unlink(filename); +#else + DeleteFile(filename); +#endif + } + if (*f->backupname && rename(f->backupname, filename) < 0) +#ifndef NOT_MAIN + log_perror("Cannot restore backup copy of %s", filename); +#else + ; +#endif + /* Then the Lord said unto Moses, thou shalt free what thou hast malloced + * -- codemastr */ + free(f); + errno = errno_save; + return NULL; + } + return f; +} + +/*************************************************************************/ + +/** + * Open a database file for reading (*mode == 'r') or writing (*mode == 'w'). + * Return the stream pointer, or NULL on error. When opening for write, it + * is an error for rename() to return an error (when backing up the original + * file) other than ENOENT, if NO_BACKUP_OKAY is not defined; it is an error + * if the version number cannot be written to the file; and it is a fatal + * error if opening the file for write fails and the backup was successfully + * made but cannot be restored. + * @param service If error whom to return the error as + * @param filename File to open as the database + * @param mode Mode for writting or reading + * @param version Database Version + * @return dbFile struct + */ +dbFILE *open_db(const char *service, const char *filename, + const char *mode, uint32 version) +{ + if (*mode == 'r') { + return open_db_read(service, filename); + } else if (*mode == 'w') { + return open_db_write(service, filename, version); + } else { + errno = EINVAL; + return NULL; + } +} + +/*************************************************************************/ + +/** + * Restore the database file to its condition before open_db(). This is + * identical to close_db() for files open for reading; however, for files + * open for writing, we first attempt to restore any backup file before + * closing files. + * @param dbFile struct + * @return void + */ +void restore_db(dbFILE * f) +{ + int errno_save = errno; + + if (f->mode == 'w') { + int ok = 0; /* Did we manage to restore the old file? */ + errno = errno_save = 0; + if (*f->backupname && strcmp(f->backupname, f->filename) != 0) { + if (rename(f->backupname, f->filename) == 0) + ok = 1; + } + if (!ok && f->backupfp) { + char buf[1024]; + int i; + ok = 1; + if (fseek(f->fp, 0, SEEK_SET) < 0) + ok = 0; + while (ok && (i = fread(buf, 1, sizeof(buf), f->backupfp)) > 0) { + if (fwrite(buf, 1, i, f->fp) != i) + ok = 0; + } + if (ok) { + fflush(f->fp); + ftruncate(fileno(f->fp), ftell(f->fp)); + } + } +#ifndef NOT_MAIN + if (!ok && errno > 0) + log_perror("Unable to restore backup of %s", f->filename); +#endif + errno_save = errno; + if (f->backupfp) + fclose(f->backupfp); + if (*f->backupname) +#ifndef _WIN32 + unlink(f->backupname); +#else + DeleteFile(f->backupname); +#endif + } + fclose(f->fp); + if (!errno_save) + errno_save = errno; + free(f); + errno = errno_save; +} + +/*************************************************************************/ + +/** + * Close a database file. If the file was opened for write, remove the + * backup we (may have) created earlier. + * @param dbFile struct + * @return void + */ +void close_db(dbFILE * f) +{ + if (f->mode == 'w' && *f->backupname + && strcmp(f->backupname, f->filename) != 0) { + if (f->backupfp) + fclose(f->backupfp); +#ifndef _WIN32 + unlink(f->backupname); +#else + DeleteFile(f->backupname); +#endif + } + fclose(f->fp); + free(f); +} + +/*************************************************************************/ + +/** + * Read and write 2- and 4-byte quantities, pointers, and strings. All + * multibyte values are stored in big-endian order (most significant byte + * first). A pointer is stored as a byte, either 0 if NULL or 1 if not, + * and read pointers are returned as either (void *)0 or (void *)1. A + * string is stored with a 2-byte unsigned length (including the trailing + * \0) first; a length of 0 indicates that the string pointer is NULL. + * Written strings are truncated silently at 65534 bytes, and are always + * null-terminated. + * + * @param ret 16bit integer to write + * @param dbFile struct + * @return -1 on error, 0 otherwise. + */ +int read_int16(uint16 * ret, dbFILE * f) +{ + int c1, c2; + + c1 = fgetc(f->fp); + c2 = fgetc(f->fp); + if (c1 == EOF || c2 == EOF) + return -1; + *ret = c1 << 8 | c2; + return 0; +} + +/*************************************************************************/ + +/** + * Write a 16bit integer + * + * @param ret 16bit integer to write + * @param dbFile struct + * @return -1 on error, 0 otherwise. + */ +int write_int16(uint16 val, dbFILE * f) +{ + if (fputc((val >> 8) & 0xFF, f->fp) == EOF + || fputc(val & 0xFF, f->fp) == EOF) { + return -1; + } + return 0; +} + +/*************************************************************************/ + +/** + * Read a unsigned 32bit integer + * + * @param ret unsigned 32bit integer to read + * @param dbFile struct + * @return -1 on error, 0 otherwise. + */ +int read_int32(uint32 * ret, dbFILE * f) +{ + int c1, c2, c3, c4; + + c1 = fgetc(f->fp); + c2 = fgetc(f->fp); + c3 = fgetc(f->fp); + c4 = fgetc(f->fp); + if (c1 == EOF || c2 == EOF || c3 == EOF || c4 == EOF) + return -1; + *ret = c1 << 24 | c2 << 16 | c3 << 8 | c4; + return 0; +} + +/*************************************************************************/ + +/** + * Write a unsigned 32bit integer + * + * @param ret unsigned 32bit integer to write + * @param dbFile struct + * @return -1 on error, 0 otherwise. + */ +int write_int32(uint32 val, dbFILE * f) +{ + if (fputc((val >> 24) & 0xFF, f->fp) == EOF) + return -1; + if (fputc((val >> 16) & 0xFF, f->fp) == EOF) + return -1; + if (fputc((val >> 8) & 0xFF, f->fp) == EOF) + return -1; + if (fputc((val) & 0xFF, f->fp) == EOF) + return -1; + return 0; +} + +/*************************************************************************/ + +/** + * Read Pointer + * + * @param ret pointer to read + * @param dbFile struct + * @return -1 on error, 0 otherwise. + */ +int read_ptr(void **ret, dbFILE * f) +{ + int c; + + c = fgetc(f->fp); + if (c == EOF) + return -1; + *ret = (c ? (void *) 1 : (void *) 0); + return 0; +} + +/*************************************************************************/ + +/** + * Write Pointer + * + * @param ret pointer to write + * @param dbFile struct + * @return -1 on error, 0 otherwise. + */ +int write_ptr(const void *ptr, dbFILE * f) +{ + if (fputc(ptr ? 1 : 0, f->fp) == EOF) + return -1; + return 0; +} + +/*************************************************************************/ + +/** + * Read String + * + * @param ret string + * @param dbFile struct + * @return -1 on error, 0 otherwise. + */ +int read_string(char **ret, dbFILE * f) +{ + char *s; + uint16 len; + + if (read_int16(&len, f) < 0) + return -1; + if (len == 0) { + *ret = NULL; + return 0; + } + s = scalloc(len, 1); + if (len != fread(s, 1, len, f->fp)) { + free(s); + return -1; + } + *ret = s; + return 0; +} + +/*************************************************************************/ + +/** + * Write String + * + * @param ret string + * @param dbFile struct + * @return -1 on error, 0 otherwise. + */ +int write_string(const char *s, dbFILE * f) +{ + uint32 len; + + if (!s) + return write_int16(0, f); + len = strlen(s); + if (len > 65534) + len = 65534; + if (write_int16((uint16) (len + 1), f) < 0) + return -1; + if (len > 0 && fwrite(s, 1, len, f->fp) != len) + return -1; + if (fputc(0, f->fp) == EOF) + return -1; + return 0; +} + +/*************************************************************************/ + +/** + * Renames a database + * + * @param name Database to name + * @param ext Extention + * @return void + */ +static void rename_database(char *name, char *ext) +{ + + char destpath[PATH_MAX]; + + snprintf(destpath, sizeof(destpath), "backups/%s.%s", name, ext); + if (rename(name, destpath) != 0) { + alog("Backup of %s failed.", name); + anope_cmd_global(s_OperServ, "WARNING! Backup of %s failed.", + name); + } +} + +/*************************************************************************/ + +/** + * Removes old databases + * + * @return void + */ +static void remove_backups(void) +{ + + char ext[9]; + char path[PATH_MAX]; + + time_t t; + struct tm tm; + + time(&t); + t -= (60 * 60 * 24 * KeepBackups); + tm = *localtime(&t); + strftime(ext, sizeof(ext), "%Y%m%d", &tm); + + snprintf(path, sizeof(path), "backups/%s.%s", NickDBName, ext); +#ifndef _WIN32 + unlink(path); +#else + DeleteFile(path); +#endif + snprintf(path, sizeof(path), "backups/%s.%s", ChanDBName, ext); +#ifndef _WIN32 + unlink(path); +#else + DeleteFile(path); +#endif + snprintf(path, sizeof(path), "backups/%s.%s", OperDBName, ext); +#ifndef _WIN32 + unlink(path); +#else + DeleteFile(path); +#endif + snprintf(path, sizeof(path), "backups/%s.%s", NewsDBName, ext); +#ifndef _WIN32 + unlink(path); +#else + DeleteFile(path); +#endif + snprintf(path, sizeof(path), "backups/%s.%s", ExceptionDBName, ext); +#ifndef _WIN32 + unlink(path); +#else + DeleteFile(path); +#endif + + if (s_BotServ) { + snprintf(path, sizeof(path), "backups/%s.%s", BotDBName, ext); +#ifndef _WIN32 + unlink(path); +#else + DeleteFile(path); +#endif + } + if (s_HostServ) { + snprintf(path, sizeof(path), "backups/%s.%s", HostDBName, ext); +#ifndef _WIN32 + unlink(path); +#else + DeleteFile(path); +#endif + } + if (NSEmailReg) { + snprintf(path, sizeof(path), "backups/%s.%s", PreNickDBName, ext); +#ifndef _WIN32 + unlink(path); +#else + DeleteFile(path); +#endif + } +} + +/*************************************************************************/ + +/** + * Handles database backups. + * + * @return void + */ +void backup_databases(void) +{ + + time_t t; + struct tm tm; + + if (!KeepBackups) { + return; + } + + time(&t); + tm = *localtime(&t); + + if (!curday) { + curday = tm.tm_yday; + return; + } + + if (curday != tm.tm_yday) { + + char ext[9]; + + send_event(EVENT_DB_BACKUP, 1, EVENT_START); + alog("Backing up databases"); + + remove_backups(); + + curday = tm.tm_yday; + strftime(ext, sizeof(ext), "%Y%m%d", &tm); + + if (!skeleton) { + rename_database(NickDBName, ext); + if (s_BotServ) { + rename_database(BotDBName, ext); + } + rename_database(ChanDBName, ext); + if (s_HostServ) { + rename_database(HostDBName, ext); + } + if (NSEmailReg) { + rename_database(PreNickDBName, ext); + } + } + + rename_database(OperDBName, ext); + rename_database(NewsDBName, ext); + rename_database(ExceptionDBName, ext); + send_event(EVENT_DB_BACKUP, 1, EVENT_STOP); + } +} + +/*************************************************************************/ + +void ModuleDatabaseBackup(char *dbname) +{ + + time_t t; + struct tm tm; + + if (!KeepBackups) { + return; + } + + time(&t); + tm = *localtime(&t); + + if (!curday) { + curday = tm.tm_yday; + return; + } + + if (curday != tm.tm_yday) { + + char ext[9]; + + if (debug) { + alog("Module Database Backing up %s", dbname); + } + ModuleRemoveBackups(dbname); + curday = tm.tm_yday; + strftime(ext, sizeof(ext), "%Y%m%d", &tm); + rename_database(dbname, ext); + } +} + +/*************************************************************************/ + +void ModuleRemoveBackups(char *dbname) +{ + char ext[9]; + char path[PATH_MAX]; + + time_t t; + struct tm tm; + + time(&t); + t -= (60 * 60 * 24 * KeepBackups); + tm = *localtime(&t); + strftime(ext, sizeof(ext), "%Y%m%d", &tm); + + snprintf(path, sizeof(path), "backups/%s.%s", dbname, ext); +#ifndef _WIN32 + unlink(path); +#else + DeleteFile(path); +#endif +} + +/*************************************************************************/ |