Logo Search packages:      
Sourcecode: rageircd version File versions  Download package

modules.c

/*
 * RageIRCd: an advanced Internet Relay Chat daemon (ircd).
 * (C) 2000-2005 the RageIRCd Development Team, all rights reserved.
 *
 * This software is free, licensed under the General Public License.
 * Please refer to doc/LICENSE and doc/README for further details.
 *
 * $Id: modules.c,v 1.106.2.4 2005/02/21 01:44:57 amcwilliam Exp $
 */

#include "struct.h"
#include "setup.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "h.h"
#include "msg.h"
#include "channel.h"
#include "send.h"
#include "memory.h"
#include "patchlevel.h"
#include "hook.h"
#include "dlink.h"
#include "modules.h"
#include "xmode.h"
#include "conf2.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>

static dlink_list module_list = DLINK_LIST_INIT;

#ifdef STATIC_MODULES

extern void static_modules_load(dlink_list *);

void init_modules()
{
      DupString(commands_header.filename, "commands.so");
      commands_load();
      dlink_add(&module_list, &commands_header);

      static_modules_load(&module_list);
}

#else

void init_modules()
{
      add_command(&CMD_MODULE, m_module);
}

static dlink_list delayed_list = DLINK_LIST_INIT;

#include <dlfcn.h>
#include <dirent.h>

static const char *mod_err_tab[] = {
      "object not loaded",
      "object already loaded",
      "missing module header",
      "invalid module header",
      "bad module version",
      "missing module load",
      "missing module unload",
      "cannot unload permanent modules",
      "module load failure",
      "module unload failure",
      NULL
};

static int load_one_module(char *);
static int unload_one_module(char *, int);
static int reload_one_module(char *);

#ifndef RTLD_NOW
#define RTLD_NOW RTLD_LAZY
#endif

int do_module(char *call, char *name, aClient *sptr)
{
      int retval = 0;
      const char *mod_err;

      ASSERT(!BadPtr(call));
      ASSERT(!BadPtr(name));

      if (!mycmp(call, "LOAD")) {
            retval = load_one_module(name);
            call = "load";
      }
      else if (!mycmp(call, "UNLOAD")) {
            retval = unload_one_module(name, 0);
            call = "unload";
      }
      else if (!mycmp(call, "RELOAD")) {
            retval = reload_one_module(name);
            call = "reload";
      }
      else {
            return MOD_UNKNOWN;
      }

      if (retval == MOD_SUCCESS) {
            if (sptr != NULL) {
                  sendto_realops("%s has successfully %sed %s", sptr->name, call, name);
            }
            return MOD_SUCCESS;
      }

      mod_err = dlerror();
      if (BadPtr(mod_err)) {
            /* Use our own error table if dlerror() is NULL */
            mod_err = mod_err_tab[retval];
      }

      ircdlog(LOG_ERROR, "Couldn't %s %s: %s", call, name, mod_err);
      if (sptr != NULL) {
            send_me_notice(sptr, ":Couldn't %s %s: %s", call, name, mod_err);
      }

      return MOD_FAILURE;
}

static dlink_node *find_module_node_by_filename(char *name)
{
      dlink_node *node;
      Module *header;

      DLINK_FOREACH_DATA(module_list.head, node, header, Module) {
            if (!irccmp(header->filename, name)) {
                  return node;
            }
      }

      return NULL;
}

static void *get_symbol(char *symbol, void *source_ptr)
{
      char _symbol[512];
      void *symbol_ptr = NULL;
      const char *errstr = NULL;
      dlink_node *node;
      Module *header;

      ircsprintf(_symbol, "_%s", symbol);

      if (source_ptr != NULL) {
            symbol_ptr = dlsym(source_ptr, symbol);
            if ((errstr = dlerror()) == NULL) {
                  return symbol_ptr;
            }

            symbol_ptr = dlsym(source_ptr, _symbol);
            if ((errstr = dlerror()) == NULL) {
                  return symbol_ptr;
            }

            return NULL;
      }

      DLINK_FOREACH_DATA(module_list.head, node, header, Module) {
            symbol_ptr = dlsym(header->ptr, symbol);
            if ((errstr = dlerror()) == NULL) {
                  return symbol_ptr;
            }

            symbol_ptr = dlsym(header->ptr, _symbol);
            if ((errstr = dlerror()) == NULL) {
                  return symbol_ptr;
            }
      }

      return NULL;
}

void *get_symbol_from_source(char *name, char *source)
{
      void *symbol;

      if ((symbol = get_symbol(name, NULL)) != NULL) {
            return symbol;
      }

      if (do_module("load", source, NULL) == MOD_SUCCESS) {
            if ((symbol = get_symbol(name, NULL)) != NULL) {
                  return symbol;
            }
      }

      return NULL;
}

static void load_delayed_modules()
{
      dlink_node *node, *next = NULL;
      char *delayed;

      DLINK_FOREACH_SAFE_DATA(delayed_list.head, node, next, delayed, char) {
            do_module("load", delayed, NULL);
            dlink_del(&delayed_list, NULL, node);
            MyFree(delayed);
      }
}

static void add_delayed(char *name)
{
      char *delayed;

      DupString(delayed, name);
      dlink_add_tail(&delayed_list, delayed);

      if (dlink_length(&delayed_list) == 1) {
            add_event("load_delayed_modules", load_delayed_modules, NULL, 1, 0); /* load on the next loop */
      }
}

static int load_one_module(char *name)
{
      /* char *path; -- supposedly unused, 20050209 */
      void *ptr = NULL;
      int (*loadfunc)(), (*unloadfunc)();
      Module *header;
      
      Debug((DEBUG_DEBUG, "load_one_module() name %s", name));

      if (find_module_node_by_filename(name) != NULL) {
            return MOD_ERR_ALREADY_LOADED;
      }

      if ((ptr = dlopen(name, RTLD_NOW)) == NULL) {
            return MOD_FAILURE;
      }

      if ((header = get_symbol("module_header", ptr)) == NULL) {
            dlclose(ptr);
            return MOD_ERR_HEADER_MISSING;
      }

      if (BadPtr(header->name)) {
            dlclose(ptr);
            return MOD_ERR_HEADER_INVALID;
      }

      if (header->version != MODULE_VERSION) {
            dlclose(ptr);
            return MOD_ERR_BAD_VERSION;
      }

      if ((loadfunc = get_symbol("module_load", ptr)) == NULL) {
            dlclose(ptr);
            return MOD_ERR_LOAD_MISSING;
      }

      if ((unloadfunc = get_symbol("module_unload", ptr)) == NULL) {
            dlclose(ptr);
            return MOD_ERR_UNLOAD_MISSING;
      }

      if ((*loadfunc)() != MOD_SUCCESS) {
            clear_objects(header);
            dlclose(ptr);
            return MOD_ERR_LOAD_FAILED;
      }
      
      DupString(header->filename, name);
      header->ptr = ptr;
      header->unloadfunc = unloadfunc;

      dlink_add(&module_list, header);
      return MOD_SUCCESS;
}

static inline int do_module_unload(Module *header, dlink_node *node)
{
      ASSERT(header != NULL);
      ASSERT(node != NULL);

      if ((*header->unloadfunc)() == MOD_FAILURE) {
            return MOD_FAILURE;
      }
      
      clear_objects(header);
      MyFree(header->filename);
      dlclose(header->ptr);
      
      dlink_del(&module_list, NULL, node);

      return MOD_SUCCESS;
}

static int unload_one_module(char *name, int reloading)
{
      dlink_node *node;
      Module *header;

      if ((node = find_module_node_by_filename(name)) == NULL) {
            return MOD_ERR_NOT_LOADED;
      }
      header = (Module *)node->data;

      if (!reloading && (header->flags & MOD_FLAG_PERM)) {
            return MOD_ERR_PERM;
      }

      if (do_module_unload(header, node) != MOD_SUCCESS) {
            return MOD_ERR_UNLOAD_FAILED;
      }

      return MOD_SUCCESS;
}

static int reload_one_module(char *name)
{
      int i;

      if ((i = unload_one_module(name, 1)) != MOD_SUCCESS) {
            return i;
      }

      return load_one_module(name);
}

void load_modules_from_path(char *mod_path)
{
      DIR *mod_dir;
      struct dirent *file_ptr;
      char mod_name[PATH_MAX + 1];
      int len;

      if ((mod_dir = opendir(mod_path)) == NULL) {
            char *error_msg = strerror(errno);
            ircdlog(LOG_ERROR, "Couldn't load modules from path %s: %s", mod_path,
                  error_msg);
            report(1, "ERROR: couldn't load modules from path %s: %s", mod_path,
                  error_msg);
            return;
      }

      while ((file_ptr = readdir(mod_dir)) != NULL) {
            if ((len = strlen(file_ptr->d_name)) <= 3) {
                  continue;
            }
            if (!((file_ptr->d_name[len - 3] == '.') && (file_ptr->d_name[len - 2] == 's')
              && (file_ptr->d_name[len - 1] == 'o'))) {
                  continue;
            }

            ircsprintf(mod_name, "%s/%s", mod_path, file_ptr->d_name);
            
            if (do_module("load", mod_name, NULL) == MOD_FAILURE) {
                  add_delayed(mod_name);
            }
      }

      closedir(mod_dir);
}

void load_all_modules()
{
      dlink_node *node;
      char *mod;

      DLINK_FOREACH_PREV_DATA(conf_modulefile_list.tail, node, mod, char) {
            if (do_module("load", mod, NULL) == MOD_FAILURE) {
                  add_delayed(mod);
            }
      }
      DLINK_FOREACH_PREV_DATA(conf_modulepath_list.tail, node, mod, char) {
            load_modules_from_path(mod);
      }
}

void unload_all_modules()
{
      dlink_node *node, *next = NULL;
      Module *header;

      DLINK_FOREACH_SAFE_DATA(module_list.head, node, next, header, Module) {
            if (do_module_unload(header, node) == MOD_FAILURE) {
                  ircdlog(LOG_ERROR, "unload_all_modules() encountered MOD_FAILURE with %s", header->name);
                  sendto_realops("Warning! %s failed to unload during server rehash.", header->name);
            }
      }
}

/*
 * m_module
 *    parv[0] = sender prefix
 *    parv[1] = function
 *    parv[2] = module
 */
int m_module(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
      if (!HasMode(sptr, UMODE_NETADMIN|UMODE_ADMIN)) {
            send_me_numericNA(sptr, ERR_NOPRIVILEGES);
            return 0;
      }

      if (parc < 3 || BadPtr(parv[2])) {
            send_me_numeric(sptr, ERR_NEEDMOREPARAMS, "MODULE");
            return 0;
      }

      if (do_module(parv[1], parv[2], sptr) == MOD_UNKNOWN) {
            send_me_noticeNA(sptr, ":Syntax: /MODULE <load|unload|reload> [module.so]");
      }

      return 0;
}

#endif

void do_module_list(aClient *sptr)
{
      dlink_node *node;
      Module *header;

      send_me_debugNA(sptr, "M :Object                                   Module Version");

      DLINK_FOREACH_DATA(module_list.head, node, header, Module) {
            send_me_debug(sptr, "M :%-40s %s", header->filename,
                  !BadPtr(header->revision) ? header->revision : "(unknown)");
      }
}

Generated by  Doxygen 1.6.0   Back to index