-/* CobraMUSH Module System based upon cross-platform libldl */
+/* CobraMUSH Module System based upon cross-platform libldl
+ */
#ifndef _MODULES_H_
#define _MODULES_H_
+#include <stdarg.h>
-struct module_entry_t {
- char *name; /* Name of module */
- char *info; /* Short Description */
- float version; /* Version of Module */
- int (*load)(struct module_entry_t *); /* Load Function */
- int (*unload)(struct module_entry_t *); /* Unload function */
+/* Externs */
+extern HASHTAB chook_htab;
+
+/* This is the linked list entry for each hash event */
+struct core_entry_t
+{
+ char flags; /* Individual Hook Flags */
+ char priority; /* Priority 0-10 */
+ void *data; /* Handler of type of event */
+ struct core_entry_t *next;
+ struct core_entry_t *prev;
+ struct core_hook_t *up;
+};
+
+/* Priority levels: */
+#define MODHOOK_PRI_LOW 0 /* Lowest Priority */
+#define MODHOOK_PRI_SC 2 /* Default SoftCode Priority */
+#define MODHOOK_PRI_HIGH 10 /* Highest Priority */
+
+/* Flags for how the hook is executed */
+#define MODHOOK_SOFTCODE 0x01 /* Use the Softcode hook interface */
+#define MODHOOK_MODULE 0x02 /* Use the Module hook interface */
+#define MODHOOK_LUA 0x04 /* Use the lua interface to execute */
+
+struct core_hook_t
+{
+ const char *key; /* Hook Name for all of this */
+ char flags; /* Hook flags */
+ struct core_entry_t *hooks; /* Points to first hook in list.. highest priority */
+};
+/* Flags for overall hook information */
+#define MODHOOK_NONE 0x0 /* No special values */
+#define MODHOOK_INTRET 0x01 /* The call hook will return an int value */
+#define MODHOOK_CHARRET 0x02 /* the call hook will return a char string */
+#define MODHOOK_AROUND 0x04 /* The call hook is an around method.. Take the highest priority & stop */
+#define MODHOOK_AFTER 0x08
+#define MODHOOK_BEFORE 0x10
+
+/* And now our table to load all our chooks at first */
+struct chook_table_t {
+ const char *key; /* This is the internal key we'll use to reference it */
+ const char *mush_key; /* If we're a SC Hook, this is the SC Hook name. This will primarily exist in a small htab that just points to the regular key. */
+ char flags;
+};
+
+
+/* Entry for softcode core hook */
+struct softcode_hook
+{
+ dbref obj; /* Object to Execute */
+ char attribute[ATTRIBUTE_NAME_LIMIT]; /* Attribute Name to Execute */
+};
+
+/* Entry for Module core hook */
+struct module_hook {
+ void *func; /* This will serve as storage for our function ptr */
+ struct module_entry_t *module; /* Incase the hook needs to reference back to the module */
+};
+
+
+/* Our Table of loaded modules */
+struct module_entry_t
+{
+ char *name; /* Name of module */
+ char *info; /* Short Description */
+ float version; /* Version of Module */
+ int (*load) (struct module_entry_t *); /* Load Function */
+ int (*unload) (struct module_entry_t *); /* Unload function */
void *handle;
struct module_entry_t *next;
};
-struct module_entry_t *module_entry_add(char *name);
-int module_entry_del(struct module_entry_t *entry);
-int modules_init(void);
-int module_open(char *path, char *name);
-int module_close(struct module_entry_t *m);
-int modules_shutdown(void);
+/* Module Functions */
+struct module_entry_t *module_entry_add (char *name);
+int module_entry_del (struct module_entry_t *entry);
+int modules_init (void);
+int module_open (char *path, char *name);
+int module_close (struct module_entry_t *m);
+int modules_shutdown (void);
+/* Core Hook Functions */
+void CHooks_Init (); /* Initialize the Core Hook interface */
+struct core_hook_t *CHook_Create (const char *key, int flags); /* Create a CHook */
+struct core_entry_t *CHook_Insert (const char *key, char priority, int flags, void *data); /* inserts an individual piece of data in hook table */
+int CHook_Delete(struct core_entry_t *entry);
+int CHook_Run (struct core_entry_t *entry, char *format, ...); /* Given a chook entry executes it however it suppose to be executed.. */
+int CHook_SC(struct core_entry_t *entry, char *format, va_list args); /* Execution function to run SC associated with a hook */
+int CHook_Insert_Module(char *key, char priority, struct module_hook *hook); /* Insert a module hook */
/* Iterate through Module List */
#define MODULE_ITER(m) for(m = module_list ; m != NULL ; m = m->next)
#define MODULE_FUNCPTR(var, m, func) *((void **) (&var)) = lt_dlsym(m, func)
+
#endif /* _MODULES_H_ */
#include <string.h>
#include <ltdl.h>
#include <ctype.h>
+#include <signal.h>
#include "config.h"
#include "conf.h"
#include "switches.h"
#include "mushtype.h"
#include "log.h"
+#include "htab.h"
+#include "attrib.h"
+#include "parse.h"
+#include "match.h"
#include "modules.h"
struct module_entry_t *module_list = NULL;
+HASHTAB chook_htab;
+HASHTAB chook_mevents; /* MUSH Events aliased to Hooks */
+extern sig_atomic_t cpu_time_limit_hit; /**< Have we used too much CPU? */
+
+
+struct chook_table_t chook_tab[] = {
+ /* Key, MUSH Event Name(or NULL if not), flags */
+ {"DUMP_5MIN.AROUND", "DUMP_5MIN", MODHOOK_AROUND}, /* Overrides the default 5min message */
+ {"DUMP_1MIN.AROUND", "DUMP_1MIN", MODHOOK_AROUND}, /* Overrides teh default 1min message */
+ {"PCREATE.AROUND", NULL, MODHOOK_AROUND},
+ {"PCREATE.BEFORE", NULL, MODHOOK_BEFORE},
+ {"PCREATE.AFTER", "PCREATE", MODHOOK_AFTER},
+ {"LOG_ALL.AFTER", "LOG_ALL", MODHOOK_AFTER},
+ {"OBJECT_CREATE.AROUND", NULL, MODHOOK_AROUND | MODHOOK_INTRET},
+ {"OBJECT_CREATE.BEFORE", NULL, MODHOOK_BEFORE | MODHOOK_INTRET },
+ {"OBJECT_CREATE.AFTER", "OBJECT_CREATE", MODHOOK_AFTER | MODHOOK_NONE},
+ {"OBJECT_DESTROY.AFTER", "OBJECT_DESTROY", MODHOOK_AFTER | MODHOOK_NONE},
+ {"OBJECT_MOVE.BEFORE", NULL, MODHOOK_BEFORE | MODHOOK_INTRET},
+ {"OBJECT_MOVE.AROUND", NULL, MODHOOK_AROUND | MODHOOK_INTRET},
+ {"OBJECT_MOVE.AFTER", "OBJECT_MOVE", MODHOOK_AFTER},
+ {"PLAYER_CONNECT.AFTER", "PLAYER_CONNECT", MODHOOK_NONE},
+ {"PLAYER_DISCONNECT.AROUND", NULL, MODHOOK_AROUND},
+ {"PLAYER_DISCONNECT.BEFORE", NULL, MODHOOK_BEFORE | MODHOOK_INTRET},
+ {"PLAYER_DISCONNECT.AFTER", "PLAYER_DISCONNECT", MODHOOK_AFTER},
+ {"PLAYER_IDLEOUT", "PLAYER_IDLEOUT", MODHOOK_AFTER}, /* Run after a player idles out */
+ {"SOCKET_CONNECT.AFTER", "SOCKET_CONNECT", MODHOOK_AFTER},
+ {"SOCKET_DISCONNECT.AFTER", "SOCKET_DISCONNECT", MODHOOK_AFTER},
+ {"SOCKET_LOGINFAIL.AFTER", "SOCKET_LOGINFAIL", MODHOOK_AFTER},
+ {"SOCKET_CREATEFAIL.AFTER", "SOCKET_CREATEFAIL", MODHOOK_AFTER},
+ {"SQL_CONNECT.AFTER", "SQL_CONNECT", MODHOOK_AFTER},
+ {NULL, NULL, MODHOOK_NONE}
+};
+
+void CHooks_Init() {
+ int i, sz;
+
+ /* Figure out size */
+ for(i = 0 ;chook_tab[i].key != NULL; i++)
+ ;
+ hashinit(&chook_htab, i);
+ /* Initialize Chook Hashtab */
+
+ for(i = 0; chook_tab[i].key != NULL; i++)
+ CHook_Create(chook_tab[i].key, chook_tab[i].flags);
+
+ /* Setup the MUSH Event aliases to our actual Hooks */
+ for(sz = i = 0 ;chook_tab[i].key != NULL; i++)
+ if(chook_tab[i].mush_key != NULL)
+ sz++;
+ hashinit(&chook_mevents, sz);
+ for(sz = i = 0 ;chook_tab[i].key != NULL; i++)
+ if(chook_tab[i].mush_key != NULL)
+ hashadd( chook_tab[i].mush_key, (void *)chook_tab[i].key, &chook_mevents);
+}
+
+struct core_hook_t *CHook_Create(const char *key, int flags) {
+ struct core_hook_t *chook;
+
+ /* check to see if we need to create it */
+ if((chook = (struct core_hook_t *) hashfind(key, &chook_htab))) {
+ /* It already exists.. just return this one */
+ return chook;
+ }
+
+ /* First Create our struct */
+ chook = mush_malloc(sizeof(struct core_hook_t), "CORE_HOOK.PARENT");
+
+ if(!chook)
+ mush_panic("Out of memory!");
+
+ /* Initialize data */
+ chook->key = mush_strdup(strupper(key), "CORE_HOOK.KEY");
+ chook->flags = flags;
+ chook->hooks = NULL; /* Initialized as having no children */
+ /* Add to the hashtab */
+ hashadd(key, chook, &chook_htab);
+
+ return chook;
+}
+
+struct core_entry_t *CHook_Insert (const char *key, char priority, int flags, void *data) {
+ struct core_hook_t *parent;
+ struct core_entry_t *new_child, *cur_child;
+
+ /* Ok first look for the htab entry */
+ parent = hashfind(key, &chook_htab);
+
+ if(!parent) /* It doesn't exist, return NULL */
+ return NULL;
+
+ /* It does exist lets allocate some information for this entry */
+ new_child = mush_malloc(sizeof(struct core_entry_t), "CORE_HOOK.CHILD");
+
+ if(!new_child)
+ mush_panic("Out of memory!");
+ /* Now go ahead, give it priority, flags & data */
+ new_child->priority = priority;
+ new_child->flags = flags;
+ new_child->data = data;
+ new_child->up = parent;
+ new_child->next = NULL;
+ new_child->prev = NULL;
+
+ /* Find a place before it within the parent */
+ if(parent->hooks == NULL) { /* simple. set it and return */
+ parent->hooks = new_child;
+ return new_child;
+ }
+
+ for(cur_child = parent->hooks ; cur_child != NULL; cur_child = cur_child->next)
+ if(cur_child->priority < priority)
+ break;
+ else if(cur_child->next == NULL) {
+ /* We're going at the end of the list */
+ new_child->prev = cur_child;
+ cur_child->next = new_child;
+ return new_child;
+ }
+
+ /* Place us in prev */
+ cur_child->prev->next = new_child;
+ new_child->next = cur_child;
+ new_child->prev = cur_child->prev;
+ cur_child->prev = new_child;
+ return new_child;
+ }
+
+/* Create SC event associated with 'key' */
+int CHook_Insert_SC(char *key, dbref object, char *attribute) {
+ struct core_entry_t *entry;
+ struct softcode_hook *softcode_block;
+
+ if(!GoodObject(object))
+ return 0;
+ softcode_block = (struct softcode_hook *) mush_malloc(sizeof(struct softcode_hook), "CHOOK.SOFTCODE_BLOCK");
+ softcode_block->obj = object;
+ memset(softcode_block->attribute, '\0', ATTRIBUTE_NAME_LIMIT);
+ strncpy(softcode_block->attribute, strupper(attribute), ATTRIBUTE_NAME_LIMIT-1);
+
+ entry = CHook_Insert(key, MODHOOK_PRI_SC, MODHOOK_SOFTCODE, (void *) softcode_block);
+ if(!entry) {
+ /* Free the softcode block we created */
+ mush_free(softcode_block, "CHOOK.SOFTCODE_BLOCK");
+ return 0;
+ }
+ /* We inserted it succesfully */
+ return 1;
+}
+
+/* Insert a hook for a module for the key */
+int CHook_Insert_Module(char *key __attribute__ ((__unused__)), char priority __attribute__ ((__unused__)), struct module_hook *hook __attribute__ ((__unused__))) {
+ return 0;
+}
+
+/* arg_left -> key
+ * arg_right -> obj/attribute
+ */
+COMMAND(cmd_event) {
+ dbref thing;
+ char *key;
+ char *s;
+
+ s = strchr(arg_right, '/');
+ *s++ = '\0';
+
+ if(!s) {
+ notify(player, "Bad Event format");
+ return;
+ }
+ thing = match_thing(player, arg_right);
+ if(!GoodObject(thing))
+ return;
+
+ /* Look for event */
+ if(!(key = (char *) hashfind(arg_left, &chook_mevents))) {
+ notify(player, "No such event.");
+ return;
+ }
+
+ if(!CHook_Insert_SC(key, thing, s)) {
+ /* This shouldn't happen.. */
+ notify(player, "ERROR: Could not add event.");
+ return;
+ }
+ notify(player, "Event added.");
+
+}
+
+/* Give a hook parent, delete the child from its linked list */
+int CHook_Delete(struct core_entry_t *entry) {
+
+ if(!entry)
+ return 0;
+ /* Detach from list */
+ if(entry->next == NULL && entry->up->hooks == entry) {
+ /* Quick one */
+ entry->up->hooks = NULL;
+ goto free_chook_child;
+ } else if(entry->prev)
+ entry->prev->next = entry->next;
+ else if(entry->up) /* We're rooted from the parent.. deal with that situation */
+ entry->up->hooks = entry->next;
+ if(entry->next)
+ entry->next->prev = entry->prev;
+free_chook_child:
+ /* Free entry */
+ mush_free(entry, "CORE_HOOK.CHILD");
+
+ return 1;
+}
+
+int CHook_Run(struct core_entry_t *entry, char *format, ...) {
+ va_list argp;
+ int rval;
+ int (*module_func)(struct core_entry_t *entry, va_list argp);
+
+ module_func = NULL; /* Just for initialization purposes */
+ va_start(argp, format);
+ if(entry->flags & MODHOOK_SOFTCODE) {
+ rval = CHook_SC(entry, format, argp);
+ va_end(argp);
+ } else if(entry->flags & MODHOOK_MODULE) {
+ /* We'll do this code later */
+ va_end(argp);
+ return -1;
+ } else {
+ va_end(argp);
+ return -1;
+ }
+
+ /* We get ehre something is wrong */
+ return -2;
+
+}
+
+int CHook_SC(struct core_entry_t *entry, char *format, va_list arguments) {
+ /* Register Saving */
+ char *preserve_wnxt[10], *preserve_rnxt[NUMQ];
+ char *val_wnxt[10], *val_rnxt[NUMQ];
+ char *preserves[10], *preserveq[NUMQ];
+ HASHTAB preserve_namedregs;
+ /* */
+ struct softcode_hook *sc_hook;
+ const char *s;
+ char cmd_buf[BUFFER_LEN], tbuf[BUFFER_LEN];
+ char *fptr, *r;
+ char next = 0;
+ ATTR *atr;
+ dbref enactor, cause,executor;
+ dbref local_ooref;
+ int break_count, spot = 0;
+
+ sc_hook = (struct softcode_hook *) entry->data;
+
+ /* Before we bother with va_list .. Make sure A) Its a good object, B) Its a good attribute */
+ if(!GoodObject(sc_hook->obj))
+ return -1;
+ atr = atr_get(sc_hook->obj, sc_hook->attribute);
+ if(!atr)
+ return -1;
+ /* Save Environment */
+ save_global_nxt("chook_sc_save", preserve_wnxt, preserve_rnxt, val_wnxt, val_rnxt);
+ save_global_regs("chook_sc_save", preserveq);
+ save_global_env("chook_sc_save", preserves);
+ init_namedregs(&preserve_namedregs);
+ copy_namedregs(&preserve_namedregs, &global_eval_context.namedregs);
+ clear_namedregs(&global_eval_context.namedregs);
+ enactor = executor = cause = sc_hook->obj;
+
+ /* Ok.. Now that we haev all the info we need we need to build a parse_que with 0-10 being the args passed */
+ for(fptr = format; fptr && *fptr ; fptr++) {
+ /* Look for the '%' symbol.. Thats are argument starting processor */
+ if(*fptr != '%' && !next)
+ continue;
+ if(!next) { /* we caught % */
+ next = 1;
+ continue;
+ }
+ switch(*fptr) {
+ case '@': /* The cause */
+ cause = (dbref) va_arg(arguments, dbref);
+ next = 0;
+ break;
+ case '#': /* This should set the enactor */
+ enactor = (dbref) va_arg(arguments, dbref);
+ next = 0;
+ break;
+ case 'D':
+ case 'd':
+ global_eval_context.wenv[spot++] = unparse_integer(va_arg(arguments, intmax_t));
+ next = 0;
+ break;
+ case 'S':
+ case 's':
+ global_eval_context.wenv[spot++] = va_arg(arguments, char *);
+ next = 0;
+ break;
+ case ' ': /* this shouldn't happen.. but just in case */
+ break;
+ default:
+ /* Bad Format.. don't run & return an error */
+ do_rawlog(LT_ERR, "%s:%d -> Unexpected format character '%c' in format \"%s\"", __FILE__, __LINE__, *fptr, format);
+ /* Restore Environment */
+ copy_namedregs(&global_eval_context.namedregs, &preserve_namedregs);
+ free_namedregs(&preserve_namedregs);
+ restore_global_regs("chook_sc_save", preserveq);
+ restore_global_env("chook_sc_save", preserves);
+ restore_global_nxt("chook_sc_save", preserve_wnxt, preserve_rnxt,
+ val_wnxt, val_rnxt);
+
+ return -2;
+ }
+ }
+ strncpy(cmd_buf, atr_value(atr), BUFFER_LEN-1);
+ /* FIXME: Make immediate parse execution.. And alter the cause/enactor appropriately.. parse_que doesn't give us that flexibility */
+ s = cmd_buf;
+ global_eval_context.process_command_port = 0;
+ *(global_eval_context.break_replace) = '\0';
+ global_eval_context.break_called = 0;
+ break_count = 100;
+ start_cpu_timer();
+ while (!cpu_time_limit_hit && *s) {
+ r = cmd_buf;
+ local_ooref = ooref;
+ ooref = atr->creator;
+ process_expression(cmd_buf, &r, &s,
+ executor, enactor, cause, PE_NOTHING, PT_SEMI,
+ NULL);
+ *r = '\0';
+ if (*s == ';')
+ s++;
+ strcpy(tbuf, cmd_buf);
+ process_command(executor, tbuf, enactor,
+ cause, 0);
+ if (global_eval_context.break_called) {
+ global_eval_context.break_called = 0;
+ s = global_eval_context.break_replace;
+ if (!*global_eval_context.break_replace) {
+ ooref = local_ooref;
+ break;
+ }
+ break_count--;
+ if (!break_count) {
+ notify(global_eval_context.cplr,
+ T("@break recursion exceeded."));
+ ooref = local_ooref;
+ break;
+ }
+ }
+ ooref = local_ooref;
+ }
+ reset_cpu_timer();
+ /* Restore Environment */
+ copy_namedregs(&global_eval_context.namedregs, &preserve_namedregs);
+ free_namedregs(&preserve_namedregs);
+ restore_global_regs("chook_sc_save", preserveq);
+ restore_global_env("chook_sc_save", preserves);
+ restore_global_nxt("chook_sc_save", preserve_wnxt, preserve_rnxt,
+ val_wnxt, val_rnxt);
+ return 1;
+
+}
+
+
+/* FIXME: The rest of the module code has to be adjusted to fit into the new CoreHook system */
+
/* Ads a module entry and returns */
struct module_entry_t *module_entry_add(char *name) {