Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/VibeDevelopers/Comet.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885/* * ircd-ratbox: A slightly useful ircd. * modules.c: A module loader. * * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center * Copyright (C) 1996-2002 Hybrid Development Team * Copyright (C) 2002-2005 ircd-ratbox development team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */
#include "stdinc.h"#include "modules.h"#include "logger.h"#include "ircd.h"#include "client.h"#include "send.h"#include "s_conf.h"#include "s_newconf.h"#include "numeric.h"#include "parse.h"#include "ircd_defs.h"#include "match.h"#include "s_serv.h"#include "capability.h"#include "hash.h"
#include <ltdl.h>
#ifndef LT_MODULE_EXT# error "Comet requires loadable module support."#endif
struct module_dentry{ char name[PATH_MAX];};
rb_dlink_list module_list;rb_dlink_list mod_paths;
#define MOD_WARN_DELTA (90 * 86400) /* time in seconds, 86400 seconds in a day */
voidinit_modules(void){ if(lt_dlinit()) { ilog(L_MAIN, "lt_dlinit failed"); exit(EXIT_FAILURE); }
memset(&module_list, 0, sizeof(module_list)); memset(&mod_paths, 0, sizeof(mod_paths));
/* Add the default paths we look in to the module system --nenolod */ mod_add_path(ircd_paths[IRCD_PATH_MODULES]); mod_add_path(ircd_paths[IRCD_PATH_AUTOLOAD_MODULES]);}
static unsigned int prev_caps;
voidmod_remember_clicaps(void){ prev_caps = capability_index_mask(cli_capindex);}
voidmod_notify_clicaps(void){ unsigned int cur_caps = capability_index_mask(cli_capindex); unsigned int del = prev_caps & ~cur_caps; unsigned int new = cur_caps & ~prev_caps;
if (del) sendto_local_clients_with_capability(CLICAP_CAP_NOTIFY, ":%s CAP * DEL :%s", me.name, capability_index_list(cli_capindex, del)); if (new) sendto_local_clients_with_capability(CLICAP_CAP_NOTIFY, ":%s CAP * NEW :%s", me.name, capability_index_list(cli_capindex, new));}
/* mod_find_path() * * input - path * output - none * side effects - returns a module path from path */static char *mod_find_path(const char *path){ rb_dlink_node *ptr; char *mpath;
RB_DLINK_FOREACH(ptr, mod_paths.head) { mpath = ptr->data;
if(!strcmp(path, mpath)) return mpath; }
return NULL;}
/* mod_add_path * * input - path * ouput - * side effects - adds path to list */voidmod_add_path(const char *path){ char *pathst;
if(mod_find_path(path)) return;
pathst = rb_strdup(path); rb_dlinkAddAlloc(pathst, &mod_paths);}
/* mod_clear_paths() * * input - * output - * side effects - clear the lists of paths */voidmod_clear_paths(void){ rb_dlink_node *ptr, *next_ptr;
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, mod_paths.head) { rb_free(ptr->data); rb_free_rb_dlink_node(ptr); }
mod_paths.head = mod_paths.tail = NULL; mod_paths.length = 0;}
/* findmodule_byname * * input - module to load * output - index of module on success, -1 on failure * side effects - none */struct module *findmodule_byname(const char *name){ rb_dlink_node *ptr; char name_ext[PATH_MAX + 1];
rb_strlcpy(name_ext, name, sizeof name_ext); rb_strlcat(name_ext, LT_MODULE_EXT, sizeof name_ext);
RB_DLINK_FOREACH(ptr, module_list.head) { struct module *mod = ptr->data;
if(!irccmp(mod->name, name)) return mod;
if(!irccmp(mod->name, name_ext)) return mod; }
return NULL;}
/* module_dentry_sort_cb() * * Helper function for load_all_modules_dir() * * input - 2 module file names * output - file name sorting order * side effects - the array of modules is sorted according to the return value */static intmodule_dentry_sort_cb(const void *ptr1, const void *ptr2){ const struct module_dentry *mod1 = ptr1; const struct module_dentry *mod2 = ptr2;
return strcmp(mod1->name, mod2->name);}
/* load_all_modules_dir() * * Helper function for load_all_modules() * * input - Module directory, whether this directory is for core modules or not, * whether to warn about module loading * output - None * side effects - Loads every module located in the directory. If it is a core module * and it fails to load, the process exits in failure. */static voidload_all_modules_dir(const char *path, bool core, bool warn){ const size_t module_ext_len = strlen(LT_MODULE_EXT); struct module_dentry *dentries = NULL; size_t dentries_length = 0; size_t dentries_used = 0; char modpath[PATH_MAX + 1]; struct dirent *dirent; struct stat statbuf; DIR *moddir; size_t len;
if ((moddir = opendir(path)) == NULL) { if (! core) { ilog(L_MAIN, "Could not load modules from %s (%s)", path, strerror(errno)); return; }
ilog(L_MAIN, "Could not load core modules from %s (%s); terminating ircd", path, strerror(errno)); exit(EXIT_FAILURE); }
while ((dirent = readdir(moddir)) != NULL) { // File name length is not greater than the size of required extension (e.g. ".so") if ((len = strlen(dirent->d_name)) <= module_ext_len) continue;
// File name does not end with required extension if (strncasecmp(dirent->d_name + (len - module_ext_len), LT_MODULE_EXT, module_ext_len) != 0) continue;
// Unable to obtain dentry info or dentry is not actually a file? snprintf(modpath, sizeof modpath, "%s/%s", path, dirent->d_name); if (! (stat(modpath, &statbuf) == 0 && S_ISREG(statbuf.st_mode))) continue;
// Extend the modules array if it is not long enough if (dentries_used == dentries_length) { dentries_length += 32; dentries = rb_realloc(dentries, dentries_length * sizeof *dentries); }
// Add this module to the array of modules to load rb_strlcpy(dentries[dentries_used].name, dirent->d_name, sizeof dentries[dentries_used].name);
// Advance dentries_used++; }
closedir(moddir);
if (! dentries_used) { if (core) { ilog(L_MAIN, "Could not load core modules from %s (empty); terminating ircd", path); exit(EXIT_FAILURE); }
return; }
// Sort the array of modules to load based on their file name qsort(dentries, dentries_used, sizeof(struct module_dentry), &module_dentry_sort_cb);
// Attempt to load each module for (size_t i = 0; i < dentries_used; i++) { char modpath[PATH_MAX + 1];
snprintf(modpath, sizeof modpath, "%s/%s", path, dentries[i].name);
if (load_a_module(modpath, warn, MAPI_ORIGIN_CORE, core)) continue;
if (core) { ilog(L_MAIN, "Error while loading core module %s; terminating ircd", dentries[i].name); exit(EXIT_FAILURE); }
ilog(L_MAIN, "Error while loading module %s", dentries[i].name); }
// Clean up rb_free(dentries);}
/* load_all_modules() * * input - * output - * side effects - Loads every core module, then every module in the autoload directory. * If a core module fails to load, the process exits in failure. */voidload_all_modules(bool warn){ load_all_modules_dir(ircd_paths[IRCD_PATH_MODULES], true, warn); load_all_modules_dir(ircd_paths[IRCD_PATH_AUTOLOAD_MODULES], false, warn);}
/* load_one_module() * * input - * output - * side effects - */boolload_one_module(const char *path, int origin, bool coremodule){ char modpath[PATH_MAX]; rb_dlink_node *pathst;
if (coremodule) origin = MAPI_ORIGIN_CORE;
RB_DLINK_FOREACH(pathst, mod_paths.head) { struct stat statbuf; const char *mpath = pathst->data;
snprintf(modpath, sizeof(modpath), "%s/%s%s", mpath, path, LT_MODULE_EXT); if((strstr(modpath, "../") == NULL) && (strstr(modpath, "/..") == NULL)) { if(stat(modpath, &statbuf) == 0 && S_ISREG(statbuf.st_mode)) { /* Regular files only please */ return load_a_module(modpath, true, origin, coremodule); }
} }
sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Cannot locate module %s", path);
if (server_state_foreground) ierror("cannot locate module %s", path);
return false;}
static char unknown_ver[] = "<unknown>";static char unknown_description[] = "<none>";
/* unload_one_module() * * inputs - name of module to unload * - true to say modules unloaded, false to not * output - true if successful, false if error * side effects - module is unloaded */boolunload_one_module(const char *name, bool warn){ struct module *mod;
if((mod = findmodule_byname(name)) == NULL) return false;
/* ** XXX - The type system in C does not allow direct conversion between ** data and function pointers, but as it happens, most C compilers will ** safely do this, however it is a theoretical overlow to cast as we ** must do here. I have library functions to take care of this, but ** despite being more "correct" for the C language, this is more ** practical. Removing the abuse of the ability to cast ANY pointer ** to and from an integer value here will break some compilers. ** -jmallett */ /* Left the comment in but the code isn't here any more -larne */ switch (mod->mapi_version) { case 1: { struct mapi_mheader_av1 *mheader = mod->mapi_header; if(mheader->mapi_command_list) { struct Message **m; for (m = mheader->mapi_command_list; *m; ++m) mod_del_cmd(*m); }
/* hook events are never removed, we simply lose the * ability to call them --fl */ if(mheader->mapi_hfn_list) { mapi_hfn_list_av1 *m; for (m = mheader->mapi_hfn_list; m->hapi_name; ++m) remove_hook(m->hapi_name, m->fn); }
if(mheader->mapi_unregister) mheader->mapi_unregister(); break; } case 2: { struct mapi_mheader_av2 *mheader = mod->mapi_header;
/* XXX duplicate code :( */ if(mheader->mapi_command_list) { struct Message **m; for (m = mheader->mapi_command_list; *m; ++m) mod_del_cmd(*m); }
/* hook events are never removed, we simply lose the * ability to call them --fl */ if(mheader->mapi_hfn_list) { mapi_hfn_list_av1 *m; for (m = mheader->mapi_hfn_list; m->hapi_name; ++m) remove_hook(m->hapi_name, m->fn); }
if(mheader->mapi_unregister) mheader->mapi_unregister();
if(mheader->mapi_cap_list) { mapi_cap_list_av2 *m; for (m = mheader->mapi_cap_list; m->cap_name; ++m) { struct CapabilityIndex *idx;
switch (m->cap_index) { case MAPI_CAP_CLIENT: idx = cli_capindex; break; case MAPI_CAP_SERVER: idx = serv_capindex; break; default: sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Unknown/unsupported CAP index found of type %d on capability %s when unloading %s", m->cap_index, m->cap_name, mod->name); ilog(L_MAIN, "Unknown/unsupported CAP index found of type %d on capability %s when unloading %s", m->cap_index, m->cap_name, mod->name); continue; }
capability_orphan(idx, m->cap_name); } } break; } default: sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Unknown/unsupported MAPI version %d when unloading %s!", mod->mapi_version, mod->name); ilog(L_MAIN, "Unknown/unsupported MAPI version %d when unloading %s!", mod->mapi_version, mod->name); break; }
lt_dlclose(mod->address);
rb_dlinkDelete(&mod->node, &module_list); rb_free(mod->name); rb_free(mod->path); rb_free(mod);
if(warn) { ilog(L_MAIN, "Module %s unloaded", name); sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Module %s unloaded", name); }
return true;}
/* * load_a_module() * * inputs - path name of module, bool to notice, int of origin, bool if core * output - false if error true if success * side effects - loads a module if successful */boolload_a_module(const char *path, bool warn, int origin, bool core){ const char *modpath = path; struct module *mod; lt_dlhandle tmpptr; char *mod_displayname, *c; const char *ver, *description = NULL;
int *mapi_version;
if (server_state_foreground) { // Trim off the module installation prefix if the path matches it if (strncmp(path, ircd_paths[IRCD_PATH_MODULES], strlen(ircd_paths[IRCD_PATH_MODULES])) == 0) { modpath += strlen(ircd_paths[IRCD_PATH_MODULES]);
if (*modpath == '/') modpath++; else modpath = path; }
inotice("loading module %s ...", modpath); }
mod_displayname = rb_basename(path);
/* Trim off the ending for the display name if we have to */ if((c = rb_strcasestr(mod_displayname, LT_MODULE_EXT)) != NULL) *c = '\0';
/* Quietly succeed if the module is already loaded */ if(findmodule_byname(mod_displayname) != NULL) return true;
tmpptr = lt_dlopenext(path);
if(tmpptr == NULL) { const char *err = lt_dlerror();
sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Error loading module %s: %s", mod_displayname, err); ilog(L_MAIN, "Error loading module %s: %s", mod_displayname, err); rb_free(mod_displayname); return false; }
/* * _mheader is actually a struct mapi_mheader_*, but mapi_version * is always the first member of this structure, so we treate it * as a single int in order to determine the API version. * -larne. */ mapi_version = (int *) (uintptr_t) lt_dlsym(tmpptr, "_mheader"); if((mapi_version == NULL && (mapi_version = (int *) (uintptr_t) lt_dlsym(tmpptr, "__mheader")) == NULL) || MAPI_MAGIC(*mapi_version) != MAPI_MAGIC_HDR) { sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Data format error: module %s has no MAPI header.", mod_displayname); ilog(L_MAIN, "Data format error: module %s has no MAPI header.", mod_displayname); (void) lt_dlclose(tmpptr); rb_free(mod_displayname); return false; }
/* flag to determine if a rehash is needed after this module load */ conf_changed = false;
switch (MAPI_VERSION(*mapi_version)) { case 1: { struct mapi_mheader_av1 *mheader = (struct mapi_mheader_av1 *)(void *)mapi_version; /* see above */ if(mheader->mapi_register && (mheader->mapi_register() == -1)) { ilog(L_MAIN, "Module %s indicated failure during load.", mod_displayname); sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Module %s indicated failure during load.", mod_displayname); lt_dlclose(tmpptr); rb_free(mod_displayname); return false; } if(mheader->mapi_command_list) { struct Message **m; for (m = mheader->mapi_command_list; *m; ++m) mod_add_cmd(*m); }
if(mheader->mapi_hook_list) { mapi_hlist_av1 *m; for (m = mheader->mapi_hook_list; m->hapi_name; ++m) *m->hapi_id = register_hook(m->hapi_name); }
if(mheader->mapi_hfn_list) { mapi_hfn_list_av1 *m; for (m = mheader->mapi_hfn_list; m->hapi_name; ++m) add_hook(m->hapi_name, m->fn); }
ver = mheader->mapi_module_version; break; } case 2: { struct mapi_mheader_av2 *mheader = (struct mapi_mheader_av2 *)(void *)mapi_version; /* see above */
if(mheader->mapi_cap_list) { mapi_cap_list_av2 *m; for (m = mheader->mapi_cap_list; m->cap_name; ++m) { struct CapabilityIndex *idx; int result;
switch (m->cap_index) { case MAPI_CAP_CLIENT: idx = cli_capindex; break; case MAPI_CAP_SERVER: idx = serv_capindex; break; default: sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Unknown/unsupported CAP index found of type %d on capability %s when loading %s", m->cap_index, m->cap_name, mod_displayname); ilog(L_MAIN, "Unknown/unsupported CAP index found of type %d on capability %s when loading %s", m->cap_index, m->cap_name, mod_displayname); continue; }
result = capability_put(idx, m->cap_name, m->cap_ownerdata); if (m->cap_id != NULL) *(m->cap_id) = result; } }
/* XXX duplicated code :( */ if(mheader->mapi_register && (mheader->mapi_register() == -1)) { ilog(L_MAIN, "Module %s indicated failure during load.", mod_displayname); sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Module %s indicated failure during load.", mod_displayname); if(mheader->mapi_cap_list) { mapi_cap_list_av2 *m; for (m = mheader->mapi_cap_list; m->cap_name; ++m) { struct CapabilityIndex *idx; switch (m->cap_index) { case MAPI_CAP_CLIENT: idx = cli_capindex; break; case MAPI_CAP_SERVER: idx = serv_capindex; break; default: continue; } capability_orphan(idx, m->cap_name); } } lt_dlclose(tmpptr); rb_free(mod_displayname); return false; }
/* Basic date code checks * * Don't make them fatal, but do complain about differences within a certain time frame. * Later on if there are major API changes we can add fatal checks. * -- Elizafox */ if(mheader->mapi_datecode != datecode && mheader->mapi_datecode > 0) { long int delta = datecode - mheader->mapi_datecode; if (delta > MOD_WARN_DELTA) { delta /= 86400; iwarn("Module %s build date is out of sync with ircd build date by %ld days, expect problems", mod_displayname, delta); sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Module %s build date is out of sync with ircd build date by %ld days, expect problems", mod_displayname, delta); } }
if(mheader->mapi_command_list) { struct Message **m; for (m = mheader->mapi_command_list; *m; ++m) mod_add_cmd(*m); }
if(mheader->mapi_hook_list) { mapi_hlist_av1 *m; for (m = mheader->mapi_hook_list; m->hapi_name; ++m) *m->hapi_id = register_hook(m->hapi_name); }
if(mheader->mapi_hfn_list) { mapi_hfn_list_av1 *m; for (m = mheader->mapi_hfn_list; m->hapi_name; ++m) { int priority = m->priority; if (priority == 0) priority = HOOK_NORMAL; add_hook_prio(m->hapi_name, m->fn, priority); } }
/* New in MAPI v2 - version replacement */ ver = mheader->mapi_module_version ? mheader->mapi_module_version : ircd_version; description = mheader->mapi_module_description; }
break; default: ilog(L_MAIN, "Module %s has unknown/unsupported MAPI version %d.", mod_displayname, MAPI_VERSION(*mapi_version)); sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Module %s has unknown/unsupported MAPI version %d.", mod_displayname, *mapi_version); lt_dlclose(tmpptr); rb_free(mod_displayname); return false; }
if(ver == NULL) ver = unknown_ver;
if(description == NULL) description = unknown_description;
mod = rb_malloc(sizeof(struct module)); mod->address = tmpptr; mod->version = ver; mod->description = description; mod->core = core; mod->name = rb_strdup(mod_displayname); mod->mapi_header = mapi_version; mod->mapi_version = MAPI_VERSION(*mapi_version); mod->origin = origin; mod->path = rb_strdup(path); rb_dlinkAddTail(mod, &mod->node, &module_list);
/* queue a rehash if the conf changed */ if (conf_changed) rehash(false);
if(warn) { const char *o;
switch (origin) { case MAPI_ORIGIN_EXTENSION: o = "extension"; break; case MAPI_ORIGIN_CORE: o = "core"; break; default: o = "unknown"; break; }
sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Module %s [version: %s; MAPI version: %d; origin: %s; description: \"%s\"] loaded at %p", mod_displayname, ver, MAPI_VERSION(*mapi_version), o, description, (void *) tmpptr); ilog(L_MAIN, "Module %s [version: %s; MAPI version: %d; origin: %s; description: \"%s\"] loaded at %p", mod_displayname, ver, MAPI_VERSION(*mapi_version), o, description, (void *) tmpptr); } rb_free(mod_displayname); return true;}
voidmodules_do_reload(void *info_){ struct modreload *info = info_; struct module *mod; int check_core; int origin; char *m_bn = rb_basename(info->module); char *path; struct Client *source_p = find_id(info->id);
if((mod = findmodule_byname(m_bn)) == NULL) { if (source_p) sendto_one_notice(source_p, ":Module %s is not loaded", m_bn); rb_free(info); rb_free(m_bn); return; }
origin = mod->origin; check_core = mod->core; path = rb_strdup(mod->path);
mod_remember_clicaps();
if(unload_one_module(m_bn, true) == false) { if (source_p) sendto_one_notice(source_p, ":Module %s is not loaded", m_bn); rb_free(info); rb_free(m_bn); rb_free(path); return; }
if((load_a_module(path, true, origin, check_core) == false) && check_core) { sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Error reloading core module: %s: terminating ircd", m_bn); ilog(L_MAIN, "Error loading core module %s: terminating ircd", m_bn); exit(0); }
mod_notify_clicaps();
rb_free(info); rb_free(m_bn); rb_free(path);}
voidmodules_do_restart(void *unused){ unsigned int modnum = 0; rb_dlink_node *ptr, *nptr;
mod_remember_clicaps();
RB_DLINK_FOREACH_SAFE(ptr, nptr, module_list.head) { struct module *mod = ptr->data; if(!unload_one_module(mod->name, false)) { ilog(L_MAIN, "Module Restart: %s was not unloaded %s", mod->name, mod->core? "(core module)" : "");
if(!mod->core) sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Module Restart: %s failed to unload", mod->name); continue; }
modnum++; }
load_all_modules(false); rehash(false);
mod_notify_clicaps();
sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Module Restart: %u modules unloaded, %lu modules loaded", modnum, rb_dlink_list_length(&module_list)); ilog(L_MAIN, "Module Restart: %u modules unloaded, %lu modules loaded", modnum, rb_dlink_list_length(&module_list));}