diff options
author | MeirShpilraien <meir@redislabs.com> | 2018-12-09 14:32:55 +0200 |
---|---|---|
committer | antirez <antirez@gmail.com> | 2018-12-20 17:55:18 +0100 |
commit | ab37289fa6035a774d7438f8a7342d3177fdc1be (patch) | |
tree | 8534131752f2961f9dc2fc90aaefd194555b5e72 | |
parent | 009a9292694491ff9eec78c024d38b0b5ca83f2e (diff) | |
download | redis-ab37289fa6035a774d7438f8a7342d3177fdc1be.tar.gz |
added module ability to register api to be used by other modules
-rw-r--r-- | src/module.c | 115 | ||||
-rw-r--r-- | src/redismodule.h | 4 | ||||
-rw-r--r-- | src/server.h | 1 |
3 files changed, 120 insertions, 0 deletions
diff --git a/src/module.c b/src/module.c index 20d159d33..d177af24a 100644 --- a/src/module.c +++ b/src/module.c @@ -47,7 +47,16 @@ struct RedisModule { int ver; /* Module version. We use just progressive integers. */ int apiver; /* Module API version as requested during initialization.*/ list *types; /* Module data types. */ + list *usedBy; /* list of modules names using this module api. */ + list *using; /* list of modules names that this module is using thier api . */ + list *exportedFunctions; /* list of function names exported by this module. */ }; + +struct ModuleExportedApi { + void* funcPointer; + struct RedisModule* module; +}; + typedef struct RedisModule RedisModule; static dict *modules; /* Hash table of modules. SDS -> RedisModule ptr.*/ @@ -700,6 +709,9 @@ void RM_SetModuleAttribs(RedisModuleCtx *ctx, const char *name, int ver, int api module->ver = ver; module->apiver = apiver; module->types = listCreate(); + module->usedBy = listCreate(); + module->using = listCreate(); + module->exportedFunctions = listCreate(); ctx->module = module; } @@ -4615,6 +4627,59 @@ void RM_GetRandomHexChars(char *dst, size_t len) { getRandomHexChars(dst,len); } +/* Used to register an api to be used by other modules. */ +int RM_RegisterApi(RedisModuleCtx *ctx, const char *funcname, void *funcptr) { + struct ModuleExportedApi* eapi = zmalloc(sizeof(*eapi)); + eapi->module = ctx->module; + eapi->funcPointer = funcptr; + if(dictAdd(server.exportedapi, (char*)funcname, eapi) != DICT_OK){ + zfree(eapi); + return REDISMODULE_ERR; + } + listAddNodeHead(ctx->module->exportedFunctions, (char*)funcname); + return REDISMODULE_OK; +} + +static inline int IsModuleInList(list *l, const char* moduleName){ + listIter *iter = listGetIterator(l, AL_START_HEAD); + listNode *node = NULL; + while((node = listNext(iter))){ + char* name = listNodeValue(node); + if(strcmp(name, moduleName) == 0){ + listReleaseIterator(iter); + return 1; + } + } + listReleaseIterator(iter); + return 0; +} + +static inline void RemoveFromList(list *l, const char* moduleName){ + listIter *iter = listGetIterator(l, AL_START_HEAD); + listNode *node = NULL; + while((node = listNext(iter))){ + char* name = listNodeValue(node); + if(strcmp(name, moduleName) == 0){ + listDelNode(l, node); + return; + } + } + listReleaseIterator(iter); +} + +void* RM_GetExportedApi(RedisModuleCtx *ctx, const char *funcname) { + dictEntry* entry = dictFind(server.exportedapi, funcname); + if(!entry){ + return NULL; + } + struct ModuleExportedApi* eapi = dictGetVal(entry); + if(!IsModuleInList(eapi->module->usedBy, ctx->module->name)){ + listAddNodeHead(eapi->module->usedBy, ctx->module->name); + listAddNodeHead(ctx->module->using, eapi->module->name); + } + return eapi->funcPointer; +} + /* -------------------------------------------------------------------------- * Modules API internals * -------------------------------------------------------------------------- */ @@ -4735,6 +4800,28 @@ void moduleUnregisterCommands(struct RedisModule *module) { dictReleaseIterator(di); } +void moduleUnregisterApi(struct RedisModule *module) { + listIter *iter = listGetIterator(module->exportedFunctions, AL_START_HEAD); + listNode* node = NULL; + while((node = listNext(iter))){ + char* functionName = listNodeValue(node); + struct ModuleExportedApi* eapi = dictFetchValue(server.exportedapi, functionName); + serverAssert(eapi); + zfree(eapi); + dictDelete(server.exportedapi, functionName); + } + listReleaseIterator(iter); + iter = listGetIterator(module->using, AL_START_HEAD); + node = NULL; + while((node = listNext(iter))){ + char* moduleName = listNodeValue(node); + struct RedisModule* usingModule = dictFetchValue(modules, moduleName); + serverAssert(usingModule); + RemoveFromList(usingModule->usedBy, module->name); + } + listReleaseIterator(iter); +} + /* Load a module and initialize it. On success C_OK is returned, otherwise * C_ERR is returned. */ int moduleLoad(const char *path, void **module_argv, int module_argc) { @@ -4794,6 +4881,12 @@ int moduleUnload(sds name) { return REDISMODULE_ERR; } + if (listLength(module->usedBy)) { + errno = EPERM; + return REDISMODULE_ERR; + } + + moduleUnregisterApi(module); moduleUnregisterCommands(module); /* Remove any notification subscribers this module might have */ @@ -4826,6 +4919,7 @@ void moduleCommand(client *c) { if (c->argc == 2 && !strcasecmp(subcmd,"help")) { const char *help[] = { "LIST -- Return a list of loaded modules.", +"LISTAPI <module-name> -- Return a list of exported api.", "LOAD <path> [arg ...] -- Load a module library from <path>.", "UNLOAD <name> -- Unload a module.", NULL @@ -4858,6 +4952,9 @@ NULL case EBUSY: errmsg = "the module exports one or more module-side data types, can't unload"; break; + case EPERM: + errmsg = "the module api is used by other modules, please unload them first and try again."; + break; default: errmsg = "operation not possible."; break; @@ -4879,6 +4976,21 @@ NULL addReplyLongLong(c,module->ver); } dictReleaseIterator(di); + } else if (!strcasecmp(subcmd,"listapi") && c->argc == 3) { + char *moduleName = c->argv[2]->ptr; + struct RedisModule* module = dictFetchValue(modules, moduleName); + if(!module){ + addReplyErrorFormat(c,"Error listing module api: no such module %s", moduleName); + return; + } + addReplyMultiBulkLen(c,listLength(module->exportedFunctions)); + listIter *iter = listGetIterator(module->exportedFunctions, AL_START_HEAD); + listNode* node = NULL; + while((node = listNext(iter))){ + char* functionName = listNodeValue(node); + addReplyBulkCString(c,functionName); + } + listReleaseIterator(iter); } else { addReplySubcommandSyntaxError(c); return; @@ -4894,6 +5006,7 @@ size_t moduleCount(void) { * file so that's easy to seek it to add new entries. */ void moduleRegisterCoreAPI(void) { server.moduleapi = dictCreate(&moduleAPIDictType,NULL); + server.exportedapi = dictCreate(&moduleAPIDictType,NULL); REGISTER_API(Alloc); REGISTER_API(Calloc); REGISTER_API(Realloc); @@ -5044,4 +5157,6 @@ void moduleRegisterCoreAPI(void) { REGISTER_API(DictPrev); REGISTER_API(DictCompareC); REGISTER_API(DictCompare); + REGISTER_API(RegisterApi); + REGISTER_API(GetExportedApi); } diff --git a/src/redismodule.h b/src/redismodule.h index d18c38881..3c76fa02b 100644 --- a/src/redismodule.h +++ b/src/redismodule.h @@ -332,6 +332,8 @@ void REDISMODULE_API_FUNC(RedisModule_GetRandomBytes)(unsigned char *dst, size_t void REDISMODULE_API_FUNC(RedisModule_GetRandomHexChars)(char *dst, size_t len); void REDISMODULE_API_FUNC(RedisModule_SetDisconnectCallback)(RedisModuleBlockedClient *bc, RedisModuleDisconnectFunc callback); void REDISMODULE_API_FUNC(RedisModule_SetClusterFlags)(RedisModuleCtx *ctx, uint64_t flags); +int REDISMODULE_API_FUNC(RedisModule_RegisterApi)(RedisModuleCtx *ctx, const char *funcname, void *funcptr); +void* REDISMODULE_API_FUNC(RedisModule_GetExportedApi)(RedisModuleCtx *ctx, const char *funcname); #endif /* This is included inline inside each Redis module. */ @@ -492,6 +494,8 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int REDISMODULE_GET_API(GetRandomBytes); REDISMODULE_GET_API(GetRandomHexChars); REDISMODULE_GET_API(SetClusterFlags); + REDISMODULE_GET_API(RegisterApi); + REDISMODULE_GET_API(GetExportedApi); #endif if (RedisModule_IsModuleNameBusy && RedisModule_IsModuleNameBusy(name)) return REDISMODULE_ERR; diff --git a/src/server.h b/src/server.h index da4c6d45a..379cda058 100644 --- a/src/server.h +++ b/src/server.h @@ -955,6 +955,7 @@ struct redisServer { int always_show_logo; /* Show logo even for non-stdout logging. */ /* Modules */ dict *moduleapi; /* Exported APIs dictionary for modules. */ + dict *exportedapi; /* Api exported by other modules. */ list *loadmodule_queue; /* List of modules to load at startup. */ int module_blocked_pipe[2]; /* Pipe used to awake the event loop if a client blocked on a module command needs |