summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMeirShpilraien <meir@redislabs.com>2018-12-09 14:32:55 +0200
committerantirez <antirez@gmail.com>2018-12-20 17:55:18 +0100
commitab37289fa6035a774d7438f8a7342d3177fdc1be (patch)
tree8534131752f2961f9dc2fc90aaefd194555b5e72
parent009a9292694491ff9eec78c024d38b0b5ca83f2e (diff)
downloadredis-ab37289fa6035a774d7438f8a7342d3177fdc1be.tar.gz
added module ability to register api to be used by other modules
-rw-r--r--src/module.c115
-rw-r--r--src/redismodule.h4
-rw-r--r--src/server.h1
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