summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2019-10-21 17:51:18 +0200
committerantirez <antirez@gmail.com>2019-10-21 17:51:18 +0200
commitb503a85bde17fb8a5f1c5c3381e7b7a138d0fb57 (patch)
tree8e864b77749082d12be5362c5141211cdd7bd981
parent9908995190cf41e8070f26c6be96c0ab574da211 (diff)
downloadredis-b503a85bde17fb8a5f1c5c3381e7b7a138d0fb57.tar.gz
Modules hooks: event firing logic.
-rw-r--r--src/module.c89
-rw-r--r--src/redismodule.h18
2 files changed, 88 insertions, 19 deletions
diff --git a/src/module.c b/src/module.c
index a1c3394d2..853dc39d5 100644
--- a/src/module.c
+++ b/src/module.c
@@ -1553,19 +1553,22 @@ unsigned long long RM_GetClientId(RedisModuleCtx *ctx) {
* fields depending on the version provided. If the version is not valid
* then REDISMODULE_ERR is returned. Otherwise the function returns
* REDISMODULE_OK and the structure pointed by 'ci' gets populated. */
+
+/* Note that we may have multiple versions of the client info structure,
+ * as the API evolves. */
+struct moduleClientInfoV1 {
+ uint64_t version; /* Version of this structure for ABI compat. */
+ uint64_t flags; /* REDISMODULE_CLIENTINFO_FLAG_* */
+ uint64_t id; /* Client ID. */
+ char addr[46]; /* IPv4 or IPv6 address. */
+ uint16_t port; /* TCP port. */
+ uint16_t db; /* Selected DB. */
+};
+
int modulePopulateClientInfoStructure(void *ci, client *client, int structver) {
if (structver != 1) return REDISMODULE_ERR;
- struct {
- uint64_t version; /* Version of this structure for ABI compat. */
- uint64_t flags; /* REDISMODULE_CLIENTINFO_FLAG_* */
- uint64_t id; /* Client ID. */
- char addr[46]; /* IPv4 or IPv6 address. */
- uint16_t port; /* TCP port. */
- uint16_t db; /* Selected DB. */
- } *ci1;
-
- ci1 = ci;
+ struct moduleClientInfoV1 *ci1 = ci;
memset(ci1,0,sizeof(*ci1));
ci1->version = structver;
if (client->flags & CLIENT_MULTI)
@@ -5685,6 +5688,40 @@ void ModuleForkDoneHandler(int exitcode, int bysignal) {
* will be unsubscribed. If there was a previous subscription and the callback
* is not null, the old callback will be replaced with the new one.
*
+ * The callback must be of this type:
+ *
+ * int (*RedisModuleEventCallback)(RedisModuleCtx *ctx,
+ * RedisModuleEvent eid,
+ * uint64_t subevent,
+ * void *data);
+ *
+ * The 'ctx' is a normal Redis module context that the callback can use in
+ * order to call other modules APIs. The 'eid' is the event itself, this
+ * is only useful in the case the module subscribed to multiple events: using
+ * the 'id' field of this structure it is possible to check if the event
+ * is one of the events we registered with this callback. The 'subevent' field
+ * depends on the event that fired. Here is a list of sub events:
+ *
+ * REDISMODULE_EVENT_PERSISTENCE_RDB_START
+ * REDISMODULE_EVENT_PERSISTENCE_RDB_END
+ * REDISMODULE_EVENT_PERSISTENCE_AOF_START
+ * REDISMODULE_EVENT_PERSISTENCE_AOF_END
+ * REDISMODULE_EVENT_LOADING_START
+ * REDISMODULE_EVENT_LOADING_END
+ * REDISMODULE_EVENT_CLIENT_CHANGE_CONNECTED
+ * REDISMODULE_EVENT_CLIENT_CHANGE_DISCONNECTED
+ * REDISMODULE_EVENT_MASTER_LINK_UP
+ * REDISMODULE_EVENT_MASTER_LINK_DOWN
+ * REDISMODULE_EVENT_REPLICA_CHANGE_CONNECTED
+ * REDISMODULE_EVENT_REPLICA_CHANGE_DISCONNECTED
+ *
+ * Finally the 'data' pointer may be populated, only for certain events, with
+ * more relevant data.
+ *
+ * In the case of REDISMODULE_EVENT_CLIENT_CHANGE events it gets populated
+ * with a RedisModuleClientInfo structure: the module should just cast the
+ * void pointer to the structure and access its fields.
+ *
* The function returns REDISMODULE_OK if the module was successfully subscrived
* for the specified event. If the API is called from a wrong context then
* REDISMODULE_ERR is returned. */
@@ -5722,6 +5759,38 @@ int RedisModule_SubscribeToServerEvent(RedisModuleCtx *ctx, RedisModuleEvent eve
return REDISMODULE_OK;
}
+/* This is called by the Redis internals every time we want to fire an
+ * event that can be interceppted by some module. The pointer 'data' is useful
+ * in order to populate the event-specific structure when needed, in order
+ * to return the structure with more information to the callback.
+ *
+ * 'eid' and 'subid' are just the main event ID and the sub event associated
+ * with the event, depending on what exactly happened. */
+void RedisModule_FireServerEvent(uint64_t eid, int subid, void *data) {
+ listIter li;
+ listNode *ln;
+ listRewind(RedisModule_EventListeners,&li);
+ while((ln = listNext(&li))) {
+ RedisModuleEventListener *el = ln->value;
+ if (el->event.id == eid) {
+ RedisModuleCtx ctx = REDISMODULE_CTX_INIT;
+ ctx.module = el->module;
+ ctx.client = moduleFreeContextReusedClient;
+
+ void *moduledata = NULL;
+ struct moduleClientInfoV1 civ1;
+ if (eid == REDISMODULE_EVENT_ID_CLIENT_CONNNECTED ||
+ eid == REDISMODULE_EVENT_ID_CLIENT_DISCONNECTED)
+ {
+ modulePopulateClientInfoStructure(&civ1,data,
+ el->event.dataver);
+ }
+ el->callback(&ctx,el->event,subid,moduledata);
+ moduleFreeContext(&ctx);
+ }
+ }
+}
+
/* --------------------------------------------------------------------------
* Modules API internals
* -------------------------------------------------------------------------- */
diff --git a/src/redismodule.h b/src/redismodule.h
index 8cb1575d4..d7ada3d5b 100644
--- a/src/redismodule.h
+++ b/src/redismodule.h
@@ -180,38 +180,38 @@ typedef struct RedisModuleEvent {
RedisModuleEvent
RedisModuleEvent_ReplicationRoleChanged = {
REDISMODULE_EVENT_ID_REPLICATION_ROLE_CHANGED,
- 0
+ 1
},
RedisModuleEvent_Persistence = {
REDISMODULE_EVENT_ID_PERSISTENCE,
- 0
+ 1
},
RedisModuleEvent_FlushDB = {
REDISMODULE_EVENT_ID_FLUSHDB,
- 0
+ 1
},
RedisModuleEvent_Loading = {
REDISMODULE_EVENT_ID_LOADING,
- 0
+ 1
},
RedisModuleEvent_ClientChange = {
REDISMODULE_EVENT_ID_CLIENT_CHANGE,
- 0
+ 1
},
RedisModuleEvent_Shutdown = {
REDISMODULE_EVENT_ID_SHUTDOWN,
- 0
+ 1
},
RedisModuleEvent_ReplicaChange = {
REDISMODULE_EVENT_ID_REPLICA_CHANGE,
- 0
+ 1
},
RedisModuleEvent_MasterLinkChange = {
REDISMODULE_EVENT_ID_MASTER_LINK_CHANGE,
- 0
+ 1
};
-typedef int (*RedisModuleEventCallback)(RedisModuleEvent eid, uint64_t subevent, void *data);
+typedef int (*RedisModuleEventCallback)(struct RedisModuleCtx *ctx, RedisModuleEvent eid, uint64_t subevent, void *data);
/* Those are values that are used for the 'subevent' callback argument. */
#define REDISMODULE_EVENT_PERSISTENCE_RDB_START 0