From 68dbf8b00b485f7f2b8e915a115115e3769d069b Mon Sep 17 00:00:00 2001 From: Rick L Bird Date: Tue, 10 May 2011 05:52:09 -0400 Subject: [PATCH] Began Core Hook System. this will be the basis as a replacement for how C modules, lua extensions, and SC events will all be called from. Right now I only have one example Hook, and thats for @log's to the SC. @event LOG_ALL=/ to see how it works. Still need to write actual C module & lua interfaces to this system. Refs #3 --- hdrs/modules.h | 100 +++++++++++-- src/bsd.c | 3 - src/command.c | 1 + src/game.c | 3 + src/log.c | 7 + src/modules.c | 371 +++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 469 insertions(+), 16 deletions(-) diff --git a/hdrs/modules.h b/hdrs/modules.h index dfc2f94..f678af3 100644 --- a/hdrs/modules.h +++ b/hdrs/modules.h @@ -1,24 +1,97 @@ -/* CobraMUSH Module System based upon cross-platform libldl */ +/* CobraMUSH Module System based upon cross-platform libldl + */ #ifndef _MODULES_H_ #define _MODULES_H_ +#include -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) @@ -41,4 +114,5 @@ int modules_shutdown(void); #define MODULE_FUNCPTR(var, m, func) *((void **) (&var)) = lt_dlsym(m, func) + #endif /* _MODULES_H_ */ diff --git a/src/bsd.c b/src/bsd.c index fd3c3e6..ec3f02a 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -625,9 +625,6 @@ main(int argc, char **argv) options.mem_check = 1; - /* Initialize Module interface */ - modules_init(); - /* If we have setlocale, call it to set locale info * from environment variables */ diff --git a/src/command.c b/src/command.c index 0c91fae..0f76307 100644 --- a/src/command.c +++ b/src/command.c @@ -154,6 +154,7 @@ COMLIST commands[] = { CMD_T_NOGAGGED, NULL}, {"@ELOCK", NULL, cmd_elock, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, NULL}, + {"@EVENT", NULL, cmd_event, CMD_T_ANY | CMD_T_EQSPLIT, "POWER^SITE"}, {"@EMIT", "ROOM NOEVAL SILENT SPOOF", cmd_emit, CMD_T_ANY | CMD_T_NOGAGGED, NULL}, {"@EMPOWER", NULL, cmd_empower, CMD_T_ANY | CMD_T_EQSPLIT, "POWER^EMPOWER"}, {"@ENABLE", NULL, cmd_enable, CMD_T_ANY | CMD_T_NOGAGGED, "POWER^SITE"}, diff --git a/src/game.c b/src/game.c index 4ed9440..abf3a29 100644 --- a/src/game.c +++ b/src/game.c @@ -761,6 +761,9 @@ init_game_config(const char *conf) /* initialize random number generator */ initialize_mt(); + CHooks_Init(); /* Initialize Core Hooks */ + modules_init(); /* Initialize Module Interface */ + init_queue(); global_eval_context.process_command_port = 0; diff --git a/src/log.c b/src/log.c index e30c09b..4ec8e8b 100644 --- a/src/log.c +++ b/src/log.c @@ -36,6 +36,7 @@ #include "htab.h" #include "bufferq.h" #include "log.h" +#include "modules.h" static char *quick_unparse(dbref object); static void start_log(FILE ** fp, const char *filename); @@ -191,6 +192,8 @@ end_all_logs(void) void WIN32_CDECL do_rawlog(int logtype, const char *fmt, ...) { + struct core_hook_t *chook; + struct core_entry_t *centry; struct tm *ttm; char timebuf[18]; char tbuf1[BUFFER_LEN + 50]; @@ -239,6 +242,10 @@ do_rawlog(int logtype, const char *fmt, ...) f = stderr; break; } + if((chook = (struct core_hook_t *) hashfind("LOG_ALL.AFTER", &chook_htab))) { + for(centry = chook->hooks; centry != NULL ; centry = centry->next) + CHook_Run(centry, "%# %d %s", global_eval_context.cplr, logtype, tbuf1); + } lock_file(f); fprintf(f, "%s %s\n", timebuf, tbuf1); fflush(f); diff --git a/src/modules.c b/src/modules.c index 9e538df..6a0581f 100644 --- a/src/modules.c +++ b/src/modules.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "config.h" #include "conf.h" @@ -16,9 +17,379 @@ #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) { -- 2.30.2