From 9c234ffdf84adf85d8bd31dccb892e079e07703d Mon Sep 17 00:00:00 2001 From: Bram Matthys Date: Mon, 23 Nov 2009 09:43:10 +0000 Subject: [PATCH] - Added special caching of remote includes. When a remote include fails to load (for example when the webserver is down), then the most recent version of that remote include will be used, and the ircd will still boot and be able to rehash. Even though this is quite a simple feature, it can make a key difference when deciding to roll out remote includes on your network. Previously, servers would be unable to boot or rehash when the webserver was down, which would be a big problem (often unacceptable). The latest version of fetched urls are cached in the cache/ directory as cache/. Obviously, if there's no 'latest version' and an url fails, the ircd will still not be able to boot. This would be the case if you added or changed the path of a remote include and it's trying to fetch it for the first time. To disable this new behavior, check out REMOTEINC_SPECIALCACHE in include/config.h. --- Changes | 14 +++++++++++ include/h.h | 3 +++ src/ircd.c | 6 +++++ src/md5.c | 18 +++++++++++++++ src/s_conf.c | 64 +++++++++++++++++++++++++++++++++++++-------------- src/support.c | 18 +++++++++++++++ 6 files changed, 106 insertions(+), 17 deletions(-) diff --git a/Changes b/Changes index 089f63b8e..d9c61c66a 100644 --- a/Changes +++ b/Changes @@ -1810,3 +1810,17 @@ - When an incorrect command line argument is passed, the IRCd will no longer boot. Previously it said 'Server not started' but started anyway. Reported and patch provided by ohnobinki (#0003870). +- Added special caching of remote includes. When a remote include fails to + load (for example when the webserver is down), then the most recent + version of that remote include will be used, and the ircd will still boot + and be able to rehash. Even though this is quite a simple feature, it + can make a key difference when deciding to roll out remote includes on + your network. Previously, servers would be unable to boot or rehash when + the webserver was down, which would be a big problem (often unacceptable). + The latest version of fetched urls are cached in the cache/ directory as + cache/. + Obviously, if there's no 'latest version' and an url fails, the ircd will + still not be able to boot. This would be the case if you added or changed + the path of a remote include and it's trying to fetch it for the first time. + To disable this new behavior, check out REMOTEINC_SPECIALCACHE in + include/config.h. diff --git a/include/h.h b/include/h.h index d8bf3de92..a8f3199b5 100644 --- a/include/h.h +++ b/include/h.h @@ -635,6 +635,8 @@ extern int count_oper_sessions(char *); extern char *unreal_mktemp(char *dir, char *suffix); extern char *unreal_getpathname(char *filepath, char *path); extern char *unreal_getfilename(char *path); +extern char *unreal_mkcache(char *url); +extern int has_cached_version(char *url); extern int unreal_copyfile(char *src, char *dest); extern int unreal_copyfileex(char *src, char *dest, int tryhardlink); extern time_t unreal_getfilemodtime(char *filename); @@ -752,6 +754,7 @@ extern char *clean_ban_mask(char *, int, aClient *); extern void chanfloodtimer_stopchantimers(aChannel *chptr); extern int find_invex(aChannel *chptr, aClient *sptr); extern void DoMD5(unsigned char *mdout, unsigned char *src, unsigned long n); +extern char *md5hash(unsigned char *dst, unsigned char *src, unsigned long n); #ifdef JOINTHROTTLE aJFlood *cmodej_addentry(aClient *cptr, aChannel *chptr); void cmodej_delentry(aJFlood *e); diff --git a/src/ircd.c b/src/ircd.c index 932de66b0..9e04d8082 100644 --- a/src/ircd.c +++ b/src/ircd.c @@ -1370,8 +1370,14 @@ int InitwIRCD(int argc, char *argv[]) #endif #ifndef _WIN32 mkdir("tmp", S_IRUSR|S_IWUSR|S_IXUSR); /* Create the tmp dir, if it doesn't exist */ + #if defined(USE_LIBCURL) && defined(REMOTEINC_SPECIALCACHE) + mkdir("cache", S_IRUSR|S_IWUSR|S_IXUSR); /* Create the cache dir, if using curl and it doesn't exist */ + #endif #else mkdir("tmp"); + #if defined(USE_LIBCURL) && defined(REMOTEINC_SPECIALCACHE) + mkdir("cache"); + #endif #endif #ifndef _WIN32 /* diff --git a/src/md5.c b/src/md5.c index 5d0393cbd..20d0c440a 100644 --- a/src/md5.c +++ b/src/md5.c @@ -16,6 +16,7 @@ */ #include "config.h" +#include #if !defined(USE_SSL) #include @@ -292,3 +293,20 @@ MD5_CTX hash; MD5_Update(&hash, src, n); MD5_Final(mdout, &hash); } + +/** Generates an MD5 checksum - ASCII printable string (0011223344..etc..). + * @param dst[out] Buffer to store result in, this will be the result will be + * 32 characters + nul terminator, so needs to be at least 33 characters. + * @param src[in] The input data used to generate the checksum. + * @param n[in] Length of data. + */ +char *md5hash(unsigned char *dst, unsigned char *src, unsigned long n) +{ +unsigned char tmp[16]; + + DoMD5(tmp, src, n); + sprintf(dst, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8], + tmp[9], tmp[10], tmp[11], tmp[12], tmp[13], tmp[14], tmp[15]); + return dst; +} diff --git a/src/s_conf.c b/src/s_conf.c index e01a8d7eb..3a60e05c0 100644 --- a/src/s_conf.c +++ b/src/s_conf.c @@ -9438,7 +9438,7 @@ static void conf_download_complete(char *url, char *file, char *errorbuf, int ca } } if (!file && !cached) - add_remote_include(file, url, 0, errorbuf); + add_remote_include(file, url, 0, errorbuf); /* DOWNLOAD FAILED */ else { if (cached) @@ -9447,11 +9447,18 @@ static void conf_download_complete(char *url, char *file, char *errorbuf, int ca char *file = unreal_getfilename(urlfile); char *tmp = unreal_mktemp("tmp", file); unreal_copyfileex(inc->file, tmp, 1); +#ifdef REMOTEINC_SPECIALCACHE + unreal_copyfileex(inc->file, unreal_mkcache(url), 0); +#endif add_remote_include(tmp, url, 0, NULL); free(urlfile); } - else + else { add_remote_include(file, url, 0, NULL); +#ifdef REMOTEINC_SPECIALCACHE + unreal_copyfileex(file, unreal_mkcache(url), 0); +#endif + } } for (inc = conf_include; inc; inc = (ConfigItem_include *)inc->next) { @@ -9648,27 +9655,50 @@ int remote_include(ConfigEntry *ce) file = download_file(ce->ce_vardata, &error); if (!file) { - config_error("%s:%i: include: error downloading '%s': %s", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - ce->ce_vardata, error); - return -1; - } - else - { - if ((ret = load_conf(file)) >= 0) - add_remote_include(file, ce->ce_vardata, INCLUDE_USED, NULL); - free(file); - return ret; +#ifdef REMOTEINC_SPECIALCACHE + if (has_cached_version(ce->ce_vardata)) + { + config_warn("%s:%i: include: error downloading '%s': %s -- using cached version instead.", + ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + ce->ce_vardata, error); + file = strdup(unreal_mkcache(ce->ce_vardata)); + /* Let it pass to load_conf()... */ + } else { +#endif + config_error("%s:%i: include: error downloading '%s': %s", + ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + ce->ce_vardata, error); + return -1; +#ifdef REMOTEINC_SPECIALCACHE + } +#endif } + if ((ret = load_conf(file)) >= 0) + add_remote_include(file, ce->ce_vardata, INCLUDE_USED, NULL); + free(file); + return ret; } else { if (errorbuf) { - config_error("%s:%i: include: error downloading '%s': %s", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - ce->ce_vardata, errorbuf); - return -1; +#ifdef REMOTEINC_SPECIALCACHE + if (has_cached_version(ce->ce_vardata)) + { + config_warn("%s:%i: include: error downloading '%s': %s -- using cached version instead.", + ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + ce->ce_vardata, errorbuf); + /* Let it pass to load_conf()... */ + file = strdup(unreal_mkcache(ce->ce_vardata)); + } else { +#endif + config_error("%s:%i: include: error downloading '%s': %s", + ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + ce->ce_vardata, errorbuf); + return -1; +#ifdef REMOTEINC_SPECIALCACHE + } +#endif } if (config_verbose > 0) config_status("Loading %s from download", ce->ce_vardata); diff --git a/src/support.c b/src/support.c index f8a5a8c9c..e4f5f19b2 100644 --- a/src/support.c +++ b/src/support.c @@ -1760,6 +1760,24 @@ char *unreal_getfilename(char *path) return end; } +/* Returns a consistent filename for the cache/ directory. + * Returned value will be like: cache/ + */ +char *unreal_mkcache(char *url) +{ +static char tempbuf[PATH_MAX+1]; +char tmp2[33]; + + snprintf(tempbuf, PATH_MAX, "cache/%s", md5hash(tmp2, url, strlen(url))); + return tempbuf; +} + +/* Returns 1 if a cached version of the url exists, otherwise 0. */ +int has_cached_version(char *url) +{ + return file_exists(unreal_mkcache(url)); +} + /* Copys the contents of the src file to the dest file. * The dest file will have permissions r-x------ */