diff options
| author | Mitja Felicijan <mitja.felicijan@gmail.com> | 2026-01-21 22:40:55 +0100 |
|---|---|---|
| committer | Mitja Felicijan <mitja.felicijan@gmail.com> | 2026-01-21 22:40:55 +0100 |
| commit | 5d8dfe892a2ea89f706ee140c3bdcfd89fe03fda (patch) | |
| tree | 1acdfa5220cd13b7be43a2a01368e80d306473ca /examples/redis-unstable/src/module.c | |
| parent | c7ab12bba64d9c20ccd79b132dac475f7bc3923e (diff) | |
| download | crep-5d8dfe892a2ea89f706ee140c3bdcfd89fe03fda.tar.gz | |
Add Redis source code for testing
Diffstat (limited to 'examples/redis-unstable/src/module.c')
| -rw-r--r-- | examples/redis-unstable/src/module.c | 15545 |
1 files changed, 15545 insertions, 0 deletions
diff --git a/examples/redis-unstable/src/module.c b/examples/redis-unstable/src/module.c new file mode 100644 index 0000000..3cabd9c --- /dev/null +++ b/examples/redis-unstable/src/module.c | |||
| @@ -0,0 +1,15545 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2016-Present, Redis Ltd. | ||
| 3 | * All rights reserved. | ||
| 4 | * | ||
| 5 | * Copyright (c) 2024-present, Valkey contributors. | ||
| 6 | * All rights reserved. | ||
| 7 | * | ||
| 8 | * Licensed under your choice of (a) the Redis Source Available License 2.0 | ||
| 9 | * (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the | ||
| 10 | * GNU Affero General Public License v3 (AGPLv3). | ||
| 11 | * | ||
| 12 | * Portions of this file are available under BSD3 terms; see REDISCONTRIBUTIONS for more information. | ||
| 13 | */ | ||
| 14 | |||
| 15 | /* -------------------------------------------------------------------------- | ||
| 16 | * Modules API documentation information | ||
| 17 | * | ||
| 18 | * The comments in this file are used to generate the API documentation on the | ||
| 19 | * Redis website. | ||
| 20 | * | ||
| 21 | * Each function starting with RM_ and preceded by a block comment is included | ||
| 22 | * in the API documentation. To hide an RM_ function, put a blank line between | ||
| 23 | * the comment and the function definition or put the comment inside the | ||
| 24 | * function body. | ||
| 25 | * | ||
| 26 | * The functions are divided into sections. Each section is preceded by a | ||
| 27 | * documentation block, which is comment block starting with a markdown level 2 | ||
| 28 | * heading, i.e. a line starting with ##, on the first line of the comment block | ||
| 29 | * (with the exception of a ----- line which can appear first). Other comment | ||
| 30 | * blocks, which are not intended for the modules API user, such as this comment | ||
| 31 | * block, do NOT start with a markdown level 2 heading, so they are included in | ||
| 32 | * the generated a API documentation. | ||
| 33 | * | ||
| 34 | * The documentation comments may contain markdown formatting. Some automatic | ||
| 35 | * replacements are done, such as the replacement of RM with RedisModule in | ||
| 36 | * function names. For details, see the script src/modules/gendoc.rb. | ||
| 37 | * -------------------------------------------------------------------------- */ | ||
| 38 | |||
| 39 | #include "server.h" | ||
| 40 | #include "cluster.h" | ||
| 41 | #include "cluster_asm.h" | ||
| 42 | #include "slowlog.h" | ||
| 43 | #include "rdb.h" | ||
| 44 | #include "monotonic.h" | ||
| 45 | #include "script.h" | ||
| 46 | #include "call_reply.h" | ||
| 47 | #include "hdr_histogram.h" | ||
| 48 | #include "crc16_slottable.h" | ||
| 49 | #include <dlfcn.h> | ||
| 50 | #include <sys/stat.h> | ||
| 51 | #include <sys/wait.h> | ||
| 52 | #include <fcntl.h> | ||
| 53 | #include <string.h> | ||
| 54 | |||
| 55 | /* -------------------------------------------------------------------------- | ||
| 56 | * Private data structures used by the modules system. Those are data | ||
| 57 | * structures that are never exposed to Redis Modules, if not as void | ||
| 58 | * pointers that have an API the module can call with them) | ||
| 59 | * -------------------------------------------------------------------------- */ | ||
| 60 | |||
| 61 | struct RedisModuleInfoCtx { | ||
| 62 | struct RedisModule *module; | ||
| 63 | dict *requested_sections; | ||
| 64 | sds info; /* info string we collected so far */ | ||
| 65 | int sections; /* number of sections we collected so far */ | ||
| 66 | int in_section; /* indication if we're in an active section or not */ | ||
| 67 | int in_dict_field; /* indication that we're currently appending to a dict */ | ||
| 68 | }; | ||
| 69 | |||
| 70 | /* This represents a shared API. Shared APIs will be used to populate | ||
| 71 | * the server.sharedapi dictionary, mapping names of APIs exported by | ||
| 72 | * modules for other modules to use, to their structure specifying the | ||
| 73 | * function pointer that can be called. */ | ||
| 74 | struct RedisModuleSharedAPI { | ||
| 75 | void *func; | ||
| 76 | RedisModule *module; | ||
| 77 | }; | ||
| 78 | typedef struct RedisModuleSharedAPI RedisModuleSharedAPI; | ||
| 79 | typedef struct RedisModuleKeyOptCtx RedisModuleKeyOptCtx; | ||
| 80 | |||
| 81 | dict *modules; /* Hash table of modules. SDS -> RedisModule ptr.*/ | ||
| 82 | |||
| 83 | /* Entries in the context->amqueue array, representing objects to free | ||
| 84 | * when the callback returns. */ | ||
| 85 | struct AutoMemEntry { | ||
| 86 | void *ptr; | ||
| 87 | int type; | ||
| 88 | }; | ||
| 89 | |||
| 90 | /* AutoMemEntry type field values. */ | ||
| 91 | #define REDISMODULE_AM_KEY 0 | ||
| 92 | #define REDISMODULE_AM_STRING 1 | ||
| 93 | #define REDISMODULE_AM_REPLY 2 | ||
| 94 | #define REDISMODULE_AM_FREED 3 /* Explicitly freed by user already. */ | ||
| 95 | #define REDISMODULE_AM_DICT 4 | ||
| 96 | #define REDISMODULE_AM_INFO 5 | ||
| 97 | #define REDISMODULE_AM_CONFIG 6 | ||
| 98 | #define REDISMODULE_AM_SLOTRANGEARRAY 7 | ||
| 99 | |||
| 100 | /* The pool allocator block. Redis Modules can allocate memory via this special | ||
| 101 | * allocator that will automatically release it all once the callback returns. | ||
| 102 | * This means that it can only be used for ephemeral allocations. However | ||
| 103 | * there are two advantages for modules to use this API: | ||
| 104 | * | ||
| 105 | * 1) The memory is automatically released when the callback returns. | ||
| 106 | * 2) This allocator is faster for many small allocations since whole blocks | ||
| 107 | * are allocated, and small pieces returned to the caller just advancing | ||
| 108 | * the index of the allocation. | ||
| 109 | * | ||
| 110 | * Allocations are always rounded to the size of the void pointer in order | ||
| 111 | * to always return aligned memory chunks. */ | ||
| 112 | |||
| 113 | #define REDISMODULE_POOL_ALLOC_MIN_SIZE (1024*8) | ||
| 114 | #define REDISMODULE_POOL_ALLOC_ALIGN (sizeof(void*)) | ||
| 115 | |||
| 116 | typedef struct RedisModulePoolAllocBlock { | ||
| 117 | uint32_t size; | ||
| 118 | uint32_t used; | ||
| 119 | struct RedisModulePoolAllocBlock *next; | ||
| 120 | char memory[]; | ||
| 121 | } RedisModulePoolAllocBlock; | ||
| 122 | |||
| 123 | /* This structure represents the context in which Redis modules operate. | ||
| 124 | * Most APIs module can access, get a pointer to the context, so that the API | ||
| 125 | * implementation can hold state across calls, or remember what to free after | ||
| 126 | * the call and so forth. | ||
| 127 | * | ||
| 128 | * Note that not all the context structure is always filled with actual values | ||
| 129 | * but only the fields needed in a given context. */ | ||
| 130 | |||
| 131 | struct RedisModuleBlockedClient; | ||
| 132 | struct RedisModuleUser; | ||
| 133 | |||
| 134 | struct RedisModuleCtx { | ||
| 135 | void *getapifuncptr; /* NOTE: Must be the first field. */ | ||
| 136 | struct RedisModule *module; /* Module reference. */ | ||
| 137 | client *client; /* Client calling a command. */ | ||
| 138 | struct RedisModuleBlockedClient *blocked_client; /* Blocked client for | ||
| 139 | thread safe context. */ | ||
| 140 | struct AutoMemEntry *amqueue; /* Auto memory queue of objects to free. */ | ||
| 141 | int amqueue_len; /* Number of slots in amqueue. */ | ||
| 142 | int amqueue_used; /* Number of used slots in amqueue. */ | ||
| 143 | int flags; /* REDISMODULE_CTX_... flags. */ | ||
| 144 | void **postponed_arrays; /* To set with RM_ReplySetArrayLength(). */ | ||
| 145 | int postponed_arrays_count; /* Number of entries in postponed_arrays. */ | ||
| 146 | void *blocked_privdata; /* Privdata set when unblocking a client. */ | ||
| 147 | RedisModuleString *blocked_ready_key; /* Key ready when the reply callback | ||
| 148 | gets called for clients blocked | ||
| 149 | on keys. */ | ||
| 150 | |||
| 151 | /* Used if there is the REDISMODULE_CTX_KEYS_POS_REQUEST or | ||
| 152 | * REDISMODULE_CTX_CHANNEL_POS_REQUEST flag set. */ | ||
| 153 | getKeysResult *keys_result; | ||
| 154 | |||
| 155 | struct RedisModulePoolAllocBlock *pa_head; | ||
| 156 | long long next_yield_time; | ||
| 157 | |||
| 158 | const struct RedisModuleUser *user; /* RedisModuleUser commands executed via | ||
| 159 | RM_Call should be executed as, if set */ | ||
| 160 | }; | ||
| 161 | typedef struct RedisModuleCtx RedisModuleCtx; | ||
| 162 | |||
| 163 | #define REDISMODULE_CTX_NONE (0) | ||
| 164 | #define REDISMODULE_CTX_AUTO_MEMORY (1<<0) | ||
| 165 | #define REDISMODULE_CTX_KEYS_POS_REQUEST (1<<1) | ||
| 166 | #define REDISMODULE_CTX_BLOCKED_REPLY (1<<2) | ||
| 167 | #define REDISMODULE_CTX_BLOCKED_TIMEOUT (1<<3) | ||
| 168 | #define REDISMODULE_CTX_THREAD_SAFE (1<<4) | ||
| 169 | #define REDISMODULE_CTX_BLOCKED_DISCONNECTED (1<<5) | ||
| 170 | #define REDISMODULE_CTX_TEMP_CLIENT (1<<6) /* Return client object to the pool | ||
| 171 | when the context is destroyed */ | ||
| 172 | #define REDISMODULE_CTX_NEW_CLIENT (1<<7) /* Free client object when the | ||
| 173 | context is destroyed */ | ||
| 174 | #define REDISMODULE_CTX_CHANNELS_POS_REQUEST (1<<8) | ||
| 175 | #define REDISMODULE_CTX_COMMAND (1<<9) /* Context created to serve a command from call() or AOF (which calls cmd->proc directly) */ | ||
| 176 | |||
| 177 | |||
| 178 | /* This represents a Redis key opened with RM_OpenKey(). */ | ||
| 179 | struct RedisModuleKey { | ||
| 180 | RedisModuleCtx *ctx; | ||
| 181 | redisDb *db; | ||
| 182 | robj *key; /* Key name object. */ | ||
| 183 | kvobj *kv; /* Key-Value object, or NULL if the key was not found. */ | ||
| 184 | void *iter; /* Iterator. */ | ||
| 185 | int mode; /* Opening mode. */ | ||
| 186 | |||
| 187 | union { | ||
| 188 | struct { | ||
| 189 | /* List, use only if value->type == OBJ_LIST */ | ||
| 190 | listTypeEntry entry; /* Current entry in iteration. */ | ||
| 191 | long index; /* Current 0-based index in iteration. */ | ||
| 192 | } list; | ||
| 193 | struct { | ||
| 194 | /* Zset iterator, use only if value->type == OBJ_ZSET */ | ||
| 195 | uint32_t type; /* REDISMODULE_ZSET_RANGE_* */ | ||
| 196 | zrangespec rs; /* Score range. */ | ||
| 197 | zlexrangespec lrs; /* Lex range. */ | ||
| 198 | uint32_t start; /* Start pos for positional ranges. */ | ||
| 199 | uint32_t end; /* End pos for positional ranges. */ | ||
| 200 | void *current; /* Zset iterator current node. */ | ||
| 201 | int er; /* Zset iterator end reached flag | ||
| 202 | (true if end was reached). */ | ||
| 203 | } zset; | ||
| 204 | struct { | ||
| 205 | /* Stream, use only if value->type == OBJ_STREAM */ | ||
| 206 | streamID currentid; /* Current entry while iterating. */ | ||
| 207 | int64_t numfieldsleft; /* Fields left to fetch for current entry. */ | ||
| 208 | int signalready; /* Flag that signalKeyAsReady() is needed. */ | ||
| 209 | } stream; | ||
| 210 | } u; | ||
| 211 | }; | ||
| 212 | |||
| 213 | /* RedisModuleKey 'ztype' values. */ | ||
| 214 | #define REDISMODULE_ZSET_RANGE_NONE 0 /* This must always be 0. */ | ||
| 215 | #define REDISMODULE_ZSET_RANGE_LEX 1 | ||
| 216 | #define REDISMODULE_ZSET_RANGE_SCORE 2 | ||
| 217 | #define REDISMODULE_ZSET_RANGE_POS 3 | ||
| 218 | |||
| 219 | /* Function pointer type of a function representing a command inside | ||
| 220 | * a Redis module. */ | ||
| 221 | struct RedisModuleBlockedClient; | ||
| 222 | typedef int (*RedisModuleCmdFunc) (RedisModuleCtx *ctx, void **argv, int argc); | ||
| 223 | typedef int (*RedisModuleAuthCallback)(RedisModuleCtx *ctx, void *username, void *password, RedisModuleString **err); | ||
| 224 | typedef void (*RedisModuleDisconnectFunc) (RedisModuleCtx *ctx, struct RedisModuleBlockedClient *bc); | ||
| 225 | |||
| 226 | /* This struct holds the information about a command registered by a module.*/ | ||
| 227 | struct RedisModuleCommand { | ||
| 228 | struct RedisModule *module; | ||
| 229 | RedisModuleCmdFunc func; | ||
| 230 | struct redisCommand *rediscmd; | ||
| 231 | }; | ||
| 232 | typedef struct RedisModuleCommand RedisModuleCommand; | ||
| 233 | |||
| 234 | #define REDISMODULE_REPLYFLAG_NONE 0 | ||
| 235 | #define REDISMODULE_REPLYFLAG_TOPARSE (1<<0) /* Protocol must be parsed. */ | ||
| 236 | #define REDISMODULE_REPLYFLAG_NESTED (1<<1) /* Nested reply object. No proto | ||
| 237 | or struct free. */ | ||
| 238 | |||
| 239 | /* Reply of RM_Call() function. The function is filled in a lazy | ||
| 240 | * way depending on the function called on the reply structure. By default | ||
| 241 | * only the type, proto and protolen are filled. */ | ||
| 242 | typedef struct CallReply RedisModuleCallReply; | ||
| 243 | |||
| 244 | /* Structure to hold the module auth callback & the Module implementing it. */ | ||
| 245 | typedef struct RedisModuleAuthCtx { | ||
| 246 | struct RedisModule *module; | ||
| 247 | RedisModuleAuthCallback auth_cb; | ||
| 248 | } RedisModuleAuthCtx; | ||
| 249 | |||
| 250 | /* Structure representing a blocked client. We get a pointer to such | ||
| 251 | * an object when blocking from modules. */ | ||
| 252 | typedef struct RedisModuleBlockedClient { | ||
| 253 | client *client; /* Pointer to the blocked client. or NULL if the client | ||
| 254 | was destroyed during the life of this object. */ | ||
| 255 | RedisModule *module; /* Module blocking the client. */ | ||
| 256 | RedisModuleCmdFunc reply_callback; /* Reply callback on normal completion.*/ | ||
| 257 | RedisModuleAuthCallback auth_reply_cb; /* Reply callback on completing blocking | ||
| 258 | module authentication. */ | ||
| 259 | RedisModuleCmdFunc timeout_callback; /* Reply callback on timeout. */ | ||
| 260 | RedisModuleDisconnectFunc disconnect_callback; /* Called on disconnection.*/ | ||
| 261 | void (*free_privdata)(RedisModuleCtx*,void*);/* privdata cleanup callback.*/ | ||
| 262 | void *privdata; /* Module private data that may be used by the reply | ||
| 263 | or timeout callback. It is set via the | ||
| 264 | RedisModule_UnblockClient() API. */ | ||
| 265 | client *thread_safe_ctx_client; /* Fake client to be used for thread safe | ||
| 266 | context so that no lock is required. */ | ||
| 267 | client *reply_client; /* Fake client used to accumulate replies | ||
| 268 | in thread safe contexts. */ | ||
| 269 | int dbid; /* Database number selected by the original client. */ | ||
| 270 | int blocked_on_keys; /* If blocked via RM_BlockClientOnKeys(). */ | ||
| 271 | int unblocked; /* Already on the moduleUnblocked list. */ | ||
| 272 | monotime background_timer; /* Timer tracking the start of background work */ | ||
| 273 | uint64_t background_duration; /* Current command background time duration. | ||
| 274 | Used for measuring latency of blocking cmds */ | ||
| 275 | int blocked_on_keys_explicit_unblock; /* Set to 1 only in the case of an explicit RM_Unblock on | ||
| 276 | * a client that is blocked on keys. In this case we will | ||
| 277 | * call the timeout call back from within | ||
| 278 | * moduleHandleBlockedClients which runs from the main thread */ | ||
| 279 | } RedisModuleBlockedClient; | ||
| 280 | |||
| 281 | /* This is a list of Module Auth Contexts. Each time a Module registers a callback, a new ctx is | ||
| 282 | * added to this list. Multiple modules can register auth callbacks and the same Module can have | ||
| 283 | * multiple auth callbacks. */ | ||
| 284 | static list *moduleAuthCallbacks; | ||
| 285 | |||
| 286 | static pthread_mutex_t moduleUnblockedClientsMutex = PTHREAD_MUTEX_INITIALIZER; | ||
| 287 | static list *moduleUnblockedClients; | ||
| 288 | |||
| 289 | /* Pool for temporary client objects. Creating and destroying a client object is | ||
| 290 | * costly. We manage a pool of clients to avoid this cost. Pool expands when | ||
| 291 | * more clients are needed and shrinks when unused. Please see modulesCron() | ||
| 292 | * for more details. */ | ||
| 293 | static client **moduleTempClients; | ||
| 294 | static size_t moduleTempClientCap = 0; | ||
| 295 | static size_t moduleTempClientCount = 0; /* Client count in pool */ | ||
| 296 | static size_t moduleTempClientMinCount = 0; /* Min client count in pool since | ||
| 297 | the last cron. */ | ||
| 298 | |||
| 299 | /* We need a mutex that is unlocked / relocked in beforeSleep() in order to | ||
| 300 | * allow thread safe contexts to execute commands at a safe moment. */ | ||
| 301 | static pthread_mutex_t moduleGIL = PTHREAD_MUTEX_INITIALIZER; | ||
| 302 | |||
| 303 | /* Function pointer type for keyspace event notification subscriptions from modules. */ | ||
| 304 | typedef int (*RedisModuleNotificationFunc) (RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key); | ||
| 305 | |||
| 306 | /* Function pointer type for post jobs */ | ||
| 307 | typedef void (*RedisModulePostNotificationJobFunc) (RedisModuleCtx *ctx, void *pd); | ||
| 308 | |||
| 309 | /* Keyspace notification subscriber information. | ||
| 310 | * See RM_SubscribeToKeyspaceEvents() for more information. */ | ||
| 311 | typedef struct RedisModuleKeyspaceSubscriber { | ||
| 312 | /* The module subscribed to the event */ | ||
| 313 | RedisModule *module; | ||
| 314 | /* Notification callback in the module*/ | ||
| 315 | RedisModuleNotificationFunc notify_callback; | ||
| 316 | /* A bit mask of the events the module is interested in */ | ||
| 317 | int event_mask; | ||
| 318 | /* Active flag set on entry, to avoid reentrant subscribers | ||
| 319 | * calling themselves */ | ||
| 320 | int active; | ||
| 321 | } RedisModuleKeyspaceSubscriber; | ||
| 322 | |||
| 323 | typedef struct RedisModulePostExecUnitJob { | ||
| 324 | /* The module subscribed to the event */ | ||
| 325 | RedisModule *module; | ||
| 326 | RedisModulePostNotificationJobFunc callback; | ||
| 327 | void *pd; | ||
| 328 | void (*free_pd)(void*); | ||
| 329 | int dbid; | ||
| 330 | } RedisModulePostExecUnitJob; | ||
| 331 | |||
| 332 | /* The module keyspace notification subscribers list */ | ||
| 333 | static list *moduleKeyspaceSubscribers; | ||
| 334 | |||
| 335 | /* The module post keyspace jobs list */ | ||
| 336 | static list *modulePostExecUnitJobs; | ||
| 337 | |||
| 338 | /* Data structures related to the exported dictionary data structure. */ | ||
| 339 | typedef struct RedisModuleDict { | ||
| 340 | rax *rax; /* The radix tree. */ | ||
| 341 | } RedisModuleDict; | ||
| 342 | |||
| 343 | typedef struct RedisModuleDictIter { | ||
| 344 | RedisModuleDict *dict; | ||
| 345 | raxIterator ri; | ||
| 346 | } RedisModuleDictIter; | ||
| 347 | |||
| 348 | typedef struct RedisModuleCommandFilterCtx { | ||
| 349 | RedisModuleString **argv; | ||
| 350 | int argv_len; | ||
| 351 | int argc; | ||
| 352 | client *c; | ||
| 353 | } RedisModuleCommandFilterCtx; | ||
| 354 | |||
| 355 | typedef void (*RedisModuleCommandFilterFunc) (RedisModuleCommandFilterCtx *filter); | ||
| 356 | |||
| 357 | typedef struct RedisModuleCommandFilter { | ||
| 358 | /* The module that registered the filter */ | ||
| 359 | RedisModule *module; | ||
| 360 | /* Filter callback function */ | ||
| 361 | RedisModuleCommandFilterFunc callback; | ||
| 362 | /* REDISMODULE_CMDFILTER_* flags */ | ||
| 363 | int flags; | ||
| 364 | } RedisModuleCommandFilter; | ||
| 365 | |||
| 366 | /* Registered filters */ | ||
| 367 | static list *moduleCommandFilters; | ||
| 368 | |||
| 369 | typedef void (*RedisModuleForkDoneHandler) (int exitcode, int bysignal, void *user_data); | ||
| 370 | |||
| 371 | static struct RedisModuleForkInfo { | ||
| 372 | RedisModuleForkDoneHandler done_handler; | ||
| 373 | void* done_handler_user_data; | ||
| 374 | } moduleForkInfo = {0}; | ||
| 375 | |||
| 376 | typedef struct RedisModuleServerInfoData { | ||
| 377 | rax *rax; /* parsed info data. */ | ||
| 378 | } RedisModuleServerInfoData; | ||
| 379 | |||
| 380 | typedef struct RedisModuleConfigIterator { | ||
| 381 | dictIterator *di; /* Iterator for the configs dict. */ | ||
| 382 | sds pattern; /* Pattern to filter configs by name. */ | ||
| 383 | int is_glob; /* Is the pattern a glob-pattern or a fixed string? */ | ||
| 384 | } RedisModuleConfigIterator; | ||
| 385 | |||
| 386 | /* Flags for moduleCreateArgvFromUserFormat(). */ | ||
| 387 | #define REDISMODULE_ARGV_REPLICATE (1<<0) | ||
| 388 | #define REDISMODULE_ARGV_NO_AOF (1<<1) | ||
| 389 | #define REDISMODULE_ARGV_NO_REPLICAS (1<<2) | ||
| 390 | #define REDISMODULE_ARGV_RESP_3 (1<<3) | ||
| 391 | #define REDISMODULE_ARGV_RESP_AUTO (1<<4) | ||
| 392 | #define REDISMODULE_ARGV_RUN_AS_USER (1<<5) | ||
| 393 | #define REDISMODULE_ARGV_SCRIPT_MODE (1<<6) | ||
| 394 | #define REDISMODULE_ARGV_NO_WRITES (1<<7) | ||
| 395 | #define REDISMODULE_ARGV_CALL_REPLIES_AS_ERRORS (1<<8) | ||
| 396 | #define REDISMODULE_ARGV_RESPECT_DENY_OOM (1<<9) | ||
| 397 | #define REDISMODULE_ARGV_DRY_RUN (1<<10) | ||
| 398 | #define REDISMODULE_ARGV_ALLOW_BLOCK (1<<11) | ||
| 399 | |||
| 400 | /* Determine whether Redis should signal modified key implicitly. | ||
| 401 | * In case 'ctx' has no 'module' member (and therefore no module->options), | ||
| 402 | * we assume default behavior, that is, Redis signals. | ||
| 403 | * (see RM_GetThreadSafeContext) */ | ||
| 404 | #define SHOULD_SIGNAL_MODIFIED_KEYS(ctx) \ | ||
| 405 | ((ctx)->module? !((ctx)->module->options & REDISMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED) : 1) | ||
| 406 | |||
| 407 | /* Server events hooks data structures and defines: this modules API | ||
| 408 | * allow modules to subscribe to certain events in Redis, such as | ||
| 409 | * the start and end of an RDB or AOF save, the change of role in replication, | ||
| 410 | * and similar other events. */ | ||
| 411 | |||
| 412 | typedef struct RedisModuleEventListener { | ||
| 413 | RedisModule *module; | ||
| 414 | RedisModuleEvent event; | ||
| 415 | RedisModuleEventCallback callback; | ||
| 416 | } RedisModuleEventListener; | ||
| 417 | |||
| 418 | list *RedisModule_EventListeners; /* Global list of all the active events. */ | ||
| 419 | |||
| 420 | /* Data structures related to the redis module users */ | ||
| 421 | |||
| 422 | /* This is the object returned by RM_CreateModuleUser(). The module API is | ||
| 423 | * able to create users, set ACLs to such users, and later authenticate | ||
| 424 | * clients using such newly created users. */ | ||
| 425 | typedef struct RedisModuleUser { | ||
| 426 | user *user; /* Reference to the real redis user */ | ||
| 427 | int free_user; /* Indicates that user should also be freed when this object is freed */ | ||
| 428 | } RedisModuleUser; | ||
| 429 | |||
| 430 | /* Data structures related to redis module configurations */ | ||
| 431 | /* The function signatures for module config get callbacks. These are identical to the ones exposed in redismodule.h. */ | ||
| 432 | typedef RedisModuleString * (*RedisModuleConfigGetStringFunc)(const char *name, void *privdata); | ||
| 433 | typedef long long (*RedisModuleConfigGetNumericFunc)(const char *name, void *privdata); | ||
| 434 | typedef int (*RedisModuleConfigGetBoolFunc)(const char *name, void *privdata); | ||
| 435 | typedef int (*RedisModuleConfigGetEnumFunc)(const char *name, void *privdata); | ||
| 436 | /* The function signatures for module config set callbacks. These are identical to the ones exposed in redismodule.h. */ | ||
| 437 | typedef int (*RedisModuleConfigSetStringFunc)(const char *name, RedisModuleString *val, void *privdata, RedisModuleString **err); | ||
| 438 | typedef int (*RedisModuleConfigSetNumericFunc)(const char *name, long long val, void *privdata, RedisModuleString **err); | ||
| 439 | typedef int (*RedisModuleConfigSetBoolFunc)(const char *name, int val, void *privdata, RedisModuleString **err); | ||
| 440 | typedef int (*RedisModuleConfigSetEnumFunc)(const char *name, int val, void *privdata, RedisModuleString **err); | ||
| 441 | /* Apply signature, identical to redismodule.h */ | ||
| 442 | typedef int (*RedisModuleConfigApplyFunc)(RedisModuleCtx *ctx, void *privdata, RedisModuleString **err); | ||
| 443 | |||
| 444 | /* Struct representing a module config. These are stored in a list in the module struct */ | ||
| 445 | struct ModuleConfig { | ||
| 446 | sds name; /* Fullname of the config (as it appears in the config file) */ | ||
| 447 | sds alias; /* Optional alias for the configuration. NULL if none exists */ | ||
| 448 | |||
| 449 | int unprefixedFlag; /* Indicates if the REDISMODULE_CONFIG_UNPREFIXED flag was set. | ||
| 450 | * If the configuration name was prefixed,during get_fn/set_fn | ||
| 451 | * callbacks, it should be reported without the prefix */ | ||
| 452 | |||
| 453 | void *privdata; /* Optional data passed into the module config callbacks */ | ||
| 454 | union get_fn { /* The get callback specified by the module */ | ||
| 455 | RedisModuleConfigGetStringFunc get_string; | ||
| 456 | RedisModuleConfigGetNumericFunc get_numeric; | ||
| 457 | RedisModuleConfigGetBoolFunc get_bool; | ||
| 458 | RedisModuleConfigGetEnumFunc get_enum; | ||
| 459 | } get_fn; | ||
| 460 | union set_fn { /* The set callback specified by the module */ | ||
| 461 | RedisModuleConfigSetStringFunc set_string; | ||
| 462 | RedisModuleConfigSetNumericFunc set_numeric; | ||
| 463 | RedisModuleConfigSetBoolFunc set_bool; | ||
| 464 | RedisModuleConfigSetEnumFunc set_enum; | ||
| 465 | } set_fn; | ||
| 466 | RedisModuleConfigApplyFunc apply_fn; | ||
| 467 | RedisModule *module; | ||
| 468 | }; | ||
| 469 | |||
| 470 | typedef struct RedisModuleAsyncRMCallPromise{ | ||
| 471 | size_t ref_count; | ||
| 472 | void *private_data; | ||
| 473 | RedisModule *module; | ||
| 474 | RedisModuleOnUnblocked on_unblocked; | ||
| 475 | client *c; | ||
| 476 | RedisModuleCtx *ctx; | ||
| 477 | } RedisModuleAsyncRMCallPromise; | ||
| 478 | |||
| 479 | /* -------------------------------------------------------------------------- | ||
| 480 | * Prototypes | ||
| 481 | * -------------------------------------------------------------------------- */ | ||
| 482 | |||
| 483 | void RM_FreeCallReply(RedisModuleCallReply *reply); | ||
| 484 | void RM_CloseKey(RedisModuleKey *key); | ||
| 485 | void autoMemoryCollect(RedisModuleCtx *ctx); | ||
| 486 | robj **moduleCreateArgvFromUserFormat(const char *cmdname, const char *fmt, int *argcp, int *flags, va_list ap); | ||
| 487 | void RM_ZsetRangeStop(RedisModuleKey *kp); | ||
| 488 | static void zsetKeyReset(RedisModuleKey *key); | ||
| 489 | static void moduleInitKeyTypeSpecific(RedisModuleKey *key); | ||
| 490 | void RM_FreeDict(RedisModuleCtx *ctx, RedisModuleDict *d); | ||
| 491 | void RM_FreeServerInfo(RedisModuleCtx *ctx, RedisModuleServerInfoData *data); | ||
| 492 | void RM_ConfigIteratorRelease(RedisModuleCtx *ctx, RedisModuleConfigIterator *iter); | ||
| 493 | void RM_ClusterFreeSlotRanges(RedisModuleCtx *ctx, RedisModuleSlotRangeArray *slots); | ||
| 494 | |||
| 495 | /* Helpers for RM_SetCommandInfo. */ | ||
| 496 | static int moduleValidateCommandInfo(const RedisModuleCommandInfo *info); | ||
| 497 | static int64_t moduleConvertKeySpecsFlags(int64_t flags, int from_api); | ||
| 498 | static int moduleValidateCommandArgs(RedisModuleCommandArg *args, | ||
| 499 | const RedisModuleCommandInfoVersion *version); | ||
| 500 | static struct redisCommandArg *moduleCopyCommandArgs(RedisModuleCommandArg *args, | ||
| 501 | const RedisModuleCommandInfoVersion *version); | ||
| 502 | static redisCommandArgType moduleConvertArgType(RedisModuleCommandArgType type, int *error); | ||
| 503 | static int moduleConvertArgFlags(int flags); | ||
| 504 | void moduleCreateContext(RedisModuleCtx *out_ctx, RedisModule *module, int ctx_flags); | ||
| 505 | |||
| 506 | /* Common helper functions. */ | ||
| 507 | int moduleVerifyResourceName(const char *name); | ||
| 508 | |||
| 509 | /* -------------------------------------------------------------------------- | ||
| 510 | * ## Heap allocation raw functions | ||
| 511 | * | ||
| 512 | * Memory allocated with these functions are taken into account by Redis key | ||
| 513 | * eviction algorithms and are reported in Redis memory usage information. | ||
| 514 | * -------------------------------------------------------------------------- */ | ||
| 515 | |||
| 516 | /* Use like malloc(). Memory allocated with this function is reported in | ||
| 517 | * Redis INFO memory, used for keys eviction according to maxmemory settings | ||
| 518 | * and in general is taken into account as memory allocated by Redis. | ||
| 519 | * You should avoid using malloc(). | ||
| 520 | * This function panics if unable to allocate enough memory. */ | ||
| 521 | void *RM_Alloc(size_t bytes) { | ||
| 522 | /* Use 'zmalloc_usable()' instead of 'zmalloc()' to allow the compiler | ||
| 523 | * to recognize the additional memory size, which means that modules can | ||
| 524 | * use the memory reported by 'RM_MallocUsableSize()' safely. In theory this | ||
| 525 | * isn't really needed since this API can't be inlined (not even for embedded | ||
| 526 | * modules like TLS (we use function pointers for module APIs), and the API doesn't | ||
| 527 | * have the malloc_size attribute, but it's hard to predict how smart future compilers | ||
| 528 | * will be, so better safe than sorry. */ | ||
| 529 | return zmalloc_usable(bytes,NULL); | ||
| 530 | } | ||
| 531 | |||
| 532 | /* Similar to RM_Alloc, but returns NULL in case of allocation failure, instead | ||
| 533 | * of panicking. */ | ||
| 534 | void *RM_TryAlloc(size_t bytes) { | ||
| 535 | return ztrymalloc_usable(bytes,NULL); | ||
| 536 | } | ||
| 537 | |||
| 538 | /* Use like calloc(). Memory allocated with this function is reported in | ||
| 539 | * Redis INFO memory, used for keys eviction according to maxmemory settings | ||
| 540 | * and in general is taken into account as memory allocated by Redis. | ||
| 541 | * You should avoid using calloc() directly. */ | ||
| 542 | void *RM_Calloc(size_t nmemb, size_t size) { | ||
| 543 | return zcalloc_usable(nmemb*size,NULL); | ||
| 544 | } | ||
| 545 | |||
| 546 | /* Similar to RM_Calloc, but returns NULL in case of allocation failure, instead | ||
| 547 | * of panicking. */ | ||
| 548 | void *RM_TryCalloc(size_t nmemb, size_t size) { | ||
| 549 | return ztrycalloc_usable(nmemb*size,NULL); | ||
| 550 | } | ||
| 551 | |||
| 552 | /* Use like realloc() for memory obtained with RedisModule_Alloc(). */ | ||
| 553 | void* RM_Realloc(void *ptr, size_t bytes) { | ||
| 554 | return zrealloc_usable(ptr,bytes,NULL,NULL); | ||
| 555 | } | ||
| 556 | |||
| 557 | /* Similar to RM_Realloc, but returns NULL in case of allocation failure, | ||
| 558 | * instead of panicking. */ | ||
| 559 | void *RM_TryRealloc(void *ptr, size_t bytes) { | ||
| 560 | return ztryrealloc_usable(ptr,bytes,NULL,NULL); | ||
| 561 | } | ||
| 562 | |||
| 563 | /* Use like free() for memory obtained by RedisModule_Alloc() and | ||
| 564 | * RedisModule_Realloc(). However you should never try to free with | ||
| 565 | * RedisModule_Free() memory allocated with malloc() inside your module. */ | ||
| 566 | void RM_Free(void *ptr) { | ||
| 567 | zfree(ptr); | ||
| 568 | } | ||
| 569 | |||
| 570 | /* Like strdup() but returns memory allocated with RedisModule_Alloc(). */ | ||
| 571 | char *RM_Strdup(const char *str) { | ||
| 572 | return zstrdup(str); | ||
| 573 | } | ||
| 574 | |||
| 575 | /* -------------------------------------------------------------------------- | ||
| 576 | * Pool allocator | ||
| 577 | * -------------------------------------------------------------------------- */ | ||
| 578 | |||
| 579 | /* Release the chain of blocks used for pool allocations. */ | ||
| 580 | void poolAllocRelease(RedisModuleCtx *ctx) { | ||
| 581 | RedisModulePoolAllocBlock *head = ctx->pa_head, *next; | ||
| 582 | |||
| 583 | while(head != NULL) { | ||
| 584 | next = head->next; | ||
| 585 | zfree(head); | ||
| 586 | head = next; | ||
| 587 | } | ||
| 588 | ctx->pa_head = NULL; | ||
| 589 | } | ||
| 590 | |||
| 591 | /* Return heap allocated memory that will be freed automatically when the | ||
| 592 | * module callback function returns. Mostly suitable for small allocations | ||
| 593 | * that are short living and must be released when the callback returns | ||
| 594 | * anyway. The returned memory is aligned to the architecture word size | ||
| 595 | * if at least word size bytes are requested, otherwise it is just | ||
| 596 | * aligned to the next power of two, so for example a 3 bytes request is | ||
| 597 | * 4 bytes aligned while a 2 bytes request is 2 bytes aligned. | ||
| 598 | * | ||
| 599 | * There is no realloc style function since when this is needed to use the | ||
| 600 | * pool allocator is not a good idea. | ||
| 601 | * | ||
| 602 | * The function returns NULL if `bytes` is 0. */ | ||
| 603 | void *RM_PoolAlloc(RedisModuleCtx *ctx, size_t bytes) { | ||
| 604 | if (bytes == 0) return NULL; | ||
| 605 | RedisModulePoolAllocBlock *b = ctx->pa_head; | ||
| 606 | size_t left = b ? b->size - b->used : 0; | ||
| 607 | |||
| 608 | /* Fix alignment. */ | ||
| 609 | if (left >= bytes) { | ||
| 610 | size_t alignment = REDISMODULE_POOL_ALLOC_ALIGN; | ||
| 611 | while (bytes < alignment && alignment/2 >= bytes) alignment /= 2; | ||
| 612 | if (b->used % alignment) | ||
| 613 | b->used += alignment - (b->used % alignment); | ||
| 614 | left = (b->used > b->size) ? 0 : b->size - b->used; | ||
| 615 | } | ||
| 616 | |||
| 617 | /* Create a new block if needed. */ | ||
| 618 | if (left < bytes) { | ||
| 619 | size_t blocksize = REDISMODULE_POOL_ALLOC_MIN_SIZE; | ||
| 620 | if (blocksize < bytes) blocksize = bytes; | ||
| 621 | b = zmalloc(sizeof(*b) + blocksize); | ||
| 622 | b->size = blocksize; | ||
| 623 | b->used = 0; | ||
| 624 | b->next = ctx->pa_head; | ||
| 625 | ctx->pa_head = b; | ||
| 626 | } | ||
| 627 | |||
| 628 | char *retval = b->memory + b->used; | ||
| 629 | b->used += bytes; | ||
| 630 | return retval; | ||
| 631 | } | ||
| 632 | |||
| 633 | /* -------------------------------------------------------------------------- | ||
| 634 | * Helpers for modules API implementation | ||
| 635 | * -------------------------------------------------------------------------- */ | ||
| 636 | |||
| 637 | client *moduleAllocTempClient(void) { | ||
| 638 | client *c = NULL; | ||
| 639 | |||
| 640 | if (moduleTempClientCount > 0) { | ||
| 641 | c = moduleTempClients[--moduleTempClientCount]; | ||
| 642 | if (moduleTempClientCount < moduleTempClientMinCount) | ||
| 643 | moduleTempClientMinCount = moduleTempClientCount; | ||
| 644 | } else { | ||
| 645 | c = createClient(NULL); | ||
| 646 | c->flags |= CLIENT_MODULE; | ||
| 647 | c->user = NULL; /* Root user */ | ||
| 648 | } | ||
| 649 | return c; | ||
| 650 | } | ||
| 651 | |||
| 652 | static void freeRedisModuleAsyncRMCallPromise(RedisModuleAsyncRMCallPromise *promise) { | ||
| 653 | if (--promise->ref_count > 0) { | ||
| 654 | return; | ||
| 655 | } | ||
| 656 | /* When the promise is finally freed it can not have a client attached to it. | ||
| 657 | * Either releasing the client or RM_CallReplyPromiseAbort would have removed it. */ | ||
| 658 | serverAssert(!promise->c); | ||
| 659 | zfree(promise); | ||
| 660 | } | ||
| 661 | |||
| 662 | void moduleReleaseTempClient(client *c) { | ||
| 663 | if (moduleTempClientCount == moduleTempClientCap) { | ||
| 664 | moduleTempClientCap = moduleTempClientCap ? moduleTempClientCap*2 : 32; | ||
| 665 | moduleTempClients = zrealloc(moduleTempClients, sizeof(c)*moduleTempClientCap); | ||
| 666 | } | ||
| 667 | clearClientConnectionState(c); | ||
| 668 | listEmpty(c->reply); | ||
| 669 | c->reply_bytes = 0; | ||
| 670 | c->duration = 0; | ||
| 671 | resetClient(c, -1); | ||
| 672 | serverAssert(c->all_argv_len_sum == 0); | ||
| 673 | c->bufpos = 0; | ||
| 674 | c->flags = CLIENT_MODULE; | ||
| 675 | c->user = NULL; /* Root user */ | ||
| 676 | c->cmd = c->lastcmd = c->realcmd = NULL; | ||
| 677 | if (c->bstate.async_rm_call_handle) { | ||
| 678 | RedisModuleAsyncRMCallPromise *promise = c->bstate.async_rm_call_handle; | ||
| 679 | promise->c = NULL; /* Remove the client from the promise so it will no longer be possible to abort it. */ | ||
| 680 | freeRedisModuleAsyncRMCallPromise(promise); | ||
| 681 | c->bstate.async_rm_call_handle = NULL; | ||
| 682 | } | ||
| 683 | moduleTempClients[moduleTempClientCount++] = c; | ||
| 684 | } | ||
| 685 | |||
| 686 | /* Create an empty key of the specified type. `key` must point to a key object | ||
| 687 | * opened for writing where the `.value` member is set to NULL because the | ||
| 688 | * key was found to be non existing. | ||
| 689 | * | ||
| 690 | * On success REDISMODULE_OK is returned and the key is populated with | ||
| 691 | * the value of the specified type. The function fails and returns | ||
| 692 | * REDISMODULE_ERR if: | ||
| 693 | * | ||
| 694 | * 1. The key is not open for writing. | ||
| 695 | * 2. The key is not empty. | ||
| 696 | * 3. The specified type is unknown. | ||
| 697 | */ | ||
| 698 | int moduleCreateEmptyKey(RedisModuleKey *key, int type) { | ||
| 699 | robj *obj; | ||
| 700 | |||
| 701 | /* The key must be open for writing and non existing to proceed. */ | ||
| 702 | if (!(key->mode & REDISMODULE_WRITE) || key->kv) | ||
| 703 | return REDISMODULE_ERR; | ||
| 704 | |||
| 705 | switch(type) { | ||
| 706 | case REDISMODULE_KEYTYPE_LIST: | ||
| 707 | obj = createListListpackObject(); | ||
| 708 | break; | ||
| 709 | case REDISMODULE_KEYTYPE_ZSET: | ||
| 710 | obj = createZsetListpackObject(); | ||
| 711 | break; | ||
| 712 | case REDISMODULE_KEYTYPE_HASH: | ||
| 713 | obj = createHashObject(); | ||
| 714 | break; | ||
| 715 | case REDISMODULE_KEYTYPE_STREAM: | ||
| 716 | obj = createStreamObject(); | ||
| 717 | break; | ||
| 718 | default: return REDISMODULE_ERR; | ||
| 719 | } | ||
| 720 | |||
| 721 | key->kv = dbAdd(key->db, key->key, &obj); | ||
| 722 | moduleInitKeyTypeSpecific(key); | ||
| 723 | return REDISMODULE_OK; | ||
| 724 | } | ||
| 725 | |||
| 726 | /* Frees key->iter and sets it to NULL. */ | ||
| 727 | static void moduleFreeKeyIterator(RedisModuleKey *key) { | ||
| 728 | serverAssert(key->iter != NULL); | ||
| 729 | switch (key->kv->type) { | ||
| 730 | case OBJ_LIST: | ||
| 731 | listTypeResetIterator(key->iter); | ||
| 732 | zfree(key->iter); | ||
| 733 | break; | ||
| 734 | case OBJ_STREAM: | ||
| 735 | streamIteratorStop(key->iter); | ||
| 736 | zfree(key->iter); | ||
| 737 | break; | ||
| 738 | default: serverAssert(0); /* No key->iter for other types. */ | ||
| 739 | } | ||
| 740 | key->iter = NULL; | ||
| 741 | } | ||
| 742 | |||
| 743 | /* Callback for listTypeTryConversion(). | ||
| 744 | * Frees list iterator and sets it to NULL. */ | ||
| 745 | static void moduleFreeListIterator(void *data) { | ||
| 746 | RedisModuleKey *key = (RedisModuleKey*)data; | ||
| 747 | serverAssert(key->kv->type == OBJ_LIST); | ||
| 748 | if (key->iter) moduleFreeKeyIterator(key); | ||
| 749 | } | ||
| 750 | |||
| 751 | /* This function is called in low-level API implementation functions in order | ||
| 752 | * to check if the value associated with the key remained empty after an | ||
| 753 | * operation that removed elements from an aggregate data type. | ||
| 754 | * | ||
| 755 | * If this happens, the key is deleted from the DB and the key object state | ||
| 756 | * is set to the right one in order to be targeted again by write operations | ||
| 757 | * possibly recreating the key if needed. | ||
| 758 | * | ||
| 759 | * The function returns 1 if the key value object is found empty and is | ||
| 760 | * deleted, otherwise 0 is returned. */ | ||
| 761 | int moduleDelKeyIfEmpty(RedisModuleKey *key) { | ||
| 762 | if (!(key->mode & REDISMODULE_WRITE) || key->kv == NULL) return 0; | ||
| 763 | int isempty; | ||
| 764 | robj *o = key->kv; | ||
| 765 | |||
| 766 | switch(o->type) { | ||
| 767 | case OBJ_LIST: isempty = listTypeLength(o) == 0; break; | ||
| 768 | case OBJ_SET: isempty = setTypeSize(o) == 0; break; | ||
| 769 | case OBJ_ZSET: isempty = zsetLength(o) == 0; break; | ||
| 770 | case OBJ_HASH: isempty = hashTypeLength(o, 0) == 0; break; | ||
| 771 | case OBJ_STREAM: isempty = streamLength(o) == 0; break; | ||
| 772 | default: isempty = 0; | ||
| 773 | } | ||
| 774 | |||
| 775 | if (isempty) { | ||
| 776 | if (key->iter) moduleFreeKeyIterator(key); | ||
| 777 | dbDelete(key->db,key->key); | ||
| 778 | key->kv = NULL; | ||
| 779 | return 1; | ||
| 780 | } else { | ||
| 781 | return 0; | ||
| 782 | } | ||
| 783 | } | ||
| 784 | |||
| 785 | /* -------------------------------------------------------------------------- | ||
| 786 | * Service API exported to modules | ||
| 787 | * | ||
| 788 | * Note that all the exported APIs are called RM_<funcname> in the core | ||
| 789 | * and RedisModule_<funcname> in the module side (defined as function | ||
| 790 | * pointers in redismodule.h). In this way the dynamic linker does not | ||
| 791 | * mess with our global function pointers, overriding it with the symbols | ||
| 792 | * defined in the main executable having the same names. | ||
| 793 | * -------------------------------------------------------------------------- */ | ||
| 794 | |||
| 795 | int RM_GetApi(const char *funcname, void **targetPtrPtr) { | ||
| 796 | /* Lookup the requested module API and store the function pointer into the | ||
| 797 | * target pointer. The function returns REDISMODULE_ERR if there is no such | ||
| 798 | * named API, otherwise REDISMODULE_OK. | ||
| 799 | * | ||
| 800 | * This function is not meant to be used by modules developer, it is only | ||
| 801 | * used implicitly by including redismodule.h. */ | ||
| 802 | dictEntry *he = dictFind(server.moduleapi, funcname); | ||
| 803 | if (!he) return REDISMODULE_ERR; | ||
| 804 | *targetPtrPtr = dictGetVal(he); | ||
| 805 | return REDISMODULE_OK; | ||
| 806 | } | ||
| 807 | |||
| 808 | void modulePostExecutionUnitOperations(void) { | ||
| 809 | if (server.execution_nesting) | ||
| 810 | return; | ||
| 811 | |||
| 812 | if (server.busy_module_yield_flags) { | ||
| 813 | blockingOperationEnds(); | ||
| 814 | server.busy_module_yield_flags = BUSY_MODULE_YIELD_NONE; | ||
| 815 | if (server.current_client) | ||
| 816 | unprotectClient(server.current_client); | ||
| 817 | unblockPostponedClients(); | ||
| 818 | } | ||
| 819 | } | ||
| 820 | |||
| 821 | /* Free the context after the user function was called. */ | ||
| 822 | void moduleFreeContext(RedisModuleCtx *ctx) { | ||
| 823 | /* See comment in moduleCreateContext */ | ||
| 824 | if (!(ctx->flags & (REDISMODULE_CTX_THREAD_SAFE|REDISMODULE_CTX_COMMAND))) { | ||
| 825 | exitExecutionUnit(); | ||
| 826 | postExecutionUnitOperations(); | ||
| 827 | } | ||
| 828 | autoMemoryCollect(ctx); | ||
| 829 | poolAllocRelease(ctx); | ||
| 830 | if (ctx->postponed_arrays) { | ||
| 831 | zfree(ctx->postponed_arrays); | ||
| 832 | ctx->postponed_arrays_count = 0; | ||
| 833 | serverLog(LL_WARNING, | ||
| 834 | "API misuse detected in module %s: " | ||
| 835 | "RedisModule_ReplyWith*(REDISMODULE_POSTPONED_LEN) " | ||
| 836 | "not matched by the same number of RedisModule_SetReply*Len() " | ||
| 837 | "calls.", | ||
| 838 | ctx->module->name); | ||
| 839 | } | ||
| 840 | /* If this context has a temp client, we return it back to the pool. | ||
| 841 | * If this context created a new client (e.g detached context), we free it. | ||
| 842 | * If the client is assigned manually, e.g ctx->client = someClientInstance, | ||
| 843 | * none of these flags will be set and we do not attempt to free it. */ | ||
| 844 | if (ctx->flags & REDISMODULE_CTX_TEMP_CLIENT) | ||
| 845 | moduleReleaseTempClient(ctx->client); | ||
| 846 | else if (ctx->flags & REDISMODULE_CTX_NEW_CLIENT) | ||
| 847 | freeClient(ctx->client); | ||
| 848 | } | ||
| 849 | |||
| 850 | static CallReply *moduleParseReply(client *c, RedisModuleCtx *ctx) { | ||
| 851 | /* Convert the result of the Redis command into a module reply. */ | ||
| 852 | sds proto = sdsnewlen(c->buf,c->bufpos); | ||
| 853 | c->bufpos = 0; | ||
| 854 | while(listLength(c->reply)) { | ||
| 855 | clientReplyBlock *o = listNodeValue(listFirst(c->reply)); | ||
| 856 | |||
| 857 | proto = sdscatlen(proto,o->buf,o->used); | ||
| 858 | listDelNode(c->reply,listFirst(c->reply)); | ||
| 859 | } | ||
| 860 | CallReply *reply = callReplyCreate(proto, c->deferred_reply_errors, ctx); | ||
| 861 | c->deferred_reply_errors = NULL; /* now the responsibility of the reply object. */ | ||
| 862 | return reply; | ||
| 863 | } | ||
| 864 | |||
| 865 | void moduleCallCommandUnblockedHandler(client *c) { | ||
| 866 | RedisModuleCtx ctx; | ||
| 867 | RedisModuleAsyncRMCallPromise *promise = c->bstate.async_rm_call_handle; | ||
| 868 | serverAssert(promise); | ||
| 869 | RedisModule *module = promise->module; | ||
| 870 | if (!promise->on_unblocked) { | ||
| 871 | moduleReleaseTempClient(c); | ||
| 872 | return; /* module did not set any unblock callback. */ | ||
| 873 | } | ||
| 874 | moduleCreateContext(&ctx, module, REDISMODULE_CTX_TEMP_CLIENT); | ||
| 875 | selectDb(ctx.client, c->db->id); | ||
| 876 | |||
| 877 | CallReply *reply = moduleParseReply(c, NULL); | ||
| 878 | module->in_call++; | ||
| 879 | promise->on_unblocked(&ctx, reply, promise->private_data); | ||
| 880 | module->in_call--; | ||
| 881 | |||
| 882 | moduleFreeContext(&ctx); | ||
| 883 | moduleReleaseTempClient(c); | ||
| 884 | } | ||
| 885 | |||
| 886 | /* Create a module ctx and keep track of the nesting level. | ||
| 887 | * | ||
| 888 | * Note: When creating ctx for threads (RM_GetThreadSafeContext and | ||
| 889 | * RM_GetDetachedThreadSafeContext) we do not bump up the nesting level | ||
| 890 | * because we only need to track of nesting level in the main thread | ||
| 891 | * (only the main thread uses propagatePendingCommands) */ | ||
| 892 | void moduleCreateContext(RedisModuleCtx *out_ctx, RedisModule *module, int ctx_flags) { | ||
| 893 | memset(out_ctx, 0 ,sizeof(RedisModuleCtx)); | ||
| 894 | out_ctx->getapifuncptr = (void*)(unsigned long)&RM_GetApi; | ||
| 895 | out_ctx->module = module; | ||
| 896 | out_ctx->flags = ctx_flags; | ||
| 897 | if (ctx_flags & REDISMODULE_CTX_TEMP_CLIENT) | ||
| 898 | out_ctx->client = moduleAllocTempClient(); | ||
| 899 | else if (ctx_flags & REDISMODULE_CTX_NEW_CLIENT) | ||
| 900 | out_ctx->client = createClient(NULL); | ||
| 901 | |||
| 902 | /* Calculate the initial yield time for long blocked contexts. | ||
| 903 | * in loading we depend on the server hz, but in other cases we also wait | ||
| 904 | * for busy_reply_threshold. | ||
| 905 | * Note that in theory we could have started processing BUSY_MODULE_YIELD_EVENTS | ||
| 906 | * sooner, and only delay the processing for clients till the busy_reply_threshold, | ||
| 907 | * but this carries some overheads of frequently marking clients with BLOCKED_POSTPONE | ||
| 908 | * and releasing them, i.e. if modules only block for short periods. */ | ||
| 909 | if (server.loading) | ||
| 910 | out_ctx->next_yield_time = getMonotonicUs() + 1000000 / server.hz; | ||
| 911 | else | ||
| 912 | out_ctx->next_yield_time = getMonotonicUs() + server.busy_reply_threshold * 1000; | ||
| 913 | |||
| 914 | /* Increment the execution_nesting counter (module is about to execute some code), | ||
| 915 | * except in the following cases: | ||
| 916 | * 1. We came here from cmd->proc (either call() or AOF load). | ||
| 917 | * In the former, the counter has been already incremented from within | ||
| 918 | * call() and in the latter we don't care about execution_nesting | ||
| 919 | * 2. If we are running in a thread (execution_nesting will be dealt with | ||
| 920 | * when locking/unlocking the GIL) */ | ||
| 921 | if (!(ctx_flags & (REDISMODULE_CTX_THREAD_SAFE|REDISMODULE_CTX_COMMAND))) { | ||
| 922 | enterExecutionUnit(1, 0); | ||
| 923 | } | ||
| 924 | } | ||
| 925 | |||
| 926 | /* This Redis command binds the normal Redis command invocation with commands | ||
| 927 | * exported by modules. */ | ||
| 928 | void RedisModuleCommandDispatcher(client *c) { | ||
| 929 | RedisModuleCommand *cp = c->cmd->module_cmd; | ||
| 930 | RedisModuleCtx ctx; | ||
| 931 | moduleCreateContext(&ctx, cp->module, REDISMODULE_CTX_COMMAND); | ||
| 932 | |||
| 933 | ctx.client = c; | ||
| 934 | cp->func(&ctx,(void**)c->argv,c->argc); | ||
| 935 | moduleFreeContext(&ctx); | ||
| 936 | |||
| 937 | /* In some cases processMultibulkBuffer uses sdsMakeRoomFor to | ||
| 938 | * expand the query buffer, and in order to avoid a big object copy | ||
| 939 | * the query buffer SDS may be used directly as the SDS string backing | ||
| 940 | * the client argument vectors: sometimes this will result in the SDS | ||
| 941 | * string having unused space at the end. Later if a module takes ownership | ||
| 942 | * of the RedisString, such space will be wasted forever. Inside the | ||
| 943 | * Redis core this is not a problem because tryObjectEncoding() is called | ||
| 944 | * before storing strings in the key space. Here we need to do it | ||
| 945 | * for the module. */ | ||
| 946 | for (int i = 0; i < c->argc; i++) { | ||
| 947 | /* Only do the work if the module took ownership of the object: | ||
| 948 | * in that case the refcount is no longer 1. */ | ||
| 949 | if (c->argv[i]->refcount > 1) | ||
| 950 | trimStringObjectIfNeeded(c->argv[i], 0); | ||
| 951 | } | ||
| 952 | } | ||
| 953 | |||
| 954 | /* This function returns the list of keys, with the same interface as the | ||
| 955 | * 'getkeys' function of the native commands, for module commands that exported | ||
| 956 | * the "getkeys-api" flag during the registration. This is done when the | ||
| 957 | * list of keys are not at fixed positions, so that first/last/step cannot | ||
| 958 | * be used. | ||
| 959 | * | ||
| 960 | * In order to accomplish its work, the module command is called, flagging | ||
| 961 | * the context in a way that the command can recognize this is a special | ||
| 962 | * "get keys" call by calling RedisModule_IsKeysPositionRequest(ctx). */ | ||
| 963 | int moduleGetCommandKeysViaAPI(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) { | ||
| 964 | RedisModuleCommand *cp = cmd->module_cmd; | ||
| 965 | RedisModuleCtx ctx; | ||
| 966 | moduleCreateContext(&ctx, cp->module, REDISMODULE_CTX_KEYS_POS_REQUEST); | ||
| 967 | |||
| 968 | /* Initialize getKeysResult */ | ||
| 969 | getKeysPrepareResult(result, MAX_KEYS_BUFFER); | ||
| 970 | ctx.keys_result = result; | ||
| 971 | |||
| 972 | cp->func(&ctx,(void**)argv,argc); | ||
| 973 | /* We currently always use the array allocated by RM_KeyAtPos() and don't try | ||
| 974 | * to optimize for the pre-allocated buffer. | ||
| 975 | */ | ||
| 976 | moduleFreeContext(&ctx); | ||
| 977 | return result->numkeys; | ||
| 978 | } | ||
| 979 | |||
| 980 | /* This function returns the list of channels, with the same interface as | ||
| 981 | * moduleGetCommandKeysViaAPI, for modules that declare "getchannels-api" | ||
| 982 | * during registration. Unlike keys, this is the only way to declare channels. */ | ||
| 983 | int moduleGetCommandChannelsViaAPI(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) { | ||
| 984 | RedisModuleCommand *cp = cmd->module_cmd; | ||
| 985 | RedisModuleCtx ctx; | ||
| 986 | moduleCreateContext(&ctx, cp->module, REDISMODULE_CTX_CHANNELS_POS_REQUEST); | ||
| 987 | |||
| 988 | /* Initialize getKeysResult */ | ||
| 989 | getKeysPrepareResult(result, MAX_KEYS_BUFFER); | ||
| 990 | ctx.keys_result = result; | ||
| 991 | |||
| 992 | cp->func(&ctx,(void**)argv,argc); | ||
| 993 | /* We currently always use the array allocated by RM_RM_ChannelAtPosWithFlags() and don't try | ||
| 994 | * to optimize for the pre-allocated buffer. */ | ||
| 995 | moduleFreeContext(&ctx); | ||
| 996 | return result->numkeys; | ||
| 997 | } | ||
| 998 | |||
| 999 | /* -------------------------------------------------------------------------- | ||
| 1000 | * ## Commands API | ||
| 1001 | * | ||
| 1002 | * These functions are used to implement custom Redis commands. | ||
| 1003 | * | ||
| 1004 | * For examples, see https://redis.io/docs/latest/develop/reference/modules/. | ||
| 1005 | * -------------------------------------------------------------------------- */ | ||
| 1006 | |||
| 1007 | /* Return non-zero if a module command, that was declared with the | ||
| 1008 | * flag "getkeys-api", is called in a special way to get the keys positions | ||
| 1009 | * and not to get executed. Otherwise zero is returned. */ | ||
| 1010 | int RM_IsKeysPositionRequest(RedisModuleCtx *ctx) { | ||
| 1011 | return (ctx->flags & REDISMODULE_CTX_KEYS_POS_REQUEST) != 0; | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | /* When a module command is called in order to obtain the position of | ||
| 1015 | * keys, since it was flagged as "getkeys-api" during the registration, | ||
| 1016 | * the command implementation checks for this special call using the | ||
| 1017 | * RedisModule_IsKeysPositionRequest() API and uses this function in | ||
| 1018 | * order to report keys. | ||
| 1019 | * | ||
| 1020 | * The supported flags are the ones used by RM_SetCommandInfo, see REDISMODULE_CMD_KEY_*. | ||
| 1021 | * | ||
| 1022 | * | ||
| 1023 | * The following is an example of how it could be used: | ||
| 1024 | * | ||
| 1025 | * if (RedisModule_IsKeysPositionRequest(ctx)) { | ||
| 1026 | * RedisModule_KeyAtPosWithFlags(ctx, 2, REDISMODULE_CMD_KEY_RO | REDISMODULE_CMD_KEY_ACCESS); | ||
| 1027 | * RedisModule_KeyAtPosWithFlags(ctx, 1, REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE | REDISMODULE_CMD_KEY_ACCESS); | ||
| 1028 | * } | ||
| 1029 | * | ||
| 1030 | * Note: in the example above the get keys API could have been handled by key-specs (preferred). | ||
| 1031 | * Implementing the getkeys-api is required only when is it not possible to declare key-specs that cover all keys. | ||
| 1032 | * | ||
| 1033 | */ | ||
| 1034 | void RM_KeyAtPosWithFlags(RedisModuleCtx *ctx, int pos, int flags) { | ||
| 1035 | if (!(ctx->flags & REDISMODULE_CTX_KEYS_POS_REQUEST) || !ctx->keys_result) return; | ||
| 1036 | if (pos <= 0) return; | ||
| 1037 | |||
| 1038 | getKeysResult *res = ctx->keys_result; | ||
| 1039 | |||
| 1040 | /* Check overflow */ | ||
| 1041 | if (res->numkeys == res->size) { | ||
| 1042 | int newsize = res->size + (res->size > 8192 ? 8192 : res->size); | ||
| 1043 | getKeysPrepareResult(res, newsize); | ||
| 1044 | } | ||
| 1045 | |||
| 1046 | res->keys[res->numkeys].pos = pos; | ||
| 1047 | res->keys[res->numkeys].flags = moduleConvertKeySpecsFlags(flags, 1); | ||
| 1048 | res->numkeys++; | ||
| 1049 | } | ||
| 1050 | |||
| 1051 | /* This API existed before RM_KeyAtPosWithFlags was added, now deprecated and | ||
| 1052 | * can be used for compatibility with older versions, before key-specs and flags | ||
| 1053 | * were introduced. */ | ||
| 1054 | void RM_KeyAtPos(RedisModuleCtx *ctx, int pos) { | ||
| 1055 | /* Default flags require full access */ | ||
| 1056 | int flags = moduleConvertKeySpecsFlags(CMD_KEY_FULL_ACCESS, 0); | ||
| 1057 | RM_KeyAtPosWithFlags(ctx, pos, flags); | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | /* Return non-zero if a module command, that was declared with the | ||
| 1061 | * flag "getchannels-api", is called in a special way to get the channel positions | ||
| 1062 | * and not to get executed. Otherwise zero is returned. */ | ||
| 1063 | int RM_IsChannelsPositionRequest(RedisModuleCtx *ctx) { | ||
| 1064 | return (ctx->flags & REDISMODULE_CTX_CHANNELS_POS_REQUEST) != 0; | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | /* When a module command is called in order to obtain the position of | ||
| 1068 | * channels, since it was flagged as "getchannels-api" during the | ||
| 1069 | * registration, the command implementation checks for this special call | ||
| 1070 | * using the RedisModule_IsChannelsPositionRequest() API and uses this | ||
| 1071 | * function in order to report the channels. | ||
| 1072 | * | ||
| 1073 | * The supported flags are: | ||
| 1074 | * * REDISMODULE_CMD_CHANNEL_SUBSCRIBE: This command will subscribe to the channel. | ||
| 1075 | * * REDISMODULE_CMD_CHANNEL_UNSUBSCRIBE: This command will unsubscribe from this channel. | ||
| 1076 | * * REDISMODULE_CMD_CHANNEL_PUBLISH: This command will publish to this channel. | ||
| 1077 | * * REDISMODULE_CMD_CHANNEL_PATTERN: Instead of acting on a specific channel, will act on any | ||
| 1078 | * channel specified by the pattern. This is the same access | ||
| 1079 | * used by the PSUBSCRIBE and PUNSUBSCRIBE commands available | ||
| 1080 | * in Redis. Not intended to be used with PUBLISH permissions. | ||
| 1081 | * | ||
| 1082 | * The following is an example of how it could be used: | ||
| 1083 | * | ||
| 1084 | * if (RedisModule_IsChannelsPositionRequest(ctx)) { | ||
| 1085 | * RedisModule_ChannelAtPosWithFlags(ctx, 1, REDISMODULE_CMD_CHANNEL_SUBSCRIBE | REDISMODULE_CMD_CHANNEL_PATTERN); | ||
| 1086 | * RedisModule_ChannelAtPosWithFlags(ctx, 1, REDISMODULE_CMD_CHANNEL_PUBLISH); | ||
| 1087 | * } | ||
| 1088 | * | ||
| 1089 | * Note: One usage of declaring channels is for evaluating ACL permissions. In this context, | ||
| 1090 | * unsubscribing is always allowed, so commands will only be checked against subscribe and | ||
| 1091 | * publish permissions. This is preferred over using RM_ACLCheckChannelPermissions, since | ||
| 1092 | * it allows the ACLs to be checked before the command is executed. */ | ||
| 1093 | void RM_ChannelAtPosWithFlags(RedisModuleCtx *ctx, int pos, int flags) { | ||
| 1094 | if (!(ctx->flags & REDISMODULE_CTX_CHANNELS_POS_REQUEST) || !ctx->keys_result) return; | ||
| 1095 | if (pos <= 0) return; | ||
| 1096 | |||
| 1097 | getKeysResult *res = ctx->keys_result; | ||
| 1098 | |||
| 1099 | /* Check overflow */ | ||
| 1100 | if (res->numkeys == res->size) { | ||
| 1101 | int newsize = res->size + (res->size > 8192 ? 8192 : res->size); | ||
| 1102 | getKeysPrepareResult(res, newsize); | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | int new_flags = 0; | ||
| 1106 | if (flags & REDISMODULE_CMD_CHANNEL_SUBSCRIBE) new_flags |= CMD_CHANNEL_SUBSCRIBE; | ||
| 1107 | if (flags & REDISMODULE_CMD_CHANNEL_UNSUBSCRIBE) new_flags |= CMD_CHANNEL_UNSUBSCRIBE; | ||
| 1108 | if (flags & REDISMODULE_CMD_CHANNEL_PUBLISH) new_flags |= CMD_CHANNEL_PUBLISH; | ||
| 1109 | if (flags & REDISMODULE_CMD_CHANNEL_PATTERN) new_flags |= CMD_CHANNEL_PATTERN; | ||
| 1110 | |||
| 1111 | res->keys[res->numkeys].pos = pos; | ||
| 1112 | res->keys[res->numkeys].flags = new_flags; | ||
| 1113 | res->numkeys++; | ||
| 1114 | } | ||
| 1115 | |||
| 1116 | /* Returns 1 if name is valid, otherwise returns 0. | ||
| 1117 | * | ||
| 1118 | * We want to block some chars in module command names that we know can | ||
| 1119 | * mess things up. | ||
| 1120 | * | ||
| 1121 | * There are these characters: | ||
| 1122 | * ' ' (space) - issues with old inline protocol. | ||
| 1123 | * '\r', '\n' (newline) - can mess up the protocol on acl error replies. | ||
| 1124 | * '|' - sub-commands. | ||
| 1125 | * '@' - ACL categories. | ||
| 1126 | * '=', ',' - info and client list fields (':' handled by getSafeInfoString). | ||
| 1127 | * */ | ||
| 1128 | int isCommandNameValid(const char *name) { | ||
| 1129 | const char *block_chars = " \r\n|@=,"; | ||
| 1130 | |||
| 1131 | if (strpbrk(name, block_chars)) | ||
| 1132 | return 0; | ||
| 1133 | return 1; | ||
| 1134 | } | ||
| 1135 | |||
| 1136 | /* Helper for RM_CreateCommand(). Turns a string representing command | ||
| 1137 | * flags into the command flags used by the Redis core. | ||
| 1138 | * | ||
| 1139 | * It returns the set of flags, or -1 if unknown flags are found. */ | ||
| 1140 | int64_t commandFlagsFromString(char *s) { | ||
| 1141 | int count, j; | ||
| 1142 | int64_t flags = 0; | ||
| 1143 | sds *tokens = sdssplitlen(s,strlen(s)," ",1,&count); | ||
| 1144 | for (j = 0; j < count; j++) { | ||
| 1145 | char *t = tokens[j]; | ||
| 1146 | if (!strcasecmp(t,"write")) flags |= CMD_WRITE; | ||
| 1147 | else if (!strcasecmp(t,"readonly")) flags |= CMD_READONLY; | ||
| 1148 | else if (!strcasecmp(t,"admin")) flags |= CMD_ADMIN; | ||
| 1149 | else if (!strcasecmp(t,"deny-oom")) flags |= CMD_DENYOOM; | ||
| 1150 | else if (!strcasecmp(t,"deny-script")) flags |= CMD_NOSCRIPT; | ||
| 1151 | else if (!strcasecmp(t,"allow-loading")) flags |= CMD_LOADING; | ||
| 1152 | else if (!strcasecmp(t,"pubsub")) flags |= CMD_PUBSUB; | ||
| 1153 | else if (!strcasecmp(t,"random")) { /* Deprecated. Silently ignore. */ } | ||
| 1154 | else if (!strcasecmp(t,"blocking")) flags |= CMD_BLOCKING; | ||
| 1155 | else if (!strcasecmp(t,"allow-stale")) flags |= CMD_STALE; | ||
| 1156 | else if (!strcasecmp(t,"no-monitor")) flags |= CMD_SKIP_MONITOR; | ||
| 1157 | else if (!strcasecmp(t,"no-slowlog")) flags |= CMD_SKIP_SLOWLOG; | ||
| 1158 | else if (!strcasecmp(t,"fast")) flags |= CMD_FAST; | ||
| 1159 | else if (!strcasecmp(t,"no-auth")) flags |= CMD_NO_AUTH; | ||
| 1160 | else if (!strcasecmp(t,"may-replicate")) flags |= CMD_MAY_REPLICATE; | ||
| 1161 | else if (!strcasecmp(t,"getkeys-api")) flags |= CMD_MODULE_GETKEYS; | ||
| 1162 | else if (!strcasecmp(t,"getchannels-api")) flags |= CMD_MODULE_GETCHANNELS; | ||
| 1163 | else if (!strcasecmp(t,"no-cluster")) flags |= CMD_MODULE_NO_CLUSTER; | ||
| 1164 | else if (!strcasecmp(t,"no-mandatory-keys")) flags |= CMD_NO_MANDATORY_KEYS; | ||
| 1165 | else if (!strcasecmp(t,"allow-busy")) flags |= CMD_ALLOW_BUSY; | ||
| 1166 | else if (!strcasecmp(t,"internal")) flags |= (CMD_INTERNAL|CMD_NOSCRIPT); /* We also disallow internal commands in scripts. */ | ||
| 1167 | else if (!strcasecmp(t,"touches-arbitrary-keys")) flags |= CMD_TOUCHES_ARBITRARY_KEYS; | ||
| 1168 | else break; | ||
| 1169 | } | ||
| 1170 | sdsfreesplitres(tokens,count); | ||
| 1171 | if (j != count) return -1; /* Some token not processed correctly. */ | ||
| 1172 | return flags; | ||
| 1173 | } | ||
| 1174 | |||
| 1175 | RedisModuleCommand *moduleCreateCommandProxy(struct RedisModule *module, sds declared_name, sds fullname, RedisModuleCmdFunc cmdfunc, int64_t flags, int firstkey, int lastkey, int keystep); | ||
| 1176 | |||
| 1177 | /* Register a new command in the Redis server, that will be handled by | ||
| 1178 | * calling the function pointer 'cmdfunc' using the RedisModule calling | ||
| 1179 | * convention. | ||
| 1180 | * | ||
| 1181 | * The function returns REDISMODULE_ERR in these cases: | ||
| 1182 | * - If creation of module command is called outside the RedisModule_OnLoad. | ||
| 1183 | * - The specified command is already busy. | ||
| 1184 | * - The command name contains some chars that are not allowed. | ||
| 1185 | * - A set of invalid flags were passed. | ||
| 1186 | * | ||
| 1187 | * Otherwise REDISMODULE_OK is returned and the new command is registered. | ||
| 1188 | * | ||
| 1189 | * This function must be called during the initialization of the module | ||
| 1190 | * inside the RedisModule_OnLoad() function. Calling this function outside | ||
| 1191 | * of the initialization function is not defined. | ||
| 1192 | * | ||
| 1193 | * The command function type is the following: | ||
| 1194 | * | ||
| 1195 | * int MyCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc); | ||
| 1196 | * | ||
| 1197 | * And is supposed to always return REDISMODULE_OK. | ||
| 1198 | * | ||
| 1199 | * The set of flags 'strflags' specify the behavior of the command, and should | ||
| 1200 | * be passed as a C string composed of space separated words, like for | ||
| 1201 | * example "write deny-oom". The set of flags are: | ||
| 1202 | * | ||
| 1203 | * * **"write"**: The command may modify the data set (it may also read | ||
| 1204 | * from it). | ||
| 1205 | * * **"readonly"**: The command returns data from keys but never writes. | ||
| 1206 | * * **"admin"**: The command is an administrative command (may change | ||
| 1207 | * replication or perform similar tasks). | ||
| 1208 | * * **"deny-oom"**: The command may use additional memory and should be | ||
| 1209 | * denied during out of memory conditions. | ||
| 1210 | * * **"deny-script"**: Don't allow this command in Lua scripts. | ||
| 1211 | * * **"allow-loading"**: Allow this command while the server is loading data. | ||
| 1212 | * Only commands not interacting with the data set | ||
| 1213 | * should be allowed to run in this mode. If not sure | ||
| 1214 | * don't use this flag. | ||
| 1215 | * * **"pubsub"**: The command publishes things on Pub/Sub channels. | ||
| 1216 | * * **"random"**: The command may have different outputs even starting | ||
| 1217 | * from the same input arguments and key values. | ||
| 1218 | * Starting from Redis 7.0 this flag has been deprecated. | ||
| 1219 | * Declaring a command as "random" can be done using | ||
| 1220 | * command tips, see https://redis.io/docs/latest/develop/reference/command-tips/. | ||
| 1221 | * * **"allow-stale"**: The command is allowed to run on slaves that don't | ||
| 1222 | * serve stale data. Don't use if you don't know what | ||
| 1223 | * this means. | ||
| 1224 | * * **"no-monitor"**: Don't propagate the command on monitor. Use this if | ||
| 1225 | * the command has sensitive data among the arguments. | ||
| 1226 | * * **"no-slowlog"**: Don't log this command in the slowlog. Use this if | ||
| 1227 | * the command has sensitive data among the arguments. | ||
| 1228 | * * **"fast"**: The command time complexity is not greater | ||
| 1229 | * than O(log(N)) where N is the size of the collection or | ||
| 1230 | * anything else representing the normal scalability | ||
| 1231 | * issue with the command. | ||
| 1232 | * * **"getkeys-api"**: The command implements the interface to return | ||
| 1233 | * the arguments that are keys. Used when start/stop/step | ||
| 1234 | * is not enough because of the command syntax. | ||
| 1235 | * * **"no-cluster"**: The command should not register in Redis Cluster | ||
| 1236 | * since is not designed to work with it because, for | ||
| 1237 | * example, is unable to report the position of the | ||
| 1238 | * keys, programmatically creates key names, or any | ||
| 1239 | * other reason. | ||
| 1240 | * * **"no-auth"**: This command can be run by an un-authenticated client. | ||
| 1241 | * Normally this is used by a command that is used | ||
| 1242 | * to authenticate a client. | ||
| 1243 | * * **"may-replicate"**: This command may generate replication traffic, even | ||
| 1244 | * though it's not a write command. | ||
| 1245 | * * **"no-mandatory-keys"**: All the keys this command may take are optional | ||
| 1246 | * * **"blocking"**: The command has the potential to block the client. | ||
| 1247 | * * **"allow-busy"**: Permit the command while the server is blocked either by | ||
| 1248 | * a script or by a slow module command, see | ||
| 1249 | * RM_Yield. | ||
| 1250 | * * **"getchannels-api"**: The command implements the interface to return | ||
| 1251 | * the arguments that are channels. | ||
| 1252 | * * **"internal"**: Internal command, one that should not be exposed to the user connections. | ||
| 1253 | * For example, module commands that are called by the modules, | ||
| 1254 | * commands that do not perform ACL validations (relying on earlier checks) | ||
| 1255 | * * **"touches-arbitrary-keys"**: This command may modify arbitrary keys (i.e. not provided via argv). | ||
| 1256 | * This flag is used so we don't wrap the replicated commands with MULTI/EXEC. | ||
| 1257 | * | ||
| 1258 | * The last three parameters specify which arguments of the new command are | ||
| 1259 | * Redis keys. See https://redis.io/commands/command for more information. | ||
| 1260 | * | ||
| 1261 | * * `firstkey`: One-based index of the first argument that's a key. | ||
| 1262 | * Position 0 is always the command name itself. | ||
| 1263 | * 0 for commands with no keys. | ||
| 1264 | * * `lastkey`: One-based index of the last argument that's a key. | ||
| 1265 | * Negative numbers refer to counting backwards from the last | ||
| 1266 | * argument (-1 means the last argument provided) | ||
| 1267 | * 0 for commands with no keys. | ||
| 1268 | * * `keystep`: Step between first and last key indexes. | ||
| 1269 | * 0 for commands with no keys. | ||
| 1270 | * | ||
| 1271 | * This information is used by ACL, Cluster and the `COMMAND` command. | ||
| 1272 | * | ||
| 1273 | * NOTE: The scheme described above serves a limited purpose and can | ||
| 1274 | * only be used to find keys that exist at constant indices. | ||
| 1275 | * For non-trivial key arguments, you may pass 0,0,0 and use | ||
| 1276 | * RedisModule_SetCommandInfo to set key specs using a more advanced scheme and use | ||
| 1277 | * RedisModule_SetCommandACLCategories to set Redis ACL categories of the commands. */ | ||
| 1278 | int RM_CreateCommand(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep) { | ||
| 1279 | if (!ctx->module->onload) | ||
| 1280 | return REDISMODULE_ERR; | ||
| 1281 | int64_t flags = strflags ? commandFlagsFromString((char*)strflags) : 0; | ||
| 1282 | if (flags == -1) return REDISMODULE_ERR; | ||
| 1283 | if ((flags & CMD_MODULE_NO_CLUSTER) && server.cluster_enabled) | ||
| 1284 | return REDISMODULE_ERR; | ||
| 1285 | |||
| 1286 | /* We will encounter an error as above if cluster is enable */ | ||
| 1287 | if (flags & CMD_MODULE_NO_CLUSTER) | ||
| 1288 | server.stat_cluster_incompatible_ops++; | ||
| 1289 | |||
| 1290 | /* Check if the command name is valid. */ | ||
| 1291 | if (!isCommandNameValid(name)) | ||
| 1292 | return REDISMODULE_ERR; | ||
| 1293 | |||
| 1294 | /* Check if the command name is busy. */ | ||
| 1295 | if (lookupCommandByCString(name) != NULL) | ||
| 1296 | return REDISMODULE_ERR; | ||
| 1297 | |||
| 1298 | sds declared_name = sdsnew(name); | ||
| 1299 | RedisModuleCommand *cp = moduleCreateCommandProxy(ctx->module, declared_name, sdsdup(declared_name), cmdfunc, flags, firstkey, lastkey, keystep); | ||
| 1300 | cp->rediscmd->arity = cmdfunc ? -1 : -2; /* Default value, can be changed later via dedicated API */ | ||
| 1301 | |||
| 1302 | pauseAllIOThreads(); | ||
| 1303 | serverAssert(dictAdd(server.commands, sdsdup(declared_name), cp->rediscmd) == DICT_OK); | ||
| 1304 | serverAssert(dictAdd(server.orig_commands, sdsdup(declared_name), cp->rediscmd) == DICT_OK); | ||
| 1305 | resumeAllIOThreads(); | ||
| 1306 | |||
| 1307 | cp->rediscmd->id = ACLGetCommandID(declared_name); /* ID used for ACL. */ | ||
| 1308 | return REDISMODULE_OK; | ||
| 1309 | } | ||
| 1310 | |||
| 1311 | /* A proxy that help create a module command / subcommand. | ||
| 1312 | * | ||
| 1313 | * 'declared_name': it contains the sub_name, which is just the fullname for non-subcommands. | ||
| 1314 | * 'fullname': sds string representing the command fullname. | ||
| 1315 | * | ||
| 1316 | * Function will take the ownership of both 'declared_name' and 'fullname' SDS. | ||
| 1317 | */ | ||
| 1318 | RedisModuleCommand *moduleCreateCommandProxy(struct RedisModule *module, sds declared_name, sds fullname, RedisModuleCmdFunc cmdfunc, int64_t flags, int firstkey, int lastkey, int keystep) { | ||
| 1319 | struct redisCommand *rediscmd; | ||
| 1320 | RedisModuleCommand *cp; | ||
| 1321 | |||
| 1322 | /* Create a command "proxy", which is a structure that is referenced | ||
| 1323 | * in the command table, so that the generic command that works as | ||
| 1324 | * binding between modules and Redis, can know what function to call | ||
| 1325 | * and what the module is. */ | ||
| 1326 | cp = zcalloc(sizeof(*cp)); | ||
| 1327 | cp->module = module; | ||
| 1328 | cp->func = cmdfunc; | ||
| 1329 | cp->rediscmd = zcalloc(sizeof(*rediscmd)); | ||
| 1330 | cp->rediscmd->declared_name = declared_name; /* SDS for module commands */ | ||
| 1331 | cp->rediscmd->fullname = fullname; | ||
| 1332 | cp->rediscmd->group = COMMAND_GROUP_MODULE; | ||
| 1333 | cp->rediscmd->proc = RedisModuleCommandDispatcher; | ||
| 1334 | cp->rediscmd->flags = flags | CMD_MODULE; | ||
| 1335 | cp->rediscmd->module_cmd = cp; | ||
| 1336 | if (firstkey != 0) { | ||
| 1337 | cp->rediscmd->key_specs_num = 1; | ||
| 1338 | cp->rediscmd->key_specs = zcalloc(sizeof(keySpec)); | ||
| 1339 | cp->rediscmd->key_specs[0].flags = CMD_KEY_FULL_ACCESS; | ||
| 1340 | if (flags & CMD_MODULE_GETKEYS) | ||
| 1341 | cp->rediscmd->key_specs[0].flags |= CMD_KEY_VARIABLE_FLAGS; | ||
| 1342 | cp->rediscmd->key_specs[0].begin_search_type = KSPEC_BS_INDEX; | ||
| 1343 | cp->rediscmd->key_specs[0].bs.index.pos = firstkey; | ||
| 1344 | cp->rediscmd->key_specs[0].find_keys_type = KSPEC_FK_RANGE; | ||
| 1345 | cp->rediscmd->key_specs[0].fk.range.lastkey = lastkey < 0 ? lastkey : (lastkey-firstkey); | ||
| 1346 | cp->rediscmd->key_specs[0].fk.range.keystep = keystep; | ||
| 1347 | cp->rediscmd->key_specs[0].fk.range.limit = 0; | ||
| 1348 | } else { | ||
| 1349 | cp->rediscmd->key_specs_num = 0; | ||
| 1350 | cp->rediscmd->key_specs = NULL; | ||
| 1351 | } | ||
| 1352 | populateCommandLegacyRangeSpec(cp->rediscmd); | ||
| 1353 | cp->rediscmd->microseconds = 0; | ||
| 1354 | cp->rediscmd->calls = 0; | ||
| 1355 | cp->rediscmd->rejected_calls = 0; | ||
| 1356 | cp->rediscmd->failed_calls = 0; | ||
| 1357 | return cp; | ||
| 1358 | } | ||
| 1359 | |||
| 1360 | /* Get an opaque structure, representing a module command, by command name. | ||
| 1361 | * This structure is used in some of the command-related APIs. | ||
| 1362 | * | ||
| 1363 | * NULL is returned in case of the following errors: | ||
| 1364 | * | ||
| 1365 | * * Command not found | ||
| 1366 | * * The command is not a module command | ||
| 1367 | * * The command doesn't belong to the calling module | ||
| 1368 | */ | ||
| 1369 | RedisModuleCommand *RM_GetCommand(RedisModuleCtx *ctx, const char *name) { | ||
| 1370 | struct redisCommand *cmd = lookupCommandByCString(name); | ||
| 1371 | |||
| 1372 | if (!cmd || !(cmd->flags & CMD_MODULE)) | ||
| 1373 | return NULL; | ||
| 1374 | |||
| 1375 | RedisModuleCommand *cp = cmd->module_cmd; | ||
| 1376 | if (cp->module != ctx->module) | ||
| 1377 | return NULL; | ||
| 1378 | |||
| 1379 | return cp; | ||
| 1380 | } | ||
| 1381 | |||
| 1382 | /* Very similar to RedisModule_CreateCommand except that it is used to create | ||
| 1383 | * a subcommand, associated with another, container, command. | ||
| 1384 | * | ||
| 1385 | * Example: If a module has a configuration command, MODULE.CONFIG, then | ||
| 1386 | * GET and SET should be individual subcommands, while MODULE.CONFIG is | ||
| 1387 | * a command, but should not be registered with a valid `funcptr`: | ||
| 1388 | * | ||
| 1389 | * if (RedisModule_CreateCommand(ctx,"module.config",NULL,"",0,0,0) == REDISMODULE_ERR) | ||
| 1390 | * return REDISMODULE_ERR; | ||
| 1391 | * | ||
| 1392 | * RedisModuleCommand *parent = RedisModule_GetCommand(ctx,,"module.config"); | ||
| 1393 | * | ||
| 1394 | * if (RedisModule_CreateSubcommand(parent,"set",cmd_config_set,"",0,0,0) == REDISMODULE_ERR) | ||
| 1395 | * return REDISMODULE_ERR; | ||
| 1396 | * | ||
| 1397 | * if (RedisModule_CreateSubcommand(parent,"get",cmd_config_get,"",0,0,0) == REDISMODULE_ERR) | ||
| 1398 | * return REDISMODULE_ERR; | ||
| 1399 | * | ||
| 1400 | * Returns REDISMODULE_OK on success and REDISMODULE_ERR in case of the following errors: | ||
| 1401 | * | ||
| 1402 | * * Error while parsing `strflags` | ||
| 1403 | * * Command is marked as `no-cluster` but cluster mode is enabled | ||
| 1404 | * * `parent` is already a subcommand (we do not allow more than one level of command nesting) | ||
| 1405 | * * `parent` is a command with an implementation (RedisModuleCmdFunc) (A parent command should be a pure container of subcommands) | ||
| 1406 | * * `parent` already has a subcommand called `name` | ||
| 1407 | * * Creating a subcommand is called outside of RedisModule_OnLoad. | ||
| 1408 | */ | ||
| 1409 | int RM_CreateSubcommand(RedisModuleCommand *parent, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep) { | ||
| 1410 | if (!parent->module->onload) | ||
| 1411 | return REDISMODULE_ERR; | ||
| 1412 | int64_t flags = strflags ? commandFlagsFromString((char*)strflags) : 0; | ||
| 1413 | if (flags == -1) return REDISMODULE_ERR; | ||
| 1414 | if ((flags & CMD_MODULE_NO_CLUSTER) && server.cluster_enabled) | ||
| 1415 | return REDISMODULE_ERR; | ||
| 1416 | |||
| 1417 | /* We will encounter an error as above if cluster is enable */ | ||
| 1418 | if (flags & CMD_MODULE_NO_CLUSTER) | ||
| 1419 | server.stat_cluster_incompatible_ops++; | ||
| 1420 | |||
| 1421 | struct redisCommand *parent_cmd = parent->rediscmd; | ||
| 1422 | |||
| 1423 | if (parent_cmd->parent) | ||
| 1424 | return REDISMODULE_ERR; /* We don't allow more than one level of subcommands */ | ||
| 1425 | |||
| 1426 | RedisModuleCommand *parent_cp = parent_cmd->module_cmd; | ||
| 1427 | if (parent_cp->func) | ||
| 1428 | return REDISMODULE_ERR; /* A parent command should be a pure container of subcommands */ | ||
| 1429 | |||
| 1430 | /* Check if the command name is valid. */ | ||
| 1431 | if (!isCommandNameValid(name)) | ||
| 1432 | return REDISMODULE_ERR; | ||
| 1433 | |||
| 1434 | /* Check if the command name is busy within the parent command. */ | ||
| 1435 | sds declared_name = sdsnew(name); | ||
| 1436 | if (parent_cmd->subcommands_dict && lookupSubcommand(parent_cmd, declared_name) != NULL) { | ||
| 1437 | sdsfree(declared_name); | ||
| 1438 | return REDISMODULE_ERR; | ||
| 1439 | } | ||
| 1440 | |||
| 1441 | sds fullname = catSubCommandFullname(parent_cmd->fullname, name); | ||
| 1442 | RedisModuleCommand *cp = moduleCreateCommandProxy(parent->module, declared_name, fullname, cmdfunc, flags, firstkey, lastkey, keystep); | ||
| 1443 | cp->rediscmd->arity = -2; | ||
| 1444 | |||
| 1445 | commandAddSubcommand(parent_cmd, cp->rediscmd, name); | ||
| 1446 | return REDISMODULE_OK; | ||
| 1447 | } | ||
| 1448 | |||
| 1449 | /* Accessors of array elements of structs where the element size is stored | ||
| 1450 | * separately in the version struct. */ | ||
| 1451 | static RedisModuleCommandHistoryEntry * | ||
| 1452 | moduleCmdHistoryEntryAt(const RedisModuleCommandInfoVersion *version, | ||
| 1453 | RedisModuleCommandHistoryEntry *entries, int index) { | ||
| 1454 | off_t offset = index * version->sizeof_historyentry; | ||
| 1455 | return (RedisModuleCommandHistoryEntry *)((char *)(entries) + offset); | ||
| 1456 | } | ||
| 1457 | static RedisModuleCommandKeySpec * | ||
| 1458 | moduleCmdKeySpecAt(const RedisModuleCommandInfoVersion *version, | ||
| 1459 | RedisModuleCommandKeySpec *keyspecs, int index) { | ||
| 1460 | off_t offset = index * version->sizeof_keyspec; | ||
| 1461 | return (RedisModuleCommandKeySpec *)((char *)(keyspecs) + offset); | ||
| 1462 | } | ||
| 1463 | static RedisModuleCommandArg * | ||
| 1464 | moduleCmdArgAt(const RedisModuleCommandInfoVersion *version, | ||
| 1465 | const RedisModuleCommandArg *args, int index) { | ||
| 1466 | off_t offset = index * version->sizeof_arg; | ||
| 1467 | return (RedisModuleCommandArg *)((char *)(args) + offset); | ||
| 1468 | } | ||
| 1469 | |||
| 1470 | /* Recursively populate the args structure (setting num_args to the number of | ||
| 1471 | * subargs) and return the number of args. */ | ||
| 1472 | int populateArgsStructure(struct redisCommandArg *args) { | ||
| 1473 | if (!args) | ||
| 1474 | return 0; | ||
| 1475 | int count = 0; | ||
| 1476 | while (args->name) { | ||
| 1477 | serverAssert(count < INT_MAX); | ||
| 1478 | args->num_args = populateArgsStructure(args->subargs); | ||
| 1479 | count++; | ||
| 1480 | args++; | ||
| 1481 | } | ||
| 1482 | return count; | ||
| 1483 | } | ||
| 1484 | |||
| 1485 | /* RedisModule_AddACLCategory can be used to add new ACL command categories. Category names | ||
| 1486 | * can only contain alphanumeric characters, underscores, or dashes. Categories can only be added | ||
| 1487 | * during the RedisModule_OnLoad function. Once a category has been added, it can not be removed. | ||
| 1488 | * Any module can register a command to any added categories using RedisModule_SetCommandACLCategories. | ||
| 1489 | * | ||
| 1490 | * Returns: | ||
| 1491 | * - REDISMODULE_OK on successfully adding the new ACL category. | ||
| 1492 | * - REDISMODULE_ERR on failure. | ||
| 1493 | * | ||
| 1494 | * On error the errno is set to: | ||
| 1495 | * - EINVAL if the name contains invalid characters. | ||
| 1496 | * - EBUSY if the category name already exists. | ||
| 1497 | * - ENOMEM if the number of categories reached the max limit of 64 categories. | ||
| 1498 | */ | ||
| 1499 | int RM_AddACLCategory(RedisModuleCtx *ctx, const char *name) { | ||
| 1500 | if (!ctx->module->onload) { | ||
| 1501 | errno = EINVAL; | ||
| 1502 | return REDISMODULE_ERR; | ||
| 1503 | } | ||
| 1504 | |||
| 1505 | if (moduleVerifyResourceName(name) == REDISMODULE_ERR) { | ||
| 1506 | errno = EINVAL; | ||
| 1507 | return REDISMODULE_ERR; | ||
| 1508 | } | ||
| 1509 | |||
| 1510 | if (ACLGetCommandCategoryFlagByName(name)) { | ||
| 1511 | errno = EBUSY; | ||
| 1512 | return REDISMODULE_ERR; | ||
| 1513 | } | ||
| 1514 | |||
| 1515 | if (ACLAddCommandCategory(name, 0)) { | ||
| 1516 | ctx->module->num_acl_categories_added++; | ||
| 1517 | return REDISMODULE_OK; | ||
| 1518 | } else { | ||
| 1519 | errno = ENOMEM; | ||
| 1520 | return REDISMODULE_ERR; | ||
| 1521 | } | ||
| 1522 | } | ||
| 1523 | |||
| 1524 | /* Helper for categoryFlagsFromString(). Attempts to find an acl flag representing the provided flag string | ||
| 1525 | * and adds that flag to acl_categories_flags if a match is found. | ||
| 1526 | * | ||
| 1527 | * Returns '1' if acl category flag is recognized or | ||
| 1528 | * returns '0' if not recognized */ | ||
| 1529 | int matchAclCategoryFlag(char *flag, int64_t *acl_categories_flags) { | ||
| 1530 | uint64_t this_flag = ACLGetCommandCategoryFlagByName(flag); | ||
| 1531 | if (this_flag) { | ||
| 1532 | *acl_categories_flags |= (int64_t) this_flag; | ||
| 1533 | return 1; | ||
| 1534 | } | ||
| 1535 | return 0; /* Unrecognized */ | ||
| 1536 | } | ||
| 1537 | |||
| 1538 | /* Helper for RM_SetCommandACLCategories(). Turns a string representing acl category | ||
| 1539 | * flags into the acl category flags used by Redis ACL which allows users to access | ||
| 1540 | * the module commands by acl categories. | ||
| 1541 | * | ||
| 1542 | * It returns the set of acl flags, or -1 if unknown flags are found. */ | ||
| 1543 | int64_t categoryFlagsFromString(char *aclflags) { | ||
| 1544 | int count, j; | ||
| 1545 | int64_t acl_categories_flags = 0; | ||
| 1546 | sds *tokens = sdssplitlen(aclflags,strlen(aclflags)," ",1,&count); | ||
| 1547 | for (j = 0; j < count; j++) { | ||
| 1548 | char *t = tokens[j]; | ||
| 1549 | if (!matchAclCategoryFlag(t, &acl_categories_flags)) { | ||
| 1550 | serverLog(LL_WARNING,"Unrecognized categories flag %s on module load", t); | ||
| 1551 | break; | ||
| 1552 | } | ||
| 1553 | } | ||
| 1554 | sdsfreesplitres(tokens,count); | ||
| 1555 | if (j != count) return -1; /* Some token not processed correctly. */ | ||
| 1556 | return acl_categories_flags; | ||
| 1557 | } | ||
| 1558 | |||
| 1559 | /* RedisModule_SetCommandACLCategories can be used to set ACL categories to module | ||
| 1560 | * commands and subcommands. The set of ACL categories should be passed as | ||
| 1561 | * a space separated C string 'aclflags'. | ||
| 1562 | * | ||
| 1563 | * Example, the acl flags 'write slow' marks the command as part of the write and | ||
| 1564 | * slow ACL categories. | ||
| 1565 | * | ||
| 1566 | * On success REDISMODULE_OK is returned. On error REDISMODULE_ERR is returned. | ||
| 1567 | * | ||
| 1568 | * This function can only be called during the RedisModule_OnLoad function. If called | ||
| 1569 | * outside of this function, an error is returned. | ||
| 1570 | */ | ||
| 1571 | int RM_SetCommandACLCategories(RedisModuleCommand *command, const char *aclflags) { | ||
| 1572 | if (!command || !command->module || !command->module->onload) return REDISMODULE_ERR; | ||
| 1573 | int64_t categories_flags = aclflags ? categoryFlagsFromString((char*)aclflags) : 0; | ||
| 1574 | if (categories_flags == -1) return REDISMODULE_ERR; | ||
| 1575 | struct redisCommand *rcmd = command->rediscmd; | ||
| 1576 | rcmd->acl_categories = categories_flags; /* ACL categories flags for module command */ | ||
| 1577 | command->module->num_commands_with_acl_categories++; | ||
| 1578 | return REDISMODULE_OK; | ||
| 1579 | } | ||
| 1580 | |||
| 1581 | /* Set additional command information. | ||
| 1582 | * | ||
| 1583 | * Affects the output of `COMMAND`, `COMMAND INFO` and `COMMAND DOCS`, Cluster, | ||
| 1584 | * ACL and is used to filter commands with the wrong number of arguments before | ||
| 1585 | * the call reaches the module code. | ||
| 1586 | * | ||
| 1587 | * This function can be called after creating a command using RM_CreateCommand | ||
| 1588 | * and fetching the command pointer using RM_GetCommand. The information can | ||
| 1589 | * only be set once for each command and has the following structure: | ||
| 1590 | * | ||
| 1591 | * typedef struct RedisModuleCommandInfo { | ||
| 1592 | * const RedisModuleCommandInfoVersion *version; | ||
| 1593 | * const char *summary; | ||
| 1594 | * const char *complexity; | ||
| 1595 | * const char *since; | ||
| 1596 | * RedisModuleCommandHistoryEntry *history; | ||
| 1597 | * const char *tips; | ||
| 1598 | * int arity; | ||
| 1599 | * RedisModuleCommandKeySpec *key_specs; | ||
| 1600 | * RedisModuleCommandArg *args; | ||
| 1601 | * } RedisModuleCommandInfo; | ||
| 1602 | * | ||
| 1603 | * All fields except `version` are optional. Explanation of the fields: | ||
| 1604 | * | ||
| 1605 | * - `version`: This field enables compatibility with different Redis versions. | ||
| 1606 | * Always set this field to REDISMODULE_COMMAND_INFO_VERSION. | ||
| 1607 | * | ||
| 1608 | * - `summary`: A short description of the command (optional). | ||
| 1609 | * | ||
| 1610 | * - `complexity`: Complexity description (optional). | ||
| 1611 | * | ||
| 1612 | * - `since`: The version where the command was introduced (optional). | ||
| 1613 | * Note: The version specified should be the module's, not Redis version. | ||
| 1614 | * | ||
| 1615 | * - `history`: An array of RedisModuleCommandHistoryEntry (optional), which is | ||
| 1616 | * a struct with the following fields: | ||
| 1617 | * | ||
| 1618 | * const char *since; | ||
| 1619 | * const char *changes; | ||
| 1620 | * | ||
| 1621 | * `since` is a version string and `changes` is a string describing the | ||
| 1622 | * changes. The array is terminated by a zeroed entry, i.e. an entry with | ||
| 1623 | * both strings set to NULL. | ||
| 1624 | * | ||
| 1625 | * - `tips`: A string of space-separated tips regarding this command, meant for | ||
| 1626 | * clients and proxies. See https://redis.io/docs/latest/develop/reference/command-tips/. | ||
| 1627 | * | ||
| 1628 | * - `arity`: Number of arguments, including the command name itself. A positive | ||
| 1629 | * number specifies an exact number of arguments and a negative number | ||
| 1630 | * specifies a minimum number of arguments, so use -N to say >= N. Redis | ||
| 1631 | * validates a call before passing it to a module, so this can replace an | ||
| 1632 | * arity check inside the module command implementation. A value of 0 (or an | ||
| 1633 | * omitted arity field) is equivalent to -2 if the command has sub commands | ||
| 1634 | * and -1 otherwise. | ||
| 1635 | * | ||
| 1636 | * - `key_specs`: An array of RedisModuleCommandKeySpec, terminated by an | ||
| 1637 | * element memset to zero. This is a scheme that tries to describe the | ||
| 1638 | * positions of key arguments better than the old RM_CreateCommand arguments | ||
| 1639 | * `firstkey`, `lastkey`, `keystep` and is needed if those three are not | ||
| 1640 | * enough to describe the key positions. There are two steps to retrieve key | ||
| 1641 | * positions: *begin search* (BS) in which index should find the first key and | ||
| 1642 | * *find keys* (FK) which, relative to the output of BS, describes how can we | ||
| 1643 | * will which arguments are keys. Additionally, there are key specific flags. | ||
| 1644 | * | ||
| 1645 | * Key-specs cause the triplet (firstkey, lastkey, keystep) given in | ||
| 1646 | * RM_CreateCommand to be recomputed, but it is still useful to provide | ||
| 1647 | * these three parameters in RM_CreateCommand, to better support old Redis | ||
| 1648 | * versions where RM_SetCommandInfo is not available. | ||
| 1649 | * | ||
| 1650 | * Note that key-specs don't fully replace the "getkeys-api" (see | ||
| 1651 | * RM_CreateCommand, RM_IsKeysPositionRequest and RM_KeyAtPosWithFlags) so | ||
| 1652 | * it may be a good idea to supply both key-specs and implement the | ||
| 1653 | * getkeys-api. | ||
| 1654 | * | ||
| 1655 | * A key-spec has the following structure: | ||
| 1656 | * | ||
| 1657 | * typedef struct RedisModuleCommandKeySpec { | ||
| 1658 | * const char *notes; | ||
| 1659 | * uint64_t flags; | ||
| 1660 | * RedisModuleKeySpecBeginSearchType begin_search_type; | ||
| 1661 | * union { | ||
| 1662 | * struct { | ||
| 1663 | * int pos; | ||
| 1664 | * } index; | ||
| 1665 | * struct { | ||
| 1666 | * const char *keyword; | ||
| 1667 | * int startfrom; | ||
| 1668 | * } keyword; | ||
| 1669 | * } bs; | ||
| 1670 | * RedisModuleKeySpecFindKeysType find_keys_type; | ||
| 1671 | * union { | ||
| 1672 | * struct { | ||
| 1673 | * int lastkey; | ||
| 1674 | * int keystep; | ||
| 1675 | * int limit; | ||
| 1676 | * } range; | ||
| 1677 | * struct { | ||
| 1678 | * int keynumidx; | ||
| 1679 | * int firstkey; | ||
| 1680 | * int keystep; | ||
| 1681 | * } keynum; | ||
| 1682 | * } fk; | ||
| 1683 | * } RedisModuleCommandKeySpec; | ||
| 1684 | * | ||
| 1685 | * Explanation of the fields of RedisModuleCommandKeySpec: | ||
| 1686 | * | ||
| 1687 | * * `notes`: Optional notes or clarifications about this key spec. | ||
| 1688 | * | ||
| 1689 | * * `flags`: A bitwise or of key-spec flags described below. | ||
| 1690 | * | ||
| 1691 | * * `begin_search_type`: This describes how the first key is discovered. | ||
| 1692 | * There are two ways to determine the first key: | ||
| 1693 | * | ||
| 1694 | * * `REDISMODULE_KSPEC_BS_UNKNOWN`: There is no way to tell where the | ||
| 1695 | * key args start. | ||
| 1696 | * * `REDISMODULE_KSPEC_BS_INDEX`: Key args start at a constant index. | ||
| 1697 | * * `REDISMODULE_KSPEC_BS_KEYWORD`: Key args start just after a | ||
| 1698 | * specific keyword. | ||
| 1699 | * | ||
| 1700 | * * `bs`: This is a union in which the `index` or `keyword` branch is used | ||
| 1701 | * depending on the value of the `begin_search_type` field. | ||
| 1702 | * | ||
| 1703 | * * `bs.index.pos`: The index from which we start the search for keys. | ||
| 1704 | * (`REDISMODULE_KSPEC_BS_INDEX` only.) | ||
| 1705 | * | ||
| 1706 | * * `bs.keyword.keyword`: The keyword (string) that indicates the | ||
| 1707 | * beginning of key arguments. (`REDISMODULE_KSPEC_BS_KEYWORD` only.) | ||
| 1708 | * | ||
| 1709 | * * `bs.keyword.startfrom`: An index in argv from which to start | ||
| 1710 | * searching. Can be negative, which means start search from the end, | ||
| 1711 | * in reverse. Example: -2 means to start in reverse from the | ||
| 1712 | * penultimate argument. (`REDISMODULE_KSPEC_BS_KEYWORD` only.) | ||
| 1713 | * | ||
| 1714 | * * `find_keys_type`: After the "begin search", this describes which | ||
| 1715 | * arguments are keys. The strategies are: | ||
| 1716 | * | ||
| 1717 | * * `REDISMODULE_KSPEC_BS_UNKNOWN`: There is no way to tell where the | ||
| 1718 | * key args are located. | ||
| 1719 | * * `REDISMODULE_KSPEC_FK_RANGE`: Keys end at a specific index (or | ||
| 1720 | * relative to the last argument). | ||
| 1721 | * * `REDISMODULE_KSPEC_FK_KEYNUM`: There's an argument that contains | ||
| 1722 | * the number of key args somewhere before the keys themselves. | ||
| 1723 | * | ||
| 1724 | * `find_keys_type` and `fk` can be omitted if this keyspec describes | ||
| 1725 | * exactly one key. | ||
| 1726 | * | ||
| 1727 | * * `fk`: This is a union in which the `range` or `keynum` branch is used | ||
| 1728 | * depending on the value of the `find_keys_type` field. | ||
| 1729 | * | ||
| 1730 | * * `fk.range` (for `REDISMODULE_KSPEC_FK_RANGE`): A struct with the | ||
| 1731 | * following fields: | ||
| 1732 | * | ||
| 1733 | * * `lastkey`: Index of the last key relative to the result of the | ||
| 1734 | * begin search step. Can be negative, in which case it's not | ||
| 1735 | * relative. -1 indicates the last argument, -2 one before the | ||
| 1736 | * last and so on. | ||
| 1737 | * | ||
| 1738 | * * `keystep`: How many arguments should we skip after finding a | ||
| 1739 | * key, in order to find the next one? | ||
| 1740 | * | ||
| 1741 | * * `limit`: If `lastkey` is -1, we use `limit` to stop the search | ||
| 1742 | * by a factor. 0 and 1 mean no limit. 2 means 1/2 of the | ||
| 1743 | * remaining args, 3 means 1/3, and so on. | ||
| 1744 | * | ||
| 1745 | * * `fk.keynum` (for `REDISMODULE_KSPEC_FK_KEYNUM`): A struct with the | ||
| 1746 | * following fields: | ||
| 1747 | * | ||
| 1748 | * * `keynumidx`: Index of the argument containing the number of | ||
| 1749 | * keys to come, relative to the result of the begin search step. | ||
| 1750 | * | ||
| 1751 | * * `firstkey`: Index of the fist key relative to the result of the | ||
| 1752 | * begin search step. (Usually it's just after `keynumidx`, in | ||
| 1753 | * which case it should be set to `keynumidx + 1`.) | ||
| 1754 | * | ||
| 1755 | * * `keystep`: How many arguments should we skip after finding a | ||
| 1756 | * key, in order to find the next one? | ||
| 1757 | * | ||
| 1758 | * Key-spec flags: | ||
| 1759 | * | ||
| 1760 | * The first four refer to what the command actually does with the *value or | ||
| 1761 | * metadata of the key*, and not necessarily the user data or how it affects | ||
| 1762 | * it. Each key-spec may must have exactly one of these. Any operation | ||
| 1763 | * that's not distinctly deletion, overwrite or read-only would be marked as | ||
| 1764 | * RW. | ||
| 1765 | * | ||
| 1766 | * * `REDISMODULE_CMD_KEY_RO`: Read-Only. Reads the value of the key, but | ||
| 1767 | * doesn't necessarily return it. | ||
| 1768 | * | ||
| 1769 | * * `REDISMODULE_CMD_KEY_RW`: Read-Write. Modifies the data stored in the | ||
| 1770 | * value of the key or its metadata. | ||
| 1771 | * | ||
| 1772 | * * `REDISMODULE_CMD_KEY_OW`: Overwrite. Overwrites the data stored in the | ||
| 1773 | * value of the key. | ||
| 1774 | * | ||
| 1775 | * * `REDISMODULE_CMD_KEY_RM`: Deletes the key. | ||
| 1776 | * | ||
| 1777 | * The next four refer to *user data inside the value of the key*, not the | ||
| 1778 | * metadata like LRU, type, cardinality. It refers to the logical operation | ||
| 1779 | * on the user's data (actual input strings or TTL), being | ||
| 1780 | * used/returned/copied/changed. It doesn't refer to modification or | ||
| 1781 | * returning of metadata (like type, count, presence of data). ACCESS can be | ||
| 1782 | * combined with one of the write operations INSERT, DELETE or UPDATE. Any | ||
| 1783 | * write that's not an INSERT or a DELETE would be UPDATE. | ||
| 1784 | * | ||
| 1785 | * * `REDISMODULE_CMD_KEY_ACCESS`: Returns, copies or uses the user data | ||
| 1786 | * from the value of the key. | ||
| 1787 | * | ||
| 1788 | * * `REDISMODULE_CMD_KEY_UPDATE`: Updates data to the value, new value may | ||
| 1789 | * depend on the old value. | ||
| 1790 | * | ||
| 1791 | * * `REDISMODULE_CMD_KEY_INSERT`: Adds data to the value with no chance of | ||
| 1792 | * modification or deletion of existing data. | ||
| 1793 | * | ||
| 1794 | * * `REDISMODULE_CMD_KEY_DELETE`: Explicitly deletes some content from the | ||
| 1795 | * value of the key. | ||
| 1796 | * | ||
| 1797 | * Other flags: | ||
| 1798 | * | ||
| 1799 | * * `REDISMODULE_CMD_KEY_NOT_KEY`: The key is not actually a key, but | ||
| 1800 | * should be routed in cluster mode as if it was a key. | ||
| 1801 | * | ||
| 1802 | * * `REDISMODULE_CMD_KEY_INCOMPLETE`: The keyspec might not point out all | ||
| 1803 | * the keys it should cover. | ||
| 1804 | * | ||
| 1805 | * * `REDISMODULE_CMD_KEY_VARIABLE_FLAGS`: Some keys might have different | ||
| 1806 | * flags depending on arguments. | ||
| 1807 | * | ||
| 1808 | * - `args`: An array of RedisModuleCommandArg, terminated by an element memset | ||
| 1809 | * to zero. RedisModuleCommandArg is a structure with at the fields described | ||
| 1810 | * below. | ||
| 1811 | * | ||
| 1812 | * typedef struct RedisModuleCommandArg { | ||
| 1813 | * const char *name; | ||
| 1814 | * RedisModuleCommandArgType type; | ||
| 1815 | * int key_spec_index; | ||
| 1816 | * const char *token; | ||
| 1817 | * const char *summary; | ||
| 1818 | * const char *since; | ||
| 1819 | * int flags; | ||
| 1820 | * struct RedisModuleCommandArg *subargs; | ||
| 1821 | * } RedisModuleCommandArg; | ||
| 1822 | * | ||
| 1823 | * Explanation of the fields: | ||
| 1824 | * | ||
| 1825 | * * `name`: Name of the argument. | ||
| 1826 | * | ||
| 1827 | * * `type`: The type of the argument. See below for details. The types | ||
| 1828 | * `REDISMODULE_ARG_TYPE_ONEOF` and `REDISMODULE_ARG_TYPE_BLOCK` require | ||
| 1829 | * an argument to have sub-arguments, i.e. `subargs`. | ||
| 1830 | * | ||
| 1831 | * * `key_spec_index`: If the `type` is `REDISMODULE_ARG_TYPE_KEY` you must | ||
| 1832 | * provide the index of the key-spec associated with this argument. See | ||
| 1833 | * `key_specs` above. If the argument is not a key, you may specify -1. | ||
| 1834 | * | ||
| 1835 | * * `token`: The token preceding the argument (optional). Example: the | ||
| 1836 | * argument `seconds` in `SET` has a token `EX`. If the argument consists | ||
| 1837 | * of only a token (for example `NX` in `SET`) the type should be | ||
| 1838 | * `REDISMODULE_ARG_TYPE_PURE_TOKEN` and `value` should be NULL. | ||
| 1839 | * | ||
| 1840 | * * `summary`: A short description of the argument (optional). | ||
| 1841 | * | ||
| 1842 | * * `since`: The first version which included this argument (optional). | ||
| 1843 | * | ||
| 1844 | * * `flags`: A bitwise or of the macros `REDISMODULE_CMD_ARG_*`. See below. | ||
| 1845 | * | ||
| 1846 | * * `value`: The display-value of the argument. This string is what should | ||
| 1847 | * be displayed when creating the command syntax from the output of | ||
| 1848 | * `COMMAND`. If `token` is not NULL, it should also be displayed. | ||
| 1849 | * | ||
| 1850 | * Explanation of `RedisModuleCommandArgType`: | ||
| 1851 | * | ||
| 1852 | * * `REDISMODULE_ARG_TYPE_STRING`: String argument. | ||
| 1853 | * * `REDISMODULE_ARG_TYPE_INTEGER`: Integer argument. | ||
| 1854 | * * `REDISMODULE_ARG_TYPE_DOUBLE`: Double-precision float argument. | ||
| 1855 | * * `REDISMODULE_ARG_TYPE_KEY`: String argument representing a keyname. | ||
| 1856 | * * `REDISMODULE_ARG_TYPE_PATTERN`: String, but regex pattern. | ||
| 1857 | * * `REDISMODULE_ARG_TYPE_UNIX_TIME`: Integer, but Unix timestamp. | ||
| 1858 | * * `REDISMODULE_ARG_TYPE_PURE_TOKEN`: Argument doesn't have a placeholder. | ||
| 1859 | * It's just a token without a value. Example: the `KEEPTTL` option of the | ||
| 1860 | * `SET` command. | ||
| 1861 | * * `REDISMODULE_ARG_TYPE_ONEOF`: Used when the user can choose only one of | ||
| 1862 | * a few sub-arguments. Requires `subargs`. Example: the `NX` and `XX` | ||
| 1863 | * options of `SET`. | ||
| 1864 | * * `REDISMODULE_ARG_TYPE_BLOCK`: Used when one wants to group together | ||
| 1865 | * several sub-arguments, usually to apply something on all of them, like | ||
| 1866 | * making the entire group "optional". Requires `subargs`. Example: the | ||
| 1867 | * `LIMIT offset count` parameters in `ZRANGE`. | ||
| 1868 | * | ||
| 1869 | * Explanation of the command argument flags: | ||
| 1870 | * | ||
| 1871 | * * `REDISMODULE_CMD_ARG_OPTIONAL`: The argument is optional (like GET in | ||
| 1872 | * the SET command). | ||
| 1873 | * * `REDISMODULE_CMD_ARG_MULTIPLE`: The argument may repeat itself (like | ||
| 1874 | * key in DEL). | ||
| 1875 | * * `REDISMODULE_CMD_ARG_MULTIPLE_TOKEN`: The argument may repeat itself, | ||
| 1876 | * and so does its token (like `GET pattern` in SORT). | ||
| 1877 | * | ||
| 1878 | * On success REDISMODULE_OK is returned. On error REDISMODULE_ERR is returned | ||
| 1879 | * and `errno` is set to EINVAL if invalid info was provided or EEXIST if info | ||
| 1880 | * has already been set. If the info is invalid, a warning is logged explaining | ||
| 1881 | * which part of the info is invalid and why. */ | ||
| 1882 | int RM_SetCommandInfo(RedisModuleCommand *command, const RedisModuleCommandInfo *info) { | ||
| 1883 | if (!moduleValidateCommandInfo(info)) { | ||
| 1884 | errno = EINVAL; | ||
| 1885 | return REDISMODULE_ERR; | ||
| 1886 | } | ||
| 1887 | |||
| 1888 | struct redisCommand *cmd = command->rediscmd; | ||
| 1889 | |||
| 1890 | /* Check if any info has already been set. Overwriting info involves freeing | ||
| 1891 | * the old info, which is not implemented. */ | ||
| 1892 | if (cmd->summary || cmd->complexity || cmd->since || cmd->history || | ||
| 1893 | cmd->tips || cmd->args || | ||
| 1894 | !(cmd->key_specs_num == 0 || | ||
| 1895 | /* Allow key spec populated from legacy (first,last,step) to exist. */ | ||
| 1896 | (cmd->key_specs_num == 1 && | ||
| 1897 | cmd->key_specs[0].begin_search_type == KSPEC_BS_INDEX && | ||
| 1898 | cmd->key_specs[0].find_keys_type == KSPEC_FK_RANGE))) { | ||
| 1899 | errno = EEXIST; | ||
| 1900 | return REDISMODULE_ERR; | ||
| 1901 | } | ||
| 1902 | |||
| 1903 | if (info->summary) cmd->summary = zstrdup(info->summary); | ||
| 1904 | if (info->complexity) cmd->complexity = zstrdup(info->complexity); | ||
| 1905 | if (info->since) cmd->since = zstrdup(info->since); | ||
| 1906 | |||
| 1907 | const RedisModuleCommandInfoVersion *version = info->version; | ||
| 1908 | if (info->history) { | ||
| 1909 | size_t count = 0; | ||
| 1910 | while (moduleCmdHistoryEntryAt(version, info->history, count)->since) | ||
| 1911 | count++; | ||
| 1912 | serverAssert(count < SIZE_MAX / sizeof(commandHistory)); | ||
| 1913 | cmd->history = zmalloc(sizeof(commandHistory) * (count + 1)); | ||
| 1914 | for (size_t j = 0; j < count; j++) { | ||
| 1915 | RedisModuleCommandHistoryEntry *entry = | ||
| 1916 | moduleCmdHistoryEntryAt(version, info->history, j); | ||
| 1917 | cmd->history[j].since = zstrdup(entry->since); | ||
| 1918 | cmd->history[j].changes = zstrdup(entry->changes); | ||
| 1919 | } | ||
| 1920 | cmd->history[count].since = NULL; | ||
| 1921 | cmd->history[count].changes = NULL; | ||
| 1922 | cmd->num_history = count; | ||
| 1923 | } | ||
| 1924 | |||
| 1925 | if (info->tips) { | ||
| 1926 | int count; | ||
| 1927 | sds *tokens = sdssplitlen(info->tips, strlen(info->tips), " ", 1, &count); | ||
| 1928 | if (tokens) { | ||
| 1929 | cmd->tips = zmalloc(sizeof(char *) * (count + 1)); | ||
| 1930 | for (int j = 0; j < count; j++) { | ||
| 1931 | cmd->tips[j] = zstrdup(tokens[j]); | ||
| 1932 | } | ||
| 1933 | cmd->tips[count] = NULL; | ||
| 1934 | cmd->num_tips = count; | ||
| 1935 | sdsfreesplitres(tokens, count); | ||
| 1936 | } | ||
| 1937 | } | ||
| 1938 | |||
| 1939 | if (info->arity) cmd->arity = info->arity; | ||
| 1940 | |||
| 1941 | if (info->key_specs) { | ||
| 1942 | /* Count and allocate the key specs. */ | ||
| 1943 | size_t count = 0; | ||
| 1944 | while (moduleCmdKeySpecAt(version, info->key_specs, count)->begin_search_type) | ||
| 1945 | count++; | ||
| 1946 | serverAssert(count < INT_MAX); | ||
| 1947 | zfree(cmd->key_specs); | ||
| 1948 | cmd->key_specs = zmalloc(sizeof(keySpec) * count); | ||
| 1949 | |||
| 1950 | /* Copy the contents of the RedisModuleCommandKeySpec array. */ | ||
| 1951 | cmd->key_specs_num = count; | ||
| 1952 | for (size_t j = 0; j < count; j++) { | ||
| 1953 | RedisModuleCommandKeySpec *spec = | ||
| 1954 | moduleCmdKeySpecAt(version, info->key_specs, j); | ||
| 1955 | cmd->key_specs[j].notes = spec->notes ? zstrdup(spec->notes) : NULL; | ||
| 1956 | cmd->key_specs[j].flags = moduleConvertKeySpecsFlags(spec->flags, 1); | ||
| 1957 | switch (spec->begin_search_type) { | ||
| 1958 | case REDISMODULE_KSPEC_BS_UNKNOWN: | ||
| 1959 | cmd->key_specs[j].begin_search_type = KSPEC_BS_UNKNOWN; | ||
| 1960 | break; | ||
| 1961 | case REDISMODULE_KSPEC_BS_INDEX: | ||
| 1962 | cmd->key_specs[j].begin_search_type = KSPEC_BS_INDEX; | ||
| 1963 | cmd->key_specs[j].bs.index.pos = spec->bs.index.pos; | ||
| 1964 | break; | ||
| 1965 | case REDISMODULE_KSPEC_BS_KEYWORD: | ||
| 1966 | cmd->key_specs[j].begin_search_type = KSPEC_BS_KEYWORD; | ||
| 1967 | cmd->key_specs[j].bs.keyword.keyword = zstrdup(spec->bs.keyword.keyword); | ||
| 1968 | cmd->key_specs[j].bs.keyword.startfrom = spec->bs.keyword.startfrom; | ||
| 1969 | break; | ||
| 1970 | default: | ||
| 1971 | /* Can't happen; stopped in moduleValidateCommandInfo(). */ | ||
| 1972 | serverPanic("Unknown begin_search_type"); | ||
| 1973 | } | ||
| 1974 | |||
| 1975 | switch (spec->find_keys_type) { | ||
| 1976 | case REDISMODULE_KSPEC_FK_OMITTED: | ||
| 1977 | /* Omitted field is shorthand to say that it's a single key. */ | ||
| 1978 | cmd->key_specs[j].find_keys_type = KSPEC_FK_RANGE; | ||
| 1979 | cmd->key_specs[j].fk.range.lastkey = 0; | ||
| 1980 | cmd->key_specs[j].fk.range.keystep = 1; | ||
| 1981 | cmd->key_specs[j].fk.range.limit = 0; | ||
| 1982 | break; | ||
| 1983 | case REDISMODULE_KSPEC_FK_UNKNOWN: | ||
| 1984 | cmd->key_specs[j].find_keys_type = KSPEC_FK_UNKNOWN; | ||
| 1985 | break; | ||
| 1986 | case REDISMODULE_KSPEC_FK_RANGE: | ||
| 1987 | cmd->key_specs[j].find_keys_type = KSPEC_FK_RANGE; | ||
| 1988 | cmd->key_specs[j].fk.range.lastkey = spec->fk.range.lastkey; | ||
| 1989 | cmd->key_specs[j].fk.range.keystep = spec->fk.range.keystep; | ||
| 1990 | cmd->key_specs[j].fk.range.limit = spec->fk.range.limit; | ||
| 1991 | break; | ||
| 1992 | case REDISMODULE_KSPEC_FK_KEYNUM: | ||
| 1993 | cmd->key_specs[j].find_keys_type = KSPEC_FK_KEYNUM; | ||
| 1994 | cmd->key_specs[j].fk.keynum.keynumidx = spec->fk.keynum.keynumidx; | ||
| 1995 | cmd->key_specs[j].fk.keynum.firstkey = spec->fk.keynum.firstkey; | ||
| 1996 | cmd->key_specs[j].fk.keynum.keystep = spec->fk.keynum.keystep; | ||
| 1997 | break; | ||
| 1998 | default: | ||
| 1999 | /* Can't happen; stopped in moduleValidateCommandInfo(). */ | ||
| 2000 | serverPanic("Unknown find_keys_type"); | ||
| 2001 | } | ||
| 2002 | } | ||
| 2003 | |||
| 2004 | /* Update the legacy (first,last,step) spec and "movablekeys" flag used by the COMMAND command, | ||
| 2005 | * by trying to "glue" consecutive range key specs. */ | ||
| 2006 | populateCommandLegacyRangeSpec(cmd); | ||
| 2007 | } | ||
| 2008 | |||
| 2009 | if (info->args) { | ||
| 2010 | cmd->args = moduleCopyCommandArgs(info->args, version); | ||
| 2011 | /* Populate arg.num_args with the number of subargs, recursively */ | ||
| 2012 | cmd->num_args = populateArgsStructure(cmd->args); | ||
| 2013 | } | ||
| 2014 | |||
| 2015 | /* Fields added in future versions to be added here, under conditions like | ||
| 2016 | * `if (info->version >= 2) { access version 2 fields here }` */ | ||
| 2017 | |||
| 2018 | return REDISMODULE_OK; | ||
| 2019 | } | ||
| 2020 | |||
| 2021 | /* Returns 1 if v is a power of two, 0 otherwise. */ | ||
| 2022 | static inline int isPowerOfTwo(uint64_t v) { | ||
| 2023 | return v && !(v & (v - 1)); | ||
| 2024 | } | ||
| 2025 | |||
| 2026 | /* Returns 1 if the command info is valid and 0 otherwise. */ | ||
| 2027 | static int moduleValidateCommandInfo(const RedisModuleCommandInfo *info) { | ||
| 2028 | const RedisModuleCommandInfoVersion *version = info->version; | ||
| 2029 | if (!version) { | ||
| 2030 | serverLog(LL_WARNING, "Invalid command info: version missing"); | ||
| 2031 | return 0; | ||
| 2032 | } | ||
| 2033 | |||
| 2034 | /* No validation for the fields summary, complexity, since, tips (strings or | ||
| 2035 | * NULL) and arity (any integer). */ | ||
| 2036 | |||
| 2037 | /* History: If since is set, changes must also be set. */ | ||
| 2038 | if (info->history) { | ||
| 2039 | for (size_t j = 0; | ||
| 2040 | moduleCmdHistoryEntryAt(version, info->history, j)->since; | ||
| 2041 | j++) | ||
| 2042 | { | ||
| 2043 | if (!moduleCmdHistoryEntryAt(version, info->history, j)->changes) { | ||
| 2044 | serverLog(LL_WARNING, "Invalid command info: history[%zd].changes missing", j); | ||
| 2045 | return 0; | ||
| 2046 | } | ||
| 2047 | } | ||
| 2048 | } | ||
| 2049 | |||
| 2050 | /* Key specs. */ | ||
| 2051 | if (info->key_specs) { | ||
| 2052 | for (size_t j = 0; | ||
| 2053 | moduleCmdKeySpecAt(version, info->key_specs, j)->begin_search_type; | ||
| 2054 | j++) | ||
| 2055 | { | ||
| 2056 | RedisModuleCommandKeySpec *spec = | ||
| 2057 | moduleCmdKeySpecAt(version, info->key_specs, j); | ||
| 2058 | if (j >= INT_MAX) { | ||
| 2059 | serverLog(LL_WARNING, "Invalid command info: Too many key specs"); | ||
| 2060 | return 0; /* redisCommand.key_specs_num is an int. */ | ||
| 2061 | } | ||
| 2062 | |||
| 2063 | /* Flags. Exactly one flag in a group is set if and only if the | ||
| 2064 | * masked bits is a power of two. */ | ||
| 2065 | uint64_t key_flags = | ||
| 2066 | REDISMODULE_CMD_KEY_RO | REDISMODULE_CMD_KEY_RW | | ||
| 2067 | REDISMODULE_CMD_KEY_OW | REDISMODULE_CMD_KEY_RM; | ||
| 2068 | uint64_t write_flags = | ||
| 2069 | REDISMODULE_CMD_KEY_INSERT | REDISMODULE_CMD_KEY_DELETE | | ||
| 2070 | REDISMODULE_CMD_KEY_UPDATE; | ||
| 2071 | if (!isPowerOfTwo(spec->flags & key_flags)) { | ||
| 2072 | serverLog(LL_WARNING, | ||
| 2073 | "Invalid command info: key_specs[%zd].flags: " | ||
| 2074 | "Exactly one of the flags RO, RW, OW, RM required", j); | ||
| 2075 | return 0; | ||
| 2076 | } | ||
| 2077 | if ((spec->flags & write_flags) != 0 && | ||
| 2078 | !isPowerOfTwo(spec->flags & write_flags)) | ||
| 2079 | { | ||
| 2080 | serverLog(LL_WARNING, | ||
| 2081 | "Invalid command info: key_specs[%zd].flags: " | ||
| 2082 | "INSERT, DELETE and UPDATE are mutually exclusive", j); | ||
| 2083 | return 0; | ||
| 2084 | } | ||
| 2085 | |||
| 2086 | switch (spec->begin_search_type) { | ||
| 2087 | case REDISMODULE_KSPEC_BS_UNKNOWN: break; | ||
| 2088 | case REDISMODULE_KSPEC_BS_INDEX: break; | ||
| 2089 | case REDISMODULE_KSPEC_BS_KEYWORD: | ||
| 2090 | if (spec->bs.keyword.keyword == NULL) { | ||
| 2091 | serverLog(LL_WARNING, | ||
| 2092 | "Invalid command info: key_specs[%zd].bs.keyword.keyword " | ||
| 2093 | "required when begin_search_type is KEYWORD", j); | ||
| 2094 | return 0; | ||
| 2095 | } | ||
| 2096 | break; | ||
| 2097 | default: | ||
| 2098 | serverLog(LL_WARNING, | ||
| 2099 | "Invalid command info: key_specs[%zd].begin_search_type: " | ||
| 2100 | "Invalid value %d", j, spec->begin_search_type); | ||
| 2101 | return 0; | ||
| 2102 | } | ||
| 2103 | |||
| 2104 | /* Validate find_keys_type. */ | ||
| 2105 | switch (spec->find_keys_type) { | ||
| 2106 | case REDISMODULE_KSPEC_FK_OMITTED: break; /* short for RANGE {0,1,0} */ | ||
| 2107 | case REDISMODULE_KSPEC_FK_UNKNOWN: break; | ||
| 2108 | case REDISMODULE_KSPEC_FK_RANGE: break; | ||
| 2109 | case REDISMODULE_KSPEC_FK_KEYNUM: break; | ||
| 2110 | default: | ||
| 2111 | serverLog(LL_WARNING, | ||
| 2112 | "Invalid command info: key_specs[%zd].find_keys_type: " | ||
| 2113 | "Invalid value %d", j, spec->find_keys_type); | ||
| 2114 | return 0; | ||
| 2115 | } | ||
| 2116 | } | ||
| 2117 | } | ||
| 2118 | |||
| 2119 | /* Args, subargs (recursive) */ | ||
| 2120 | return moduleValidateCommandArgs(info->args, version); | ||
| 2121 | } | ||
| 2122 | |||
| 2123 | /* When from_api is true, converts from REDISMODULE_CMD_KEY_* flags to CMD_KEY_* flags. | ||
| 2124 | * When from_api is false, converts from CMD_KEY_* flags to REDISMODULE_CMD_KEY_* flags. */ | ||
| 2125 | static int64_t moduleConvertKeySpecsFlags(int64_t flags, int from_api) { | ||
| 2126 | int64_t out = 0; | ||
| 2127 | int64_t map[][2] = { | ||
| 2128 | {REDISMODULE_CMD_KEY_RO, CMD_KEY_RO}, | ||
| 2129 | {REDISMODULE_CMD_KEY_RW, CMD_KEY_RW}, | ||
| 2130 | {REDISMODULE_CMD_KEY_OW, CMD_KEY_OW}, | ||
| 2131 | {REDISMODULE_CMD_KEY_RM, CMD_KEY_RM}, | ||
| 2132 | {REDISMODULE_CMD_KEY_ACCESS, CMD_KEY_ACCESS}, | ||
| 2133 | {REDISMODULE_CMD_KEY_INSERT, CMD_KEY_INSERT}, | ||
| 2134 | {REDISMODULE_CMD_KEY_UPDATE, CMD_KEY_UPDATE}, | ||
| 2135 | {REDISMODULE_CMD_KEY_DELETE, CMD_KEY_DELETE}, | ||
| 2136 | {REDISMODULE_CMD_KEY_NOT_KEY, CMD_KEY_NOT_KEY}, | ||
| 2137 | {REDISMODULE_CMD_KEY_INCOMPLETE, CMD_KEY_INCOMPLETE}, | ||
| 2138 | {REDISMODULE_CMD_KEY_VARIABLE_FLAGS, CMD_KEY_VARIABLE_FLAGS}, | ||
| 2139 | {0,0}}; | ||
| 2140 | |||
| 2141 | int from_idx = from_api ? 0 : 1, to_idx = !from_idx; | ||
| 2142 | for (int i=0; map[i][0]; i++) | ||
| 2143 | if (flags & map[i][from_idx]) out |= map[i][to_idx]; | ||
| 2144 | return out; | ||
| 2145 | } | ||
| 2146 | |||
| 2147 | /* Validates an array of RedisModuleCommandArg. Returns 1 if it's valid and 0 if | ||
| 2148 | * it's invalid. */ | ||
| 2149 | static int moduleValidateCommandArgs(RedisModuleCommandArg *args, | ||
| 2150 | const RedisModuleCommandInfoVersion *version) { | ||
| 2151 | if (args == NULL) return 1; /* Missing args is OK. */ | ||
| 2152 | for (size_t j = 0; moduleCmdArgAt(version, args, j)->name != NULL; j++) { | ||
| 2153 | RedisModuleCommandArg *arg = moduleCmdArgAt(version, args, j); | ||
| 2154 | int arg_type_error = 0; | ||
| 2155 | moduleConvertArgType(arg->type, &arg_type_error); | ||
| 2156 | if (arg_type_error) { | ||
| 2157 | serverLog(LL_WARNING, | ||
| 2158 | "Invalid command info: Argument \"%s\": Undefined type %d", | ||
| 2159 | arg->name, arg->type); | ||
| 2160 | return 0; | ||
| 2161 | } | ||
| 2162 | if (arg->type == REDISMODULE_ARG_TYPE_PURE_TOKEN && !arg->token) { | ||
| 2163 | serverLog(LL_WARNING, | ||
| 2164 | "Invalid command info: Argument \"%s\": " | ||
| 2165 | "token required when type is PURE_TOKEN", args[j].name); | ||
| 2166 | return 0; | ||
| 2167 | } | ||
| 2168 | |||
| 2169 | if (arg->type == REDISMODULE_ARG_TYPE_KEY) { | ||
| 2170 | if (arg->key_spec_index < 0) { | ||
| 2171 | serverLog(LL_WARNING, | ||
| 2172 | "Invalid command info: Argument \"%s\": " | ||
| 2173 | "key_spec_index required when type is KEY", | ||
| 2174 | arg->name); | ||
| 2175 | return 0; | ||
| 2176 | } | ||
| 2177 | } else if (arg->key_spec_index != -1 && arg->key_spec_index != 0) { | ||
| 2178 | /* 0 is allowed for convenience, to allow it to be omitted in | ||
| 2179 | * compound struct literals on the form `.field = value`. */ | ||
| 2180 | serverLog(LL_WARNING, | ||
| 2181 | "Invalid command info: Argument \"%s\": " | ||
| 2182 | "key_spec_index specified but type isn't KEY", | ||
| 2183 | arg->name); | ||
| 2184 | return 0; | ||
| 2185 | } | ||
| 2186 | |||
| 2187 | if (arg->flags & ~(_REDISMODULE_CMD_ARG_NEXT - 1)) { | ||
| 2188 | serverLog(LL_WARNING, | ||
| 2189 | "Invalid command info: Argument \"%s\": Invalid flags", | ||
| 2190 | arg->name); | ||
| 2191 | return 0; | ||
| 2192 | } | ||
| 2193 | |||
| 2194 | if (arg->type == REDISMODULE_ARG_TYPE_ONEOF || | ||
| 2195 | arg->type == REDISMODULE_ARG_TYPE_BLOCK) | ||
| 2196 | { | ||
| 2197 | if (arg->subargs == NULL) { | ||
| 2198 | serverLog(LL_WARNING, | ||
| 2199 | "Invalid command info: Argument \"%s\": " | ||
| 2200 | "subargs required when type is ONEOF or BLOCK", | ||
| 2201 | arg->name); | ||
| 2202 | return 0; | ||
| 2203 | } | ||
| 2204 | if (!moduleValidateCommandArgs(arg->subargs, version)) return 0; | ||
| 2205 | } else { | ||
| 2206 | if (arg->subargs != NULL) { | ||
| 2207 | serverLog(LL_WARNING, | ||
| 2208 | "Invalid command info: Argument \"%s\": " | ||
| 2209 | "subargs specified but type isn't ONEOF nor BLOCK", | ||
| 2210 | arg->name); | ||
| 2211 | return 0; | ||
| 2212 | } | ||
| 2213 | } | ||
| 2214 | } | ||
| 2215 | return 1; | ||
| 2216 | } | ||
| 2217 | |||
| 2218 | /* Converts an array of RedisModuleCommandArg into a freshly allocated array of | ||
| 2219 | * struct redisCommandArg. */ | ||
| 2220 | static struct redisCommandArg *moduleCopyCommandArgs(RedisModuleCommandArg *args, | ||
| 2221 | const RedisModuleCommandInfoVersion *version) { | ||
| 2222 | size_t count = 0; | ||
| 2223 | while (moduleCmdArgAt(version, args, count)->name) count++; | ||
| 2224 | serverAssert(count < SIZE_MAX / sizeof(struct redisCommandArg)); | ||
| 2225 | struct redisCommandArg *realargs = zcalloc((count+1) * sizeof(redisCommandArg)); | ||
| 2226 | |||
| 2227 | for (size_t j = 0; j < count; j++) { | ||
| 2228 | RedisModuleCommandArg *arg = moduleCmdArgAt(version, args, j); | ||
| 2229 | realargs[j].name = zstrdup(arg->name); | ||
| 2230 | realargs[j].type = moduleConvertArgType(arg->type, NULL); | ||
| 2231 | if (arg->type == REDISMODULE_ARG_TYPE_KEY) | ||
| 2232 | realargs[j].key_spec_index = arg->key_spec_index; | ||
| 2233 | else | ||
| 2234 | realargs[j].key_spec_index = -1; | ||
| 2235 | if (arg->token) realargs[j].token = zstrdup(arg->token); | ||
| 2236 | if (arg->summary) realargs[j].summary = zstrdup(arg->summary); | ||
| 2237 | if (arg->since) realargs[j].since = zstrdup(arg->since); | ||
| 2238 | if (arg->deprecated_since) realargs[j].deprecated_since = zstrdup(arg->deprecated_since); | ||
| 2239 | if (arg->display_text) realargs[j].display_text = zstrdup(arg->display_text); | ||
| 2240 | realargs[j].flags = moduleConvertArgFlags(arg->flags); | ||
| 2241 | if (arg->subargs) realargs[j].subargs = moduleCopyCommandArgs(arg->subargs, version); | ||
| 2242 | } | ||
| 2243 | return realargs; | ||
| 2244 | } | ||
| 2245 | |||
| 2246 | static redisCommandArgType moduleConvertArgType(RedisModuleCommandArgType type, int *error) { | ||
| 2247 | if (error) *error = 0; | ||
| 2248 | switch (type) { | ||
| 2249 | case REDISMODULE_ARG_TYPE_STRING: return ARG_TYPE_STRING; | ||
| 2250 | case REDISMODULE_ARG_TYPE_INTEGER: return ARG_TYPE_INTEGER; | ||
| 2251 | case REDISMODULE_ARG_TYPE_DOUBLE: return ARG_TYPE_DOUBLE; | ||
| 2252 | case REDISMODULE_ARG_TYPE_KEY: return ARG_TYPE_KEY; | ||
| 2253 | case REDISMODULE_ARG_TYPE_PATTERN: return ARG_TYPE_PATTERN; | ||
| 2254 | case REDISMODULE_ARG_TYPE_UNIX_TIME: return ARG_TYPE_UNIX_TIME; | ||
| 2255 | case REDISMODULE_ARG_TYPE_PURE_TOKEN: return ARG_TYPE_PURE_TOKEN; | ||
| 2256 | case REDISMODULE_ARG_TYPE_ONEOF: return ARG_TYPE_ONEOF; | ||
| 2257 | case REDISMODULE_ARG_TYPE_BLOCK: return ARG_TYPE_BLOCK; | ||
| 2258 | default: | ||
| 2259 | if (error) *error = 1; | ||
| 2260 | return -1; | ||
| 2261 | } | ||
| 2262 | } | ||
| 2263 | |||
| 2264 | static int moduleConvertArgFlags(int flags) { | ||
| 2265 | int realflags = 0; | ||
| 2266 | if (flags & REDISMODULE_CMD_ARG_OPTIONAL) realflags |= CMD_ARG_OPTIONAL; | ||
| 2267 | if (flags & REDISMODULE_CMD_ARG_MULTIPLE) realflags |= CMD_ARG_MULTIPLE; | ||
| 2268 | if (flags & REDISMODULE_CMD_ARG_MULTIPLE_TOKEN) realflags |= CMD_ARG_MULTIPLE_TOKEN; | ||
| 2269 | return realflags; | ||
| 2270 | } | ||
| 2271 | |||
| 2272 | /* Return `struct RedisModule *` as `void *` to avoid exposing it outside of module.c. */ | ||
| 2273 | void *moduleGetHandleByName(char *modulename) { | ||
| 2274 | return dictFetchValue(modules,modulename); | ||
| 2275 | } | ||
| 2276 | |||
| 2277 | /* Returns 1 if `cmd` is a command of the module `modulename`. 0 otherwise. */ | ||
| 2278 | int moduleIsModuleCommand(void *module_handle, struct redisCommand *cmd) { | ||
| 2279 | if (cmd->proc != RedisModuleCommandDispatcher) | ||
| 2280 | return 0; | ||
| 2281 | if (module_handle == NULL) | ||
| 2282 | return 0; | ||
| 2283 | RedisModuleCommand *cp = cmd->module_cmd; | ||
| 2284 | return (cp->module == module_handle); | ||
| 2285 | } | ||
| 2286 | |||
| 2287 | /* -------------------------------------------------------------------------- | ||
| 2288 | * ## Module information and time measurement | ||
| 2289 | * -------------------------------------------------------------------------- */ | ||
| 2290 | |||
| 2291 | int moduleListConfigMatch(void *config, void *name) { | ||
| 2292 | ModuleConfig *mc = (ModuleConfig *) config; | ||
| 2293 | /* Compare the provided name with the config's name and alias if it exists */ | ||
| 2294 | return strcasecmp(mc->name, (char *) name) == 0 || | ||
| 2295 | ((mc->alias) && strcasecmp(mc->alias, (char *) name) == 0); | ||
| 2296 | } | ||
| 2297 | |||
| 2298 | void moduleListFree(void *config) { | ||
| 2299 | ModuleConfig *module_config = (ModuleConfig *) config; | ||
| 2300 | sdsfree(module_config->name); | ||
| 2301 | sdsfree(module_config->alias); | ||
| 2302 | zfree(config); | ||
| 2303 | } | ||
| 2304 | |||
| 2305 | void RM_SetModuleAttribs(RedisModuleCtx *ctx, const char *name, int ver, int apiver) { | ||
| 2306 | /* Called by RM_Init() to setup the `ctx->module` structure. | ||
| 2307 | * | ||
| 2308 | * This is an internal function, Redis modules developers don't need | ||
| 2309 | * to use it. */ | ||
| 2310 | RedisModule *module; | ||
| 2311 | |||
| 2312 | if (ctx->module != NULL) return; | ||
| 2313 | module = zmalloc(sizeof(*module)); | ||
| 2314 | module->name = sdsnew(name); | ||
| 2315 | module->ver = ver; | ||
| 2316 | module->apiver = apiver; | ||
| 2317 | module->types = listCreate(); | ||
| 2318 | module->usedby = listCreate(); | ||
| 2319 | module->using = listCreate(); | ||
| 2320 | module->filters = listCreate(); | ||
| 2321 | module->module_configs = listCreate(); | ||
| 2322 | listSetMatchMethod(module->module_configs, moduleListConfigMatch); | ||
| 2323 | listSetFreeMethod(module->module_configs, moduleListFree); | ||
| 2324 | module->in_call = 0; | ||
| 2325 | module->configs_initialized = 0; | ||
| 2326 | module->in_hook = 0; | ||
| 2327 | module->options = 0; | ||
| 2328 | module->info_cb = 0; | ||
| 2329 | module->defrag_cb = 0; | ||
| 2330 | module->defrag_cb_2 = 0; | ||
| 2331 | module->defrag_start_cb = 0; | ||
| 2332 | module->defrag_end_cb = 0; | ||
| 2333 | module->loadmod = NULL; | ||
| 2334 | module->num_commands_with_acl_categories = 0; | ||
| 2335 | module->onload = 1; | ||
| 2336 | module->num_acl_categories_added = 0; | ||
| 2337 | ctx->module = module; | ||
| 2338 | } | ||
| 2339 | |||
| 2340 | /* Return non-zero if the module name is busy. | ||
| 2341 | * Otherwise zero is returned. */ | ||
| 2342 | int RM_IsModuleNameBusy(const char *name) { | ||
| 2343 | sds modulename = sdsnew(name); | ||
| 2344 | dictEntry *de = dictFind(modules,modulename); | ||
| 2345 | sdsfree(modulename); | ||
| 2346 | return de != NULL; | ||
| 2347 | } | ||
| 2348 | |||
| 2349 | /* Return the current UNIX time in milliseconds. */ | ||
| 2350 | mstime_t RM_Milliseconds(void) { | ||
| 2351 | return mstime(); | ||
| 2352 | } | ||
| 2353 | |||
| 2354 | /* Return counter of micro-seconds relative to an arbitrary point in time. */ | ||
| 2355 | uint64_t RM_MonotonicMicroseconds(void) { | ||
| 2356 | return getMonotonicUs(); | ||
| 2357 | } | ||
| 2358 | |||
| 2359 | /* Return the current UNIX time in microseconds */ | ||
| 2360 | ustime_t RM_Microseconds(void) { | ||
| 2361 | return ustime(); | ||
| 2362 | } | ||
| 2363 | |||
| 2364 | /* Return the cached UNIX time in microseconds. | ||
| 2365 | * It is updated in the server cron job and before executing a command. | ||
| 2366 | * It is useful for complex call stacks, such as a command causing a | ||
| 2367 | * key space notification, causing a module to execute a RedisModule_Call, | ||
| 2368 | * causing another notification, etc. | ||
| 2369 | * It makes sense that all this callbacks would use the same clock. */ | ||
| 2370 | ustime_t RM_CachedMicroseconds(void) { | ||
| 2371 | return server.ustime; | ||
| 2372 | } | ||
| 2373 | |||
| 2374 | /* Mark a point in time that will be used as the start time to calculate | ||
| 2375 | * the elapsed execution time when RM_BlockedClientMeasureTimeEnd() is called. | ||
| 2376 | * Within the same command, you can call multiple times | ||
| 2377 | * RM_BlockedClientMeasureTimeStart() and RM_BlockedClientMeasureTimeEnd() | ||
| 2378 | * to accumulate independent time intervals to the background duration. | ||
| 2379 | * This method always return REDISMODULE_OK. | ||
| 2380 | * | ||
| 2381 | * This function is not thread safe, If used in module thread and blocked callback (possibly main thread) | ||
| 2382 | * simultaneously, it's recommended to protect them with lock owned by caller instead of GIL. */ | ||
| 2383 | int RM_BlockedClientMeasureTimeStart(RedisModuleBlockedClient *bc) { | ||
| 2384 | elapsedStart(&(bc->background_timer)); | ||
| 2385 | return REDISMODULE_OK; | ||
| 2386 | } | ||
| 2387 | |||
| 2388 | /* Mark a point in time that will be used as the end time | ||
| 2389 | * to calculate the elapsed execution time. | ||
| 2390 | * On success REDISMODULE_OK is returned. | ||
| 2391 | * This method only returns REDISMODULE_ERR if no start time was | ||
| 2392 | * previously defined ( meaning RM_BlockedClientMeasureTimeStart was not called ). | ||
| 2393 | * | ||
| 2394 | * This function is not thread safe, If used in module thread and blocked callback (possibly main thread) | ||
| 2395 | * simultaneously, it's recommended to protect them with lock owned by caller instead of GIL. */ | ||
| 2396 | int RM_BlockedClientMeasureTimeEnd(RedisModuleBlockedClient *bc) { | ||
| 2397 | // If the counter is 0 then we haven't called RM_BlockedClientMeasureTimeStart | ||
| 2398 | if (!bc->background_timer) | ||
| 2399 | return REDISMODULE_ERR; | ||
| 2400 | bc->background_duration += elapsedUs(bc->background_timer); | ||
| 2401 | return REDISMODULE_OK; | ||
| 2402 | } | ||
| 2403 | |||
| 2404 | /* This API allows modules to let Redis process background tasks, and some | ||
| 2405 | * commands during long blocking execution of a module command. | ||
| 2406 | * The module can call this API periodically. | ||
| 2407 | * The flags is a bit mask of these: | ||
| 2408 | * | ||
| 2409 | * - `REDISMODULE_YIELD_FLAG_NONE`: No special flags, can perform some background | ||
| 2410 | * operations, but not process client commands. | ||
| 2411 | * - `REDISMODULE_YIELD_FLAG_CLIENTS`: Redis can also process client commands. | ||
| 2412 | * | ||
| 2413 | * The `busy_reply` argument is optional, and can be used to control the verbose | ||
| 2414 | * error string after the `-BUSY` error code. | ||
| 2415 | * | ||
| 2416 | * When the `REDISMODULE_YIELD_FLAG_CLIENTS` is used, Redis will only start | ||
| 2417 | * processing client commands after the time defined by the | ||
| 2418 | * `busy-reply-threshold` config, in which case Redis will start rejecting most | ||
| 2419 | * commands with `-BUSY` error, but allow the ones marked with the `allow-busy` | ||
| 2420 | * flag to be executed. | ||
| 2421 | * This API can also be used in thread safe context (while locked), and during | ||
| 2422 | * loading (in the `rdb_load` callback, in which case it'll reject commands with | ||
| 2423 | * the -LOADING error) | ||
| 2424 | */ | ||
| 2425 | void RM_Yield(RedisModuleCtx *ctx, int flags, const char *busy_reply) { | ||
| 2426 | static int yield_nesting = 0; | ||
| 2427 | /* Avoid nested calls to RM_Yield */ | ||
| 2428 | if (yield_nesting) | ||
| 2429 | return; | ||
| 2430 | yield_nesting++; | ||
| 2431 | |||
| 2432 | long long now = getMonotonicUs(); | ||
| 2433 | if (now >= ctx->next_yield_time) { | ||
| 2434 | /* In loading mode, there's no need to handle busy_module_yield_reply, | ||
| 2435 | * and busy_module_yield_flags, since redis is anyway rejecting all | ||
| 2436 | * commands with -LOADING. */ | ||
| 2437 | if (server.loading) { | ||
| 2438 | /* Let redis process events */ | ||
| 2439 | processEventsWhileBlocked(); | ||
| 2440 | } else { | ||
| 2441 | const char *prev_busy_module_yield_reply = server.busy_module_yield_reply; | ||
| 2442 | server.busy_module_yield_reply = busy_reply; | ||
| 2443 | /* start the blocking operation if not already started. */ | ||
| 2444 | if (!server.busy_module_yield_flags) { | ||
| 2445 | server.busy_module_yield_flags = BUSY_MODULE_YIELD_EVENTS; | ||
| 2446 | blockingOperationStarts(); | ||
| 2447 | if (server.current_client) | ||
| 2448 | protectClient(server.current_client); | ||
| 2449 | } | ||
| 2450 | if (flags & REDISMODULE_YIELD_FLAG_CLIENTS) | ||
| 2451 | server.busy_module_yield_flags |= BUSY_MODULE_YIELD_CLIENTS; | ||
| 2452 | |||
| 2453 | /* Let redis process events */ | ||
| 2454 | if (!pthread_equal(server.main_thread_id, pthread_self())) { | ||
| 2455 | /* If we are not in the main thread, we defer event loop processing to the main thread | ||
| 2456 | * after the main thread enters acquiring GIL state in order to protect the event | ||
| 2457 | * loop (ae.c) and avoid potential race conditions. */ | ||
| 2458 | |||
| 2459 | int acquiring; | ||
| 2460 | atomicGet(server.module_gil_acquring, acquiring); | ||
| 2461 | if (!acquiring) { | ||
| 2462 | /* If the main thread has not yet entered the acquiring GIL state, | ||
| 2463 | * we attempt to wake it up and exit without waiting for it to | ||
| 2464 | * acquire the GIL. This avoids blocking the caller, allowing them to | ||
| 2465 | * continue with unfinished tasks before the next yield. | ||
| 2466 | * We assume the caller keeps the GIL locked. */ | ||
| 2467 | if (write(server.module_pipe[1],"A",1) != 1) { | ||
| 2468 | /* Ignore the error, this is best-effort. */ | ||
| 2469 | } | ||
| 2470 | } else { | ||
| 2471 | /* Release the GIL, yielding CPU to give the main thread an opportunity to start | ||
| 2472 | * event processing, and then acquire the GIL again until the main thread releases it. */ | ||
| 2473 | moduleReleaseGIL(); | ||
| 2474 | usleep(0); | ||
| 2475 | moduleAcquireGIL(); | ||
| 2476 | } | ||
| 2477 | } else { | ||
| 2478 | /* If we are in the main thread, we can safely process events. */ | ||
| 2479 | processEventsWhileBlocked(); | ||
| 2480 | } | ||
| 2481 | |||
| 2482 | server.busy_module_yield_reply = prev_busy_module_yield_reply; | ||
| 2483 | /* Possibly restore the previous flags in case of two nested contexts | ||
| 2484 | * that use this API with different flags, but keep the first bit | ||
| 2485 | * (PROCESS_EVENTS) set, so we know to call blockingOperationEnds on time. */ | ||
| 2486 | server.busy_module_yield_flags &= ~BUSY_MODULE_YIELD_CLIENTS; | ||
| 2487 | } | ||
| 2488 | |||
| 2489 | /* decide when the next event should fire. */ | ||
| 2490 | ctx->next_yield_time = now + 1000000 / server.hz; | ||
| 2491 | } | ||
| 2492 | yield_nesting--; | ||
| 2493 | } | ||
| 2494 | |||
| 2495 | /* Set flags defining capabilities or behavior bit flags. | ||
| 2496 | * | ||
| 2497 | * REDISMODULE_OPTIONS_HANDLE_IO_ERRORS: | ||
| 2498 | * Generally, modules don't need to bother with this, as the process will just | ||
| 2499 | * terminate if a read error happens, however, setting this flag would allow | ||
| 2500 | * repl-diskless-load to work if enabled. | ||
| 2501 | * The module should use RedisModule_IsIOError after reads, before using the | ||
| 2502 | * data that was read, and in case of error, propagate it upwards, and also be | ||
| 2503 | * able to release the partially populated value and all it's allocations. | ||
| 2504 | * | ||
| 2505 | * REDISMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED: | ||
| 2506 | * See RM_SignalModifiedKey(). | ||
| 2507 | * | ||
| 2508 | * REDISMODULE_OPTIONS_HANDLE_REPL_ASYNC_LOAD: | ||
| 2509 | * Setting this flag indicates module awareness of diskless async replication (repl-diskless-load=swapdb) | ||
| 2510 | * and that redis could be serving reads during replication instead of blocking with LOADING status. | ||
| 2511 | * | ||
| 2512 | * REDISMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS: | ||
| 2513 | * Declare that the module wants to get nested key-space notifications. | ||
| 2514 | * By default, Redis will not fire key-space notifications that happened inside | ||
| 2515 | * a key-space notification callback. This flag allows to change this behavior | ||
| 2516 | * and fire nested key-space notifications. Notice: if enabled, the module | ||
| 2517 | * should protected itself from infinite recursion. */ | ||
| 2518 | void RM_SetModuleOptions(RedisModuleCtx *ctx, int options) { | ||
| 2519 | ctx->module->options = options; | ||
| 2520 | } | ||
| 2521 | |||
| 2522 | /* Signals that the key is modified from user's perspective (i.e. invalidate WATCH | ||
| 2523 | * and client side caching). | ||
| 2524 | * | ||
| 2525 | * This is done automatically when a key opened for writing is closed, unless | ||
| 2526 | * the option REDISMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED has been set using | ||
| 2527 | * RM_SetModuleOptions(). | ||
| 2528 | */ | ||
| 2529 | int RM_SignalModifiedKey(RedisModuleCtx *ctx, RedisModuleString *keyname) { | ||
| 2530 | kvobj *kv = lookupKeyReadWithFlags(ctx->client->db, keyname, LOOKUP_NOTOUCH); | ||
| 2531 | keyModified(ctx->client,ctx->client->db,keyname,kv,1); | ||
| 2532 | return REDISMODULE_OK; | ||
| 2533 | } | ||
| 2534 | |||
| 2535 | /* -------------------------------------------------------------------------- | ||
| 2536 | * ## Automatic memory management for modules | ||
| 2537 | * -------------------------------------------------------------------------- */ | ||
| 2538 | |||
| 2539 | /* Enable automatic memory management. | ||
| 2540 | * | ||
| 2541 | * The function must be called as the first function of a command implementation | ||
| 2542 | * that wants to use automatic memory. | ||
| 2543 | * | ||
| 2544 | * When enabled, automatic memory management tracks and automatically frees | ||
| 2545 | * keys, call replies and Redis string objects once the command returns. In most | ||
| 2546 | * cases this eliminates the need of calling the following functions: | ||
| 2547 | * | ||
| 2548 | * 1. RedisModule_CloseKey() | ||
| 2549 | * 2. RedisModule_FreeCallReply() | ||
| 2550 | * 3. RedisModule_FreeString() | ||
| 2551 | * | ||
| 2552 | * These functions can still be used with automatic memory management enabled, | ||
| 2553 | * to optimize loops that make numerous allocations for example. */ | ||
| 2554 | void RM_AutoMemory(RedisModuleCtx *ctx) { | ||
| 2555 | ctx->flags |= REDISMODULE_CTX_AUTO_MEMORY; | ||
| 2556 | } | ||
| 2557 | |||
| 2558 | /* Add a new object to release automatically when the callback returns. */ | ||
| 2559 | void autoMemoryAdd(RedisModuleCtx *ctx, int type, void *ptr) { | ||
| 2560 | if (!(ctx->flags & REDISMODULE_CTX_AUTO_MEMORY)) return; | ||
| 2561 | if (ctx->amqueue_used == ctx->amqueue_len) { | ||
| 2562 | ctx->amqueue_len *= 2; | ||
| 2563 | if (ctx->amqueue_len < 16) ctx->amqueue_len = 16; | ||
| 2564 | ctx->amqueue = zrealloc(ctx->amqueue,sizeof(struct AutoMemEntry)*ctx->amqueue_len); | ||
| 2565 | } | ||
| 2566 | ctx->amqueue[ctx->amqueue_used].type = type; | ||
| 2567 | ctx->amqueue[ctx->amqueue_used].ptr = ptr; | ||
| 2568 | ctx->amqueue_used++; | ||
| 2569 | } | ||
| 2570 | |||
| 2571 | /* Mark an object as freed in the auto release queue, so that users can still | ||
| 2572 | * free things manually if they want. | ||
| 2573 | * | ||
| 2574 | * The function returns 1 if the object was actually found in the auto memory | ||
| 2575 | * pool, otherwise 0 is returned. */ | ||
| 2576 | int autoMemoryFreed(RedisModuleCtx *ctx, int type, void *ptr) { | ||
| 2577 | if (!(ctx->flags & REDISMODULE_CTX_AUTO_MEMORY)) return 0; | ||
| 2578 | |||
| 2579 | int count = (ctx->amqueue_used+1)/2; | ||
| 2580 | for (int j = 0; j < count; j++) { | ||
| 2581 | for (int side = 0; side < 2; side++) { | ||
| 2582 | /* For side = 0 check right side of the array, for | ||
| 2583 | * side = 1 check the left side instead (zig-zag scanning). */ | ||
| 2584 | int i = (side == 0) ? (ctx->amqueue_used - 1 - j) : j; | ||
| 2585 | if (ctx->amqueue[i].type == type && | ||
| 2586 | ctx->amqueue[i].ptr == ptr) | ||
| 2587 | { | ||
| 2588 | ctx->amqueue[i].type = REDISMODULE_AM_FREED; | ||
| 2589 | |||
| 2590 | /* Switch the freed element and the last element, to avoid growing | ||
| 2591 | * the queue unnecessarily if we allocate/free in a loop */ | ||
| 2592 | if (i != ctx->amqueue_used-1) { | ||
| 2593 | ctx->amqueue[i] = ctx->amqueue[ctx->amqueue_used-1]; | ||
| 2594 | } | ||
| 2595 | |||
| 2596 | /* Reduce the size of the queue because we either moved the top | ||
| 2597 | * element elsewhere or freed it */ | ||
| 2598 | ctx->amqueue_used--; | ||
| 2599 | return 1; | ||
| 2600 | } | ||
| 2601 | } | ||
| 2602 | } | ||
| 2603 | return 0; | ||
| 2604 | } | ||
| 2605 | |||
| 2606 | /* Release all the objects in queue. */ | ||
| 2607 | void autoMemoryCollect(RedisModuleCtx *ctx) { | ||
| 2608 | if (!(ctx->flags & REDISMODULE_CTX_AUTO_MEMORY)) return; | ||
| 2609 | /* Clear the AUTO_MEMORY flag from the context, otherwise the functions | ||
| 2610 | * we call to free the resources, will try to scan the auto release | ||
| 2611 | * queue to mark the entries as freed. */ | ||
| 2612 | ctx->flags &= ~REDISMODULE_CTX_AUTO_MEMORY; | ||
| 2613 | int j; | ||
| 2614 | for (j = 0; j < ctx->amqueue_used; j++) { | ||
| 2615 | void *ptr = ctx->amqueue[j].ptr; | ||
| 2616 | switch(ctx->amqueue[j].type) { | ||
| 2617 | case REDISMODULE_AM_STRING: decrRefCount(ptr); break; | ||
| 2618 | case REDISMODULE_AM_REPLY: RM_FreeCallReply(ptr); break; | ||
| 2619 | case REDISMODULE_AM_KEY: RM_CloseKey(ptr); break; | ||
| 2620 | case REDISMODULE_AM_DICT: RM_FreeDict(NULL,ptr); break; | ||
| 2621 | case REDISMODULE_AM_INFO: RM_FreeServerInfo(NULL,ptr); break; | ||
| 2622 | case REDISMODULE_AM_CONFIG: RM_ConfigIteratorRelease(NULL, ptr); break; | ||
| 2623 | case REDISMODULE_AM_SLOTRANGEARRAY: RM_ClusterFreeSlotRanges(NULL, ptr); break; | ||
| 2624 | } | ||
| 2625 | } | ||
| 2626 | ctx->flags |= REDISMODULE_CTX_AUTO_MEMORY; | ||
| 2627 | zfree(ctx->amqueue); | ||
| 2628 | ctx->amqueue = NULL; | ||
| 2629 | ctx->amqueue_len = 0; | ||
| 2630 | ctx->amqueue_used = 0; | ||
| 2631 | } | ||
| 2632 | |||
| 2633 | /* -------------------------------------------------------------------------- | ||
| 2634 | * ## String objects APIs | ||
| 2635 | * -------------------------------------------------------------------------- */ | ||
| 2636 | |||
| 2637 | /* Create a new module string object. The returned string must be freed | ||
| 2638 | * with RedisModule_FreeString(), unless automatic memory is enabled. | ||
| 2639 | * | ||
| 2640 | * The string is created by copying the `len` bytes starting | ||
| 2641 | * at `ptr`. No reference is retained to the passed buffer. | ||
| 2642 | * | ||
| 2643 | * The module context 'ctx' is optional and may be NULL if you want to create | ||
| 2644 | * a string out of the context scope. However in that case, the automatic | ||
| 2645 | * memory management will not be available, and the string memory must be | ||
| 2646 | * managed manually. */ | ||
| 2647 | RedisModuleString *RM_CreateString(RedisModuleCtx *ctx, const char *ptr, size_t len) { | ||
| 2648 | RedisModuleString *o = createStringObject(ptr,len); | ||
| 2649 | if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_STRING,o); | ||
| 2650 | return o; | ||
| 2651 | } | ||
| 2652 | |||
| 2653 | /* Create a new module string object from a printf format and arguments. | ||
| 2654 | * The returned string must be freed with RedisModule_FreeString(), unless | ||
| 2655 | * automatic memory is enabled. | ||
| 2656 | * | ||
| 2657 | * The string is created using the sds formatter function sdscatvprintf(). | ||
| 2658 | * | ||
| 2659 | * The passed context 'ctx' may be NULL if necessary, see the | ||
| 2660 | * RedisModule_CreateString() documentation for more info. */ | ||
| 2661 | RedisModuleString *RM_CreateStringPrintf(RedisModuleCtx *ctx, const char *fmt, ...) { | ||
| 2662 | sds s = sdsempty(); | ||
| 2663 | |||
| 2664 | va_list ap; | ||
| 2665 | va_start(ap, fmt); | ||
| 2666 | s = sdscatvprintf(s, fmt, ap); | ||
| 2667 | va_end(ap); | ||
| 2668 | |||
| 2669 | RedisModuleString *o = createObject(OBJ_STRING, s); | ||
| 2670 | if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_STRING,o); | ||
| 2671 | |||
| 2672 | return o; | ||
| 2673 | } | ||
| 2674 | |||
| 2675 | |||
| 2676 | /* Like RedisModule_CreateString(), but creates a string starting from a `long long` | ||
| 2677 | * integer instead of taking a buffer and its length. | ||
| 2678 | * | ||
| 2679 | * The returned string must be released with RedisModule_FreeString() or by | ||
| 2680 | * enabling automatic memory management. | ||
| 2681 | * | ||
| 2682 | * The passed context 'ctx' may be NULL if necessary, see the | ||
| 2683 | * RedisModule_CreateString() documentation for more info. */ | ||
| 2684 | RedisModuleString *RM_CreateStringFromLongLong(RedisModuleCtx *ctx, long long ll) { | ||
| 2685 | char buf[LONG_STR_SIZE]; | ||
| 2686 | size_t len = ll2string(buf,sizeof(buf),ll); | ||
| 2687 | return RM_CreateString(ctx,buf,len); | ||
| 2688 | } | ||
| 2689 | |||
| 2690 | /* Like RedisModule_CreateString(), but creates a string starting from a `unsigned long long` | ||
| 2691 | * integer instead of taking a buffer and its length. | ||
| 2692 | * | ||
| 2693 | * The returned string must be released with RedisModule_FreeString() or by | ||
| 2694 | * enabling automatic memory management. | ||
| 2695 | * | ||
| 2696 | * The passed context 'ctx' may be NULL if necessary, see the | ||
| 2697 | * RedisModule_CreateString() documentation for more info. */ | ||
| 2698 | RedisModuleString *RM_CreateStringFromULongLong(RedisModuleCtx *ctx, unsigned long long ull) { | ||
| 2699 | char buf[LONG_STR_SIZE]; | ||
| 2700 | size_t len = ull2string(buf,sizeof(buf),ull); | ||
| 2701 | return RM_CreateString(ctx,buf,len); | ||
| 2702 | } | ||
| 2703 | |||
| 2704 | /* Like RedisModule_CreateString(), but creates a string starting from a double | ||
| 2705 | * instead of taking a buffer and its length. | ||
| 2706 | * | ||
| 2707 | * The returned string must be released with RedisModule_FreeString() or by | ||
| 2708 | * enabling automatic memory management. */ | ||
| 2709 | RedisModuleString *RM_CreateStringFromDouble(RedisModuleCtx *ctx, double d) { | ||
| 2710 | char buf[MAX_D2STRING_CHARS]; | ||
| 2711 | size_t len = d2string(buf,sizeof(buf),d); | ||
| 2712 | return RM_CreateString(ctx,buf,len); | ||
| 2713 | } | ||
| 2714 | |||
| 2715 | /* Like RedisModule_CreateString(), but creates a string starting from a long | ||
| 2716 | * double. | ||
| 2717 | * | ||
| 2718 | * The returned string must be released with RedisModule_FreeString() or by | ||
| 2719 | * enabling automatic memory management. | ||
| 2720 | * | ||
| 2721 | * The passed context 'ctx' may be NULL if necessary, see the | ||
| 2722 | * RedisModule_CreateString() documentation for more info. */ | ||
| 2723 | RedisModuleString *RM_CreateStringFromLongDouble(RedisModuleCtx *ctx, long double ld, int humanfriendly) { | ||
| 2724 | char buf[MAX_LONG_DOUBLE_CHARS]; | ||
| 2725 | size_t len = ld2string(buf,sizeof(buf),ld, | ||
| 2726 | (humanfriendly ? LD_STR_HUMAN : LD_STR_AUTO)); | ||
| 2727 | return RM_CreateString(ctx,buf,len); | ||
| 2728 | } | ||
| 2729 | |||
| 2730 | /* Like RedisModule_CreateString(), but creates a string starting from another | ||
| 2731 | * RedisModuleString. | ||
| 2732 | * | ||
| 2733 | * The returned string must be released with RedisModule_FreeString() or by | ||
| 2734 | * enabling automatic memory management. | ||
| 2735 | * | ||
| 2736 | * The passed context 'ctx' may be NULL if necessary, see the | ||
| 2737 | * RedisModule_CreateString() documentation for more info. */ | ||
| 2738 | RedisModuleString *RM_CreateStringFromString(RedisModuleCtx *ctx, const RedisModuleString *str) { | ||
| 2739 | RedisModuleString *o = dupStringObject(str); | ||
| 2740 | if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_STRING,o); | ||
| 2741 | return o; | ||
| 2742 | } | ||
| 2743 | |||
| 2744 | /* Creates a string from a stream ID. The returned string must be released with | ||
| 2745 | * RedisModule_FreeString(), unless automatic memory is enabled. | ||
| 2746 | * | ||
| 2747 | * The passed context `ctx` may be NULL if necessary. See the | ||
| 2748 | * RedisModule_CreateString() documentation for more info. */ | ||
| 2749 | RedisModuleString *RM_CreateStringFromStreamID(RedisModuleCtx *ctx, const RedisModuleStreamID *id) { | ||
| 2750 | streamID streamid = {id->ms, id->seq}; | ||
| 2751 | RedisModuleString *o = createObjectFromStreamID(&streamid); | ||
| 2752 | if (ctx != NULL) autoMemoryAdd(ctx, REDISMODULE_AM_STRING, o); | ||
| 2753 | return o; | ||
| 2754 | } | ||
| 2755 | |||
| 2756 | /* Free a module string object obtained with one of the Redis modules API calls | ||
| 2757 | * that return new string objects. | ||
| 2758 | * | ||
| 2759 | * It is possible to call this function even when automatic memory management | ||
| 2760 | * is enabled. In that case the string will be released ASAP and removed | ||
| 2761 | * from the pool of string to release at the end. | ||
| 2762 | * | ||
| 2763 | * If the string was created with a NULL context 'ctx', it is also possible to | ||
| 2764 | * pass ctx as NULL when releasing the string (but passing a context will not | ||
| 2765 | * create any issue). Strings created with a context should be freed also passing | ||
| 2766 | * the context, so if you want to free a string out of context later, make sure | ||
| 2767 | * to create it using a NULL context. | ||
| 2768 | * | ||
| 2769 | * This API is not thread safe, access to these retained strings (if they originated | ||
| 2770 | * from a client command arguments) must be done with GIL locked. */ | ||
| 2771 | void RM_FreeString(RedisModuleCtx *ctx, RedisModuleString *str) { | ||
| 2772 | decrRefCount(str); | ||
| 2773 | if (ctx != NULL) autoMemoryFreed(ctx,REDISMODULE_AM_STRING,str); | ||
| 2774 | } | ||
| 2775 | |||
| 2776 | /* Every call to this function, will make the string 'str' requiring | ||
| 2777 | * an additional call to RedisModule_FreeString() in order to really | ||
| 2778 | * free the string. Note that the automatic freeing of the string obtained | ||
| 2779 | * enabling modules automatic memory management counts for one | ||
| 2780 | * RedisModule_FreeString() call (it is just executed automatically). | ||
| 2781 | * | ||
| 2782 | * Normally you want to call this function when, at the same time | ||
| 2783 | * the following conditions are true: | ||
| 2784 | * | ||
| 2785 | * 1. You have automatic memory management enabled. | ||
| 2786 | * 2. You want to create string objects. | ||
| 2787 | * 3. Those string objects you create need to live *after* the callback | ||
| 2788 | * function(for example a command implementation) creating them returns. | ||
| 2789 | * | ||
| 2790 | * Usually you want this in order to store the created string object | ||
| 2791 | * into your own data structure, for example when implementing a new data | ||
| 2792 | * type. | ||
| 2793 | * | ||
| 2794 | * Note that when memory management is turned off, you don't need | ||
| 2795 | * any call to RetainString() since creating a string will always result | ||
| 2796 | * into a string that lives after the callback function returns, if | ||
| 2797 | * no FreeString() call is performed. | ||
| 2798 | * | ||
| 2799 | * It is possible to call this function with a NULL context. | ||
| 2800 | * | ||
| 2801 | * When strings are going to be retained for an extended duration, it is good | ||
| 2802 | * practice to also call RedisModule_TrimStringAllocation() in order to | ||
| 2803 | * optimize memory usage. | ||
| 2804 | * | ||
| 2805 | * Threaded modules that reference retained strings from other threads *must* | ||
| 2806 | * explicitly trim the allocation as soon as the string is retained. Not doing | ||
| 2807 | * so may result with automatic trimming which is not thread safe. | ||
| 2808 | * | ||
| 2809 | * This API is not thread safe, access to these retained strings (if they originated | ||
| 2810 | * from a client command arguments) must be done with GIL locked. */ | ||
| 2811 | void RM_RetainString(RedisModuleCtx *ctx, RedisModuleString *str) { | ||
| 2812 | if (ctx == NULL || !autoMemoryFreed(ctx,REDISMODULE_AM_STRING,str)) { | ||
| 2813 | /* Increment the string reference counting only if we can't | ||
| 2814 | * just remove the object from the list of objects that should | ||
| 2815 | * be reclaimed. Why we do that, instead of just incrementing | ||
| 2816 | * the refcount in any case, and let the automatic FreeString() | ||
| 2817 | * call at the end to bring the refcount back at the desired | ||
| 2818 | * value? Because this way we ensure that the object refcount | ||
| 2819 | * value is 1 (instead of going to 2 to be dropped later to 1) | ||
| 2820 | * after the call to this function. This is needed for functions | ||
| 2821 | * like RedisModule_StringAppendBuffer() to work. */ | ||
| 2822 | incrRefCount(str); | ||
| 2823 | } | ||
| 2824 | } | ||
| 2825 | |||
| 2826 | /** | ||
| 2827 | * This function can be used instead of RedisModule_RetainString(). | ||
| 2828 | * The main difference between the two is that this function will always | ||
| 2829 | * succeed, whereas RedisModule_RetainString() may fail because of an | ||
| 2830 | * assertion. | ||
| 2831 | * | ||
| 2832 | * The function returns a pointer to RedisModuleString, which is owned | ||
| 2833 | * by the caller. It requires a call to RedisModule_FreeString() to free | ||
| 2834 | * the string when automatic memory management is disabled for the context. | ||
| 2835 | * When automatic memory management is enabled, you can either call | ||
| 2836 | * RedisModule_FreeString() or let the automation free it. | ||
| 2837 | * | ||
| 2838 | * This function is more efficient than RedisModule_CreateStringFromString() | ||
| 2839 | * because whenever possible, it avoids copying the underlying | ||
| 2840 | * RedisModuleString. The disadvantage of using this function is that it | ||
| 2841 | * might not be possible to use RedisModule_StringAppendBuffer() on the | ||
| 2842 | * returned RedisModuleString. | ||
| 2843 | * | ||
| 2844 | * It is possible to call this function with a NULL context. | ||
| 2845 | * | ||
| 2846 | * When strings are going to be held for an extended duration, it is good | ||
| 2847 | * practice to also call RedisModule_TrimStringAllocation() in order to | ||
| 2848 | * optimize memory usage. | ||
| 2849 | * | ||
| 2850 | * Threaded modules that reference held strings from other threads *must* | ||
| 2851 | * explicitly trim the allocation as soon as the string is held. Not doing | ||
| 2852 | * so may result with automatic trimming which is not thread safe. | ||
| 2853 | * | ||
| 2854 | * This API is not thread safe, access to these retained strings (if they originated | ||
| 2855 | * from a client command arguments) must be done with GIL locked. */ | ||
| 2856 | RedisModuleString* RM_HoldString(RedisModuleCtx *ctx, RedisModuleString *str) { | ||
| 2857 | if (str->refcount == OBJ_STATIC_REFCOUNT) { | ||
| 2858 | return RM_CreateStringFromString(ctx, str); | ||
| 2859 | } | ||
| 2860 | |||
| 2861 | incrRefCount(str); | ||
| 2862 | if (ctx != NULL) { | ||
| 2863 | /* | ||
| 2864 | * Put the str in the auto memory management of the ctx. | ||
| 2865 | * It might already be there, in this case, the ref count will | ||
| 2866 | * be 2 and we will decrease the ref count twice and free the | ||
| 2867 | * object in the auto memory free function. | ||
| 2868 | * | ||
| 2869 | * Why we can not do the same trick of just remove the object | ||
| 2870 | * from the auto memory (like in RM_RetainString)? | ||
| 2871 | * This code shows the issue: | ||
| 2872 | * | ||
| 2873 | * RM_AutoMemory(ctx); | ||
| 2874 | * str1 = RM_CreateString(ctx, "test", 4); | ||
| 2875 | * str2 = RM_HoldString(ctx, str1); | ||
| 2876 | * RM_FreeString(str1); | ||
| 2877 | * RM_FreeString(str2); | ||
| 2878 | * | ||
| 2879 | * If after the RM_HoldString we would just remove the string from | ||
| 2880 | * the auto memory, this example will cause access to a freed memory | ||
| 2881 | * on 'RM_FreeString(str2);' because the String will be free | ||
| 2882 | * on 'RM_FreeString(str1);'. | ||
| 2883 | * | ||
| 2884 | * So it's safer to just increase the ref count | ||
| 2885 | * and add the String to auto memory again. | ||
| 2886 | * | ||
| 2887 | * The limitation is that it is not possible to use RedisModule_StringAppendBuffer | ||
| 2888 | * on the String. | ||
| 2889 | */ | ||
| 2890 | autoMemoryAdd(ctx,REDISMODULE_AM_STRING,str); | ||
| 2891 | } | ||
| 2892 | return str; | ||
| 2893 | } | ||
| 2894 | |||
| 2895 | /* Given a string module object, this function returns the string pointer | ||
| 2896 | * and length of the string. The returned pointer and length should only | ||
| 2897 | * be used for read only accesses and never modified. */ | ||
| 2898 | const char *RM_StringPtrLen(const RedisModuleString *str, size_t *len) { | ||
| 2899 | if (str == NULL) { | ||
| 2900 | const char *errmsg = "(NULL string reply referenced in module)"; | ||
| 2901 | if (len) *len = strlen(errmsg); | ||
| 2902 | return errmsg; | ||
| 2903 | } | ||
| 2904 | if (len) *len = sdslen(str->ptr); | ||
| 2905 | return str->ptr; | ||
| 2906 | } | ||
| 2907 | |||
| 2908 | /* -------------------------------------------------------------------------- | ||
| 2909 | * Higher level string operations | ||
| 2910 | * ------------------------------------------------------------------------- */ | ||
| 2911 | |||
| 2912 | /* Convert the string into a `long long` integer, storing it at `*ll`. | ||
| 2913 | * Returns REDISMODULE_OK on success. If the string can't be parsed | ||
| 2914 | * as a valid, strict `long long` (no spaces before/after), REDISMODULE_ERR | ||
| 2915 | * is returned. */ | ||
| 2916 | int RM_StringToLongLong(const RedisModuleString *str, long long *ll) { | ||
| 2917 | return string2ll(str->ptr,sdslen(str->ptr),ll) ? REDISMODULE_OK : | ||
| 2918 | REDISMODULE_ERR; | ||
| 2919 | } | ||
| 2920 | |||
| 2921 | /* Convert the string into a `unsigned long long` integer, storing it at `*ull`. | ||
| 2922 | * Returns REDISMODULE_OK on success. If the string can't be parsed | ||
| 2923 | * as a valid, strict `unsigned long long` (no spaces before/after), REDISMODULE_ERR | ||
| 2924 | * is returned. */ | ||
| 2925 | int RM_StringToULongLong(const RedisModuleString *str, unsigned long long *ull) { | ||
| 2926 | return string2ull(str->ptr,ull) ? REDISMODULE_OK : REDISMODULE_ERR; | ||
| 2927 | } | ||
| 2928 | |||
| 2929 | /* Convert the string into a double, storing it at `*d`. | ||
| 2930 | * Returns REDISMODULE_OK on success or REDISMODULE_ERR if the string is | ||
| 2931 | * not a valid string representation of a double value. */ | ||
| 2932 | int RM_StringToDouble(const RedisModuleString *str, double *d) { | ||
| 2933 | int retval = getDoubleFromObject(str,d); | ||
| 2934 | return (retval == C_OK) ? REDISMODULE_OK : REDISMODULE_ERR; | ||
| 2935 | } | ||
| 2936 | |||
| 2937 | /* Convert the string into a long double, storing it at `*ld`. | ||
| 2938 | * Returns REDISMODULE_OK on success or REDISMODULE_ERR if the string is | ||
| 2939 | * not a valid string representation of a double value. */ | ||
| 2940 | int RM_StringToLongDouble(const RedisModuleString *str, long double *ld) { | ||
| 2941 | int retval = string2ld(str->ptr,sdslen(str->ptr),ld); | ||
| 2942 | return retval ? REDISMODULE_OK : REDISMODULE_ERR; | ||
| 2943 | } | ||
| 2944 | |||
| 2945 | /* Convert the string into a stream ID, storing it at `*id`. | ||
| 2946 | * Returns REDISMODULE_OK on success and returns REDISMODULE_ERR if the string | ||
| 2947 | * is not a valid string representation of a stream ID. The special IDs "+" and | ||
| 2948 | * "-" are allowed. | ||
| 2949 | */ | ||
| 2950 | int RM_StringToStreamID(const RedisModuleString *str, RedisModuleStreamID *id) { | ||
| 2951 | streamID streamid; | ||
| 2952 | if (streamParseID(str, &streamid) == C_OK) { | ||
| 2953 | id->ms = streamid.ms; | ||
| 2954 | id->seq = streamid.seq; | ||
| 2955 | return REDISMODULE_OK; | ||
| 2956 | } else { | ||
| 2957 | return REDISMODULE_ERR; | ||
| 2958 | } | ||
| 2959 | } | ||
| 2960 | |||
| 2961 | /* Compare two string objects, returning -1, 0 or 1 respectively if | ||
| 2962 | * a < b, a == b, a > b. Strings are compared byte by byte as two | ||
| 2963 | * binary blobs without any encoding care / collation attempt. */ | ||
| 2964 | int RM_StringCompare(const RedisModuleString *a, const RedisModuleString *b) { | ||
| 2965 | return compareStringObjects(a,b); | ||
| 2966 | } | ||
| 2967 | |||
| 2968 | /* Return the (possibly modified in encoding) input 'str' object if | ||
| 2969 | * the string is unshared, otherwise NULL is returned. */ | ||
| 2970 | RedisModuleString *moduleAssertUnsharedString(RedisModuleString *str) { | ||
| 2971 | if (str->refcount != 1) { | ||
| 2972 | serverLog(LL_WARNING, | ||
| 2973 | "Module attempted to use an in-place string modify operation " | ||
| 2974 | "with a string referenced multiple times. Please check the code " | ||
| 2975 | "for API usage correctness."); | ||
| 2976 | return NULL; | ||
| 2977 | } | ||
| 2978 | if (str->encoding == OBJ_ENCODING_EMBSTR) { | ||
| 2979 | /* Note: here we "leak" the additional allocation that was | ||
| 2980 | * used in order to store the embedded string in the object. */ | ||
| 2981 | str->ptr = sdsnewlen(str->ptr,sdslen(str->ptr)); | ||
| 2982 | str->encoding = OBJ_ENCODING_RAW; | ||
| 2983 | } else if (str->encoding == OBJ_ENCODING_INT) { | ||
| 2984 | /* Convert the string from integer to raw encoding. */ | ||
| 2985 | str->ptr = sdsfromlonglong((long)str->ptr); | ||
| 2986 | str->encoding = OBJ_ENCODING_RAW; | ||
| 2987 | } | ||
| 2988 | return str; | ||
| 2989 | } | ||
| 2990 | |||
| 2991 | /* Append the specified buffer to the string 'str'. The string must be a | ||
| 2992 | * string created by the user that is referenced only a single time, otherwise | ||
| 2993 | * REDISMODULE_ERR is returned and the operation is not performed. */ | ||
| 2994 | int RM_StringAppendBuffer(RedisModuleCtx *ctx, RedisModuleString *str, const char *buf, size_t len) { | ||
| 2995 | UNUSED(ctx); | ||
| 2996 | str = moduleAssertUnsharedString(str); | ||
| 2997 | if (str == NULL) return REDISMODULE_ERR; | ||
| 2998 | str->ptr = sdscatlen(str->ptr,buf,len); | ||
| 2999 | return REDISMODULE_OK; | ||
| 3000 | } | ||
| 3001 | |||
| 3002 | /* Trim possible excess memory allocated for a RedisModuleString. | ||
| 3003 | * | ||
| 3004 | * Sometimes a RedisModuleString may have more memory allocated for | ||
| 3005 | * it than required, typically for argv arguments that were constructed | ||
| 3006 | * from network buffers. This function optimizes such strings by reallocating | ||
| 3007 | * their memory, which is useful for strings that are not short lived but | ||
| 3008 | * retained for an extended duration. | ||
| 3009 | * | ||
| 3010 | * This operation is *not thread safe* and should only be called when | ||
| 3011 | * no concurrent access to the string is guaranteed. Using it for an argv | ||
| 3012 | * string in a module command before the string is potentially available | ||
| 3013 | * to other threads is generally safe. | ||
| 3014 | * | ||
| 3015 | * Currently, Redis may also automatically trim retained strings when a | ||
| 3016 | * module command returns. However, doing this explicitly should still be | ||
| 3017 | * a preferred option: | ||
| 3018 | * | ||
| 3019 | * 1. Future versions of Redis may abandon auto-trimming. | ||
| 3020 | * 2. Auto-trimming as currently implemented is *not thread safe*. | ||
| 3021 | * A background thread manipulating a recently retained string may end up | ||
| 3022 | * in a race condition with the auto-trim, which could result with | ||
| 3023 | * data corruption. | ||
| 3024 | */ | ||
| 3025 | void RM_TrimStringAllocation(RedisModuleString *str) { | ||
| 3026 | if (!str) return; | ||
| 3027 | trimStringObjectIfNeeded(str, 1); | ||
| 3028 | } | ||
| 3029 | |||
| 3030 | /* -------------------------------------------------------------------------- | ||
| 3031 | * ## Reply APIs | ||
| 3032 | * | ||
| 3033 | * These functions are used for sending replies to the client. | ||
| 3034 | * | ||
| 3035 | * Most functions always return REDISMODULE_OK so you can use it with | ||
| 3036 | * 'return' in order to return from the command implementation with: | ||
| 3037 | * | ||
| 3038 | * if (... some condition ...) | ||
| 3039 | * return RedisModule_ReplyWithLongLong(ctx,mycount); | ||
| 3040 | * | ||
| 3041 | * ### Reply with collection functions | ||
| 3042 | * | ||
| 3043 | * After starting a collection reply, the module must make calls to other | ||
| 3044 | * `ReplyWith*` style functions in order to emit the elements of the collection. | ||
| 3045 | * Collection types include: Array, Map, Set and Attribute. | ||
| 3046 | * | ||
| 3047 | * When producing collections with a number of elements that is not known | ||
| 3048 | * beforehand, the function can be called with a special flag | ||
| 3049 | * REDISMODULE_POSTPONED_LEN (REDISMODULE_POSTPONED_ARRAY_LEN in the past), | ||
| 3050 | * and the actual number of elements can be later set with RM_ReplySet*Length() | ||
| 3051 | * call (which will set the latest "open" count if there are multiple ones). | ||
| 3052 | * -------------------------------------------------------------------------- */ | ||
| 3053 | |||
| 3054 | /* Send an error about the number of arguments given to the command, | ||
| 3055 | * citing the command name in the error message. Returns REDISMODULE_OK. | ||
| 3056 | * | ||
| 3057 | * Example: | ||
| 3058 | * | ||
| 3059 | * if (argc != 3) return RedisModule_WrongArity(ctx); | ||
| 3060 | */ | ||
| 3061 | int RM_WrongArity(RedisModuleCtx *ctx) { | ||
| 3062 | addReplyErrorArity(ctx->client); | ||
| 3063 | return REDISMODULE_OK; | ||
| 3064 | } | ||
| 3065 | |||
| 3066 | /* Return the client object the `RM_Reply*` functions should target. | ||
| 3067 | * Normally this is just `ctx->client`, that is the client that called | ||
| 3068 | * the module command, however in the case of thread safe contexts there | ||
| 3069 | * is no directly associated client (since it would not be safe to access | ||
| 3070 | * the client from a thread), so instead the blocked client object referenced | ||
| 3071 | * in the thread safe context, has a fake client that we just use to accumulate | ||
| 3072 | * the replies. Later, when the client is unblocked, the accumulated replies | ||
| 3073 | * are appended to the actual client. | ||
| 3074 | * | ||
| 3075 | * The function returns the client pointer depending on the context, or | ||
| 3076 | * NULL if there is no potential client. This happens when we are in the | ||
| 3077 | * context of a thread safe context that was not initialized with a blocked | ||
| 3078 | * client object. Other contexts without associated clients are the ones | ||
| 3079 | * initialized to run the timers callbacks. */ | ||
| 3080 | client *moduleGetReplyClient(RedisModuleCtx *ctx) { | ||
| 3081 | if (ctx->flags & REDISMODULE_CTX_THREAD_SAFE) { | ||
| 3082 | if (ctx->blocked_client) | ||
| 3083 | return ctx->blocked_client->reply_client; | ||
| 3084 | else | ||
| 3085 | return NULL; | ||
| 3086 | } else { | ||
| 3087 | /* If this is a non thread safe context, just return the client | ||
| 3088 | * that is running the command if any. This may be NULL as well | ||
| 3089 | * in the case of contexts that are not executed with associated | ||
| 3090 | * clients, like timer contexts. */ | ||
| 3091 | return ctx->client; | ||
| 3092 | } | ||
| 3093 | } | ||
| 3094 | |||
| 3095 | /* Send an integer reply to the client, with the specified `long long` value. | ||
| 3096 | * The function always returns REDISMODULE_OK. */ | ||
| 3097 | int RM_ReplyWithLongLong(RedisModuleCtx *ctx, long long ll) { | ||
| 3098 | client *c = moduleGetReplyClient(ctx); | ||
| 3099 | if (c == NULL) return REDISMODULE_OK; | ||
| 3100 | addReplyLongLong(c,ll); | ||
| 3101 | return REDISMODULE_OK; | ||
| 3102 | } | ||
| 3103 | |||
| 3104 | /* Reply with the error 'err'. | ||
| 3105 | * | ||
| 3106 | * Note that 'err' must contain all the error, including | ||
| 3107 | * the initial error code. The function only provides the initial "-", so | ||
| 3108 | * the usage is, for example: | ||
| 3109 | * | ||
| 3110 | * RedisModule_ReplyWithError(ctx,"ERR Wrong Type"); | ||
| 3111 | * | ||
| 3112 | * and not just: | ||
| 3113 | * | ||
| 3114 | * RedisModule_ReplyWithError(ctx,"Wrong Type"); | ||
| 3115 | * | ||
| 3116 | * The function always returns REDISMODULE_OK. | ||
| 3117 | */ | ||
| 3118 | int RM_ReplyWithError(RedisModuleCtx *ctx, const char *err) { | ||
| 3119 | client *c = moduleGetReplyClient(ctx); | ||
| 3120 | if (c == NULL) return REDISMODULE_OK; | ||
| 3121 | addReplyErrorFormat(c,"-%s",err); | ||
| 3122 | return REDISMODULE_OK; | ||
| 3123 | } | ||
| 3124 | |||
| 3125 | /* Reply with the error create from a printf format and arguments. | ||
| 3126 | * | ||
| 3127 | * Note that 'fmt' must contain all the error, including | ||
| 3128 | * the initial error code. The function only provides the initial "-", so | ||
| 3129 | * the usage is, for example: | ||
| 3130 | * | ||
| 3131 | * RedisModule_ReplyWithErrorFormat(ctx,"ERR Wrong Type: %s",type); | ||
| 3132 | * | ||
| 3133 | * and not just: | ||
| 3134 | * | ||
| 3135 | * RedisModule_ReplyWithErrorFormat(ctx,"Wrong Type: %s",type); | ||
| 3136 | * | ||
| 3137 | * The function always returns REDISMODULE_OK. | ||
| 3138 | */ | ||
| 3139 | int RM_ReplyWithErrorFormat(RedisModuleCtx *ctx, const char *fmt, ...) { | ||
| 3140 | client *c = moduleGetReplyClient(ctx); | ||
| 3141 | if (c == NULL) return REDISMODULE_OK; | ||
| 3142 | |||
| 3143 | int len = strlen(fmt) + 2; /* 1 for the \0 and 1 for the hyphen */ | ||
| 3144 | char *hyphenfmt = zmalloc(len); | ||
| 3145 | snprintf(hyphenfmt, len, "-%s", fmt); | ||
| 3146 | |||
| 3147 | va_list ap; | ||
| 3148 | va_start(ap, fmt); | ||
| 3149 | addReplyErrorFormatInternal(c, 0, hyphenfmt, ap); | ||
| 3150 | va_end(ap); | ||
| 3151 | |||
| 3152 | zfree(hyphenfmt); | ||
| 3153 | |||
| 3154 | return REDISMODULE_OK; | ||
| 3155 | } | ||
| 3156 | |||
| 3157 | /* Reply with a simple string (`+... \r\n` in RESP protocol). This replies | ||
| 3158 | * are suitable only when sending a small non-binary string with small | ||
| 3159 | * overhead, like "OK" or similar replies. | ||
| 3160 | * | ||
| 3161 | * The function always returns REDISMODULE_OK. */ | ||
| 3162 | int RM_ReplyWithSimpleString(RedisModuleCtx *ctx, const char *msg) { | ||
| 3163 | client *c = moduleGetReplyClient(ctx); | ||
| 3164 | if (c == NULL) return REDISMODULE_OK; | ||
| 3165 | addReplyProto(c,"+",1); | ||
| 3166 | addReplyProto(c,msg,strlen(msg)); | ||
| 3167 | addReplyProto(c,"\r\n",2); | ||
| 3168 | return REDISMODULE_OK; | ||
| 3169 | } | ||
| 3170 | |||
| 3171 | #define COLLECTION_REPLY_ARRAY 1 | ||
| 3172 | #define COLLECTION_REPLY_MAP 2 | ||
| 3173 | #define COLLECTION_REPLY_SET 3 | ||
| 3174 | #define COLLECTION_REPLY_ATTRIBUTE 4 | ||
| 3175 | |||
| 3176 | int moduleReplyWithCollection(RedisModuleCtx *ctx, long len, int type) { | ||
| 3177 | client *c = moduleGetReplyClient(ctx); | ||
| 3178 | if (c == NULL) return REDISMODULE_OK; | ||
| 3179 | if (len == REDISMODULE_POSTPONED_LEN) { | ||
| 3180 | ctx->postponed_arrays = zrealloc(ctx->postponed_arrays,sizeof(void*)* | ||
| 3181 | (ctx->postponed_arrays_count+1)); | ||
| 3182 | ctx->postponed_arrays[ctx->postponed_arrays_count] = | ||
| 3183 | addReplyDeferredLen(c); | ||
| 3184 | ctx->postponed_arrays_count++; | ||
| 3185 | } else if (len == 0) { | ||
| 3186 | switch (type) { | ||
| 3187 | case COLLECTION_REPLY_ARRAY: | ||
| 3188 | addReply(c, shared.emptyarray); | ||
| 3189 | break; | ||
| 3190 | case COLLECTION_REPLY_MAP: | ||
| 3191 | addReply(c, shared.emptymap[c->resp]); | ||
| 3192 | break; | ||
| 3193 | case COLLECTION_REPLY_SET: | ||
| 3194 | addReply(c, shared.emptyset[c->resp]); | ||
| 3195 | break; | ||
| 3196 | case COLLECTION_REPLY_ATTRIBUTE: | ||
| 3197 | addReplyAttributeLen(c,len); | ||
| 3198 | break; | ||
| 3199 | default: | ||
| 3200 | serverPanic("Invalid module empty reply type %d", type); } | ||
| 3201 | } else { | ||
| 3202 | switch (type) { | ||
| 3203 | case COLLECTION_REPLY_ARRAY: | ||
| 3204 | addReplyArrayLen(c,len); | ||
| 3205 | break; | ||
| 3206 | case COLLECTION_REPLY_MAP: | ||
| 3207 | addReplyMapLen(c,len); | ||
| 3208 | break; | ||
| 3209 | case COLLECTION_REPLY_SET: | ||
| 3210 | addReplySetLen(c,len); | ||
| 3211 | break; | ||
| 3212 | case COLLECTION_REPLY_ATTRIBUTE: | ||
| 3213 | addReplyAttributeLen(c,len); | ||
| 3214 | break; | ||
| 3215 | default: | ||
| 3216 | serverPanic("Invalid module reply type %d", type); | ||
| 3217 | } | ||
| 3218 | } | ||
| 3219 | return REDISMODULE_OK; | ||
| 3220 | } | ||
| 3221 | |||
| 3222 | /* Reply with an array type of 'len' elements. | ||
| 3223 | * | ||
| 3224 | * After starting an array reply, the module must make `len` calls to other | ||
| 3225 | * `ReplyWith*` style functions in order to emit the elements of the array. | ||
| 3226 | * See Reply APIs section for more details. | ||
| 3227 | * | ||
| 3228 | * Use RM_ReplySetArrayLength() to set deferred length. | ||
| 3229 | * | ||
| 3230 | * The function always returns REDISMODULE_OK. */ | ||
| 3231 | int RM_ReplyWithArray(RedisModuleCtx *ctx, long len) { | ||
| 3232 | return moduleReplyWithCollection(ctx, len, COLLECTION_REPLY_ARRAY); | ||
| 3233 | } | ||
| 3234 | |||
| 3235 | /* Reply with a RESP3 Map type of 'len' pairs. | ||
| 3236 | * Visit https://github.com/antirez/RESP3/blob/master/spec.md for more info about RESP3. | ||
| 3237 | * | ||
| 3238 | * After starting a map reply, the module must make `len*2` calls to other | ||
| 3239 | * `ReplyWith*` style functions in order to emit the elements of the map. | ||
| 3240 | * See Reply APIs section for more details. | ||
| 3241 | * | ||
| 3242 | * If the connected client is using RESP2, the reply will be converted to a flat | ||
| 3243 | * array. | ||
| 3244 | * | ||
| 3245 | * Use RM_ReplySetMapLength() to set deferred length. | ||
| 3246 | * | ||
| 3247 | * The function always returns REDISMODULE_OK. */ | ||
| 3248 | int RM_ReplyWithMap(RedisModuleCtx *ctx, long len) { | ||
| 3249 | return moduleReplyWithCollection(ctx, len, COLLECTION_REPLY_MAP); | ||
| 3250 | } | ||
| 3251 | |||
| 3252 | /* Reply with a RESP3 Set type of 'len' elements. | ||
| 3253 | * Visit https://github.com/antirez/RESP3/blob/master/spec.md for more info about RESP3. | ||
| 3254 | * | ||
| 3255 | * After starting a set reply, the module must make `len` calls to other | ||
| 3256 | * `ReplyWith*` style functions in order to emit the elements of the set. | ||
| 3257 | * See Reply APIs section for more details. | ||
| 3258 | * | ||
| 3259 | * If the connected client is using RESP2, the reply will be converted to an | ||
| 3260 | * array type. | ||
| 3261 | * | ||
| 3262 | * Use RM_ReplySetSetLength() to set deferred length. | ||
| 3263 | * | ||
| 3264 | * The function always returns REDISMODULE_OK. */ | ||
| 3265 | int RM_ReplyWithSet(RedisModuleCtx *ctx, long len) { | ||
| 3266 | return moduleReplyWithCollection(ctx, len, COLLECTION_REPLY_SET); | ||
| 3267 | } | ||
| 3268 | |||
| 3269 | |||
| 3270 | /* Add attributes (metadata) to the reply. Should be done before adding the | ||
| 3271 | * actual reply. see https://github.com/antirez/RESP3/blob/master/spec.md#attribute-type | ||
| 3272 | * | ||
| 3273 | * After starting an attribute's reply, the module must make `len*2` calls to other | ||
| 3274 | * `ReplyWith*` style functions in order to emit the elements of the attribute map. | ||
| 3275 | * See Reply APIs section for more details. | ||
| 3276 | * | ||
| 3277 | * Use RM_ReplySetAttributeLength() to set deferred length. | ||
| 3278 | * | ||
| 3279 | * Not supported by RESP2 and will return REDISMODULE_ERR, otherwise | ||
| 3280 | * the function always returns REDISMODULE_OK. */ | ||
| 3281 | int RM_ReplyWithAttribute(RedisModuleCtx *ctx, long len) { | ||
| 3282 | if (ctx->client->resp == 2) return REDISMODULE_ERR; | ||
| 3283 | |||
| 3284 | return moduleReplyWithCollection(ctx, len, COLLECTION_REPLY_ATTRIBUTE); | ||
| 3285 | } | ||
| 3286 | |||
| 3287 | /* Reply to the client with a null array, simply null in RESP3, | ||
| 3288 | * null array in RESP2. | ||
| 3289 | * | ||
| 3290 | * Note: In RESP3 there's no difference between Null reply and | ||
| 3291 | * NullArray reply, so to prevent ambiguity it's better to avoid | ||
| 3292 | * using this API and use RedisModule_ReplyWithNull instead. | ||
| 3293 | * | ||
| 3294 | * The function always returns REDISMODULE_OK. */ | ||
| 3295 | int RM_ReplyWithNullArray(RedisModuleCtx *ctx) { | ||
| 3296 | client *c = moduleGetReplyClient(ctx); | ||
| 3297 | if (c == NULL) return REDISMODULE_OK; | ||
| 3298 | addReplyNullArray(c); | ||
| 3299 | return REDISMODULE_OK; | ||
| 3300 | } | ||
| 3301 | |||
| 3302 | /* Reply to the client with an empty array. | ||
| 3303 | * | ||
| 3304 | * The function always returns REDISMODULE_OK. */ | ||
| 3305 | int RM_ReplyWithEmptyArray(RedisModuleCtx *ctx) { | ||
| 3306 | client *c = moduleGetReplyClient(ctx); | ||
| 3307 | if (c == NULL) return REDISMODULE_OK; | ||
| 3308 | addReply(c,shared.emptyarray); | ||
| 3309 | return REDISMODULE_OK; | ||
| 3310 | } | ||
| 3311 | |||
| 3312 | void moduleReplySetCollectionLength(RedisModuleCtx *ctx, long len, int type) { | ||
| 3313 | client *c = moduleGetReplyClient(ctx); | ||
| 3314 | if (c == NULL) return; | ||
| 3315 | if (ctx->postponed_arrays_count == 0) { | ||
| 3316 | serverLog(LL_WARNING, | ||
| 3317 | "API misuse detected in module %s: " | ||
| 3318 | "RedisModule_ReplySet*Length() called without previous " | ||
| 3319 | "RedisModule_ReplyWith*(ctx,REDISMODULE_POSTPONED_LEN) " | ||
| 3320 | "call.", ctx->module->name); | ||
| 3321 | return; | ||
| 3322 | } | ||
| 3323 | ctx->postponed_arrays_count--; | ||
| 3324 | switch(type) { | ||
| 3325 | case COLLECTION_REPLY_ARRAY: | ||
| 3326 | setDeferredArrayLen(c,ctx->postponed_arrays[ctx->postponed_arrays_count],len); | ||
| 3327 | break; | ||
| 3328 | case COLLECTION_REPLY_MAP: | ||
| 3329 | setDeferredMapLen(c,ctx->postponed_arrays[ctx->postponed_arrays_count],len); | ||
| 3330 | break; | ||
| 3331 | case COLLECTION_REPLY_SET: | ||
| 3332 | setDeferredSetLen(c,ctx->postponed_arrays[ctx->postponed_arrays_count],len); | ||
| 3333 | break; | ||
| 3334 | case COLLECTION_REPLY_ATTRIBUTE: | ||
| 3335 | setDeferredAttributeLen(c,ctx->postponed_arrays[ctx->postponed_arrays_count],len); | ||
| 3336 | break; | ||
| 3337 | default: | ||
| 3338 | serverPanic("Invalid module reply type %d", type); | ||
| 3339 | } | ||
| 3340 | if (ctx->postponed_arrays_count == 0) { | ||
| 3341 | zfree(ctx->postponed_arrays); | ||
| 3342 | ctx->postponed_arrays = NULL; | ||
| 3343 | } | ||
| 3344 | } | ||
| 3345 | |||
| 3346 | /* When RedisModule_ReplyWithArray() is used with the argument | ||
| 3347 | * REDISMODULE_POSTPONED_LEN, because we don't know beforehand the number | ||
| 3348 | * of items we are going to output as elements of the array, this function | ||
| 3349 | * will take care to set the array length. | ||
| 3350 | * | ||
| 3351 | * Since it is possible to have multiple array replies pending with unknown | ||
| 3352 | * length, this function guarantees to always set the latest array length | ||
| 3353 | * that was created in a postponed way. | ||
| 3354 | * | ||
| 3355 | * For example in order to output an array like [1,[10,20,30]] we | ||
| 3356 | * could write: | ||
| 3357 | * | ||
| 3358 | * RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_LEN); | ||
| 3359 | * RedisModule_ReplyWithLongLong(ctx,1); | ||
| 3360 | * RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_LEN); | ||
| 3361 | * RedisModule_ReplyWithLongLong(ctx,10); | ||
| 3362 | * RedisModule_ReplyWithLongLong(ctx,20); | ||
| 3363 | * RedisModule_ReplyWithLongLong(ctx,30); | ||
| 3364 | * RedisModule_ReplySetArrayLength(ctx,3); // Set len of 10,20,30 array. | ||
| 3365 | * RedisModule_ReplySetArrayLength(ctx,2); // Set len of top array | ||
| 3366 | * | ||
| 3367 | * Note that in the above example there is no reason to postpone the array | ||
| 3368 | * length, since we produce a fixed number of elements, but in the practice | ||
| 3369 | * the code may use an iterator or other ways of creating the output so | ||
| 3370 | * that is not easy to calculate in advance the number of elements. | ||
| 3371 | */ | ||
| 3372 | void RM_ReplySetArrayLength(RedisModuleCtx *ctx, long len) { | ||
| 3373 | moduleReplySetCollectionLength(ctx, len, COLLECTION_REPLY_ARRAY); | ||
| 3374 | } | ||
| 3375 | |||
| 3376 | /* Very similar to RedisModule_ReplySetArrayLength except `len` should | ||
| 3377 | * exactly half of the number of `ReplyWith*` functions called in the | ||
| 3378 | * context of the map. | ||
| 3379 | * Visit https://github.com/antirez/RESP3/blob/master/spec.md for more info about RESP3. */ | ||
| 3380 | void RM_ReplySetMapLength(RedisModuleCtx *ctx, long len) { | ||
| 3381 | moduleReplySetCollectionLength(ctx, len, COLLECTION_REPLY_MAP); | ||
| 3382 | } | ||
| 3383 | |||
| 3384 | /* Very similar to RedisModule_ReplySetArrayLength | ||
| 3385 | * Visit https://github.com/antirez/RESP3/blob/master/spec.md for more info about RESP3. */ | ||
| 3386 | void RM_ReplySetSetLength(RedisModuleCtx *ctx, long len) { | ||
| 3387 | moduleReplySetCollectionLength(ctx, len, COLLECTION_REPLY_SET); | ||
| 3388 | } | ||
| 3389 | |||
| 3390 | /* Very similar to RedisModule_ReplySetMapLength | ||
| 3391 | * Visit https://github.com/antirez/RESP3/blob/master/spec.md for more info about RESP3. | ||
| 3392 | * | ||
| 3393 | * Must not be called if RM_ReplyWithAttribute returned an error. */ | ||
| 3394 | void RM_ReplySetAttributeLength(RedisModuleCtx *ctx, long len) { | ||
| 3395 | if (ctx->client->resp == 2) return; | ||
| 3396 | moduleReplySetCollectionLength(ctx, len, COLLECTION_REPLY_ATTRIBUTE); | ||
| 3397 | } | ||
| 3398 | |||
| 3399 | /* Reply with a bulk string, taking in input a C buffer pointer and length. | ||
| 3400 | * | ||
| 3401 | * The function always returns REDISMODULE_OK. */ | ||
| 3402 | int RM_ReplyWithStringBuffer(RedisModuleCtx *ctx, const char *buf, size_t len) { | ||
| 3403 | client *c = moduleGetReplyClient(ctx); | ||
| 3404 | if (c == NULL) return REDISMODULE_OK; | ||
| 3405 | addReplyBulkCBuffer(c,(char*)buf,len); | ||
| 3406 | return REDISMODULE_OK; | ||
| 3407 | } | ||
| 3408 | |||
| 3409 | /* Reply with a bulk string, taking in input a C buffer pointer that is | ||
| 3410 | * assumed to be null-terminated. | ||
| 3411 | * | ||
| 3412 | * The function always returns REDISMODULE_OK. */ | ||
| 3413 | int RM_ReplyWithCString(RedisModuleCtx *ctx, const char *buf) { | ||
| 3414 | client *c = moduleGetReplyClient(ctx); | ||
| 3415 | if (c == NULL) return REDISMODULE_OK; | ||
| 3416 | addReplyBulkCString(c,(char*)buf); | ||
| 3417 | return REDISMODULE_OK; | ||
| 3418 | } | ||
| 3419 | |||
| 3420 | /* Reply with a bulk string, taking in input a RedisModuleString object. | ||
| 3421 | * | ||
| 3422 | * The function always returns REDISMODULE_OK. */ | ||
| 3423 | int RM_ReplyWithString(RedisModuleCtx *ctx, RedisModuleString *str) { | ||
| 3424 | client *c = moduleGetReplyClient(ctx); | ||
| 3425 | if (c == NULL) return REDISMODULE_OK; | ||
| 3426 | addReplyBulk(c,str); | ||
| 3427 | return REDISMODULE_OK; | ||
| 3428 | } | ||
| 3429 | |||
| 3430 | /* Reply with an empty string. | ||
| 3431 | * | ||
| 3432 | * The function always returns REDISMODULE_OK. */ | ||
| 3433 | int RM_ReplyWithEmptyString(RedisModuleCtx *ctx) { | ||
| 3434 | client *c = moduleGetReplyClient(ctx); | ||
| 3435 | if (c == NULL) return REDISMODULE_OK; | ||
| 3436 | addReply(c,shared.emptybulk); | ||
| 3437 | return REDISMODULE_OK; | ||
| 3438 | } | ||
| 3439 | |||
| 3440 | /* Reply with a binary safe string, which should not be escaped or filtered | ||
| 3441 | * taking in input a C buffer pointer, length and a 3 character type/extension. | ||
| 3442 | * | ||
| 3443 | * The function always returns REDISMODULE_OK. */ | ||
| 3444 | int RM_ReplyWithVerbatimStringType(RedisModuleCtx *ctx, const char *buf, size_t len, const char *ext) { | ||
| 3445 | client *c = moduleGetReplyClient(ctx); | ||
| 3446 | if (c == NULL) return REDISMODULE_OK; | ||
| 3447 | addReplyVerbatim(c, buf, len, ext); | ||
| 3448 | return REDISMODULE_OK; | ||
| 3449 | } | ||
| 3450 | |||
| 3451 | /* Reply with a binary safe string, which should not be escaped or filtered | ||
| 3452 | * taking in input a C buffer pointer and length. | ||
| 3453 | * | ||
| 3454 | * The function always returns REDISMODULE_OK. */ | ||
| 3455 | int RM_ReplyWithVerbatimString(RedisModuleCtx *ctx, const char *buf, size_t len) { | ||
| 3456 | return RM_ReplyWithVerbatimStringType(ctx, buf, len, "txt"); | ||
| 3457 | } | ||
| 3458 | |||
| 3459 | /* Reply to the client with a NULL. | ||
| 3460 | * | ||
| 3461 | * The function always returns REDISMODULE_OK. */ | ||
| 3462 | int RM_ReplyWithNull(RedisModuleCtx *ctx) { | ||
| 3463 | client *c = moduleGetReplyClient(ctx); | ||
| 3464 | if (c == NULL) return REDISMODULE_OK; | ||
| 3465 | addReplyNull(c); | ||
| 3466 | return REDISMODULE_OK; | ||
| 3467 | } | ||
| 3468 | |||
| 3469 | /* Reply with a RESP3 Boolean type. | ||
| 3470 | * Visit https://github.com/antirez/RESP3/blob/master/spec.md for more info about RESP3. | ||
| 3471 | * | ||
| 3472 | * In RESP3, this is boolean type | ||
| 3473 | * In RESP2, it's a string response of "1" and "0" for true and false respectively. | ||
| 3474 | * | ||
| 3475 | * The function always returns REDISMODULE_OK. */ | ||
| 3476 | int RM_ReplyWithBool(RedisModuleCtx *ctx, int b) { | ||
| 3477 | client *c = moduleGetReplyClient(ctx); | ||
| 3478 | if (c == NULL) return REDISMODULE_OK; | ||
| 3479 | addReplyBool(c,b); | ||
| 3480 | return REDISMODULE_OK; | ||
| 3481 | } | ||
| 3482 | |||
| 3483 | /* Reply exactly what a Redis command returned us with RedisModule_Call(). | ||
| 3484 | * This function is useful when we use RedisModule_Call() in order to | ||
| 3485 | * execute some command, as we want to reply to the client exactly the | ||
| 3486 | * same reply we obtained by the command. | ||
| 3487 | * | ||
| 3488 | * Return: | ||
| 3489 | * - REDISMODULE_OK on success. | ||
| 3490 | * - REDISMODULE_ERR if the given reply is in RESP3 format but the client expects RESP2. | ||
| 3491 | * In case of an error, it's the module writer responsibility to translate the reply | ||
| 3492 | * to RESP2 (or handle it differently by returning an error). Notice that for | ||
| 3493 | * module writer convenience, it is possible to pass `0` as a parameter to the fmt | ||
| 3494 | * argument of `RM_Call` so that the RedisModuleCallReply will return in the same | ||
| 3495 | * protocol (RESP2 or RESP3) as set in the current client's context. */ | ||
| 3496 | int RM_ReplyWithCallReply(RedisModuleCtx *ctx, RedisModuleCallReply *reply) { | ||
| 3497 | client *c = moduleGetReplyClient(ctx); | ||
| 3498 | if (c == NULL) return REDISMODULE_OK; | ||
| 3499 | if (c->resp == 2 && callReplyIsResp3(reply)) { | ||
| 3500 | /* The reply is in RESP3 format and the client is RESP2, | ||
| 3501 | * so it isn't possible to send this reply to the client. */ | ||
| 3502 | return REDISMODULE_ERR; | ||
| 3503 | } | ||
| 3504 | size_t proto_len; | ||
| 3505 | const char *proto = callReplyGetProto(reply, &proto_len); | ||
| 3506 | addReplyProto(c, proto, proto_len); | ||
| 3507 | /* Propagate the error list from that reply to the other client, to do some | ||
| 3508 | * post error reply handling, like statistics. | ||
| 3509 | * Note that if the original reply had an array with errors, and the module | ||
| 3510 | * replied with just a portion of the original reply, and not the entire | ||
| 3511 | * reply, the errors are currently not propagated and the errors stats | ||
| 3512 | * will not get propagated. */ | ||
| 3513 | list *errors = callReplyDeferredErrorList(reply); | ||
| 3514 | if (errors) | ||
| 3515 | deferredAfterErrorReply(c, errors); | ||
| 3516 | return REDISMODULE_OK; | ||
| 3517 | } | ||
| 3518 | |||
| 3519 | /* Reply with a RESP3 Double type. | ||
| 3520 | * Visit https://github.com/antirez/RESP3/blob/master/spec.md for more info about RESP3. | ||
| 3521 | * | ||
| 3522 | * Send a string reply obtained converting the double 'd' into a bulk string. | ||
| 3523 | * This function is basically equivalent to converting a double into | ||
| 3524 | * a string into a C buffer, and then calling the function | ||
| 3525 | * RedisModule_ReplyWithStringBuffer() with the buffer and length. | ||
| 3526 | * | ||
| 3527 | * In RESP3 the string is tagged as a double, while in RESP2 it's just a plain string | ||
| 3528 | * that the user will have to parse. | ||
| 3529 | * | ||
| 3530 | * The function always returns REDISMODULE_OK. */ | ||
| 3531 | int RM_ReplyWithDouble(RedisModuleCtx *ctx, double d) { | ||
| 3532 | client *c = moduleGetReplyClient(ctx); | ||
| 3533 | if (c == NULL) return REDISMODULE_OK; | ||
| 3534 | addReplyDouble(c,d); | ||
| 3535 | return REDISMODULE_OK; | ||
| 3536 | } | ||
| 3537 | |||
| 3538 | /* Reply with a RESP3 BigNumber type. | ||
| 3539 | * Visit https://github.com/antirez/RESP3/blob/master/spec.md for more info about RESP3. | ||
| 3540 | * | ||
| 3541 | * In RESP3, this is a string of length `len` that is tagged as a BigNumber, | ||
| 3542 | * however, it's up to the caller to ensure that it's a valid BigNumber. | ||
| 3543 | * In RESP2, this is just a plain bulk string response. | ||
| 3544 | * | ||
| 3545 | * The function always returns REDISMODULE_OK. */ | ||
| 3546 | int RM_ReplyWithBigNumber(RedisModuleCtx *ctx, const char *bignum, size_t len) { | ||
| 3547 | client *c = moduleGetReplyClient(ctx); | ||
| 3548 | if (c == NULL) return REDISMODULE_OK; | ||
| 3549 | addReplyBigNum(c, bignum, len); | ||
| 3550 | return REDISMODULE_OK; | ||
| 3551 | } | ||
| 3552 | |||
| 3553 | /* Send a string reply obtained converting the long double 'ld' into a bulk | ||
| 3554 | * string. This function is basically equivalent to converting a long double | ||
| 3555 | * into a string into a C buffer, and then calling the function | ||
| 3556 | * RedisModule_ReplyWithStringBuffer() with the buffer and length. | ||
| 3557 | * The double string uses human readable formatting (see | ||
| 3558 | * `addReplyHumanLongDouble` in networking.c). | ||
| 3559 | * | ||
| 3560 | * The function always returns REDISMODULE_OK. */ | ||
| 3561 | int RM_ReplyWithLongDouble(RedisModuleCtx *ctx, long double ld) { | ||
| 3562 | client *c = moduleGetReplyClient(ctx); | ||
| 3563 | if (c == NULL) return REDISMODULE_OK; | ||
| 3564 | addReplyHumanLongDouble(c, ld); | ||
| 3565 | return REDISMODULE_OK; | ||
| 3566 | } | ||
| 3567 | |||
| 3568 | /* -------------------------------------------------------------------------- | ||
| 3569 | * ## Commands replication API | ||
| 3570 | * -------------------------------------------------------------------------- */ | ||
| 3571 | |||
| 3572 | /* Replicate the specified command and arguments to slaves and AOF, as effect | ||
| 3573 | * of execution of the calling command implementation. | ||
| 3574 | * | ||
| 3575 | * The replicated commands are always wrapped into the MULTI/EXEC that | ||
| 3576 | * contains all the commands replicated in a given module command | ||
| 3577 | * execution, in the order they were executed. | ||
| 3578 | * | ||
| 3579 | * Modules should try to use one interface or the other. | ||
| 3580 | * | ||
| 3581 | * This command follows exactly the same interface of RedisModule_Call(), | ||
| 3582 | * so a set of format specifiers must be passed, followed by arguments | ||
| 3583 | * matching the provided format specifiers. | ||
| 3584 | * | ||
| 3585 | * Please refer to RedisModule_Call() for more information. | ||
| 3586 | * | ||
| 3587 | * Using the special "A" and "R" modifiers, the caller can exclude either | ||
| 3588 | * the AOF or the replicas from the propagation of the specified command. | ||
| 3589 | * Otherwise, by default, the command will be propagated in both channels. | ||
| 3590 | * | ||
| 3591 | * #### Note about calling this function from a thread safe context: | ||
| 3592 | * | ||
| 3593 | * Normally when you call this function from the callback implementing a | ||
| 3594 | * module command, or any other callback provided by the Redis Module API, | ||
| 3595 | * Redis will accumulate all the calls to this function in the context of | ||
| 3596 | * the callback, and will propagate all the commands wrapped in a MULTI/EXEC | ||
| 3597 | * transaction. However when calling this function from a threaded safe context | ||
| 3598 | * that can live an undefined amount of time, and can be locked/unlocked in | ||
| 3599 | * at will, it is important to note that this API is not thread-safe and | ||
| 3600 | * must be executed while holding the GIL. | ||
| 3601 | * | ||
| 3602 | * #### Return value | ||
| 3603 | * | ||
| 3604 | * The command returns REDISMODULE_ERR if the format specifiers are invalid | ||
| 3605 | * or the command name does not belong to a known command. */ | ||
| 3606 | int RM_Replicate(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...) { | ||
| 3607 | struct redisCommand *cmd; | ||
| 3608 | robj **argv = NULL; | ||
| 3609 | int argc = 0, flags = 0, j; | ||
| 3610 | va_list ap; | ||
| 3611 | |||
| 3612 | cmd = lookupCommandByCString((char*)cmdname); | ||
| 3613 | if (!cmd) return REDISMODULE_ERR; | ||
| 3614 | |||
| 3615 | /* Create the client and dispatch the command. */ | ||
| 3616 | va_start(ap, fmt); | ||
| 3617 | argv = moduleCreateArgvFromUserFormat(cmdname,fmt,&argc,&flags,ap); | ||
| 3618 | va_end(ap); | ||
| 3619 | if (argv == NULL) return REDISMODULE_ERR; | ||
| 3620 | |||
| 3621 | /* Select the propagation target. Usually is AOF + replicas, however | ||
| 3622 | * the caller can exclude one or the other using the "A" or "R" | ||
| 3623 | * modifiers. */ | ||
| 3624 | int target = 0; | ||
| 3625 | if (!(flags & REDISMODULE_ARGV_NO_AOF)) target |= PROPAGATE_AOF; | ||
| 3626 | if (!(flags & REDISMODULE_ARGV_NO_REPLICAS)) target |= PROPAGATE_REPL; | ||
| 3627 | |||
| 3628 | alsoPropagate(ctx->client->db->id,argv,argc,target); | ||
| 3629 | |||
| 3630 | /* Release the argv. */ | ||
| 3631 | for (j = 0; j < argc; j++) decrRefCount(argv[j]); | ||
| 3632 | zfree(argv); | ||
| 3633 | server.dirty++; | ||
| 3634 | return REDISMODULE_OK; | ||
| 3635 | } | ||
| 3636 | |||
| 3637 | /* This function will replicate the command exactly as it was invoked | ||
| 3638 | * by the client. Note that the replicated commands are always wrapped | ||
| 3639 | * into the MULTI/EXEC that contains all the commands replicated in a | ||
| 3640 | * given module command execution, in the order they were executed. | ||
| 3641 | * | ||
| 3642 | * Basically this form of replication is useful when you want to propagate | ||
| 3643 | * the command to the slaves and AOF file exactly as it was called, since | ||
| 3644 | * the command can just be re-executed to deterministically re-create the | ||
| 3645 | * new state starting from the old one. | ||
| 3646 | * | ||
| 3647 | * It is important to note that this API is not thread-safe and | ||
| 3648 | * must be executed while holding the GIL. | ||
| 3649 | * | ||
| 3650 | * The function always returns REDISMODULE_OK. */ | ||
| 3651 | int RM_ReplicateVerbatim(RedisModuleCtx *ctx) { | ||
| 3652 | alsoPropagate(ctx->client->db->id, | ||
| 3653 | ctx->client->argv,ctx->client->argc, | ||
| 3654 | PROPAGATE_AOF|PROPAGATE_REPL); | ||
| 3655 | server.dirty++; | ||
| 3656 | return REDISMODULE_OK; | ||
| 3657 | } | ||
| 3658 | |||
| 3659 | /* -------------------------------------------------------------------------- | ||
| 3660 | * ## DB and Key APIs -- Generic API | ||
| 3661 | * -------------------------------------------------------------------------- */ | ||
| 3662 | |||
| 3663 | /* Return the ID of the current client calling the currently active module | ||
| 3664 | * command. The returned ID has a few guarantees: | ||
| 3665 | * | ||
| 3666 | * 1. The ID is different for each different client, so if the same client | ||
| 3667 | * executes a module command multiple times, it can be recognized as | ||
| 3668 | * having the same ID, otherwise the ID will be different. | ||
| 3669 | * 2. The ID increases monotonically. Clients connecting to the server later | ||
| 3670 | * are guaranteed to get IDs greater than any past ID previously seen. | ||
| 3671 | * | ||
| 3672 | * Valid IDs are from 1 to 2^64 - 1. If 0 is returned it means there is no way | ||
| 3673 | * to fetch the ID in the context the function was currently called. | ||
| 3674 | * | ||
| 3675 | * After obtaining the ID, it is possible to check if the command execution | ||
| 3676 | * is actually happening in the context of AOF loading, using this macro: | ||
| 3677 | * | ||
| 3678 | * if (RedisModule_IsAOFClient(RedisModule_GetClientId(ctx)) { | ||
| 3679 | * // Handle it differently. | ||
| 3680 | * } | ||
| 3681 | */ | ||
| 3682 | unsigned long long RM_GetClientId(RedisModuleCtx *ctx) { | ||
| 3683 | if (ctx->client == NULL) return 0; | ||
| 3684 | return ctx->client->id; | ||
| 3685 | } | ||
| 3686 | |||
| 3687 | /* Return the ACL user name used by the client with the specified client ID. | ||
| 3688 | * Client ID can be obtained with RM_GetClientId() API. If the client does not | ||
| 3689 | * exist, NULL is returned and errno is set to ENOENT. If the client isn't | ||
| 3690 | * using an ACL user, NULL is returned and errno is set to ENOTSUP */ | ||
| 3691 | RedisModuleString *RM_GetClientUserNameById(RedisModuleCtx *ctx, uint64_t id) { | ||
| 3692 | client *client = lookupClientByID(id); | ||
| 3693 | if (client == NULL) { | ||
| 3694 | errno = ENOENT; | ||
| 3695 | return NULL; | ||
| 3696 | } | ||
| 3697 | |||
| 3698 | if (client->user == NULL) { | ||
| 3699 | errno = ENOTSUP; | ||
| 3700 | return NULL; | ||
| 3701 | } | ||
| 3702 | |||
| 3703 | sds name = sdsnew(client->user->name); | ||
| 3704 | robj *str = createObject(OBJ_STRING, name); | ||
| 3705 | autoMemoryAdd(ctx, REDISMODULE_AM_STRING, str); | ||
| 3706 | return str; | ||
| 3707 | } | ||
| 3708 | |||
| 3709 | /* This is a helper for RM_GetClientInfoById() and other functions: given | ||
| 3710 | * a client, it populates the client info structure with the appropriate | ||
| 3711 | * fields depending on the version provided. If the version is not valid | ||
| 3712 | * then REDISMODULE_ERR is returned. Otherwise the function returns | ||
| 3713 | * REDISMODULE_OK and the structure pointed by 'ci' gets populated. */ | ||
| 3714 | |||
| 3715 | int modulePopulateClientInfoStructure(void *ci, client *client, int structver) { | ||
| 3716 | if (structver != 1) return REDISMODULE_ERR; | ||
| 3717 | |||
| 3718 | RedisModuleClientInfoV1 *ci1 = ci; | ||
| 3719 | memset(ci1,0,sizeof(*ci1)); | ||
| 3720 | ci1->version = structver; | ||
| 3721 | if (client->flags & CLIENT_MULTI) | ||
| 3722 | ci1->flags |= REDISMODULE_CLIENTINFO_FLAG_MULTI; | ||
| 3723 | if (client->flags & CLIENT_PUBSUB) | ||
| 3724 | ci1->flags |= REDISMODULE_CLIENTINFO_FLAG_PUBSUB; | ||
| 3725 | if (client->flags & CLIENT_UNIX_SOCKET) | ||
| 3726 | ci1->flags |= REDISMODULE_CLIENTINFO_FLAG_UNIXSOCKET; | ||
| 3727 | if (client->flags & CLIENT_TRACKING) | ||
| 3728 | ci1->flags |= REDISMODULE_CLIENTINFO_FLAG_TRACKING; | ||
| 3729 | if (client->flags & CLIENT_BLOCKED) | ||
| 3730 | ci1->flags |= REDISMODULE_CLIENTINFO_FLAG_BLOCKED; | ||
| 3731 | if (client->conn->type == connectionTypeTls()) | ||
| 3732 | ci1->flags |= REDISMODULE_CLIENTINFO_FLAG_SSL; | ||
| 3733 | |||
| 3734 | int port; | ||
| 3735 | connAddrPeerName(client->conn,ci1->addr,sizeof(ci1->addr),&port); | ||
| 3736 | ci1->port = port; | ||
| 3737 | ci1->db = client->db->id; | ||
| 3738 | ci1->id = client->id; | ||
| 3739 | return REDISMODULE_OK; | ||
| 3740 | } | ||
| 3741 | |||
| 3742 | /* This is a helper for moduleFireServerEvent() and other functions: | ||
| 3743 | * It populates the replication info structure with the appropriate | ||
| 3744 | * fields depending on the version provided. If the version is not valid | ||
| 3745 | * then REDISMODULE_ERR is returned. Otherwise the function returns | ||
| 3746 | * REDISMODULE_OK and the structure pointed by 'ri' gets populated. */ | ||
| 3747 | int modulePopulateReplicationInfoStructure(void *ri, int structver) { | ||
| 3748 | if (structver != 1) return REDISMODULE_ERR; | ||
| 3749 | |||
| 3750 | RedisModuleReplicationInfoV1 *ri1 = ri; | ||
| 3751 | memset(ri1,0,sizeof(*ri1)); | ||
| 3752 | ri1->version = structver; | ||
| 3753 | ri1->master = server.masterhost==NULL; | ||
| 3754 | ri1->masterhost = server.masterhost? server.masterhost: ""; | ||
| 3755 | ri1->masterport = server.masterport; | ||
| 3756 | ri1->replid1 = server.replid; | ||
| 3757 | ri1->replid2 = server.replid2; | ||
| 3758 | ri1->repl1_offset = server.master_repl_offset; | ||
| 3759 | ri1->repl2_offset = server.second_replid_offset; | ||
| 3760 | return REDISMODULE_OK; | ||
| 3761 | } | ||
| 3762 | |||
| 3763 | /* Return information about the client with the specified ID (that was | ||
| 3764 | * previously obtained via the RedisModule_GetClientId() API). If the | ||
| 3765 | * client exists, REDISMODULE_OK is returned, otherwise REDISMODULE_ERR | ||
| 3766 | * is returned. | ||
| 3767 | * | ||
| 3768 | * When the client exist and the `ci` pointer is not NULL, but points to | ||
| 3769 | * a structure of type RedisModuleClientInfoV1, previously initialized with | ||
| 3770 | * the correct REDISMODULE_CLIENTINFO_INITIALIZER_V1, the structure is populated | ||
| 3771 | * with the following fields: | ||
| 3772 | * | ||
| 3773 | * uint64_t flags; // REDISMODULE_CLIENTINFO_FLAG_* | ||
| 3774 | * uint64_t id; // Client ID | ||
| 3775 | * char addr[46]; // IPv4 or IPv6 address. | ||
| 3776 | * uint16_t port; // TCP port. | ||
| 3777 | * uint16_t db; // Selected DB. | ||
| 3778 | * | ||
| 3779 | * Note: the client ID is useless in the context of this call, since we | ||
| 3780 | * already know, however the same structure could be used in other | ||
| 3781 | * contexts where we don't know the client ID, yet the same structure | ||
| 3782 | * is returned. | ||
| 3783 | * | ||
| 3784 | * With flags having the following meaning: | ||
| 3785 | * | ||
| 3786 | * REDISMODULE_CLIENTINFO_FLAG_SSL Client using SSL connection. | ||
| 3787 | * REDISMODULE_CLIENTINFO_FLAG_PUBSUB Client in Pub/Sub mode. | ||
| 3788 | * REDISMODULE_CLIENTINFO_FLAG_BLOCKED Client blocked in command. | ||
| 3789 | * REDISMODULE_CLIENTINFO_FLAG_TRACKING Client with keys tracking on. | ||
| 3790 | * REDISMODULE_CLIENTINFO_FLAG_UNIXSOCKET Client using unix domain socket. | ||
| 3791 | * REDISMODULE_CLIENTINFO_FLAG_MULTI Client in MULTI state. | ||
| 3792 | * | ||
| 3793 | * However passing NULL is a way to just check if the client exists in case | ||
| 3794 | * we are not interested in any additional information. | ||
| 3795 | * | ||
| 3796 | * This is the correct usage when we want the client info structure | ||
| 3797 | * returned: | ||
| 3798 | * | ||
| 3799 | * RedisModuleClientInfo ci = REDISMODULE_CLIENTINFO_INITIALIZER; | ||
| 3800 | * int retval = RedisModule_GetClientInfoById(&ci,client_id); | ||
| 3801 | * if (retval == REDISMODULE_OK) { | ||
| 3802 | * printf("Address: %s\n", ci.addr); | ||
| 3803 | * } | ||
| 3804 | */ | ||
| 3805 | int RM_GetClientInfoById(void *ci, uint64_t id) { | ||
| 3806 | client *client = lookupClientByID(id); | ||
| 3807 | if (client == NULL) return REDISMODULE_ERR; | ||
| 3808 | if (ci == NULL) return REDISMODULE_OK; | ||
| 3809 | |||
| 3810 | /* Fill the info structure if passed. */ | ||
| 3811 | uint64_t structver = ((uint64_t*)ci)[0]; | ||
| 3812 | return modulePopulateClientInfoStructure(ci,client,structver); | ||
| 3813 | } | ||
| 3814 | |||
| 3815 | /* Returns the name of the client connection with the given ID. | ||
| 3816 | * | ||
| 3817 | * If the client ID does not exist or if the client has no name associated with | ||
| 3818 | * it, NULL is returned. */ | ||
| 3819 | RedisModuleString *RM_GetClientNameById(RedisModuleCtx *ctx, uint64_t id) { | ||
| 3820 | client *client = lookupClientByID(id); | ||
| 3821 | if (client == NULL || client->name == NULL) return NULL; | ||
| 3822 | robj *name = client->name; | ||
| 3823 | incrRefCount(name); | ||
| 3824 | autoMemoryAdd(ctx, REDISMODULE_AM_STRING, name); | ||
| 3825 | return name; | ||
| 3826 | } | ||
| 3827 | |||
| 3828 | /* Sets the name of the client with the given ID. This is equivalent to the client calling | ||
| 3829 | * `CLIENT SETNAME name`. | ||
| 3830 | * | ||
| 3831 | * Returns REDISMODULE_OK on success. On failure, REDISMODULE_ERR is returned | ||
| 3832 | * and errno is set as follows: | ||
| 3833 | * | ||
| 3834 | * - ENOENT if the client does not exist | ||
| 3835 | * - EINVAL if the name contains invalid characters */ | ||
| 3836 | int RM_SetClientNameById(uint64_t id, RedisModuleString *name) { | ||
| 3837 | client *client = lookupClientByID(id); | ||
| 3838 | if (client == NULL) { | ||
| 3839 | errno = ENOENT; | ||
| 3840 | return REDISMODULE_ERR; | ||
| 3841 | } | ||
| 3842 | if (clientSetName(client, name, NULL) == C_ERR) { | ||
| 3843 | errno = EINVAL; | ||
| 3844 | return REDISMODULE_ERR; | ||
| 3845 | } | ||
| 3846 | return REDISMODULE_OK; | ||
| 3847 | } | ||
| 3848 | |||
| 3849 | /* Publish a message to subscribers (see PUBLISH command). */ | ||
| 3850 | int RM_PublishMessage(RedisModuleCtx *ctx, RedisModuleString *channel, RedisModuleString *message) { | ||
| 3851 | UNUSED(ctx); | ||
| 3852 | return pubsubPublishMessageAndPropagateToCluster(channel, message, 0); | ||
| 3853 | } | ||
| 3854 | |||
| 3855 | /* Publish a message to shard-subscribers (see SPUBLISH command). */ | ||
| 3856 | int RM_PublishMessageShard(RedisModuleCtx *ctx, RedisModuleString *channel, RedisModuleString *message) { | ||
| 3857 | UNUSED(ctx); | ||
| 3858 | return pubsubPublishMessageAndPropagateToCluster(channel, message, 1); | ||
| 3859 | } | ||
| 3860 | |||
| 3861 | /* Return the currently selected DB. */ | ||
| 3862 | int RM_GetSelectedDb(RedisModuleCtx *ctx) { | ||
| 3863 | return ctx->client->db->id; | ||
| 3864 | } | ||
| 3865 | |||
| 3866 | |||
| 3867 | /* Return the current context's flags. The flags provide information on the | ||
| 3868 | * current request context (whether the client is a Lua script or in a MULTI), | ||
| 3869 | * and about the Redis instance in general, i.e replication and persistence. | ||
| 3870 | * | ||
| 3871 | * It is possible to call this function even with a NULL context, however | ||
| 3872 | * in this case the following flags will not be reported: | ||
| 3873 | * | ||
| 3874 | * * LUA, MULTI, REPLICATED, DIRTY (see below for more info). | ||
| 3875 | * | ||
| 3876 | * Available flags and their meaning: | ||
| 3877 | * | ||
| 3878 | * * REDISMODULE_CTX_FLAGS_LUA: The command is running in a Lua script | ||
| 3879 | * | ||
| 3880 | * * REDISMODULE_CTX_FLAGS_MULTI: The command is running inside a transaction | ||
| 3881 | * | ||
| 3882 | * * REDISMODULE_CTX_FLAGS_REPLICATED: The command was sent over the replication | ||
| 3883 | * link by the MASTER | ||
| 3884 | * | ||
| 3885 | * * REDISMODULE_CTX_FLAGS_MASTER: The Redis instance is a master | ||
| 3886 | * | ||
| 3887 | * * REDISMODULE_CTX_FLAGS_SLAVE: The Redis instance is a slave | ||
| 3888 | * | ||
| 3889 | * * REDISMODULE_CTX_FLAGS_READONLY: The Redis instance is read-only | ||
| 3890 | * | ||
| 3891 | * * REDISMODULE_CTX_FLAGS_CLUSTER: The Redis instance is in cluster mode | ||
| 3892 | * | ||
| 3893 | * * REDISMODULE_CTX_FLAGS_AOF: The Redis instance has AOF enabled | ||
| 3894 | * | ||
| 3895 | * * REDISMODULE_CTX_FLAGS_RDB: The instance has RDB enabled | ||
| 3896 | * | ||
| 3897 | * * REDISMODULE_CTX_FLAGS_MAXMEMORY: The instance has Maxmemory set | ||
| 3898 | * | ||
| 3899 | * * REDISMODULE_CTX_FLAGS_EVICT: Maxmemory is set and has an eviction | ||
| 3900 | * policy that may delete keys | ||
| 3901 | * | ||
| 3902 | * * REDISMODULE_CTX_FLAGS_OOM: Redis is out of memory according to the | ||
| 3903 | * maxmemory setting. | ||
| 3904 | * | ||
| 3905 | * * REDISMODULE_CTX_FLAGS_OOM_WARNING: Less than 25% of memory remains before | ||
| 3906 | * reaching the maxmemory level. | ||
| 3907 | * | ||
| 3908 | * * REDISMODULE_CTX_FLAGS_LOADING: Server is loading RDB/AOF | ||
| 3909 | * | ||
| 3910 | * * REDISMODULE_CTX_FLAGS_REPLICA_IS_STALE: No active link with the master. | ||
| 3911 | * | ||
| 3912 | * * REDISMODULE_CTX_FLAGS_REPLICA_IS_CONNECTING: The replica is trying to | ||
| 3913 | * connect with the master. | ||
| 3914 | * | ||
| 3915 | * * REDISMODULE_CTX_FLAGS_REPLICA_IS_TRANSFERRING: Master -> Replica RDB | ||
| 3916 | * transfer is in progress. | ||
| 3917 | * | ||
| 3918 | * * REDISMODULE_CTX_FLAGS_REPLICA_IS_ONLINE: The replica has an active link | ||
| 3919 | * with its master. This is the | ||
| 3920 | * contrary of STALE state. | ||
| 3921 | * | ||
| 3922 | * * REDISMODULE_CTX_FLAGS_ACTIVE_CHILD: There is currently some background | ||
| 3923 | * process active (RDB, AUX or module). | ||
| 3924 | * | ||
| 3925 | * * REDISMODULE_CTX_FLAGS_MULTI_DIRTY: The next EXEC will fail due to dirty | ||
| 3926 | * CAS (touched keys). | ||
| 3927 | * | ||
| 3928 | * * REDISMODULE_CTX_FLAGS_IS_CHILD: Redis is currently running inside | ||
| 3929 | * background child process. | ||
| 3930 | * | ||
| 3931 | * * REDISMODULE_CTX_FLAGS_RESP3: Indicate the that client attached to this | ||
| 3932 | * context is using RESP3. | ||
| 3933 | * | ||
| 3934 | * * REDISMODULE_CTX_FLAGS_SERVER_STARTUP: The Redis instance is starting | ||
| 3935 | * | ||
| 3936 | * * REDISMODULE_CTX_FLAGS_DEBUG_ENABLED: Debug commands are enabled for this | ||
| 3937 | * context. | ||
| 3938 | * * REDISMODULE_CTX_FLAGS_TRIM_IN_PROGRESS: Trim is in progress due to slot | ||
| 3939 | * migration. | ||
| 3940 | */ | ||
| 3941 | int RM_GetContextFlags(RedisModuleCtx *ctx) { | ||
| 3942 | int flags = 0; | ||
| 3943 | |||
| 3944 | /* Client specific flags */ | ||
| 3945 | if (ctx) { | ||
| 3946 | if (ctx->client) { | ||
| 3947 | if (ctx->client->flags & CLIENT_DENY_BLOCKING) | ||
| 3948 | flags |= REDISMODULE_CTX_FLAGS_DENY_BLOCKING; | ||
| 3949 | /* Module command received from MASTER, is replicated. */ | ||
| 3950 | if (ctx->client->flags & CLIENT_MASTER) | ||
| 3951 | flags |= REDISMODULE_CTX_FLAGS_REPLICATED; | ||
| 3952 | if (ctx->client->resp == 3) { | ||
| 3953 | flags |= REDISMODULE_CTX_FLAGS_RESP3; | ||
| 3954 | } | ||
| 3955 | } | ||
| 3956 | |||
| 3957 | /* For DIRTY flags, we need the blocked client if used */ | ||
| 3958 | client *c = ctx->blocked_client ? ctx->blocked_client->client : ctx->client; | ||
| 3959 | if (c && (c->flags & (CLIENT_DIRTY_CAS|CLIENT_DIRTY_EXEC))) { | ||
| 3960 | flags |= REDISMODULE_CTX_FLAGS_MULTI_DIRTY; | ||
| 3961 | } | ||
| 3962 | if (c && allowProtectedAction(server.enable_debug_cmd, c)) { | ||
| 3963 | flags |= REDISMODULE_CTX_FLAGS_DEBUG_ENABLED; | ||
| 3964 | } | ||
| 3965 | } | ||
| 3966 | |||
| 3967 | if (scriptIsRunning()) | ||
| 3968 | flags |= REDISMODULE_CTX_FLAGS_LUA; | ||
| 3969 | |||
| 3970 | if (server.in_exec) | ||
| 3971 | flags |= REDISMODULE_CTX_FLAGS_MULTI; | ||
| 3972 | |||
| 3973 | if (server.cluster_enabled) | ||
| 3974 | flags |= REDISMODULE_CTX_FLAGS_CLUSTER; | ||
| 3975 | |||
| 3976 | if (server.async_loading) | ||
| 3977 | flags |= REDISMODULE_CTX_FLAGS_ASYNC_LOADING; | ||
| 3978 | else if (server.loading) | ||
| 3979 | flags |= REDISMODULE_CTX_FLAGS_LOADING; | ||
| 3980 | |||
| 3981 | /* Maxmemory and eviction policy */ | ||
| 3982 | if (server.maxmemory > 0 && (!server.masterhost || !server.repl_slave_ignore_maxmemory)) { | ||
| 3983 | flags |= REDISMODULE_CTX_FLAGS_MAXMEMORY; | ||
| 3984 | |||
| 3985 | if (server.maxmemory_policy != MAXMEMORY_NO_EVICTION) | ||
| 3986 | flags |= REDISMODULE_CTX_FLAGS_EVICT; | ||
| 3987 | } | ||
| 3988 | |||
| 3989 | /* Persistence flags */ | ||
| 3990 | if (server.aof_state != AOF_OFF) | ||
| 3991 | flags |= REDISMODULE_CTX_FLAGS_AOF; | ||
| 3992 | if (server.saveparamslen > 0) | ||
| 3993 | flags |= REDISMODULE_CTX_FLAGS_RDB; | ||
| 3994 | |||
| 3995 | /* Replication flags */ | ||
| 3996 | if (server.masterhost == NULL) { | ||
| 3997 | flags |= REDISMODULE_CTX_FLAGS_MASTER; | ||
| 3998 | } else { | ||
| 3999 | flags |= REDISMODULE_CTX_FLAGS_SLAVE; | ||
| 4000 | if (server.repl_slave_ro) | ||
| 4001 | flags |= REDISMODULE_CTX_FLAGS_READONLY; | ||
| 4002 | |||
| 4003 | /* Replica state flags. */ | ||
| 4004 | if (server.repl_state == REPL_STATE_CONNECT || | ||
| 4005 | server.repl_state == REPL_STATE_CONNECTING) | ||
| 4006 | { | ||
| 4007 | flags |= REDISMODULE_CTX_FLAGS_REPLICA_IS_CONNECTING; | ||
| 4008 | } else if (server.repl_state == REPL_STATE_TRANSFER) { | ||
| 4009 | flags |= REDISMODULE_CTX_FLAGS_REPLICA_IS_TRANSFERRING; | ||
| 4010 | } else if (server.repl_state == REPL_STATE_CONNECTED) { | ||
| 4011 | flags |= REDISMODULE_CTX_FLAGS_REPLICA_IS_ONLINE; | ||
| 4012 | } | ||
| 4013 | |||
| 4014 | if (server.repl_state != REPL_STATE_CONNECTED) | ||
| 4015 | flags |= REDISMODULE_CTX_FLAGS_REPLICA_IS_STALE; | ||
| 4016 | } | ||
| 4017 | |||
| 4018 | /* OOM flag. */ | ||
| 4019 | float level; | ||
| 4020 | int retval = getMaxmemoryState(NULL,NULL,NULL,&level); | ||
| 4021 | if (retval == C_ERR) flags |= REDISMODULE_CTX_FLAGS_OOM; | ||
| 4022 | if (level > 0.75) flags |= REDISMODULE_CTX_FLAGS_OOM_WARNING; | ||
| 4023 | |||
| 4024 | /* Presence of children processes. */ | ||
| 4025 | if (hasActiveChildProcess()) flags |= REDISMODULE_CTX_FLAGS_ACTIVE_CHILD; | ||
| 4026 | if (server.in_fork_child) flags |= REDISMODULE_CTX_FLAGS_IS_CHILD; | ||
| 4027 | |||
| 4028 | /* Non-empty server.loadmodule_queue means that Redis is starting. */ | ||
| 4029 | if (listLength(server.loadmodule_queue) > 0) | ||
| 4030 | flags |= REDISMODULE_CTX_FLAGS_SERVER_STARTUP; | ||
| 4031 | |||
| 4032 | /* If debug commands are completely enabled */ | ||
| 4033 | if (server.enable_debug_cmd == PROTECTED_ACTION_ALLOWED_YES) { | ||
| 4034 | flags |= REDISMODULE_CTX_FLAGS_DEBUG_ENABLED; | ||
| 4035 | } | ||
| 4036 | |||
| 4037 | if (asmIsTrimInProgress()) | ||
| 4038 | flags |= REDISMODULE_CTX_FLAGS_TRIM_IN_PROGRESS; | ||
| 4039 | |||
| 4040 | return flags; | ||
| 4041 | } | ||
| 4042 | |||
| 4043 | /* Returns true if a client sent the CLIENT PAUSE command to the server or | ||
| 4044 | * if Redis Cluster does a manual failover, pausing the clients. | ||
| 4045 | * This is needed when we have a master with replicas, and want to write, | ||
| 4046 | * without adding further data to the replication channel, that the replicas | ||
| 4047 | * replication offset, match the one of the master. When this happens, it is | ||
| 4048 | * safe to failover the master without data loss. | ||
| 4049 | * | ||
| 4050 | * However modules may generate traffic by calling RedisModule_Call() with | ||
| 4051 | * the "!" flag, or by calling RedisModule_Replicate(), in a context outside | ||
| 4052 | * commands execution, for instance in timeout callbacks, threads safe | ||
| 4053 | * contexts, and so forth. When modules will generate too much traffic, it | ||
| 4054 | * will be hard for the master and replicas offset to match, because there | ||
| 4055 | * is more data to send in the replication channel. | ||
| 4056 | * | ||
| 4057 | * So modules may want to try to avoid very heavy background work that has | ||
| 4058 | * the effect of creating data to the replication channel, when this function | ||
| 4059 | * returns true. This is mostly useful for modules that have background | ||
| 4060 | * garbage collection tasks, or that do writes and replicate such writes | ||
| 4061 | * periodically in timer callbacks or other periodic callbacks. | ||
| 4062 | */ | ||
| 4063 | int RM_AvoidReplicaTraffic(void) { | ||
| 4064 | return !!(isPausedActionsWithUpdate(PAUSE_ACTION_REPLICA)); | ||
| 4065 | } | ||
| 4066 | |||
| 4067 | /* Change the currently selected DB. Returns an error if the id | ||
| 4068 | * is out of range. | ||
| 4069 | * | ||
| 4070 | * Note that the client will retain the currently selected DB even after | ||
| 4071 | * the Redis command implemented by the module calling this function | ||
| 4072 | * returns. | ||
| 4073 | * | ||
| 4074 | * If the module command wishes to change something in a different DB and | ||
| 4075 | * returns back to the original one, it should call RedisModule_GetSelectedDb() | ||
| 4076 | * before in order to restore the old DB number before returning. */ | ||
| 4077 | int RM_SelectDb(RedisModuleCtx *ctx, int newid) { | ||
| 4078 | int retval = selectDb(ctx->client,newid); | ||
| 4079 | return (retval == C_OK) ? REDISMODULE_OK : REDISMODULE_ERR; | ||
| 4080 | } | ||
| 4081 | |||
| 4082 | /* Check if a key exists, without affecting its last access time. | ||
| 4083 | * | ||
| 4084 | * This is equivalent to calling RM_OpenKey with the mode REDISMODULE_READ | | ||
| 4085 | * REDISMODULE_OPEN_KEY_NOTOUCH, then checking if NULL was returned and, if not, | ||
| 4086 | * calling RM_CloseKey on the opened key. | ||
| 4087 | */ | ||
| 4088 | int RM_KeyExists(RedisModuleCtx *ctx, robj *keyname) { | ||
| 4089 | kvobj *kv = lookupKeyReadWithFlags(ctx->client->db, keyname, LOOKUP_NOTOUCH); | ||
| 4090 | return (kv != NULL); | ||
| 4091 | } | ||
| 4092 | |||
| 4093 | /* Initialize a RedisModuleKey struct */ | ||
| 4094 | static void moduleInitKey(RedisModuleKey *kp, RedisModuleCtx *ctx, robj *keyname, kvobj *kv, int mode){ | ||
| 4095 | kp->ctx = ctx; | ||
| 4096 | kp->db = ctx->client->db; | ||
| 4097 | kp->key = keyname; | ||
| 4098 | incrRefCount(keyname); | ||
| 4099 | kp->kv = kv; | ||
| 4100 | kp->iter = NULL; | ||
| 4101 | kp->mode = mode; | ||
| 4102 | if (kp->kv) moduleInitKeyTypeSpecific(kp); | ||
| 4103 | } | ||
| 4104 | |||
| 4105 | /* Initialize the type-specific part of the key. Only when key has a value. */ | ||
| 4106 | static void moduleInitKeyTypeSpecific(RedisModuleKey *key) { | ||
| 4107 | switch (key->kv->type) { | ||
| 4108 | case OBJ_ZSET: zsetKeyReset(key); break; | ||
| 4109 | case OBJ_STREAM: key->u.stream.signalready = 0; break; | ||
| 4110 | } | ||
| 4111 | } | ||
| 4112 | |||
| 4113 | /* Return a handle representing a Redis key, so that it is possible | ||
| 4114 | * to call other APIs with the key handle as argument to perform | ||
| 4115 | * operations on the key. | ||
| 4116 | * | ||
| 4117 | * The return value is the handle representing the key, that must be | ||
| 4118 | * closed with RM_CloseKey(). | ||
| 4119 | * | ||
| 4120 | * If the key does not exist and REDISMODULE_WRITE mode is requested, the handle | ||
| 4121 | * is still returned, since it is possible to perform operations on | ||
| 4122 | * a yet not existing key (that will be created, for example, after | ||
| 4123 | * a list push operation). If the mode is just REDISMODULE_READ instead, and the | ||
| 4124 | * key does not exist, NULL is returned. However it is still safe to | ||
| 4125 | * call RedisModule_CloseKey() and RedisModule_KeyType() on a NULL | ||
| 4126 | * value. | ||
| 4127 | * | ||
| 4128 | * Extra flags that can be pass to the API under the mode argument: | ||
| 4129 | * * REDISMODULE_OPEN_KEY_NOTOUCH - Avoid touching the LRU/LFU of the key when opened. | ||
| 4130 | * * REDISMODULE_OPEN_KEY_NONOTIFY - Don't trigger keyspace event on key misses. | ||
| 4131 | * * REDISMODULE_OPEN_KEY_NOSTATS - Don't update keyspace hits/misses counters. | ||
| 4132 | * * REDISMODULE_OPEN_KEY_NOEXPIRE - Avoid deleting lazy expired keys. | ||
| 4133 | * * REDISMODULE_OPEN_KEY_NOEFFECTS - Avoid any effects from fetching the key. | ||
| 4134 | * * REDISMODULE_OPEN_KEY_ACCESS_EXPIRED - Access expired keys that have not yet been deleted */ | ||
| 4135 | RedisModuleKey *RM_OpenKey(RedisModuleCtx *ctx, robj *keyname, int mode) { | ||
| 4136 | RedisModuleKey *kp; | ||
| 4137 | kvobj *kv; | ||
| 4138 | int flags = 0; | ||
| 4139 | flags |= (mode & REDISMODULE_OPEN_KEY_NOTOUCH? LOOKUP_NOTOUCH: 0); | ||
| 4140 | flags |= (mode & REDISMODULE_OPEN_KEY_NONOTIFY? LOOKUP_NONOTIFY: 0); | ||
| 4141 | flags |= (mode & REDISMODULE_OPEN_KEY_NOSTATS? LOOKUP_NOSTATS: 0); | ||
| 4142 | flags |= (mode & REDISMODULE_OPEN_KEY_NOEXPIRE? LOOKUP_NOEXPIRE: 0); | ||
| 4143 | flags |= (mode & REDISMODULE_OPEN_KEY_NOEFFECTS? LOOKUP_NOEFFECTS: 0); | ||
| 4144 | flags |= (mode & REDISMODULE_OPEN_KEY_ACCESS_EXPIRED ? (LOOKUP_ACCESS_EXPIRED) : 0); | ||
| 4145 | flags |= (mode & REDISMODULE_OPEN_KEY_ACCESS_TRIMMED ? (LOOKUP_ACCESS_TRIMMED) : 0); | ||
| 4146 | |||
| 4147 | if (mode & REDISMODULE_WRITE) { | ||
| 4148 | kv = lookupKeyWriteWithFlags(ctx->client->db,keyname, flags); | ||
| 4149 | } else { | ||
| 4150 | kv = lookupKeyReadWithFlags(ctx->client->db,keyname, flags); | ||
| 4151 | if (kv == NULL) { | ||
| 4152 | return NULL; | ||
| 4153 | } | ||
| 4154 | } | ||
| 4155 | |||
| 4156 | /* Setup the key handle. */ | ||
| 4157 | kp = zmalloc(sizeof(*kp)); | ||
| 4158 | moduleInitKey(kp, ctx, keyname, kv, mode); | ||
| 4159 | autoMemoryAdd(ctx,REDISMODULE_AM_KEY,kp); | ||
| 4160 | return kp; | ||
| 4161 | } | ||
| 4162 | |||
| 4163 | /** | ||
| 4164 | * Returns the full OpenKey modes mask, using the return value | ||
| 4165 | * the module can check if a certain set of OpenKey modes are supported | ||
| 4166 | * by the redis server version in use. | ||
| 4167 | * Example: | ||
| 4168 | * | ||
| 4169 | * int supportedMode = RM_GetOpenKeyModesAll(); | ||
| 4170 | * if (supportedMode & REDISMODULE_OPEN_KEY_NOTOUCH) { | ||
| 4171 | * // REDISMODULE_OPEN_KEY_NOTOUCH is supported | ||
| 4172 | * } else{ | ||
| 4173 | * // REDISMODULE_OPEN_KEY_NOTOUCH is not supported | ||
| 4174 | * } | ||
| 4175 | */ | ||
| 4176 | int RM_GetOpenKeyModesAll(void) { | ||
| 4177 | return _REDISMODULE_OPEN_KEY_ALL; | ||
| 4178 | } | ||
| 4179 | |||
| 4180 | /* Destroy a RedisModuleKey struct (freeing is the responsibility of the caller). */ | ||
| 4181 | static void moduleCloseKey(RedisModuleKey *key) { | ||
| 4182 | int signal = SHOULD_SIGNAL_MODIFIED_KEYS(key->ctx); | ||
| 4183 | if ((key->mode & REDISMODULE_WRITE) && signal) | ||
| 4184 | keyModified(key->ctx->client,key->db,key->key,key->kv,1); | ||
| 4185 | if (key->kv) { | ||
| 4186 | if (key->iter) moduleFreeKeyIterator(key); | ||
| 4187 | switch (key->kv->type) { | ||
| 4188 | case OBJ_ZSET: | ||
| 4189 | RM_ZsetRangeStop(key); | ||
| 4190 | break; | ||
| 4191 | case OBJ_STREAM: | ||
| 4192 | if (key->u.stream.signalready) | ||
| 4193 | /* One or more RM_StreamAdd() have been done. */ | ||
| 4194 | signalKeyAsReady(key->db, key->key, OBJ_STREAM); | ||
| 4195 | break; | ||
| 4196 | } | ||
| 4197 | } | ||
| 4198 | serverAssert(key->iter == NULL); | ||
| 4199 | decrRefCount(key->key); | ||
| 4200 | } | ||
| 4201 | |||
| 4202 | /* Close a key handle. */ | ||
| 4203 | void RM_CloseKey(RedisModuleKey *key) { | ||
| 4204 | if (key == NULL) return; | ||
| 4205 | moduleCloseKey(key); | ||
| 4206 | autoMemoryFreed(key->ctx,REDISMODULE_AM_KEY,key); | ||
| 4207 | zfree(key); | ||
| 4208 | } | ||
| 4209 | |||
| 4210 | /* Return the type of the key. If the key pointer is NULL then | ||
| 4211 | * REDISMODULE_KEYTYPE_EMPTY is returned. */ | ||
| 4212 | int RM_KeyType(RedisModuleKey *key) { | ||
| 4213 | if (key == NULL || key->kv == NULL) return REDISMODULE_KEYTYPE_EMPTY; | ||
| 4214 | /* We map between defines so that we are free to change the internal | ||
| 4215 | * defines as desired. */ | ||
| 4216 | switch(key->kv->type) { | ||
| 4217 | case OBJ_STRING: return REDISMODULE_KEYTYPE_STRING; | ||
| 4218 | case OBJ_LIST: return REDISMODULE_KEYTYPE_LIST; | ||
| 4219 | case OBJ_SET: return REDISMODULE_KEYTYPE_SET; | ||
| 4220 | case OBJ_ZSET: return REDISMODULE_KEYTYPE_ZSET; | ||
| 4221 | case OBJ_HASH: return REDISMODULE_KEYTYPE_HASH; | ||
| 4222 | case OBJ_MODULE: return REDISMODULE_KEYTYPE_MODULE; | ||
| 4223 | case OBJ_STREAM: return REDISMODULE_KEYTYPE_STREAM; | ||
| 4224 | default: return REDISMODULE_KEYTYPE_EMPTY; | ||
| 4225 | } | ||
| 4226 | } | ||
| 4227 | |||
| 4228 | /* Return the length of the value associated with the key. | ||
| 4229 | * For strings this is the length of the string. For all the other types | ||
| 4230 | * is the number of elements (just counting keys for hashes). | ||
| 4231 | * | ||
| 4232 | * If the key pointer is NULL or the key is empty, zero is returned. */ | ||
| 4233 | size_t RM_ValueLength(RedisModuleKey *key) { | ||
| 4234 | if (key == NULL || key->kv == NULL) return 0; | ||
| 4235 | return getObjectLength(key->kv); | ||
| 4236 | } | ||
| 4237 | |||
| 4238 | /* If the key is open for writing, remove it, and setup the key to | ||
| 4239 | * accept new writes as an empty key (that will be created on demand). | ||
| 4240 | * On success REDISMODULE_OK is returned. If the key is not open for | ||
| 4241 | * writing REDISMODULE_ERR is returned. */ | ||
| 4242 | int RM_DeleteKey(RedisModuleKey *key) { | ||
| 4243 | if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR; | ||
| 4244 | if (key->kv) { | ||
| 4245 | dbDelete(key->db,key->key); | ||
| 4246 | key->kv = NULL; | ||
| 4247 | } | ||
| 4248 | return REDISMODULE_OK; | ||
| 4249 | } | ||
| 4250 | |||
| 4251 | /* If the key is open for writing, unlink it (that is delete it in a | ||
| 4252 | * non-blocking way, not reclaiming memory immediately) and setup the key to | ||
| 4253 | * accept new writes as an empty key (that will be created on demand). | ||
| 4254 | * On success REDISMODULE_OK is returned. If the key is not open for | ||
| 4255 | * writing REDISMODULE_ERR is returned. */ | ||
| 4256 | int RM_UnlinkKey(RedisModuleKey *key) { | ||
| 4257 | if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR; | ||
| 4258 | if (key->kv) { | ||
| 4259 | dbAsyncDelete(key->db,key->key); | ||
| 4260 | key->kv = NULL; | ||
| 4261 | } | ||
| 4262 | return REDISMODULE_OK; | ||
| 4263 | } | ||
| 4264 | |||
| 4265 | /* Return the key expire value, as milliseconds of remaining TTL. | ||
| 4266 | * If no TTL is associated with the key or if the key is empty, | ||
| 4267 | * REDISMODULE_NO_EXPIRE is returned. */ | ||
| 4268 | mstime_t RM_GetExpire(RedisModuleKey *key) { | ||
| 4269 | mstime_t expire = kvobjGetExpire(key->kv); | ||
| 4270 | if (expire == -1 || key->kv == NULL) | ||
| 4271 | return REDISMODULE_NO_EXPIRE; | ||
| 4272 | expire -= commandTimeSnapshot(); | ||
| 4273 | return expire >= 0 ? expire : 0; | ||
| 4274 | } | ||
| 4275 | |||
| 4276 | /* Set a new expire for the key. If the special expire | ||
| 4277 | * REDISMODULE_NO_EXPIRE is set, the expire is cancelled if there was | ||
| 4278 | * one (the same as the PERSIST command). | ||
| 4279 | * | ||
| 4280 | * Note that the expire must be provided as a positive integer representing | ||
| 4281 | * the number of milliseconds of TTL the key should have. | ||
| 4282 | * | ||
| 4283 | * The function returns REDISMODULE_OK on success or REDISMODULE_ERR if | ||
| 4284 | * the key was not open for writing or is an empty key. */ | ||
| 4285 | int RM_SetExpire(RedisModuleKey *key, mstime_t expire) { | ||
| 4286 | if (!(key->mode & REDISMODULE_WRITE) || key->kv == NULL || (expire < 0 && expire != REDISMODULE_NO_EXPIRE)) | ||
| 4287 | return REDISMODULE_ERR; | ||
| 4288 | if (expire != REDISMODULE_NO_EXPIRE) { | ||
| 4289 | expire += commandTimeSnapshot(); | ||
| 4290 | /* setExpire() might realloc kvobj */ | ||
| 4291 | key->kv = setExpire(key->ctx->client,key->db,key->key,expire); | ||
| 4292 | } else { | ||
| 4293 | removeExpire(key->db,key->key); | ||
| 4294 | } | ||
| 4295 | return REDISMODULE_OK; | ||
| 4296 | } | ||
| 4297 | |||
| 4298 | /* Return the key expire value, as absolute Unix timestamp. | ||
| 4299 | * If no TTL is associated with the key or if the key is empty, | ||
| 4300 | * REDISMODULE_NO_EXPIRE is returned. */ | ||
| 4301 | mstime_t RM_GetAbsExpire(RedisModuleKey *key) { | ||
| 4302 | mstime_t expire = kvobjGetExpire(key->kv); | ||
| 4303 | if (expire == -1 || key->kv == NULL) | ||
| 4304 | return REDISMODULE_NO_EXPIRE; | ||
| 4305 | return expire; | ||
| 4306 | } | ||
| 4307 | |||
| 4308 | /* Set a new expire for the key. If the special expire | ||
| 4309 | * REDISMODULE_NO_EXPIRE is set, the expire is cancelled if there was | ||
| 4310 | * one (the same as the PERSIST command). | ||
| 4311 | * | ||
| 4312 | * Note that the expire must be provided as a positive integer representing | ||
| 4313 | * the absolute Unix timestamp the key should have. | ||
| 4314 | * | ||
| 4315 | * The function returns REDISMODULE_OK on success or REDISMODULE_ERR if | ||
| 4316 | * the key was not open for writing or is an empty key. */ | ||
| 4317 | int RM_SetAbsExpire(RedisModuleKey *key, mstime_t expire) { | ||
| 4318 | if (!(key->mode & REDISMODULE_WRITE) || key->kv == NULL || (expire < 0 && expire != REDISMODULE_NO_EXPIRE)) | ||
| 4319 | return REDISMODULE_ERR; | ||
| 4320 | if (expire != REDISMODULE_NO_EXPIRE) { | ||
| 4321 | key->kv = setExpire(key->ctx->client,key->db,key->key,expire); | ||
| 4322 | } else { | ||
| 4323 | removeExpire(key->db,key->key); | ||
| 4324 | } | ||
| 4325 | return REDISMODULE_OK; | ||
| 4326 | } | ||
| 4327 | |||
| 4328 | /* Register a new key metadata class exported by the module. | ||
| 4329 | * | ||
| 4330 | * Key metadata allows modules to attach up to 8 bytes of metadata to any Redis key, | ||
| 4331 | * regardless of the key's type. This metadata persists across key operations like | ||
| 4332 | * COPY, RENAME, MOVE, and can be saved/loaded from RDB files. | ||
| 4333 | * | ||
| 4334 | * The parameters are the following: | ||
| 4335 | * | ||
| 4336 | * * **metaname**: A 9 characters metadata class name that MUST be unique in the Redis | ||
| 4337 | * Modules ecosystem. Use the charset A-Z a-z 0-9, plus the two "-_" characters. | ||
| 4338 | * A good idea is to use, for example `<metaname>-<vendor>`. For example | ||
| 4339 | * "idx-RediSearch" may mean "Index metadata by RediSearch module". To use both | ||
| 4340 | * lower case and upper case letters helps in order to prevent collisions. | ||
| 4341 | * | ||
| 4342 | * * **metaver**: Encoding version, which is the version of the serialization | ||
| 4343 | * that a module used in order to persist metadata. As long as the "metaname" | ||
| 4344 | * matches, the RDB loading will be dispatched to the metadata class callbacks | ||
| 4345 | * whatever 'metaver' is used, however the module can understand if | ||
| 4346 | * the encoding it must load is of an older version of the module. | ||
| 4347 | * For example the module "idx-RediSearch" initially used metaver=0. Later | ||
| 4348 | * after an upgrade, it started to serialize metadata in a different format | ||
| 4349 | * and to register the class with metaver=1. However this module may | ||
| 4350 | * still load old data produced by an older version if the rdb_load | ||
| 4351 | * callback is able to check the metaver value and act accordingly. | ||
| 4352 | * The metaver must be a positive value between 0 and 1023. | ||
| 4353 | * | ||
| 4354 | * * **confPtr** is a pointer to a RedisModuleKeyMetaClassConfig structure | ||
| 4355 | * that should be populated with the configuration and callbacks, like in | ||
| 4356 | * the following example: | ||
| 4357 | * | ||
| 4358 | * RedisModuleKeyMetaClassConfig config = { | ||
| 4359 | * .version = REDISMODULE_KEY_META_VERSION, | ||
| 4360 | * .flags = 1 << REDISMODULE_META_ALLOW_IGNORE, | ||
| 4361 | * .reset_value = 0, | ||
| 4362 | * .copy = myMeta_CopyCallback, | ||
| 4363 | * .rename = myMeta_RenameCallback, | ||
| 4364 | * .move = myMeta_MoveCallback, | ||
| 4365 | * .unlink = myMeta_UnlinkCallback, | ||
| 4366 | * .free = myMeta_FreeCallback, | ||
| 4367 | * .rdb_load = myMeta_RDBLoadCallback, | ||
| 4368 | * .rdb_save = myMeta_RDBSaveCallback, | ||
| 4369 | * .aof_rewrite = myMeta_AOFRewriteCallback, | ||
| 4370 | * .defrag = myMeta_DefragCallback, | ||
| 4371 | * .mem_usage = myMeta_MemUsageCallback, | ||
| 4372 | * .free_effort = myMeta_FreeEffortCallback | ||
| 4373 | * } | ||
| 4374 | * | ||
| 4375 | * Redis does NOT take ownership of the config structure itself. The `confPtr` | ||
| 4376 | * parameter only needs to remain valid during the RM_CreateKeyMetaClass() call | ||
| 4377 | * and can be freed immediately after. | ||
| 4378 | * | ||
| 4379 | * * **version**: Module must set it to REDISMODULE_KEY_META_VERSION. This field is | ||
| 4380 | * bumped when new fields are added; Redis keeps backward compatibility in | ||
| 4381 | * RM_CreateKeyMetaClass(). | ||
| 4382 | * | ||
| 4383 | * * **flags**: Currently supports REDISMODULE_META_ALLOW_IGNORE (value 0). | ||
| 4384 | * When set, metadata will be silently ignored during RDB load if the module | ||
| 4385 | * is not available or if rdb_load callback is NULL. Otherwise, RDB loading | ||
| 4386 | * will fail if metadata is encountered but cannot be loaded. | ||
| 4387 | * | ||
| 4388 | * * **reset_value**: The value to which metadata should be reset when it is being | ||
| 4389 | * "removed" from a key. Typically 0, but can be any 8-byte value. This is | ||
| 4390 | * especially relevant when metadata is a pointer/handler to external resources. | ||
| 4391 | * | ||
| 4392 | * IMPORTANT GUARANTEE: Redis only invokes callbacks when meta != reset_value. | ||
| 4393 | * | ||
| 4394 | * * **copy**: A callback function pointer for COPY command (optional). | ||
| 4395 | * - Return 1 to attach `meta` to the new key, or 0 to skip attaching metadata. | ||
| 4396 | * - If NULL, metadata is ignored during copy. | ||
| 4397 | * - The `meta` value may be modified in-place to produce a different value | ||
| 4398 | * for the new key. | ||
| 4399 | * | ||
| 4400 | * * **rename**: A callback function pointer for RENAME command (optional). | ||
| 4401 | * - If NULL, then metadata is kept during rename. | ||
| 4402 | * - The `meta` value may be modified in-place to produce a different value | ||
| 4403 | * for the new key. | ||
| 4404 | * | ||
| 4405 | * * **move**: A callback function pointer for MOVE command (optional). | ||
| 4406 | * - Return 1 to keep metadata, 0 to drop. | ||
| 4407 | * - If NULL, then metadata is kept during move. | ||
| 4408 | * - The `meta` value may be modified in-place to produce a different value | ||
| 4409 | * for the new key. | ||
| 4410 | * | ||
| 4411 | * * **unlink**: A callback function pointer for unlink operations (optional). | ||
| 4412 | * - If not provided, then metadata is ignored during unlink. | ||
| 4413 | * - Indication that key may soon be freed by background thread. | ||
| 4414 | * - Pointer to meta is provided for modification. If the metadata holds a pointer | ||
| 4415 | * or handle to resources and you free them here, you should set `*meta=reset_value` | ||
| 4416 | * to prevent the free callback from being invoked (Redis skips callbacks when | ||
| 4417 | * meta == reset_value, see reset_value documentation above). | ||
| 4418 | * | ||
| 4419 | * * **free**: A callback function pointer for cleanup (optional). | ||
| 4420 | * Invoked when a key with this metadata is deleted/overwritten/expired, | ||
| 4421 | * or when Redis needs to release per-key metadata during lifecycle operations. | ||
| 4422 | * The module should free any external allocation referenced by `meta` | ||
| 4423 | * if it uses the 8 bytes as a handle/pointer. | ||
| 4424 | * This callback may run in a background thread and is not protected by GIL. | ||
| 4425 | * It also might be called during RDB loading if the load fails after some | ||
| 4426 | * metadata has been successfully loaded. In this case, keyname will be NULL | ||
| 4427 | * since the key hasn't been created yet. | ||
| 4428 | * | ||
| 4429 | * * **rdb_load**: A callback function pointer for RDB loading (optional). | ||
| 4430 | * - Called during RDB loading when metadata for this class is encountered. | ||
| 4431 | * - Behavior when NULL: | ||
| 4432 | * > If rdb_load is NULL AND REDISMODULE_META_ALLOW_IGNORE flag is set, | ||
| 4433 | * the metadata will be silently ignored during RDB load. | ||
| 4434 | * > If rdb_load is NULL AND the flag is NOT set, RDB loading will fail | ||
| 4435 | * if metadata for this class is encountered. | ||
| 4436 | * - Behavior when class is not registered: | ||
| 4437 | * > If the class was saved with REDISMODULE_META_ALLOW_IGNORE flag but | ||
| 4438 | * is not registered at load time, the metadata will be silently ignored. | ||
| 4439 | * > Otherwise, RDB loading will fail. | ||
| 4440 | * - Callback responsibilities: | ||
| 4441 | * > Read custom serialized data from `rdb` using RedisModule_Load*() APIs | ||
| 4442 | * > Deserialize and reconstruct the 8-byte metadata value | ||
| 4443 | * > Write the final 8-byte value into `*meta` | ||
| 4444 | * > Return appropriate status code (see below) | ||
| 4445 | * > Database ID can be derived from `rdb` if needed. The associated key | ||
| 4446 | * will be loaded immediately after this callback returns. | ||
| 4447 | * - Parameters: | ||
| 4448 | * > rdb: RDB I/O context (use RedisModule_Load*() functions to read data) | ||
| 4449 | * > meta: Pointer to 8-byte metadata slot (write your deserialized value here) | ||
| 4450 | * > encver: Encoding version (the metadata class version at save time) | ||
| 4451 | * - Return values: | ||
| 4452 | * > 1: Attach value `*meta` to the key (success) | ||
| 4453 | * > 0: Ignore/skip metadata (don't attach, but continue loading - not an error) | ||
| 4454 | * > -1: Error - abort RDB load (e.g., invalid data, version incompatibility) | ||
| 4455 | * Module MUST clean up any allocated metadata before returning -1. | ||
| 4456 | * | ||
| 4457 | * * **rdb_save**: A callback function pointer for RDB saving (optional). | ||
| 4458 | * - If set to NULL, Redis will not save metadata to RDB. | ||
| 4459 | * - Callback should write data using RDB assisting functions: RedisModule_Save*(). | ||
| 4460 | * | ||
| 4461 | * * **aof_rewrite**: A callback function pointer for AOF rewrite (optional). | ||
| 4462 | * Called during AOF rewrite to emit commands that reconstruct the metadata. | ||
| 4463 | * IMPORTANT: For AOF/RDB persistence to work correctly, metadata classes must be | ||
| 4464 | * registered in RedisModule_OnLoad() so they are available when loading persisted | ||
| 4465 | * data on server startup. | ||
| 4466 | * | ||
| 4467 | * * **defrag**: A callback function pointer for active defragmentation (optional). | ||
| 4468 | * If the metadata contains pointers, this callback should defragment them. | ||
| 4469 | * | ||
| 4470 | * * **mem_usage**: A callback function pointer for MEMORY USAGE command (optional). | ||
| 4471 | * Should return the memory used by the metadata in bytes. | ||
| 4472 | * | ||
| 4473 | * * **free_effort**: A callback function pointer for lazy free (optional). | ||
| 4474 | * Should return the complexity of freeing the metadata to determine if | ||
| 4475 | * lazy free should be used. | ||
| 4476 | * | ||
| 4477 | * Note: the metadata class name "AAAAAAAAA" is reserved and produces an error. | ||
| 4478 | * | ||
| 4479 | * If RM_CreateKeyMetaClass() is called outside of RedisModule_OnLoad() function, | ||
| 4480 | * there is already a metadata class registered with the same name, | ||
| 4481 | * or if the metadata class name or metaver is invalid, a negative value is returned. | ||
| 4482 | * Otherwise the new metadata class is registered into Redis, and a reference of | ||
| 4483 | * type RedisModuleKeyMetaClassId is returned: the caller of the function should store | ||
| 4484 | * this reference into a global variable to make future use of it in the | ||
| 4485 | * modules metadata API, since a single module may register multiple metadata classes. | ||
| 4486 | * Example code fragment: | ||
| 4487 | * | ||
| 4488 | * static RedisModuleKeyMetaClassId IndexMetaClass; | ||
| 4489 | * | ||
| 4490 | * int RedisModule_OnLoad(RedisModuleCtx *ctx) { | ||
| 4491 | * // some code here ... | ||
| 4492 | * IndexMetaClass = RM_CreateKeyMetaClass(...); | ||
| 4493 | * } | ||
| 4494 | */ | ||
| 4495 | RedisModuleKeyMetaClassId RM_CreateKeyMetaClass(RedisModuleCtx *ctx, | ||
| 4496 | const char *metaname, | ||
| 4497 | int metaver, | ||
| 4498 | void *confPtr) | ||
| 4499 | { | ||
| 4500 | RedisModuleKeyMetaClassId id; | ||
| 4501 | |||
| 4502 | /* Allow registration only OnLoad (and when debug commands disabled) */ | ||
| 4503 | if ((!ctx->module->onload) && (server.enable_debug_cmd == PROTECTED_ACTION_ALLOWED_NO)) | ||
| 4504 | return -1; | ||
| 4505 | |||
| 4506 | if (!confPtr) | ||
| 4507 | return -2; | ||
| 4508 | |||
| 4509 | /* This structure supposed to evolve over time and defines the superset of all | ||
| 4510 | * module type methods supported across different Redis module API versions */ | ||
| 4511 | struct KeyMetaConfAllVersions { | ||
| 4512 | uint64_t version; | ||
| 4513 | uint64_t flags; | ||
| 4514 | uint64_t reset_value; | ||
| 4515 | KeyMetaCopyFunc copy; | ||
| 4516 | KeyMetaRenameFunc rename; | ||
| 4517 | KeyMetaMoveFunc move; | ||
| 4518 | KeyMetaUnlinkFunc unlink; | ||
| 4519 | KeyMetaFreeFunc free; | ||
| 4520 | /********** TBD: **********/ | ||
| 4521 | KeyMetaLoadFunc rdb_load; | ||
| 4522 | KeyMetaSaveFunc rdb_save; | ||
| 4523 | KeyMetaAOFRewriteFunc aof_rewrite; | ||
| 4524 | KeyMetaDefragFunc defrag; | ||
| 4525 | KeyMetaMemUsageFunc mem_usage; | ||
| 4526 | KeyMetaFreeEffortFunc free_effort; | ||
| 4527 | } *legacy = (struct KeyMetaConfAllVersions *)confPtr; | ||
| 4528 | |||
| 4529 | if (legacy->version == 0 || legacy->version > REDISMODULE_KEY_META_VERSION) | ||
| 4530 | return -3; | ||
| 4531 | |||
| 4532 | KeyMetaClassConf conf = { | ||
| 4533 | .flags = legacy->flags, | ||
| 4534 | .reset_value = legacy->reset_value, | ||
| 4535 | |||
| 4536 | .copy = legacy->copy, | ||
| 4537 | .rename = legacy->rename, | ||
| 4538 | .move = legacy->move, | ||
| 4539 | .unlink = legacy->unlink, | ||
| 4540 | .free = legacy->free, | ||
| 4541 | |||
| 4542 | .rdb_load = legacy->rdb_load, | ||
| 4543 | .rdb_save = legacy->rdb_save, | ||
| 4544 | .aof_rewrite = legacy->aof_rewrite, | ||
| 4545 | .defrag = legacy->defrag, | ||
| 4546 | .mem_usage = legacy->mem_usage, | ||
| 4547 | .free_effort = legacy->free_effort | ||
| 4548 | }; | ||
| 4549 | |||
| 4550 | id = keyMetaClassCreate(ctx->module, metaname, metaver, &conf); | ||
| 4551 | if (id == 0) return -4; | ||
| 4552 | |||
| 4553 | return id; | ||
| 4554 | } | ||
| 4555 | |||
| 4556 | /* Release a class by its ID. Returns 1 on success, 0 on failure. */ | ||
| 4557 | int RM_ReleaseKeyMetaClass(RedisModuleKeyMetaClassId id) { | ||
| 4558 | return (keyMetaClassRelease(id)) ? REDISMODULE_OK : REDISMODULE_ERR; | ||
| 4559 | } | ||
| 4560 | |||
| 4561 | /* Set metadata of class id on an opened key. If metadata is already attached, | ||
| 4562 | * it will be overwritten. The caller is responsible for retrieving and freeing | ||
| 4563 | * any existing pointer-based metadata before setting a new value. */ | ||
| 4564 | int RM_SetKeyMeta(RedisModuleKeyMetaClassId id, RedisModuleKey *key, uint64_t metadata) { | ||
| 4565 | if ((!key) || !(key->mode & REDISMODULE_WRITE) || (key->kv == NULL)) | ||
| 4566 | return REDISMODULE_ERR; | ||
| 4567 | |||
| 4568 | kvobj *new_kv = keyMetaSetMetadata(key->db, key->kv, id, metadata); | ||
| 4569 | if (new_kv == NULL) | ||
| 4570 | return REDISMODULE_ERR; | ||
| 4571 | |||
| 4572 | /* Update the key->kv pointer in case it was reallocated */ | ||
| 4573 | key->kv = new_kv; | ||
| 4574 | |||
| 4575 | return REDISMODULE_OK; | ||
| 4576 | } | ||
| 4577 | |||
| 4578 | /* Get metadata of class id from an opened key. */ | ||
| 4579 | int RM_GetKeyMeta(RedisModuleKeyMetaClassId id, RedisModuleKey *key, uint64_t *metadata) { | ||
| 4580 | if ((!key) || (key->kv == NULL) || (!metadata)) | ||
| 4581 | return REDISMODULE_ERR; | ||
| 4582 | |||
| 4583 | if (keyMetaGetMetadata(id, key->kv, metadata) == 0) | ||
| 4584 | return REDISMODULE_ERR; | ||
| 4585 | |||
| 4586 | return REDISMODULE_OK; | ||
| 4587 | } | ||
| 4588 | |||
| 4589 | /* Performs similar operation to FLUSHALL, and optionally start a new AOF file (if enabled) | ||
| 4590 | * If restart_aof is true, you must make sure the command that triggered this call is not | ||
| 4591 | * propagated to the AOF file. | ||
| 4592 | * When async is set to true, db contents will be freed by a background thread. */ | ||
| 4593 | void RM_ResetDataset(int restart_aof, int async) { | ||
| 4594 | if (restart_aof && server.aof_state != AOF_OFF) stopAppendOnly(); | ||
| 4595 | flushAllDataAndResetRDB((async? EMPTYDB_ASYNC: EMPTYDB_NO_FLAGS) | EMPTYDB_NOFUNCTIONS); | ||
| 4596 | if (server.aof_enabled && restart_aof) startAppendOnlyWithRetry(); | ||
| 4597 | } | ||
| 4598 | |||
| 4599 | /* Returns the number of keys in the current db. */ | ||
| 4600 | unsigned long long RM_DbSize(RedisModuleCtx *ctx) { | ||
| 4601 | return dbSize(ctx->client->db); | ||
| 4602 | } | ||
| 4603 | |||
| 4604 | /* Returns a name of a random key, or NULL if current db is empty. */ | ||
| 4605 | RedisModuleString *RM_RandomKey(RedisModuleCtx *ctx) { | ||
| 4606 | robj *key = dbRandomKey(ctx->client->db); | ||
| 4607 | autoMemoryAdd(ctx,REDISMODULE_AM_STRING,key); | ||
| 4608 | return key; | ||
| 4609 | } | ||
| 4610 | |||
| 4611 | /* Returns the name of the key currently being processed. */ | ||
| 4612 | const RedisModuleString *RM_GetKeyNameFromOptCtx(RedisModuleKeyOptCtx *ctx) { | ||
| 4613 | return ctx->from_key; | ||
| 4614 | } | ||
| 4615 | |||
| 4616 | /* Returns the name of the target key currently being processed. */ | ||
| 4617 | const RedisModuleString *RM_GetToKeyNameFromOptCtx(RedisModuleKeyOptCtx *ctx) { | ||
| 4618 | return ctx->to_key; | ||
| 4619 | } | ||
| 4620 | |||
| 4621 | /* Returns the dbid currently being processed. */ | ||
| 4622 | int RM_GetDbIdFromOptCtx(RedisModuleKeyOptCtx *ctx) { | ||
| 4623 | return ctx->from_dbid; | ||
| 4624 | } | ||
| 4625 | |||
| 4626 | /* Returns the target dbid currently being processed. */ | ||
| 4627 | int RM_GetToDbIdFromOptCtx(RedisModuleKeyOptCtx *ctx) { | ||
| 4628 | return ctx->to_dbid; | ||
| 4629 | } | ||
| 4630 | /* -------------------------------------------------------------------------- | ||
| 4631 | * ## Key API for String type | ||
| 4632 | * | ||
| 4633 | * See also RM_ValueLength(), which returns the length of a string. | ||
| 4634 | * -------------------------------------------------------------------------- */ | ||
| 4635 | |||
| 4636 | /* If the key is open for writing, set the specified string 'str' as the | ||
| 4637 | * value of the key, deleting the old value if any. | ||
| 4638 | * On success REDISMODULE_OK is returned. If the key is not open for | ||
| 4639 | * writing or there is an active iterator, REDISMODULE_ERR is returned. */ | ||
| 4640 | int RM_StringSet(RedisModuleKey *key, RedisModuleString *str) { | ||
| 4641 | if (!(key->mode & REDISMODULE_WRITE) || key->iter) return REDISMODULE_ERR; | ||
| 4642 | RM_DeleteKey(key); | ||
| 4643 | /* Retain str so setKey copies it to db rather than reallocating it. */ | ||
| 4644 | incrRefCount(str); | ||
| 4645 | setKey(key->ctx->client,key->db,key->key,&str,SETKEY_NO_SIGNAL); | ||
| 4646 | key->kv = str; | ||
| 4647 | return REDISMODULE_OK; | ||
| 4648 | } | ||
| 4649 | |||
| 4650 | /* Prepare the key associated string value for DMA access, and returns | ||
| 4651 | * a pointer and size (by reference), that the user can use to read or | ||
| 4652 | * modify the string in-place accessing it directly via pointer. | ||
| 4653 | * | ||
| 4654 | * The 'mode' is composed by bitwise OR-ing the following flags: | ||
| 4655 | * | ||
| 4656 | * REDISMODULE_READ -- Read access | ||
| 4657 | * REDISMODULE_WRITE -- Write access | ||
| 4658 | * | ||
| 4659 | * If the DMA is not requested for writing, the pointer returned should | ||
| 4660 | * only be accessed in a read-only fashion. | ||
| 4661 | * | ||
| 4662 | * On error (wrong type) NULL is returned. | ||
| 4663 | * | ||
| 4664 | * DMA access rules: | ||
| 4665 | * | ||
| 4666 | * 1. No other key writing function should be called since the moment | ||
| 4667 | * the pointer is obtained, for all the time we want to use DMA access | ||
| 4668 | * to read or modify the string. | ||
| 4669 | * | ||
| 4670 | * 2. Each time RM_StringTruncate() is called, to continue with the DMA | ||
| 4671 | * access, RM_StringDMA() should be called again to re-obtain | ||
| 4672 | * a new pointer and length. | ||
| 4673 | * | ||
| 4674 | * 3. If the returned pointer is not NULL, but the length is zero, no | ||
| 4675 | * byte can be touched (the string is empty, or the key itself is empty) | ||
| 4676 | * so a RM_StringTruncate() call should be used if there is to enlarge | ||
| 4677 | * the string, and later call StringDMA() again to get the pointer. | ||
| 4678 | */ | ||
| 4679 | char *RM_StringDMA(RedisModuleKey *key, size_t *len, int mode) { | ||
| 4680 | /* We need to return *some* pointer for empty keys, we just return | ||
| 4681 | * a string literal pointer, that is the advantage to be mapped into | ||
| 4682 | * a read only memory page, so the module will segfault if a write | ||
| 4683 | * attempt is performed. */ | ||
| 4684 | char *emptystring = "<dma-empty-string>"; | ||
| 4685 | if (key->kv == NULL) { | ||
| 4686 | *len = 0; | ||
| 4687 | return emptystring; | ||
| 4688 | } | ||
| 4689 | |||
| 4690 | if (key->kv->type != OBJ_STRING) return NULL; | ||
| 4691 | |||
| 4692 | /* For write access, and even for read access if the object is encoded, | ||
| 4693 | * we unshare the string (that has the side effect of decoding it). */ | ||
| 4694 | if ((mode & REDISMODULE_WRITE) || key->kv->encoding != OBJ_ENCODING_RAW) | ||
| 4695 | key->kv = dbUnshareStringValue(key->db, key->key, key->kv); | ||
| 4696 | |||
| 4697 | *len = sdslen(key->kv->ptr); | ||
| 4698 | return key->kv->ptr; | ||
| 4699 | } | ||
| 4700 | |||
| 4701 | /* If the key is open for writing and is of string type, resize it, padding | ||
| 4702 | * with zero bytes if the new length is greater than the old one. | ||
| 4703 | * | ||
| 4704 | * After this call, RM_StringDMA() must be called again to continue | ||
| 4705 | * DMA access with the new pointer. | ||
| 4706 | * | ||
| 4707 | * The function returns REDISMODULE_OK on success, and REDISMODULE_ERR on | ||
| 4708 | * error, that is, the key is not open for writing, is not a string | ||
| 4709 | * or resizing for more than 512 MB is requested. | ||
| 4710 | * | ||
| 4711 | * If the key is empty, a string key is created with the new string value | ||
| 4712 | * unless the new length value requested is zero. */ | ||
| 4713 | int RM_StringTruncate(RedisModuleKey *key, size_t newlen) { | ||
| 4714 | if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR; | ||
| 4715 | if (key->kv && key->kv->type != OBJ_STRING) return REDISMODULE_ERR; | ||
| 4716 | if (newlen > 512*1024*1024) return REDISMODULE_ERR; | ||
| 4717 | |||
| 4718 | /* Empty key and new len set to 0. Just return REDISMODULE_OK without | ||
| 4719 | * doing anything. */ | ||
| 4720 | if (key->kv == NULL && newlen == 0) return REDISMODULE_OK; | ||
| 4721 | |||
| 4722 | if (key->kv == NULL) { | ||
| 4723 | /* Empty key: create it with the new size. */ | ||
| 4724 | robj *o = createObject(OBJ_STRING,sdsnewlen(NULL, newlen)); | ||
| 4725 | setKey(key->ctx->client, key->db, key->key, &o, SETKEY_NO_SIGNAL); | ||
| 4726 | key->kv = o; | ||
| 4727 | } else { | ||
| 4728 | /* Unshare and resize. */ | ||
| 4729 | key->kv = dbUnshareStringValue(key->db, key->key, key->kv); | ||
| 4730 | size_t curlen = sdslen(key->kv->ptr); | ||
| 4731 | if (newlen > curlen) { | ||
| 4732 | key->kv->ptr = sdsgrowzero(key->kv->ptr,newlen); | ||
| 4733 | } else if (newlen < curlen) { | ||
| 4734 | sdssubstr(key->kv->ptr,0,newlen); | ||
| 4735 | /* If the string is too wasteful, reallocate it. */ | ||
| 4736 | if (sdslen(key->kv->ptr) < sdsavail(key->kv->ptr)) | ||
| 4737 | key->kv->ptr = sdsRemoveFreeSpace(key->kv->ptr, 0); | ||
| 4738 | } | ||
| 4739 | } | ||
| 4740 | return REDISMODULE_OK; | ||
| 4741 | } | ||
| 4742 | |||
| 4743 | /* -------------------------------------------------------------------------- | ||
| 4744 | * ## Key API for List type | ||
| 4745 | * | ||
| 4746 | * Many of the list functions access elements by index. Since a list is in | ||
| 4747 | * essence a doubly-linked list, accessing elements by index is generally an | ||
| 4748 | * O(N) operation. However, if elements are accessed sequentially or with | ||
| 4749 | * indices close together, the functions are optimized to seek the index from | ||
| 4750 | * the previous index, rather than seeking from the ends of the list. | ||
| 4751 | * | ||
| 4752 | * This enables iteration to be done efficiently using a simple for loop: | ||
| 4753 | * | ||
| 4754 | * long n = RM_ValueLength(key); | ||
| 4755 | * for (long i = 0; i < n; i++) { | ||
| 4756 | * RedisModuleString *elem = RedisModule_ListGet(key, i); | ||
| 4757 | * // Do stuff... | ||
| 4758 | * } | ||
| 4759 | * | ||
| 4760 | * Note that after modifying a list using RM_ListPop, RM_ListSet or | ||
| 4761 | * RM_ListInsert, the internal iterator is invalidated so the next operation | ||
| 4762 | * will require a linear seek. | ||
| 4763 | * | ||
| 4764 | * Modifying a list in any another way, for example using RM_Call(), while a key | ||
| 4765 | * is open will confuse the internal iterator and may cause trouble if the key | ||
| 4766 | * is used after such modifications. The key must be reopened in this case. | ||
| 4767 | * | ||
| 4768 | * See also RM_ValueLength(), which returns the length of a list. | ||
| 4769 | * -------------------------------------------------------------------------- */ | ||
| 4770 | |||
| 4771 | /* Seeks the key's internal list iterator to the given index. On success, 1 is | ||
| 4772 | * returned and key->iter, key->u.list.entry and key->u.list.index are set. On | ||
| 4773 | * failure, 0 is returned and errno is set as required by the list API | ||
| 4774 | * functions. */ | ||
| 4775 | int moduleListIteratorSeek(RedisModuleKey *key, long index, int mode) { | ||
| 4776 | if (!key) { | ||
| 4777 | errno = EINVAL; | ||
| 4778 | return 0; | ||
| 4779 | } else if (!key->kv || key->kv->type != OBJ_LIST) { | ||
| 4780 | errno = ENOTSUP; | ||
| 4781 | return 0; | ||
| 4782 | } if (!(key->mode & mode)) { | ||
| 4783 | errno = EBADF; | ||
| 4784 | return 0; | ||
| 4785 | } | ||
| 4786 | |||
| 4787 | long length = listTypeLength(key->kv); | ||
| 4788 | if (index < -length || index >= length) { | ||
| 4789 | errno = EDOM; /* Invalid index */ | ||
| 4790 | return 0; | ||
| 4791 | } | ||
| 4792 | |||
| 4793 | if (key->iter == NULL) { | ||
| 4794 | /* No existing iterator. Create one. */ | ||
| 4795 | key->iter = zmalloc(sizeof(listTypeIterator)); | ||
| 4796 | listTypeInitIterator(key->iter, key->kv, index, LIST_TAIL); | ||
| 4797 | serverAssert(listTypeNext(key->iter, &key->u.list.entry)); | ||
| 4798 | key->u.list.index = index; | ||
| 4799 | return 1; | ||
| 4800 | } | ||
| 4801 | |||
| 4802 | /* There's an existing iterator. Make sure the requested index has the same | ||
| 4803 | * sign as the iterator's index. */ | ||
| 4804 | if (index < 0 && key->u.list.index >= 0) index += length; | ||
| 4805 | else if (index >= 0 && key->u.list.index < 0) index -= length; | ||
| 4806 | |||
| 4807 | if (index == key->u.list.index) return 1; /* We're done. */ | ||
| 4808 | |||
| 4809 | /* Seek the iterator to the requested index. */ | ||
| 4810 | unsigned char dir = key->u.list.index < index ? LIST_TAIL : LIST_HEAD; | ||
| 4811 | listTypeSetIteratorDirection(key->iter, &key->u.list.entry, dir); | ||
| 4812 | while (key->u.list.index != index) { | ||
| 4813 | serverAssert(listTypeNext(key->iter, &key->u.list.entry)); | ||
| 4814 | key->u.list.index += dir == LIST_HEAD ? -1 : 1; | ||
| 4815 | } | ||
| 4816 | return 1; | ||
| 4817 | } | ||
| 4818 | |||
| 4819 | /* Push an element into a list, on head or tail depending on 'where' argument | ||
| 4820 | * (REDISMODULE_LIST_HEAD or REDISMODULE_LIST_TAIL). If the key refers to an | ||
| 4821 | * empty key opened for writing, the key is created. On success, REDISMODULE_OK | ||
| 4822 | * is returned. On failure, REDISMODULE_ERR is returned and `errno` is set as | ||
| 4823 | * follows: | ||
| 4824 | * | ||
| 4825 | * - EINVAL if key or ele is NULL. | ||
| 4826 | * - ENOTSUP if the key is of another type than list. | ||
| 4827 | * - EBADF if the key is not opened for writing. | ||
| 4828 | * | ||
| 4829 | * Note: Before Redis 7.0, `errno` was not set by this function. */ | ||
| 4830 | int RM_ListPush(RedisModuleKey *key, int where, RedisModuleString *ele) { | ||
| 4831 | size_t oldsize = 0; | ||
| 4832 | if (!key || !ele) { | ||
| 4833 | errno = EINVAL; | ||
| 4834 | return REDISMODULE_ERR; | ||
| 4835 | } else if (key->kv != NULL && key->kv->type != OBJ_LIST) { | ||
| 4836 | errno = ENOTSUP; | ||
| 4837 | return REDISMODULE_ERR; | ||
| 4838 | } if (!(key->mode & REDISMODULE_WRITE)) { | ||
| 4839 | errno = EBADF; | ||
| 4840 | return REDISMODULE_ERR; | ||
| 4841 | } | ||
| 4842 | |||
| 4843 | if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR; | ||
| 4844 | if (key->kv && key->kv->type != OBJ_LIST) return REDISMODULE_ERR; | ||
| 4845 | if (key->iter) moduleFreeKeyIterator(key); | ||
| 4846 | if (key->kv == NULL) moduleCreateEmptyKey(key,REDISMODULE_KEYTYPE_LIST); | ||
| 4847 | if (server.memory_tracking_per_slot) | ||
| 4848 | oldsize = listTypeAllocSize(key->kv); | ||
| 4849 | listTypeTryConversionAppend(key->kv, &ele, 0, 0, moduleFreeListIterator, key); | ||
| 4850 | listTypePush(key->kv, ele, | ||
| 4851 | (where == REDISMODULE_LIST_HEAD) ? LIST_HEAD : LIST_TAIL); | ||
| 4852 | int64_t l = listTypeLength(key->kv); | ||
| 4853 | updateKeysizesHist(key->db, getKeySlot(key->key->ptr), OBJ_LIST, l-1, l); | ||
| 4854 | if (server.memory_tracking_per_slot) | ||
| 4855 | updateSlotAllocSize(key->db, getKeySlot(key->key->ptr), oldsize, listTypeAllocSize(key->kv)); | ||
| 4856 | return REDISMODULE_OK; | ||
| 4857 | } | ||
| 4858 | |||
| 4859 | /* Pop an element from the list, and returns it as a module string object | ||
| 4860 | * that the user should be free with RM_FreeString() or by enabling | ||
| 4861 | * automatic memory. The `where` argument specifies if the element should be | ||
| 4862 | * popped from the beginning or the end of the list (REDISMODULE_LIST_HEAD or | ||
| 4863 | * REDISMODULE_LIST_TAIL). On failure, the command returns NULL and sets | ||
| 4864 | * `errno` as follows: | ||
| 4865 | * | ||
| 4866 | * - EINVAL if key is NULL. | ||
| 4867 | * - ENOTSUP if the key is empty or of another type than list. | ||
| 4868 | * - EBADF if the key is not opened for writing. | ||
| 4869 | * | ||
| 4870 | * Note: Before Redis 7.0, `errno` was not set by this function. */ | ||
| 4871 | RedisModuleString *RM_ListPop(RedisModuleKey *key, int where) { | ||
| 4872 | size_t oldsize = 0; | ||
| 4873 | if (!key) { | ||
| 4874 | errno = EINVAL; | ||
| 4875 | return NULL; | ||
| 4876 | } else if (key->kv == NULL || key->kv->type != OBJ_LIST) { | ||
| 4877 | errno = ENOTSUP; | ||
| 4878 | return NULL; | ||
| 4879 | } else if (!(key->mode & REDISMODULE_WRITE)) { | ||
| 4880 | errno = EBADF; | ||
| 4881 | return NULL; | ||
| 4882 | } | ||
| 4883 | if (key->iter) moduleFreeKeyIterator(key); | ||
| 4884 | if (server.memory_tracking_per_slot) | ||
| 4885 | oldsize = listTypeAllocSize(key->kv); | ||
| 4886 | robj *ele = listTypePop(key->kv, | ||
| 4887 | (where == REDISMODULE_LIST_HEAD) ? LIST_HEAD : LIST_TAIL); | ||
| 4888 | robj *decoded = getDecodedObject(ele); | ||
| 4889 | decrRefCount(ele); | ||
| 4890 | int64_t l = (int64_t) listTypeLength(key->kv); | ||
| 4891 | updateKeysizesHist(key->db, getKeySlot(key->key->ptr), OBJ_LIST, l+1, l); | ||
| 4892 | if (server.memory_tracking_per_slot) | ||
| 4893 | updateSlotAllocSize(key->db, getKeySlot(key->key->ptr), oldsize, listTypeAllocSize(key->kv)); | ||
| 4894 | if (!moduleDelKeyIfEmpty(key)) { | ||
| 4895 | if (server.memory_tracking_per_slot) | ||
| 4896 | oldsize = listTypeAllocSize(key->kv); | ||
| 4897 | listTypeTryConversion(key->kv, LIST_CONV_SHRINKING, moduleFreeListIterator, key); | ||
| 4898 | if (server.memory_tracking_per_slot) | ||
| 4899 | updateSlotAllocSize(key->db, getKeySlot(key->key->ptr), oldsize, listTypeAllocSize(key->kv)); | ||
| 4900 | } | ||
| 4901 | autoMemoryAdd(key->ctx,REDISMODULE_AM_STRING,decoded); | ||
| 4902 | return decoded; | ||
| 4903 | } | ||
| 4904 | |||
| 4905 | /* Returns the element at index `index` in the list stored at `key`, like the | ||
| 4906 | * LINDEX command. The element should be free'd using RM_FreeString() or using | ||
| 4907 | * automatic memory management. | ||
| 4908 | * | ||
| 4909 | * The index is zero-based, so 0 means the first element, 1 the second element | ||
| 4910 | * and so on. Negative indices can be used to designate elements starting at the | ||
| 4911 | * tail of the list. Here, -1 means the last element, -2 means the penultimate | ||
| 4912 | * and so forth. | ||
| 4913 | * | ||
| 4914 | * When no value is found at the given key and index, NULL is returned and | ||
| 4915 | * `errno` is set as follows: | ||
| 4916 | * | ||
| 4917 | * - EINVAL if key is NULL. | ||
| 4918 | * - ENOTSUP if the key is not a list. | ||
| 4919 | * - EBADF if the key is not opened for reading. | ||
| 4920 | * - EDOM if the index is not a valid index in the list. | ||
| 4921 | */ | ||
| 4922 | RedisModuleString *RM_ListGet(RedisModuleKey *key, long index) { | ||
| 4923 | if (moduleListIteratorSeek(key, index, REDISMODULE_READ)) { | ||
| 4924 | robj *elem = listTypeGet(&key->u.list.entry); | ||
| 4925 | robj *decoded = getDecodedObject(elem); | ||
| 4926 | decrRefCount(elem); | ||
| 4927 | autoMemoryAdd(key->ctx, REDISMODULE_AM_STRING, decoded); | ||
| 4928 | return decoded; | ||
| 4929 | } else { | ||
| 4930 | return NULL; | ||
| 4931 | } | ||
| 4932 | } | ||
| 4933 | |||
| 4934 | /* Replaces the element at index `index` in the list stored at `key`. | ||
| 4935 | * | ||
| 4936 | * The index is zero-based, so 0 means the first element, 1 the second element | ||
| 4937 | * and so on. Negative indices can be used to designate elements starting at the | ||
| 4938 | * tail of the list. Here, -1 means the last element, -2 means the penultimate | ||
| 4939 | * and so forth. | ||
| 4940 | * | ||
| 4941 | * On success, REDISMODULE_OK is returned. On failure, REDISMODULE_ERR is | ||
| 4942 | * returned and `errno` is set as follows: | ||
| 4943 | * | ||
| 4944 | * - EINVAL if key or value is NULL. | ||
| 4945 | * - ENOTSUP if the key is not a list. | ||
| 4946 | * - EBADF if the key is not opened for writing. | ||
| 4947 | * - EDOM if the index is not a valid index in the list. | ||
| 4948 | */ | ||
| 4949 | int RM_ListSet(RedisModuleKey *key, long index, RedisModuleString *value) { | ||
| 4950 | size_t oldsize = 0; | ||
| 4951 | if (!value) { | ||
| 4952 | errno = EINVAL; | ||
| 4953 | return REDISMODULE_ERR; | ||
| 4954 | } | ||
| 4955 | if (!key->kv || key->kv->type != OBJ_LIST) { | ||
| 4956 | errno = ENOTSUP; | ||
| 4957 | return REDISMODULE_ERR; | ||
| 4958 | } | ||
| 4959 | if (server.memory_tracking_per_slot) | ||
| 4960 | oldsize = listTypeAllocSize(key->kv); | ||
| 4961 | listTypeTryConversionAppend(key->kv, &value, 0, 0, moduleFreeListIterator, key); | ||
| 4962 | if (moduleListIteratorSeek(key, index, REDISMODULE_WRITE)) { | ||
| 4963 | listTypeReplace(&key->u.list.entry, value); | ||
| 4964 | if (server.memory_tracking_per_slot) | ||
| 4965 | updateSlotAllocSize(key->db, getKeySlot(key->key->ptr), oldsize, listTypeAllocSize(key->kv)); | ||
| 4966 | /* A note in quicklist.c forbids use of iterator after insert, so | ||
| 4967 | * probably also after replace. */ | ||
| 4968 | moduleFreeKeyIterator(key); | ||
| 4969 | return REDISMODULE_OK; | ||
| 4970 | } else { | ||
| 4971 | if (server.memory_tracking_per_slot) | ||
| 4972 | updateSlotAllocSize(key->db, getKeySlot(key->key->ptr), oldsize, listTypeAllocSize(key->kv)); | ||
| 4973 | return REDISMODULE_ERR; | ||
| 4974 | } | ||
| 4975 | } | ||
| 4976 | |||
| 4977 | /* Inserts an element at the given index. | ||
| 4978 | * | ||
| 4979 | * The index is zero-based, so 0 means the first element, 1 the second element | ||
| 4980 | * and so on. Negative indices can be used to designate elements starting at the | ||
| 4981 | * tail of the list. Here, -1 means the last element, -2 means the penultimate | ||
| 4982 | * and so forth. The index is the element's index after inserting it. | ||
| 4983 | * | ||
| 4984 | * On success, REDISMODULE_OK is returned. On failure, REDISMODULE_ERR is | ||
| 4985 | * returned and `errno` is set as follows: | ||
| 4986 | * | ||
| 4987 | * - EINVAL if key or value is NULL. | ||
| 4988 | * - ENOTSUP if the key of another type than list. | ||
| 4989 | * - EBADF if the key is not opened for writing. | ||
| 4990 | * - EDOM if the index is not a valid index in the list. | ||
| 4991 | */ | ||
| 4992 | int RM_ListInsert(RedisModuleKey *key, long index, RedisModuleString *value) { | ||
| 4993 | size_t oldsize = 0; | ||
| 4994 | if (!value) { | ||
| 4995 | errno = EINVAL; | ||
| 4996 | return REDISMODULE_ERR; | ||
| 4997 | } else if (key != NULL && key->kv == NULL && | ||
| 4998 | (index == 0 || index == -1)) { | ||
| 4999 | /* Insert in empty key => push. */ | ||
| 5000 | return RM_ListPush(key, REDISMODULE_LIST_TAIL, value); | ||
| 5001 | } else if (key != NULL && key->kv != NULL && | ||
| 5002 | key->kv->type == OBJ_LIST && | ||
| 5003 | (index == (long)listTypeLength(key->kv) || index == -1)) { | ||
| 5004 | /* Insert after the last element => push tail. */ | ||
| 5005 | return RM_ListPush(key, REDISMODULE_LIST_TAIL, value); | ||
| 5006 | } else if (key != NULL && key->kv != NULL && | ||
| 5007 | key->kv->type == OBJ_LIST && | ||
| 5008 | (index == 0 || index == -(long)listTypeLength(key->kv) - 1)) { | ||
| 5009 | /* Insert before the first element => push head. */ | ||
| 5010 | return RM_ListPush(key, REDISMODULE_LIST_HEAD, value); | ||
| 5011 | } | ||
| 5012 | if (server.memory_tracking_per_slot) | ||
| 5013 | oldsize = listTypeAllocSize(key->kv); | ||
| 5014 | listTypeTryConversionAppend(key->kv, &value, 0, 0, moduleFreeListIterator, key); | ||
| 5015 | if (moduleListIteratorSeek(key, index, REDISMODULE_WRITE)) { | ||
| 5016 | int where = index < 0 ? LIST_TAIL : LIST_HEAD; | ||
| 5017 | listTypeInsert(&key->u.list.entry, value, where); | ||
| 5018 | int64_t l = (int64_t) listTypeLength(key->kv); | ||
| 5019 | updateKeysizesHist(key->db, getKeySlot(key->key->ptr), OBJ_LIST, l-1, l); | ||
| 5020 | if (server.memory_tracking_per_slot) | ||
| 5021 | updateSlotAllocSize(key->db, getKeySlot(key->key->ptr), oldsize, listTypeAllocSize(key->kv)); | ||
| 5022 | /* A note in quicklist.c forbids use of iterator after insert. */ | ||
| 5023 | moduleFreeKeyIterator(key); | ||
| 5024 | return REDISMODULE_OK; | ||
| 5025 | } else { | ||
| 5026 | if (server.memory_tracking_per_slot) | ||
| 5027 | updateSlotAllocSize(key->db, getKeySlot(key->key->ptr), oldsize, listTypeAllocSize(key->kv)); | ||
| 5028 | return REDISMODULE_ERR; | ||
| 5029 | } | ||
| 5030 | } | ||
| 5031 | |||
| 5032 | /* Removes an element at the given index. The index is 0-based. A negative index | ||
| 5033 | * can also be used, counting from the end of the list. | ||
| 5034 | * | ||
| 5035 | * On success, REDISMODULE_OK is returned. On failure, REDISMODULE_ERR is | ||
| 5036 | * returned and `errno` is set as follows: | ||
| 5037 | * | ||
| 5038 | * - EINVAL if key or value is NULL. | ||
| 5039 | * - ENOTSUP if the key is not a list. | ||
| 5040 | * - EBADF if the key is not opened for writing. | ||
| 5041 | * - EDOM if the index is not a valid index in the list. | ||
| 5042 | */ | ||
| 5043 | int RM_ListDelete(RedisModuleKey *key, long index) { | ||
| 5044 | if (moduleListIteratorSeek(key, index, REDISMODULE_WRITE)) { | ||
| 5045 | size_t oldsize = 0; | ||
| 5046 | if (server.memory_tracking_per_slot) | ||
| 5047 | oldsize = listTypeAllocSize(key->kv); | ||
| 5048 | listTypeDelete(key->iter, &key->u.list.entry); | ||
| 5049 | int64_t l = (int64_t) listTypeLength(key->kv); | ||
| 5050 | updateKeysizesHist(key->db, getKeySlot(key->key->ptr), OBJ_LIST, l+1, l); | ||
| 5051 | if (server.memory_tracking_per_slot) | ||
| 5052 | updateSlotAllocSize(key->db, getKeySlot(key->key->ptr), oldsize, listTypeAllocSize(key->kv)); | ||
| 5053 | if (moduleDelKeyIfEmpty(key)) return REDISMODULE_OK; | ||
| 5054 | if (server.memory_tracking_per_slot) | ||
| 5055 | oldsize = listTypeAllocSize(key->kv); | ||
| 5056 | listTypeTryConversion(key->kv, LIST_CONV_SHRINKING, moduleFreeListIterator, key); | ||
| 5057 | if (server.memory_tracking_per_slot) | ||
| 5058 | updateSlotAllocSize(key->db, getKeySlot(key->key->ptr), oldsize, listTypeAllocSize(key->kv)); | ||
| 5059 | if (!key->iter) return REDISMODULE_OK; /* Return ASAP if iterator has been freed */ | ||
| 5060 | if (listTypeNext(key->iter, &key->u.list.entry)) { | ||
| 5061 | /* After delete entry at position 'index', we need to update | ||
| 5062 | * 'key->u.list.index' according to the following cases: | ||
| 5063 | * 1) [1, 2, 3] => dir: forward, index: 0 => [2, 3] => index: still 0 | ||
| 5064 | * 2) [1, 2, 3] => dir: forward, index: -3 => [2, 3] => index: -2 | ||
| 5065 | * 3) [1, 2, 3] => dir: reverse, index: 2 => [1, 2] => index: 1 | ||
| 5066 | * 4) [1, 2, 3] => dir: reverse, index: -1 => [1, 2] => index: still -1 */ | ||
| 5067 | listTypeIterator *li = key->iter; | ||
| 5068 | int reverse = li->direction == LIST_HEAD; | ||
| 5069 | if (key->u.list.index < 0) | ||
| 5070 | key->u.list.index += reverse ? 0 : 1; | ||
| 5071 | else | ||
| 5072 | key->u.list.index += reverse ? -1 : 0; | ||
| 5073 | } else { | ||
| 5074 | /* Reset list iterator if the next entry doesn't exist. */ | ||
| 5075 | moduleFreeKeyIterator(key); | ||
| 5076 | } | ||
| 5077 | return REDISMODULE_OK; | ||
| 5078 | } else { | ||
| 5079 | return REDISMODULE_ERR; | ||
| 5080 | } | ||
| 5081 | } | ||
| 5082 | |||
| 5083 | /* -------------------------------------------------------------------------- | ||
| 5084 | * ## Key API for Sorted Set type | ||
| 5085 | * | ||
| 5086 | * See also RM_ValueLength(), which returns the length of a sorted set. | ||
| 5087 | * -------------------------------------------------------------------------- */ | ||
| 5088 | |||
| 5089 | /* Conversion from/to public flags of the Modules API and our private flags, | ||
| 5090 | * so that we have everything decoupled. */ | ||
| 5091 | int moduleZsetAddFlagsToCoreFlags(int flags) { | ||
| 5092 | int retflags = 0; | ||
| 5093 | if (flags & REDISMODULE_ZADD_XX) retflags |= ZADD_IN_XX; | ||
| 5094 | if (flags & REDISMODULE_ZADD_NX) retflags |= ZADD_IN_NX; | ||
| 5095 | if (flags & REDISMODULE_ZADD_GT) retflags |= ZADD_IN_GT; | ||
| 5096 | if (flags & REDISMODULE_ZADD_LT) retflags |= ZADD_IN_LT; | ||
| 5097 | return retflags; | ||
| 5098 | } | ||
| 5099 | |||
| 5100 | /* See previous function comment. */ | ||
| 5101 | int moduleZsetAddFlagsFromCoreFlags(int flags) { | ||
| 5102 | int retflags = 0; | ||
| 5103 | if (flags & ZADD_OUT_ADDED) retflags |= REDISMODULE_ZADD_ADDED; | ||
| 5104 | if (flags & ZADD_OUT_UPDATED) retflags |= REDISMODULE_ZADD_UPDATED; | ||
| 5105 | if (flags & ZADD_OUT_NOP) retflags |= REDISMODULE_ZADD_NOP; | ||
| 5106 | return retflags; | ||
| 5107 | } | ||
| 5108 | |||
| 5109 | /* Add a new element into a sorted set, with the specified 'score'. | ||
| 5110 | * If the element already exists, the score is updated. | ||
| 5111 | * | ||
| 5112 | * A new sorted set is created at value if the key is an empty open key | ||
| 5113 | * setup for writing. | ||
| 5114 | * | ||
| 5115 | * Additional flags can be passed to the function via a pointer, the flags | ||
| 5116 | * are both used to receive input and to communicate state when the function | ||
| 5117 | * returns. 'flagsptr' can be NULL if no special flags are used. | ||
| 5118 | * | ||
| 5119 | * The input flags are: | ||
| 5120 | * | ||
| 5121 | * REDISMODULE_ZADD_XX: Element must already exist. Do nothing otherwise. | ||
| 5122 | * REDISMODULE_ZADD_NX: Element must not exist. Do nothing otherwise. | ||
| 5123 | * REDISMODULE_ZADD_GT: If element exists, new score must be greater than the current score. | ||
| 5124 | * Do nothing otherwise. Can optionally be combined with XX. | ||
| 5125 | * REDISMODULE_ZADD_LT: If element exists, new score must be less than the current score. | ||
| 5126 | * Do nothing otherwise. Can optionally be combined with XX. | ||
| 5127 | * | ||
| 5128 | * The output flags are: | ||
| 5129 | * | ||
| 5130 | * REDISMODULE_ZADD_ADDED: The new element was added to the sorted set. | ||
| 5131 | * REDISMODULE_ZADD_UPDATED: The score of the element was updated. | ||
| 5132 | * REDISMODULE_ZADD_NOP: No operation was performed because XX or NX flags. | ||
| 5133 | * | ||
| 5134 | * On success the function returns REDISMODULE_OK. On the following errors | ||
| 5135 | * REDISMODULE_ERR is returned: | ||
| 5136 | * | ||
| 5137 | * * The key was not opened for writing. | ||
| 5138 | * * The key is of the wrong type. | ||
| 5139 | * * 'score' double value is not a number (NaN). | ||
| 5140 | */ | ||
| 5141 | int RM_ZsetAdd(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr) { | ||
| 5142 | int in_flags = 0, out_flags = 0; | ||
| 5143 | size_t oldsize = 0; | ||
| 5144 | if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR; | ||
| 5145 | if (key->kv && key->kv->type != OBJ_ZSET) return REDISMODULE_ERR; | ||
| 5146 | if (key->kv == NULL) moduleCreateEmptyKey(key,REDISMODULE_KEYTYPE_ZSET); | ||
| 5147 | if (server.memory_tracking_per_slot) | ||
| 5148 | oldsize = zsetAllocSize(key->kv); | ||
| 5149 | if (flagsptr) in_flags = moduleZsetAddFlagsToCoreFlags(*flagsptr); | ||
| 5150 | if (zsetAdd(key->kv,score,ele->ptr,in_flags,&out_flags,NULL) == 0) { | ||
| 5151 | if (flagsptr) *flagsptr = 0; | ||
| 5152 | if (server.memory_tracking_per_slot) | ||
| 5153 | updateSlotAllocSize(key->db, getKeySlot(key->key->ptr), oldsize, zsetAllocSize(key->kv)); | ||
| 5154 | moduleDelKeyIfEmpty(key); | ||
| 5155 | return REDISMODULE_ERR; | ||
| 5156 | } | ||
| 5157 | if (flagsptr) *flagsptr = moduleZsetAddFlagsFromCoreFlags(out_flags); | ||
| 5158 | int64_t l = (int64_t) zsetLength(key->kv); | ||
| 5159 | updateKeysizesHist(key->db, getKeySlot(key->key->ptr), OBJ_ZSET, l-1, l); | ||
| 5160 | if (server.memory_tracking_per_slot) | ||
| 5161 | updateSlotAllocSize(key->db, getKeySlot(key->key->ptr), oldsize, zsetAllocSize(key->kv)); | ||
| 5162 | return REDISMODULE_OK; | ||
| 5163 | } | ||
| 5164 | |||
| 5165 | /* This function works exactly like RM_ZsetAdd(), but instead of setting | ||
| 5166 | * a new score, the score of the existing element is incremented, or if the | ||
| 5167 | * element does not already exist, it is added assuming the old score was | ||
| 5168 | * zero. | ||
| 5169 | * | ||
| 5170 | * The input and output flags, and the return value, have the same exact | ||
| 5171 | * meaning, with the only difference that this function will return | ||
| 5172 | * REDISMODULE_ERR even when 'score' is a valid double number, but adding it | ||
| 5173 | * to the existing score results into a NaN (not a number) condition. | ||
| 5174 | * | ||
| 5175 | * This function has an additional field 'newscore', if not NULL is filled | ||
| 5176 | * with the new score of the element after the increment, if no error | ||
| 5177 | * is returned. */ | ||
| 5178 | int RM_ZsetIncrby(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore) { | ||
| 5179 | int in_flags = 0, out_flags = 0; | ||
| 5180 | size_t oldsize = 0; | ||
| 5181 | if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR; | ||
| 5182 | if (key->kv && key->kv->type != OBJ_ZSET) return REDISMODULE_ERR; | ||
| 5183 | if (key->kv == NULL) moduleCreateEmptyKey(key,REDISMODULE_KEYTYPE_ZSET); | ||
| 5184 | if (server.memory_tracking_per_slot) | ||
| 5185 | oldsize = zsetAllocSize(key->kv); | ||
| 5186 | if (flagsptr) in_flags = moduleZsetAddFlagsToCoreFlags(*flagsptr); | ||
| 5187 | in_flags |= ZADD_IN_INCR; | ||
| 5188 | if (zsetAdd(key->kv,score,ele->ptr,in_flags,&out_flags,newscore) == 0) { | ||
| 5189 | if (flagsptr) *flagsptr = 0; | ||
| 5190 | if (server.memory_tracking_per_slot) | ||
| 5191 | updateSlotAllocSize(key->db, getKeySlot(key->key->ptr), oldsize, zsetAllocSize(key->kv)); | ||
| 5192 | moduleDelKeyIfEmpty(key); | ||
| 5193 | return REDISMODULE_ERR; | ||
| 5194 | } | ||
| 5195 | if (server.memory_tracking_per_slot) | ||
| 5196 | updateSlotAllocSize(key->db, getKeySlot(key->key->ptr), oldsize, zsetAllocSize(key->kv)); | ||
| 5197 | if (out_flags & ZADD_OUT_ADDED) { | ||
| 5198 | int64_t l = (int64_t) zsetLength(key->kv); | ||
| 5199 | updateKeysizesHist(key->db, getKeySlot(key->key->ptr), OBJ_ZSET, l-1, l); | ||
| 5200 | } | ||
| 5201 | if (flagsptr) *flagsptr = moduleZsetAddFlagsFromCoreFlags(out_flags); | ||
| 5202 | return REDISMODULE_OK; | ||
| 5203 | } | ||
| 5204 | |||
| 5205 | /* Remove the specified element from the sorted set. | ||
| 5206 | * The function returns REDISMODULE_OK on success, and REDISMODULE_ERR | ||
| 5207 | * on one of the following conditions: | ||
| 5208 | * | ||
| 5209 | * * The key was not opened for writing. | ||
| 5210 | * * The key is of the wrong type. | ||
| 5211 | * | ||
| 5212 | * The return value does NOT indicate the fact the element was really | ||
| 5213 | * removed (since it existed) or not, just if the function was executed | ||
| 5214 | * with success. | ||
| 5215 | * | ||
| 5216 | * In order to know if the element was removed, the additional argument | ||
| 5217 | * 'deleted' must be passed, that populates the integer by reference | ||
| 5218 | * setting it to 1 or 0 depending on the outcome of the operation. | ||
| 5219 | * The 'deleted' argument can be NULL if the caller is not interested | ||
| 5220 | * to know if the element was really removed. | ||
| 5221 | * | ||
| 5222 | * Empty keys will be handled correctly by doing nothing. */ | ||
| 5223 | int RM_ZsetRem(RedisModuleKey *key, RedisModuleString *ele, int *deleted) { | ||
| 5224 | size_t oldsize = 0; | ||
| 5225 | if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR; | ||
| 5226 | if (key->kv == NULL) { | ||
| 5227 | if (deleted) *deleted = 0; | ||
| 5228 | return REDISMODULE_OK; | ||
| 5229 | } | ||
| 5230 | if (key->kv->type != OBJ_ZSET) return REDISMODULE_ERR; | ||
| 5231 | if (server.memory_tracking_per_slot) | ||
| 5232 | oldsize = zsetAllocSize(key->kv); | ||
| 5233 | if (zsetDel(key->kv,ele->ptr)) { | ||
| 5234 | if (deleted) *deleted = 1; | ||
| 5235 | int64_t l = (int64_t) zsetLength(key->kv); | ||
| 5236 | updateKeysizesHist(key->db, getKeySlot(key->key->ptr), OBJ_ZSET, l+1, l); | ||
| 5237 | if (server.memory_tracking_per_slot) | ||
| 5238 | updateSlotAllocSize(key->db, getKeySlot(key->key->ptr), oldsize, zsetAllocSize(key->kv)); | ||
| 5239 | moduleDelKeyIfEmpty(key); | ||
| 5240 | } else { | ||
| 5241 | if (deleted) *deleted = 0; | ||
| 5242 | if (server.memory_tracking_per_slot) | ||
| 5243 | updateSlotAllocSize(key->db, getKeySlot(key->key->ptr), oldsize, zsetAllocSize(key->kv)); | ||
| 5244 | } | ||
| 5245 | return REDISMODULE_OK; | ||
| 5246 | } | ||
| 5247 | |||
| 5248 | /* On success retrieve the double score associated at the sorted set element | ||
| 5249 | * 'ele' and returns REDISMODULE_OK. Otherwise REDISMODULE_ERR is returned | ||
| 5250 | * to signal one of the following conditions: | ||
| 5251 | * | ||
| 5252 | * * There is no such element 'ele' in the sorted set. | ||
| 5253 | * * The key is not a sorted set. | ||
| 5254 | * * The key is an open empty key. | ||
| 5255 | */ | ||
| 5256 | int RM_ZsetScore(RedisModuleKey *key, RedisModuleString *ele, double *score) { | ||
| 5257 | if (key->kv == NULL) return REDISMODULE_ERR; | ||
| 5258 | if (key->kv->type != OBJ_ZSET) return REDISMODULE_ERR; | ||
| 5259 | if (zsetScore(key->kv,ele->ptr,score) == C_ERR) return REDISMODULE_ERR; | ||
| 5260 | return REDISMODULE_OK; | ||
| 5261 | } | ||
| 5262 | |||
| 5263 | /* -------------------------------------------------------------------------- | ||
| 5264 | * ## Key API for Sorted Set iterator | ||
| 5265 | * -------------------------------------------------------------------------- */ | ||
| 5266 | |||
| 5267 | void zsetKeyReset(RedisModuleKey *key) { | ||
| 5268 | key->u.zset.type = REDISMODULE_ZSET_RANGE_NONE; | ||
| 5269 | key->u.zset.current = NULL; | ||
| 5270 | key->u.zset.er = 1; | ||
| 5271 | } | ||
| 5272 | |||
| 5273 | /* Stop a sorted set iteration. */ | ||
| 5274 | void RM_ZsetRangeStop(RedisModuleKey *key) { | ||
| 5275 | if (!key->kv || key->kv->type != OBJ_ZSET) return; | ||
| 5276 | /* Free resources if needed. */ | ||
| 5277 | if (key->u.zset.type == REDISMODULE_ZSET_RANGE_LEX) | ||
| 5278 | zslFreeLexRange(&key->u.zset.lrs); | ||
| 5279 | /* Setup sensible values so that misused iteration API calls when an | ||
| 5280 | * iterator is not active will result into something more sensible | ||
| 5281 | * than crashing. */ | ||
| 5282 | zsetKeyReset(key); | ||
| 5283 | } | ||
| 5284 | |||
| 5285 | /* Return the "End of range" flag value to signal the end of the iteration. */ | ||
| 5286 | int RM_ZsetRangeEndReached(RedisModuleKey *key) { | ||
| 5287 | if (!key->kv || key->kv->type != OBJ_ZSET) return 1; | ||
| 5288 | return key->u.zset.er; | ||
| 5289 | } | ||
| 5290 | |||
| 5291 | /* Helper function for RM_ZsetFirstInScoreRange() and RM_ZsetLastInScoreRange(). | ||
| 5292 | * Setup the sorted set iteration according to the specified score range | ||
| 5293 | * (see the functions calling it for more info). If 'first' is true the | ||
| 5294 | * first element in the range is used as a starting point for the iterator | ||
| 5295 | * otherwise the last. Return REDISMODULE_OK on success otherwise | ||
| 5296 | * REDISMODULE_ERR. */ | ||
| 5297 | int zsetInitScoreRange(RedisModuleKey *key, double min, double max, int minex, int maxex, int first) { | ||
| 5298 | if (!key->kv || key->kv->type != OBJ_ZSET) return REDISMODULE_ERR; | ||
| 5299 | |||
| 5300 | RM_ZsetRangeStop(key); | ||
| 5301 | key->u.zset.type = REDISMODULE_ZSET_RANGE_SCORE; | ||
| 5302 | key->u.zset.er = 0; | ||
| 5303 | |||
| 5304 | /* Setup the range structure used by the sorted set core implementation | ||
| 5305 | * in order to seek at the specified element. */ | ||
| 5306 | zrangespec *zrs = &key->u.zset.rs; | ||
| 5307 | zrs->min = min; | ||
| 5308 | zrs->max = max; | ||
| 5309 | zrs->minex = minex; | ||
| 5310 | zrs->maxex = maxex; | ||
| 5311 | |||
| 5312 | if (key->kv->encoding == OBJ_ENCODING_LISTPACK) { | ||
| 5313 | key->u.zset.current = first ? zzlFirstInRange(key->kv->ptr,zrs) : | ||
| 5314 | zzlLastInRange(key->kv->ptr,zrs); | ||
| 5315 | } else if (key->kv->encoding == OBJ_ENCODING_SKIPLIST) { | ||
| 5316 | zset *zs = key->kv->ptr; | ||
| 5317 | zskiplist *zsl = zs->zsl; | ||
| 5318 | key->u.zset.current = first ? zslNthInRange(zsl, zrs, 0, NULL) : | ||
| 5319 | zslNthInRange(zsl, zrs, -1, NULL); | ||
| 5320 | } else { | ||
| 5321 | serverPanic("Unsupported zset encoding"); | ||
| 5322 | } | ||
| 5323 | if (key->u.zset.current == NULL) key->u.zset.er = 1; | ||
| 5324 | return REDISMODULE_OK; | ||
| 5325 | } | ||
| 5326 | |||
| 5327 | /* Setup a sorted set iterator seeking the first element in the specified | ||
| 5328 | * range. Returns REDISMODULE_OK if the iterator was correctly initialized | ||
| 5329 | * otherwise REDISMODULE_ERR is returned in the following conditions: | ||
| 5330 | * | ||
| 5331 | * 1. The value stored at key is not a sorted set or the key is empty. | ||
| 5332 | * | ||
| 5333 | * The range is specified according to the two double values 'min' and 'max'. | ||
| 5334 | * Both can be infinite using the following two macros: | ||
| 5335 | * | ||
| 5336 | * * REDISMODULE_POSITIVE_INFINITE for positive infinite value | ||
| 5337 | * * REDISMODULE_NEGATIVE_INFINITE for negative infinite value | ||
| 5338 | * | ||
| 5339 | * 'minex' and 'maxex' parameters, if true, respectively setup a range | ||
| 5340 | * where the min and max value are exclusive (not included) instead of | ||
| 5341 | * inclusive. */ | ||
| 5342 | int RM_ZsetFirstInScoreRange(RedisModuleKey *key, double min, double max, int minex, int maxex) { | ||
| 5343 | return zsetInitScoreRange(key,min,max,minex,maxex,1); | ||
| 5344 | } | ||
| 5345 | |||
| 5346 | /* Exactly like RedisModule_ZsetFirstInScoreRange() but the last element of | ||
| 5347 | * the range is selected for the start of the iteration instead. */ | ||
| 5348 | int RM_ZsetLastInScoreRange(RedisModuleKey *key, double min, double max, int minex, int maxex) { | ||
| 5349 | return zsetInitScoreRange(key,min,max,minex,maxex,0); | ||
| 5350 | } | ||
| 5351 | |||
| 5352 | /* Helper function for RM_ZsetFirstInLexRange() and RM_ZsetLastInLexRange(). | ||
| 5353 | * Setup the sorted set iteration according to the specified lexicographical | ||
| 5354 | * range (see the functions calling it for more info). If 'first' is true the | ||
| 5355 | * first element in the range is used as a starting point for the iterator | ||
| 5356 | * otherwise the last. Return REDISMODULE_OK on success otherwise | ||
| 5357 | * REDISMODULE_ERR. | ||
| 5358 | * | ||
| 5359 | * Note that this function takes 'min' and 'max' in the same form of the | ||
| 5360 | * Redis ZRANGEBYLEX command. */ | ||
| 5361 | int zsetInitLexRange(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max, int first) { | ||
| 5362 | if (!key->kv || key->kv->type != OBJ_ZSET) return REDISMODULE_ERR; | ||
| 5363 | |||
| 5364 | RM_ZsetRangeStop(key); | ||
| 5365 | key->u.zset.er = 0; | ||
| 5366 | |||
| 5367 | /* Setup the range structure used by the sorted set core implementation | ||
| 5368 | * in order to seek at the specified element. */ | ||
| 5369 | zlexrangespec *zlrs = &key->u.zset.lrs; | ||
| 5370 | if (zslParseLexRange(min, max, zlrs) == C_ERR) return REDISMODULE_ERR; | ||
| 5371 | |||
| 5372 | /* Set the range type to lex only after successfully parsing the range, | ||
| 5373 | * otherwise we don't want the zlexrangespec to be freed. */ | ||
| 5374 | key->u.zset.type = REDISMODULE_ZSET_RANGE_LEX; | ||
| 5375 | |||
| 5376 | if (key->kv->encoding == OBJ_ENCODING_LISTPACK) { | ||
| 5377 | key->u.zset.current = first ? zzlFirstInLexRange(key->kv->ptr,zlrs) : | ||
| 5378 | zzlLastInLexRange(key->kv->ptr,zlrs); | ||
| 5379 | } else if (key->kv->encoding == OBJ_ENCODING_SKIPLIST) { | ||
| 5380 | zset *zs = key->kv->ptr; | ||
| 5381 | zskiplist *zsl = zs->zsl; | ||
| 5382 | key->u.zset.current = first ? zslNthInLexRange(zsl,zlrs,0,NULL) : | ||
| 5383 | zslNthInLexRange(zsl,zlrs,-1,NULL); | ||
| 5384 | } else { | ||
| 5385 | serverPanic("Unsupported zset encoding"); | ||
| 5386 | } | ||
| 5387 | if (key->u.zset.current == NULL) key->u.zset.er = 1; | ||
| 5388 | |||
| 5389 | return REDISMODULE_OK; | ||
| 5390 | } | ||
| 5391 | |||
| 5392 | /* Setup a sorted set iterator seeking the first element in the specified | ||
| 5393 | * lexicographical range. Returns REDISMODULE_OK if the iterator was correctly | ||
| 5394 | * initialized otherwise REDISMODULE_ERR is returned in the | ||
| 5395 | * following conditions: | ||
| 5396 | * | ||
| 5397 | * 1. The value stored at key is not a sorted set or the key is empty. | ||
| 5398 | * 2. The lexicographical range 'min' and 'max' format is invalid. | ||
| 5399 | * | ||
| 5400 | * 'min' and 'max' should be provided as two RedisModuleString objects | ||
| 5401 | * in the same format as the parameters passed to the ZRANGEBYLEX command. | ||
| 5402 | * The function does not take ownership of the objects, so they can be released | ||
| 5403 | * ASAP after the iterator is setup. */ | ||
| 5404 | int RM_ZsetFirstInLexRange(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max) { | ||
| 5405 | return zsetInitLexRange(key,min,max,1); | ||
| 5406 | } | ||
| 5407 | |||
| 5408 | /* Exactly like RedisModule_ZsetFirstInLexRange() but the last element of | ||
| 5409 | * the range is selected for the start of the iteration instead. */ | ||
| 5410 | int RM_ZsetLastInLexRange(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max) { | ||
| 5411 | return zsetInitLexRange(key,min,max,0); | ||
| 5412 | } | ||
| 5413 | |||
| 5414 | /* Return the current sorted set element of an active sorted set iterator | ||
| 5415 | * or NULL if the range specified in the iterator does not include any | ||
| 5416 | * element. */ | ||
| 5417 | RedisModuleString *RM_ZsetRangeCurrentElement(RedisModuleKey *key, double *score) { | ||
| 5418 | RedisModuleString *str; | ||
| 5419 | |||
| 5420 | if (!key->kv || key->kv->type != OBJ_ZSET) return NULL; | ||
| 5421 | if (key->u.zset.current == NULL) return NULL; | ||
| 5422 | if (key->kv->encoding == OBJ_ENCODING_LISTPACK) { | ||
| 5423 | unsigned char *eptr, *sptr; | ||
| 5424 | eptr = key->u.zset.current; | ||
| 5425 | sds ele = lpGetObject(eptr); | ||
| 5426 | if (score) { | ||
| 5427 | sptr = lpNext(key->kv->ptr,eptr); | ||
| 5428 | *score = zzlGetScore(sptr); | ||
| 5429 | } | ||
| 5430 | str = createObject(OBJ_STRING,ele); | ||
| 5431 | } else if (key->kv->encoding == OBJ_ENCODING_SKIPLIST) { | ||
| 5432 | zskiplistNode *ln = key->u.zset.current; | ||
| 5433 | if (score) *score = ln->score; | ||
| 5434 | sds ele = zslGetNodeElement(ln); | ||
| 5435 | str = createStringObject(ele,sdslen(ele)); | ||
| 5436 | } else { | ||
| 5437 | serverPanic("Unsupported zset encoding"); | ||
| 5438 | } | ||
| 5439 | autoMemoryAdd(key->ctx,REDISMODULE_AM_STRING,str); | ||
| 5440 | return str; | ||
| 5441 | } | ||
| 5442 | |||
| 5443 | /* Go to the next element of the sorted set iterator. Returns 1 if there was | ||
| 5444 | * a next element, 0 if we are already at the latest element or the range | ||
| 5445 | * does not include any item at all. */ | ||
| 5446 | int RM_ZsetRangeNext(RedisModuleKey *key) { | ||
| 5447 | if (!key->kv || key->kv->type != OBJ_ZSET) return 0; | ||
| 5448 | if (!key->u.zset.type || !key->u.zset.current) return 0; /* No active iterator. */ | ||
| 5449 | |||
| 5450 | if (key->kv->encoding == OBJ_ENCODING_LISTPACK) { | ||
| 5451 | unsigned char *zl = key->kv->ptr; | ||
| 5452 | unsigned char *eptr = key->u.zset.current; | ||
| 5453 | unsigned char *next; | ||
| 5454 | next = lpNext(zl,eptr); /* Skip element. */ | ||
| 5455 | if (next) next = lpNext(zl,next); /* Skip score. */ | ||
| 5456 | if (next == NULL) { | ||
| 5457 | key->u.zset.er = 1; | ||
| 5458 | return 0; | ||
| 5459 | } else { | ||
| 5460 | /* Are we still within the range? */ | ||
| 5461 | if (key->u.zset.type == REDISMODULE_ZSET_RANGE_SCORE) { | ||
| 5462 | /* Fetch the next element score for the | ||
| 5463 | * range check. */ | ||
| 5464 | unsigned char *saved_next = next; | ||
| 5465 | next = lpNext(zl,next); /* Skip next element. */ | ||
| 5466 | double score = zzlGetScore(next); /* Obtain the next score. */ | ||
| 5467 | if (!zslValueLteMax(score,&key->u.zset.rs)) { | ||
| 5468 | key->u.zset.er = 1; | ||
| 5469 | return 0; | ||
| 5470 | } | ||
| 5471 | next = saved_next; | ||
| 5472 | } else if (key->u.zset.type == REDISMODULE_ZSET_RANGE_LEX) { | ||
| 5473 | if (!zzlLexValueLteMax(next,&key->u.zset.lrs)) { | ||
| 5474 | key->u.zset.er = 1; | ||
| 5475 | return 0; | ||
| 5476 | } | ||
| 5477 | } | ||
| 5478 | key->u.zset.current = next; | ||
| 5479 | return 1; | ||
| 5480 | } | ||
| 5481 | } else if (key->kv->encoding == OBJ_ENCODING_SKIPLIST) { | ||
| 5482 | zskiplistNode *ln = key->u.zset.current, *next = ln->level[0].forward; | ||
| 5483 | if (next == NULL) { | ||
| 5484 | key->u.zset.er = 1; | ||
| 5485 | return 0; | ||
| 5486 | } else { | ||
| 5487 | /* Are we still within the range? */ | ||
| 5488 | if (key->u.zset.type == REDISMODULE_ZSET_RANGE_SCORE && | ||
| 5489 | !zslValueLteMax(next->score,&key->u.zset.rs)) | ||
| 5490 | { | ||
| 5491 | key->u.zset.er = 1; | ||
| 5492 | return 0; | ||
| 5493 | } else if (key->u.zset.type == REDISMODULE_ZSET_RANGE_LEX) { | ||
| 5494 | if (!zslLexValueLteMax(zslGetNodeElement(next),&key->u.zset.lrs)) { | ||
| 5495 | key->u.zset.er = 1; | ||
| 5496 | return 0; | ||
| 5497 | } | ||
| 5498 | } | ||
| 5499 | key->u.zset.current = next; | ||
| 5500 | return 1; | ||
| 5501 | } | ||
| 5502 | } else { | ||
| 5503 | serverPanic("Unsupported zset encoding"); | ||
| 5504 | } | ||
| 5505 | } | ||
| 5506 | |||
| 5507 | /* Go to the previous element of the sorted set iterator. Returns 1 if there was | ||
| 5508 | * a previous element, 0 if we are already at the first element or the range | ||
| 5509 | * does not include any item at all. */ | ||
| 5510 | int RM_ZsetRangePrev(RedisModuleKey *key) { | ||
| 5511 | if (!key->kv || key->kv->type != OBJ_ZSET) return 0; | ||
| 5512 | if (!key->u.zset.type || !key->u.zset.current) return 0; /* No active iterator. */ | ||
| 5513 | |||
| 5514 | if (key->kv->encoding == OBJ_ENCODING_LISTPACK) { | ||
| 5515 | unsigned char *zl = key->kv->ptr; | ||
| 5516 | unsigned char *eptr = key->u.zset.current; | ||
| 5517 | unsigned char *prev; | ||
| 5518 | prev = lpPrev(zl,eptr); /* Go back to previous score. */ | ||
| 5519 | if (prev) prev = lpPrev(zl,prev); /* Back to previous ele. */ | ||
| 5520 | if (prev == NULL) { | ||
| 5521 | key->u.zset.er = 1; | ||
| 5522 | return 0; | ||
| 5523 | } else { | ||
| 5524 | /* Are we still within the range? */ | ||
| 5525 | if (key->u.zset.type == REDISMODULE_ZSET_RANGE_SCORE) { | ||
| 5526 | /* Fetch the previous element score for the | ||
| 5527 | * range check. */ | ||
| 5528 | unsigned char *saved_prev = prev; | ||
| 5529 | prev = lpNext(zl,prev); /* Skip element to get the score.*/ | ||
| 5530 | double score = zzlGetScore(prev); /* Obtain the prev score. */ | ||
| 5531 | if (!zslValueGteMin(score,&key->u.zset.rs)) { | ||
| 5532 | key->u.zset.er = 1; | ||
| 5533 | return 0; | ||
| 5534 | } | ||
| 5535 | prev = saved_prev; | ||
| 5536 | } else if (key->u.zset.type == REDISMODULE_ZSET_RANGE_LEX) { | ||
| 5537 | if (!zzlLexValueGteMin(prev,&key->u.zset.lrs)) { | ||
| 5538 | key->u.zset.er = 1; | ||
| 5539 | return 0; | ||
| 5540 | } | ||
| 5541 | } | ||
| 5542 | key->u.zset.current = prev; | ||
| 5543 | return 1; | ||
| 5544 | } | ||
| 5545 | } else if (key->kv->encoding == OBJ_ENCODING_SKIPLIST) { | ||
| 5546 | zskiplistNode *ln = key->u.zset.current, *prev = ln->backward; | ||
| 5547 | if (prev == NULL) { | ||
| 5548 | key->u.zset.er = 1; | ||
| 5549 | return 0; | ||
| 5550 | } else { | ||
| 5551 | /* Are we still within the range? */ | ||
| 5552 | if (key->u.zset.type == REDISMODULE_ZSET_RANGE_SCORE && | ||
| 5553 | !zslValueGteMin(prev->score,&key->u.zset.rs)) | ||
| 5554 | { | ||
| 5555 | key->u.zset.er = 1; | ||
| 5556 | return 0; | ||
| 5557 | } else if (key->u.zset.type == REDISMODULE_ZSET_RANGE_LEX) { | ||
| 5558 | if (!zslLexValueGteMin(zslGetNodeElement(prev),&key->u.zset.lrs)) { | ||
| 5559 | key->u.zset.er = 1; | ||
| 5560 | return 0; | ||
| 5561 | } | ||
| 5562 | } | ||
| 5563 | key->u.zset.current = prev; | ||
| 5564 | return 1; | ||
| 5565 | } | ||
| 5566 | } else { | ||
| 5567 | serverPanic("Unsupported zset encoding"); | ||
| 5568 | } | ||
| 5569 | } | ||
| 5570 | |||
| 5571 | /* -------------------------------------------------------------------------- | ||
| 5572 | * ## Key API for Hash type | ||
| 5573 | * | ||
| 5574 | * See also RM_ValueLength(), which returns the number of fields in a hash. | ||
| 5575 | * -------------------------------------------------------------------------- */ | ||
| 5576 | |||
| 5577 | /* Set the field of the specified hash field to the specified value. | ||
| 5578 | * If the key is an empty key open for writing, it is created with an empty | ||
| 5579 | * hash value, in order to set the specified field. | ||
| 5580 | * | ||
| 5581 | * The function is variadic and the user must specify pairs of field | ||
| 5582 | * names and values, both as RedisModuleString pointers (unless the | ||
| 5583 | * CFIELD option is set, see later). At the end of the field/value-ptr pairs, | ||
| 5584 | * NULL must be specified as last argument to signal the end of the arguments | ||
| 5585 | * in the variadic function. | ||
| 5586 | * | ||
| 5587 | * Example to set the hash argv[1] to the value argv[2]: | ||
| 5588 | * | ||
| 5589 | * RedisModule_HashSet(key,REDISMODULE_HASH_NONE,argv[1],argv[2],NULL); | ||
| 5590 | * | ||
| 5591 | * The function can also be used in order to delete fields (if they exist) | ||
| 5592 | * by setting them to the specified value of REDISMODULE_HASH_DELETE: | ||
| 5593 | * | ||
| 5594 | * RedisModule_HashSet(key,REDISMODULE_HASH_NONE,argv[1], | ||
| 5595 | * REDISMODULE_HASH_DELETE,NULL); | ||
| 5596 | * | ||
| 5597 | * The behavior of the command changes with the specified flags, that can be | ||
| 5598 | * set to REDISMODULE_HASH_NONE if no special behavior is needed. | ||
| 5599 | * | ||
| 5600 | * REDISMODULE_HASH_NX: The operation is performed only if the field was not | ||
| 5601 | * already existing in the hash. | ||
| 5602 | * REDISMODULE_HASH_XX: The operation is performed only if the field was | ||
| 5603 | * already existing, so that a new value could be | ||
| 5604 | * associated to an existing filed, but no new fields | ||
| 5605 | * are created. | ||
| 5606 | * REDISMODULE_HASH_CFIELDS: The field names passed are null terminated C | ||
| 5607 | * strings instead of RedisModuleString objects. | ||
| 5608 | * REDISMODULE_HASH_COUNT_ALL: Include the number of inserted fields in the | ||
| 5609 | * returned number, in addition to the number of | ||
| 5610 | * updated and deleted fields. (Added in Redis | ||
| 5611 | * 6.2.) | ||
| 5612 | * | ||
| 5613 | * Unless NX is specified, the command overwrites the old field value with | ||
| 5614 | * the new one. | ||
| 5615 | * | ||
| 5616 | * When using REDISMODULE_HASH_CFIELDS, field names are reported using | ||
| 5617 | * normal C strings, so for example to delete the field "foo" the following | ||
| 5618 | * code can be used: | ||
| 5619 | * | ||
| 5620 | * RedisModule_HashSet(key,REDISMODULE_HASH_CFIELDS,"foo", | ||
| 5621 | * REDISMODULE_HASH_DELETE,NULL); | ||
| 5622 | * | ||
| 5623 | * Return value: | ||
| 5624 | * | ||
| 5625 | * The number of fields existing in the hash prior to the call, which have been | ||
| 5626 | * updated (its old value has been replaced by a new value) or deleted. If the | ||
| 5627 | * flag REDISMODULE_HASH_COUNT_ALL is set, inserted fields not previously | ||
| 5628 | * existing in the hash are also counted. | ||
| 5629 | * | ||
| 5630 | * If the return value is zero, `errno` is set (since Redis 6.2) as follows: | ||
| 5631 | * | ||
| 5632 | * - EINVAL if any unknown flags are set or if key is NULL. | ||
| 5633 | * - ENOTSUP if the key is associated with a non Hash value. | ||
| 5634 | * - EBADF if the key was not opened for writing. | ||
| 5635 | * - ENOENT if no fields were counted as described under Return value above. | ||
| 5636 | * This is not actually an error. The return value can be zero if all fields | ||
| 5637 | * were just created and the COUNT_ALL flag was unset, or if changes were held | ||
| 5638 | * back due to the NX and XX flags. | ||
| 5639 | * | ||
| 5640 | * NOTICE: The return value semantics of this function are very different | ||
| 5641 | * between Redis 6.2 and older versions. Modules that use it should determine | ||
| 5642 | * the Redis version and handle it accordingly. | ||
| 5643 | */ | ||
| 5644 | int RM_HashSet(RedisModuleKey *key, int flags, ...) { | ||
| 5645 | va_list ap; | ||
| 5646 | size_t oldsize = 0; | ||
| 5647 | if (!key || (flags & ~(REDISMODULE_HASH_NX | | ||
| 5648 | REDISMODULE_HASH_XX | | ||
| 5649 | REDISMODULE_HASH_CFIELDS | | ||
| 5650 | REDISMODULE_HASH_COUNT_ALL))) { | ||
| 5651 | errno = EINVAL; | ||
| 5652 | return 0; | ||
| 5653 | } else if (key->kv && key->kv->type != OBJ_HASH) { | ||
| 5654 | errno = ENOTSUP; | ||
| 5655 | return 0; | ||
| 5656 | } else if (!(key->mode & REDISMODULE_WRITE)) { | ||
| 5657 | errno = EBADF; | ||
| 5658 | return 0; | ||
| 5659 | } | ||
| 5660 | if (key->kv == NULL) moduleCreateEmptyKey(key,REDISMODULE_KEYTYPE_HASH); | ||
| 5661 | |||
| 5662 | int64_t oldlen = (int64_t) getObjectLength(key->kv); | ||
| 5663 | |||
| 5664 | int count = 0; | ||
| 5665 | va_start(ap, flags); | ||
| 5666 | while(1) { | ||
| 5667 | RedisModuleString *field, *value; | ||
| 5668 | /* Get the field and value objects. */ | ||
| 5669 | if (flags & REDISMODULE_HASH_CFIELDS) { | ||
| 5670 | char *cfield = va_arg(ap,char*); | ||
| 5671 | if (cfield == NULL) break; | ||
| 5672 | field = createRawStringObject(cfield,strlen(cfield)); | ||
| 5673 | } else { | ||
| 5674 | field = va_arg(ap,RedisModuleString*); | ||
| 5675 | if (field == NULL) break; | ||
| 5676 | } | ||
| 5677 | value = va_arg(ap,RedisModuleString*); | ||
| 5678 | |||
| 5679 | /* Handle XX and NX */ | ||
| 5680 | if (flags & (REDISMODULE_HASH_XX|REDISMODULE_HASH_NX)) { | ||
| 5681 | int hfeFlags = HFE_LAZY_AVOID_HASH_DEL | HFE_LAZY_NO_UPDATE_KEYSIZES; | ||
| 5682 | |||
| 5683 | /* | ||
| 5684 | * The hash might contain expired fields. If we lazily delete expired | ||
| 5685 | * field and the command was sent with XX flag, the operation could | ||
| 5686 | * fail and leave the hash empty, which the caller might not expect. | ||
| 5687 | * To prevent unexpected behavior, we avoid lazy deletion in this case | ||
| 5688 | * yet let the operation fail. Note that moduleDelKeyIfEmpty() | ||
| 5689 | * below won't delete the hash if it left with single expired key | ||
| 5690 | * because hash counts blindly expired fields as well. | ||
| 5691 | */ | ||
| 5692 | if (flags & REDISMODULE_HASH_XX) | ||
| 5693 | hfeFlags |= HFE_LAZY_AVOID_FIELD_DEL; | ||
| 5694 | |||
| 5695 | int exists = hashTypeExists(key->db, key->kv, field->ptr, hfeFlags, NULL); | ||
| 5696 | if (((flags & REDISMODULE_HASH_XX) && !exists) || | ||
| 5697 | ((flags & REDISMODULE_HASH_NX) && exists)) | ||
| 5698 | { | ||
| 5699 | if (flags & REDISMODULE_HASH_CFIELDS) decrRefCount(field); | ||
| 5700 | continue; | ||
| 5701 | } | ||
| 5702 | } | ||
| 5703 | |||
| 5704 | /* Handle deletion if value is REDISMODULE_HASH_DELETE. */ | ||
| 5705 | if (value == REDISMODULE_HASH_DELETE) { | ||
| 5706 | if (server.memory_tracking_per_slot) | ||
| 5707 | oldsize = hashTypeAllocSize(key->kv); | ||
| 5708 | count += hashTypeDelete(key->kv, field->ptr); | ||
| 5709 | if (server.memory_tracking_per_slot) | ||
| 5710 | updateSlotAllocSize(key->db, getKeySlot(key->key->ptr), oldsize, hashTypeAllocSize(key->kv)); | ||
| 5711 | if (flags & REDISMODULE_HASH_CFIELDS) decrRefCount(field); | ||
| 5712 | continue; | ||
| 5713 | } | ||
| 5714 | |||
| 5715 | int low_flags = HASH_SET_COPY; | ||
| 5716 | /* If CFIELDS is active, we can pass the ownership of the | ||
| 5717 | * SDS object to the low level function that sets the field | ||
| 5718 | * to avoid a useless copy. */ | ||
| 5719 | if (flags & REDISMODULE_HASH_CFIELDS) | ||
| 5720 | low_flags |= HASH_SET_TAKE_FIELD; | ||
| 5721 | |||
| 5722 | robj *argv[2] = {field,value}; | ||
| 5723 | if (server.memory_tracking_per_slot) | ||
| 5724 | oldsize = hashTypeAllocSize(key->kv); | ||
| 5725 | hashTypeTryConversion(key->db,key->kv,argv,0,1); | ||
| 5726 | int updated = hashTypeSet(key->db, key->kv, field->ptr, value->ptr, low_flags); | ||
| 5727 | if (server.memory_tracking_per_slot) | ||
| 5728 | updateSlotAllocSize(key->db, getKeySlot(key->key->ptr), oldsize, hashTypeAllocSize(key->kv)); | ||
| 5729 | count += (flags & REDISMODULE_HASH_COUNT_ALL) ? 1 : updated; | ||
| 5730 | |||
| 5731 | /* If CFIELDS is active, SDS string ownership is now of hashTypeSet(), | ||
| 5732 | * however we still have to release the 'field' object shell. */ | ||
| 5733 | if (flags & REDISMODULE_HASH_CFIELDS) { | ||
| 5734 | field->ptr = NULL; /* Prevent the SDS string from being freed. */ | ||
| 5735 | decrRefCount(field); | ||
| 5736 | } | ||
| 5737 | } | ||
| 5738 | va_end(ap); | ||
| 5739 | updateKeysizesHist(key->db, getKeySlot(key->key->ptr), OBJ_HASH, oldlen, | ||
| 5740 | (int64_t) hashTypeLength(key->kv, 0)); | ||
| 5741 | |||
| 5742 | moduleDelKeyIfEmpty(key); | ||
| 5743 | if (count == 0) errno = ENOENT; | ||
| 5744 | return count; | ||
| 5745 | } | ||
| 5746 | |||
| 5747 | /* Get fields from a hash value. This function is called using a variable | ||
| 5748 | * number of arguments, alternating a field name (as a RedisModuleString | ||
| 5749 | * pointer) with a pointer to a RedisModuleString pointer, that is set to the | ||
| 5750 | * value of the field if the field exists, or NULL if the field does not exist. | ||
| 5751 | * At the end of the field/value-ptr pairs, NULL must be specified as last | ||
| 5752 | * argument to signal the end of the arguments in the variadic function. | ||
| 5753 | * | ||
| 5754 | * This is an example usage: | ||
| 5755 | * | ||
| 5756 | * RedisModuleString *first, *second; | ||
| 5757 | * RedisModule_HashGet(mykey,REDISMODULE_HASH_NONE,argv[1],&first, | ||
| 5758 | * argv[2],&second,NULL); | ||
| 5759 | * | ||
| 5760 | * As with RedisModule_HashSet() the behavior of the command can be specified | ||
| 5761 | * passing flags different than REDISMODULE_HASH_NONE: | ||
| 5762 | * | ||
| 5763 | * REDISMODULE_HASH_CFIELDS: field names as null terminated C strings. | ||
| 5764 | * | ||
| 5765 | * REDISMODULE_HASH_EXISTS: instead of setting the value of the field | ||
| 5766 | * expecting a RedisModuleString pointer to pointer, the function just | ||
| 5767 | * reports if the field exists or not and expects an integer pointer | ||
| 5768 | * as the second element of each pair. | ||
| 5769 | * | ||
| 5770 | * REDISMODULE_HASH_EXPIRE_TIME: retrieves the expiration time of a field in the hash. | ||
| 5771 | * The function expects a `mstime_t` pointer as the second element of each pair. | ||
| 5772 | * If the field does not exist or has no expiration, the value is set to | ||
| 5773 | * `REDISMODULE_NO_EXPIRE`. This flag must not be used with `REDISMODULE_HASH_EXISTS`. | ||
| 5774 | * | ||
| 5775 | * Example of REDISMODULE_HASH_CFIELDS: | ||
| 5776 | * | ||
| 5777 | * RedisModuleString *username, *hashedpass; | ||
| 5778 | * RedisModule_HashGet(mykey,REDISMODULE_HASH_CFIELDS,"username",&username,"hp",&hashedpass, NULL); | ||
| 5779 | * | ||
| 5780 | * Example of REDISMODULE_HASH_EXISTS: | ||
| 5781 | * | ||
| 5782 | * int exists; | ||
| 5783 | * RedisModule_HashGet(mykey,REDISMODULE_HASH_EXISTS,"username",&exists,NULL); | ||
| 5784 | * | ||
| 5785 | * Example of REDISMODULE_HASH_EXPIRE_TIME: | ||
| 5786 | * | ||
| 5787 | * mstime_t hpExpireTime; | ||
| 5788 | * RedisModule_HashGet(mykey,REDISMODULE_HASH_EXPIRE_TIME,"hp",&hpExpireTime,NULL); | ||
| 5789 | * | ||
| 5790 | * The function returns REDISMODULE_OK on success and REDISMODULE_ERR if | ||
| 5791 | * the key is not a hash value. | ||
| 5792 | * | ||
| 5793 | * Memory management: | ||
| 5794 | * | ||
| 5795 | * The returned RedisModuleString objects should be released with | ||
| 5796 | * RedisModule_FreeString(), or by enabling automatic memory management. | ||
| 5797 | */ | ||
| 5798 | int RM_HashGet(RedisModuleKey *key, int flags, ...) { | ||
| 5799 | int hfeFlags = HFE_LAZY_AVOID_FIELD_DEL | HFE_LAZY_AVOID_HASH_DEL | HFE_LAZY_NO_UPDATE_KEYSIZES; | ||
| 5800 | va_list ap; | ||
| 5801 | if (key->kv && key->kv->type != OBJ_HASH) return REDISMODULE_ERR; | ||
| 5802 | |||
| 5803 | if (key->mode & REDISMODULE_OPEN_KEY_ACCESS_EXPIRED) | ||
| 5804 | hfeFlags = HFE_LAZY_ACCESS_EXPIRED; /* allow read also expired fields */ | ||
| 5805 | |||
| 5806 | /* Verify flag HASH_EXISTS is not set together with HASH_EXPIRE_TIME */ | ||
| 5807 | if ((flags & REDISMODULE_HASH_EXISTS) && (flags & REDISMODULE_HASH_EXPIRE_TIME)) | ||
| 5808 | return REDISMODULE_ERR; | ||
| 5809 | |||
| 5810 | va_start(ap, flags); | ||
| 5811 | while(1) { | ||
| 5812 | RedisModuleString *field, **valueptr; | ||
| 5813 | int *existsptr; | ||
| 5814 | /* Get the field object and the value pointer to pointer. */ | ||
| 5815 | if (flags & REDISMODULE_HASH_CFIELDS) { | ||
| 5816 | char *cfield = va_arg(ap,char*); | ||
| 5817 | if (cfield == NULL) break; | ||
| 5818 | field = createRawStringObject(cfield,strlen(cfield)); | ||
| 5819 | } else { | ||
| 5820 | field = va_arg(ap,RedisModuleString*); | ||
| 5821 | if (field == NULL) break; | ||
| 5822 | } | ||
| 5823 | |||
| 5824 | /* Query the hash for existence or value object. */ | ||
| 5825 | if (flags & REDISMODULE_HASH_EXISTS) { | ||
| 5826 | existsptr = va_arg(ap,int*); | ||
| 5827 | if (key->kv) { | ||
| 5828 | *existsptr = hashTypeExists(key->db, key->kv, field->ptr, hfeFlags, NULL); | ||
| 5829 | } else { | ||
| 5830 | *existsptr = 0; | ||
| 5831 | } | ||
| 5832 | } else if (flags & REDISMODULE_HASH_EXPIRE_TIME) { | ||
| 5833 | mstime_t *expireptr = va_arg(ap,mstime_t*); | ||
| 5834 | *expireptr = REDISMODULE_NO_EXPIRE; | ||
| 5835 | if (key->kv) { | ||
| 5836 | uint64_t expireTime = 0; | ||
| 5837 | /* As an opt, avoid fetching value, only expire time */ | ||
| 5838 | int res = hashTypeGetValueObject(key->db, key->kv, field->ptr, | ||
| 5839 | hfeFlags, NULL, &expireTime, NULL); | ||
| 5840 | /* If field has expiration time */ | ||
| 5841 | if (res && expireTime != 0) *expireptr = expireTime; | ||
| 5842 | } | ||
| 5843 | } else { | ||
| 5844 | valueptr = va_arg(ap,RedisModuleString**); | ||
| 5845 | if (key->kv) { | ||
| 5846 | hashTypeGetValueObject(key->db, key->kv, field->ptr, | ||
| 5847 | hfeFlags, valueptr, NULL, NULL); | ||
| 5848 | |||
| 5849 | if (*valueptr) { | ||
| 5850 | robj *decoded = getDecodedObject(*valueptr); | ||
| 5851 | decrRefCount(*valueptr); | ||
| 5852 | *valueptr = decoded; | ||
| 5853 | } | ||
| 5854 | if (*valueptr) | ||
| 5855 | autoMemoryAdd(key->ctx,REDISMODULE_AM_STRING,*valueptr); | ||
| 5856 | } else { | ||
| 5857 | *valueptr = NULL; | ||
| 5858 | } | ||
| 5859 | } | ||
| 5860 | |||
| 5861 | /* Cleanup */ | ||
| 5862 | if (flags & REDISMODULE_HASH_CFIELDS) decrRefCount(field); | ||
| 5863 | } | ||
| 5864 | va_end(ap); | ||
| 5865 | return REDISMODULE_OK; | ||
| 5866 | } | ||
| 5867 | |||
| 5868 | /** | ||
| 5869 | * Retrieves the minimum expiration time of fields in a hash. | ||
| 5870 | * | ||
| 5871 | * Return: | ||
| 5872 | * - The minimum expiration time (in milliseconds) of the hash fields if at | ||
| 5873 | * least one field has an expiration set. | ||
| 5874 | * - REDISMODULE_NO_EXPIRE if no fields have an expiration set or if the key | ||
| 5875 | * is not a hash. | ||
| 5876 | */ | ||
| 5877 | mstime_t RM_HashFieldMinExpire(RedisModuleKey *key) { | ||
| 5878 | if ((!key->kv) || (key->kv->type != OBJ_HASH)) | ||
| 5879 | return REDISMODULE_NO_EXPIRE; | ||
| 5880 | |||
| 5881 | mstime_t min = hashTypeGetMinExpire(key->kv, 1); | ||
| 5882 | return (min == EB_EXPIRE_TIME_INVALID) ? REDISMODULE_NO_EXPIRE : min; | ||
| 5883 | } | ||
| 5884 | |||
| 5885 | /* -------------------------------------------------------------------------- | ||
| 5886 | * ## Key API for Stream type | ||
| 5887 | * | ||
| 5888 | * For an introduction to streams, see https://redis.io/docs/latest/develop/data-types/streams/. | ||
| 5889 | * | ||
| 5890 | * The type RedisModuleStreamID, which is used in stream functions, is a struct | ||
| 5891 | * with two 64-bit fields and is defined as | ||
| 5892 | * | ||
| 5893 | * typedef struct RedisModuleStreamID { | ||
| 5894 | * uint64_t ms; | ||
| 5895 | * uint64_t seq; | ||
| 5896 | * } RedisModuleStreamID; | ||
| 5897 | * | ||
| 5898 | * See also RM_ValueLength(), which returns the length of a stream, and the | ||
| 5899 | * conversion functions RM_StringToStreamID() and RM_CreateStringFromStreamID(). | ||
| 5900 | * -------------------------------------------------------------------------- */ | ||
| 5901 | |||
| 5902 | /* Adds an entry to a stream. Like XADD without trimming. | ||
| 5903 | * | ||
| 5904 | * - `key`: The key where the stream is (or will be) stored | ||
| 5905 | * - `flags`: A bit field of | ||
| 5906 | * - `REDISMODULE_STREAM_ADD_AUTOID`: Assign a stream ID automatically, like | ||
| 5907 | * `*` in the XADD command. | ||
| 5908 | * - `id`: If the `AUTOID` flag is set, this is where the assigned ID is | ||
| 5909 | * returned. Can be NULL if `AUTOID` is set, if you don't care to receive the | ||
| 5910 | * ID. If `AUTOID` is not set, this is the requested ID. | ||
| 5911 | * - `argv`: A pointer to an array of size `numfields * 2` containing the | ||
| 5912 | * fields and values. | ||
| 5913 | * - `numfields`: The number of field-value pairs in `argv`. | ||
| 5914 | * | ||
| 5915 | * Returns REDISMODULE_OK if an entry has been added. On failure, | ||
| 5916 | * REDISMODULE_ERR is returned and `errno` is set as follows: | ||
| 5917 | * | ||
| 5918 | * - EINVAL if called with invalid arguments | ||
| 5919 | * - ENOTSUP if the key refers to a value of a type other than stream | ||
| 5920 | * - EBADF if the key was not opened for writing | ||
| 5921 | * - EDOM if the given ID was 0-0 or not greater than all other IDs in the | ||
| 5922 | * stream (only if the AUTOID flag is unset) | ||
| 5923 | * - EFBIG if the stream has reached the last possible ID | ||
| 5924 | * - ERANGE if the elements are too large to be stored. | ||
| 5925 | */ | ||
| 5926 | int RM_StreamAdd(RedisModuleKey *key, int flags, RedisModuleStreamID *id, RedisModuleString **argv, long numfields) { | ||
| 5927 | /* Validate args */ | ||
| 5928 | if (!key || (numfields != 0 && !argv) || /* invalid key or argv */ | ||
| 5929 | (flags & ~(REDISMODULE_STREAM_ADD_AUTOID)) || /* invalid flags */ | ||
| 5930 | (!(flags & REDISMODULE_STREAM_ADD_AUTOID) && !id)) { /* id required */ | ||
| 5931 | errno = EINVAL; | ||
| 5932 | return REDISMODULE_ERR; | ||
| 5933 | } else if (key->kv && key->kv->type != OBJ_STREAM) { | ||
| 5934 | errno = ENOTSUP; /* wrong type */ | ||
| 5935 | return REDISMODULE_ERR; | ||
| 5936 | } else if (!(key->mode & REDISMODULE_WRITE)) { | ||
| 5937 | errno = EBADF; /* key not open for writing */ | ||
| 5938 | return REDISMODULE_ERR; | ||
| 5939 | } else if (!(flags & REDISMODULE_STREAM_ADD_AUTOID) && | ||
| 5940 | id->ms == 0 && id->seq == 0) { | ||
| 5941 | errno = EDOM; /* ID out of range */ | ||
| 5942 | return REDISMODULE_ERR; | ||
| 5943 | } | ||
| 5944 | |||
| 5945 | /* Create key if necessary */ | ||
| 5946 | int created = 0; | ||
| 5947 | if (key->kv == NULL) { | ||
| 5948 | moduleCreateEmptyKey(key, REDISMODULE_KEYTYPE_STREAM); | ||
| 5949 | created = 1; | ||
| 5950 | } | ||
| 5951 | |||
| 5952 | stream *s = key->kv->ptr; | ||
| 5953 | if (s->last_id.ms == UINT64_MAX && s->last_id.seq == UINT64_MAX) { | ||
| 5954 | /* The stream has reached the last possible ID */ | ||
| 5955 | errno = EFBIG; | ||
| 5956 | return REDISMODULE_ERR; | ||
| 5957 | } | ||
| 5958 | |||
| 5959 | streamID added_id; | ||
| 5960 | streamID use_id; | ||
| 5961 | streamID *use_id_ptr = NULL; | ||
| 5962 | if (!(flags & REDISMODULE_STREAM_ADD_AUTOID)) { | ||
| 5963 | use_id.ms = id->ms; | ||
| 5964 | use_id.seq = id->seq; | ||
| 5965 | use_id_ptr = &use_id; | ||
| 5966 | } | ||
| 5967 | |||
| 5968 | size_t oldsize = s->alloc_size; | ||
| 5969 | if (streamAppendItem(s,argv,numfields,&added_id,use_id_ptr,1) == C_ERR) { | ||
| 5970 | /* Either the ID not greater than all existing IDs in the stream, or | ||
| 5971 | * the elements are too large to be stored. either way, errno is already | ||
| 5972 | * set by streamAppendItem. */ | ||
| 5973 | if (created) moduleDelKeyIfEmpty(key); | ||
| 5974 | return REDISMODULE_ERR; | ||
| 5975 | } | ||
| 5976 | if (server.memory_tracking_per_slot) | ||
| 5977 | updateSlotAllocSize(key->db, getKeySlot(key->key->ptr), oldsize, s->alloc_size); | ||
| 5978 | /* Postponed signalKeyAsReady(). Done implicitly by moduleCreateEmptyKey() | ||
| 5979 | * so not needed if the stream has just been created. */ | ||
| 5980 | if (!created) key->u.stream.signalready = 1; | ||
| 5981 | |||
| 5982 | if (id != NULL) { | ||
| 5983 | id->ms = added_id.ms; | ||
| 5984 | id->seq = added_id.seq; | ||
| 5985 | } | ||
| 5986 | |||
| 5987 | return REDISMODULE_OK; | ||
| 5988 | } | ||
| 5989 | |||
| 5990 | /* Deletes an entry from a stream. | ||
| 5991 | * | ||
| 5992 | * - `key`: A key opened for writing, with no stream iterator started. | ||
| 5993 | * - `id`: The stream ID of the entry to delete. | ||
| 5994 | * | ||
| 5995 | * Returns REDISMODULE_OK on success. On failure, REDISMODULE_ERR is returned | ||
| 5996 | * and `errno` is set as follows: | ||
| 5997 | * | ||
| 5998 | * - EINVAL if called with invalid arguments | ||
| 5999 | * - ENOTSUP if the key refers to a value of a type other than stream or if the | ||
| 6000 | * key is empty | ||
| 6001 | * - EBADF if the key was not opened for writing or if a stream iterator is | ||
| 6002 | * associated with the key | ||
| 6003 | * - ENOENT if no entry with the given stream ID exists | ||
| 6004 | * | ||
| 6005 | * See also RM_StreamIteratorDelete() for deleting the current entry while | ||
| 6006 | * iterating using a stream iterator. | ||
| 6007 | */ | ||
| 6008 | int RM_StreamDelete(RedisModuleKey *key, RedisModuleStreamID *id) { | ||
| 6009 | if (!key || !id) { | ||
| 6010 | errno = EINVAL; | ||
| 6011 | return REDISMODULE_ERR; | ||
| 6012 | } else if (!key->kv || key->kv->type != OBJ_STREAM) { | ||
| 6013 | errno = ENOTSUP; /* wrong type */ | ||
| 6014 | return REDISMODULE_ERR; | ||
| 6015 | } else if (!(key->mode & REDISMODULE_WRITE) || | ||
| 6016 | key->iter != NULL) { | ||
| 6017 | errno = EBADF; /* key not opened for writing or iterator started */ | ||
| 6018 | return REDISMODULE_ERR; | ||
| 6019 | } | ||
| 6020 | stream *s = key->kv->ptr; | ||
| 6021 | size_t oldsize = s->alloc_size; | ||
| 6022 | streamID streamid = {id->ms, id->seq}; | ||
| 6023 | if (streamDeleteItem(s, &streamid)) { | ||
| 6024 | if (server.memory_tracking_per_slot) | ||
| 6025 | updateSlotAllocSize(key->db, getKeySlot(key->key->ptr), oldsize, s->alloc_size); | ||
| 6026 | return REDISMODULE_OK; | ||
| 6027 | } else { | ||
| 6028 | errno = ENOENT; /* no entry with this id */ | ||
| 6029 | return REDISMODULE_ERR; | ||
| 6030 | } | ||
| 6031 | } | ||
| 6032 | |||
| 6033 | /* Sets up a stream iterator. | ||
| 6034 | * | ||
| 6035 | * - `key`: The stream key opened for reading using RedisModule_OpenKey(). | ||
| 6036 | * - `flags`: | ||
| 6037 | * - `REDISMODULE_STREAM_ITERATOR_EXCLUSIVE`: Don't include `start` and `end` | ||
| 6038 | * in the iterated range. | ||
| 6039 | * - `REDISMODULE_STREAM_ITERATOR_REVERSE`: Iterate in reverse order, starting | ||
| 6040 | * from the `end` of the range. | ||
| 6041 | * - `start`: The lower bound of the range. Use NULL for the beginning of the | ||
| 6042 | * stream. | ||
| 6043 | * - `end`: The upper bound of the range. Use NULL for the end of the stream. | ||
| 6044 | * | ||
| 6045 | * Returns REDISMODULE_OK on success. On failure, REDISMODULE_ERR is returned | ||
| 6046 | * and `errno` is set as follows: | ||
| 6047 | * | ||
| 6048 | * - EINVAL if called with invalid arguments | ||
| 6049 | * - ENOTSUP if the key refers to a value of a type other than stream or if the | ||
| 6050 | * key is empty | ||
| 6051 | * - EBADF if the key was not opened for writing or if a stream iterator is | ||
| 6052 | * already associated with the key | ||
| 6053 | * - EDOM if `start` or `end` is outside the valid range | ||
| 6054 | * | ||
| 6055 | * Returns REDISMODULE_OK on success and REDISMODULE_ERR if the key doesn't | ||
| 6056 | * refer to a stream or if invalid arguments were given. | ||
| 6057 | * | ||
| 6058 | * The stream IDs are retrieved using RedisModule_StreamIteratorNextID() and | ||
| 6059 | * for each stream ID, the fields and values are retrieved using | ||
| 6060 | * RedisModule_StreamIteratorNextField(). The iterator is freed by calling | ||
| 6061 | * RedisModule_StreamIteratorStop(). | ||
| 6062 | * | ||
| 6063 | * Example (error handling omitted): | ||
| 6064 | * | ||
| 6065 | * RedisModule_StreamIteratorStart(key, 0, startid_ptr, endid_ptr); | ||
| 6066 | * RedisModuleStreamID id; | ||
| 6067 | * long numfields; | ||
| 6068 | * while (RedisModule_StreamIteratorNextID(key, &id, &numfields) == | ||
| 6069 | * REDISMODULE_OK) { | ||
| 6070 | * RedisModuleString *field, *value; | ||
| 6071 | * while (RedisModule_StreamIteratorNextField(key, &field, &value) == | ||
| 6072 | * REDISMODULE_OK) { | ||
| 6073 | * // | ||
| 6074 | * // ... Do stuff ... | ||
| 6075 | * // | ||
| 6076 | * RedisModule_FreeString(ctx, field); | ||
| 6077 | * RedisModule_FreeString(ctx, value); | ||
| 6078 | * } | ||
| 6079 | * } | ||
| 6080 | * RedisModule_StreamIteratorStop(key); | ||
| 6081 | */ | ||
| 6082 | int RM_StreamIteratorStart(RedisModuleKey *key, int flags, RedisModuleStreamID *start, RedisModuleStreamID *end) { | ||
| 6083 | /* check args */ | ||
| 6084 | if (!key || | ||
| 6085 | (flags & ~(REDISMODULE_STREAM_ITERATOR_EXCLUSIVE | | ||
| 6086 | REDISMODULE_STREAM_ITERATOR_REVERSE))) { | ||
| 6087 | errno = EINVAL; /* key missing or invalid flags */ | ||
| 6088 | return REDISMODULE_ERR; | ||
| 6089 | } else if (!key->kv || key->kv->type != OBJ_STREAM) { | ||
| 6090 | errno = ENOTSUP; | ||
| 6091 | return REDISMODULE_ERR; /* not a stream */ | ||
| 6092 | } else if (key->iter) { | ||
| 6093 | errno = EBADF; /* iterator already started */ | ||
| 6094 | return REDISMODULE_ERR; | ||
| 6095 | } | ||
| 6096 | |||
| 6097 | /* define range for streamIteratorStart() */ | ||
| 6098 | streamID lower, upper; | ||
| 6099 | if (start) lower = (streamID){start->ms, start->seq}; | ||
| 6100 | if (end) upper = (streamID){end->ms, end->seq}; | ||
| 6101 | if (flags & REDISMODULE_STREAM_ITERATOR_EXCLUSIVE) { | ||
| 6102 | if ((start && streamIncrID(&lower) != C_OK) || | ||
| 6103 | (end && streamDecrID(&upper) != C_OK)) { | ||
| 6104 | errno = EDOM; /* end is 0-0 or start is MAX-MAX? */ | ||
| 6105 | return REDISMODULE_ERR; | ||
| 6106 | } | ||
| 6107 | } | ||
| 6108 | |||
| 6109 | /* create iterator */ | ||
| 6110 | stream *s = key->kv->ptr; | ||
| 6111 | int rev = flags & REDISMODULE_STREAM_ITERATOR_REVERSE; | ||
| 6112 | streamIterator *si = zmalloc(sizeof(*si)); | ||
| 6113 | streamIteratorStart(si, s, start ? &lower : NULL, end ? &upper : NULL, rev); | ||
| 6114 | key->iter = si; | ||
| 6115 | key->u.stream.currentid.ms = 0; /* for RM_StreamIteratorDelete() */ | ||
| 6116 | key->u.stream.currentid.seq = 0; | ||
| 6117 | key->u.stream.numfieldsleft = 0; /* for RM_StreamIteratorNextField() */ | ||
| 6118 | return REDISMODULE_OK; | ||
| 6119 | } | ||
| 6120 | |||
| 6121 | /* Stops a stream iterator created using RedisModule_StreamIteratorStart() and | ||
| 6122 | * reclaims its memory. | ||
| 6123 | * | ||
| 6124 | * Returns REDISMODULE_OK on success. On failure, REDISMODULE_ERR is returned | ||
| 6125 | * and `errno` is set as follows: | ||
| 6126 | * | ||
| 6127 | * - EINVAL if called with a NULL key | ||
| 6128 | * - ENOTSUP if the key refers to a value of a type other than stream or if the | ||
| 6129 | * key is empty | ||
| 6130 | * - EBADF if the key was not opened for writing or if no stream iterator is | ||
| 6131 | * associated with the key | ||
| 6132 | */ | ||
| 6133 | int RM_StreamIteratorStop(RedisModuleKey *key) { | ||
| 6134 | if (!key) { | ||
| 6135 | errno = EINVAL; | ||
| 6136 | return REDISMODULE_ERR; | ||
| 6137 | } else if (!key->kv || key->kv->type != OBJ_STREAM) { | ||
| 6138 | errno = ENOTSUP; | ||
| 6139 | return REDISMODULE_ERR; | ||
| 6140 | } else if (!key->iter) { | ||
| 6141 | errno = EBADF; | ||
| 6142 | return REDISMODULE_ERR; | ||
| 6143 | } | ||
| 6144 | streamIteratorStop(key->iter); | ||
| 6145 | zfree(key->iter); | ||
| 6146 | key->iter = NULL; | ||
| 6147 | return REDISMODULE_OK; | ||
| 6148 | } | ||
| 6149 | |||
| 6150 | /* Finds the next stream entry and returns its stream ID and the number of | ||
| 6151 | * fields. | ||
| 6152 | * | ||
| 6153 | * - `key`: Key for which a stream iterator has been started using | ||
| 6154 | * RedisModule_StreamIteratorStart(). | ||
| 6155 | * - `id`: The stream ID returned. NULL if you don't care. | ||
| 6156 | * - `numfields`: The number of fields in the found stream entry. NULL if you | ||
| 6157 | * don't care. | ||
| 6158 | * | ||
| 6159 | * Returns REDISMODULE_OK and sets `*id` and `*numfields` if an entry was found. | ||
| 6160 | * On failure, REDISMODULE_ERR is returned and `errno` is set as follows: | ||
| 6161 | * | ||
| 6162 | * - EINVAL if called with a NULL key | ||
| 6163 | * - ENOTSUP if the key refers to a value of a type other than stream or if the | ||
| 6164 | * key is empty | ||
| 6165 | * - EBADF if no stream iterator is associated with the key | ||
| 6166 | * - ENOENT if there are no more entries in the range of the iterator | ||
| 6167 | * | ||
| 6168 | * In practice, if RM_StreamIteratorNextID() is called after a successful call | ||
| 6169 | * to RM_StreamIteratorStart() and with the same key, it is safe to assume that | ||
| 6170 | * an REDISMODULE_ERR return value means that there are no more entries. | ||
| 6171 | * | ||
| 6172 | * Use RedisModule_StreamIteratorNextField() to retrieve the fields and values. | ||
| 6173 | * See the example at RedisModule_StreamIteratorStart(). | ||
| 6174 | */ | ||
| 6175 | int RM_StreamIteratorNextID(RedisModuleKey *key, RedisModuleStreamID *id, long *numfields) { | ||
| 6176 | if (!key) { | ||
| 6177 | errno = EINVAL; | ||
| 6178 | return REDISMODULE_ERR; | ||
| 6179 | } else if (!key->kv || key->kv->type != OBJ_STREAM) { | ||
| 6180 | errno = ENOTSUP; | ||
| 6181 | return REDISMODULE_ERR; | ||
| 6182 | } else if (!key->iter) { | ||
| 6183 | errno = EBADF; | ||
| 6184 | return REDISMODULE_ERR; | ||
| 6185 | } | ||
| 6186 | streamIterator *si = key->iter; | ||
| 6187 | int64_t *num_ptr = &key->u.stream.numfieldsleft; | ||
| 6188 | streamID *streamid_ptr = &key->u.stream.currentid; | ||
| 6189 | if (streamIteratorGetID(si, streamid_ptr, num_ptr)) { | ||
| 6190 | if (id) { | ||
| 6191 | id->ms = streamid_ptr->ms; | ||
| 6192 | id->seq = streamid_ptr->seq; | ||
| 6193 | } | ||
| 6194 | if (numfields) *numfields = *num_ptr; | ||
| 6195 | return REDISMODULE_OK; | ||
| 6196 | } else { | ||
| 6197 | /* No entry found. */ | ||
| 6198 | key->u.stream.currentid.ms = 0; /* for RM_StreamIteratorDelete() */ | ||
| 6199 | key->u.stream.currentid.seq = 0; | ||
| 6200 | key->u.stream.numfieldsleft = 0; /* for RM_StreamIteratorNextField() */ | ||
| 6201 | errno = ENOENT; | ||
| 6202 | return REDISMODULE_ERR; | ||
| 6203 | } | ||
| 6204 | } | ||
| 6205 | |||
| 6206 | /* Retrieves the next field of the current stream ID and its corresponding value | ||
| 6207 | * in a stream iteration. This function should be called repeatedly after calling | ||
| 6208 | * RedisModule_StreamIteratorNextID() to fetch each field-value pair. | ||
| 6209 | * | ||
| 6210 | * - `key`: Key where a stream iterator has been started. | ||
| 6211 | * - `field_ptr`: This is where the field is returned. | ||
| 6212 | * - `value_ptr`: This is where the value is returned. | ||
| 6213 | * | ||
| 6214 | * Returns REDISMODULE_OK and points `*field_ptr` and `*value_ptr` to freshly | ||
| 6215 | * allocated RedisModuleString objects. The string objects are freed | ||
| 6216 | * automatically when the callback finishes if automatic memory is enabled. On | ||
| 6217 | * failure, REDISMODULE_ERR is returned and `errno` is set as follows: | ||
| 6218 | * | ||
| 6219 | * - EINVAL if called with a NULL key | ||
| 6220 | * - ENOTSUP if the key refers to a value of a type other than stream or if the | ||
| 6221 | * key is empty | ||
| 6222 | * - EBADF if no stream iterator is associated with the key | ||
| 6223 | * - ENOENT if there are no more fields in the current stream entry | ||
| 6224 | * | ||
| 6225 | * In practice, if RM_StreamIteratorNextField() is called after a successful | ||
| 6226 | * call to RM_StreamIteratorNextID() and with the same key, it is safe to assume | ||
| 6227 | * that an REDISMODULE_ERR return value means that there are no more fields. | ||
| 6228 | * | ||
| 6229 | * See the example at RedisModule_StreamIteratorStart(). | ||
| 6230 | */ | ||
| 6231 | int RM_StreamIteratorNextField(RedisModuleKey *key, RedisModuleString **field_ptr, RedisModuleString **value_ptr) { | ||
| 6232 | if (!key) { | ||
| 6233 | errno = EINVAL; | ||
| 6234 | return REDISMODULE_ERR; | ||
| 6235 | } else if (!key->kv || key->kv->type != OBJ_STREAM) { | ||
| 6236 | errno = ENOTSUP; | ||
| 6237 | return REDISMODULE_ERR; | ||
| 6238 | } else if (!key->iter) { | ||
| 6239 | errno = EBADF; | ||
| 6240 | return REDISMODULE_ERR; | ||
| 6241 | } else if (key->u.stream.numfieldsleft <= 0) { | ||
| 6242 | errno = ENOENT; | ||
| 6243 | return REDISMODULE_ERR; | ||
| 6244 | } | ||
| 6245 | streamIterator *si = key->iter; | ||
| 6246 | unsigned char *field, *value; | ||
| 6247 | int64_t field_len, value_len; | ||
| 6248 | streamIteratorGetField(si, &field, &value, &field_len, &value_len); | ||
| 6249 | if (field_ptr) { | ||
| 6250 | *field_ptr = createRawStringObject((char *)field, field_len); | ||
| 6251 | autoMemoryAdd(key->ctx, REDISMODULE_AM_STRING, *field_ptr); | ||
| 6252 | } | ||
| 6253 | if (value_ptr) { | ||
| 6254 | *value_ptr = createRawStringObject((char *)value, value_len); | ||
| 6255 | autoMemoryAdd(key->ctx, REDISMODULE_AM_STRING, *value_ptr); | ||
| 6256 | } | ||
| 6257 | key->u.stream.numfieldsleft--; | ||
| 6258 | return REDISMODULE_OK; | ||
| 6259 | } | ||
| 6260 | |||
| 6261 | /* Deletes the current stream entry while iterating. | ||
| 6262 | * | ||
| 6263 | * This function can be called after RM_StreamIteratorNextID() or after any | ||
| 6264 | * calls to RM_StreamIteratorNextField(). | ||
| 6265 | * | ||
| 6266 | * Returns REDISMODULE_OK on success. On failure, REDISMODULE_ERR is returned | ||
| 6267 | * and `errno` is set as follows: | ||
| 6268 | * | ||
| 6269 | * - EINVAL if key is NULL | ||
| 6270 | * - ENOTSUP if the key is empty or is of another type than stream | ||
| 6271 | * - EBADF if the key is not opened for writing, if no iterator has been started | ||
| 6272 | * - ENOENT if the iterator has no current stream entry | ||
| 6273 | */ | ||
| 6274 | int RM_StreamIteratorDelete(RedisModuleKey *key) { | ||
| 6275 | if (!key) { | ||
| 6276 | errno = EINVAL; | ||
| 6277 | return REDISMODULE_ERR; | ||
| 6278 | } else if (!key->kv || key->kv->type != OBJ_STREAM) { | ||
| 6279 | errno = ENOTSUP; | ||
| 6280 | return REDISMODULE_ERR; | ||
| 6281 | } else if (!(key->mode & REDISMODULE_WRITE) || !key->iter) { | ||
| 6282 | errno = EBADF; | ||
| 6283 | return REDISMODULE_ERR; | ||
| 6284 | } else if (key->u.stream.currentid.ms == 0 && | ||
| 6285 | key->u.stream.currentid.seq == 0) { | ||
| 6286 | errno = ENOENT; | ||
| 6287 | return REDISMODULE_ERR; | ||
| 6288 | } | ||
| 6289 | streamIterator *si = key->iter; | ||
| 6290 | streamIteratorRemoveEntry(si, &key->u.stream.currentid); | ||
| 6291 | key->u.stream.currentid.ms = 0; /* Make sure repeated Delete() fails */ | ||
| 6292 | key->u.stream.currentid.seq = 0; | ||
| 6293 | key->u.stream.numfieldsleft = 0; /* Make sure NextField() fails */ | ||
| 6294 | return REDISMODULE_OK; | ||
| 6295 | } | ||
| 6296 | |||
| 6297 | /* Trim a stream by length, similar to XTRIM with MAXLEN. | ||
| 6298 | * | ||
| 6299 | * - `key`: Key opened for writing. | ||
| 6300 | * - `flags`: A bitfield of | ||
| 6301 | * - `REDISMODULE_STREAM_TRIM_APPROX`: Trim less if it improves performance, | ||
| 6302 | * like XTRIM with `~`. | ||
| 6303 | * - `length`: The number of stream entries to keep after trimming. | ||
| 6304 | * | ||
| 6305 | * Returns the number of entries deleted. On failure, a negative value is | ||
| 6306 | * returned and `errno` is set as follows: | ||
| 6307 | * | ||
| 6308 | * - EINVAL if called with invalid arguments | ||
| 6309 | * - ENOTSUP if the key is empty or of a type other than stream | ||
| 6310 | * - EBADF if the key is not opened for writing | ||
| 6311 | */ | ||
| 6312 | long long RM_StreamTrimByLength(RedisModuleKey *key, int flags, long long length) { | ||
| 6313 | if (!key || (flags & ~(REDISMODULE_STREAM_TRIM_APPROX)) || length < 0) { | ||
| 6314 | errno = EINVAL; | ||
| 6315 | return -1; | ||
| 6316 | } else if (!key->kv || key->kv->type != OBJ_STREAM) { | ||
| 6317 | errno = ENOTSUP; | ||
| 6318 | return -1; | ||
| 6319 | } else if (!(key->mode & REDISMODULE_WRITE)) { | ||
| 6320 | errno = EBADF; | ||
| 6321 | return -1; | ||
| 6322 | } | ||
| 6323 | int approx = flags & REDISMODULE_STREAM_TRIM_APPROX ? 1 : 0; | ||
| 6324 | stream *s = key->kv->ptr; | ||
| 6325 | size_t oldsize = s->alloc_size; | ||
| 6326 | long long retval = streamTrimByLength(s, length, approx); | ||
| 6327 | if (server.memory_tracking_per_slot) | ||
| 6328 | updateSlotAllocSize(key->db, getKeySlot(key->key->ptr), oldsize, s->alloc_size); | ||
| 6329 | return retval; | ||
| 6330 | } | ||
| 6331 | |||
| 6332 | /* Trim a stream by ID, similar to XTRIM with MINID. | ||
| 6333 | * | ||
| 6334 | * - `key`: Key opened for writing. | ||
| 6335 | * - `flags`: A bitfield of | ||
| 6336 | * - `REDISMODULE_STREAM_TRIM_APPROX`: Trim less if it improves performance, | ||
| 6337 | * like XTRIM with `~`. | ||
| 6338 | * - `id`: The smallest stream ID to keep after trimming. | ||
| 6339 | * | ||
| 6340 | * Returns the number of entries deleted. On failure, a negative value is | ||
| 6341 | * returned and `errno` is set as follows: | ||
| 6342 | * | ||
| 6343 | * - EINVAL if called with invalid arguments | ||
| 6344 | * - ENOTSUP if the key is empty or of a type other than stream | ||
| 6345 | * - EBADF if the key is not opened for writing | ||
| 6346 | */ | ||
| 6347 | long long RM_StreamTrimByID(RedisModuleKey *key, int flags, RedisModuleStreamID *id) { | ||
| 6348 | if (!key || (flags & ~(REDISMODULE_STREAM_TRIM_APPROX)) || !id) { | ||
| 6349 | errno = EINVAL; | ||
| 6350 | return -1; | ||
| 6351 | } else if (!key->kv || key->kv->type != OBJ_STREAM) { | ||
| 6352 | errno = ENOTSUP; | ||
| 6353 | return -1; | ||
| 6354 | } else if (!(key->mode & REDISMODULE_WRITE)) { | ||
| 6355 | errno = EBADF; | ||
| 6356 | return -1; | ||
| 6357 | } | ||
| 6358 | int approx = flags & REDISMODULE_STREAM_TRIM_APPROX ? 1 : 0; | ||
| 6359 | streamID minid = (streamID){id->ms, id->seq}; | ||
| 6360 | stream *s = key->kv->ptr; | ||
| 6361 | size_t oldsize = s->alloc_size; | ||
| 6362 | long long retval = streamTrimByID(s, minid, approx); | ||
| 6363 | if (server.memory_tracking_per_slot) | ||
| 6364 | updateSlotAllocSize(key->db, getKeySlot(key->key->ptr), oldsize, s->alloc_size); | ||
| 6365 | return retval; | ||
| 6366 | } | ||
| 6367 | |||
| 6368 | /* -------------------------------------------------------------------------- | ||
| 6369 | * ## Calling Redis commands from modules | ||
| 6370 | * | ||
| 6371 | * RM_Call() sends a command to Redis. The remaining functions handle the reply. | ||
| 6372 | * -------------------------------------------------------------------------- */ | ||
| 6373 | |||
| 6374 | |||
| 6375 | void moduleParseCallReply_Int(RedisModuleCallReply *reply); | ||
| 6376 | void moduleParseCallReply_BulkString(RedisModuleCallReply *reply); | ||
| 6377 | void moduleParseCallReply_SimpleString(RedisModuleCallReply *reply); | ||
| 6378 | void moduleParseCallReply_Array(RedisModuleCallReply *reply); | ||
| 6379 | |||
| 6380 | |||
| 6381 | |||
| 6382 | |||
| 6383 | /* Free a Call reply and all the nested replies it contains if it's an | ||
| 6384 | * array. */ | ||
| 6385 | void RM_FreeCallReply(RedisModuleCallReply *reply) { | ||
| 6386 | /* This is a wrapper for the recursive free reply function. This is needed | ||
| 6387 | * in order to have the first level function to return on nested replies, | ||
| 6388 | * but only if called by the module API. */ | ||
| 6389 | |||
| 6390 | RedisModuleCtx *ctx = NULL; | ||
| 6391 | if(callReplyType(reply) == REDISMODULE_REPLY_PROMISE) { | ||
| 6392 | RedisModuleAsyncRMCallPromise *promise = callReplyGetPrivateData(reply); | ||
| 6393 | ctx = promise->ctx; | ||
| 6394 | freeRedisModuleAsyncRMCallPromise(promise); | ||
| 6395 | } else { | ||
| 6396 | ctx = callReplyGetPrivateData(reply); | ||
| 6397 | } | ||
| 6398 | |||
| 6399 | freeCallReply(reply); | ||
| 6400 | if (ctx) { | ||
| 6401 | autoMemoryFreed(ctx,REDISMODULE_AM_REPLY,reply); | ||
| 6402 | } | ||
| 6403 | } | ||
| 6404 | |||
| 6405 | /* Return the reply type as one of the following: | ||
| 6406 | * | ||
| 6407 | * - REDISMODULE_REPLY_UNKNOWN | ||
| 6408 | * - REDISMODULE_REPLY_STRING | ||
| 6409 | * - REDISMODULE_REPLY_ERROR | ||
| 6410 | * - REDISMODULE_REPLY_INTEGER | ||
| 6411 | * - REDISMODULE_REPLY_ARRAY | ||
| 6412 | * - REDISMODULE_REPLY_NULL | ||
| 6413 | * - REDISMODULE_REPLY_MAP | ||
| 6414 | * - REDISMODULE_REPLY_SET | ||
| 6415 | * - REDISMODULE_REPLY_BOOL | ||
| 6416 | * - REDISMODULE_REPLY_DOUBLE | ||
| 6417 | * - REDISMODULE_REPLY_BIG_NUMBER | ||
| 6418 | * - REDISMODULE_REPLY_VERBATIM_STRING | ||
| 6419 | * - REDISMODULE_REPLY_ATTRIBUTE | ||
| 6420 | * - REDISMODULE_REPLY_PROMISE */ | ||
| 6421 | int RM_CallReplyType(RedisModuleCallReply *reply) { | ||
| 6422 | return callReplyType(reply); | ||
| 6423 | } | ||
| 6424 | |||
| 6425 | /* Return the reply type length, where applicable. */ | ||
| 6426 | size_t RM_CallReplyLength(RedisModuleCallReply *reply) { | ||
| 6427 | return callReplyGetLen(reply); | ||
| 6428 | } | ||
| 6429 | |||
| 6430 | /* Return the 'idx'-th nested call reply element of an array reply, or NULL | ||
| 6431 | * if the reply type is wrong or the index is out of range. */ | ||
| 6432 | RedisModuleCallReply *RM_CallReplyArrayElement(RedisModuleCallReply *reply, size_t idx) { | ||
| 6433 | return callReplyGetArrayElement(reply, idx); | ||
| 6434 | } | ||
| 6435 | |||
| 6436 | /* Return the `long long` of an integer reply. */ | ||
| 6437 | long long RM_CallReplyInteger(RedisModuleCallReply *reply) { | ||
| 6438 | return callReplyGetLongLong(reply); | ||
| 6439 | } | ||
| 6440 | |||
| 6441 | /* Return the double value of a double reply. */ | ||
| 6442 | double RM_CallReplyDouble(RedisModuleCallReply *reply) { | ||
| 6443 | return callReplyGetDouble(reply); | ||
| 6444 | } | ||
| 6445 | |||
| 6446 | /* Return the big number value of a big number reply. */ | ||
| 6447 | const char *RM_CallReplyBigNumber(RedisModuleCallReply *reply, size_t *len) { | ||
| 6448 | return callReplyGetBigNumber(reply, len); | ||
| 6449 | } | ||
| 6450 | |||
| 6451 | /* Return the value of a verbatim string reply, | ||
| 6452 | * An optional output argument can be given to get verbatim reply format. */ | ||
| 6453 | const char *RM_CallReplyVerbatim(RedisModuleCallReply *reply, size_t *len, const char **format) { | ||
| 6454 | return callReplyGetVerbatim(reply, len, format); | ||
| 6455 | } | ||
| 6456 | |||
| 6457 | /* Return the Boolean value of a Boolean reply. */ | ||
| 6458 | int RM_CallReplyBool(RedisModuleCallReply *reply) { | ||
| 6459 | return callReplyGetBool(reply); | ||
| 6460 | } | ||
| 6461 | |||
| 6462 | /* Return the 'idx'-th nested call reply element of a set reply, or NULL | ||
| 6463 | * if the reply type is wrong or the index is out of range. */ | ||
| 6464 | RedisModuleCallReply *RM_CallReplySetElement(RedisModuleCallReply *reply, size_t idx) { | ||
| 6465 | return callReplyGetSetElement(reply, idx); | ||
| 6466 | } | ||
| 6467 | |||
| 6468 | /* Retrieve the 'idx'-th key and value of a map reply. | ||
| 6469 | * | ||
| 6470 | * Returns: | ||
| 6471 | * - REDISMODULE_OK on success. | ||
| 6472 | * - REDISMODULE_ERR if idx out of range or if the reply type is wrong. | ||
| 6473 | * | ||
| 6474 | * The `key` and `value` arguments are used to return by reference, and may be | ||
| 6475 | * NULL if not required. */ | ||
| 6476 | int RM_CallReplyMapElement(RedisModuleCallReply *reply, size_t idx, RedisModuleCallReply **key, RedisModuleCallReply **val) { | ||
| 6477 | if (callReplyGetMapElement(reply, idx, key, val) == C_OK){ | ||
| 6478 | return REDISMODULE_OK; | ||
| 6479 | } | ||
| 6480 | return REDISMODULE_ERR; | ||
| 6481 | } | ||
| 6482 | |||
| 6483 | /* Return the attribute of the given reply, or NULL if no attribute exists. */ | ||
| 6484 | RedisModuleCallReply *RM_CallReplyAttribute(RedisModuleCallReply *reply) { | ||
| 6485 | return callReplyGetAttribute(reply); | ||
| 6486 | } | ||
| 6487 | |||
| 6488 | /* Retrieve the 'idx'-th key and value of an attribute reply. | ||
| 6489 | * | ||
| 6490 | * Returns: | ||
| 6491 | * - REDISMODULE_OK on success. | ||
| 6492 | * - REDISMODULE_ERR if idx out of range or if the reply type is wrong. | ||
| 6493 | * | ||
| 6494 | * The `key` and `value` arguments are used to return by reference, and may be | ||
| 6495 | * NULL if not required. */ | ||
| 6496 | int RM_CallReplyAttributeElement(RedisModuleCallReply *reply, size_t idx, RedisModuleCallReply **key, RedisModuleCallReply **val) { | ||
| 6497 | if (callReplyGetAttributeElement(reply, idx, key, val) == C_OK){ | ||
| 6498 | return REDISMODULE_OK; | ||
| 6499 | } | ||
| 6500 | return REDISMODULE_ERR; | ||
| 6501 | } | ||
| 6502 | |||
| 6503 | /* Set unblock handler (callback and private data) on the given promise RedisModuleCallReply. | ||
| 6504 | * The given reply must be of promise type (REDISMODULE_REPLY_PROMISE). */ | ||
| 6505 | void RM_CallReplyPromiseSetUnblockHandler(RedisModuleCallReply *reply, RedisModuleOnUnblocked on_unblock, void *private_data) { | ||
| 6506 | RedisModuleAsyncRMCallPromise *promise = callReplyGetPrivateData(reply); | ||
| 6507 | promise->on_unblocked = on_unblock; | ||
| 6508 | promise->private_data = private_data; | ||
| 6509 | } | ||
| 6510 | |||
| 6511 | /* Abort the execution of a given promise RedisModuleCallReply. | ||
| 6512 | * return REDMODULE_OK in case the abort was done successfully and REDISMODULE_ERR | ||
| 6513 | * if its not possible to abort the execution (execution already finished). | ||
| 6514 | * In case the execution was aborted (REDMODULE_OK was returned), the private_data out parameter | ||
| 6515 | * will be set with the value of the private data that was given on 'RM_CallReplyPromiseSetUnblockHandler' | ||
| 6516 | * so the caller will be able to release the private data. | ||
| 6517 | * | ||
| 6518 | * If the execution was aborted successfully, it is promised that the unblock handler will not be called. | ||
| 6519 | * That said, it is possible that the abort operation will successes but the operation will still continue. | ||
| 6520 | * This can happened if, for example, a module implements some blocking command and does not respect the | ||
| 6521 | * disconnect callback. For pure Redis commands this can not happened.*/ | ||
| 6522 | int RM_CallReplyPromiseAbort(RedisModuleCallReply *reply, void **private_data) { | ||
| 6523 | RedisModuleAsyncRMCallPromise *promise = callReplyGetPrivateData(reply); | ||
| 6524 | if (!promise->c) return REDISMODULE_ERR; /* Promise can not be aborted, either already aborted or already finished. */ | ||
| 6525 | if (!(promise->c->flags & CLIENT_BLOCKED)) return REDISMODULE_ERR; /* Client is not blocked anymore, can not abort it. */ | ||
| 6526 | |||
| 6527 | /* Client is still blocked, remove it from any blocking state and release it. */ | ||
| 6528 | if (private_data) *private_data = promise->private_data; | ||
| 6529 | promise->private_data = NULL; | ||
| 6530 | promise->on_unblocked = NULL; | ||
| 6531 | unblockClient(promise->c, 0); | ||
| 6532 | moduleReleaseTempClient(promise->c); | ||
| 6533 | return REDISMODULE_OK; | ||
| 6534 | } | ||
| 6535 | |||
| 6536 | /* Return the pointer and length of a string or error reply. */ | ||
| 6537 | const char *RM_CallReplyStringPtr(RedisModuleCallReply *reply, size_t *len) { | ||
| 6538 | size_t private_len; | ||
| 6539 | if (!len) len = &private_len; | ||
| 6540 | return callReplyGetString(reply, len); | ||
| 6541 | } | ||
| 6542 | |||
| 6543 | /* Return a new string object from a call reply of type string, error or | ||
| 6544 | * integer. Otherwise (wrong reply type) return NULL. */ | ||
| 6545 | RedisModuleString *RM_CreateStringFromCallReply(RedisModuleCallReply *reply) { | ||
| 6546 | RedisModuleCtx* ctx = callReplyGetPrivateData(reply); | ||
| 6547 | size_t len; | ||
| 6548 | const char *str; | ||
| 6549 | switch(callReplyType(reply)) { | ||
| 6550 | case REDISMODULE_REPLY_STRING: | ||
| 6551 | case REDISMODULE_REPLY_ERROR: | ||
| 6552 | str = callReplyGetString(reply, &len); | ||
| 6553 | return RM_CreateString(ctx, str, len); | ||
| 6554 | case REDISMODULE_REPLY_INTEGER: { | ||
| 6555 | char buf[64]; | ||
| 6556 | int len = ll2string(buf,sizeof(buf),callReplyGetLongLong(reply)); | ||
| 6557 | return RM_CreateString(ctx ,buf,len); | ||
| 6558 | } | ||
| 6559 | default: | ||
| 6560 | return NULL; | ||
| 6561 | } | ||
| 6562 | } | ||
| 6563 | |||
| 6564 | /* Modifies the user that RM_Call will use (e.g. for ACL checks) */ | ||
| 6565 | void RM_SetContextUser(RedisModuleCtx *ctx, const RedisModuleUser *user) { | ||
| 6566 | ctx->user = user; | ||
| 6567 | } | ||
| 6568 | |||
| 6569 | /* Returns an array of robj pointers, by parsing the format specifier "fmt" as described for | ||
| 6570 | * the RM_Call(), RM_Replicate() and other module APIs. Populates *argcp with the number of | ||
| 6571 | * items (which equals to the length of the allocated argv). | ||
| 6572 | * | ||
| 6573 | * The integer pointed by 'flags' is populated with flags according | ||
| 6574 | * to special modifiers in "fmt". | ||
| 6575 | * | ||
| 6576 | * "!" -> REDISMODULE_ARGV_REPLICATE | ||
| 6577 | * "A" -> REDISMODULE_ARGV_NO_AOF | ||
| 6578 | * "R" -> REDISMODULE_ARGV_NO_REPLICAS | ||
| 6579 | * "3" -> REDISMODULE_ARGV_RESP_3 | ||
| 6580 | * "0" -> REDISMODULE_ARGV_RESP_AUTO | ||
| 6581 | * "C" -> REDISMODULE_ARGV_RUN_AS_USER | ||
| 6582 | * "M" -> REDISMODULE_ARGV_RESPECT_DENY_OOM | ||
| 6583 | * "K" -> REDISMODULE_ARGV_ALLOW_BLOCK | ||
| 6584 | * | ||
| 6585 | * On error (format specifier error) NULL is returned and nothing is | ||
| 6586 | * allocated. On success the argument vector is returned. */ | ||
| 6587 | robj **moduleCreateArgvFromUserFormat(const char *cmdname, const char *fmt, int *argcp, int *flags, va_list ap) { | ||
| 6588 | int argc = 0, argv_size, j; | ||
| 6589 | robj **argv = NULL; | ||
| 6590 | |||
| 6591 | /* As a first guess to avoid useless reallocations, size argv to | ||
| 6592 | * hold one argument for each char specifier in 'fmt'. */ | ||
| 6593 | argv_size = strlen(fmt)+1; /* +1 because of the command name. */ | ||
| 6594 | argv = zrealloc(argv,sizeof(robj*)*argv_size); | ||
| 6595 | |||
| 6596 | /* Build the arguments vector based on the format specifier. */ | ||
| 6597 | argv[0] = createStringObject(cmdname,strlen(cmdname)); | ||
| 6598 | argc++; | ||
| 6599 | |||
| 6600 | /* Create the client and dispatch the command. */ | ||
| 6601 | const char *p = fmt; | ||
| 6602 | while(*p) { | ||
| 6603 | if (*p == 'c') { | ||
| 6604 | char *cstr = va_arg(ap,char*); | ||
| 6605 | argv[argc++] = createStringObject(cstr,strlen(cstr)); | ||
| 6606 | } else if (*p == 's') { | ||
| 6607 | robj *obj = va_arg(ap,void*); | ||
| 6608 | if (obj->refcount == OBJ_STATIC_REFCOUNT) | ||
| 6609 | obj = createStringObject(obj->ptr,sdslen(obj->ptr)); | ||
| 6610 | else | ||
| 6611 | incrRefCount(obj); | ||
| 6612 | argv[argc++] = obj; | ||
| 6613 | } else if (*p == 'b') { | ||
| 6614 | char *buf = va_arg(ap,char*); | ||
| 6615 | size_t len = va_arg(ap,size_t); | ||
| 6616 | argv[argc++] = createStringObject(buf,len); | ||
| 6617 | } else if (*p == 'l') { | ||
| 6618 | long long ll = va_arg(ap,long long); | ||
| 6619 | argv[argc++] = createStringObjectFromLongLongWithSds(ll); | ||
| 6620 | } else if (*p == 'v') { | ||
| 6621 | /* A vector of strings */ | ||
| 6622 | robj **v = va_arg(ap, void*); | ||
| 6623 | size_t vlen = va_arg(ap, size_t); | ||
| 6624 | |||
| 6625 | /* We need to grow argv to hold the vector's elements. | ||
| 6626 | * We resize by vector_len-1 elements, because we held | ||
| 6627 | * one element in argv for the vector already */ | ||
| 6628 | argv_size += vlen-1; | ||
| 6629 | argv = zrealloc(argv,sizeof(robj*)*argv_size); | ||
| 6630 | |||
| 6631 | size_t i = 0; | ||
| 6632 | for (i = 0; i < vlen; i++) { | ||
| 6633 | incrRefCount(v[i]); | ||
| 6634 | argv[argc++] = v[i]; | ||
| 6635 | } | ||
| 6636 | } else if (*p == '!') { | ||
| 6637 | if (flags) (*flags) |= REDISMODULE_ARGV_REPLICATE; | ||
| 6638 | } else if (*p == 'A') { | ||
| 6639 | if (flags) (*flags) |= REDISMODULE_ARGV_NO_AOF; | ||
| 6640 | } else if (*p == 'R') { | ||
| 6641 | if (flags) (*flags) |= REDISMODULE_ARGV_NO_REPLICAS; | ||
| 6642 | } else if (*p == '3') { | ||
| 6643 | if (flags) (*flags) |= REDISMODULE_ARGV_RESP_3; | ||
| 6644 | } else if (*p == '0') { | ||
| 6645 | if (flags) (*flags) |= REDISMODULE_ARGV_RESP_AUTO; | ||
| 6646 | } else if (*p == 'C') { | ||
| 6647 | if (flags) (*flags) |= REDISMODULE_ARGV_RUN_AS_USER; | ||
| 6648 | } else if (*p == 'S') { | ||
| 6649 | if (flags) (*flags) |= REDISMODULE_ARGV_SCRIPT_MODE; | ||
| 6650 | } else if (*p == 'W') { | ||
| 6651 | if (flags) (*flags) |= REDISMODULE_ARGV_NO_WRITES; | ||
| 6652 | } else if (*p == 'M') { | ||
| 6653 | if (flags) (*flags) |= REDISMODULE_ARGV_RESPECT_DENY_OOM; | ||
| 6654 | } else if (*p == 'E') { | ||
| 6655 | if (flags) (*flags) |= REDISMODULE_ARGV_CALL_REPLIES_AS_ERRORS; | ||
| 6656 | } else if (*p == 'D') { | ||
| 6657 | if (flags) (*flags) |= (REDISMODULE_ARGV_DRY_RUN | REDISMODULE_ARGV_CALL_REPLIES_AS_ERRORS); | ||
| 6658 | } else if (*p == 'K') { | ||
| 6659 | if (flags) (*flags) |= REDISMODULE_ARGV_ALLOW_BLOCK; | ||
| 6660 | } else { | ||
| 6661 | goto fmterr; | ||
| 6662 | } | ||
| 6663 | p++; | ||
| 6664 | } | ||
| 6665 | if (argcp) *argcp = argc; | ||
| 6666 | return argv; | ||
| 6667 | |||
| 6668 | fmterr: | ||
| 6669 | for (j = 0; j < argc; j++) | ||
| 6670 | decrRefCount(argv[j]); | ||
| 6671 | zfree(argv); | ||
| 6672 | return NULL; | ||
| 6673 | } | ||
| 6674 | |||
| 6675 | /* Exported API to call any Redis command from modules. | ||
| 6676 | * | ||
| 6677 | * * **cmdname**: The Redis command to call. | ||
| 6678 | * * **fmt**: A format specifier string for the command's arguments. Each | ||
| 6679 | * of the arguments should be specified by a valid type specification. The | ||
| 6680 | * format specifier can also contain the modifiers `!`, `A`, `3` and `R` which | ||
| 6681 | * don't have a corresponding argument. | ||
| 6682 | * | ||
| 6683 | * * `b` -- The argument is a buffer and is immediately followed by another | ||
| 6684 | * argument that is the buffer's length. | ||
| 6685 | * * `c` -- The argument is a pointer to a plain C string (null-terminated). | ||
| 6686 | * * `l` -- The argument is a `long long` integer. | ||
| 6687 | * * `s` -- The argument is a RedisModuleString. | ||
| 6688 | * * `v` -- The argument(s) is a vector of RedisModuleString. | ||
| 6689 | * * `!` -- Sends the Redis command and its arguments to replicas and AOF. | ||
| 6690 | * * `A` -- Suppress AOF propagation, send only to replicas (requires `!`). | ||
| 6691 | * * `R` -- Suppress replicas propagation, send only to AOF (requires `!`). | ||
| 6692 | * * `3` -- Return a RESP3 reply. This will change the command reply. | ||
| 6693 | * e.g., HGETALL returns a map instead of a flat array. | ||
| 6694 | * * `0` -- Return the reply in auto mode, i.e. the reply format will be the | ||
| 6695 | * same as the client attached to the given RedisModuleCtx. This will | ||
| 6696 | * probably used when you want to pass the reply directly to the client. | ||
| 6697 | * * `C` -- Run a command as the user attached to the context. | ||
| 6698 | * User is either attached automatically via the client that directly | ||
| 6699 | * issued the command and created the context or via RM_SetContextUser. | ||
| 6700 | * If the context is not directly created by an issued command (such as a | ||
| 6701 | * background context and no user was set on it via RM_SetContextUser, | ||
| 6702 | * RM_Call will fail. | ||
| 6703 | * Checks if the command can be executed according to ACL rules and causes | ||
| 6704 | * the command to run as the determined user, so that any future user | ||
| 6705 | * dependent activity, such as ACL checks within scripts will proceed as | ||
| 6706 | * expected. | ||
| 6707 | * Otherwise, the command will run as the Redis unrestricted user. | ||
| 6708 | * Upon sending a command from an internal connection, this flag is | ||
| 6709 | * ignored and the command will run as the Redis unrestricted user. | ||
| 6710 | * * `S` -- Run the command in a script mode, this means that it will raise | ||
| 6711 | * an error if a command which are not allowed inside a script | ||
| 6712 | * (flagged with the `deny-script` flag) is invoked (like SHUTDOWN). | ||
| 6713 | * In addition, on script mode, write commands are not allowed if there are | ||
| 6714 | * not enough good replicas (as configured with `min-replicas-to-write`) | ||
| 6715 | * or when the server is unable to persist to the disk. | ||
| 6716 | * * `W` -- Do not allow to run any write command (flagged with the `write` flag). | ||
| 6717 | * * `M` -- Do not allow `deny-oom` flagged commands when over the memory limit. | ||
| 6718 | * * `E` -- Return error as RedisModuleCallReply. If there is an error before | ||
| 6719 | * invoking the command, the error is returned using errno mechanism. | ||
| 6720 | * This flag allows to get the error also as an error CallReply with | ||
| 6721 | * relevant error message. | ||
| 6722 | * * 'D' -- A "Dry Run" mode. Return before executing the underlying call(). | ||
| 6723 | * If everything succeeded, it will return with a NULL, otherwise it will | ||
| 6724 | * return with a CallReply object denoting the error, as if it was called with | ||
| 6725 | * the 'E' code. | ||
| 6726 | * * 'K' -- Allow running blocking commands. If enabled and the command gets blocked, a | ||
| 6727 | * special REDISMODULE_REPLY_PROMISE will be returned. This reply type | ||
| 6728 | * indicates that the command was blocked and the reply will be given asynchronously. | ||
| 6729 | * The module can use this reply object to set a handler which will be called when | ||
| 6730 | * the command gets unblocked using RedisModule_CallReplyPromiseSetUnblockHandler. | ||
| 6731 | * The handler must be set immediately after the command invocation (without releasing | ||
| 6732 | * the Redis lock in between). If the handler is not set, the blocking command will | ||
| 6733 | * still continue its execution but the reply will be ignored (fire and forget), | ||
| 6734 | * notice that this is dangerous in case of role change, as explained below. | ||
| 6735 | * The module can use RedisModule_CallReplyPromiseAbort to abort the command invocation | ||
| 6736 | * if it was not yet finished (see RedisModule_CallReplyPromiseAbort documentation for more | ||
| 6737 | * details). It is also the module's responsibility to abort the execution on role change, either by using | ||
| 6738 | * server event (to get notified when the instance becomes a replica) or relying on the disconnect | ||
| 6739 | * callback of the original client. Failing to do so can result in a write operation on a replica. | ||
| 6740 | * Unlike other call replies, promise call reply **must** be freed while the Redis GIL is locked. | ||
| 6741 | * Notice that on unblocking, the only promise is that the unblock handler will be called, | ||
| 6742 | * If the blocking RM_Call caused the module to also block some real client (using RM_BlockClient), | ||
| 6743 | * it is the module responsibility to unblock this client on the unblock handler. | ||
| 6744 | * On the unblock handler it is only allowed to perform the following: | ||
| 6745 | * * Calling additional Redis commands using RM_Call | ||
| 6746 | * * Open keys using RM_OpenKey | ||
| 6747 | * * Replicate data to the replica or AOF | ||
| 6748 | * | ||
| 6749 | * Specifically, it is not allowed to call any Redis module API which are client related such as: | ||
| 6750 | * * RM_Reply* API's | ||
| 6751 | * * RM_BlockClient | ||
| 6752 | * * RM_GetCurrentUserName | ||
| 6753 | * | ||
| 6754 | * * **...**: The actual arguments to the Redis command. | ||
| 6755 | * | ||
| 6756 | * On success a RedisModuleCallReply object is returned, otherwise | ||
| 6757 | * NULL is returned and errno is set to the following values: | ||
| 6758 | * | ||
| 6759 | * * EBADF: wrong format specifier. | ||
| 6760 | * * EINVAL: wrong command arity. | ||
| 6761 | * * ENOENT: command does not exist. | ||
| 6762 | * * EPERM: operation in Cluster instance with key in non local slot. | ||
| 6763 | * * EROFS: operation in Cluster instance when a write command is sent | ||
| 6764 | * in a readonly state. | ||
| 6765 | * * ENETDOWN: operation in Cluster instance when cluster is down. | ||
| 6766 | * * ENOTSUP: No ACL user for the specified module context | ||
| 6767 | * * EACCES: Command cannot be executed, according to ACL rules | ||
| 6768 | * * ENOSPC: Write or deny-oom command is not allowed | ||
| 6769 | * * ESPIPE: Command not allowed on script mode | ||
| 6770 | * | ||
| 6771 | * Example code fragment: | ||
| 6772 | * | ||
| 6773 | * reply = RedisModule_Call(ctx,"INCRBY","sc",argv[1],"10"); | ||
| 6774 | * if (RedisModule_CallReplyType(reply) == REDISMODULE_REPLY_INTEGER) { | ||
| 6775 | * long long myval = RedisModule_CallReplyInteger(reply); | ||
| 6776 | * // Do something with myval. | ||
| 6777 | * } | ||
| 6778 | * | ||
| 6779 | * This API is documented here: https://redis.io/docs/latest/develop/reference/modules/ | ||
| 6780 | */ | ||
| 6781 | RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...) { | ||
| 6782 | client *c = NULL; | ||
| 6783 | robj **argv = NULL; | ||
| 6784 | int argc = 0, flags = 0; | ||
| 6785 | va_list ap; | ||
| 6786 | RedisModuleCallReply *reply = NULL; | ||
| 6787 | int replicate = 0; /* Replicate this command? */ | ||
| 6788 | int error_as_call_replies = 0; /* return errors as RedisModuleCallReply object */ | ||
| 6789 | uint64_t cmd_flags; | ||
| 6790 | |||
| 6791 | /* Handle arguments. */ | ||
| 6792 | va_start(ap, fmt); | ||
| 6793 | argv = moduleCreateArgvFromUserFormat(cmdname,fmt,&argc,&flags,ap); | ||
| 6794 | replicate = flags & REDISMODULE_ARGV_REPLICATE; | ||
| 6795 | error_as_call_replies = flags & REDISMODULE_ARGV_CALL_REPLIES_AS_ERRORS; | ||
| 6796 | va_end(ap); | ||
| 6797 | |||
| 6798 | c = moduleAllocTempClient(); | ||
| 6799 | |||
| 6800 | if (!(flags & REDISMODULE_ARGV_ALLOW_BLOCK)) { | ||
| 6801 | /* We do not want to allow block, the module do not expect it */ | ||
| 6802 | c->flags |= CLIENT_DENY_BLOCKING; | ||
| 6803 | } | ||
| 6804 | c->db = ctx->client->db; | ||
| 6805 | c->argv = argv; | ||
| 6806 | /* We have to assign argv_len, which is equal to argc in that case (RM_Call) | ||
| 6807 | * because we may be calling a command that uses rewriteClientCommandArgument */ | ||
| 6808 | c->argc = c->argv_len = argc; | ||
| 6809 | c->resp = 2; | ||
| 6810 | if (flags & REDISMODULE_ARGV_RESP_3) { | ||
| 6811 | c->resp = 3; | ||
| 6812 | } else if (flags & REDISMODULE_ARGV_RESP_AUTO) { | ||
| 6813 | /* Auto mode means to take the same protocol as the ctx client. */ | ||
| 6814 | c->resp = ctx->client->resp; | ||
| 6815 | } | ||
| 6816 | if (ctx->module) ctx->module->in_call++; | ||
| 6817 | |||
| 6818 | /* Attach the user of the context or client. | ||
| 6819 | * Internal connections always run with the unrestricted user. */ | ||
| 6820 | user *user = NULL; | ||
| 6821 | if ((flags & REDISMODULE_ARGV_RUN_AS_USER) && | ||
| 6822 | !(ctx->client->flags & CLIENT_INTERNAL)) | ||
| 6823 | { | ||
| 6824 | user = ctx->user ? ctx->user->user : ctx->client->user; | ||
| 6825 | if (!user) { | ||
| 6826 | errno = ENOTSUP; | ||
| 6827 | if (error_as_call_replies) { | ||
| 6828 | sds msg = sdsnew("cannot run as user, no user directly attached to context or context's client"); | ||
| 6829 | reply = callReplyCreateError(msg, ctx); | ||
| 6830 | } | ||
| 6831 | goto cleanup; | ||
| 6832 | } | ||
| 6833 | c->user = user; | ||
| 6834 | } | ||
| 6835 | |||
| 6836 | /* We handle the above format error only when the client is setup so that | ||
| 6837 | * we can free it normally. */ | ||
| 6838 | if (argv == NULL) { | ||
| 6839 | /* We do not return a call reply here this is an error that should only | ||
| 6840 | * be catch by the module indicating wrong fmt was given, the module should | ||
| 6841 | * handle this error and decide how to continue. It is not an error that | ||
| 6842 | * should be propagated to the user. */ | ||
| 6843 | errno = EBADF; | ||
| 6844 | goto cleanup; | ||
| 6845 | } | ||
| 6846 | |||
| 6847 | /* Call command filters */ | ||
| 6848 | moduleCallCommandFilters(c); | ||
| 6849 | |||
| 6850 | /* Lookup command now, after filters had a chance to make modifications | ||
| 6851 | * if necessary. | ||
| 6852 | */ | ||
| 6853 | c->cmd = c->lastcmd = c->realcmd = lookupCommand(c->argv,c->argc); | ||
| 6854 | |||
| 6855 | /* We nullify the command if it is not supposed to be seen by the client, | ||
| 6856 | * such that it will be rejected like an unknown command. */ | ||
| 6857 | if (c->cmd && | ||
| 6858 | (c->cmd->flags & CMD_INTERNAL) && | ||
| 6859 | (flags & REDISMODULE_ARGV_RUN_AS_USER) && | ||
| 6860 | !((ctx->client->flags & CLIENT_INTERNAL) || mustObeyClient(ctx->client))) | ||
| 6861 | { | ||
| 6862 | c->cmd = c->lastcmd = c->realcmd = NULL; | ||
| 6863 | } | ||
| 6864 | |||
| 6865 | sds err; | ||
| 6866 | if (!commandCheckExistence(c, error_as_call_replies? &err : NULL)) { | ||
| 6867 | errno = ENOENT; | ||
| 6868 | if (error_as_call_replies) | ||
| 6869 | reply = callReplyCreateError(err, ctx); | ||
| 6870 | goto cleanup; | ||
| 6871 | } | ||
| 6872 | if (!commandCheckArity(c->cmd, c->argc, error_as_call_replies? &err : NULL)) { | ||
| 6873 | errno = EINVAL; | ||
| 6874 | if (error_as_call_replies) | ||
| 6875 | reply = callReplyCreateError(err, ctx); | ||
| 6876 | goto cleanup; | ||
| 6877 | } | ||
| 6878 | |||
| 6879 | cmd_flags = getCommandFlags(c); | ||
| 6880 | |||
| 6881 | if (flags & REDISMODULE_ARGV_SCRIPT_MODE) { | ||
| 6882 | /* Basically on script mode we want to only allow commands that can | ||
| 6883 | * be executed on scripts (CMD_NOSCRIPT is not set on the command flags) */ | ||
| 6884 | if (cmd_flags & CMD_NOSCRIPT) { | ||
| 6885 | errno = ESPIPE; | ||
| 6886 | if (error_as_call_replies) { | ||
| 6887 | sds msg = sdscatfmt(sdsempty(), "command '%S' is not allowed on script mode", c->cmd->fullname); | ||
| 6888 | reply = callReplyCreateError(msg, ctx); | ||
| 6889 | } | ||
| 6890 | goto cleanup; | ||
| 6891 | } | ||
| 6892 | } | ||
| 6893 | |||
| 6894 | if (flags & REDISMODULE_ARGV_RESPECT_DENY_OOM && server.maxmemory) { | ||
| 6895 | if (cmd_flags & CMD_DENYOOM) { | ||
| 6896 | int oom_state; | ||
| 6897 | if (ctx->flags & REDISMODULE_CTX_THREAD_SAFE) { | ||
| 6898 | /* On background thread we can not count on server.pre_command_oom_state. | ||
| 6899 | * Because it is only set on the main thread, in such case we will check | ||
| 6900 | * the actual memory usage. */ | ||
| 6901 | oom_state = (getMaxmemoryState(NULL,NULL,NULL,NULL) == C_ERR); | ||
| 6902 | } else { | ||
| 6903 | oom_state = server.pre_command_oom_state; | ||
| 6904 | } | ||
| 6905 | if (oom_state) { | ||
| 6906 | errno = ENOSPC; | ||
| 6907 | if (error_as_call_replies) { | ||
| 6908 | sds msg = sdsdup(shared.oomerr->ptr); | ||
| 6909 | reply = callReplyCreateError(msg, ctx); | ||
| 6910 | } | ||
| 6911 | goto cleanup; | ||
| 6912 | } | ||
| 6913 | } | ||
| 6914 | } else { | ||
| 6915 | /* if we aren't OOM checking in RM_Call, we want further executions from this client to also not fail on OOM */ | ||
| 6916 | c->flags |= CLIENT_ALLOW_OOM; | ||
| 6917 | } | ||
| 6918 | |||
| 6919 | if (flags & REDISMODULE_ARGV_NO_WRITES) { | ||
| 6920 | if (cmd_flags & CMD_WRITE) { | ||
| 6921 | errno = ENOSPC; | ||
| 6922 | if (error_as_call_replies) { | ||
| 6923 | sds msg = sdscatfmt(sdsempty(), "Write command '%S' was " | ||
| 6924 | "called while write is not allowed.", c->cmd->fullname); | ||
| 6925 | reply = callReplyCreateError(msg, ctx); | ||
| 6926 | } | ||
| 6927 | goto cleanup; | ||
| 6928 | } | ||
| 6929 | } | ||
| 6930 | |||
| 6931 | /* Script mode tests */ | ||
| 6932 | if (flags & REDISMODULE_ARGV_SCRIPT_MODE) { | ||
| 6933 | if (cmd_flags & CMD_WRITE) { | ||
| 6934 | /* on script mode, if a command is a write command, | ||
| 6935 | * We will not run it if we encounter disk error | ||
| 6936 | * or we do not have enough replicas */ | ||
| 6937 | |||
| 6938 | if (!checkGoodReplicasStatus()) { | ||
| 6939 | errno = ESPIPE; | ||
| 6940 | if (error_as_call_replies) { | ||
| 6941 | sds msg = sdsdup(shared.noreplicaserr->ptr); | ||
| 6942 | reply = callReplyCreateError(msg, ctx); | ||
| 6943 | } | ||
| 6944 | goto cleanup; | ||
| 6945 | } | ||
| 6946 | |||
| 6947 | int deny_write_type = writeCommandsDeniedByDiskError(); | ||
| 6948 | int obey_client = (server.current_client && mustObeyClient(server.current_client)); | ||
| 6949 | |||
| 6950 | if (deny_write_type != DISK_ERROR_TYPE_NONE && !obey_client) { | ||
| 6951 | errno = ESPIPE; | ||
| 6952 | if (error_as_call_replies) { | ||
| 6953 | sds msg = writeCommandsGetDiskErrorMessage(deny_write_type); | ||
| 6954 | reply = callReplyCreateError(msg, ctx); | ||
| 6955 | } | ||
| 6956 | goto cleanup; | ||
| 6957 | } | ||
| 6958 | |||
| 6959 | if (server.masterhost && server.repl_slave_ro && !obey_client) { | ||
| 6960 | errno = ESPIPE; | ||
| 6961 | if (error_as_call_replies) { | ||
| 6962 | sds msg = sdsdup(shared.roslaveerr->ptr); | ||
| 6963 | reply = callReplyCreateError(msg, ctx); | ||
| 6964 | } | ||
| 6965 | goto cleanup; | ||
| 6966 | } | ||
| 6967 | } | ||
| 6968 | |||
| 6969 | if (server.masterhost && server.repl_state != REPL_STATE_CONNECTED && | ||
| 6970 | server.repl_serve_stale_data == 0 && !(cmd_flags & CMD_STALE)) { | ||
| 6971 | errno = ESPIPE; | ||
| 6972 | if (error_as_call_replies) { | ||
| 6973 | sds msg = sdsdup(shared.masterdownerr->ptr); | ||
| 6974 | reply = callReplyCreateError(msg, ctx); | ||
| 6975 | } | ||
| 6976 | goto cleanup; | ||
| 6977 | } | ||
| 6978 | } | ||
| 6979 | |||
| 6980 | /* Check if the user can run this command according to the current | ||
| 6981 | * ACLs. | ||
| 6982 | * | ||
| 6983 | * If RM_SetContextUser has set a user, that user is used, otherwise | ||
| 6984 | * use the attached client's user. If there is no attached client user and no manually | ||
| 6985 | * set user, an error will be returned. | ||
| 6986 | * An internal command should only succeed for an internal connection, AOF, | ||
| 6987 | * and master commands. */ | ||
| 6988 | if (flags & REDISMODULE_ARGV_RUN_AS_USER) { | ||
| 6989 | int acl_errpos; | ||
| 6990 | int acl_retval; | ||
| 6991 | |||
| 6992 | acl_retval = ACLCheckAllUserCommandPerm(user,c->cmd,c->argv,c->argc,NULL,&acl_errpos); | ||
| 6993 | if (acl_retval != ACL_OK) { | ||
| 6994 | sds object = (acl_retval == ACL_DENIED_CMD) ? sdsdup(c->cmd->fullname) : sdsdup(c->argv[acl_errpos]->ptr); | ||
| 6995 | addACLLogEntry(ctx->client, acl_retval, ACL_LOG_CTX_MODULE, -1, c->user->name, object); | ||
| 6996 | if (error_as_call_replies) { | ||
| 6997 | /* verbosity should be same as processCommand() in server.c */ | ||
| 6998 | sds acl_msg = getAclErrorMessage(acl_retval, c->user, c->cmd, c->argv[acl_errpos]->ptr, 0); | ||
| 6999 | sds msg = sdscatfmt(sdsempty(), "-NOPERM %S\r\n", acl_msg); | ||
| 7000 | sdsfree(acl_msg); | ||
| 7001 | reply = callReplyCreateError(msg, ctx); | ||
| 7002 | } | ||
| 7003 | errno = EACCES; | ||
| 7004 | goto cleanup; | ||
| 7005 | } | ||
| 7006 | } | ||
| 7007 | |||
| 7008 | /* If this is a Redis Cluster node, we need to make sure the module is not | ||
| 7009 | * trying to access non-local keys, with the exception of commands | ||
| 7010 | * received from our master. */ | ||
| 7011 | if (server.cluster_enabled && !mustObeyClient(ctx->client)) { | ||
| 7012 | int error_code; | ||
| 7013 | /* Duplicate relevant flags in the module client. */ | ||
| 7014 | c->flags &= ~(CLIENT_READONLY|CLIENT_ASKING); | ||
| 7015 | c->flags |= ctx->client->flags & (CLIENT_READONLY|CLIENT_ASKING); | ||
| 7016 | const uint64_t cmd_flags = getCommandFlags(c); | ||
| 7017 | if (getNodeByQuery(c,c->cmd,c->argv,c->argc,NULL,NULL,0,cmd_flags,&error_code) != | ||
| 7018 | getMyClusterNode()) | ||
| 7019 | { | ||
| 7020 | sds msg = NULL; | ||
| 7021 | if (error_code == CLUSTER_REDIR_DOWN_RO_STATE) { | ||
| 7022 | if (error_as_call_replies) { | ||
| 7023 | msg = sdscatfmt(sdsempty(), "Can not execute a write command '%S' while the cluster is down and readonly", c->cmd->fullname); | ||
| 7024 | } | ||
| 7025 | errno = EROFS; | ||
| 7026 | } else if (error_code == CLUSTER_REDIR_DOWN_STATE) { | ||
| 7027 | if (error_as_call_replies) { | ||
| 7028 | msg = sdscatfmt(sdsempty(), "Can not execute a command '%S' while the cluster is down", c->cmd->fullname); | ||
| 7029 | } | ||
| 7030 | errno = ENETDOWN; | ||
| 7031 | } else { | ||
| 7032 | if (error_as_call_replies) { | ||
| 7033 | msg = sdsnew("Attempted to access a non local key in a cluster node"); | ||
| 7034 | } | ||
| 7035 | errno = EPERM; | ||
| 7036 | } | ||
| 7037 | if (msg) { | ||
| 7038 | reply = callReplyCreateError(msg, ctx); | ||
| 7039 | } | ||
| 7040 | goto cleanup; | ||
| 7041 | } | ||
| 7042 | } | ||
| 7043 | |||
| 7044 | if (flags & REDISMODULE_ARGV_DRY_RUN) { | ||
| 7045 | goto cleanup; | ||
| 7046 | } | ||
| 7047 | |||
| 7048 | /* We need to use a global replication_allowed flag in order to prevent | ||
| 7049 | * replication of nested RM_Calls. Example: | ||
| 7050 | * 1. module1.foo does RM_Call of module2.bar without replication (i.e. no '!') | ||
| 7051 | * 2. module2.bar internally calls RM_Call of INCR with '!' | ||
| 7052 | * 3. at the end of module1.foo we call RM_ReplicateVerbatim | ||
| 7053 | * We want the replica/AOF to see only module1.foo and not the INCR from module2.bar */ | ||
| 7054 | int prev_replication_allowed = server.replication_allowed; | ||
| 7055 | server.replication_allowed = replicate && server.replication_allowed; | ||
| 7056 | |||
| 7057 | /* Run the command */ | ||
| 7058 | int call_flags = CMD_CALL_FROM_MODULE; | ||
| 7059 | if (replicate) { | ||
| 7060 | if (!(flags & REDISMODULE_ARGV_NO_AOF)) | ||
| 7061 | call_flags |= CMD_CALL_PROPAGATE_AOF; | ||
| 7062 | if (!(flags & REDISMODULE_ARGV_NO_REPLICAS)) | ||
| 7063 | call_flags |= CMD_CALL_PROPAGATE_REPL; | ||
| 7064 | } | ||
| 7065 | call(c,call_flags); | ||
| 7066 | server.replication_allowed = prev_replication_allowed; | ||
| 7067 | |||
| 7068 | if (c->flags & CLIENT_BLOCKED) { | ||
| 7069 | serverAssert(flags & REDISMODULE_ARGV_ALLOW_BLOCK); | ||
| 7070 | serverAssert(ctx->module); | ||
| 7071 | RedisModuleAsyncRMCallPromise *promise = zmalloc(sizeof(RedisModuleAsyncRMCallPromise)); | ||
| 7072 | *promise = (RedisModuleAsyncRMCallPromise) { | ||
| 7073 | /* We start with ref_count value of 2 because this object is held | ||
| 7074 | * by the promise CallReply and the fake client that was used to execute the command. */ | ||
| 7075 | .ref_count = 2, | ||
| 7076 | .module = ctx->module, | ||
| 7077 | .on_unblocked = NULL, | ||
| 7078 | .private_data = NULL, | ||
| 7079 | .c = c, | ||
| 7080 | .ctx = (ctx->flags & REDISMODULE_CTX_AUTO_MEMORY) ? ctx : NULL, | ||
| 7081 | }; | ||
| 7082 | reply = callReplyCreatePromise(promise); | ||
| 7083 | c->bstate.async_rm_call_handle = promise; | ||
| 7084 | if (!(call_flags & CMD_CALL_PROPAGATE_AOF)) { | ||
| 7085 | /* No need for AOF propagation, set the relevant flags of the client */ | ||
| 7086 | c->flags |= CLIENT_MODULE_PREVENT_AOF_PROP; | ||
| 7087 | } | ||
| 7088 | if (!(call_flags & CMD_CALL_PROPAGATE_REPL)) { | ||
| 7089 | /* No need for replication propagation, set the relevant flags of the client */ | ||
| 7090 | c->flags |= CLIENT_MODULE_PREVENT_REPL_PROP; | ||
| 7091 | } | ||
| 7092 | c = NULL; /* Make sure not to free the client */ | ||
| 7093 | } else { | ||
| 7094 | reply = moduleParseReply(c, (ctx->flags & REDISMODULE_CTX_AUTO_MEMORY) ? ctx : NULL); | ||
| 7095 | } | ||
| 7096 | |||
| 7097 | cleanup: | ||
| 7098 | if (reply) autoMemoryAdd(ctx,REDISMODULE_AM_REPLY,reply); | ||
| 7099 | if (ctx->module) ctx->module->in_call--; | ||
| 7100 | if (c) moduleReleaseTempClient(c); | ||
| 7101 | return reply; | ||
| 7102 | } | ||
| 7103 | |||
| 7104 | /* Return a pointer, and a length, to the protocol returned by the command | ||
| 7105 | * that returned the reply object. */ | ||
| 7106 | const char *RM_CallReplyProto(RedisModuleCallReply *reply, size_t *len) { | ||
| 7107 | return callReplyGetProto(reply, len); | ||
| 7108 | } | ||
| 7109 | |||
| 7110 | /* -------------------------------------------------------------------------- | ||
| 7111 | * ## Modules data types | ||
| 7112 | * | ||
| 7113 | * When String DMA or using existing data structures is not enough, it is | ||
| 7114 | * possible to create new data types from scratch and export them to | ||
| 7115 | * Redis. The module must provide a set of callbacks for handling the | ||
| 7116 | * new values exported (for example in order to provide RDB saving/loading, | ||
| 7117 | * AOF rewrite, and so forth). In this section we define this API. | ||
| 7118 | * -------------------------------------------------------------------------- */ | ||
| 7119 | |||
| 7120 | /* Turn a 9 chars name in the specified charset and a 10 bit encver into | ||
| 7121 | * a single 64 bit unsigned integer that represents this exact module name | ||
| 7122 | * and version. This final number is called a "type ID" and is used when | ||
| 7123 | * writing module exported values to RDB files, in order to re-associate the | ||
| 7124 | * value to the right module to load them during RDB loading. | ||
| 7125 | * | ||
| 7126 | * If the string is not of the right length or the charset is wrong, or | ||
| 7127 | * if encver is outside the unsigned 10 bit integer range, 0 is returned, | ||
| 7128 | * otherwise the function returns the right type ID. | ||
| 7129 | * | ||
| 7130 | * The resulting 64 bit integer is composed as follows: | ||
| 7131 | * | ||
| 7132 | * (high order bits) 6|6|6|6|6|6|6|6|6|10 (low order bits) | ||
| 7133 | * | ||
| 7134 | * The first 6 bits value is the first character, name[0], while the last | ||
| 7135 | * 6 bits value, immediately before the 10 bits integer, is name[8]. | ||
| 7136 | * The last 10 bits are the encoding version. | ||
| 7137 | * | ||
| 7138 | * Note that a name and encver combo of "AAAAAAAAA" and 0, will produce | ||
| 7139 | * zero as return value, that is the same we use to signal errors, thus | ||
| 7140 | * this combination is invalid, and also useless since type names should | ||
| 7141 | * try to be vary to avoid collisions. */ | ||
| 7142 | |||
| 7143 | const char *ModuleTypeNameCharSet = | ||
| 7144 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||
| 7145 | "abcdefghijklmnopqrstuvwxyz" | ||
| 7146 | "0123456789-_"; | ||
| 7147 | |||
| 7148 | uint64_t moduleTypeEncodeId(const char *name, int encver) { | ||
| 7149 | /* We use 64 symbols so that we can map each character into 6 bits | ||
| 7150 | * of the final output. */ | ||
| 7151 | const char *cset = ModuleTypeNameCharSet; | ||
| 7152 | if (strlen(name) != 9) return 0; | ||
| 7153 | if (encver < 0 || encver > 1023) return 0; | ||
| 7154 | |||
| 7155 | uint64_t id = 0; | ||
| 7156 | for (int j = 0; j < 9; j++) { | ||
| 7157 | char *p = strchr(cset,name[j]); | ||
| 7158 | if (!p) return 0; | ||
| 7159 | unsigned long pos = p-cset; | ||
| 7160 | id = (id << 6) | pos; | ||
| 7161 | } | ||
| 7162 | id = (id << 10) | encver; | ||
| 7163 | return id; | ||
| 7164 | } | ||
| 7165 | |||
| 7166 | /* Search, in the list of exported data types of all the modules registered, | ||
| 7167 | * a type with the same name as the one given. Returns the moduleType | ||
| 7168 | * structure pointer if such a module is found, or NULL otherwise. */ | ||
| 7169 | moduleType *moduleTypeLookupModuleByNameInternal(const char *name, int ignore_case) { | ||
| 7170 | dictIterator di; | ||
| 7171 | dictEntry *de; | ||
| 7172 | |||
| 7173 | dictInitIterator(&di, modules); | ||
| 7174 | while ((de = dictNext(&di)) != NULL) { | ||
| 7175 | struct RedisModule *module = dictGetVal(de); | ||
| 7176 | listIter li; | ||
| 7177 | listNode *ln; | ||
| 7178 | |||
| 7179 | listRewind(module->types,&li); | ||
| 7180 | while((ln = listNext(&li))) { | ||
| 7181 | moduleType *mt = ln->value; | ||
| 7182 | if ((!ignore_case && memcmp(name,mt->entity.name,sizeof(mt->entity.name)) == 0) | ||
| 7183 | || (ignore_case && !strcasecmp(name, mt->entity.name))) | ||
| 7184 | { | ||
| 7185 | dictResetIterator(&di); | ||
| 7186 | return mt; | ||
| 7187 | } | ||
| 7188 | } | ||
| 7189 | } | ||
| 7190 | dictResetIterator(&di); | ||
| 7191 | return NULL; | ||
| 7192 | } | ||
| 7193 | /* Search all registered modules by name, and name is case sensitive */ | ||
| 7194 | moduleType *moduleTypeLookupModuleByName(const char *name) { | ||
| 7195 | return moduleTypeLookupModuleByNameInternal(name, 0); | ||
| 7196 | } | ||
| 7197 | |||
| 7198 | /* Search all registered modules by name, but case insensitive */ | ||
| 7199 | moduleType *moduleTypeLookupModuleByNameIgnoreCase(const char *name) { | ||
| 7200 | return moduleTypeLookupModuleByNameInternal(name, 1); | ||
| 7201 | } | ||
| 7202 | |||
| 7203 | /* Lookup a module by ID, with caching. This function is used during RDB | ||
| 7204 | * loading. Modules exporting data types should never be able to unload, so | ||
| 7205 | * our cache does not need to expire. */ | ||
| 7206 | #define MODULE_LOOKUP_CACHE_SIZE 3 | ||
| 7207 | |||
| 7208 | moduleType *moduleTypeLookupModuleByID(uint64_t id) { | ||
| 7209 | static struct { | ||
| 7210 | uint64_t id; | ||
| 7211 | moduleType *mt; | ||
| 7212 | } cache[MODULE_LOOKUP_CACHE_SIZE]; | ||
| 7213 | |||
| 7214 | /* Search in cache to start. */ | ||
| 7215 | int j; | ||
| 7216 | for (j = 0; j < MODULE_LOOKUP_CACHE_SIZE && cache[j].mt != NULL; j++) | ||
| 7217 | if (cache[j].id == id) return cache[j].mt; | ||
| 7218 | |||
| 7219 | /* Slow module by module lookup. */ | ||
| 7220 | moduleType *mt = NULL; | ||
| 7221 | dictIterator di; | ||
| 7222 | dictEntry *de; | ||
| 7223 | |||
| 7224 | dictInitIterator(&di, modules); | ||
| 7225 | while ((de = dictNext(&di)) != NULL && mt == NULL) { | ||
| 7226 | struct RedisModule *module = dictGetVal(de); | ||
| 7227 | listIter li; | ||
| 7228 | listNode *ln; | ||
| 7229 | |||
| 7230 | listRewind(module->types,&li); | ||
| 7231 | while((ln = listNext(&li))) { | ||
| 7232 | moduleType *this_mt = ln->value; | ||
| 7233 | /* Compare only the 54 bit module identifier and not the | ||
| 7234 | * encoding version. */ | ||
| 7235 | if (this_mt->entity.id >> 10 == id >> 10) { | ||
| 7236 | mt = this_mt; | ||
| 7237 | break; | ||
| 7238 | } | ||
| 7239 | } | ||
| 7240 | } | ||
| 7241 | dictResetIterator(&di); | ||
| 7242 | |||
| 7243 | /* Add to cache if possible. */ | ||
| 7244 | if (mt && j < MODULE_LOOKUP_CACHE_SIZE) { | ||
| 7245 | cache[j].id = id; | ||
| 7246 | cache[j].mt = mt; | ||
| 7247 | } | ||
| 7248 | return mt; | ||
| 7249 | } | ||
| 7250 | |||
| 7251 | /* Turn an (unresolved) module ID into a type name, to show the user an | ||
| 7252 | * error when RDB files contain module data we can't load. | ||
| 7253 | * The buffer pointed by 'name' must be 10 bytes at least. The function will | ||
| 7254 | * fill it with a null terminated module name. */ | ||
| 7255 | void moduleTypeNameByID(char *name, uint64_t moduleid) { | ||
| 7256 | const char *cset = ModuleTypeNameCharSet; | ||
| 7257 | |||
| 7258 | name[9] = '\0'; | ||
| 7259 | char *p = name+8; | ||
| 7260 | moduleid >>= 10; | ||
| 7261 | for (int j = 0; j < 9; j++) { | ||
| 7262 | *p-- = cset[moduleid & 63]; | ||
| 7263 | moduleid >>= 6; | ||
| 7264 | } | ||
| 7265 | } | ||
| 7266 | |||
| 7267 | /* Return the name of the module that owns the specified moduleType. */ | ||
| 7268 | const char *moduleTypeModuleName(moduleType *mt) { | ||
| 7269 | if (!mt || !mt->entity.module) return NULL; | ||
| 7270 | return mt->entity.module->name; | ||
| 7271 | } | ||
| 7272 | |||
| 7273 | /* Return the module name from a module command */ | ||
| 7274 | const char *moduleNameFromCommand(struct redisCommand *cmd) { | ||
| 7275 | serverAssert(cmd->proc == RedisModuleCommandDispatcher); | ||
| 7276 | |||
| 7277 | RedisModuleCommand *cp = cmd->module_cmd; | ||
| 7278 | return cp->module->name; | ||
| 7279 | } | ||
| 7280 | |||
| 7281 | /* Create a copy of a module type value using the copy callback. If failed | ||
| 7282 | * or not supported, produce an error reply and return NULL. | ||
| 7283 | */ | ||
| 7284 | robj *moduleTypeDupOrReply(client *c, robj *fromkey, robj *tokey, int todb, robj *value) { | ||
| 7285 | moduleValue *mv = value->ptr; | ||
| 7286 | moduleType *mt = mv->type; | ||
| 7287 | if (!mt->copy && !mt->copy2) { | ||
| 7288 | addReplyError(c, "not supported for this module key"); | ||
| 7289 | return NULL; | ||
| 7290 | } | ||
| 7291 | void *newval = NULL; | ||
| 7292 | if (mt->copy2 != NULL) { | ||
| 7293 | RedisModuleKeyOptCtx ctx = {fromkey, tokey, c->db->id, todb}; | ||
| 7294 | newval = mt->copy2(&ctx, mv->value); | ||
| 7295 | } else { | ||
| 7296 | newval = mt->copy(fromkey, tokey, mv->value); | ||
| 7297 | } | ||
| 7298 | |||
| 7299 | if (!newval) { | ||
| 7300 | addReplyError(c, "module key failed to copy"); | ||
| 7301 | return NULL; | ||
| 7302 | } | ||
| 7303 | return createModuleObject(mt, newval); | ||
| 7304 | } | ||
| 7305 | |||
| 7306 | /* Register a new data type exported by the module. The parameters are the | ||
| 7307 | * following. Please for in depth documentation check the modules API | ||
| 7308 | * documentation, especially https://redis.io/docs/latest/develop/reference/modules/modules-native-types/. | ||
| 7309 | * | ||
| 7310 | * * **name**: A 9 characters data type name that MUST be unique in the Redis | ||
| 7311 | * Modules ecosystem. Be creative... and there will be no collisions. Use | ||
| 7312 | * the charset A-Z a-z 9-0, plus the two "-_" characters. A good | ||
| 7313 | * idea is to use, for example `<typename>-<vendor>`. For example | ||
| 7314 | * "tree-AntZ" may mean "Tree data structure by @antirez". To use both | ||
| 7315 | * lower case and upper case letters helps in order to prevent collisions. | ||
| 7316 | * * **encver**: Encoding version, which is, the version of the serialization | ||
| 7317 | * that a module used in order to persist data. As long as the "name" | ||
| 7318 | * matches, the RDB loading will be dispatched to the type callbacks | ||
| 7319 | * whatever 'encver' is used, however the module can understand if | ||
| 7320 | * the encoding it must load are of an older version of the module. | ||
| 7321 | * For example the module "tree-AntZ" initially used encver=0. Later | ||
| 7322 | * after an upgrade, it started to serialize data in a different format | ||
| 7323 | * and to register the type with encver=1. However this module may | ||
| 7324 | * still load old data produced by an older version if the rdb_load | ||
| 7325 | * callback is able to check the encver value and act accordingly. | ||
| 7326 | * The encver must be a positive value between 0 and 1023. | ||
| 7327 | * | ||
| 7328 | * * **typemethods_ptr** is a pointer to a RedisModuleTypeMethods structure | ||
| 7329 | * that should be populated with the methods callbacks and structure | ||
| 7330 | * version, like in the following example: | ||
| 7331 | * | ||
| 7332 | * RedisModuleTypeMethods tm = { | ||
| 7333 | * .version = REDISMODULE_TYPE_METHOD_VERSION, | ||
| 7334 | * .rdb_load = myType_RDBLoadCallBack, | ||
| 7335 | * .rdb_save = myType_RDBSaveCallBack, | ||
| 7336 | * .aof_rewrite = myType_AOFRewriteCallBack, | ||
| 7337 | * .free = myType_FreeCallBack, | ||
| 7338 | * | ||
| 7339 | * // Optional fields | ||
| 7340 | * .digest = myType_DigestCallBack, | ||
| 7341 | * .mem_usage = myType_MemUsageCallBack, | ||
| 7342 | * .aux_load = myType_AuxRDBLoadCallBack, | ||
| 7343 | * .aux_save = myType_AuxRDBSaveCallBack, | ||
| 7344 | * .free_effort = myType_FreeEffortCallBack, | ||
| 7345 | * .unlink = myType_UnlinkCallBack, | ||
| 7346 | * .copy = myType_CopyCallback, | ||
| 7347 | * .defrag = myType_DefragCallback | ||
| 7348 | * | ||
| 7349 | * // Enhanced optional fields | ||
| 7350 | * .mem_usage2 = myType_MemUsageCallBack2, | ||
| 7351 | * .free_effort2 = myType_FreeEffortCallBack2, | ||
| 7352 | * .unlink2 = myType_UnlinkCallBack2, | ||
| 7353 | * .copy2 = myType_CopyCallback2, | ||
| 7354 | * } | ||
| 7355 | * | ||
| 7356 | * * **rdb_load**: A callback function pointer that loads data from RDB files. | ||
| 7357 | * * **rdb_save**: A callback function pointer that saves data to RDB files. | ||
| 7358 | * * **aof_rewrite**: A callback function pointer that rewrites data as commands. | ||
| 7359 | * * **digest**: A callback function pointer that is used for `DEBUG DIGEST`. | ||
| 7360 | * * **free**: A callback function pointer that can free a type value. | ||
| 7361 | * * **aux_save**: A callback function pointer that saves out of keyspace data to RDB files. | ||
| 7362 | * 'when' argument is either REDISMODULE_AUX_BEFORE_RDB or REDISMODULE_AUX_AFTER_RDB. | ||
| 7363 | * * **aux_load**: A callback function pointer that loads out of keyspace data from RDB files. | ||
| 7364 | * Similar to aux_save, returns REDISMODULE_OK on success, and ERR otherwise. | ||
| 7365 | * * **free_effort**: A callback function pointer that used to determine whether the module's | ||
| 7366 | * memory needs to be lazy reclaimed. The module should return the complexity involved by | ||
| 7367 | * freeing the value. for example: how many pointers are gonna be freed. Note that if it | ||
| 7368 | * returns 0, we'll always do an async free. | ||
| 7369 | * * **unlink**: A callback function pointer that used to notifies the module that the key has | ||
| 7370 | * been removed from the DB by redis, and may soon be freed by a background thread. Note that | ||
| 7371 | * it won't be called on FLUSHALL/FLUSHDB (both sync and async), and the module can use the | ||
| 7372 | * RedisModuleEvent_FlushDB to hook into that. | ||
| 7373 | * * **copy**: A callback function pointer that is used to make a copy of the specified key. | ||
| 7374 | * The module is expected to perform a deep copy of the specified value and return it. | ||
| 7375 | * In addition, hints about the names of the source and destination keys is provided. | ||
| 7376 | * A NULL return value is considered an error and the copy operation fails. | ||
| 7377 | * Note: if the target key exists and is being overwritten, the copy callback will be | ||
| 7378 | * called first, followed by a free callback to the value that is being replaced. | ||
| 7379 | * | ||
| 7380 | * * **defrag**: A callback function pointer that is used to request the module to defrag | ||
| 7381 | * a key. The module should then iterate pointers and call the relevant RM_Defrag*() | ||
| 7382 | * functions to defragment pointers or complex types. The module should continue | ||
| 7383 | * iterating as long as RM_DefragShouldStop() returns a zero value, and return a | ||
| 7384 | * zero value if finished or non-zero value if more work is left to be done. If more work | ||
| 7385 | * needs to be done, RM_DefragCursorSet() and RM_DefragCursorGet() can be used to track | ||
| 7386 | * this work across different calls. | ||
| 7387 | * Normally, the defrag mechanism invokes the callback without a time limit, so | ||
| 7388 | * RM_DefragShouldStop() always returns zero. The "late defrag" mechanism which has | ||
| 7389 | * a time limit and provides cursor support is used only for keys that are determined | ||
| 7390 | * to have significant internal complexity. To determine this, the defrag mechanism | ||
| 7391 | * uses the free_effort callback and the 'active-defrag-max-scan-fields' config directive. | ||
| 7392 | * NOTE: The value is passed as a `void**` and the function is expected to update the | ||
| 7393 | * pointer if the top-level value pointer is defragmented and consequently changes. | ||
| 7394 | * | ||
| 7395 | * * **mem_usage2**: Similar to `mem_usage`, but provides the `RedisModuleKeyOptCtx` parameter | ||
| 7396 | * so that meta information such as key name and db id can be obtained, and | ||
| 7397 | * the `sample_size` for size estimation (see MEMORY USAGE command). | ||
| 7398 | * * **free_effort2**: Similar to `free_effort`, but provides the `RedisModuleKeyOptCtx` parameter | ||
| 7399 | * so that meta information such as key name and db id can be obtained. | ||
| 7400 | * * **unlink2**: Similar to `unlink`, but provides the `RedisModuleKeyOptCtx` parameter | ||
| 7401 | * so that meta information such as key name and db id can be obtained. | ||
| 7402 | * * **copy2**: Similar to `copy`, but provides the `RedisModuleKeyOptCtx` parameter | ||
| 7403 | * so that meta information such as key names and db ids can be obtained. | ||
| 7404 | * * **aux_save2**: Similar to `aux_save`, but with small semantic change, if the module | ||
| 7405 | * saves nothing on this callback then no data about this aux field will be written to the | ||
| 7406 | * RDB and it will be possible to load the RDB even if the module is not loaded. | ||
| 7407 | * | ||
| 7408 | * Note: the module name "AAAAAAAAA" is reserved and produces an error, it | ||
| 7409 | * happens to be pretty lame as well. | ||
| 7410 | * | ||
| 7411 | * If RedisModule_CreateDataType() is called outside of RedisModule_OnLoad() function, | ||
| 7412 | * there is already a module registering a type with the same name, | ||
| 7413 | * or if the module name or encver is invalid, NULL is returned. | ||
| 7414 | * Otherwise the new type is registered into Redis, and a reference of | ||
| 7415 | * type RedisModuleType is returned: the caller of the function should store | ||
| 7416 | * this reference into a global variable to make future use of it in the | ||
| 7417 | * modules type API, since a single module may register multiple types. | ||
| 7418 | * Example code fragment: | ||
| 7419 | * | ||
| 7420 | * static RedisModuleType *BalancedTreeType; | ||
| 7421 | * | ||
| 7422 | * int RedisModule_OnLoad(RedisModuleCtx *ctx) { | ||
| 7423 | * // some code here ... | ||
| 7424 | * BalancedTreeType = RM_CreateDataType(...); | ||
| 7425 | * } | ||
| 7426 | */ | ||
| 7427 | moduleType *RM_CreateDataType(RedisModuleCtx *ctx, const char *name, int encver, void *typemethods_ptr) { | ||
| 7428 | if (!ctx->module->onload) | ||
| 7429 | return NULL; | ||
| 7430 | uint64_t id = moduleTypeEncodeId(name,encver); | ||
| 7431 | if (id == 0) return NULL; | ||
| 7432 | if (moduleTypeLookupModuleByName(name) != NULL) return NULL; | ||
| 7433 | |||
| 7434 | long typemethods_version = ((long*)typemethods_ptr)[0]; | ||
| 7435 | if (typemethods_version == 0) return NULL; | ||
| 7436 | |||
| 7437 | struct typemethods { | ||
| 7438 | uint64_t version; | ||
| 7439 | moduleTypeLoadFunc rdb_load; | ||
| 7440 | moduleTypeSaveFunc rdb_save; | ||
| 7441 | moduleTypeRewriteFunc aof_rewrite; | ||
| 7442 | moduleTypeMemUsageFunc mem_usage; | ||
| 7443 | moduleTypeDigestFunc digest; | ||
| 7444 | moduleTypeFreeFunc free; | ||
| 7445 | struct { | ||
| 7446 | moduleTypeAuxLoadFunc aux_load; | ||
| 7447 | moduleTypeAuxSaveFunc aux_save; | ||
| 7448 | int aux_save_triggers; | ||
| 7449 | } v2; | ||
| 7450 | struct { | ||
| 7451 | moduleTypeFreeEffortFunc free_effort; | ||
| 7452 | moduleTypeUnlinkFunc unlink; | ||
| 7453 | moduleTypeCopyFunc copy; | ||
| 7454 | moduleTypeDefragFunc defrag; | ||
| 7455 | } v3; | ||
| 7456 | struct { | ||
| 7457 | moduleTypeMemUsageFunc2 mem_usage2; | ||
| 7458 | moduleTypeFreeEffortFunc2 free_effort2; | ||
| 7459 | moduleTypeUnlinkFunc2 unlink2; | ||
| 7460 | moduleTypeCopyFunc2 copy2; | ||
| 7461 | } v4; | ||
| 7462 | struct { | ||
| 7463 | moduleTypeAuxSaveFunc aux_save2; | ||
| 7464 | } v5; | ||
| 7465 | } *tms = (struct typemethods*) typemethods_ptr; | ||
| 7466 | |||
| 7467 | moduleType *mt = zcalloc(sizeof(*mt)); | ||
| 7468 | mt->entity.id = id; | ||
| 7469 | mt->entity.module = ctx->module; | ||
| 7470 | mt->rdb_load = tms->rdb_load; | ||
| 7471 | mt->rdb_save = tms->rdb_save; | ||
| 7472 | mt->aof_rewrite = tms->aof_rewrite; | ||
| 7473 | mt->mem_usage = tms->mem_usage; | ||
| 7474 | mt->digest = tms->digest; | ||
| 7475 | mt->free = tms->free; | ||
| 7476 | if (tms->version >= 2) { | ||
| 7477 | mt->aux_load = tms->v2.aux_load; | ||
| 7478 | mt->aux_save = tms->v2.aux_save; | ||
| 7479 | mt->aux_save_triggers = tms->v2.aux_save_triggers; | ||
| 7480 | } | ||
| 7481 | if (tms->version >= 3) { | ||
| 7482 | mt->free_effort = tms->v3.free_effort; | ||
| 7483 | mt->unlink = tms->v3.unlink; | ||
| 7484 | mt->copy = tms->v3.copy; | ||
| 7485 | mt->defrag = tms->v3.defrag; | ||
| 7486 | } | ||
| 7487 | if (tms->version >= 4) { | ||
| 7488 | mt->mem_usage2 = tms->v4.mem_usage2; | ||
| 7489 | mt->unlink2 = tms->v4.unlink2; | ||
| 7490 | mt->free_effort2 = tms->v4.free_effort2; | ||
| 7491 | mt->copy2 = tms->v4.copy2; | ||
| 7492 | } | ||
| 7493 | if (tms->version >= 5) { | ||
| 7494 | mt->aux_save2 = tms->v5.aux_save2; | ||
| 7495 | } | ||
| 7496 | memcpy(mt->entity.name,name,sizeof(mt->entity.name)); | ||
| 7497 | listAddNodeTail(ctx->module->types,mt); | ||
| 7498 | return mt; | ||
| 7499 | } | ||
| 7500 | |||
| 7501 | /* If the key is open for writing, set the specified module type object | ||
| 7502 | * as the value of the key, deleting the old value if any. | ||
| 7503 | * On success REDISMODULE_OK is returned. If the key is not open for | ||
| 7504 | * writing or there is an active iterator, REDISMODULE_ERR is returned. */ | ||
| 7505 | int RM_ModuleTypeSetValue(RedisModuleKey *key, moduleType *mt, void *value) { | ||
| 7506 | if (!(key->mode & REDISMODULE_WRITE) || key->iter) return REDISMODULE_ERR; | ||
| 7507 | RM_DeleteKey(key); | ||
| 7508 | robj *o = createModuleObject(mt,value); | ||
| 7509 | setKey(key->ctx->client,key->db,key->key, &o,SETKEY_NO_SIGNAL); | ||
| 7510 | key->kv = o; | ||
| 7511 | return REDISMODULE_OK; | ||
| 7512 | } | ||
| 7513 | |||
| 7514 | /* Assuming RedisModule_KeyType() returned REDISMODULE_KEYTYPE_MODULE on | ||
| 7515 | * the key, returns the module type pointer of the value stored at key. | ||
| 7516 | * | ||
| 7517 | * If the key is NULL, is not associated with a module type, or is empty, | ||
| 7518 | * then NULL is returned instead. */ | ||
| 7519 | moduleType *RM_ModuleTypeGetType(RedisModuleKey *key) { | ||
| 7520 | if (key == NULL || | ||
| 7521 | key->kv == NULL || | ||
| 7522 | RM_KeyType(key) != REDISMODULE_KEYTYPE_MODULE) return NULL; | ||
| 7523 | moduleValue *mv = key->kv->ptr; | ||
| 7524 | return mv->type; | ||
| 7525 | } | ||
| 7526 | |||
| 7527 | /* Assuming RedisModule_KeyType() returned REDISMODULE_KEYTYPE_MODULE on | ||
| 7528 | * the key, returns the module type low-level value stored at key, as | ||
| 7529 | * it was set by the user via RedisModule_ModuleTypeSetValue(). | ||
| 7530 | * | ||
| 7531 | * If the key is NULL, is not associated with a module type, or is empty, | ||
| 7532 | * then NULL is returned instead. */ | ||
| 7533 | void *RM_ModuleTypeGetValue(RedisModuleKey *key) { | ||
| 7534 | if (key == NULL || | ||
| 7535 | key->kv == NULL || | ||
| 7536 | RM_KeyType(key) != REDISMODULE_KEYTYPE_MODULE) return NULL; | ||
| 7537 | moduleValue *mv = key->kv->ptr; | ||
| 7538 | return mv->value; | ||
| 7539 | } | ||
| 7540 | |||
| 7541 | /* -------------------------------------------------------------------------- | ||
| 7542 | * ## RDB loading and saving functions | ||
| 7543 | * -------------------------------------------------------------------------- */ | ||
| 7544 | |||
| 7545 | /* Called when there is a load error in the context of a module. On some | ||
| 7546 | * modules this cannot be recovered, but if the module declared capability | ||
| 7547 | * to handle errors, we'll raise a flag rather than exiting. */ | ||
| 7548 | void moduleRDBLoadError(RedisModuleIO *io) { | ||
| 7549 | if (io->entity->module->options & REDISMODULE_OPTIONS_HANDLE_IO_ERRORS) { | ||
| 7550 | io->error = 1; | ||
| 7551 | return; | ||
| 7552 | } | ||
| 7553 | serverPanic( | ||
| 7554 | "Error loading data from RDB (short read or EOF). " | ||
| 7555 | "Read performed by module '%s' about type '%s' " | ||
| 7556 | "after reading '%llu' bytes of a value " | ||
| 7557 | "for key named: '%s'.", | ||
| 7558 | io->entity->module->name, | ||
| 7559 | io->entity->name, | ||
| 7560 | (unsigned long long)io->bytes, | ||
| 7561 | io->key? (char*)io->key->ptr: "(null)"); | ||
| 7562 | } | ||
| 7563 | |||
| 7564 | /* Returns 0 if there's at least one registered data type that did not declare | ||
| 7565 | * REDISMODULE_OPTIONS_HANDLE_IO_ERRORS, in which case diskless loading should | ||
| 7566 | * be avoided since it could cause data loss. */ | ||
| 7567 | int moduleAllDatatypesHandleErrors(void) { | ||
| 7568 | dictIterator di; | ||
| 7569 | dictEntry *de; | ||
| 7570 | |||
| 7571 | dictInitIterator(&di, modules); | ||
| 7572 | while ((de = dictNext(&di)) != NULL) { | ||
| 7573 | struct RedisModule *module = dictGetVal(de); | ||
| 7574 | if (listLength(module->types) && | ||
| 7575 | !(module->options & REDISMODULE_OPTIONS_HANDLE_IO_ERRORS)) | ||
| 7576 | { | ||
| 7577 | dictResetIterator(&di); | ||
| 7578 | return 0; | ||
| 7579 | } | ||
| 7580 | } | ||
| 7581 | dictResetIterator(&di); | ||
| 7582 | return 1; | ||
| 7583 | } | ||
| 7584 | |||
| 7585 | /* Returns 0 if module did not declare REDISMODULE_OPTIONS_HANDLE_REPL_ASYNC_LOAD, in which case | ||
| 7586 | * diskless async loading should be avoided because module doesn't know there can be traffic during | ||
| 7587 | * database full resynchronization. */ | ||
| 7588 | int moduleAllModulesHandleReplAsyncLoad(void) { | ||
| 7589 | dictIterator di; | ||
| 7590 | dictEntry *de; | ||
| 7591 | |||
| 7592 | dictInitIterator(&di, modules); | ||
| 7593 | while ((de = dictNext(&di)) != NULL) { | ||
| 7594 | struct RedisModule *module = dictGetVal(de); | ||
| 7595 | if (!(module->options & REDISMODULE_OPTIONS_HANDLE_REPL_ASYNC_LOAD)) { | ||
| 7596 | dictResetIterator(&di); | ||
| 7597 | return 0; | ||
| 7598 | } | ||
| 7599 | } | ||
| 7600 | dictResetIterator(&di); | ||
| 7601 | return 1; | ||
| 7602 | } | ||
| 7603 | |||
| 7604 | /* Returns true if any previous IO API failed. | ||
| 7605 | * for `Load*` APIs the REDISMODULE_OPTIONS_HANDLE_IO_ERRORS flag must be set with | ||
| 7606 | * RedisModule_SetModuleOptions first. */ | ||
| 7607 | int RM_IsIOError(RedisModuleIO *io) { | ||
| 7608 | return io->error; | ||
| 7609 | } | ||
| 7610 | |||
| 7611 | static int flushRedisModuleIOBuffer(RedisModuleIO *io) { | ||
| 7612 | if (!io->pre_flush_buffer) return 0; | ||
| 7613 | |||
| 7614 | /* We have data that must be flushed before saving the current data. | ||
| 7615 | * Lets flush it. */ | ||
| 7616 | sds pre_flush_buffer = io->pre_flush_buffer; | ||
| 7617 | io->pre_flush_buffer = NULL; | ||
| 7618 | ssize_t retval = rdbWriteRaw(io->rio, pre_flush_buffer, sdslen(pre_flush_buffer)); | ||
| 7619 | sdsfree(pre_flush_buffer); | ||
| 7620 | if (retval >= 0) io->bytes += retval; | ||
| 7621 | return retval; | ||
| 7622 | } | ||
| 7623 | |||
| 7624 | /* Save an unsigned 64 bit value into the RDB file. This function should only | ||
| 7625 | * be called in the context of the rdb_save method of modules implementing new | ||
| 7626 | * data types. */ | ||
| 7627 | void RM_SaveUnsigned(RedisModuleIO *io, uint64_t value) { | ||
| 7628 | if (io->error) return; | ||
| 7629 | if (flushRedisModuleIOBuffer(io) == -1) goto saveerr; | ||
| 7630 | /* Save opcode. */ | ||
| 7631 | int retval = rdbSaveLen(io->rio, RDB_MODULE_OPCODE_UINT); | ||
| 7632 | if (retval == -1) goto saveerr; | ||
| 7633 | io->bytes += retval; | ||
| 7634 | /* Save value. */ | ||
| 7635 | retval = rdbSaveLen(io->rio, value); | ||
| 7636 | if (retval == -1) goto saveerr; | ||
| 7637 | io->bytes += retval; | ||
| 7638 | return; | ||
| 7639 | |||
| 7640 | saveerr: | ||
| 7641 | io->error = 1; | ||
| 7642 | } | ||
| 7643 | |||
| 7644 | /* Load an unsigned 64 bit value from the RDB file. This function should only | ||
| 7645 | * be called in the context of the `rdb_load` method of modules implementing | ||
| 7646 | * new data types. */ | ||
| 7647 | uint64_t RM_LoadUnsigned(RedisModuleIO *io) { | ||
| 7648 | if (io->error) return 0; | ||
| 7649 | uint64_t opcode = rdbLoadLen(io->rio,NULL); | ||
| 7650 | if (opcode != RDB_MODULE_OPCODE_UINT) goto loaderr; | ||
| 7651 | uint64_t value; | ||
| 7652 | int retval = rdbLoadLenByRef(io->rio, NULL, &value); | ||
| 7653 | if (retval == -1) goto loaderr; | ||
| 7654 | return value; | ||
| 7655 | |||
| 7656 | loaderr: | ||
| 7657 | moduleRDBLoadError(io); | ||
| 7658 | return 0; | ||
| 7659 | } | ||
| 7660 | |||
| 7661 | /* Like RedisModule_SaveUnsigned() but for signed 64 bit values. */ | ||
| 7662 | void RM_SaveSigned(RedisModuleIO *io, int64_t value) { | ||
| 7663 | union {uint64_t u; int64_t i;} conv; | ||
| 7664 | conv.i = value; | ||
| 7665 | RM_SaveUnsigned(io,conv.u); | ||
| 7666 | } | ||
| 7667 | |||
| 7668 | /* Like RedisModule_LoadUnsigned() but for signed 64 bit values. */ | ||
| 7669 | int64_t RM_LoadSigned(RedisModuleIO *io) { | ||
| 7670 | union {uint64_t u; int64_t i;} conv; | ||
| 7671 | conv.u = RM_LoadUnsigned(io); | ||
| 7672 | return conv.i; | ||
| 7673 | } | ||
| 7674 | |||
| 7675 | /* In the context of the rdb_save method of a module type, saves a | ||
| 7676 | * string into the RDB file taking as input a RedisModuleString. | ||
| 7677 | * | ||
| 7678 | * The string can be later loaded with RedisModule_LoadString() or | ||
| 7679 | * other Load family functions expecting a serialized string inside | ||
| 7680 | * the RDB file. */ | ||
| 7681 | void RM_SaveString(RedisModuleIO *io, RedisModuleString *s) { | ||
| 7682 | if (io->error) return; | ||
| 7683 | if (flushRedisModuleIOBuffer(io) == -1) goto saveerr; | ||
| 7684 | /* Save opcode. */ | ||
| 7685 | ssize_t retval = rdbSaveLen(io->rio, RDB_MODULE_OPCODE_STRING); | ||
| 7686 | if (retval == -1) goto saveerr; | ||
| 7687 | io->bytes += retval; | ||
| 7688 | /* Save value. */ | ||
| 7689 | retval = rdbSaveStringObject(io->rio, s); | ||
| 7690 | if (retval == -1) goto saveerr; | ||
| 7691 | io->bytes += retval; | ||
| 7692 | return; | ||
| 7693 | |||
| 7694 | saveerr: | ||
| 7695 | io->error = 1; | ||
| 7696 | } | ||
| 7697 | |||
| 7698 | /* Like RedisModule_SaveString() but takes a raw C pointer and length | ||
| 7699 | * as input. */ | ||
| 7700 | void RM_SaveStringBuffer(RedisModuleIO *io, const char *str, size_t len) { | ||
| 7701 | if (io->error) return; | ||
| 7702 | if (flushRedisModuleIOBuffer(io) == -1) goto saveerr; | ||
| 7703 | /* Save opcode. */ | ||
| 7704 | ssize_t retval = rdbSaveLen(io->rio, RDB_MODULE_OPCODE_STRING); | ||
| 7705 | if (retval == -1) goto saveerr; | ||
| 7706 | io->bytes += retval; | ||
| 7707 | /* Save value. */ | ||
| 7708 | retval = rdbSaveRawString(io->rio, (unsigned char*)str,len); | ||
| 7709 | if (retval == -1) goto saveerr; | ||
| 7710 | io->bytes += retval; | ||
| 7711 | return; | ||
| 7712 | |||
| 7713 | saveerr: | ||
| 7714 | io->error = 1; | ||
| 7715 | } | ||
| 7716 | |||
| 7717 | /* Implements RM_LoadString() and RM_LoadStringBuffer() */ | ||
| 7718 | void *moduleLoadString(RedisModuleIO *io, int plain, size_t *lenptr) { | ||
| 7719 | if (io->error) return NULL; | ||
| 7720 | uint64_t opcode = rdbLoadLen(io->rio,NULL); | ||
| 7721 | if (opcode != RDB_MODULE_OPCODE_STRING) goto loaderr; | ||
| 7722 | void *s = rdbGenericLoadStringObject(io->rio, | ||
| 7723 | plain ? RDB_LOAD_PLAIN : RDB_LOAD_NONE, lenptr); | ||
| 7724 | if (s == NULL) goto loaderr; | ||
| 7725 | return s; | ||
| 7726 | |||
| 7727 | loaderr: | ||
| 7728 | moduleRDBLoadError(io); | ||
| 7729 | return NULL; | ||
| 7730 | } | ||
| 7731 | |||
| 7732 | /* In the context of the rdb_load method of a module data type, loads a string | ||
| 7733 | * from the RDB file, that was previously saved with RedisModule_SaveString() | ||
| 7734 | * functions family. | ||
| 7735 | * | ||
| 7736 | * The returned string is a newly allocated RedisModuleString object, and | ||
| 7737 | * the user should at some point free it with a call to RedisModule_FreeString(). | ||
| 7738 | * | ||
| 7739 | * If the data structure does not store strings as RedisModuleString objects, | ||
| 7740 | * the similar function RedisModule_LoadStringBuffer() could be used instead. */ | ||
| 7741 | RedisModuleString *RM_LoadString(RedisModuleIO *io) { | ||
| 7742 | return moduleLoadString(io,0,NULL); | ||
| 7743 | } | ||
| 7744 | |||
| 7745 | /* Like RedisModule_LoadString() but returns a heap allocated string that | ||
| 7746 | * was allocated with RedisModule_Alloc(), and can be resized or freed with | ||
| 7747 | * RedisModule_Realloc() or RedisModule_Free(). | ||
| 7748 | * | ||
| 7749 | * The size of the string is stored at '*lenptr' if not NULL. | ||
| 7750 | * The returned string is not automatically NULL terminated, it is loaded | ||
| 7751 | * exactly as it was stored inside the RDB file. */ | ||
| 7752 | char *RM_LoadStringBuffer(RedisModuleIO *io, size_t *lenptr) { | ||
| 7753 | return moduleLoadString(io,1,lenptr); | ||
| 7754 | } | ||
| 7755 | |||
| 7756 | /* In the context of the rdb_save method of a module data type, saves a double | ||
| 7757 | * value to the RDB file. The double can be a valid number, a NaN or infinity. | ||
| 7758 | * It is possible to load back the value with RedisModule_LoadDouble(). */ | ||
| 7759 | void RM_SaveDouble(RedisModuleIO *io, double value) { | ||
| 7760 | if (io->error) return; | ||
| 7761 | if (flushRedisModuleIOBuffer(io) == -1) goto saveerr; | ||
| 7762 | /* Save opcode. */ | ||
| 7763 | int retval = rdbSaveLen(io->rio, RDB_MODULE_OPCODE_DOUBLE); | ||
| 7764 | if (retval == -1) goto saveerr; | ||
| 7765 | io->bytes += retval; | ||
| 7766 | /* Save value. */ | ||
| 7767 | retval = rdbSaveBinaryDoubleValue(io->rio, value); | ||
| 7768 | if (retval == -1) goto saveerr; | ||
| 7769 | io->bytes += retval; | ||
| 7770 | return; | ||
| 7771 | |||
| 7772 | saveerr: | ||
| 7773 | io->error = 1; | ||
| 7774 | } | ||
| 7775 | |||
| 7776 | /* In the context of the rdb_save method of a module data type, loads back the | ||
| 7777 | * double value saved by RedisModule_SaveDouble(). */ | ||
| 7778 | double RM_LoadDouble(RedisModuleIO *io) { | ||
| 7779 | if (io->error) return 0; | ||
| 7780 | uint64_t opcode = rdbLoadLen(io->rio,NULL); | ||
| 7781 | if (opcode != RDB_MODULE_OPCODE_DOUBLE) goto loaderr; | ||
| 7782 | double value; | ||
| 7783 | int retval = rdbLoadBinaryDoubleValue(io->rio, &value); | ||
| 7784 | if (retval == -1) goto loaderr; | ||
| 7785 | return value; | ||
| 7786 | |||
| 7787 | loaderr: | ||
| 7788 | moduleRDBLoadError(io); | ||
| 7789 | return 0; | ||
| 7790 | } | ||
| 7791 | |||
| 7792 | /* In the context of the rdb_save method of a module data type, saves a float | ||
| 7793 | * value to the RDB file. The float can be a valid number, a NaN or infinity. | ||
| 7794 | * It is possible to load back the value with RedisModule_LoadFloat(). */ | ||
| 7795 | void RM_SaveFloat(RedisModuleIO *io, float value) { | ||
| 7796 | if (io->error) return; | ||
| 7797 | if (flushRedisModuleIOBuffer(io) == -1) goto saveerr; | ||
| 7798 | /* Save opcode. */ | ||
| 7799 | int retval = rdbSaveLen(io->rio, RDB_MODULE_OPCODE_FLOAT); | ||
| 7800 | if (retval == -1) goto saveerr; | ||
| 7801 | io->bytes += retval; | ||
| 7802 | /* Save value. */ | ||
| 7803 | retval = rdbSaveBinaryFloatValue(io->rio, value); | ||
| 7804 | if (retval == -1) goto saveerr; | ||
| 7805 | io->bytes += retval; | ||
| 7806 | return; | ||
| 7807 | |||
| 7808 | saveerr: | ||
| 7809 | io->error = 1; | ||
| 7810 | } | ||
| 7811 | |||
| 7812 | /* In the context of the rdb_save method of a module data type, loads back the | ||
| 7813 | * float value saved by RedisModule_SaveFloat(). */ | ||
| 7814 | float RM_LoadFloat(RedisModuleIO *io) { | ||
| 7815 | if (io->error) return 0; | ||
| 7816 | uint64_t opcode = rdbLoadLen(io->rio,NULL); | ||
| 7817 | if (opcode != RDB_MODULE_OPCODE_FLOAT) goto loaderr; | ||
| 7818 | float value; | ||
| 7819 | int retval = rdbLoadBinaryFloatValue(io->rio, &value); | ||
| 7820 | if (retval == -1) goto loaderr; | ||
| 7821 | return value; | ||
| 7822 | |||
| 7823 | loaderr: | ||
| 7824 | moduleRDBLoadError(io); | ||
| 7825 | return 0; | ||
| 7826 | } | ||
| 7827 | |||
| 7828 | /* In the context of the rdb_save method of a module data type, saves a long double | ||
| 7829 | * value to the RDB file. The double can be a valid number, a NaN or infinity. | ||
| 7830 | * It is possible to load back the value with RedisModule_LoadLongDouble(). */ | ||
| 7831 | void RM_SaveLongDouble(RedisModuleIO *io, long double value) { | ||
| 7832 | if (io->error) return; | ||
| 7833 | char buf[MAX_LONG_DOUBLE_CHARS]; | ||
| 7834 | /* Long double has different number of bits in different platforms, so we | ||
| 7835 | * save it as a string type. */ | ||
| 7836 | size_t len = ld2string(buf,sizeof(buf),value,LD_STR_HEX); | ||
| 7837 | RM_SaveStringBuffer(io,buf,len); | ||
| 7838 | } | ||
| 7839 | |||
| 7840 | /* In the context of the rdb_save method of a module data type, loads back the | ||
| 7841 | * long double value saved by RedisModule_SaveLongDouble(). */ | ||
| 7842 | long double RM_LoadLongDouble(RedisModuleIO *io) { | ||
| 7843 | if (io->error) return 0; | ||
| 7844 | long double value; | ||
| 7845 | size_t len; | ||
| 7846 | char* str = RM_LoadStringBuffer(io,&len); | ||
| 7847 | if (!str) return 0; | ||
| 7848 | string2ld(str,len,&value); | ||
| 7849 | RM_Free(str); | ||
| 7850 | return value; | ||
| 7851 | } | ||
| 7852 | |||
| 7853 | /* Iterate over modules, and trigger rdb aux saving for the ones modules types | ||
| 7854 | * who asked for it. */ | ||
| 7855 | ssize_t rdbSaveModulesAux(rio *rdb, int when) { | ||
| 7856 | size_t total_written = 0; | ||
| 7857 | dictIterator di; | ||
| 7858 | dictEntry *de; | ||
| 7859 | |||
| 7860 | dictInitIterator(&di, modules); | ||
| 7861 | while ((de = dictNext(&di)) != NULL) { | ||
| 7862 | struct RedisModule *module = dictGetVal(de); | ||
| 7863 | listIter li; | ||
| 7864 | listNode *ln; | ||
| 7865 | |||
| 7866 | listRewind(module->types,&li); | ||
| 7867 | while((ln = listNext(&li))) { | ||
| 7868 | moduleType *mt = ln->value; | ||
| 7869 | if ((!mt->aux_save && !mt->aux_save2) || !(mt->aux_save_triggers & when)) | ||
| 7870 | continue; | ||
| 7871 | ssize_t ret = rdbSaveSingleModuleAux(rdb, when, mt); | ||
| 7872 | if (ret==-1) { | ||
| 7873 | dictResetIterator(&di); | ||
| 7874 | return -1; | ||
| 7875 | } | ||
| 7876 | total_written += ret; | ||
| 7877 | } | ||
| 7878 | } | ||
| 7879 | |||
| 7880 | dictResetIterator(&di); | ||
| 7881 | return total_written; | ||
| 7882 | } | ||
| 7883 | |||
| 7884 | /* -------------------------------------------------------------------------- | ||
| 7885 | * ## Key digest API (DEBUG DIGEST interface for modules types) | ||
| 7886 | * -------------------------------------------------------------------------- */ | ||
| 7887 | |||
| 7888 | /* Add a new element to the digest. This function can be called multiple times | ||
| 7889 | * one element after the other, for all the elements that constitute a given | ||
| 7890 | * data structure. The function call must be followed by the call to | ||
| 7891 | * `RedisModule_DigestEndSequence` eventually, when all the elements that are | ||
| 7892 | * always in a given order are added. See the Redis Modules data types | ||
| 7893 | * documentation for more info. However this is a quick example that uses Redis | ||
| 7894 | * data types as an example. | ||
| 7895 | * | ||
| 7896 | * To add a sequence of unordered elements (for example in the case of a Redis | ||
| 7897 | * Set), the pattern to use is: | ||
| 7898 | * | ||
| 7899 | * foreach element { | ||
| 7900 | * AddElement(element); | ||
| 7901 | * EndSequence(); | ||
| 7902 | * } | ||
| 7903 | * | ||
| 7904 | * Because Sets are not ordered, so every element added has a position that | ||
| 7905 | * does not depend from the other. However if instead our elements are | ||
| 7906 | * ordered in pairs, like field-value pairs of a Hash, then one should | ||
| 7907 | * use: | ||
| 7908 | * | ||
| 7909 | * foreach key,value { | ||
| 7910 | * AddElement(key); | ||
| 7911 | * AddElement(value); | ||
| 7912 | * EndSequence(); | ||
| 7913 | * } | ||
| 7914 | * | ||
| 7915 | * Because the key and value will be always in the above order, while instead | ||
| 7916 | * the single key-value pairs, can appear in any position into a Redis hash. | ||
| 7917 | * | ||
| 7918 | * A list of ordered elements would be implemented with: | ||
| 7919 | * | ||
| 7920 | * foreach element { | ||
| 7921 | * AddElement(element); | ||
| 7922 | * } | ||
| 7923 | * EndSequence(); | ||
| 7924 | * | ||
| 7925 | */ | ||
| 7926 | void RM_DigestAddStringBuffer(RedisModuleDigest *md, const char *ele, size_t len) { | ||
| 7927 | mixDigest(md->o,ele,len); | ||
| 7928 | } | ||
| 7929 | |||
| 7930 | /* Like `RedisModule_DigestAddStringBuffer()` but takes a `long long` as input | ||
| 7931 | * that gets converted into a string before adding it to the digest. */ | ||
| 7932 | void RM_DigestAddLongLong(RedisModuleDigest *md, long long ll) { | ||
| 7933 | char buf[LONG_STR_SIZE]; | ||
| 7934 | size_t len = ll2string(buf,sizeof(buf),ll); | ||
| 7935 | mixDigest(md->o,buf,len); | ||
| 7936 | } | ||
| 7937 | |||
| 7938 | /* See the documentation for `RedisModule_DigestAddElement()`. */ | ||
| 7939 | void RM_DigestEndSequence(RedisModuleDigest *md) { | ||
| 7940 | xorDigest(md->x,md->o,sizeof(md->o)); | ||
| 7941 | memset(md->o,0,sizeof(md->o)); | ||
| 7942 | } | ||
| 7943 | |||
| 7944 | /* Decode a serialized representation of a module data type 'mt', in a specific encoding version 'encver' | ||
| 7945 | * from string 'str' and return a newly allocated value, or NULL if decoding failed. | ||
| 7946 | * | ||
| 7947 | * This call basically reuses the 'rdb_load' callback which module data types | ||
| 7948 | * implement in order to allow a module to arbitrarily serialize/de-serialize | ||
| 7949 | * keys, similar to how the Redis 'DUMP' and 'RESTORE' commands are implemented. | ||
| 7950 | * | ||
| 7951 | * Modules should generally use the REDISMODULE_OPTIONS_HANDLE_IO_ERRORS flag and | ||
| 7952 | * make sure the de-serialization code properly checks and handles IO errors | ||
| 7953 | * (freeing allocated buffers and returning a NULL). | ||
| 7954 | * | ||
| 7955 | * If this is NOT done, Redis will handle corrupted (or just truncated) serialized | ||
| 7956 | * data by producing an error message and terminating the process. | ||
| 7957 | */ | ||
| 7958 | void *RM_LoadDataTypeFromStringEncver(const RedisModuleString *str, const moduleType *mt, int encver) { | ||
| 7959 | rio payload; | ||
| 7960 | RedisModuleIO io; | ||
| 7961 | void *ret; | ||
| 7962 | |||
| 7963 | rioInitWithBuffer(&payload, str->ptr); | ||
| 7964 | moduleType *mt_non_const = (moduleType *)mt; /*cast const away*/ | ||
| 7965 | moduleInitIOContext(&io, &mt_non_const->entity, &payload, NULL, -1); | ||
| 7966 | |||
| 7967 | /* All RM_Save*() calls always write a version 2 compatible format, so we | ||
| 7968 | * need to make sure we read the same. | ||
| 7969 | */ | ||
| 7970 | ret = mt->rdb_load(&io,encver); | ||
| 7971 | if (io.ctx) { | ||
| 7972 | moduleFreeContext(io.ctx); | ||
| 7973 | zfree(io.ctx); | ||
| 7974 | } | ||
| 7975 | return ret; | ||
| 7976 | } | ||
| 7977 | |||
| 7978 | /* Similar to RM_LoadDataTypeFromStringEncver, original version of the API, kept | ||
| 7979 | * for backward compatibility. | ||
| 7980 | */ | ||
| 7981 | void *RM_LoadDataTypeFromString(const RedisModuleString *str, const moduleType *mt) { | ||
| 7982 | return RM_LoadDataTypeFromStringEncver(str, mt, 0); | ||
| 7983 | } | ||
| 7984 | |||
| 7985 | /* Encode a module data type 'mt' value 'data' into serialized form, and return it | ||
| 7986 | * as a newly allocated RedisModuleString. | ||
| 7987 | * | ||
| 7988 | * This call basically reuses the 'rdb_save' callback which module data types | ||
| 7989 | * implement in order to allow a module to arbitrarily serialize/de-serialize | ||
| 7990 | * keys, similar to how the Redis 'DUMP' and 'RESTORE' commands are implemented. | ||
| 7991 | */ | ||
| 7992 | RedisModuleString *RM_SaveDataTypeToString(RedisModuleCtx *ctx, void *data, const moduleType *mt) { | ||
| 7993 | rio payload; | ||
| 7994 | RedisModuleIO io; | ||
| 7995 | |||
| 7996 | rioInitWithBuffer(&payload,sdsempty()); | ||
| 7997 | moduleType *mt_non_const = (moduleType *)mt; /*cast const away*/ | ||
| 7998 | moduleInitIOContext(&io, &mt_non_const->entity, &payload, NULL, -1); | ||
| 7999 | mt->rdb_save(&io,data); | ||
| 8000 | if (io.ctx) { | ||
| 8001 | moduleFreeContext(io.ctx); | ||
| 8002 | zfree(io.ctx); | ||
| 8003 | } | ||
| 8004 | if (io.error) { | ||
| 8005 | return NULL; | ||
| 8006 | } else { | ||
| 8007 | robj *str = createObject(OBJ_STRING,payload.io.buffer.ptr); | ||
| 8008 | if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_STRING,str); | ||
| 8009 | return str; | ||
| 8010 | } | ||
| 8011 | } | ||
| 8012 | |||
| 8013 | /* Returns the name of the key currently being processed. */ | ||
| 8014 | const RedisModuleString *RM_GetKeyNameFromDigest(RedisModuleDigest *dig) { | ||
| 8015 | return dig->key; | ||
| 8016 | } | ||
| 8017 | |||
| 8018 | /* Returns the database id of the key currently being processed. */ | ||
| 8019 | int RM_GetDbIdFromDigest(RedisModuleDigest *dig) { | ||
| 8020 | return dig->dbid; | ||
| 8021 | } | ||
| 8022 | /* -------------------------------------------------------------------------- | ||
| 8023 | * ## AOF API for modules data types | ||
| 8024 | * -------------------------------------------------------------------------- */ | ||
| 8025 | |||
| 8026 | /* Emits a command into the AOF during the AOF rewriting process. This function | ||
| 8027 | * is only called in the context of the aof_rewrite method of data types exported | ||
| 8028 | * by a module. The command works exactly like RedisModule_Call() in the way | ||
| 8029 | * the parameters are passed, but it does not return anything as the error | ||
| 8030 | * handling is performed by Redis itself. */ | ||
| 8031 | void RM_EmitAOF(RedisModuleIO *io, const char *cmdname, const char *fmt, ...) { | ||
| 8032 | if (io->error) return; | ||
| 8033 | struct redisCommand *cmd; | ||
| 8034 | robj **argv = NULL; | ||
| 8035 | int argc = 0, flags = 0, j; | ||
| 8036 | va_list ap; | ||
| 8037 | |||
| 8038 | cmd = lookupCommandByCString((char*)cmdname); | ||
| 8039 | if (!cmd) { | ||
| 8040 | serverLog(LL_WARNING, | ||
| 8041 | "Fatal: AOF method for module data type '%s' tried to " | ||
| 8042 | "emit unknown command '%s'", | ||
| 8043 | io->entity->name, cmdname); | ||
| 8044 | io->error = 1; | ||
| 8045 | errno = EINVAL; | ||
| 8046 | return; | ||
| 8047 | } | ||
| 8048 | |||
| 8049 | /* Emit the arguments into the AOF in Redis protocol format. */ | ||
| 8050 | va_start(ap, fmt); | ||
| 8051 | argv = moduleCreateArgvFromUserFormat(cmdname,fmt,&argc,&flags,ap); | ||
| 8052 | va_end(ap); | ||
| 8053 | if (argv == NULL) { | ||
| 8054 | serverLog(LL_WARNING, | ||
| 8055 | "Fatal: AOF method for module data type '%s' tried to " | ||
| 8056 | "call RedisModule_EmitAOF() with wrong format specifiers '%s'", | ||
| 8057 | io->entity->name, fmt); | ||
| 8058 | io->error = 1; | ||
| 8059 | errno = EINVAL; | ||
| 8060 | return; | ||
| 8061 | } | ||
| 8062 | |||
| 8063 | /* Bulk count. */ | ||
| 8064 | if (!io->error && rioWriteBulkCount(io->rio,'*',argc) == 0) | ||
| 8065 | io->error = 1; | ||
| 8066 | |||
| 8067 | /* Arguments. */ | ||
| 8068 | for (j = 0; j < argc; j++) { | ||
| 8069 | if (!io->error && rioWriteBulkObject(io->rio,argv[j]) == 0) | ||
| 8070 | io->error = 1; | ||
| 8071 | decrRefCount(argv[j]); | ||
| 8072 | } | ||
| 8073 | zfree(argv); | ||
| 8074 | return; | ||
| 8075 | } | ||
| 8076 | |||
| 8077 | /* -------------------------------------------------------------------------- | ||
| 8078 | * ## IO context handling | ||
| 8079 | * -------------------------------------------------------------------------- */ | ||
| 8080 | |||
| 8081 | RedisModuleCtx *RM_GetContextFromIO(RedisModuleIO *io) { | ||
| 8082 | if (io->ctx) return io->ctx; /* Can't have more than one... */ | ||
| 8083 | io->ctx = zmalloc(sizeof(RedisModuleCtx)); | ||
| 8084 | moduleCreateContext(io->ctx, io->entity->module, REDISMODULE_CTX_NONE); | ||
| 8085 | return io->ctx; | ||
| 8086 | } | ||
| 8087 | |||
| 8088 | /* Returns the name of the key currently being processed. | ||
| 8089 | * There is no guarantee that the key name is always available, so this may return NULL. | ||
| 8090 | */ | ||
| 8091 | const RedisModuleString *RM_GetKeyNameFromIO(RedisModuleIO *io) { | ||
| 8092 | return io->key; | ||
| 8093 | } | ||
| 8094 | |||
| 8095 | /* Returns a RedisModuleString with the name of the key from RedisModuleKey. */ | ||
| 8096 | const RedisModuleString *RM_GetKeyNameFromModuleKey(RedisModuleKey *key) { | ||
| 8097 | return key ? key->key : NULL; | ||
| 8098 | } | ||
| 8099 | |||
| 8100 | /* Returns a database id of the key from RedisModuleKey. */ | ||
| 8101 | int RM_GetDbIdFromModuleKey(RedisModuleKey *key) { | ||
| 8102 | return key ? key->db->id : -1; | ||
| 8103 | } | ||
| 8104 | |||
| 8105 | /* Returns the database id of the key currently being processed. | ||
| 8106 | * There is no guarantee that this info is always available, so this may return -1. | ||
| 8107 | */ | ||
| 8108 | int RM_GetDbIdFromIO(RedisModuleIO *io) { | ||
| 8109 | return io->dbid; | ||
| 8110 | } | ||
| 8111 | |||
| 8112 | /* -------------------------------------------------------------------------- | ||
| 8113 | * ## Logging | ||
| 8114 | * -------------------------------------------------------------------------- */ | ||
| 8115 | |||
| 8116 | /* This is the low level function implementing both: | ||
| 8117 | * | ||
| 8118 | * RM_Log() | ||
| 8119 | * RM_LogIOError() | ||
| 8120 | * | ||
| 8121 | */ | ||
| 8122 | void moduleLogRaw(RedisModule *module, const char *levelstr, const char *fmt, va_list ap) { | ||
| 8123 | char msg[LOG_MAX_LEN]; | ||
| 8124 | size_t name_len; | ||
| 8125 | int level; | ||
| 8126 | |||
| 8127 | if (!strcasecmp(levelstr,"debug")) level = LL_DEBUG; | ||
| 8128 | else if (!strcasecmp(levelstr,"verbose")) level = LL_VERBOSE; | ||
| 8129 | else if (!strcasecmp(levelstr,"notice")) level = LL_NOTICE; | ||
| 8130 | else if (!strcasecmp(levelstr,"warning")) level = LL_WARNING; | ||
| 8131 | else level = LL_VERBOSE; /* Default. */ | ||
| 8132 | |||
| 8133 | if (level < server.verbosity) return; | ||
| 8134 | |||
| 8135 | name_len = snprintf(msg, sizeof(msg),"<%s> ", module? module->name: "module"); | ||
| 8136 | vsnprintf(msg + name_len, sizeof(msg) - name_len, fmt, ap); | ||
| 8137 | serverLogRaw(level,msg); | ||
| 8138 | } | ||
| 8139 | |||
| 8140 | /* Produces a log message to the standard Redis log, the format accepts | ||
| 8141 | * printf-alike specifiers, while level is a string describing the log | ||
| 8142 | * level to use when emitting the log, and must be one of the following: | ||
| 8143 | * | ||
| 8144 | * * "debug" (`REDISMODULE_LOGLEVEL_DEBUG`) | ||
| 8145 | * * "verbose" (`REDISMODULE_LOGLEVEL_VERBOSE`) | ||
| 8146 | * * "notice" (`REDISMODULE_LOGLEVEL_NOTICE`) | ||
| 8147 | * * "warning" (`REDISMODULE_LOGLEVEL_WARNING`) | ||
| 8148 | * | ||
| 8149 | * If the specified log level is invalid, verbose is used by default. | ||
| 8150 | * There is a fixed limit to the length of the log line this function is able | ||
| 8151 | * to emit, this limit is not specified but is guaranteed to be more than | ||
| 8152 | * a few lines of text. | ||
| 8153 | * | ||
| 8154 | * The ctx argument may be NULL if cannot be provided in the context of the | ||
| 8155 | * caller for instance threads or callbacks, in which case a generic "module" | ||
| 8156 | * will be used instead of the module name. | ||
| 8157 | */ | ||
| 8158 | void RM_Log(RedisModuleCtx *ctx, const char *levelstr, const char *fmt, ...) { | ||
| 8159 | va_list ap; | ||
| 8160 | va_start(ap, fmt); | ||
| 8161 | moduleLogRaw(ctx? ctx->module: NULL,levelstr,fmt,ap); | ||
| 8162 | va_end(ap); | ||
| 8163 | } | ||
| 8164 | |||
| 8165 | /* Log errors from RDB / AOF serialization callbacks. | ||
| 8166 | * | ||
| 8167 | * This function should be used when a callback is returning a critical | ||
| 8168 | * error to the caller since cannot load or save the data for some | ||
| 8169 | * critical reason. */ | ||
| 8170 | void RM_LogIOError(RedisModuleIO *io, const char *levelstr, const char *fmt, ...) { | ||
| 8171 | va_list ap; | ||
| 8172 | va_start(ap, fmt); | ||
| 8173 | moduleLogRaw(io->entity->module, levelstr, fmt, ap); | ||
| 8174 | va_end(ap); | ||
| 8175 | } | ||
| 8176 | |||
| 8177 | /* Redis-like assert function. | ||
| 8178 | * | ||
| 8179 | * The macro `RedisModule_Assert(expression)` is recommended, rather than | ||
| 8180 | * calling this function directly. | ||
| 8181 | * | ||
| 8182 | * A failed assertion will shut down the server and produce logging information | ||
| 8183 | * that looks identical to information generated by Redis itself. | ||
| 8184 | */ | ||
| 8185 | void RM__Assert(const char *estr, const char *file, int line) { | ||
| 8186 | _serverAssert(estr, file, line); | ||
| 8187 | } | ||
| 8188 | |||
| 8189 | /* Allows adding event to the latency monitor to be observed by the LATENCY | ||
| 8190 | * command. The call is skipped if the latency is smaller than the configured | ||
| 8191 | * latency-monitor-threshold. */ | ||
| 8192 | void RM_LatencyAddSample(const char *event, mstime_t latency) { | ||
| 8193 | if (latency >= server.latency_monitor_threshold) | ||
| 8194 | latencyAddSample(event, latency); | ||
| 8195 | } | ||
| 8196 | |||
| 8197 | /* -------------------------------------------------------------------------- | ||
| 8198 | * ## Blocking clients from modules | ||
| 8199 | * | ||
| 8200 | * For a guide about blocking commands in modules, see | ||
| 8201 | * https://redis.io/docs/latest/develop/reference/modules/modules-blocking-ops/. | ||
| 8202 | * -------------------------------------------------------------------------- */ | ||
| 8203 | |||
| 8204 | /* Returns 1 if the client already in the moduleUnblocked list, 0 otherwise. */ | ||
| 8205 | int isModuleClientUnblocked(client *c) { | ||
| 8206 | RedisModuleBlockedClient *bc = c->bstate.module_blocked_handle; | ||
| 8207 | |||
| 8208 | return bc->unblocked == 1; | ||
| 8209 | } | ||
| 8210 | |||
| 8211 | /* This is called from blocked.c in order to unblock a client: may be called | ||
| 8212 | * for multiple reasons while the client is in the middle of being blocked | ||
| 8213 | * because the client is terminated, but is also called for cleanup when a | ||
| 8214 | * client is unblocked in a clean way after replaying. | ||
| 8215 | * | ||
| 8216 | * What we do here is just to set the client to NULL in the redis module | ||
| 8217 | * blocked client handle. This way if the client is terminated while there | ||
| 8218 | * is a pending threaded operation involving the blocked client, we'll know | ||
| 8219 | * that the client no longer exists and no reply callback should be called. | ||
| 8220 | * | ||
| 8221 | * The structure RedisModuleBlockedClient will be always deallocated when | ||
| 8222 | * running the list of clients blocked by a module that need to be unblocked. */ | ||
| 8223 | void unblockClientFromModule(client *c) { | ||
| 8224 | RedisModuleBlockedClient *bc = c->bstate.module_blocked_handle; | ||
| 8225 | |||
| 8226 | /* Call the disconnection callback if any. Note that | ||
| 8227 | * bc->disconnect_callback is set to NULL if the client gets disconnected | ||
| 8228 | * by the module itself or because of a timeout, so the callback will NOT | ||
| 8229 | * get called if this is not an actual disconnection event. */ | ||
| 8230 | if (bc->disconnect_callback) { | ||
| 8231 | RedisModuleCtx ctx; | ||
| 8232 | moduleCreateContext(&ctx, bc->module, REDISMODULE_CTX_NONE); | ||
| 8233 | ctx.blocked_privdata = bc->privdata; | ||
| 8234 | ctx.client = bc->client; | ||
| 8235 | bc->disconnect_callback(&ctx,bc); | ||
| 8236 | moduleFreeContext(&ctx); | ||
| 8237 | } | ||
| 8238 | |||
| 8239 | /* If we made it here and client is still blocked it means that the command | ||
| 8240 | * timed-out, client was killed or disconnected and disconnect_callback was | ||
| 8241 | * not implemented (or it was, but RM_UnblockClient was not called from | ||
| 8242 | * within it, as it should). | ||
| 8243 | * We must call moduleUnblockClient in order to free privdata and | ||
| 8244 | * RedisModuleBlockedClient. | ||
| 8245 | * | ||
| 8246 | * Note that we only do that for clients that are blocked on keys, for which | ||
| 8247 | * the contract is that the module should not call RM_UnblockClient under | ||
| 8248 | * normal circumstances. | ||
| 8249 | * Clients implementing threads and working with private data should be | ||
| 8250 | * aware that calling RM_UnblockClient for every blocked client is their | ||
| 8251 | * responsibility, and if they fail to do so memory may leak. Ideally they | ||
| 8252 | * should implement the disconnect and timeout callbacks and call | ||
| 8253 | * RM_UnblockClient, but any other way is also acceptable. */ | ||
| 8254 | if (bc->blocked_on_keys && !bc->unblocked) | ||
| 8255 | moduleUnblockClient(c); | ||
| 8256 | |||
| 8257 | bc->client = NULL; | ||
| 8258 | } | ||
| 8259 | |||
| 8260 | /* Block a client in the context of a module: this function implements both | ||
| 8261 | * RM_BlockClient() and RM_BlockClientOnKeys() depending on the fact the | ||
| 8262 | * keys are passed or not. | ||
| 8263 | * | ||
| 8264 | * When not blocking for keys, the keys, numkeys, and privdata parameters are | ||
| 8265 | * not needed. The privdata in that case must be NULL, since later is | ||
| 8266 | * RM_UnblockClient() that will provide some private data that the reply | ||
| 8267 | * callback will receive. | ||
| 8268 | * | ||
| 8269 | * Instead when blocking for keys, normally RM_UnblockClient() will not be | ||
| 8270 | * called (because the client will unblock when the key is modified), so | ||
| 8271 | * 'privdata' should be provided in that case, so that once the client is | ||
| 8272 | * unlocked and the reply callback is called, it will receive its associated | ||
| 8273 | * private data. | ||
| 8274 | * | ||
| 8275 | * Even when blocking on keys, RM_UnblockClient() can be called however, but | ||
| 8276 | * in that case the privdata argument is disregarded, because we pass the | ||
| 8277 | * reply callback the privdata that is set here while blocking. | ||
| 8278 | * | ||
| 8279 | */ | ||
| 8280 | RedisModuleBlockedClient *moduleBlockClient(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, | ||
| 8281 | RedisModuleAuthCallback auth_reply_callback, | ||
| 8282 | RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), | ||
| 8283 | long long timeout_ms, RedisModuleString **keys, int numkeys, void *privdata, | ||
| 8284 | int flags) { | ||
| 8285 | client *c = ctx->client; | ||
| 8286 | int islua = scriptIsRunning(); | ||
| 8287 | int ismulti = server.in_exec; | ||
| 8288 | |||
| 8289 | c->bstate.module_blocked_handle = zcalloc(sizeof(RedisModuleBlockedClient)); | ||
| 8290 | RedisModuleBlockedClient *bc = c->bstate.module_blocked_handle; | ||
| 8291 | ctx->module->blocked_clients++; | ||
| 8292 | |||
| 8293 | /* We need to handle the invalid operation of calling modules blocking | ||
| 8294 | * commands from Lua or MULTI. We actually create an already aborted | ||
| 8295 | * (client set to NULL) blocked client handle, and actually reply with | ||
| 8296 | * an error. */ | ||
| 8297 | bc->client = (islua || ismulti) ? NULL : c; | ||
| 8298 | bc->module = ctx->module; | ||
| 8299 | bc->reply_callback = reply_callback; | ||
| 8300 | bc->auth_reply_cb = auth_reply_callback; | ||
| 8301 | bc->timeout_callback = timeout_callback; | ||
| 8302 | bc->disconnect_callback = NULL; /* Set by RM_SetDisconnectCallback() */ | ||
| 8303 | bc->free_privdata = free_privdata; | ||
| 8304 | bc->privdata = privdata; | ||
| 8305 | bc->reply_client = moduleAllocTempClient(); | ||
| 8306 | bc->thread_safe_ctx_client = moduleAllocTempClient(); | ||
| 8307 | if (bc->client) | ||
| 8308 | bc->reply_client->resp = bc->client->resp; | ||
| 8309 | bc->dbid = c->db->id; | ||
| 8310 | bc->blocked_on_keys = keys != NULL; | ||
| 8311 | bc->unblocked = 0; | ||
| 8312 | bc->background_timer = 0; | ||
| 8313 | bc->background_duration = 0; | ||
| 8314 | |||
| 8315 | mstime_t timeout = 0; | ||
| 8316 | if (timeout_ms) { | ||
| 8317 | mstime_t now = mstime(); | ||
| 8318 | if (timeout_ms > LLONG_MAX - now) { | ||
| 8319 | c->bstate.module_blocked_handle = NULL; | ||
| 8320 | addReplyError(c, "timeout is out of range"); /* 'timeout_ms+now' would overflow */ | ||
| 8321 | return bc; | ||
| 8322 | } | ||
| 8323 | timeout = timeout_ms + now; | ||
| 8324 | } | ||
| 8325 | |||
| 8326 | if (islua || ismulti) { | ||
| 8327 | c->bstate.module_blocked_handle = NULL; | ||
| 8328 | addReplyError(c, islua ? | ||
| 8329 | "Blocking module command called from Lua script" : | ||
| 8330 | "Blocking module command called from transaction"); | ||
| 8331 | } else if (ctx->flags & REDISMODULE_CTX_BLOCKED_REPLY) { | ||
| 8332 | c->bstate.module_blocked_handle = NULL; | ||
| 8333 | addReplyError(c, "Blocking module command called from a Reply callback context"); | ||
| 8334 | } else if (!auth_reply_callback && clientHasModuleAuthInProgress(c)) { | ||
| 8335 | c->bstate.module_blocked_handle = NULL; | ||
| 8336 | addReplyError(c, "Clients undergoing module based authentication can only be blocked on auth"); | ||
| 8337 | } else { | ||
| 8338 | if (keys) { | ||
| 8339 | blockForKeys(c,BLOCKED_MODULE,keys,numkeys,timeout,flags&REDISMODULE_BLOCK_UNBLOCK_DELETED); | ||
| 8340 | } else { | ||
| 8341 | c->bstate.timeout = timeout; | ||
| 8342 | blockClient(c,BLOCKED_MODULE); | ||
| 8343 | } | ||
| 8344 | } | ||
| 8345 | return bc; | ||
| 8346 | } | ||
| 8347 | |||
| 8348 | /* This API registers a callback to execute in addition to normal password based authentication. | ||
| 8349 | * Multiple callbacks can be registered across different modules. When a Module is unloaded, all the | ||
| 8350 | * auth callbacks registered by it are unregistered. | ||
| 8351 | * The callbacks are attempted (in the order of most recently registered first) when the AUTH/HELLO | ||
| 8352 | * (with AUTH field provided) commands are called. | ||
| 8353 | * The callbacks will be called with a module context along with a username and a password, and are | ||
| 8354 | * expected to take one of the following actions: | ||
| 8355 | * (1) Authenticate - Use the RM_AuthenticateClient* API and return REDISMODULE_AUTH_HANDLED. | ||
| 8356 | * This will immediately end the auth chain as successful and add the OK reply. | ||
| 8357 | * (2) Deny Authentication - Return REDISMODULE_AUTH_HANDLED without authenticating or blocking the | ||
| 8358 | * client. Optionally, `err` can be set to a custom error message and `err` will be automatically | ||
| 8359 | * freed by the server. | ||
| 8360 | * This will immediately end the auth chain as unsuccessful and add the ERR reply. | ||
| 8361 | * (3) Block a client on authentication - Use the RM_BlockClientOnAuth API and return | ||
| 8362 | * REDISMODULE_AUTH_HANDLED. Here, the client will be blocked until the RM_UnblockClient API is used | ||
| 8363 | * which will trigger the auth reply callback (provided through the RM_BlockClientOnAuth). | ||
| 8364 | * In this reply callback, the Module should authenticate, deny or skip handling authentication. | ||
| 8365 | * (4) Skip handling Authentication - Return REDISMODULE_AUTH_NOT_HANDLED without blocking the | ||
| 8366 | * client. This will allow the engine to attempt the next module auth callback. | ||
| 8367 | * If none of the callbacks authenticate or deny auth, then password based auth is attempted and | ||
| 8368 | * will authenticate or add failure logs and reply to the clients accordingly. | ||
| 8369 | * | ||
| 8370 | * Note: If a client is disconnected while it was in the middle of blocking module auth, that | ||
| 8371 | * occurrence of the AUTH or HELLO command will not be tracked in the INFO command stats. | ||
| 8372 | * | ||
| 8373 | * The following is an example of how non-blocking module based authentication can be used: | ||
| 8374 | * | ||
| 8375 | * int auth_cb(RedisModuleCtx *ctx, RedisModuleString *username, RedisModuleString *password, RedisModuleString **err) { | ||
| 8376 | * const char *user = RedisModule_StringPtrLen(username, NULL); | ||
| 8377 | * const char *pwd = RedisModule_StringPtrLen(password, NULL); | ||
| 8378 | * if (!strcmp(user,"foo") && !strcmp(pwd,"valid_password")) { | ||
| 8379 | * RedisModule_AuthenticateClientWithACLUser(ctx, "foo", 3, NULL, NULL, NULL); | ||
| 8380 | * return REDISMODULE_AUTH_HANDLED; | ||
| 8381 | * } | ||
| 8382 | * | ||
| 8383 | * else if (!strcmp(user,"foo") && !strcmp(pwd,"wrong_password")) { | ||
| 8384 | * RedisModuleString *log = RedisModule_CreateString(ctx, "Module Auth", 11); | ||
| 8385 | * RedisModule_ACLAddLogEntryByUserName(ctx, username, log, REDISMODULE_ACL_LOG_AUTH); | ||
| 8386 | * RedisModule_FreeString(ctx, log); | ||
| 8387 | * const char *err_msg = "Auth denied by Misc Module."; | ||
| 8388 | * *err = RedisModule_CreateString(ctx, err_msg, strlen(err_msg)); | ||
| 8389 | * return REDISMODULE_AUTH_HANDLED; | ||
| 8390 | * } | ||
| 8391 | * return REDISMODULE_AUTH_NOT_HANDLED; | ||
| 8392 | * } | ||
| 8393 | * | ||
| 8394 | * int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { | ||
| 8395 | * if (RedisModule_Init(ctx,"authmodule",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR) | ||
| 8396 | * return REDISMODULE_ERR; | ||
| 8397 | * RedisModule_RegisterAuthCallback(ctx, auth_cb); | ||
| 8398 | * return REDISMODULE_OK; | ||
| 8399 | * } | ||
| 8400 | */ | ||
| 8401 | void RM_RegisterAuthCallback(RedisModuleCtx *ctx, RedisModuleAuthCallback cb) { | ||
| 8402 | RedisModuleAuthCtx *auth_ctx = zmalloc(sizeof(RedisModuleAuthCtx)); | ||
| 8403 | auth_ctx->module = ctx->module; | ||
| 8404 | auth_ctx->auth_cb = cb; | ||
| 8405 | listAddNodeHead(moduleAuthCallbacks, auth_ctx); | ||
| 8406 | } | ||
| 8407 | |||
| 8408 | /* Helper function to invoke the free private data callback of a Module blocked client. */ | ||
| 8409 | void moduleInvokeFreePrivDataCallback(client *c, RedisModuleBlockedClient *bc) { | ||
| 8410 | if (bc->privdata && bc->free_privdata) { | ||
| 8411 | RedisModuleCtx ctx; | ||
| 8412 | int ctx_flags = c == NULL ? REDISMODULE_CTX_BLOCKED_DISCONNECTED : REDISMODULE_CTX_NONE; | ||
| 8413 | moduleCreateContext(&ctx, bc->module, ctx_flags); | ||
| 8414 | ctx.blocked_privdata = bc->privdata; | ||
| 8415 | ctx.client = bc->client; | ||
| 8416 | bc->free_privdata(&ctx,bc->privdata); | ||
| 8417 | moduleFreeContext(&ctx); | ||
| 8418 | } | ||
| 8419 | } | ||
| 8420 | |||
| 8421 | /* Unregisters all the module auth callbacks that have been registered by this Module. */ | ||
| 8422 | void moduleUnregisterAuthCBs(RedisModule *module) { | ||
| 8423 | listIter li; | ||
| 8424 | listNode *ln; | ||
| 8425 | listRewind(moduleAuthCallbacks, &li); | ||
| 8426 | while ((ln = listNext(&li))) { | ||
| 8427 | RedisModuleAuthCtx *ctx = listNodeValue(ln); | ||
| 8428 | if (ctx->module == module) { | ||
| 8429 | listDelNode(moduleAuthCallbacks, ln); | ||
| 8430 | zfree(ctx); | ||
| 8431 | } | ||
| 8432 | } | ||
| 8433 | } | ||
| 8434 | |||
| 8435 | /* Search for & attempt next module auth callback after skipping the ones already attempted. | ||
| 8436 | * Returns the result of the module auth callback. */ | ||
| 8437 | int attemptNextAuthCb(client *c, robj *username, robj *password, robj **err) { | ||
| 8438 | int handle_next_callback = c->module_auth_ctx == NULL; | ||
| 8439 | RedisModuleAuthCtx *cur_auth_ctx = NULL; | ||
| 8440 | listNode *ln; | ||
| 8441 | listIter li; | ||
| 8442 | listRewind(moduleAuthCallbacks, &li); | ||
| 8443 | int result = REDISMODULE_AUTH_NOT_HANDLED; | ||
| 8444 | while((ln = listNext(&li))) { | ||
| 8445 | cur_auth_ctx = listNodeValue(ln); | ||
| 8446 | /* Skip over the previously attempted auth contexts. */ | ||
| 8447 | if (!handle_next_callback) { | ||
| 8448 | handle_next_callback = cur_auth_ctx == c->module_auth_ctx; | ||
| 8449 | continue; | ||
| 8450 | } | ||
| 8451 | /* Remove the module auth complete flag before we attempt the next cb. */ | ||
| 8452 | c->flags &= ~CLIENT_MODULE_AUTH_HAS_RESULT; | ||
| 8453 | RedisModuleCtx ctx; | ||
| 8454 | moduleCreateContext(&ctx, cur_auth_ctx->module, REDISMODULE_CTX_NONE); | ||
| 8455 | ctx.client = c; | ||
| 8456 | *err = NULL; | ||
| 8457 | c->module_auth_ctx = cur_auth_ctx; | ||
| 8458 | result = cur_auth_ctx->auth_cb(&ctx, username, password, err); | ||
| 8459 | moduleFreeContext(&ctx); | ||
| 8460 | if (result == REDISMODULE_AUTH_HANDLED) break; | ||
| 8461 | /* If Auth was not handled (allowed/denied/blocked) by the Module, try the next auth cb. */ | ||
| 8462 | } | ||
| 8463 | return result; | ||
| 8464 | } | ||
| 8465 | |||
| 8466 | /* Helper function to handle a reprocessed unblocked auth client. | ||
| 8467 | * Returns REDISMODULE_AUTH_NOT_HANDLED if the client was not reprocessed after a blocking module | ||
| 8468 | * auth operation. | ||
| 8469 | * Otherwise, we attempt the auth reply callback & the free priv data callback, update fields and | ||
| 8470 | * return the result of the reply callback. */ | ||
| 8471 | int attemptBlockedAuthReplyCallback(client *c, robj *username, robj *password, robj **err) { | ||
| 8472 | int result = REDISMODULE_AUTH_NOT_HANDLED; | ||
| 8473 | if (!c->module_blocked_client) return result; | ||
| 8474 | RedisModuleBlockedClient *bc = (RedisModuleBlockedClient *) c->module_blocked_client; | ||
| 8475 | bc->client = c; | ||
| 8476 | if (bc->auth_reply_cb) { | ||
| 8477 | RedisModuleCtx ctx; | ||
| 8478 | moduleCreateContext(&ctx, bc->module, REDISMODULE_CTX_BLOCKED_REPLY); | ||
| 8479 | ctx.blocked_privdata = bc->privdata; | ||
| 8480 | ctx.blocked_ready_key = NULL; | ||
| 8481 | ctx.client = bc->client; | ||
| 8482 | ctx.blocked_client = bc; | ||
| 8483 | result = bc->auth_reply_cb(&ctx, username, password, err); | ||
| 8484 | moduleFreeContext(&ctx); | ||
| 8485 | } | ||
| 8486 | moduleInvokeFreePrivDataCallback(c, bc); | ||
| 8487 | c->module_blocked_client = NULL; | ||
| 8488 | c->lastcmd->microseconds += bc->background_duration; | ||
| 8489 | bc->module->blocked_clients--; | ||
| 8490 | zfree(bc); | ||
| 8491 | return result; | ||
| 8492 | } | ||
| 8493 | |||
| 8494 | /* Helper function to attempt Module based authentication through module auth callbacks. | ||
| 8495 | * Here, the Module is expected to authenticate the client using the RedisModule APIs and to add ACL | ||
| 8496 | * logs in case of errors. | ||
| 8497 | * Returns one of the following codes: | ||
| 8498 | * AUTH_OK - Indicates that a module handled and authenticated the client. | ||
| 8499 | * AUTH_ERR - Indicates that a module handled and denied authentication for this client. | ||
| 8500 | * AUTH_NOT_HANDLED - Indicates that authentication was not handled by any Module and that | ||
| 8501 | * normal password based authentication can be attempted next. | ||
| 8502 | * AUTH_BLOCKED - Indicates module authentication is in progress through a blocking implementation. | ||
| 8503 | * In this case, authentication is handled here again after the client is unblocked / reprocessed. */ | ||
| 8504 | int checkModuleAuthentication(client *c, robj *username, robj *password, robj **err) { | ||
| 8505 | if (!listLength(moduleAuthCallbacks)) return AUTH_NOT_HANDLED; | ||
| 8506 | int result = attemptBlockedAuthReplyCallback(c, username, password, err); | ||
| 8507 | if (result == REDISMODULE_AUTH_NOT_HANDLED) { | ||
| 8508 | result = attemptNextAuthCb(c, username, password, err); | ||
| 8509 | } | ||
| 8510 | if (c->flags & CLIENT_BLOCKED) { | ||
| 8511 | /* Modules are expected to return REDISMODULE_AUTH_HANDLED when blocking clients. */ | ||
| 8512 | serverAssert(result == REDISMODULE_AUTH_HANDLED); | ||
| 8513 | return AUTH_BLOCKED; | ||
| 8514 | } | ||
| 8515 | c->module_auth_ctx = NULL; | ||
| 8516 | if (result == REDISMODULE_AUTH_NOT_HANDLED) { | ||
| 8517 | c->flags &= ~CLIENT_MODULE_AUTH_HAS_RESULT; | ||
| 8518 | return AUTH_NOT_HANDLED; | ||
| 8519 | } | ||
| 8520 | if (c->flags & CLIENT_MODULE_AUTH_HAS_RESULT) { | ||
| 8521 | c->flags &= ~CLIENT_MODULE_AUTH_HAS_RESULT; | ||
| 8522 | if (c->authenticated) return AUTH_OK; | ||
| 8523 | } | ||
| 8524 | return AUTH_ERR; | ||
| 8525 | } | ||
| 8526 | |||
| 8527 | /* This function is called from module.c in order to check if a module | ||
| 8528 | * blocked for BLOCKED_MODULE and subtype 'on keys' (bc->blocked_on_keys true) | ||
| 8529 | * can really be unblocked, since the module was able to serve the client. | ||
| 8530 | * If the callback returns REDISMODULE_OK, then the client can be unblocked, | ||
| 8531 | * otherwise the client remains blocked and we'll retry again when one of | ||
| 8532 | * the keys it blocked for becomes "ready" again. | ||
| 8533 | * This function returns 1 if client was served (and should be unblocked) */ | ||
| 8534 | int moduleTryServeClientBlockedOnKey(client *c, robj *key) { | ||
| 8535 | int served = 0; | ||
| 8536 | RedisModuleBlockedClient *bc = c->bstate.module_blocked_handle; | ||
| 8537 | |||
| 8538 | /* Protect against re-processing: don't serve clients that are already | ||
| 8539 | * in the unblocking list for any reason (including RM_UnblockClient() | ||
| 8540 | * explicit call). See #6798. */ | ||
| 8541 | if (bc->unblocked) return 0; | ||
| 8542 | |||
| 8543 | RedisModuleCtx ctx; | ||
| 8544 | moduleCreateContext(&ctx, bc->module, REDISMODULE_CTX_BLOCKED_REPLY); | ||
| 8545 | ctx.blocked_ready_key = key; | ||
| 8546 | ctx.blocked_privdata = bc->privdata; | ||
| 8547 | ctx.client = bc->client; | ||
| 8548 | ctx.blocked_client = bc; | ||
| 8549 | if (bc->reply_callback(&ctx,(void**)c->argv,c->argc) == REDISMODULE_OK) | ||
| 8550 | served = 1; | ||
| 8551 | moduleFreeContext(&ctx); | ||
| 8552 | return served; | ||
| 8553 | } | ||
| 8554 | |||
| 8555 | /* Block a client in the context of a blocking command, returning a handle | ||
| 8556 | * which will be used, later, in order to unblock the client with a call to | ||
| 8557 | * RedisModule_UnblockClient(). The arguments specify callback functions | ||
| 8558 | * and a timeout after which the client is unblocked. | ||
| 8559 | * | ||
| 8560 | * The callbacks are called in the following contexts: | ||
| 8561 | * | ||
| 8562 | * reply_callback: called after a successful RedisModule_UnblockClient() | ||
| 8563 | * call in order to reply to the client and unblock it. | ||
| 8564 | * | ||
| 8565 | * timeout_callback: called when the timeout is reached or if `CLIENT UNBLOCK` | ||
| 8566 | * is invoked, in order to send an error to the client. | ||
| 8567 | * | ||
| 8568 | * free_privdata: called in order to free the private data that is passed | ||
| 8569 | * by RedisModule_UnblockClient() call. | ||
| 8570 | * | ||
| 8571 | * Note: RedisModule_UnblockClient should be called for every blocked client, | ||
| 8572 | * even if client was killed, timed-out or disconnected. Failing to do so | ||
| 8573 | * will result in memory leaks. | ||
| 8574 | * | ||
| 8575 | * There are some cases where RedisModule_BlockClient() cannot be used: | ||
| 8576 | * | ||
| 8577 | * 1. If the client is a Lua script. | ||
| 8578 | * 2. If the client is executing a MULTI block. | ||
| 8579 | * | ||
| 8580 | * In these cases, a call to RedisModule_BlockClient() will **not** block the | ||
| 8581 | * client, but instead produce a specific error reply. | ||
| 8582 | * | ||
| 8583 | * A module that registers a timeout_callback function can also be unblocked | ||
| 8584 | * using the `CLIENT UNBLOCK` command, which will trigger the timeout callback. | ||
| 8585 | * If a callback function is not registered, then the blocked client will be | ||
| 8586 | * treated as if it is not in a blocked state and `CLIENT UNBLOCK` will return | ||
| 8587 | * a zero value. | ||
| 8588 | * | ||
| 8589 | * Measuring background time: By default the time spent in the blocked command | ||
| 8590 | * is not account for the total command duration. To include such time you should | ||
| 8591 | * use RM_BlockedClientMeasureTimeStart() and RM_BlockedClientMeasureTimeEnd() one, | ||
| 8592 | * or multiple times within the blocking command background work. | ||
| 8593 | */ | ||
| 8594 | RedisModuleBlockedClient *RM_BlockClient(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, | ||
| 8595 | RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), | ||
| 8596 | long long timeout_ms) { | ||
| 8597 | return moduleBlockClient(ctx,reply_callback,NULL,timeout_callback,free_privdata,timeout_ms, NULL,0,NULL,0); | ||
| 8598 | } | ||
| 8599 | |||
| 8600 | /* Block the current client for module authentication in the background. If module auth is not in | ||
| 8601 | * progress on the client, the API returns NULL. Otherwise, the client is blocked and the RM_BlockedClient | ||
| 8602 | * is returned similar to the RM_BlockClient API. | ||
| 8603 | * Note: Only use this API from the context of a module auth callback. */ | ||
| 8604 | RedisModuleBlockedClient *RM_BlockClientOnAuth(RedisModuleCtx *ctx, RedisModuleAuthCallback reply_callback, | ||
| 8605 | void (*free_privdata)(RedisModuleCtx*,void*)) { | ||
| 8606 | if (!clientHasModuleAuthInProgress(ctx->client)) { | ||
| 8607 | addReplyError(ctx->client, "Module blocking client on auth when not currently undergoing module authentication"); | ||
| 8608 | return NULL; | ||
| 8609 | } | ||
| 8610 | RedisModuleBlockedClient *bc = moduleBlockClient(ctx,NULL,reply_callback,NULL,free_privdata,0, NULL,0,NULL,0); | ||
| 8611 | if (ctx->client->flags & CLIENT_BLOCKED) { | ||
| 8612 | ctx->client->flags |= CLIENT_PENDING_COMMAND; | ||
| 8613 | } | ||
| 8614 | return bc; | ||
| 8615 | } | ||
| 8616 | |||
| 8617 | /* Get the private data that was previusely set on a blocked client */ | ||
| 8618 | void *RM_BlockClientGetPrivateData(RedisModuleBlockedClient *blocked_client) { | ||
| 8619 | return blocked_client->privdata; | ||
| 8620 | } | ||
| 8621 | |||
| 8622 | /* Set private data on a blocked client */ | ||
| 8623 | void RM_BlockClientSetPrivateData(RedisModuleBlockedClient *blocked_client, void *private_data) { | ||
| 8624 | blocked_client->privdata = private_data; | ||
| 8625 | } | ||
| 8626 | |||
| 8627 | /* This call is similar to RedisModule_BlockClient(), however in this case we | ||
| 8628 | * don't just block the client, but also ask Redis to unblock it automatically | ||
| 8629 | * once certain keys become "ready", that is, contain more data. | ||
| 8630 | * | ||
| 8631 | * Basically this is similar to what a typical Redis command usually does, | ||
| 8632 | * like BLPOP or BZPOPMAX: the client blocks if it cannot be served ASAP, | ||
| 8633 | * and later when the key receives new data (a list push for instance), the | ||
| 8634 | * client is unblocked and served. | ||
| 8635 | * | ||
| 8636 | * However in the case of this module API, when the client is unblocked? | ||
| 8637 | * | ||
| 8638 | * 1. If you block on a key of a type that has blocking operations associated, | ||
| 8639 | * like a list, a sorted set, a stream, and so forth, the client may be | ||
| 8640 | * unblocked once the relevant key is targeted by an operation that normally | ||
| 8641 | * unblocks the native blocking operations for that type. So if we block | ||
| 8642 | * on a list key, an RPUSH command may unblock our client and so forth. | ||
| 8643 | * 2. If you are implementing your native data type, or if you want to add new | ||
| 8644 | * unblocking conditions in addition to "1", you can call the modules API | ||
| 8645 | * RedisModule_SignalKeyAsReady(). | ||
| 8646 | * | ||
| 8647 | * Anyway we can't be sure if the client should be unblocked just because the | ||
| 8648 | * key is signaled as ready: for instance a successive operation may change the | ||
| 8649 | * key, or a client in queue before this one can be served, modifying the key | ||
| 8650 | * as well and making it empty again. So when a client is blocked with | ||
| 8651 | * RedisModule_BlockClientOnKeys() the reply callback is not called after | ||
| 8652 | * RM_UnblockClient() is called, but every time a key is signaled as ready: | ||
| 8653 | * if the reply callback can serve the client, it returns REDISMODULE_OK | ||
| 8654 | * and the client is unblocked, otherwise it will return REDISMODULE_ERR | ||
| 8655 | * and we'll try again later. | ||
| 8656 | * | ||
| 8657 | * The reply callback can access the key that was signaled as ready by | ||
| 8658 | * calling the API RedisModule_GetBlockedClientReadyKey(), that returns | ||
| 8659 | * just the string name of the key as a RedisModuleString object. | ||
| 8660 | * | ||
| 8661 | * Thanks to this system we can setup complex blocking scenarios, like | ||
| 8662 | * unblocking a client only if a list contains at least 5 items or other | ||
| 8663 | * more fancy logics. | ||
| 8664 | * | ||
| 8665 | * Note that another difference with RedisModule_BlockClient(), is that here | ||
| 8666 | * we pass the private data directly when blocking the client: it will | ||
| 8667 | * be accessible later in the reply callback. Normally when blocking with | ||
| 8668 | * RedisModule_BlockClient() the private data to reply to the client is | ||
| 8669 | * passed when calling RedisModule_UnblockClient() but here the unblocking | ||
| 8670 | * is performed by Redis itself, so we need to have some private data before | ||
| 8671 | * hand. The private data is used to store any information about the specific | ||
| 8672 | * unblocking operation that you are implementing. Such information will be | ||
| 8673 | * freed using the free_privdata callback provided by the user. | ||
| 8674 | * | ||
| 8675 | * However the reply callback will be able to access the argument vector of | ||
| 8676 | * the command, so the private data is often not needed. | ||
| 8677 | * | ||
| 8678 | * Note: Under normal circumstances RedisModule_UnblockClient should not be | ||
| 8679 | * called for clients that are blocked on keys (Either the key will | ||
| 8680 | * become ready or a timeout will occur). If for some reason you do want | ||
| 8681 | * to call RedisModule_UnblockClient it is possible: Client will be | ||
| 8682 | * handled as if it were timed-out (You must implement the timeout | ||
| 8683 | * callback in that case). | ||
| 8684 | */ | ||
| 8685 | RedisModuleBlockedClient *RM_BlockClientOnKeys(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, | ||
| 8686 | RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), | ||
| 8687 | long long timeout_ms, RedisModuleString **keys, int numkeys, void *privdata) { | ||
| 8688 | return moduleBlockClient(ctx,reply_callback,NULL,timeout_callback,free_privdata,timeout_ms, keys,numkeys,privdata,0); | ||
| 8689 | } | ||
| 8690 | |||
| 8691 | /* Same as RedisModule_BlockClientOnKeys, but can take REDISMODULE_BLOCK_* flags | ||
| 8692 | * Can be either REDISMODULE_BLOCK_UNBLOCK_DEFAULT, which means default behavior (same | ||
| 8693 | * as calling RedisModule_BlockClientOnKeys) | ||
| 8694 | * | ||
| 8695 | * The flags is a bit mask of these: | ||
| 8696 | * | ||
| 8697 | * - `REDISMODULE_BLOCK_UNBLOCK_DELETED`: The clients should to be awakened in case any of `keys` are deleted. | ||
| 8698 | * Mostly useful for commands that require the key to exist (like XREADGROUP) | ||
| 8699 | */ | ||
| 8700 | RedisModuleBlockedClient *RM_BlockClientOnKeysWithFlags(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, | ||
| 8701 | RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), | ||
| 8702 | long long timeout_ms, RedisModuleString **keys, int numkeys, void *privdata, | ||
| 8703 | int flags) { | ||
| 8704 | return moduleBlockClient(ctx,reply_callback,NULL,timeout_callback,free_privdata,timeout_ms, keys,numkeys,privdata,flags); | ||
| 8705 | } | ||
| 8706 | |||
| 8707 | /* This function is used in order to potentially unblock a client blocked | ||
| 8708 | * on keys with RedisModule_BlockClientOnKeys(). When this function is called, | ||
| 8709 | * all the clients blocked for this key will get their reply_callback called. */ | ||
| 8710 | void RM_SignalKeyAsReady(RedisModuleCtx *ctx, RedisModuleString *key) { | ||
| 8711 | signalKeyAsReady(ctx->client->db, key, OBJ_MODULE); | ||
| 8712 | } | ||
| 8713 | |||
| 8714 | /* Implements RM_UnblockClient() and moduleUnblockClient(). */ | ||
| 8715 | int moduleUnblockClientByHandle(RedisModuleBlockedClient *bc, void *privdata) { | ||
| 8716 | pthread_mutex_lock(&moduleUnblockedClientsMutex); | ||
| 8717 | if (!bc->blocked_on_keys) bc->privdata = privdata; | ||
| 8718 | bc->unblocked = 1; | ||
| 8719 | if (listLength(moduleUnblockedClients) == 0) { | ||
| 8720 | if (write(server.module_pipe[1],"A",1) != 1) { | ||
| 8721 | /* Ignore the error, this is best-effort. */ | ||
| 8722 | } | ||
| 8723 | } | ||
| 8724 | listAddNodeTail(moduleUnblockedClients,bc); | ||
| 8725 | pthread_mutex_unlock(&moduleUnblockedClientsMutex); | ||
| 8726 | return REDISMODULE_OK; | ||
| 8727 | } | ||
| 8728 | |||
| 8729 | /* This API is used by the Redis core to unblock a client that was blocked | ||
| 8730 | * by a module. */ | ||
| 8731 | void moduleUnblockClient(client *c) { | ||
| 8732 | RedisModuleBlockedClient *bc = c->bstate.module_blocked_handle; | ||
| 8733 | moduleUnblockClientByHandle(bc,NULL); | ||
| 8734 | } | ||
| 8735 | |||
| 8736 | /* Return true if the client 'c' was blocked by a module using | ||
| 8737 | * RM_BlockClientOnKeys(). */ | ||
| 8738 | int moduleClientIsBlockedOnKeys(client *c) { | ||
| 8739 | RedisModuleBlockedClient *bc = c->bstate.module_blocked_handle; | ||
| 8740 | return bc->blocked_on_keys; | ||
| 8741 | } | ||
| 8742 | |||
| 8743 | /* Unblock a client blocked by `RedisModule_BlockedClient`. This will trigger | ||
| 8744 | * the reply callbacks to be called in order to reply to the client. | ||
| 8745 | * The 'privdata' argument will be accessible by the reply callback, so | ||
| 8746 | * the caller of this function can pass any value that is needed in order to | ||
| 8747 | * actually reply to the client. | ||
| 8748 | * | ||
| 8749 | * A common usage for 'privdata' is a thread that computes something that | ||
| 8750 | * needs to be passed to the client, included but not limited some slow | ||
| 8751 | * to compute reply or some reply obtained via networking. | ||
| 8752 | * | ||
| 8753 | * Note 1: this function can be called from threads spawned by the module. | ||
| 8754 | * | ||
| 8755 | * Note 2: when we unblock a client that is blocked for keys using the API | ||
| 8756 | * RedisModule_BlockClientOnKeys(), the privdata argument here is not used. | ||
| 8757 | * Unblocking a client that was blocked for keys using this API will still | ||
| 8758 | * require the client to get some reply, so the function will use the | ||
| 8759 | * "timeout" handler in order to do so (The privdata provided in | ||
| 8760 | * RedisModule_BlockClientOnKeys() is accessible from the timeout | ||
| 8761 | * callback via RM_GetBlockedClientPrivateData). */ | ||
| 8762 | int RM_UnblockClient(RedisModuleBlockedClient *bc, void *privdata) { | ||
| 8763 | if (bc->blocked_on_keys) { | ||
| 8764 | /* In theory the user should always pass the timeout handler as an | ||
| 8765 | * argument, but better to be safe than sorry. */ | ||
| 8766 | if (bc->timeout_callback == NULL) return REDISMODULE_ERR; | ||
| 8767 | if (bc->unblocked) return REDISMODULE_OK; | ||
| 8768 | if (bc->client) bc->blocked_on_keys_explicit_unblock = 1; | ||
| 8769 | } | ||
| 8770 | moduleUnblockClientByHandle(bc,privdata); | ||
| 8771 | return REDISMODULE_OK; | ||
| 8772 | } | ||
| 8773 | |||
| 8774 | /* Abort a blocked client blocking operation: the client will be unblocked | ||
| 8775 | * without firing any callback. */ | ||
| 8776 | int RM_AbortBlock(RedisModuleBlockedClient *bc) { | ||
| 8777 | bc->reply_callback = NULL; | ||
| 8778 | bc->disconnect_callback = NULL; | ||
| 8779 | bc->auth_reply_cb = NULL; | ||
| 8780 | return RM_UnblockClient(bc,NULL); | ||
| 8781 | } | ||
| 8782 | |||
| 8783 | /* Set a callback that will be called if a blocked client disconnects | ||
| 8784 | * before the module has a chance to call RedisModule_UnblockClient() | ||
| 8785 | * | ||
| 8786 | * Usually what you want to do there, is to cleanup your module state | ||
| 8787 | * so that you can call RedisModule_UnblockClient() safely, otherwise | ||
| 8788 | * the client will remain blocked forever if the timeout is large. | ||
| 8789 | * | ||
| 8790 | * Notes: | ||
| 8791 | * | ||
| 8792 | * 1. It is not safe to call Reply* family functions here, it is also | ||
| 8793 | * useless since the client is gone. | ||
| 8794 | * | ||
| 8795 | * 2. This callback is not called if the client disconnects because of | ||
| 8796 | * a timeout. In such a case, the client is unblocked automatically | ||
| 8797 | * and the timeout callback is called. | ||
| 8798 | */ | ||
| 8799 | void RM_SetDisconnectCallback(RedisModuleBlockedClient *bc, RedisModuleDisconnectFunc callback) { | ||
| 8800 | bc->disconnect_callback = callback; | ||
| 8801 | } | ||
| 8802 | |||
| 8803 | /* This function will check the moduleUnblockedClients queue in order to | ||
| 8804 | * call the reply callback and really unblock the client. | ||
| 8805 | * | ||
| 8806 | * Clients end into this list because of calls to RM_UnblockClient(), | ||
| 8807 | * however it is possible that while the module was doing work for the | ||
| 8808 | * blocked client, it was terminated by Redis (for timeout or other reasons). | ||
| 8809 | * When this happens the RedisModuleBlockedClient structure in the queue | ||
| 8810 | * will have the 'client' field set to NULL. */ | ||
| 8811 | void moduleHandleBlockedClients(void) { | ||
| 8812 | listNode *ln; | ||
| 8813 | RedisModuleBlockedClient *bc; | ||
| 8814 | |||
| 8815 | pthread_mutex_lock(&moduleUnblockedClientsMutex); | ||
| 8816 | while (listLength(moduleUnblockedClients)) { | ||
| 8817 | ln = listFirst(moduleUnblockedClients); | ||
| 8818 | bc = ln->value; | ||
| 8819 | client *c = bc->client; | ||
| 8820 | listDelNode(moduleUnblockedClients,ln); | ||
| 8821 | pthread_mutex_unlock(&moduleUnblockedClientsMutex); | ||
| 8822 | |||
| 8823 | /* Release the lock during the loop, as long as we don't | ||
| 8824 | * touch the shared list. */ | ||
| 8825 | |||
| 8826 | /* Call the reply callback if the client is valid and we have | ||
| 8827 | * any callback. However the callback is not called if the client | ||
| 8828 | * was blocked on keys (RM_BlockClientOnKeys()), because we already | ||
| 8829 | * called such callback in moduleTryServeClientBlockedOnKey() when | ||
| 8830 | * the key was signaled as ready. */ | ||
| 8831 | long long prev_error_replies = server.stat_total_error_replies; | ||
| 8832 | uint64_t reply_us = 0; | ||
| 8833 | if (c && !bc->blocked_on_keys && bc->reply_callback) { | ||
| 8834 | RedisModuleCtx ctx; | ||
| 8835 | moduleCreateContext(&ctx, bc->module, REDISMODULE_CTX_BLOCKED_REPLY); | ||
| 8836 | ctx.blocked_privdata = bc->privdata; | ||
| 8837 | ctx.blocked_ready_key = NULL; | ||
| 8838 | ctx.client = bc->client; | ||
| 8839 | ctx.blocked_client = bc; | ||
| 8840 | monotime replyTimer; | ||
| 8841 | elapsedStart(&replyTimer); | ||
| 8842 | bc->reply_callback(&ctx,(void**)c->argv,c->argc); | ||
| 8843 | reply_us = elapsedUs(replyTimer); | ||
| 8844 | moduleFreeContext(&ctx); | ||
| 8845 | } | ||
| 8846 | if (c && bc->blocked_on_keys_explicit_unblock) { | ||
| 8847 | serverAssert(bc->blocked_on_keys); | ||
| 8848 | moduleBlockedClientTimedOut(c); | ||
| 8849 | } | ||
| 8850 | /* Hold onto the blocked client if module auth is in progress. The reply callback is invoked | ||
| 8851 | * when the client is reprocessed. */ | ||
| 8852 | if (c && clientHasModuleAuthInProgress(c)) { | ||
| 8853 | c->module_blocked_client = bc; | ||
| 8854 | } else { | ||
| 8855 | /* Free privdata if any. */ | ||
| 8856 | moduleInvokeFreePrivDataCallback(c, bc); | ||
| 8857 | } | ||
| 8858 | |||
| 8859 | /* It is possible that this blocked client object accumulated | ||
| 8860 | * replies to send to the client in a thread safe context. | ||
| 8861 | * We need to glue such replies to the client output buffer and | ||
| 8862 | * free the temporary client we just used for the replies. */ | ||
| 8863 | if (c) AddReplyFromClient(c, bc->reply_client); | ||
| 8864 | moduleReleaseTempClient(bc->reply_client); | ||
| 8865 | moduleReleaseTempClient(bc->thread_safe_ctx_client); | ||
| 8866 | |||
| 8867 | /* Update stats now that we've finished the blocking operation. | ||
| 8868 | * This needs to be out of the reply callback above given that a | ||
| 8869 | * module might not define any callback and still do blocking ops. | ||
| 8870 | * | ||
| 8871 | * If the module is blocked on keys updateStatsOnUnblock will be | ||
| 8872 | * called from moduleUnblockClientOnKey | ||
| 8873 | */ | ||
| 8874 | if (c && !clientHasModuleAuthInProgress(c) && !bc->blocked_on_keys) { | ||
| 8875 | updateStatsOnUnblock(c, bc->background_duration, reply_us, server.stat_total_error_replies != prev_error_replies); | ||
| 8876 | } | ||
| 8877 | |||
| 8878 | if (c != NULL) { | ||
| 8879 | /* Before unblocking the client, set the disconnect callback | ||
| 8880 | * to NULL, because if we reached this point, the client was | ||
| 8881 | * properly unblocked by the module. */ | ||
| 8882 | bc->disconnect_callback = NULL; | ||
| 8883 | unblockClient(c, 1); | ||
| 8884 | |||
| 8885 | /* Update the wait offset, we don't know if this blocked client propagated anything, | ||
| 8886 | * currently we rather not add any API for that, so we just assume it did. */ | ||
| 8887 | c->woff = server.master_repl_offset; | ||
| 8888 | |||
| 8889 | /* Put the client in the list of clients that need to write | ||
| 8890 | * if there are pending replies here. This is needed since | ||
| 8891 | * during a non blocking command the client may receive output. */ | ||
| 8892 | if (!clientHasModuleAuthInProgress(c) && clientHasPendingReplies(c) && | ||
| 8893 | !(c->flags & CLIENT_PENDING_WRITE) && c->conn) | ||
| 8894 | { | ||
| 8895 | c->flags |= CLIENT_PENDING_WRITE; | ||
| 8896 | listLinkNodeHead(server.clients_pending_write, &c->clients_pending_write_node); | ||
| 8897 | } | ||
| 8898 | } | ||
| 8899 | |||
| 8900 | /* Free 'bc' only after unblocking the client, since it is | ||
| 8901 | * referenced in the client blocking context, and must be valid | ||
| 8902 | * when calling unblockClient(). */ | ||
| 8903 | if (!(c && clientHasModuleAuthInProgress(c))) { | ||
| 8904 | bc->module->blocked_clients--; | ||
| 8905 | zfree(bc); | ||
| 8906 | } | ||
| 8907 | |||
| 8908 | /* Lock again before to iterate the loop. */ | ||
| 8909 | pthread_mutex_lock(&moduleUnblockedClientsMutex); | ||
| 8910 | } | ||
| 8911 | pthread_mutex_unlock(&moduleUnblockedClientsMutex); | ||
| 8912 | } | ||
| 8913 | |||
| 8914 | /* Check if the specified client can be safely timed out using | ||
| 8915 | * moduleBlockedClientTimedOut(). | ||
| 8916 | */ | ||
| 8917 | int moduleBlockedClientMayTimeout(client *c) { | ||
| 8918 | if (c->bstate.btype != BLOCKED_MODULE) | ||
| 8919 | return 1; | ||
| 8920 | |||
| 8921 | RedisModuleBlockedClient *bc = c->bstate.module_blocked_handle; | ||
| 8922 | return (bc && bc->timeout_callback != NULL); | ||
| 8923 | } | ||
| 8924 | |||
| 8925 | /* Called when our client timed out. After this function unblockClient() | ||
| 8926 | * is called, and it will invalidate the blocked client. So this function | ||
| 8927 | * does not need to do any cleanup. Eventually the module will call the | ||
| 8928 | * API to unblock the client and the memory will be released. | ||
| 8929 | * | ||
| 8930 | * This function should only be called from the main thread, we must handle the unblocking | ||
| 8931 | * of the client synchronously. This ensures that we can reply to the client before | ||
| 8932 | * resetClient() is called. */ | ||
| 8933 | void moduleBlockedClientTimedOut(client *c) { | ||
| 8934 | RedisModuleBlockedClient *bc = c->bstate.module_blocked_handle; | ||
| 8935 | |||
| 8936 | RedisModuleCtx ctx; | ||
| 8937 | moduleCreateContext(&ctx, bc->module, REDISMODULE_CTX_BLOCKED_TIMEOUT); | ||
| 8938 | ctx.client = bc->client; | ||
| 8939 | ctx.blocked_client = bc; | ||
| 8940 | ctx.blocked_privdata = bc->privdata; | ||
| 8941 | |||
| 8942 | long long prev_error_replies = server.stat_total_error_replies; | ||
| 8943 | |||
| 8944 | if (bc->timeout_callback) { | ||
| 8945 | /* In theory, the user should always pass the timeout handler as an | ||
| 8946 | * argument, but better to be safe than sorry. */ | ||
| 8947 | bc->timeout_callback(&ctx,(void**)c->argv,c->argc); | ||
| 8948 | } | ||
| 8949 | |||
| 8950 | moduleFreeContext(&ctx); | ||
| 8951 | |||
| 8952 | updateStatsOnUnblock(c, bc->background_duration, 0, server.stat_total_error_replies != prev_error_replies); | ||
| 8953 | |||
| 8954 | /* For timeout events, we do not want to call the disconnect callback, | ||
| 8955 | * because the blocked client will be automatically disconnected in | ||
| 8956 | * this case, and the user can still hook using the timeout callback. */ | ||
| 8957 | bc->disconnect_callback = NULL; | ||
| 8958 | } | ||
| 8959 | |||
| 8960 | /* Return non-zero if a module command was called in order to fill the | ||
| 8961 | * reply for a blocked client. */ | ||
| 8962 | int RM_IsBlockedReplyRequest(RedisModuleCtx *ctx) { | ||
| 8963 | return (ctx->flags & REDISMODULE_CTX_BLOCKED_REPLY) != 0; | ||
| 8964 | } | ||
| 8965 | |||
| 8966 | /* Return non-zero if a module command was called in order to fill the | ||
| 8967 | * reply for a blocked client that timed out. */ | ||
| 8968 | int RM_IsBlockedTimeoutRequest(RedisModuleCtx *ctx) { | ||
| 8969 | return (ctx->flags & REDISMODULE_CTX_BLOCKED_TIMEOUT) != 0; | ||
| 8970 | } | ||
| 8971 | |||
| 8972 | /* Get the private data set by RedisModule_UnblockClient() */ | ||
| 8973 | void *RM_GetBlockedClientPrivateData(RedisModuleCtx *ctx) { | ||
| 8974 | return ctx->blocked_privdata; | ||
| 8975 | } | ||
| 8976 | |||
| 8977 | /* Get the key that is ready when the reply callback is called in the context | ||
| 8978 | * of a client blocked by RedisModule_BlockClientOnKeys(). */ | ||
| 8979 | RedisModuleString *RM_GetBlockedClientReadyKey(RedisModuleCtx *ctx) { | ||
| 8980 | return ctx->blocked_ready_key; | ||
| 8981 | } | ||
| 8982 | |||
| 8983 | /* Get the blocked client associated with a given context. | ||
| 8984 | * This is useful in the reply and timeout callbacks of blocked clients, | ||
| 8985 | * before sometimes the module has the blocked client handle references | ||
| 8986 | * around, and wants to cleanup it. */ | ||
| 8987 | RedisModuleBlockedClient *RM_GetBlockedClientHandle(RedisModuleCtx *ctx) { | ||
| 8988 | return ctx->blocked_client; | ||
| 8989 | } | ||
| 8990 | |||
| 8991 | /* Return true if when the free callback of a blocked client is called, | ||
| 8992 | * the reason for the client to be unblocked is that it disconnected | ||
| 8993 | * while it was blocked. */ | ||
| 8994 | int RM_BlockedClientDisconnected(RedisModuleCtx *ctx) { | ||
| 8995 | return (ctx->flags & REDISMODULE_CTX_BLOCKED_DISCONNECTED) != 0; | ||
| 8996 | } | ||
| 8997 | |||
| 8998 | /* -------------------------------------------------------------------------- | ||
| 8999 | * ## Thread Safe Contexts | ||
| 9000 | * -------------------------------------------------------------------------- */ | ||
| 9001 | |||
| 9002 | /* Return a context which can be used inside threads to make Redis context | ||
| 9003 | * calls with certain modules APIs. If 'bc' is not NULL then the module will | ||
| 9004 | * be bound to a blocked client, and it will be possible to use the | ||
| 9005 | * `RedisModule_Reply*` family of functions to accumulate a reply for when the | ||
| 9006 | * client will be unblocked. Otherwise the thread safe context will be | ||
| 9007 | * detached by a specific client. | ||
| 9008 | * | ||
| 9009 | * To call non-reply APIs, the thread safe context must be prepared with: | ||
| 9010 | * | ||
| 9011 | * RedisModule_ThreadSafeContextLock(ctx); | ||
| 9012 | * ... make your call here ... | ||
| 9013 | * RedisModule_ThreadSafeContextUnlock(ctx); | ||
| 9014 | * | ||
| 9015 | * This is not needed when using `RedisModule_Reply*` functions, assuming | ||
| 9016 | * that a blocked client was used when the context was created, otherwise | ||
| 9017 | * no RedisModule_Reply* call should be made at all. | ||
| 9018 | * | ||
| 9019 | * NOTE: If you're creating a detached thread safe context (bc is NULL), | ||
| 9020 | * consider using `RM_GetDetachedThreadSafeContext` which will also retain | ||
| 9021 | * the module ID and thus be more useful for logging. */ | ||
| 9022 | RedisModuleCtx *RM_GetThreadSafeContext(RedisModuleBlockedClient *bc) { | ||
| 9023 | RedisModuleCtx *ctx = zmalloc(sizeof(*ctx)); | ||
| 9024 | RedisModule *module = bc ? bc->module : NULL; | ||
| 9025 | int flags = REDISMODULE_CTX_THREAD_SAFE; | ||
| 9026 | |||
| 9027 | /* Creating a new client object is costly. To avoid that, we have an | ||
| 9028 | * internal pool of client objects. In blockClient(), a client object is | ||
| 9029 | * assigned to bc->thread_safe_ctx_client to be used for the thread safe | ||
| 9030 | * context. | ||
| 9031 | * For detached thread safe contexts, we create a new client object. | ||
| 9032 | * Otherwise, as this function can be called from different threads, we | ||
| 9033 | * would need to synchronize access to internal pool of client objects. | ||
| 9034 | * Assuming creating detached context is rare and not that performance | ||
| 9035 | * critical, we avoid synchronizing access to the client pool by creating | ||
| 9036 | * a new client */ | ||
| 9037 | if (!bc) flags |= REDISMODULE_CTX_NEW_CLIENT; | ||
| 9038 | moduleCreateContext(ctx, module, flags); | ||
| 9039 | /* Even when the context is associated with a blocked client, we can't | ||
| 9040 | * access it safely from another thread, so we use a fake client here | ||
| 9041 | * in order to keep things like the currently selected database and similar | ||
| 9042 | * things. */ | ||
| 9043 | if (bc) { | ||
| 9044 | ctx->blocked_client = bc; | ||
| 9045 | ctx->client = bc->thread_safe_ctx_client; | ||
| 9046 | selectDb(ctx->client,bc->dbid); | ||
| 9047 | if (bc->client) { | ||
| 9048 | ctx->client->id = bc->client->id; | ||
| 9049 | ctx->client->resp = bc->client->resp; | ||
| 9050 | } | ||
| 9051 | } | ||
| 9052 | return ctx; | ||
| 9053 | } | ||
| 9054 | |||
| 9055 | /* Return a detached thread safe context that is not associated with any | ||
| 9056 | * specific blocked client, but is associated with the module's context. | ||
| 9057 | * | ||
| 9058 | * This is useful for modules that wish to hold a global context over | ||
| 9059 | * a long term, for purposes such as logging. */ | ||
| 9060 | RedisModuleCtx *RM_GetDetachedThreadSafeContext(RedisModuleCtx *ctx) { | ||
| 9061 | RedisModuleCtx *new_ctx = zmalloc(sizeof(*new_ctx)); | ||
| 9062 | /* We create a new client object for the detached context. | ||
| 9063 | * See RM_GetThreadSafeContext() for more information */ | ||
| 9064 | moduleCreateContext(new_ctx, ctx->module, | ||
| 9065 | REDISMODULE_CTX_THREAD_SAFE|REDISMODULE_CTX_NEW_CLIENT); | ||
| 9066 | return new_ctx; | ||
| 9067 | } | ||
| 9068 | |||
| 9069 | /* Release a thread safe context. */ | ||
| 9070 | void RM_FreeThreadSafeContext(RedisModuleCtx *ctx) { | ||
| 9071 | moduleFreeContext(ctx); | ||
| 9072 | zfree(ctx); | ||
| 9073 | } | ||
| 9074 | |||
| 9075 | void moduleGILAfterLock(void) { | ||
| 9076 | /* We should never get here if we already inside a module | ||
| 9077 | * code block which already opened a context. */ | ||
| 9078 | serverAssert(server.execution_nesting == 0); | ||
| 9079 | /* Bump up the nesting level to prevent immediate propagation | ||
| 9080 | * of possible RM_Call from th thread */ | ||
| 9081 | enterExecutionUnit(1, 0); | ||
| 9082 | } | ||
| 9083 | |||
| 9084 | /* Acquire the server lock before executing a thread safe API call. | ||
| 9085 | * This is not needed for `RedisModule_Reply*` calls when there is | ||
| 9086 | * a blocked client connected to the thread safe context. */ | ||
| 9087 | void RM_ThreadSafeContextLock(RedisModuleCtx *ctx) { | ||
| 9088 | UNUSED(ctx); | ||
| 9089 | moduleAcquireGIL(); | ||
| 9090 | moduleGILAfterLock(); | ||
| 9091 | } | ||
| 9092 | |||
| 9093 | /* Similar to RM_ThreadSafeContextLock but this function | ||
| 9094 | * would not block if the server lock is already acquired. | ||
| 9095 | * | ||
| 9096 | * If successful (lock acquired) REDISMODULE_OK is returned, | ||
| 9097 | * otherwise REDISMODULE_ERR is returned and errno is set | ||
| 9098 | * accordingly. */ | ||
| 9099 | int RM_ThreadSafeContextTryLock(RedisModuleCtx *ctx) { | ||
| 9100 | UNUSED(ctx); | ||
| 9101 | |||
| 9102 | int res = moduleTryAcquireGIL(); | ||
| 9103 | if(res != 0) { | ||
| 9104 | errno = res; | ||
| 9105 | return REDISMODULE_ERR; | ||
| 9106 | } | ||
| 9107 | moduleGILAfterLock(); | ||
| 9108 | return REDISMODULE_OK; | ||
| 9109 | } | ||
| 9110 | |||
| 9111 | void moduleGILBeforeUnlock(void) { | ||
| 9112 | /* We should never get here if we already inside a module | ||
| 9113 | * code block which already opened a context, except | ||
| 9114 | * the bump-up from moduleGILAcquired. */ | ||
| 9115 | serverAssert(server.execution_nesting == 1); | ||
| 9116 | /* Restore nesting level and propagate pending commands | ||
| 9117 | * (because it's unclear when thread safe contexts are | ||
| 9118 | * released we have to propagate here). */ | ||
| 9119 | exitExecutionUnit(); | ||
| 9120 | postExecutionUnitOperations(); | ||
| 9121 | } | ||
| 9122 | |||
| 9123 | /* Release the server lock after a thread safe API call was executed. */ | ||
| 9124 | void RM_ThreadSafeContextUnlock(RedisModuleCtx *ctx) { | ||
| 9125 | UNUSED(ctx); | ||
| 9126 | moduleGILBeforeUnlock(); | ||
| 9127 | moduleReleaseGIL(); | ||
| 9128 | } | ||
| 9129 | |||
| 9130 | void moduleAcquireGIL(void) { | ||
| 9131 | pthread_mutex_lock(&moduleGIL); | ||
| 9132 | } | ||
| 9133 | |||
| 9134 | int moduleTryAcquireGIL(void) { | ||
| 9135 | return pthread_mutex_trylock(&moduleGIL); | ||
| 9136 | } | ||
| 9137 | |||
| 9138 | void moduleReleaseGIL(void) { | ||
| 9139 | pthread_mutex_unlock(&moduleGIL); | ||
| 9140 | } | ||
| 9141 | |||
| 9142 | |||
| 9143 | /* -------------------------------------------------------------------------- | ||
| 9144 | * ## Module Keyspace Notifications API | ||
| 9145 | * -------------------------------------------------------------------------- */ | ||
| 9146 | |||
| 9147 | /* Subscribe to keyspace notifications. This is a low-level version of the | ||
| 9148 | * keyspace-notifications API. A module can register callbacks to be notified | ||
| 9149 | * when keyspace events occur. | ||
| 9150 | * | ||
| 9151 | * Notification events are filtered by their type (string events, set events, | ||
| 9152 | * etc), and the subscriber callback receives only events that match a specific | ||
| 9153 | * mask of event types. | ||
| 9154 | * | ||
| 9155 | * When subscribing to notifications with RedisModule_SubscribeToKeyspaceEvents | ||
| 9156 | * the module must provide an event type-mask, denoting the events the subscriber | ||
| 9157 | * is interested in. This can be an ORed mask of any of the following flags: | ||
| 9158 | * | ||
| 9159 | * - REDISMODULE_NOTIFY_GENERIC: Generic commands like DEL, EXPIRE, RENAME | ||
| 9160 | * - REDISMODULE_NOTIFY_STRING: String events | ||
| 9161 | * - REDISMODULE_NOTIFY_LIST: List events | ||
| 9162 | * - REDISMODULE_NOTIFY_SET: Set events | ||
| 9163 | * - REDISMODULE_NOTIFY_HASH: Hash events | ||
| 9164 | * - REDISMODULE_NOTIFY_ZSET: Sorted Set events | ||
| 9165 | * - REDISMODULE_NOTIFY_EXPIRED: Expiration events | ||
| 9166 | * - REDISMODULE_NOTIFY_EVICTED: Eviction events | ||
| 9167 | * - REDISMODULE_NOTIFY_STREAM: Stream events | ||
| 9168 | * - REDISMODULE_NOTIFY_MODULE: Module types events | ||
| 9169 | * - REDISMODULE_NOTIFY_KEYMISS: Key-miss events | ||
| 9170 | * Notice, key-miss event is the only type | ||
| 9171 | * of event that is fired from within a read command. | ||
| 9172 | * Performing RM_Call with a write command from within | ||
| 9173 | * this notification is wrong and discourage. It will | ||
| 9174 | * cause the read command that trigger the event to be | ||
| 9175 | * replicated to the AOF/Replica. | ||
| 9176 | * | ||
| 9177 | * - REDISMODULE_NOTIFY_NEW: New key notification | ||
| 9178 | * - REDISMODULE_NOTIFY_OVERWRITTEN: Overwritten events | ||
| 9179 | * - REDISMODULE_NOTIFY_TYPE_CHANGED: Type-changed events | ||
| 9180 | * - REDISMODULE_NOTIFY_KEY_TRIMMED: Key trimmed events after a slot migration operation | ||
| 9181 | * - REDISMODULE_NOTIFY_ALL: All events (Excluding REDISMODULE_NOTIFY_KEYMISS, | ||
| 9182 | * REDISMODULE_NOTIFY_NEW, REDISMODULE_NOTIFY_OVERWRITTEN, | ||
| 9183 | * REDISMODULE_NOTIFY_TYPE_CHANGED | ||
| 9184 | * and REDISMODULE_NOTIFY_KEY_TRIMMED) | ||
| 9185 | * - REDISMODULE_NOTIFY_LOADED: A special notification available only for modules, | ||
| 9186 | * indicates that the key was loaded from persistence. | ||
| 9187 | * Notice, when this event fires, the given key | ||
| 9188 | * can not be retained, use RM_CreateStringFromString | ||
| 9189 | * instead. | ||
| 9190 | * | ||
| 9191 | * We do not distinguish between key events and keyspace events, and it is up | ||
| 9192 | * to the module to filter the actions taken based on the key. | ||
| 9193 | * | ||
| 9194 | * The subscriber signature is: | ||
| 9195 | * | ||
| 9196 | * int (*RedisModuleNotificationFunc) (RedisModuleCtx *ctx, int type, | ||
| 9197 | * const char *event, | ||
| 9198 | * RedisModuleString *key); | ||
| 9199 | * | ||
| 9200 | * `type` is the event type bit, that must match the mask given at registration | ||
| 9201 | * time. The event string is the actual command being executed, and key is the | ||
| 9202 | * relevant Redis key. | ||
| 9203 | * | ||
| 9204 | * Notification callback gets executed with a redis context that can not be | ||
| 9205 | * used to send anything to the client, and has the db number where the event | ||
| 9206 | * occurred as its selected db number. | ||
| 9207 | * | ||
| 9208 | * Notice that it is not necessary to enable notifications in redis.conf for | ||
| 9209 | * module notifications to work. | ||
| 9210 | * | ||
| 9211 | * Warning: the notification callbacks are performed in a synchronous manner, | ||
| 9212 | * so notification callbacks must to be fast, or they would slow Redis down. | ||
| 9213 | * If you need to take long actions, use threads to offload them. | ||
| 9214 | * | ||
| 9215 | * Moreover, the fact that the notification is executed synchronously means | ||
| 9216 | * that the notification code will be executed in the middle on Redis logic | ||
| 9217 | * (commands logic, eviction, expire). Changing the key space while the logic | ||
| 9218 | * runs is dangerous and discouraged. In order to react to key space events with | ||
| 9219 | * write actions, please refer to `RM_AddPostNotificationJob`. | ||
| 9220 | * | ||
| 9221 | * See https://redis.io/docs/latest/develop/use/keyspace-notifications/ for more information. | ||
| 9222 | */ | ||
| 9223 | int RM_SubscribeToKeyspaceEvents(RedisModuleCtx *ctx, int types, RedisModuleNotificationFunc callback) { | ||
| 9224 | RedisModuleKeyspaceSubscriber *sub = zmalloc(sizeof(*sub)); | ||
| 9225 | sub->module = ctx->module; | ||
| 9226 | sub->event_mask = types; | ||
| 9227 | sub->notify_callback = callback; | ||
| 9228 | sub->active = 0; | ||
| 9229 | |||
| 9230 | listAddNodeTail(moduleKeyspaceSubscribers, sub); | ||
| 9231 | return REDISMODULE_OK; | ||
| 9232 | } | ||
| 9233 | |||
| 9234 | /* | ||
| 9235 | * RM_UnsubscribeFromKeyspaceEvents - Unregister a module's callback from keyspace notifications for specific event types. | ||
| 9236 | * | ||
| 9237 | * This function removes a previously registered subscription identified by both the event mask and the callback function. | ||
| 9238 | * It is useful to reduce performance overhead when the module no longer requires notifications for certain events. | ||
| 9239 | * | ||
| 9240 | * Parameters: | ||
| 9241 | * - ctx: The RedisModuleCtx associated with the calling module. | ||
| 9242 | * - types: The event mask representing the keyspace notification types to unsubscribe from. | ||
| 9243 | * - callback: The callback function pointer that was originally registered for these events. | ||
| 9244 | * | ||
| 9245 | * Returns: | ||
| 9246 | * - REDISMODULE_OK on successful removal of the subscription. | ||
| 9247 | * - REDISMODULE_ERR if no matching subscription was found or if invalid parameters were provided. | ||
| 9248 | */ | ||
| 9249 | int RM_UnsubscribeFromKeyspaceEvents(RedisModuleCtx *ctx, int types, RedisModuleNotificationFunc callback) { | ||
| 9250 | if (!ctx || !callback) return REDISMODULE_ERR; | ||
| 9251 | int removed = 0; | ||
| 9252 | listIter li; | ||
| 9253 | listNode *ln; | ||
| 9254 | listRewind(moduleKeyspaceSubscribers,&li); | ||
| 9255 | while ((ln = listNext(&li))) { | ||
| 9256 | RedisModuleKeyspaceSubscriber *sub = ln->value; | ||
| 9257 | if (sub->event_mask == types && sub->notify_callback == callback && sub->module == ctx->module) { | ||
| 9258 | zfree(sub); | ||
| 9259 | listDelNode(moduleKeyspaceSubscribers, ln); | ||
| 9260 | removed++; | ||
| 9261 | } | ||
| 9262 | } | ||
| 9263 | return removed > 0 ? REDISMODULE_OK : REDISMODULE_ERR; | ||
| 9264 | } | ||
| 9265 | |||
| 9266 | /* Check any subscriber for event */ | ||
| 9267 | int moduleHasSubscribersForKeyspaceEvent(int type) { | ||
| 9268 | listIter li; | ||
| 9269 | listNode *ln; | ||
| 9270 | listRewind(moduleKeyspaceSubscribers,&li); | ||
| 9271 | while((ln = listNext(&li))) { | ||
| 9272 | RedisModuleKeyspaceSubscriber *sub = ln->value; | ||
| 9273 | if (sub->event_mask & type) return 1; | ||
| 9274 | } | ||
| 9275 | return 0; | ||
| 9276 | } | ||
| 9277 | |||
| 9278 | void firePostExecutionUnitJobs(void) { | ||
| 9279 | /* Avoid propagation of commands. | ||
| 9280 | * In that way, postExecutionUnitOperations will prevent | ||
| 9281 | * recursive calls to firePostExecutionUnitJobs. | ||
| 9282 | * This is a special case where we need to increase 'execution_nesting' | ||
| 9283 | * but we do not want to update the cached time */ | ||
| 9284 | enterExecutionUnit(0, 0); | ||
| 9285 | while (listLength(modulePostExecUnitJobs) > 0) { | ||
| 9286 | listNode *ln = listFirst(modulePostExecUnitJobs); | ||
| 9287 | RedisModulePostExecUnitJob *job = listNodeValue(ln); | ||
| 9288 | listDelNode(modulePostExecUnitJobs, ln); | ||
| 9289 | |||
| 9290 | RedisModuleCtx ctx; | ||
| 9291 | moduleCreateContext(&ctx, job->module, REDISMODULE_CTX_TEMP_CLIENT); | ||
| 9292 | selectDb(ctx.client, job->dbid); | ||
| 9293 | |||
| 9294 | job->callback(&ctx, job->pd); | ||
| 9295 | if (job->free_pd) job->free_pd(job->pd); | ||
| 9296 | |||
| 9297 | moduleFreeContext(&ctx); | ||
| 9298 | zfree(job); | ||
| 9299 | } | ||
| 9300 | exitExecutionUnit(); | ||
| 9301 | } | ||
| 9302 | |||
| 9303 | /* When running inside a key space notification callback, it is dangerous and highly discouraged to perform any write | ||
| 9304 | * operation (See `RM_SubscribeToKeyspaceEvents`). In order to still perform write actions in this scenario, | ||
| 9305 | * Redis provides `RM_AddPostNotificationJob` API. The API allows to register a job callback which Redis will call | ||
| 9306 | * when the following condition are promised to be fulfilled: | ||
| 9307 | * 1. It is safe to perform any write operation. | ||
| 9308 | * 2. The job will be called atomically along side the key space notification. | ||
| 9309 | * | ||
| 9310 | * Notice, one job might trigger key space notifications that will trigger more jobs. | ||
| 9311 | * This raises a concerns of entering an infinite loops, we consider infinite loops | ||
| 9312 | * as a logical bug that need to be fixed in the module, an attempt to protect against | ||
| 9313 | * infinite loops by halting the execution could result in violation of the feature correctness | ||
| 9314 | * and so Redis will make no attempt to protect the module from infinite loops. | ||
| 9315 | * | ||
| 9316 | * 'free_pd' can be NULL and in such case will not be used. | ||
| 9317 | * | ||
| 9318 | * Return REDISMODULE_OK on success and REDISMODULE_ERR if was called while loading data from disk (AOF or RDB) or | ||
| 9319 | * if the instance is a readonly replica. */ | ||
| 9320 | int RM_AddPostNotificationJob(RedisModuleCtx *ctx, RedisModulePostNotificationJobFunc callback, void *privdata, void (*free_privdata)(void*)) { | ||
| 9321 | if (server.loading|| (server.masterhost && server.repl_slave_ro)) { | ||
| 9322 | return REDISMODULE_ERR; | ||
| 9323 | } | ||
| 9324 | RedisModulePostExecUnitJob *job = zmalloc(sizeof(*job)); | ||
| 9325 | job->module = ctx->module; | ||
| 9326 | job->callback = callback; | ||
| 9327 | job->pd = privdata; | ||
| 9328 | job->free_pd = free_privdata; | ||
| 9329 | job->dbid = ctx->client->db->id; | ||
| 9330 | |||
| 9331 | listAddNodeTail(modulePostExecUnitJobs, job); | ||
| 9332 | return REDISMODULE_OK; | ||
| 9333 | } | ||
| 9334 | |||
| 9335 | /* Get the configured bitmap of notify-keyspace-events (Could be used | ||
| 9336 | * for additional filtering in RedisModuleNotificationFunc) */ | ||
| 9337 | int RM_GetNotifyKeyspaceEvents(void) { | ||
| 9338 | return server.notify_keyspace_events; | ||
| 9339 | } | ||
| 9340 | |||
| 9341 | /* Expose notifyKeyspaceEvent to modules */ | ||
| 9342 | int RM_NotifyKeyspaceEvent(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key) { | ||
| 9343 | if (!ctx || !ctx->client) | ||
| 9344 | return REDISMODULE_ERR; | ||
| 9345 | notifyKeyspaceEvent(type, (char *)event, key, ctx->client->db->id); | ||
| 9346 | return REDISMODULE_OK; | ||
| 9347 | } | ||
| 9348 | |||
| 9349 | /* Dispatcher for keyspace notifications to module subscriber functions. | ||
| 9350 | * This gets called only if at least one module requested to be notified on | ||
| 9351 | * keyspace notifications */ | ||
| 9352 | void moduleNotifyKeyspaceEvent(int type, const char *event, robj *key, int dbid) { | ||
| 9353 | /* Don't do anything if there aren't any subscribers */ | ||
| 9354 | if (listLength(moduleKeyspaceSubscribers) == 0) return; | ||
| 9355 | |||
| 9356 | /* Ugly hack to handle modules which use write commands from within | ||
| 9357 | * notify_callback, which they should NOT do! | ||
| 9358 | * Modules should use RedisModules_AddPostNotificationJob instead. | ||
| 9359 | * | ||
| 9360 | * Anyway, we want any propagated commands from within notify_callback | ||
| 9361 | * to be propagated inside a MULTI/EXEC together with the original | ||
| 9362 | * command that caused the KSN. | ||
| 9363 | * Note that it's only relevant for KSNs which are not generated from within | ||
| 9364 | * call(), for example active-expiry and eviction (because anyway | ||
| 9365 | * execution_nesting is incremented from within call()) | ||
| 9366 | * | ||
| 9367 | * In order to do that we increment the execution_nesting counter, thus | ||
| 9368 | * preventing postExecutionUnitOperations (from within moduleFreeContext) | ||
| 9369 | * from propagating commands from CB. | ||
| 9370 | * | ||
| 9371 | * This is a special case where we need to increase 'execution_nesting' | ||
| 9372 | * but we do not want to update the cached time */ | ||
| 9373 | enterExecutionUnit(0, 0); | ||
| 9374 | |||
| 9375 | listIter li; | ||
| 9376 | listNode *ln; | ||
| 9377 | listRewind(moduleKeyspaceSubscribers,&li); | ||
| 9378 | |||
| 9379 | /* Remove irrelevant flags from the type mask */ | ||
| 9380 | type &= ~(NOTIFY_KEYEVENT | NOTIFY_KEYSPACE); | ||
| 9381 | |||
| 9382 | while((ln = listNext(&li))) { | ||
| 9383 | RedisModuleKeyspaceSubscriber *sub = ln->value; | ||
| 9384 | /* Only notify subscribers on events matching the registration, | ||
| 9385 | * and avoid subscribers triggering themselves */ | ||
| 9386 | if ((sub->event_mask & type) && | ||
| 9387 | (sub->active == 0 || (sub->module->options & REDISMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS))) { | ||
| 9388 | RedisModuleCtx ctx; | ||
| 9389 | moduleCreateContext(&ctx, sub->module, REDISMODULE_CTX_TEMP_CLIENT); | ||
| 9390 | selectDb(ctx.client, dbid); | ||
| 9391 | |||
| 9392 | /* mark the handler as active to avoid reentrant loops. | ||
| 9393 | * If the subscriber performs an action triggering itself, | ||
| 9394 | * it will not be notified about it. */ | ||
| 9395 | int prev_active = sub->active; | ||
| 9396 | sub->active = 1; | ||
| 9397 | server.allow_access_expired++; | ||
| 9398 | server.allow_access_trimmed++; | ||
| 9399 | sub->notify_callback(&ctx, type, event, key); | ||
| 9400 | server.allow_access_expired--; | ||
| 9401 | server.allow_access_trimmed--; | ||
| 9402 | sub->active = prev_active; | ||
| 9403 | moduleFreeContext(&ctx); | ||
| 9404 | } | ||
| 9405 | } | ||
| 9406 | |||
| 9407 | exitExecutionUnit(); | ||
| 9408 | } | ||
| 9409 | |||
| 9410 | /* Unsubscribe any notification subscribers this module has upon unloading */ | ||
| 9411 | void moduleUnsubscribeNotifications(RedisModule *module) { | ||
| 9412 | listIter li; | ||
| 9413 | listNode *ln; | ||
| 9414 | listRewind(moduleKeyspaceSubscribers,&li); | ||
| 9415 | while((ln = listNext(&li))) { | ||
| 9416 | RedisModuleKeyspaceSubscriber *sub = ln->value; | ||
| 9417 | if (sub->module == module) { | ||
| 9418 | listDelNode(moduleKeyspaceSubscribers, ln); | ||
| 9419 | zfree(sub); | ||
| 9420 | } | ||
| 9421 | } | ||
| 9422 | } | ||
| 9423 | |||
| 9424 | /* -------------------------------------------------------------------------- | ||
| 9425 | * ## Modules Cluster API | ||
| 9426 | * -------------------------------------------------------------------------- */ | ||
| 9427 | |||
| 9428 | /* The Cluster message callback function pointer type. */ | ||
| 9429 | typedef void (*RedisModuleClusterMessageReceiver)(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, const unsigned char *payload, uint32_t len); | ||
| 9430 | |||
| 9431 | /* This structure identifies a registered caller: it must match a given module | ||
| 9432 | * ID, for a given message type. The callback function is just the function | ||
| 9433 | * that was registered as receiver. */ | ||
| 9434 | typedef struct moduleClusterReceiver { | ||
| 9435 | uint64_t module_id; | ||
| 9436 | RedisModuleClusterMessageReceiver callback; | ||
| 9437 | struct RedisModule *module; | ||
| 9438 | struct moduleClusterReceiver *next; | ||
| 9439 | } moduleClusterReceiver; | ||
| 9440 | |||
| 9441 | typedef struct moduleClusterNodeInfo { | ||
| 9442 | int flags; | ||
| 9443 | char ip[NET_IP_STR_LEN]; | ||
| 9444 | int port; | ||
| 9445 | char master_id[40]; /* Only if flags & REDISMODULE_NODE_MASTER is true. */ | ||
| 9446 | } mdouleClusterNodeInfo; | ||
| 9447 | |||
| 9448 | /* We have an array of message types: each bucket is a linked list of | ||
| 9449 | * configured receivers. */ | ||
| 9450 | static moduleClusterReceiver *clusterReceivers[UINT8_MAX]; | ||
| 9451 | |||
| 9452 | /* Dispatch the message to the right module receiver. */ | ||
| 9453 | void moduleCallClusterReceivers(const char *sender_id, uint64_t module_id, uint8_t type, const unsigned char *payload, uint32_t len) { | ||
| 9454 | moduleClusterReceiver *r = clusterReceivers[type]; | ||
| 9455 | while(r) { | ||
| 9456 | if (r->module_id == module_id) { | ||
| 9457 | RedisModuleCtx ctx; | ||
| 9458 | moduleCreateContext(&ctx, r->module, REDISMODULE_CTX_TEMP_CLIENT); | ||
| 9459 | r->callback(&ctx,sender_id,type,payload,len); | ||
| 9460 | moduleFreeContext(&ctx); | ||
| 9461 | return; | ||
| 9462 | } | ||
| 9463 | r = r->next; | ||
| 9464 | } | ||
| 9465 | } | ||
| 9466 | |||
| 9467 | /* Register a callback receiver for cluster messages of type 'type'. If there | ||
| 9468 | * was already a registered callback, this will replace the callback function | ||
| 9469 | * with the one provided, otherwise if the callback is set to NULL and there | ||
| 9470 | * is already a callback for this function, the callback is unregistered | ||
| 9471 | * (so this API call is also used in order to delete the receiver). */ | ||
| 9472 | void RM_RegisterClusterMessageReceiver(RedisModuleCtx *ctx, uint8_t type, RedisModuleClusterMessageReceiver callback) { | ||
| 9473 | if (!server.cluster_enabled) return; | ||
| 9474 | |||
| 9475 | uint64_t module_id = moduleTypeEncodeId(ctx->module->name,0); | ||
| 9476 | moduleClusterReceiver *r = clusterReceivers[type], *prev = NULL; | ||
| 9477 | while(r) { | ||
| 9478 | if (r->module_id == module_id) { | ||
| 9479 | /* Found! Set or delete. */ | ||
| 9480 | if (callback) { | ||
| 9481 | r->callback = callback; | ||
| 9482 | } else { | ||
| 9483 | /* Delete the receiver entry if the user is setting | ||
| 9484 | * it to NULL. Just unlink the receiver node from the | ||
| 9485 | * linked list. */ | ||
| 9486 | if (prev) | ||
| 9487 | prev->next = r->next; | ||
| 9488 | else | ||
| 9489 | clusterReceivers[type]->next = r->next; | ||
| 9490 | zfree(r); | ||
| 9491 | } | ||
| 9492 | return; | ||
| 9493 | } | ||
| 9494 | prev = r; | ||
| 9495 | r = r->next; | ||
| 9496 | } | ||
| 9497 | |||
| 9498 | /* Not found, let's add it. */ | ||
| 9499 | if (callback) { | ||
| 9500 | r = zmalloc(sizeof(*r)); | ||
| 9501 | r->module_id = module_id; | ||
| 9502 | r->module = ctx->module; | ||
| 9503 | r->callback = callback; | ||
| 9504 | r->next = clusterReceivers[type]; | ||
| 9505 | clusterReceivers[type] = r; | ||
| 9506 | } | ||
| 9507 | } | ||
| 9508 | |||
| 9509 | /* Send a message to all the nodes in the cluster if `target` is NULL, otherwise | ||
| 9510 | * at the specified target, which is a REDISMODULE_NODE_ID_LEN bytes node ID, as | ||
| 9511 | * returned by the receiver callback or by the nodes iteration functions. | ||
| 9512 | * | ||
| 9513 | * The function returns REDISMODULE_OK if the message was successfully sent, | ||
| 9514 | * otherwise if the node is not connected or such node ID does not map to any | ||
| 9515 | * known cluster node, REDISMODULE_ERR is returned. */ | ||
| 9516 | int RM_SendClusterMessage(RedisModuleCtx *ctx, const char *target_id, uint8_t type, const char *msg, uint32_t len) { | ||
| 9517 | if (!server.cluster_enabled) return REDISMODULE_ERR; | ||
| 9518 | uint64_t module_id = moduleTypeEncodeId(ctx->module->name,0); | ||
| 9519 | if (clusterSendModuleMessageToTarget(target_id,module_id,type,msg,len) == C_OK) | ||
| 9520 | return REDISMODULE_OK; | ||
| 9521 | else | ||
| 9522 | return REDISMODULE_ERR; | ||
| 9523 | } | ||
| 9524 | |||
| 9525 | /* Return an array of string pointers, each string pointer points to a cluster | ||
| 9526 | * node ID of exactly REDISMODULE_NODE_ID_LEN bytes (without any null term). | ||
| 9527 | * The number of returned node IDs is stored into `*numnodes`. | ||
| 9528 | * However if this function is called by a module not running an a Redis | ||
| 9529 | * instance with Redis Cluster enabled, NULL is returned instead. | ||
| 9530 | * | ||
| 9531 | * The IDs returned can be used with RedisModule_GetClusterNodeInfo() in order | ||
| 9532 | * to get more information about single node. | ||
| 9533 | * | ||
| 9534 | * The array returned by this function must be freed using the function | ||
| 9535 | * RedisModule_FreeClusterNodesList(). | ||
| 9536 | * | ||
| 9537 | * Example: | ||
| 9538 | * | ||
| 9539 | * size_t count, j; | ||
| 9540 | * char **ids = RedisModule_GetClusterNodesList(ctx,&count); | ||
| 9541 | * for (j = 0; j < count; j++) { | ||
| 9542 | * RedisModule_Log(ctx,"notice","Node %.*s", | ||
| 9543 | * REDISMODULE_NODE_ID_LEN,ids[j]); | ||
| 9544 | * } | ||
| 9545 | * RedisModule_FreeClusterNodesList(ids); | ||
| 9546 | */ | ||
| 9547 | char **RM_GetClusterNodesList(RedisModuleCtx *ctx, size_t *numnodes) { | ||
| 9548 | UNUSED(ctx); | ||
| 9549 | |||
| 9550 | if (!server.cluster_enabled) return NULL; | ||
| 9551 | return getClusterNodesList(numnodes); | ||
| 9552 | } | ||
| 9553 | |||
| 9554 | /* Free the node list obtained with RedisModule_GetClusterNodesList. */ | ||
| 9555 | void RM_FreeClusterNodesList(char **ids) { | ||
| 9556 | if (ids == NULL) return; | ||
| 9557 | for (int j = 0; ids[j]; j++) zfree(ids[j]); | ||
| 9558 | zfree(ids); | ||
| 9559 | } | ||
| 9560 | |||
| 9561 | /* Return this node ID (REDISMODULE_CLUSTER_ID_LEN bytes) or NULL if the cluster | ||
| 9562 | * is disabled. */ | ||
| 9563 | const char *RM_GetMyClusterID(void) { | ||
| 9564 | if (!server.cluster_enabled) return NULL; | ||
| 9565 | return getMyClusterId(); | ||
| 9566 | } | ||
| 9567 | |||
| 9568 | /* Return the number of nodes in the cluster, regardless of their state | ||
| 9569 | * (handshake, noaddress, ...) so that the number of active nodes may actually | ||
| 9570 | * be smaller, but not greater than this number. If the instance is not in | ||
| 9571 | * cluster mode, zero is returned. */ | ||
| 9572 | size_t RM_GetClusterSize(void) { | ||
| 9573 | if (!server.cluster_enabled) return 0; | ||
| 9574 | return getClusterSize(); | ||
| 9575 | } | ||
| 9576 | |||
| 9577 | /* Populate the specified info for the node having as ID the specified 'id', | ||
| 9578 | * then returns REDISMODULE_OK. Otherwise if the format of node ID is invalid | ||
| 9579 | * or the node ID does not exist from the POV of this local node, REDISMODULE_ERR | ||
| 9580 | * is returned. | ||
| 9581 | * | ||
| 9582 | * The arguments `ip`, `master_id`, `port` and `flags` can be NULL in case we don't | ||
| 9583 | * need to populate back certain info. If an `ip` and `master_id` (only populated | ||
| 9584 | * if the instance is a slave) are specified, they point to buffers holding | ||
| 9585 | * at least REDISMODULE_NODE_ID_LEN bytes. The strings written back as `ip` | ||
| 9586 | * and `master_id` are not null terminated. | ||
| 9587 | * | ||
| 9588 | * The list of flags reported is the following: | ||
| 9589 | * | ||
| 9590 | * * REDISMODULE_NODE_MYSELF: This node | ||
| 9591 | * * REDISMODULE_NODE_MASTER: The node is a master | ||
| 9592 | * * REDISMODULE_NODE_SLAVE: The node is a replica | ||
| 9593 | * * REDISMODULE_NODE_PFAIL: We see the node as failing | ||
| 9594 | * * REDISMODULE_NODE_FAIL: The cluster agrees the node is failing | ||
| 9595 | * * REDISMODULE_NODE_NOFAILOVER: The slave is configured to never failover | ||
| 9596 | */ | ||
| 9597 | int RM_GetClusterNodeInfo(RedisModuleCtx *ctx, const char *id, char *ip, char *master_id, int *port, int *flags) { | ||
| 9598 | UNUSED(ctx); | ||
| 9599 | |||
| 9600 | clusterNode *node = clusterLookupNode(id, CLUSTER_NAMELEN); | ||
| 9601 | if (node == NULL || clusterNodePending(node)) | ||
| 9602 | { | ||
| 9603 | return REDISMODULE_ERR; | ||
| 9604 | } | ||
| 9605 | |||
| 9606 | if (ip) redis_strlcpy(ip, clusterNodeIp(node),NET_IP_STR_LEN); | ||
| 9607 | |||
| 9608 | if (master_id) { | ||
| 9609 | /* If the information is not available, the function will set the | ||
| 9610 | * field to zero bytes, so that when the field can't be populated the | ||
| 9611 | * function kinda remains predictable. */ | ||
| 9612 | if (clusterNodeIsSlave(node) && clusterNodeGetSlaveof(node)) | ||
| 9613 | memcpy(master_id, clusterNodeGetName(clusterNodeGetSlaveof(node)) ,REDISMODULE_NODE_ID_LEN); | ||
| 9614 | else | ||
| 9615 | memset(master_id,0,REDISMODULE_NODE_ID_LEN); | ||
| 9616 | } | ||
| 9617 | if (port) *port = getNodeDefaultClientPort(node); | ||
| 9618 | |||
| 9619 | /* As usually we have to remap flags for modules, in order to ensure | ||
| 9620 | * we can provide binary compatibility. */ | ||
| 9621 | if (flags) { | ||
| 9622 | *flags = 0; | ||
| 9623 | if (clusterNodeIsMyself(node)) *flags |= REDISMODULE_NODE_MYSELF; | ||
| 9624 | if (clusterNodeIsMaster(node)) *flags |= REDISMODULE_NODE_MASTER; | ||
| 9625 | if (clusterNodeIsSlave(node)) *flags |= REDISMODULE_NODE_SLAVE; | ||
| 9626 | if (clusterNodeTimedOut(node)) *flags |= REDISMODULE_NODE_PFAIL; | ||
| 9627 | if (clusterNodeIsFailing(node)) *flags |= REDISMODULE_NODE_FAIL; | ||
| 9628 | if (clusterNodeIsNoFailover(node)) *flags |= REDISMODULE_NODE_NOFAILOVER; | ||
| 9629 | } | ||
| 9630 | return REDISMODULE_OK; | ||
| 9631 | } | ||
| 9632 | |||
| 9633 | /* Set Redis Cluster flags in order to change the normal behavior of | ||
| 9634 | * Redis Cluster, especially with the goal of disabling certain functions. | ||
| 9635 | * This is useful for modules that use the Cluster API in order to create | ||
| 9636 | * a different distributed system, but still want to use the Redis Cluster | ||
| 9637 | * message bus. Flags that can be set: | ||
| 9638 | * | ||
| 9639 | * * CLUSTER_MODULE_FLAG_NO_FAILOVER | ||
| 9640 | * * CLUSTER_MODULE_FLAG_NO_REDIRECTION | ||
| 9641 | * | ||
| 9642 | * With the following effects: | ||
| 9643 | * | ||
| 9644 | * * NO_FAILOVER: prevent Redis Cluster slaves from failing over a dead master. | ||
| 9645 | * Also disables the replica migration feature. | ||
| 9646 | * | ||
| 9647 | * * NO_REDIRECTION: Every node will accept any key, without trying to perform | ||
| 9648 | * partitioning according to the Redis Cluster algorithm. | ||
| 9649 | * Slots information will still be propagated across the | ||
| 9650 | * cluster, but without effect. */ | ||
| 9651 | void RM_SetClusterFlags(RedisModuleCtx *ctx, uint64_t flags) { | ||
| 9652 | UNUSED(ctx); | ||
| 9653 | server.cluster_module_flags = CLUSTER_MODULE_FLAG_NONE; | ||
| 9654 | if (flags & REDISMODULE_CLUSTER_FLAG_NO_FAILOVER) | ||
| 9655 | server.cluster_module_flags |= CLUSTER_MODULE_FLAG_NO_FAILOVER; | ||
| 9656 | if (flags & REDISMODULE_CLUSTER_FLAG_NO_REDIRECTION) | ||
| 9657 | server.cluster_module_flags |= CLUSTER_MODULE_FLAG_NO_REDIRECTION; | ||
| 9658 | } | ||
| 9659 | |||
| 9660 | /* RM_ClusterDisableTrim allows a module to temporarily prevent slot trimming | ||
| 9661 | * after a slot migration. This is useful when the module has asynchronous | ||
| 9662 | * operations that rely on keys in migrating slots, which would be trimmed. | ||
| 9663 | * | ||
| 9664 | * The module must call RM_ClusterEnableTrim once it has completed those | ||
| 9665 | * operations to re-enable trimming. | ||
| 9666 | * | ||
| 9667 | * Trimming uses a reference counter: every call to RM_ClusterDisableTrim | ||
| 9668 | * increments the counter, and every RM_ClusterEnableTrim call decrements it. | ||
| 9669 | * Trimming remains disabled as long as the counter is greater than zero. | ||
| 9670 | * | ||
| 9671 | * Disable automatic slot trimming. */ | ||
| 9672 | int RM_ClusterDisableTrim(RedisModuleCtx *ctx) { | ||
| 9673 | UNUSED(ctx); | ||
| 9674 | if (server.cluster_module_trim_disablers < INT_MAX) { | ||
| 9675 | server.cluster_module_trim_disablers++; | ||
| 9676 | return REDISMODULE_OK; | ||
| 9677 | } | ||
| 9678 | return REDISMODULE_ERR; | ||
| 9679 | } | ||
| 9680 | |||
| 9681 | /* Enable automatic slot trimming. See also comments on RM_ClusterDisableTrim. */ | ||
| 9682 | int RM_ClusterEnableTrim(RedisModuleCtx *ctx) { | ||
| 9683 | UNUSED(ctx); | ||
| 9684 | if (server.cluster_module_trim_disablers > 0) { | ||
| 9685 | server.cluster_module_trim_disablers--; | ||
| 9686 | return REDISMODULE_OK; | ||
| 9687 | } | ||
| 9688 | return REDISMODULE_ERR; | ||
| 9689 | } | ||
| 9690 | |||
| 9691 | /* Returns the cluster slot of a key, similar to the `CLUSTER KEYSLOT` command. | ||
| 9692 | * This function works even if cluster mode is not enabled. */ | ||
| 9693 | unsigned int RM_ClusterKeySlot(RedisModuleString *key) { | ||
| 9694 | return keyHashSlot(key->ptr, sdslen(key->ptr)); | ||
| 9695 | } | ||
| 9696 | |||
| 9697 | /* Like `RM_ClusterKeySlot`, but gets a char pointer and a length. | ||
| 9698 | * Returns the cluster slot of a key, similar to the `CLUSTER KEYSLOT` command. | ||
| 9699 | * This function works even if cluster mode is not enabled. */ | ||
| 9700 | unsigned int RM_ClusterKeySlotC(const char *keystr, size_t keylen) { | ||
| 9701 | return keyHashSlot(keystr, keylen); | ||
| 9702 | } | ||
| 9703 | |||
| 9704 | /* Returns a short string that can be used as a key or as a hash tag in a key, | ||
| 9705 | * such that the key maps to the given cluster slot. Returns NULL if slot is not | ||
| 9706 | * a valid slot. */ | ||
| 9707 | const char *RM_ClusterCanonicalKeyNameInSlot(unsigned int slot) { | ||
| 9708 | return (slot < CLUSTER_SLOTS) ? crc16_slot_table[slot] : NULL; | ||
| 9709 | } | ||
| 9710 | |||
| 9711 | /* Returns 1 if keys in the specified slot can be accessed by this node, 0 otherwise. | ||
| 9712 | * | ||
| 9713 | * This function returns 1 in the following cases: | ||
| 9714 | * - The slot is owned by this node or by its master if this node is a replica | ||
| 9715 | * - The slot is being imported under the old slot migration approach (CLUSTER SETSLOT <slot> IMPORTING ..) | ||
| 9716 | * - Not in cluster mode (all slots are accessible) | ||
| 9717 | * | ||
| 9718 | * Returns 0 for: | ||
| 9719 | * - Invalid slot numbers (< 0 or >= 16384) | ||
| 9720 | * - Slots owned by other nodes | ||
| 9721 | */ | ||
| 9722 | int RM_ClusterCanAccessKeysInSlot(int slot) { | ||
| 9723 | if (slot < 0 || slot >= CLUSTER_SLOTS) return 0; | ||
| 9724 | return clusterCanAccessKeysInSlot(slot); | ||
| 9725 | } | ||
| 9726 | |||
| 9727 | /* Propagate commands along with slot migration. | ||
| 9728 | * | ||
| 9729 | * This function allows modules to add commands that will be sent to the | ||
| 9730 | * destination node before the actual slot migration begins. It should only be | ||
| 9731 | * called during the REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_MODULE_PROPAGATE event. | ||
| 9732 | * | ||
| 9733 | * This function can be called multiple times within the same event to | ||
| 9734 | * replicate multiple commands. All commands will be sent before the | ||
| 9735 | * actual slot data migration begins. | ||
| 9736 | * | ||
| 9737 | * Note: This function is only available in the fork child process just before | ||
| 9738 | * slot snapshot delivery begins. | ||
| 9739 | * | ||
| 9740 | * On success REDISMODULE_OK is returned, otherwise | ||
| 9741 | * REDISMODULE_ERR is returned and errno is set to the following values: | ||
| 9742 | * | ||
| 9743 | * * EINVAL: function arguments or format specifiers are invalid. | ||
| 9744 | * * EBADF: not called in the correct context, e.g. not called in the REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_MODULE_PROPAGATE event. | ||
| 9745 | * * ENOENT: command does not exist. | ||
| 9746 | * * ENOTSUP: command is cross-slot. | ||
| 9747 | * * ERANGE: command contains keys that are not within the migrating slot range. | ||
| 9748 | */ | ||
| 9749 | int RM_ClusterPropagateForSlotMigration(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...) { | ||
| 9750 | int argc = 0, flags = 0; | ||
| 9751 | robj **argv = NULL; | ||
| 9752 | struct redisCommand *cmd; | ||
| 9753 | va_list ap; | ||
| 9754 | |||
| 9755 | if (ctx == NULL || cmdname == NULL || fmt == NULL) { | ||
| 9756 | errno = EINVAL; | ||
| 9757 | return REDISMODULE_ERR; | ||
| 9758 | } | ||
| 9759 | |||
| 9760 | errno = 0; | ||
| 9761 | cmd = lookupCommandByCString((char*)cmdname); | ||
| 9762 | if (!cmd) { | ||
| 9763 | errno = ENOENT; | ||
| 9764 | return REDISMODULE_ERR; | ||
| 9765 | } | ||
| 9766 | |||
| 9767 | va_start(ap, fmt); | ||
| 9768 | argv = moduleCreateArgvFromUserFormat(cmdname, fmt, &argc, &flags, ap); | ||
| 9769 | va_end(ap); | ||
| 9770 | if (argv == NULL) { | ||
| 9771 | errno = EINVAL; | ||
| 9772 | return REDISMODULE_ERR; | ||
| 9773 | } | ||
| 9774 | |||
| 9775 | int ret = asmModulePropagateBeforeSlotSnapshot(cmd, argv, argc); | ||
| 9776 | int saved_errno = errno; | ||
| 9777 | |||
| 9778 | /* Release the argv. */ | ||
| 9779 | for (int i = 0; i < argc; i++) decrRefCount(argv[i]); | ||
| 9780 | zfree(argv); | ||
| 9781 | errno = saved_errno; | ||
| 9782 | return ret == C_OK ? REDISMODULE_OK : REDISMODULE_ERR; | ||
| 9783 | } | ||
| 9784 | |||
| 9785 | /* Returns the locally owned slot ranges for the node. | ||
| 9786 | * | ||
| 9787 | * An optional `ctx` can be provided to enable auto-memory management. | ||
| 9788 | * If cluster mode is disabled, the array will include all slots (0–16383). | ||
| 9789 | * If the node is a replica, the slot ranges of its master are returned. | ||
| 9790 | * | ||
| 9791 | * The returned array must be freed with RM_ClusterFreeSlotRanges(). | ||
| 9792 | */ | ||
| 9793 | RedisModuleSlotRangeArray *RM_ClusterGetLocalSlotRanges(RedisModuleCtx *ctx) { | ||
| 9794 | slotRangeArray *slots = clusterGetLocalSlotRanges(); | ||
| 9795 | if (ctx) autoMemoryAdd(ctx, REDISMODULE_AM_SLOTRANGEARRAY, slots); | ||
| 9796 | return (RedisModuleSlotRangeArray *)slots; | ||
| 9797 | } | ||
| 9798 | |||
| 9799 | /* Frees a slot range array returned by RM_ClusterGetLocalSlotRanges(). | ||
| 9800 | * Pass the `ctx` pointer only if the array was created with a context. */ | ||
| 9801 | void RM_ClusterFreeSlotRanges(RedisModuleCtx *ctx, RedisModuleSlotRangeArray *slots) { | ||
| 9802 | if (ctx) autoMemoryFreed(ctx, REDISMODULE_AM_SLOTRANGEARRAY, slots); | ||
| 9803 | slotRangeArrayFree((slotRangeArray *)slots); | ||
| 9804 | } | ||
| 9805 | |||
| 9806 | /* -------------------------------------------------------------------------- | ||
| 9807 | * ## Modules Timers API | ||
| 9808 | * | ||
| 9809 | * Module timers are a high precision "green timers" abstraction where | ||
| 9810 | * every module can register even millions of timers without problems, even if | ||
| 9811 | * the actual event loop will just have a single timer that is used to awake the | ||
| 9812 | * module timers subsystem in order to process the next event. | ||
| 9813 | * | ||
| 9814 | * All the timers are stored into a radix tree, ordered by expire time, when | ||
| 9815 | * the main Redis event loop timer callback is called, we try to process all | ||
| 9816 | * the timers already expired one after the other. Then we re-enter the event | ||
| 9817 | * loop registering a timer that will expire when the next to process module | ||
| 9818 | * timer will expire. | ||
| 9819 | * | ||
| 9820 | * Every time the list of active timers drops to zero, we unregister the | ||
| 9821 | * main event loop timer, so that there is no overhead when such feature is | ||
| 9822 | * not used. | ||
| 9823 | * -------------------------------------------------------------------------- */ | ||
| 9824 | |||
| 9825 | static rax *Timers; /* The radix tree of all the timers sorted by expire. */ | ||
| 9826 | long long aeTimer = -1; /* Main event loop (ae.c) timer identifier. */ | ||
| 9827 | |||
| 9828 | typedef void (*RedisModuleTimerProc)(RedisModuleCtx *ctx, void *data); | ||
| 9829 | |||
| 9830 | /* The timer descriptor, stored as value in the radix tree. */ | ||
| 9831 | typedef struct RedisModuleTimer { | ||
| 9832 | RedisModule *module; /* Module reference. */ | ||
| 9833 | RedisModuleTimerProc callback; /* The callback to invoke on expire. */ | ||
| 9834 | void *data; /* Private data for the callback. */ | ||
| 9835 | int dbid; /* Database number selected by the original client. */ | ||
| 9836 | } RedisModuleTimer; | ||
| 9837 | |||
| 9838 | /* This is the timer handler that is called by the main event loop. We schedule | ||
| 9839 | * this timer to be called when the nearest of our module timers will expire. */ | ||
| 9840 | int moduleTimerHandler(struct aeEventLoop *eventLoop, long long id, void *clientData) { | ||
| 9841 | UNUSED(eventLoop); | ||
| 9842 | UNUSED(id); | ||
| 9843 | UNUSED(clientData); | ||
| 9844 | |||
| 9845 | /* To start let's try to fire all the timers already expired. */ | ||
| 9846 | raxIterator ri; | ||
| 9847 | raxStart(&ri,Timers); | ||
| 9848 | uint64_t now = ustime(); | ||
| 9849 | long long next_period = 0; | ||
| 9850 | while(1) { | ||
| 9851 | raxSeek(&ri,"^",NULL,0); | ||
| 9852 | if (!raxNext(&ri)) break; | ||
| 9853 | uint64_t expiretime; | ||
| 9854 | memcpy(&expiretime,ri.key,sizeof(expiretime)); | ||
| 9855 | expiretime = ntohu64(expiretime); | ||
| 9856 | if (now >= expiretime) { | ||
| 9857 | RedisModuleTimer *timer = ri.data; | ||
| 9858 | RedisModuleCtx ctx; | ||
| 9859 | moduleCreateContext(&ctx,timer->module,REDISMODULE_CTX_TEMP_CLIENT); | ||
| 9860 | selectDb(ctx.client, timer->dbid); | ||
| 9861 | timer->callback(&ctx,timer->data); | ||
| 9862 | moduleFreeContext(&ctx); | ||
| 9863 | raxRemove(Timers,(unsigned char*)ri.key,ri.key_len,NULL); | ||
| 9864 | zfree(timer); | ||
| 9865 | } else { | ||
| 9866 | /* We call ustime() again instead of using the cached 'now' so that | ||
| 9867 | * 'next_period' isn't affected by the time it took to execute | ||
| 9868 | * previous calls to 'callback. | ||
| 9869 | * We need to cast 'expiretime' so that the compiler will not treat | ||
| 9870 | * the difference as unsigned (Causing next_period to be huge) in | ||
| 9871 | * case expiretime < ustime() */ | ||
| 9872 | next_period = ((long long)expiretime-ustime())/1000; /* Scale to milliseconds. */ | ||
| 9873 | break; | ||
| 9874 | } | ||
| 9875 | } | ||
| 9876 | raxStop(&ri); | ||
| 9877 | |||
| 9878 | /* Reschedule the next timer or cancel it. */ | ||
| 9879 | if (next_period <= 0) next_period = 1; | ||
| 9880 | if (raxSize(Timers) > 0) { | ||
| 9881 | return next_period; | ||
| 9882 | } else { | ||
| 9883 | aeTimer = -1; | ||
| 9884 | return AE_NOMORE; | ||
| 9885 | } | ||
| 9886 | } | ||
| 9887 | |||
| 9888 | /* Create a new timer that will fire after `period` milliseconds, and will call | ||
| 9889 | * the specified function using `data` as argument. The returned timer ID can be | ||
| 9890 | * used to get information from the timer or to stop it before it fires. | ||
| 9891 | * Note that for the common use case of a repeating timer (Re-registration | ||
| 9892 | * of the timer inside the RedisModuleTimerProc callback) it matters when | ||
| 9893 | * this API is called: | ||
| 9894 | * If it is called at the beginning of 'callback' it means | ||
| 9895 | * the event will triggered every 'period'. | ||
| 9896 | * If it is called at the end of 'callback' it means | ||
| 9897 | * there will 'period' milliseconds gaps between events. | ||
| 9898 | * (If the time it takes to execute 'callback' is negligible the two | ||
| 9899 | * statements above mean the same) */ | ||
| 9900 | RedisModuleTimerID RM_CreateTimer(RedisModuleCtx *ctx, mstime_t period, RedisModuleTimerProc callback, void *data) { | ||
| 9901 | RedisModuleTimer *timer = zmalloc(sizeof(*timer)); | ||
| 9902 | timer->module = ctx->module; | ||
| 9903 | timer->callback = callback; | ||
| 9904 | timer->data = data; | ||
| 9905 | timer->dbid = ctx->client ? ctx->client->db->id : 0; | ||
| 9906 | uint64_t expiretime = ustime()+period*1000; | ||
| 9907 | uint64_t key; | ||
| 9908 | |||
| 9909 | while(1) { | ||
| 9910 | key = htonu64(expiretime); | ||
| 9911 | if (!raxFind(Timers, (unsigned char*)&key,sizeof(key),NULL)) { | ||
| 9912 | raxInsert(Timers,(unsigned char*)&key,sizeof(key),timer,NULL); | ||
| 9913 | break; | ||
| 9914 | } else { | ||
| 9915 | expiretime++; | ||
| 9916 | } | ||
| 9917 | } | ||
| 9918 | |||
| 9919 | /* We need to install the main event loop timer if it's not already | ||
| 9920 | * installed, or we may need to refresh its period if we just installed | ||
| 9921 | * a timer that will expire sooner than any other else (i.e. the timer | ||
| 9922 | * we just installed is the first timer in the Timers rax). */ | ||
| 9923 | if (aeTimer != -1) { | ||
| 9924 | raxIterator ri; | ||
| 9925 | raxStart(&ri,Timers); | ||
| 9926 | raxSeek(&ri,"^",NULL,0); | ||
| 9927 | raxNext(&ri); | ||
| 9928 | if (memcmp(ri.key,&key,sizeof(key)) == 0) { | ||
| 9929 | /* This is the first key, we need to re-install the timer according | ||
| 9930 | * to the just added event. */ | ||
| 9931 | aeDeleteTimeEvent(server.el,aeTimer); | ||
| 9932 | aeTimer = -1; | ||
| 9933 | } | ||
| 9934 | raxStop(&ri); | ||
| 9935 | } | ||
| 9936 | |||
| 9937 | /* If we have no main timer (the old one was invalidated, or this is the | ||
| 9938 | * first module timer we have), install one. */ | ||
| 9939 | if (aeTimer == -1) | ||
| 9940 | aeTimer = aeCreateTimeEvent(server.el,period,moduleTimerHandler,NULL,NULL); | ||
| 9941 | |||
| 9942 | return key; | ||
| 9943 | } | ||
| 9944 | |||
| 9945 | /* Stop a timer, returns REDISMODULE_OK if the timer was found, belonged to the | ||
| 9946 | * calling module, and was stopped, otherwise REDISMODULE_ERR is returned. | ||
| 9947 | * If not NULL, the data pointer is set to the value of the data argument when | ||
| 9948 | * the timer was created. */ | ||
| 9949 | int RM_StopTimer(RedisModuleCtx *ctx, RedisModuleTimerID id, void **data) { | ||
| 9950 | void *result; | ||
| 9951 | if (!raxFind(Timers,(unsigned char*)&id,sizeof(id),&result)) | ||
| 9952 | return REDISMODULE_ERR; | ||
| 9953 | RedisModuleTimer *timer = result; | ||
| 9954 | if (timer->module != ctx->module) | ||
| 9955 | return REDISMODULE_ERR; | ||
| 9956 | if (data) *data = timer->data; | ||
| 9957 | raxRemove(Timers,(unsigned char*)&id,sizeof(id),NULL); | ||
| 9958 | zfree(timer); | ||
| 9959 | return REDISMODULE_OK; | ||
| 9960 | } | ||
| 9961 | |||
| 9962 | /* Obtain information about a timer: its remaining time before firing | ||
| 9963 | * (in milliseconds), and the private data pointer associated with the timer. | ||
| 9964 | * If the timer specified does not exist or belongs to a different module | ||
| 9965 | * no information is returned and the function returns REDISMODULE_ERR, otherwise | ||
| 9966 | * REDISMODULE_OK is returned. The arguments remaining or data can be NULL if | ||
| 9967 | * the caller does not need certain information. */ | ||
| 9968 | int RM_GetTimerInfo(RedisModuleCtx *ctx, RedisModuleTimerID id, uint64_t *remaining, void **data) { | ||
| 9969 | void *result; | ||
| 9970 | if (!raxFind(Timers,(unsigned char*)&id,sizeof(id),&result)) | ||
| 9971 | return REDISMODULE_ERR; | ||
| 9972 | RedisModuleTimer *timer = result; | ||
| 9973 | if (timer->module != ctx->module) | ||
| 9974 | return REDISMODULE_ERR; | ||
| 9975 | if (remaining) { | ||
| 9976 | int64_t rem = ntohu64(id)-ustime(); | ||
| 9977 | if (rem < 0) rem = 0; | ||
| 9978 | *remaining = rem/1000; /* Scale to milliseconds. */ | ||
| 9979 | } | ||
| 9980 | if (data) *data = timer->data; | ||
| 9981 | return REDISMODULE_OK; | ||
| 9982 | } | ||
| 9983 | |||
| 9984 | /* Query timers to see if any timer belongs to the module. | ||
| 9985 | * Return 1 if any timer was found, otherwise 0 would be returned. */ | ||
| 9986 | int moduleHoldsTimer(struct RedisModule *module) { | ||
| 9987 | raxIterator iter; | ||
| 9988 | int found = 0; | ||
| 9989 | raxStart(&iter,Timers); | ||
| 9990 | raxSeek(&iter,"^",NULL,0); | ||
| 9991 | while (raxNext(&iter)) { | ||
| 9992 | RedisModuleTimer *timer = iter.data; | ||
| 9993 | if (timer->module == module) { | ||
| 9994 | found = 1; | ||
| 9995 | break; | ||
| 9996 | } | ||
| 9997 | } | ||
| 9998 | raxStop(&iter); | ||
| 9999 | return found; | ||
| 10000 | } | ||
| 10001 | |||
| 10002 | /* -------------------------------------------------------------------------- | ||
| 10003 | * ## Modules EventLoop API | ||
| 10004 | * --------------------------------------------------------------------------*/ | ||
| 10005 | |||
| 10006 | typedef struct EventLoopData { | ||
| 10007 | RedisModuleEventLoopFunc rFunc; | ||
| 10008 | RedisModuleEventLoopFunc wFunc; | ||
| 10009 | void *user_data; | ||
| 10010 | } EventLoopData; | ||
| 10011 | |||
| 10012 | typedef struct EventLoopOneShot { | ||
| 10013 | RedisModuleEventLoopOneShotFunc func; | ||
| 10014 | void *user_data; | ||
| 10015 | } EventLoopOneShot; | ||
| 10016 | |||
| 10017 | list *moduleEventLoopOneShots; | ||
| 10018 | static pthread_mutex_t moduleEventLoopMutex = PTHREAD_MUTEX_INITIALIZER; | ||
| 10019 | |||
| 10020 | static int eventLoopToAeMask(int mask) { | ||
| 10021 | int aeMask = 0; | ||
| 10022 | if (mask & REDISMODULE_EVENTLOOP_READABLE) | ||
| 10023 | aeMask |= AE_READABLE; | ||
| 10024 | if (mask & REDISMODULE_EVENTLOOP_WRITABLE) | ||
| 10025 | aeMask |= AE_WRITABLE; | ||
| 10026 | return aeMask; | ||
| 10027 | } | ||
| 10028 | |||
| 10029 | static int eventLoopFromAeMask(int ae_mask) { | ||
| 10030 | int mask = 0; | ||
| 10031 | if (ae_mask & AE_READABLE) | ||
| 10032 | mask |= REDISMODULE_EVENTLOOP_READABLE; | ||
| 10033 | if (ae_mask & AE_WRITABLE) | ||
| 10034 | mask |= REDISMODULE_EVENTLOOP_WRITABLE; | ||
| 10035 | return mask; | ||
| 10036 | } | ||
| 10037 | |||
| 10038 | static void eventLoopCbReadable(struct aeEventLoop *ae, int fd, void *user_data, int ae_mask) { | ||
| 10039 | UNUSED(ae); | ||
| 10040 | EventLoopData *data = user_data; | ||
| 10041 | data->rFunc(fd, data->user_data, eventLoopFromAeMask(ae_mask)); | ||
| 10042 | } | ||
| 10043 | |||
| 10044 | static void eventLoopCbWritable(struct aeEventLoop *ae, int fd, void *user_data, int ae_mask) { | ||
| 10045 | UNUSED(ae); | ||
| 10046 | EventLoopData *data = user_data; | ||
| 10047 | data->wFunc(fd, data->user_data, eventLoopFromAeMask(ae_mask)); | ||
| 10048 | } | ||
| 10049 | |||
| 10050 | /* Add a pipe / socket event to the event loop. | ||
| 10051 | * | ||
| 10052 | * * `mask` must be one of the following values: | ||
| 10053 | * | ||
| 10054 | * * `REDISMODULE_EVENTLOOP_READABLE` | ||
| 10055 | * * `REDISMODULE_EVENTLOOP_WRITABLE` | ||
| 10056 | * * `REDISMODULE_EVENTLOOP_READABLE | REDISMODULE_EVENTLOOP_WRITABLE` | ||
| 10057 | * | ||
| 10058 | * On success REDISMODULE_OK is returned, otherwise | ||
| 10059 | * REDISMODULE_ERR is returned and errno is set to the following values: | ||
| 10060 | * | ||
| 10061 | * * ERANGE: `fd` is negative or higher than `maxclients` Redis config. | ||
| 10062 | * * EINVAL: `callback` is NULL or `mask` value is invalid. | ||
| 10063 | * | ||
| 10064 | * `errno` might take other values in case of an internal error. | ||
| 10065 | * | ||
| 10066 | * Example: | ||
| 10067 | * | ||
| 10068 | * void onReadable(int fd, void *user_data, int mask) { | ||
| 10069 | * char buf[32]; | ||
| 10070 | * int bytes = read(fd,buf,sizeof(buf)); | ||
| 10071 | * printf("Read %d bytes \n", bytes); | ||
| 10072 | * } | ||
| 10073 | * RM_EventLoopAdd(fd, REDISMODULE_EVENTLOOP_READABLE, onReadable, NULL); | ||
| 10074 | */ | ||
| 10075 | int RM_EventLoopAdd(int fd, int mask, RedisModuleEventLoopFunc func, void *user_data) { | ||
| 10076 | if (fd < 0 || fd >= aeGetSetSize(server.el)) { | ||
| 10077 | errno = ERANGE; | ||
| 10078 | return REDISMODULE_ERR; | ||
| 10079 | } | ||
| 10080 | |||
| 10081 | if (!func || mask & ~(REDISMODULE_EVENTLOOP_READABLE | | ||
| 10082 | REDISMODULE_EVENTLOOP_WRITABLE)) { | ||
| 10083 | errno = EINVAL; | ||
| 10084 | return REDISMODULE_ERR; | ||
| 10085 | } | ||
| 10086 | |||
| 10087 | /* We are going to register stub callbacks to 'ae' for two reasons: | ||
| 10088 | * | ||
| 10089 | * - "ae" callback signature is different from RedisModuleEventLoopCallback, | ||
| 10090 | * that will be handled it in our stub callbacks. | ||
| 10091 | * - We need to remap 'mask' value to provide binary compatibility. | ||
| 10092 | * | ||
| 10093 | * For the stub callbacks, saving user 'callback' and 'user_data' in an | ||
| 10094 | * EventLoopData object and passing it to ae, later, we'll extract | ||
| 10095 | * 'callback' and 'user_data' from that. | ||
| 10096 | */ | ||
| 10097 | EventLoopData *data = aeGetFileClientData(server.el, fd); | ||
| 10098 | if (!data) | ||
| 10099 | data = zcalloc(sizeof(*data)); | ||
| 10100 | |||
| 10101 | aeFileProc *aeProc; | ||
| 10102 | if (mask & REDISMODULE_EVENTLOOP_READABLE) | ||
| 10103 | aeProc = eventLoopCbReadable; | ||
| 10104 | else | ||
| 10105 | aeProc = eventLoopCbWritable; | ||
| 10106 | |||
| 10107 | int aeMask = eventLoopToAeMask(mask); | ||
| 10108 | |||
| 10109 | if (aeCreateFileEvent(server.el, fd, aeMask, aeProc, data) != AE_OK) { | ||
| 10110 | if (aeGetFileEvents(server.el, fd) == AE_NONE) | ||
| 10111 | zfree(data); | ||
| 10112 | return REDISMODULE_ERR; | ||
| 10113 | } | ||
| 10114 | |||
| 10115 | data->user_data = user_data; | ||
| 10116 | if (mask & REDISMODULE_EVENTLOOP_READABLE) | ||
| 10117 | data->rFunc = func; | ||
| 10118 | if (mask & REDISMODULE_EVENTLOOP_WRITABLE) | ||
| 10119 | data->wFunc = func; | ||
| 10120 | |||
| 10121 | errno = 0; | ||
| 10122 | return REDISMODULE_OK; | ||
| 10123 | } | ||
| 10124 | |||
| 10125 | /* Delete a pipe / socket event from the event loop. | ||
| 10126 | * | ||
| 10127 | * * `mask` must be one of the following values: | ||
| 10128 | * | ||
| 10129 | * * `REDISMODULE_EVENTLOOP_READABLE` | ||
| 10130 | * * `REDISMODULE_EVENTLOOP_WRITABLE` | ||
| 10131 | * * `REDISMODULE_EVENTLOOP_READABLE | REDISMODULE_EVENTLOOP_WRITABLE` | ||
| 10132 | * | ||
| 10133 | * On success REDISMODULE_OK is returned, otherwise | ||
| 10134 | * REDISMODULE_ERR is returned and errno is set to the following values: | ||
| 10135 | * | ||
| 10136 | * * ERANGE: `fd` is negative or higher than `maxclients` Redis config. | ||
| 10137 | * * EINVAL: `mask` value is invalid. | ||
| 10138 | */ | ||
| 10139 | int RM_EventLoopDel(int fd, int mask) { | ||
| 10140 | if (fd < 0 || fd >= aeGetSetSize(server.el)) { | ||
| 10141 | errno = ERANGE; | ||
| 10142 | return REDISMODULE_ERR; | ||
| 10143 | } | ||
| 10144 | |||
| 10145 | if (mask & ~(REDISMODULE_EVENTLOOP_READABLE | | ||
| 10146 | REDISMODULE_EVENTLOOP_WRITABLE)) { | ||
| 10147 | errno = EINVAL; | ||
| 10148 | return REDISMODULE_ERR; | ||
| 10149 | } | ||
| 10150 | |||
| 10151 | /* After deleting the event, if fd does not have any registered event | ||
| 10152 | * anymore, we can free the EventLoopData object. */ | ||
| 10153 | EventLoopData *data = aeGetFileClientData(server.el, fd); | ||
| 10154 | aeDeleteFileEvent(server.el, fd, eventLoopToAeMask(mask)); | ||
| 10155 | if (aeGetFileEvents(server.el, fd) == AE_NONE) | ||
| 10156 | zfree(data); | ||
| 10157 | |||
| 10158 | errno = 0; | ||
| 10159 | return REDISMODULE_OK; | ||
| 10160 | } | ||
| 10161 | |||
| 10162 | /* This function can be called from other threads to trigger callback on Redis | ||
| 10163 | * main thread. On success REDISMODULE_OK is returned. If `func` is NULL | ||
| 10164 | * REDISMODULE_ERR is returned and errno is set to EINVAL. | ||
| 10165 | */ | ||
| 10166 | int RM_EventLoopAddOneShot(RedisModuleEventLoopOneShotFunc func, void *user_data) { | ||
| 10167 | if (!func) { | ||
| 10168 | errno = EINVAL; | ||
| 10169 | return REDISMODULE_ERR; | ||
| 10170 | } | ||
| 10171 | |||
| 10172 | EventLoopOneShot *oneshot = zmalloc(sizeof(*oneshot)); | ||
| 10173 | oneshot->func = func; | ||
| 10174 | oneshot->user_data = user_data; | ||
| 10175 | |||
| 10176 | pthread_mutex_lock(&moduleEventLoopMutex); | ||
| 10177 | if (!moduleEventLoopOneShots) moduleEventLoopOneShots = listCreate(); | ||
| 10178 | listAddNodeTail(moduleEventLoopOneShots, oneshot); | ||
| 10179 | pthread_mutex_unlock(&moduleEventLoopMutex); | ||
| 10180 | |||
| 10181 | if (write(server.module_pipe[1],"A",1) != 1) { | ||
| 10182 | /* Pipe is non-blocking, write() may fail if it's full. */ | ||
| 10183 | } | ||
| 10184 | |||
| 10185 | errno = 0; | ||
| 10186 | return REDISMODULE_OK; | ||
| 10187 | } | ||
| 10188 | |||
| 10189 | /* This function will check the moduleEventLoopOneShots queue in order to | ||
| 10190 | * call the callback for the registered oneshot events. */ | ||
| 10191 | static void eventLoopHandleOneShotEvents(void) { | ||
| 10192 | pthread_mutex_lock(&moduleEventLoopMutex); | ||
| 10193 | if (moduleEventLoopOneShots) { | ||
| 10194 | while (listLength(moduleEventLoopOneShots)) { | ||
| 10195 | listNode *ln = listFirst(moduleEventLoopOneShots); | ||
| 10196 | EventLoopOneShot *oneshot = ln->value; | ||
| 10197 | listDelNode(moduleEventLoopOneShots, ln); | ||
| 10198 | /* Unlock mutex before the callback. Another oneshot event can be | ||
| 10199 | * added in the callback, it will need to lock the mutex. */ | ||
| 10200 | pthread_mutex_unlock(&moduleEventLoopMutex); | ||
| 10201 | oneshot->func(oneshot->user_data); | ||
| 10202 | zfree(oneshot); | ||
| 10203 | /* Lock again for the next iteration */ | ||
| 10204 | pthread_mutex_lock(&moduleEventLoopMutex); | ||
| 10205 | } | ||
| 10206 | } | ||
| 10207 | pthread_mutex_unlock(&moduleEventLoopMutex); | ||
| 10208 | } | ||
| 10209 | |||
| 10210 | /* -------------------------------------------------------------------------- | ||
| 10211 | * ## Modules ACL API | ||
| 10212 | * | ||
| 10213 | * Implements a hook into the authentication and authorization within Redis. | ||
| 10214 | * --------------------------------------------------------------------------*/ | ||
| 10215 | |||
| 10216 | /* This function is called when a client's user has changed and invokes the | ||
| 10217 | * client's user changed callback if it was set. This callback should | ||
| 10218 | * cleanup any state the module was tracking about this client. | ||
| 10219 | * | ||
| 10220 | * A client's user can be changed through the AUTH command, module | ||
| 10221 | * authentication, and when a client is freed. */ | ||
| 10222 | void moduleNotifyUserChanged(client *c) { | ||
| 10223 | if (c->auth_callback) { | ||
| 10224 | c->auth_callback(c->id, c->auth_callback_privdata); | ||
| 10225 | |||
| 10226 | /* The callback will fire exactly once, even if the user remains | ||
| 10227 | * the same. It is expected to completely clean up the state | ||
| 10228 | * so all references are cleared here. */ | ||
| 10229 | c->auth_callback = NULL; | ||
| 10230 | c->auth_callback_privdata = NULL; | ||
| 10231 | c->auth_module = NULL; | ||
| 10232 | } | ||
| 10233 | } | ||
| 10234 | |||
| 10235 | void revokeClientAuthentication(client *c) { | ||
| 10236 | /* Freeing the client would result in moduleNotifyUserChanged() to be | ||
| 10237 | * called later, however since we use revokeClientAuthentication() also | ||
| 10238 | * in moduleFreeAuthenticatedClients() to implement module unloading, we | ||
| 10239 | * do this action ASAP: this way if the module is unloaded, when the client | ||
| 10240 | * is eventually freed we don't rely on the module to still exist. */ | ||
| 10241 | moduleNotifyUserChanged(c); | ||
| 10242 | |||
| 10243 | deauthenticateAndCloseClient(c); | ||
| 10244 | } | ||
| 10245 | |||
| 10246 | /* Cleanup all clients that have been authenticated with this module. This | ||
| 10247 | * is called from onUnload() to give the module a chance to cleanup any | ||
| 10248 | * resources associated with clients it has authenticated. */ | ||
| 10249 | static void moduleFreeAuthenticatedClients(RedisModule *module) { | ||
| 10250 | listIter li; | ||
| 10251 | listNode *ln; | ||
| 10252 | listRewind(server.clients,&li); | ||
| 10253 | while ((ln = listNext(&li)) != NULL) { | ||
| 10254 | client *c = listNodeValue(ln); | ||
| 10255 | if (!c->auth_module) continue; | ||
| 10256 | |||
| 10257 | RedisModule *auth_module = (RedisModule *) c->auth_module; | ||
| 10258 | if (auth_module == module) { | ||
| 10259 | revokeClientAuthentication(c); | ||
| 10260 | } | ||
| 10261 | } | ||
| 10262 | } | ||
| 10263 | |||
| 10264 | /* Creates a Redis ACL user that the module can use to authenticate a client. | ||
| 10265 | * After obtaining the user, the module should set what such user can do | ||
| 10266 | * using the RM_SetUserACL() function. Once configured, the user | ||
| 10267 | * can be used in order to authenticate a connection, with the specified | ||
| 10268 | * ACL rules, using the RedisModule_AuthClientWithUser() function. | ||
| 10269 | * | ||
| 10270 | * Note that: | ||
| 10271 | * | ||
| 10272 | * * Users created here are not listed by the ACL command. | ||
| 10273 | * * Users created here are not checked for duplicated name, so it's up to | ||
| 10274 | * the module calling this function to take care of not creating users | ||
| 10275 | * with the same name. | ||
| 10276 | * * The created user can be used to authenticate multiple Redis connections. | ||
| 10277 | * | ||
| 10278 | * The caller can later free the user using the function | ||
| 10279 | * RM_FreeModuleUser(). When this function is called, if there are | ||
| 10280 | * still clients authenticated with this user, they are disconnected. | ||
| 10281 | * The function to free the user should only be used when the caller really | ||
| 10282 | * wants to invalidate the user to define a new one with different | ||
| 10283 | * capabilities. */ | ||
| 10284 | RedisModuleUser *RM_CreateModuleUser(const char *name) { | ||
| 10285 | RedisModuleUser *new_user = zmalloc(sizeof(RedisModuleUser)); | ||
| 10286 | new_user->user = ACLCreateUnlinkedUser(); | ||
| 10287 | new_user->free_user = 1; | ||
| 10288 | |||
| 10289 | /* Free the previous temporarily assigned name to assign the new one */ | ||
| 10290 | sdsfree(new_user->user->name); | ||
| 10291 | new_user->user->name = sdsnew(name); | ||
| 10292 | return new_user; | ||
| 10293 | } | ||
| 10294 | |||
| 10295 | /* Frees a given user and disconnects all of the clients that have been | ||
| 10296 | * authenticated with it. See RM_CreateModuleUser for detailed usage.*/ | ||
| 10297 | int RM_FreeModuleUser(RedisModuleUser *user) { | ||
| 10298 | if (user->free_user) | ||
| 10299 | ACLFreeUserAndKillClients(user->user); | ||
| 10300 | zfree(user); | ||
| 10301 | return REDISMODULE_OK; | ||
| 10302 | } | ||
| 10303 | |||
| 10304 | /* Sets the permissions of a user created through the redis module | ||
| 10305 | * interface. The syntax is the same as ACL SETUSER, so refer to the | ||
| 10306 | * documentation in acl.c for more information. See RM_CreateModuleUser | ||
| 10307 | * for detailed usage. | ||
| 10308 | * | ||
| 10309 | * Returns REDISMODULE_OK on success and REDISMODULE_ERR on failure | ||
| 10310 | * and will set an errno describing why the operation failed. */ | ||
| 10311 | int RM_SetModuleUserACL(RedisModuleUser *user, const char* acl) { | ||
| 10312 | return ACLSetUser(user->user, acl, -1); | ||
| 10313 | } | ||
| 10314 | |||
| 10315 | /* Sets the permission of a user with a complete ACL string, such as one | ||
| 10316 | * would use on the redis ACL SETUSER command line API. This differs from | ||
| 10317 | * RM_SetModuleUserACL, which only takes single ACL operations at a time. | ||
| 10318 | * | ||
| 10319 | * Returns REDISMODULE_OK on success and REDISMODULE_ERR on failure | ||
| 10320 | * if a RedisModuleString is provided in error, a string describing the error | ||
| 10321 | * will be returned */ | ||
| 10322 | int RM_SetModuleUserACLString(RedisModuleCtx *ctx, RedisModuleUser *user, const char *acl, RedisModuleString **error) { | ||
| 10323 | serverAssert(user != NULL); | ||
| 10324 | |||
| 10325 | int argc; | ||
| 10326 | sds *argv = sdssplitargs(acl, &argc); | ||
| 10327 | |||
| 10328 | sds err = ACLStringSetUser(user->user, NULL, argv, argc); | ||
| 10329 | |||
| 10330 | sdsfreesplitres(argv, argc); | ||
| 10331 | |||
| 10332 | if (err) { | ||
| 10333 | if (error) { | ||
| 10334 | *error = createObject(OBJ_STRING, err); | ||
| 10335 | if (ctx != NULL) autoMemoryAdd(ctx, REDISMODULE_AM_STRING, *error); | ||
| 10336 | } else { | ||
| 10337 | sdsfree(err); | ||
| 10338 | } | ||
| 10339 | |||
| 10340 | return REDISMODULE_ERR; | ||
| 10341 | } | ||
| 10342 | |||
| 10343 | return REDISMODULE_OK; | ||
| 10344 | } | ||
| 10345 | |||
| 10346 | /* Get the ACL string for a given user | ||
| 10347 | * Returns a RedisModuleString | ||
| 10348 | */ | ||
| 10349 | RedisModuleString *RM_GetModuleUserACLString(RedisModuleUser *user) { | ||
| 10350 | serverAssert(user != NULL); | ||
| 10351 | |||
| 10352 | return ACLDescribeUser(user->user); | ||
| 10353 | } | ||
| 10354 | |||
| 10355 | /* Retrieve the user name of the client connection behind the current context. | ||
| 10356 | * The user name can be used later, in order to get a RedisModuleUser. | ||
| 10357 | * See more information in RM_GetModuleUserFromUserName. | ||
| 10358 | * | ||
| 10359 | * The returned string must be released with RedisModule_FreeString() or by | ||
| 10360 | * enabling automatic memory management. */ | ||
| 10361 | RedisModuleString *RM_GetCurrentUserName(RedisModuleCtx *ctx) { | ||
| 10362 | /* Sometimes, the user isn't passed along the call stack or isn't | ||
| 10363 | * even set, so we need to check for the members to avoid crashes. */ | ||
| 10364 | if (ctx->client == NULL || ctx->client->user == NULL || ctx->client->user->name == NULL) { | ||
| 10365 | return NULL; | ||
| 10366 | } | ||
| 10367 | |||
| 10368 | return RM_CreateString(ctx,ctx->client->user->name,sdslen(ctx->client->user->name)); | ||
| 10369 | } | ||
| 10370 | |||
| 10371 | /* A RedisModuleUser can be used to check if command, key or channel can be executed or | ||
| 10372 | * accessed according to the ACLs rules associated with that user. | ||
| 10373 | * When a Module wants to do ACL checks on a general ACL user (not created by RM_CreateModuleUser), | ||
| 10374 | * it can get the RedisModuleUser from this API, based on the user name retrieved by RM_GetCurrentUserName. | ||
| 10375 | * | ||
| 10376 | * Since a general ACL user can be deleted at any time, this RedisModuleUser should be used only in the context | ||
| 10377 | * where this function was called. In order to do ACL checks out of that context, the Module can store the user name, | ||
| 10378 | * and call this API at any other context. | ||
| 10379 | * | ||
| 10380 | * Returns NULL if the user is disabled or the user does not exist. | ||
| 10381 | * The caller should later free the user using the function RM_FreeModuleUser().*/ | ||
| 10382 | RedisModuleUser *RM_GetModuleUserFromUserName(RedisModuleString *name) { | ||
| 10383 | /* First, verify that the user exist */ | ||
| 10384 | user *acl_user = ACLGetUserByName(name->ptr, sdslen(name->ptr)); | ||
| 10385 | if (acl_user == NULL) { | ||
| 10386 | return NULL; | ||
| 10387 | } | ||
| 10388 | |||
| 10389 | RedisModuleUser *new_user = zmalloc(sizeof(RedisModuleUser)); | ||
| 10390 | new_user->user = acl_user; | ||
| 10391 | new_user->free_user = 0; | ||
| 10392 | return new_user; | ||
| 10393 | } | ||
| 10394 | |||
| 10395 | /* Checks if the command can be executed by the user, according to the ACLs associated with it. | ||
| 10396 | * | ||
| 10397 | * On success a REDISMODULE_OK is returned, otherwise | ||
| 10398 | * REDISMODULE_ERR is returned and errno is set to the following values: | ||
| 10399 | * | ||
| 10400 | * * ENOENT: Specified command does not exist. | ||
| 10401 | * * EACCES: Command cannot be executed, according to ACL rules | ||
| 10402 | */ | ||
| 10403 | int RM_ACLCheckCommandPermissions(RedisModuleUser *user, RedisModuleString **argv, int argc) { | ||
| 10404 | int keyidxptr; | ||
| 10405 | struct redisCommand *cmd; | ||
| 10406 | |||
| 10407 | /* Find command */ | ||
| 10408 | if ((cmd = lookupCommand(argv, argc)) == NULL) { | ||
| 10409 | errno = ENOENT; | ||
| 10410 | return REDISMODULE_ERR; | ||
| 10411 | } | ||
| 10412 | |||
| 10413 | if (ACLCheckAllUserCommandPerm(user->user, cmd, argv, argc, NULL, &keyidxptr) != ACL_OK) { | ||
| 10414 | errno = EACCES; | ||
| 10415 | return REDISMODULE_ERR; | ||
| 10416 | } | ||
| 10417 | |||
| 10418 | return REDISMODULE_OK; | ||
| 10419 | } | ||
| 10420 | |||
| 10421 | /* Check if the key can be accessed by the user according to the ACLs attached to the user | ||
| 10422 | * and the flags representing the key access. The flags are the same that are used in the | ||
| 10423 | * keyspec for logical operations. These flags are documented in RedisModule_SetCommandInfo as | ||
| 10424 | * the REDISMODULE_CMD_KEY_ACCESS, REDISMODULE_CMD_KEY_UPDATE, REDISMODULE_CMD_KEY_INSERT, | ||
| 10425 | * and REDISMODULE_CMD_KEY_DELETE flags. | ||
| 10426 | * | ||
| 10427 | * If no flags are supplied, the user is still required to have some access to the key for | ||
| 10428 | * this command to return successfully. | ||
| 10429 | * | ||
| 10430 | * If the user is able to access the key then REDISMODULE_OK is returned, otherwise | ||
| 10431 | * REDISMODULE_ERR is returned and errno is set to one of the following values: | ||
| 10432 | * | ||
| 10433 | * * EINVAL: The provided flags are invalid. | ||
| 10434 | * * EACCESS: The user does not have permission to access the key. | ||
| 10435 | */ | ||
| 10436 | int RM_ACLCheckKeyPermissions(RedisModuleUser *user, RedisModuleString *key, int flags) { | ||
| 10437 | const int allow_mask = (REDISMODULE_CMD_KEY_ACCESS | ||
| 10438 | | REDISMODULE_CMD_KEY_INSERT | ||
| 10439 | | REDISMODULE_CMD_KEY_DELETE | ||
| 10440 | | REDISMODULE_CMD_KEY_UPDATE); | ||
| 10441 | |||
| 10442 | if ((flags & allow_mask) != flags) { | ||
| 10443 | errno = EINVAL; | ||
| 10444 | return REDISMODULE_ERR; | ||
| 10445 | } | ||
| 10446 | |||
| 10447 | int keyspec_flags = moduleConvertKeySpecsFlags(flags, 0); | ||
| 10448 | if (ACLUserCheckKeyPerm(user->user, key->ptr, sdslen(key->ptr), keyspec_flags) != ACL_OK) { | ||
| 10449 | errno = EACCES; | ||
| 10450 | return REDISMODULE_ERR; | ||
| 10451 | } | ||
| 10452 | |||
| 10453 | return REDISMODULE_OK; | ||
| 10454 | } | ||
| 10455 | |||
| 10456 | /* Check if the user can access keys matching the given key prefix according to the ACLs | ||
| 10457 | * attached to the user and the flags representing key access. The flags are the same that | ||
| 10458 | * are used in the keyspec for logical operations. These flags are documented in | ||
| 10459 | * RedisModule_SetCommandInfo as the REDISMODULE_CMD_KEY_ACCESS, | ||
| 10460 | * REDISMODULE_CMD_KEY_UPDATE, REDISMODULE_CMD_KEY_INSERT, and REDISMODULE_CMD_KEY_DELETE flags. | ||
| 10461 | * | ||
| 10462 | * If no flags are supplied, the user is still required to have some access to keys matching | ||
| 10463 | * the prefix for this command to return successfully. | ||
| 10464 | * | ||
| 10465 | * If the user is able to access keys matching the prefix, then REDISMODULE_OK is returned. | ||
| 10466 | * Otherwise, REDISMODULE_ERR is returned and errno is set to one of the following values: | ||
| 10467 | * | ||
| 10468 | * * EINVAL: The provided flags are invalid. | ||
| 10469 | * * EACCES: The user does not have permission to access keys matching the prefix. | ||
| 10470 | */ | ||
| 10471 | int RM_ACLCheckKeyPrefixPermissions(RedisModuleUser *user, RedisModuleString *prefix, int flags) { | ||
| 10472 | const int allow_mask = (REDISMODULE_CMD_KEY_ACCESS | ||
| 10473 | | REDISMODULE_CMD_KEY_INSERT | ||
| 10474 | | REDISMODULE_CMD_KEY_DELETE | ||
| 10475 | | REDISMODULE_CMD_KEY_UPDATE); | ||
| 10476 | |||
| 10477 | if ((flags & allow_mask) != flags) { | ||
| 10478 | errno = EINVAL; | ||
| 10479 | return REDISMODULE_ERR; | ||
| 10480 | } | ||
| 10481 | |||
| 10482 | int keyspec_flags = moduleConvertKeySpecsFlags(flags, 0); | ||
| 10483 | |||
| 10484 | /* Add the prefix flag to the keyspec flags */ | ||
| 10485 | keyspec_flags |= CMD_KEY_PREFIX; | ||
| 10486 | |||
| 10487 | if (ACLUserCheckKeyPerm(user->user, prefix->ptr, sdslen(prefix->ptr), keyspec_flags) != ACL_OK) { | ||
| 10488 | errno = EACCES; | ||
| 10489 | return REDISMODULE_ERR; | ||
| 10490 | } | ||
| 10491 | |||
| 10492 | return REDISMODULE_OK; | ||
| 10493 | } | ||
| 10494 | |||
| 10495 | /* Check if the pubsub channel can be accessed by the user based off of the given | ||
| 10496 | * access flags. See RM_ChannelAtPosWithFlags for more information about the | ||
| 10497 | * possible flags that can be passed in. | ||
| 10498 | * | ||
| 10499 | * If the user is able to access the pubsub channel then REDISMODULE_OK is returned, otherwise | ||
| 10500 | * REDISMODULE_ERR is returned and errno is set to one of the following values: | ||
| 10501 | * | ||
| 10502 | * * EINVAL: The provided flags are invalid. | ||
| 10503 | * * EACCESS: The user does not have permission to access the pubsub channel. | ||
| 10504 | */ | ||
| 10505 | int RM_ACLCheckChannelPermissions(RedisModuleUser *user, RedisModuleString *ch, int flags) { | ||
| 10506 | const int allow_mask = (REDISMODULE_CMD_CHANNEL_PUBLISH | ||
| 10507 | | REDISMODULE_CMD_CHANNEL_SUBSCRIBE | ||
| 10508 | | REDISMODULE_CMD_CHANNEL_UNSUBSCRIBE | ||
| 10509 | | REDISMODULE_CMD_CHANNEL_PATTERN); | ||
| 10510 | |||
| 10511 | if ((flags & allow_mask) != flags) { | ||
| 10512 | errno = EINVAL; | ||
| 10513 | return REDISMODULE_ERR; | ||
| 10514 | } | ||
| 10515 | |||
| 10516 | /* Unsubscribe permissions are currently always allowed. */ | ||
| 10517 | if (flags & REDISMODULE_CMD_CHANNEL_UNSUBSCRIBE){ | ||
| 10518 | return REDISMODULE_OK; | ||
| 10519 | } | ||
| 10520 | |||
| 10521 | int is_pattern = flags & REDISMODULE_CMD_CHANNEL_PATTERN; | ||
| 10522 | if (ACLUserCheckChannelPerm(user->user, ch->ptr, is_pattern) != ACL_OK) | ||
| 10523 | return REDISMODULE_ERR; | ||
| 10524 | |||
| 10525 | return REDISMODULE_OK; | ||
| 10526 | } | ||
| 10527 | |||
| 10528 | /* Helper function to map a RedisModuleACLLogEntryReason to ACL Log entry reason. */ | ||
| 10529 | int moduleGetACLLogEntryReason(RedisModuleACLLogEntryReason reason) { | ||
| 10530 | int acl_reason = 0; | ||
| 10531 | switch (reason) { | ||
| 10532 | case REDISMODULE_ACL_LOG_AUTH: acl_reason = ACL_DENIED_AUTH; break; | ||
| 10533 | case REDISMODULE_ACL_LOG_KEY: acl_reason = ACL_DENIED_KEY; break; | ||
| 10534 | case REDISMODULE_ACL_LOG_CHANNEL: acl_reason = ACL_DENIED_CHANNEL; break; | ||
| 10535 | case REDISMODULE_ACL_LOG_CMD: acl_reason = ACL_DENIED_CMD; break; | ||
| 10536 | default: break; | ||
| 10537 | } | ||
| 10538 | return acl_reason; | ||
| 10539 | } | ||
| 10540 | |||
| 10541 | /* Adds a new entry in the ACL log. | ||
| 10542 | * Returns REDISMODULE_OK on success and REDISMODULE_ERR on error. | ||
| 10543 | * | ||
| 10544 | * For more information about ACL log, please refer to https://redis.io/commands/acl-log */ | ||
| 10545 | int RM_ACLAddLogEntry(RedisModuleCtx *ctx, RedisModuleUser *user, RedisModuleString *object, RedisModuleACLLogEntryReason reason) { | ||
| 10546 | int acl_reason = moduleGetACLLogEntryReason(reason); | ||
| 10547 | if (!acl_reason) return REDISMODULE_ERR; | ||
| 10548 | addACLLogEntry(ctx->client, acl_reason, ACL_LOG_CTX_MODULE, -1, user->user->name, sdsdup(object->ptr)); | ||
| 10549 | return REDISMODULE_OK; | ||
| 10550 | } | ||
| 10551 | |||
| 10552 | /* Adds a new entry in the ACL log with the `username` RedisModuleString provided. | ||
| 10553 | * Returns REDISMODULE_OK on success and REDISMODULE_ERR on error. | ||
| 10554 | * | ||
| 10555 | * For more information about ACL log, please refer to https://redis.io/commands/acl-log */ | ||
| 10556 | int RM_ACLAddLogEntryByUserName(RedisModuleCtx *ctx, RedisModuleString *username, RedisModuleString *object, RedisModuleACLLogEntryReason reason) { | ||
| 10557 | int acl_reason = moduleGetACLLogEntryReason(reason); | ||
| 10558 | if (!acl_reason) return REDISMODULE_ERR; | ||
| 10559 | addACLLogEntry(ctx->client, acl_reason, ACL_LOG_CTX_MODULE, -1, username->ptr, sdsdup(object->ptr)); | ||
| 10560 | return REDISMODULE_OK; | ||
| 10561 | } | ||
| 10562 | |||
| 10563 | /* Authenticate the client associated with the context with | ||
| 10564 | * the provided user. Returns REDISMODULE_OK on success and | ||
| 10565 | * REDISMODULE_ERR on error. | ||
| 10566 | * | ||
| 10567 | * This authentication can be tracked with the optional callback and private | ||
| 10568 | * data fields. The callback will be called whenever the user of the client | ||
| 10569 | * changes. This callback should be used to cleanup any state that is being | ||
| 10570 | * kept in the module related to the client authentication. It will only be | ||
| 10571 | * called once, even when the user hasn't changed, in order to allow for a | ||
| 10572 | * new callback to be specified. If this authentication does not need to be | ||
| 10573 | * tracked, pass in NULL for the callback and privdata. | ||
| 10574 | * | ||
| 10575 | * If client_id is not NULL, it will be filled with the id of the client | ||
| 10576 | * that was authenticated. This can be used with the | ||
| 10577 | * RM_DeauthenticateAndCloseClient() API in order to deauthenticate a | ||
| 10578 | * previously authenticated client if the authentication is no longer valid. | ||
| 10579 | * | ||
| 10580 | * For expensive authentication operations, it is recommended to block the | ||
| 10581 | * client and do the authentication in the background and then attach the user | ||
| 10582 | * to the client in a threadsafe context. */ | ||
| 10583 | static int authenticateClientWithUser(RedisModuleCtx *ctx, user *user, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id) { | ||
| 10584 | if (user->flags & USER_FLAG_DISABLED) { | ||
| 10585 | return REDISMODULE_ERR; | ||
| 10586 | } | ||
| 10587 | |||
| 10588 | /* Avoid settings which are meaningless and will be lost */ | ||
| 10589 | if (!ctx->client || (ctx->client->flags & CLIENT_MODULE)) { | ||
| 10590 | return REDISMODULE_ERR; | ||
| 10591 | } | ||
| 10592 | |||
| 10593 | moduleNotifyUserChanged(ctx->client); | ||
| 10594 | |||
| 10595 | ctx->client->user = user; | ||
| 10596 | ctx->client->authenticated = 1; | ||
| 10597 | |||
| 10598 | if (clientHasModuleAuthInProgress(ctx->client)) { | ||
| 10599 | ctx->client->flags |= CLIENT_MODULE_AUTH_HAS_RESULT; | ||
| 10600 | } | ||
| 10601 | |||
| 10602 | if (callback) { | ||
| 10603 | ctx->client->auth_callback = callback; | ||
| 10604 | ctx->client->auth_callback_privdata = privdata; | ||
| 10605 | ctx->client->auth_module = ctx->module; | ||
| 10606 | } | ||
| 10607 | |||
| 10608 | if (client_id) { | ||
| 10609 | *client_id = ctx->client->id; | ||
| 10610 | } | ||
| 10611 | |||
| 10612 | return REDISMODULE_OK; | ||
| 10613 | } | ||
| 10614 | |||
| 10615 | |||
| 10616 | /* Authenticate the current context's user with the provided redis acl user. | ||
| 10617 | * Returns REDISMODULE_ERR if the user is disabled. | ||
| 10618 | * | ||
| 10619 | * See authenticateClientWithUser for information about callback, client_id, | ||
| 10620 | * and general usage for authentication. */ | ||
| 10621 | int RM_AuthenticateClientWithUser(RedisModuleCtx *ctx, RedisModuleUser *module_user, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id) { | ||
| 10622 | return authenticateClientWithUser(ctx, module_user->user, callback, privdata, client_id); | ||
| 10623 | } | ||
| 10624 | |||
| 10625 | /* Authenticate the current context's user with the provided redis acl user. | ||
| 10626 | * Returns REDISMODULE_ERR if the user is disabled or the user does not exist. | ||
| 10627 | * | ||
| 10628 | * See authenticateClientWithUser for information about callback, client_id, | ||
| 10629 | * and general usage for authentication. */ | ||
| 10630 | int RM_AuthenticateClientWithACLUser(RedisModuleCtx *ctx, const char *name, size_t len, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id) { | ||
| 10631 | user *acl_user = ACLGetUserByName(name, len); | ||
| 10632 | |||
| 10633 | if (!acl_user) { | ||
| 10634 | return REDISMODULE_ERR; | ||
| 10635 | } | ||
| 10636 | return authenticateClientWithUser(ctx, acl_user, callback, privdata, client_id); | ||
| 10637 | } | ||
| 10638 | |||
| 10639 | /* Deauthenticate and close the client. The client resources will not be | ||
| 10640 | * immediately freed, but will be cleaned up in a background job. This is | ||
| 10641 | * the recommended way to deauthenticate a client since most clients can't | ||
| 10642 | * handle users becoming deauthenticated. Returns REDISMODULE_ERR when the | ||
| 10643 | * client doesn't exist and REDISMODULE_OK when the operation was successful. | ||
| 10644 | * | ||
| 10645 | * The client ID is returned from the RM_AuthenticateClientWithUser and | ||
| 10646 | * RM_AuthenticateClientWithACLUser APIs, but can be obtained through | ||
| 10647 | * the CLIENT api or through server events. | ||
| 10648 | * | ||
| 10649 | * This function is not thread safe, and must be executed within the context | ||
| 10650 | * of a command or thread safe context. */ | ||
| 10651 | int RM_DeauthenticateAndCloseClient(RedisModuleCtx *ctx, uint64_t client_id) { | ||
| 10652 | UNUSED(ctx); | ||
| 10653 | client *c = lookupClientByID(client_id); | ||
| 10654 | if (c == NULL) return REDISMODULE_ERR; | ||
| 10655 | |||
| 10656 | /* Revoke also marks client to be closed ASAP */ | ||
| 10657 | revokeClientAuthentication(c); | ||
| 10658 | return REDISMODULE_OK; | ||
| 10659 | } | ||
| 10660 | |||
| 10661 | /* Redact the client command argument specified at the given position. Redacted arguments | ||
| 10662 | * are obfuscated in user facing commands such as SLOWLOG or MONITOR, as well as | ||
| 10663 | * never being written to server logs. This command may be called multiple times on the | ||
| 10664 | * same position. | ||
| 10665 | * | ||
| 10666 | * Note that the command name, position 0, can not be redacted. | ||
| 10667 | * | ||
| 10668 | * Returns REDISMODULE_OK if the argument was redacted and REDISMODULE_ERR if there | ||
| 10669 | * was an invalid parameter passed in or the position is outside the client | ||
| 10670 | * argument range. */ | ||
| 10671 | int RM_RedactClientCommandArgument(RedisModuleCtx *ctx, int pos) { | ||
| 10672 | if (!ctx || !ctx->client || pos <= 0 || ctx->client->argc <= pos) { | ||
| 10673 | return REDISMODULE_ERR; | ||
| 10674 | } | ||
| 10675 | redactClientCommandArgument(ctx->client, pos); | ||
| 10676 | return REDISMODULE_OK; | ||
| 10677 | } | ||
| 10678 | |||
| 10679 | /* Return the X.509 client-side certificate used by the client to authenticate | ||
| 10680 | * this connection. | ||
| 10681 | * | ||
| 10682 | * The return value is an allocated RedisModuleString that is a X.509 certificate | ||
| 10683 | * encoded in PEM (Base64) format. It should be freed (or auto-freed) by the caller. | ||
| 10684 | * | ||
| 10685 | * A NULL value is returned in the following conditions: | ||
| 10686 | * | ||
| 10687 | * - Connection ID does not exist | ||
| 10688 | * - Connection is not a TLS connection | ||
| 10689 | * - Connection is a TLS connection but no client certificate was used | ||
| 10690 | */ | ||
| 10691 | RedisModuleString *RM_GetClientCertificate(RedisModuleCtx *ctx, uint64_t client_id) { | ||
| 10692 | client *c = lookupClientByID(client_id); | ||
| 10693 | if (c == NULL) return NULL; | ||
| 10694 | |||
| 10695 | sds cert = connGetPeerCert(c->conn); | ||
| 10696 | if (!cert) return NULL; | ||
| 10697 | |||
| 10698 | RedisModuleString *s = createObject(OBJ_STRING, cert); | ||
| 10699 | if (ctx != NULL) autoMemoryAdd(ctx, REDISMODULE_AM_STRING, s); | ||
| 10700 | |||
| 10701 | return s; | ||
| 10702 | } | ||
| 10703 | |||
| 10704 | /* -------------------------------------------------------------------------- | ||
| 10705 | * ## Modules Dictionary API | ||
| 10706 | * | ||
| 10707 | * Implements a sorted dictionary (actually backed by a radix tree) with | ||
| 10708 | * the usual get / set / del / num-items API, together with an iterator | ||
| 10709 | * capable of going back and forth. | ||
| 10710 | * -------------------------------------------------------------------------- */ | ||
| 10711 | |||
| 10712 | /* Create a new dictionary. The 'ctx' pointer can be the current module context | ||
| 10713 | * or NULL, depending on what you want. Please follow the following rules: | ||
| 10714 | * | ||
| 10715 | * 1. Use a NULL context if you plan to retain a reference to this dictionary | ||
| 10716 | * that will survive the time of the module callback where you created it. | ||
| 10717 | * 2. Use a NULL context if no context is available at the time you are creating | ||
| 10718 | * the dictionary (of course...). | ||
| 10719 | * 3. However use the current callback context as 'ctx' argument if the | ||
| 10720 | * dictionary time to live is just limited to the callback scope. In this | ||
| 10721 | * case, if enabled, you can enjoy the automatic memory management that will | ||
| 10722 | * reclaim the dictionary memory, as well as the strings returned by the | ||
| 10723 | * Next / Prev dictionary iterator calls. | ||
| 10724 | */ | ||
| 10725 | RedisModuleDict *RM_CreateDict(RedisModuleCtx *ctx) { | ||
| 10726 | struct RedisModuleDict *d = zmalloc(sizeof(*d)); | ||
| 10727 | d->rax = raxNew(); | ||
| 10728 | if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_DICT,d); | ||
| 10729 | return d; | ||
| 10730 | } | ||
| 10731 | |||
| 10732 | /* Free a dictionary created with RM_CreateDict(). You need to pass the | ||
| 10733 | * context pointer 'ctx' only if the dictionary was created using the | ||
| 10734 | * context instead of passing NULL. */ | ||
| 10735 | void RM_FreeDict(RedisModuleCtx *ctx, RedisModuleDict *d) { | ||
| 10736 | if (ctx != NULL) autoMemoryFreed(ctx,REDISMODULE_AM_DICT,d); | ||
| 10737 | raxFree(d->rax); | ||
| 10738 | zfree(d); | ||
| 10739 | } | ||
| 10740 | |||
| 10741 | /* Return the size of the dictionary (number of keys). */ | ||
| 10742 | uint64_t RM_DictSize(RedisModuleDict *d) { | ||
| 10743 | return raxSize(d->rax); | ||
| 10744 | } | ||
| 10745 | |||
| 10746 | /* Store the specified key into the dictionary, setting its value to the | ||
| 10747 | * pointer 'ptr'. If the key was added with success, since it did not | ||
| 10748 | * already exist, REDISMODULE_OK is returned. Otherwise if the key already | ||
| 10749 | * exists the function returns REDISMODULE_ERR. */ | ||
| 10750 | int RM_DictSetC(RedisModuleDict *d, void *key, size_t keylen, void *ptr) { | ||
| 10751 | int retval = raxTryInsert(d->rax,key,keylen,ptr,NULL); | ||
| 10752 | return (retval == 1) ? REDISMODULE_OK : REDISMODULE_ERR; | ||
| 10753 | } | ||
| 10754 | |||
| 10755 | /* Like RedisModule_DictSetC() but will replace the key with the new | ||
| 10756 | * value if the key already exists. */ | ||
| 10757 | int RM_DictReplaceC(RedisModuleDict *d, void *key, size_t keylen, void *ptr) { | ||
| 10758 | int retval = raxInsert(d->rax,key,keylen,ptr,NULL); | ||
| 10759 | return (retval == 1) ? REDISMODULE_OK : REDISMODULE_ERR; | ||
| 10760 | } | ||
| 10761 | |||
| 10762 | /* Like RedisModule_DictSetC() but takes the key as a RedisModuleString. */ | ||
| 10763 | int RM_DictSet(RedisModuleDict *d, RedisModuleString *key, void *ptr) { | ||
| 10764 | return RM_DictSetC(d,key->ptr,sdslen(key->ptr),ptr); | ||
| 10765 | } | ||
| 10766 | |||
| 10767 | /* Like RedisModule_DictReplaceC() but takes the key as a RedisModuleString. */ | ||
| 10768 | int RM_DictReplace(RedisModuleDict *d, RedisModuleString *key, void *ptr) { | ||
| 10769 | return RM_DictReplaceC(d,key->ptr,sdslen(key->ptr),ptr); | ||
| 10770 | } | ||
| 10771 | |||
| 10772 | /* Return the value stored at the specified key. The function returns NULL | ||
| 10773 | * both in the case the key does not exist, or if you actually stored | ||
| 10774 | * NULL at key. So, optionally, if the 'nokey' pointer is not NULL, it will | ||
| 10775 | * be set by reference to 1 if the key does not exist, or to 0 if the key | ||
| 10776 | * exists. */ | ||
| 10777 | void *RM_DictGetC(RedisModuleDict *d, void *key, size_t keylen, int *nokey) { | ||
| 10778 | void *res = NULL; | ||
| 10779 | int found = raxFind(d->rax,key,keylen,&res); | ||
| 10780 | if (nokey) *nokey = !found; | ||
| 10781 | return res; | ||
| 10782 | } | ||
| 10783 | |||
| 10784 | /* Like RedisModule_DictGetC() but takes the key as a RedisModuleString. */ | ||
| 10785 | void *RM_DictGet(RedisModuleDict *d, RedisModuleString *key, int *nokey) { | ||
| 10786 | return RM_DictGetC(d,key->ptr,sdslen(key->ptr),nokey); | ||
| 10787 | } | ||
| 10788 | |||
| 10789 | /* Remove the specified key from the dictionary, returning REDISMODULE_OK if | ||
| 10790 | * the key was found and deleted, or REDISMODULE_ERR if instead there was | ||
| 10791 | * no such key in the dictionary. When the operation is successful, if | ||
| 10792 | * 'oldval' is not NULL, then '*oldval' is set to the value stored at the | ||
| 10793 | * key before it was deleted. Using this feature it is possible to get | ||
| 10794 | * a pointer to the value (for instance in order to release it), without | ||
| 10795 | * having to call RedisModule_DictGet() before deleting the key. */ | ||
| 10796 | int RM_DictDelC(RedisModuleDict *d, void *key, size_t keylen, void *oldval) { | ||
| 10797 | int retval = raxRemove(d->rax,key,keylen,oldval); | ||
| 10798 | return retval ? REDISMODULE_OK : REDISMODULE_ERR; | ||
| 10799 | } | ||
| 10800 | |||
| 10801 | /* Like RedisModule_DictDelC() but gets the key as a RedisModuleString. */ | ||
| 10802 | int RM_DictDel(RedisModuleDict *d, RedisModuleString *key, void *oldval) { | ||
| 10803 | return RM_DictDelC(d,key->ptr,sdslen(key->ptr),oldval); | ||
| 10804 | } | ||
| 10805 | |||
| 10806 | /* Return an iterator, setup in order to start iterating from the specified | ||
| 10807 | * key by applying the operator 'op', which is just a string specifying the | ||
| 10808 | * comparison operator to use in order to seek the first element. The | ||
| 10809 | * operators available are: | ||
| 10810 | * | ||
| 10811 | * * `^` -- Seek the first (lexicographically smaller) key. | ||
| 10812 | * * `$` -- Seek the last (lexicographically bigger) key. | ||
| 10813 | * * `>` -- Seek the first element greater than the specified key. | ||
| 10814 | * * `>=` -- Seek the first element greater or equal than the specified key. | ||
| 10815 | * * `<` -- Seek the first element smaller than the specified key. | ||
| 10816 | * * `<=` -- Seek the first element smaller or equal than the specified key. | ||
| 10817 | * * `==` -- Seek the first element matching exactly the specified key. | ||
| 10818 | * | ||
| 10819 | * Note that for `^` and `$` the passed key is not used, and the user may | ||
| 10820 | * just pass NULL with a length of 0. | ||
| 10821 | * | ||
| 10822 | * If the element to start the iteration cannot be seeked based on the | ||
| 10823 | * key and operator passed, RedisModule_DictNext() / Prev() will just return | ||
| 10824 | * REDISMODULE_ERR at the first call, otherwise they'll produce elements. | ||
| 10825 | */ | ||
| 10826 | RedisModuleDictIter *RM_DictIteratorStartC(RedisModuleDict *d, const char *op, void *key, size_t keylen) { | ||
| 10827 | RedisModuleDictIter *di = zmalloc(sizeof(*di)); | ||
| 10828 | di->dict = d; | ||
| 10829 | raxStart(&di->ri,d->rax); | ||
| 10830 | raxSeek(&di->ri,op,key,keylen); | ||
| 10831 | return di; | ||
| 10832 | } | ||
| 10833 | |||
| 10834 | /* Exactly like RedisModule_DictIteratorStartC, but the key is passed as a | ||
| 10835 | * RedisModuleString. */ | ||
| 10836 | RedisModuleDictIter *RM_DictIteratorStart(RedisModuleDict *d, const char *op, RedisModuleString *key) { | ||
| 10837 | return RM_DictIteratorStartC(d,op,key->ptr,sdslen(key->ptr)); | ||
| 10838 | } | ||
| 10839 | |||
| 10840 | /* Release the iterator created with RedisModule_DictIteratorStart(). This call | ||
| 10841 | * is mandatory otherwise a memory leak is introduced in the module. */ | ||
| 10842 | void RM_DictIteratorStop(RedisModuleDictIter *di) { | ||
| 10843 | raxStop(&di->ri); | ||
| 10844 | zfree(di); | ||
| 10845 | } | ||
| 10846 | |||
| 10847 | /* After its creation with RedisModule_DictIteratorStart(), it is possible to | ||
| 10848 | * change the currently selected element of the iterator by using this | ||
| 10849 | * API call. The result based on the operator and key is exactly like | ||
| 10850 | * the function RedisModule_DictIteratorStart(), however in this case the | ||
| 10851 | * return value is just REDISMODULE_OK in case the seeked element was found, | ||
| 10852 | * or REDISMODULE_ERR in case it was not possible to seek the specified | ||
| 10853 | * element. It is possible to reseek an iterator as many times as you want. */ | ||
| 10854 | int RM_DictIteratorReseekC(RedisModuleDictIter *di, const char *op, void *key, size_t keylen) { | ||
| 10855 | return raxSeek(&di->ri,op,key,keylen); | ||
| 10856 | } | ||
| 10857 | |||
| 10858 | /* Like RedisModule_DictIteratorReseekC() but takes the key as a | ||
| 10859 | * RedisModuleString. */ | ||
| 10860 | int RM_DictIteratorReseek(RedisModuleDictIter *di, const char *op, RedisModuleString *key) { | ||
| 10861 | return RM_DictIteratorReseekC(di,op,key->ptr,sdslen(key->ptr)); | ||
| 10862 | } | ||
| 10863 | |||
| 10864 | /* Return the current item of the dictionary iterator `di` and steps to the | ||
| 10865 | * next element. If the iterator already yield the last element and there | ||
| 10866 | * are no other elements to return, NULL is returned, otherwise a pointer | ||
| 10867 | * to a string representing the key is provided, and the `*keylen` length | ||
| 10868 | * is set by reference (if keylen is not NULL). The `*dataptr`, if not NULL | ||
| 10869 | * is set to the value of the pointer stored at the returned key as auxiliary | ||
| 10870 | * data (as set by the RedisModule_DictSet API). | ||
| 10871 | * | ||
| 10872 | * Usage example: | ||
| 10873 | * | ||
| 10874 | * ... create the iterator here ... | ||
| 10875 | * char *key; | ||
| 10876 | * void *data; | ||
| 10877 | * while((key = RedisModule_DictNextC(iter,&keylen,&data)) != NULL) { | ||
| 10878 | * printf("%.*s %p\n", (int)keylen, key, data); | ||
| 10879 | * } | ||
| 10880 | * | ||
| 10881 | * The returned pointer is of type void because sometimes it makes sense | ||
| 10882 | * to cast it to a `char*` sometimes to an unsigned `char*` depending on the | ||
| 10883 | * fact it contains or not binary data, so this API ends being more | ||
| 10884 | * comfortable to use. | ||
| 10885 | * | ||
| 10886 | * The validity of the returned pointer is until the next call to the | ||
| 10887 | * next/prev iterator step. Also the pointer is no longer valid once the | ||
| 10888 | * iterator is released. */ | ||
| 10889 | void *RM_DictNextC(RedisModuleDictIter *di, size_t *keylen, void **dataptr) { | ||
| 10890 | if (!raxNext(&di->ri)) return NULL; | ||
| 10891 | if (keylen) *keylen = di->ri.key_len; | ||
| 10892 | if (dataptr) *dataptr = di->ri.data; | ||
| 10893 | return di->ri.key; | ||
| 10894 | } | ||
| 10895 | |||
| 10896 | /* This function is exactly like RedisModule_DictNext() but after returning | ||
| 10897 | * the currently selected element in the iterator, it selects the previous | ||
| 10898 | * element (lexicographically smaller) instead of the next one. */ | ||
| 10899 | void *RM_DictPrevC(RedisModuleDictIter *di, size_t *keylen, void **dataptr) { | ||
| 10900 | if (!raxPrev(&di->ri)) return NULL; | ||
| 10901 | if (keylen) *keylen = di->ri.key_len; | ||
| 10902 | if (dataptr) *dataptr = di->ri.data; | ||
| 10903 | return di->ri.key; | ||
| 10904 | } | ||
| 10905 | |||
| 10906 | /* Like RedisModuleNextC(), but instead of returning an internally allocated | ||
| 10907 | * buffer and key length, it returns directly a module string object allocated | ||
| 10908 | * in the specified context 'ctx' (that may be NULL exactly like for the main | ||
| 10909 | * API RedisModule_CreateString). | ||
| 10910 | * | ||
| 10911 | * The returned string object should be deallocated after use, either manually | ||
| 10912 | * or by using a context that has automatic memory management active. */ | ||
| 10913 | RedisModuleString *RM_DictNext(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr) { | ||
| 10914 | size_t keylen; | ||
| 10915 | void *key = RM_DictNextC(di,&keylen,dataptr); | ||
| 10916 | if (key == NULL) return NULL; | ||
| 10917 | return RM_CreateString(ctx,key,keylen); | ||
| 10918 | } | ||
| 10919 | |||
| 10920 | /* Like RedisModule_DictNext() but after returning the currently selected | ||
| 10921 | * element in the iterator, it selects the previous element (lexicographically | ||
| 10922 | * smaller) instead of the next one. */ | ||
| 10923 | RedisModuleString *RM_DictPrev(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr) { | ||
| 10924 | size_t keylen; | ||
| 10925 | void *key = RM_DictPrevC(di,&keylen,dataptr); | ||
| 10926 | if (key == NULL) return NULL; | ||
| 10927 | return RM_CreateString(ctx,key,keylen); | ||
| 10928 | } | ||
| 10929 | |||
| 10930 | /* Compare the element currently pointed by the iterator to the specified | ||
| 10931 | * element given by key/keylen, according to the operator 'op' (the set of | ||
| 10932 | * valid operators are the same valid for RedisModule_DictIteratorStart). | ||
| 10933 | * If the comparison is successful the command returns REDISMODULE_OK | ||
| 10934 | * otherwise REDISMODULE_ERR is returned. | ||
| 10935 | * | ||
| 10936 | * This is useful when we want to just emit a lexicographical range, so | ||
| 10937 | * in the loop, as we iterate elements, we can also check if we are still | ||
| 10938 | * on range. | ||
| 10939 | * | ||
| 10940 | * The function return REDISMODULE_ERR if the iterator reached the | ||
| 10941 | * end of elements condition as well. */ | ||
| 10942 | int RM_DictCompareC(RedisModuleDictIter *di, const char *op, void *key, size_t keylen) { | ||
| 10943 | if (raxEOF(&di->ri)) return REDISMODULE_ERR; | ||
| 10944 | int res = raxCompare(&di->ri,op,key,keylen); | ||
| 10945 | return res ? REDISMODULE_OK : REDISMODULE_ERR; | ||
| 10946 | } | ||
| 10947 | |||
| 10948 | /* Like RedisModule_DictCompareC but gets the key to compare with the current | ||
| 10949 | * iterator key as a RedisModuleString. */ | ||
| 10950 | int RM_DictCompare(RedisModuleDictIter *di, const char *op, RedisModuleString *key) { | ||
| 10951 | if (raxEOF(&di->ri)) return REDISMODULE_ERR; | ||
| 10952 | int res = raxCompare(&di->ri,op,key->ptr,sdslen(key->ptr)); | ||
| 10953 | return res ? REDISMODULE_OK : REDISMODULE_ERR; | ||
| 10954 | } | ||
| 10955 | |||
| 10956 | |||
| 10957 | |||
| 10958 | |||
| 10959 | /* -------------------------------------------------------------------------- | ||
| 10960 | * ## Modules Info fields | ||
| 10961 | * -------------------------------------------------------------------------- */ | ||
| 10962 | |||
| 10963 | int RM_InfoEndDictField(RedisModuleInfoCtx *ctx); | ||
| 10964 | |||
| 10965 | /* Used to start a new section, before adding any fields. the section name will | ||
| 10966 | * be prefixed by `<modulename>_` and must only include A-Z,a-z,0-9. | ||
| 10967 | * NULL or empty string indicates the default section (only `<modulename>`) is used. | ||
| 10968 | * When return value is REDISMODULE_ERR, the section should and will be skipped. */ | ||
| 10969 | int RM_InfoAddSection(RedisModuleInfoCtx *ctx, const char *name) { | ||
| 10970 | sds full_name = sdsdup(ctx->module->name); | ||
| 10971 | if (name != NULL && strlen(name) > 0) | ||
| 10972 | full_name = sdscatfmt(full_name, "_%s", name); | ||
| 10973 | |||
| 10974 | /* Implicitly end dicts, instead of returning an error which is likely un checked. */ | ||
| 10975 | if (ctx->in_dict_field) | ||
| 10976 | RM_InfoEndDictField(ctx); | ||
| 10977 | |||
| 10978 | /* proceed only if: | ||
| 10979 | * 1) no section was requested (emit all) | ||
| 10980 | * 2) the module name was requested (emit all) | ||
| 10981 | * 3) this specific section was requested. */ | ||
| 10982 | if (ctx->requested_sections) { | ||
| 10983 | if ((!full_name || !dictFind(ctx->requested_sections, full_name)) && | ||
| 10984 | (!dictFind(ctx->requested_sections, ctx->module->name))) | ||
| 10985 | { | ||
| 10986 | sdsfree(full_name); | ||
| 10987 | ctx->in_section = 0; | ||
| 10988 | return REDISMODULE_ERR; | ||
| 10989 | } | ||
| 10990 | } | ||
| 10991 | if (ctx->sections++) ctx->info = sdscat(ctx->info,"\r\n"); | ||
| 10992 | ctx->info = sdscatfmt(ctx->info, "# %S\r\n", full_name); | ||
| 10993 | ctx->in_section = 1; | ||
| 10994 | sdsfree(full_name); | ||
| 10995 | return REDISMODULE_OK; | ||
| 10996 | } | ||
| 10997 | |||
| 10998 | /* Starts a dict field, similar to the ones in INFO KEYSPACE. Use normal | ||
| 10999 | * RedisModule_InfoAddField* functions to add the items to this field, and | ||
| 11000 | * terminate with RedisModule_InfoEndDictField. */ | ||
| 11001 | int RM_InfoBeginDictField(RedisModuleInfoCtx *ctx, const char *name) { | ||
| 11002 | if (!ctx->in_section) | ||
| 11003 | return REDISMODULE_ERR; | ||
| 11004 | /* Implicitly end dicts, instead of returning an error which is likely un checked. */ | ||
| 11005 | if (ctx->in_dict_field) | ||
| 11006 | RM_InfoEndDictField(ctx); | ||
| 11007 | char *tmpmodname, *tmpname; | ||
| 11008 | ctx->info = sdscatfmt(ctx->info, | ||
| 11009 | "%s_%s:", | ||
| 11010 | getSafeInfoString(ctx->module->name, strlen(ctx->module->name), &tmpmodname), | ||
| 11011 | getSafeInfoString(name, strlen(name), &tmpname)); | ||
| 11012 | if (tmpmodname != NULL) zfree(tmpmodname); | ||
| 11013 | if (tmpname != NULL) zfree(tmpname); | ||
| 11014 | ctx->in_dict_field = 1; | ||
| 11015 | return REDISMODULE_OK; | ||
| 11016 | } | ||
| 11017 | |||
| 11018 | /* Ends a dict field, see RedisModule_InfoBeginDictField */ | ||
| 11019 | int RM_InfoEndDictField(RedisModuleInfoCtx *ctx) { | ||
| 11020 | if (!ctx->in_dict_field) | ||
| 11021 | return REDISMODULE_ERR; | ||
| 11022 | /* trim the last ',' if found. */ | ||
| 11023 | if (ctx->info[sdslen(ctx->info)-1]==',') | ||
| 11024 | sdsIncrLen(ctx->info, -1); | ||
| 11025 | ctx->info = sdscat(ctx->info, "\r\n"); | ||
| 11026 | ctx->in_dict_field = 0; | ||
| 11027 | return REDISMODULE_OK; | ||
| 11028 | } | ||
| 11029 | |||
| 11030 | /* Used by RedisModuleInfoFunc to add info fields. | ||
| 11031 | * Each field will be automatically prefixed by `<modulename>_`. | ||
| 11032 | * Field names or values must not include `\r\n` or `:`. */ | ||
| 11033 | int RM_InfoAddFieldString(RedisModuleInfoCtx *ctx, const char *field, RedisModuleString *value) { | ||
| 11034 | if (!ctx->in_section) | ||
| 11035 | return REDISMODULE_ERR; | ||
| 11036 | if (ctx->in_dict_field) { | ||
| 11037 | ctx->info = sdscatfmt(ctx->info, | ||
| 11038 | "%s=%S,", | ||
| 11039 | field, | ||
| 11040 | (sds)value->ptr); | ||
| 11041 | return REDISMODULE_OK; | ||
| 11042 | } | ||
| 11043 | ctx->info = sdscatfmt(ctx->info, | ||
| 11044 | "%s_%s:%S\r\n", | ||
| 11045 | ctx->module->name, | ||
| 11046 | field, | ||
| 11047 | (sds)value->ptr); | ||
| 11048 | return REDISMODULE_OK; | ||
| 11049 | } | ||
| 11050 | |||
| 11051 | /* See RedisModule_InfoAddFieldString(). */ | ||
| 11052 | int RM_InfoAddFieldCString(RedisModuleInfoCtx *ctx, const char *field, const char *value) { | ||
| 11053 | if (!ctx->in_section) | ||
| 11054 | return REDISMODULE_ERR; | ||
| 11055 | if (ctx->in_dict_field) { | ||
| 11056 | ctx->info = sdscatfmt(ctx->info, | ||
| 11057 | "%s=%s,", | ||
| 11058 | field, | ||
| 11059 | value); | ||
| 11060 | return REDISMODULE_OK; | ||
| 11061 | } | ||
| 11062 | ctx->info = sdscatfmt(ctx->info, | ||
| 11063 | "%s_%s:%s\r\n", | ||
| 11064 | ctx->module->name, | ||
| 11065 | field, | ||
| 11066 | value); | ||
| 11067 | return REDISMODULE_OK; | ||
| 11068 | } | ||
| 11069 | |||
| 11070 | /* See RedisModule_InfoAddFieldString(). */ | ||
| 11071 | int RM_InfoAddFieldDouble(RedisModuleInfoCtx *ctx, const char *field, double value) { | ||
| 11072 | if (!ctx->in_section) | ||
| 11073 | return REDISMODULE_ERR; | ||
| 11074 | if (ctx->in_dict_field) { | ||
| 11075 | ctx->info = sdscatprintf(ctx->info, | ||
| 11076 | "%s=%.17g,", | ||
| 11077 | field, | ||
| 11078 | value); | ||
| 11079 | return REDISMODULE_OK; | ||
| 11080 | } | ||
| 11081 | ctx->info = sdscatprintf(ctx->info, | ||
| 11082 | "%s_%s:%.17g\r\n", | ||
| 11083 | ctx->module->name, | ||
| 11084 | field, | ||
| 11085 | value); | ||
| 11086 | return REDISMODULE_OK; | ||
| 11087 | } | ||
| 11088 | |||
| 11089 | /* See RedisModule_InfoAddFieldString(). */ | ||
| 11090 | int RM_InfoAddFieldLongLong(RedisModuleInfoCtx *ctx, const char *field, long long value) { | ||
| 11091 | if (!ctx->in_section) | ||
| 11092 | return REDISMODULE_ERR; | ||
| 11093 | if (ctx->in_dict_field) { | ||
| 11094 | ctx->info = sdscatfmt(ctx->info, | ||
| 11095 | "%s=%I,", | ||
| 11096 | field, | ||
| 11097 | value); | ||
| 11098 | return REDISMODULE_OK; | ||
| 11099 | } | ||
| 11100 | ctx->info = sdscatfmt(ctx->info, | ||
| 11101 | "%s_%s:%I\r\n", | ||
| 11102 | ctx->module->name, | ||
| 11103 | field, | ||
| 11104 | value); | ||
| 11105 | return REDISMODULE_OK; | ||
| 11106 | } | ||
| 11107 | |||
| 11108 | /* See RedisModule_InfoAddFieldString(). */ | ||
| 11109 | int RM_InfoAddFieldULongLong(RedisModuleInfoCtx *ctx, const char *field, unsigned long long value) { | ||
| 11110 | if (!ctx->in_section) | ||
| 11111 | return REDISMODULE_ERR; | ||
| 11112 | if (ctx->in_dict_field) { | ||
| 11113 | ctx->info = sdscatfmt(ctx->info, | ||
| 11114 | "%s=%U,", | ||
| 11115 | field, | ||
| 11116 | value); | ||
| 11117 | return REDISMODULE_OK; | ||
| 11118 | } | ||
| 11119 | ctx->info = sdscatfmt(ctx->info, | ||
| 11120 | "%s_%s:%U\r\n", | ||
| 11121 | ctx->module->name, | ||
| 11122 | field, | ||
| 11123 | value); | ||
| 11124 | return REDISMODULE_OK; | ||
| 11125 | } | ||
| 11126 | |||
| 11127 | /* Registers callback for the INFO command. The callback should add INFO fields | ||
| 11128 | * by calling the `RedisModule_InfoAddField*()` functions. */ | ||
| 11129 | int RM_RegisterInfoFunc(RedisModuleCtx *ctx, RedisModuleInfoFunc cb) { | ||
| 11130 | ctx->module->info_cb = cb; | ||
| 11131 | return REDISMODULE_OK; | ||
| 11132 | } | ||
| 11133 | |||
| 11134 | sds modulesCollectInfo(sds info, dict *sections_dict, int for_crash_report, int sections) { | ||
| 11135 | dictIterator di; | ||
| 11136 | dictEntry *de; | ||
| 11137 | |||
| 11138 | dictInitIterator(&di, modules); | ||
| 11139 | while ((de = dictNext(&di)) != NULL) { | ||
| 11140 | struct RedisModule *module = dictGetVal(de); | ||
| 11141 | if (!module->info_cb) | ||
| 11142 | continue; | ||
| 11143 | RedisModuleInfoCtx info_ctx = {module, sections_dict, info, sections, 0, 0}; | ||
| 11144 | module->info_cb(&info_ctx, for_crash_report); | ||
| 11145 | /* Implicitly end dicts (no way to handle errors, and we must add the newline). */ | ||
| 11146 | if (info_ctx.in_dict_field) | ||
| 11147 | RM_InfoEndDictField(&info_ctx); | ||
| 11148 | info = info_ctx.info; | ||
| 11149 | sections = info_ctx.sections; | ||
| 11150 | } | ||
| 11151 | dictResetIterator(&di); | ||
| 11152 | return info; | ||
| 11153 | } | ||
| 11154 | |||
| 11155 | /* Get information about the server similar to the one that returns from the | ||
| 11156 | * INFO command. This function takes an optional 'section' argument that may | ||
| 11157 | * be NULL. The return value holds the output and can be used with | ||
| 11158 | * RedisModule_ServerInfoGetField and alike to get the individual fields. | ||
| 11159 | * When done, it needs to be freed with RedisModule_FreeServerInfo or with the | ||
| 11160 | * automatic memory management mechanism if enabled. */ | ||
| 11161 | RedisModuleServerInfoData *RM_GetServerInfo(RedisModuleCtx *ctx, const char *section) { | ||
| 11162 | struct RedisModuleServerInfoData *d = zmalloc(sizeof(*d)); | ||
| 11163 | d->rax = raxNew(); | ||
| 11164 | if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_INFO,d); | ||
| 11165 | int all = 0, everything = 0; | ||
| 11166 | robj *argv[1]; | ||
| 11167 | argv[0] = section ? createStringObject(section, strlen(section)) : NULL; | ||
| 11168 | dict *section_dict = genInfoSectionDict(argv, section ? 1 : 0, NULL, &all, &everything); | ||
| 11169 | sds info = genRedisInfoString(section_dict, all, everything); | ||
| 11170 | int totlines, i; | ||
| 11171 | sds *lines = sdssplitlen(info, sdslen(info), "\r\n", 2, &totlines); | ||
| 11172 | for(i=0; i<totlines; i++) { | ||
| 11173 | sds line = lines[i]; | ||
| 11174 | if (line[0]=='#') continue; | ||
| 11175 | char *sep = strchr(line, ':'); | ||
| 11176 | if (!sep) continue; | ||
| 11177 | unsigned char *key = (unsigned char*)line; | ||
| 11178 | size_t keylen = (intptr_t)sep-(intptr_t)line; | ||
| 11179 | sds val = sdsnewlen(sep+1,sdslen(line)-((intptr_t)sep-(intptr_t)line)-1); | ||
| 11180 | if (!raxTryInsert(d->rax,key,keylen,val,NULL)) | ||
| 11181 | sdsfree(val); | ||
| 11182 | } | ||
| 11183 | sdsfree(info); | ||
| 11184 | sdsfreesplitres(lines,totlines); | ||
| 11185 | releaseInfoSectionDict(section_dict); | ||
| 11186 | if(argv[0]) decrRefCount(argv[0]); | ||
| 11187 | return d; | ||
| 11188 | } | ||
| 11189 | |||
| 11190 | /* Free data created with RM_GetServerInfo(). You need to pass the | ||
| 11191 | * context pointer 'ctx' only if the dictionary was created using the | ||
| 11192 | * context instead of passing NULL. */ | ||
| 11193 | void RM_FreeServerInfo(RedisModuleCtx *ctx, RedisModuleServerInfoData *data) { | ||
| 11194 | if (ctx != NULL) autoMemoryFreed(ctx,REDISMODULE_AM_INFO,data); | ||
| 11195 | raxFreeWithCallback(data->rax, sdsfreegeneric); | ||
| 11196 | zfree(data); | ||
| 11197 | } | ||
| 11198 | |||
| 11199 | /* Get the value of a field from data collected with RM_GetServerInfo(). You | ||
| 11200 | * need to pass the context pointer 'ctx' only if you want to use auto memory | ||
| 11201 | * mechanism to release the returned string. Return value will be NULL if the | ||
| 11202 | * field was not found. */ | ||
| 11203 | RedisModuleString *RM_ServerInfoGetField(RedisModuleCtx *ctx, RedisModuleServerInfoData *data, const char* field) { | ||
| 11204 | void *result; | ||
| 11205 | if (!raxFind(data->rax, (unsigned char *)field, strlen(field), &result)) | ||
| 11206 | return NULL; | ||
| 11207 | sds val = result; | ||
| 11208 | RedisModuleString *o = createStringObject(val,sdslen(val)); | ||
| 11209 | if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_STRING,o); | ||
| 11210 | return o; | ||
| 11211 | } | ||
| 11212 | |||
| 11213 | /* Similar to RM_ServerInfoGetField, but returns a char* which should not be freed but the caller. */ | ||
| 11214 | const char *RM_ServerInfoGetFieldC(RedisModuleServerInfoData *data, const char* field) { | ||
| 11215 | void *result = NULL; | ||
| 11216 | raxFind(data->rax, (unsigned char *)field, strlen(field), &result); | ||
| 11217 | return result; | ||
| 11218 | } | ||
| 11219 | |||
| 11220 | /* Get the value of a field from data collected with RM_GetServerInfo(). If the | ||
| 11221 | * field is not found, or is not numerical or out of range, return value will be | ||
| 11222 | * 0, and the optional out_err argument will be set to REDISMODULE_ERR. */ | ||
| 11223 | long long RM_ServerInfoGetFieldSigned(RedisModuleServerInfoData *data, const char* field, int *out_err) { | ||
| 11224 | long long ll; | ||
| 11225 | void *result; | ||
| 11226 | if (!raxFind(data->rax, (unsigned char *)field, strlen(field), &result)) { | ||
| 11227 | if (out_err) *out_err = REDISMODULE_ERR; | ||
| 11228 | return 0; | ||
| 11229 | } | ||
| 11230 | sds val = result; | ||
| 11231 | if (!string2ll(val,sdslen(val),&ll)) { | ||
| 11232 | if (out_err) *out_err = REDISMODULE_ERR; | ||
| 11233 | return 0; | ||
| 11234 | } | ||
| 11235 | if (out_err) *out_err = REDISMODULE_OK; | ||
| 11236 | return ll; | ||
| 11237 | } | ||
| 11238 | |||
| 11239 | /* Get the value of a field from data collected with RM_GetServerInfo(). If the | ||
| 11240 | * field is not found, or is not numerical or out of range, return value will be | ||
| 11241 | * 0, and the optional out_err argument will be set to REDISMODULE_ERR. */ | ||
| 11242 | unsigned long long RM_ServerInfoGetFieldUnsigned(RedisModuleServerInfoData *data, const char* field, int *out_err) { | ||
| 11243 | unsigned long long ll; | ||
| 11244 | void *result; | ||
| 11245 | if (!raxFind(data->rax, (unsigned char *)field, strlen(field), &result)) { | ||
| 11246 | if (out_err) *out_err = REDISMODULE_ERR; | ||
| 11247 | return 0; | ||
| 11248 | } | ||
| 11249 | sds val = result; | ||
| 11250 | if (!string2ull(val,&ll)) { | ||
| 11251 | if (out_err) *out_err = REDISMODULE_ERR; | ||
| 11252 | return 0; | ||
| 11253 | } | ||
| 11254 | if (out_err) *out_err = REDISMODULE_OK; | ||
| 11255 | return ll; | ||
| 11256 | } | ||
| 11257 | |||
| 11258 | /* Get the value of a field from data collected with RM_GetServerInfo(). If the | ||
| 11259 | * field is not found, or is not a double, return value will be 0, and the | ||
| 11260 | * optional out_err argument will be set to REDISMODULE_ERR. */ | ||
| 11261 | double RM_ServerInfoGetFieldDouble(RedisModuleServerInfoData *data, const char* field, int *out_err) { | ||
| 11262 | double dbl; | ||
| 11263 | void *result; | ||
| 11264 | if (!raxFind(data->rax, (unsigned char *)field, strlen(field), &result)) { | ||
| 11265 | if (out_err) *out_err = REDISMODULE_ERR; | ||
| 11266 | return 0; | ||
| 11267 | } | ||
| 11268 | sds val = result; | ||
| 11269 | if (!string2d(val,sdslen(val),&dbl)) { | ||
| 11270 | if (out_err) *out_err = REDISMODULE_ERR; | ||
| 11271 | return 0; | ||
| 11272 | } | ||
| 11273 | if (out_err) *out_err = REDISMODULE_OK; | ||
| 11274 | return dbl; | ||
| 11275 | } | ||
| 11276 | |||
| 11277 | /* -------------------------------------------------------------------------- | ||
| 11278 | * ## Modules utility APIs | ||
| 11279 | * -------------------------------------------------------------------------- */ | ||
| 11280 | |||
| 11281 | /* Return random bytes using SHA1 in counter mode with a /dev/urandom | ||
| 11282 | * initialized seed. This function is fast so can be used to generate | ||
| 11283 | * many bytes without any effect on the operating system entropy pool. | ||
| 11284 | * Currently this function is not thread safe. */ | ||
| 11285 | void RM_GetRandomBytes(unsigned char *dst, size_t len) { | ||
| 11286 | getRandomBytes(dst,len); | ||
| 11287 | } | ||
| 11288 | |||
| 11289 | /* Like RedisModule_GetRandomBytes() but instead of setting the string to | ||
| 11290 | * random bytes the string is set to random characters in the in the | ||
| 11291 | * hex charset [0-9a-f]. */ | ||
| 11292 | void RM_GetRandomHexChars(char *dst, size_t len) { | ||
| 11293 | getRandomHexChars(dst,len); | ||
| 11294 | } | ||
| 11295 | |||
| 11296 | /* -------------------------------------------------------------------------- | ||
| 11297 | * ## Modules API exporting / importing | ||
| 11298 | * -------------------------------------------------------------------------- */ | ||
| 11299 | |||
| 11300 | /* This function is called by a module in order to export some API with a | ||
| 11301 | * given name. Other modules will be able to use this API by calling the | ||
| 11302 | * symmetrical function RM_GetSharedAPI() and casting the return value to | ||
| 11303 | * the right function pointer. | ||
| 11304 | * | ||
| 11305 | * The function will return REDISMODULE_OK if the name is not already taken, | ||
| 11306 | * otherwise REDISMODULE_ERR will be returned and no operation will be | ||
| 11307 | * performed. | ||
| 11308 | * | ||
| 11309 | * IMPORTANT: the apiname argument should be a string literal with static | ||
| 11310 | * lifetime. The API relies on the fact that it will always be valid in | ||
| 11311 | * the future. */ | ||
| 11312 | int RM_ExportSharedAPI(RedisModuleCtx *ctx, const char *apiname, void *func) { | ||
| 11313 | RedisModuleSharedAPI *sapi = zmalloc(sizeof(*sapi)); | ||
| 11314 | sapi->module = ctx->module; | ||
| 11315 | sapi->func = func; | ||
| 11316 | if (dictAdd(server.sharedapi, (char*)apiname, sapi) != DICT_OK) { | ||
| 11317 | zfree(sapi); | ||
| 11318 | return REDISMODULE_ERR; | ||
| 11319 | } | ||
| 11320 | return REDISMODULE_OK; | ||
| 11321 | } | ||
| 11322 | |||
| 11323 | /* Request an exported API pointer. The return value is just a void pointer | ||
| 11324 | * that the caller of this function will be required to cast to the right | ||
| 11325 | * function pointer, so this is a private contract between modules. | ||
| 11326 | * | ||
| 11327 | * If the requested API is not available then NULL is returned. Because | ||
| 11328 | * modules can be loaded at different times with different order, this | ||
| 11329 | * function calls should be put inside some module generic API registering | ||
| 11330 | * step, that is called every time a module attempts to execute a | ||
| 11331 | * command that requires external APIs: if some API cannot be resolved, the | ||
| 11332 | * command should return an error. | ||
| 11333 | * | ||
| 11334 | * Here is an example: | ||
| 11335 | * | ||
| 11336 | * int ... myCommandImplementation(void) { | ||
| 11337 | * if (getExternalAPIs() == 0) { | ||
| 11338 | * reply with an error here if we cannot have the APIs | ||
| 11339 | * } | ||
| 11340 | * // Use the API: | ||
| 11341 | * myFunctionPointer(foo); | ||
| 11342 | * } | ||
| 11343 | * | ||
| 11344 | * And the function registerAPI() is: | ||
| 11345 | * | ||
| 11346 | * int getExternalAPIs(void) { | ||
| 11347 | * static int api_loaded = 0; | ||
| 11348 | * if (api_loaded != 0) return 1; // APIs already resolved. | ||
| 11349 | * | ||
| 11350 | * myFunctionPointer = RedisModule_GetSharedAPI("..."); | ||
| 11351 | * if (myFunctionPointer == NULL) return 0; | ||
| 11352 | * | ||
| 11353 | * return 1; | ||
| 11354 | * } | ||
| 11355 | */ | ||
| 11356 | void *RM_GetSharedAPI(RedisModuleCtx *ctx, const char *apiname) { | ||
| 11357 | dictEntry *de = dictFind(server.sharedapi, apiname); | ||
| 11358 | if (de == NULL) return NULL; | ||
| 11359 | RedisModuleSharedAPI *sapi = dictGetVal(de); | ||
| 11360 | if (listSearchKey(sapi->module->usedby,ctx->module) == NULL) { | ||
| 11361 | listAddNodeTail(sapi->module->usedby,ctx->module); | ||
| 11362 | listAddNodeTail(ctx->module->using,sapi->module); | ||
| 11363 | } | ||
| 11364 | return sapi->func; | ||
| 11365 | } | ||
| 11366 | |||
| 11367 | /* Remove all the APIs registered by the specified module. Usually you | ||
| 11368 | * want this when the module is going to be unloaded. This function | ||
| 11369 | * assumes that's caller responsibility to make sure the APIs are not | ||
| 11370 | * used by other modules. | ||
| 11371 | * | ||
| 11372 | * The number of unregistered APIs is returned. */ | ||
| 11373 | int moduleUnregisterSharedAPI(RedisModule *module) { | ||
| 11374 | int count = 0; | ||
| 11375 | dictIterator di; | ||
| 11376 | dictEntry *de; | ||
| 11377 | dictInitSafeIterator(&di, server.sharedapi); | ||
| 11378 | while ((de = dictNext(&di)) != NULL) { | ||
| 11379 | const char *apiname = dictGetKey(de); | ||
| 11380 | RedisModuleSharedAPI *sapi = dictGetVal(de); | ||
| 11381 | if (sapi->module == module) { | ||
| 11382 | dictDelete(server.sharedapi,apiname); | ||
| 11383 | zfree(sapi); | ||
| 11384 | count++; | ||
| 11385 | } | ||
| 11386 | } | ||
| 11387 | dictResetIterator(&di); | ||
| 11388 | return count; | ||
| 11389 | } | ||
| 11390 | |||
| 11391 | /* Remove the specified module as an user of APIs of ever other module. | ||
| 11392 | * This is usually called when a module is unloaded. | ||
| 11393 | * | ||
| 11394 | * Returns the number of modules this module was using APIs from. */ | ||
| 11395 | int moduleUnregisterUsedAPI(RedisModule *module) { | ||
| 11396 | listIter li; | ||
| 11397 | listNode *ln; | ||
| 11398 | int count = 0; | ||
| 11399 | |||
| 11400 | listRewind(module->using,&li); | ||
| 11401 | while((ln = listNext(&li))) { | ||
| 11402 | RedisModule *used = ln->value; | ||
| 11403 | listNode *ln = listSearchKey(used->usedby,module); | ||
| 11404 | if (ln) { | ||
| 11405 | listDelNode(used->usedby,ln); | ||
| 11406 | count++; | ||
| 11407 | } | ||
| 11408 | } | ||
| 11409 | return count; | ||
| 11410 | } | ||
| 11411 | |||
| 11412 | /* Unregister all filters registered by a module. | ||
| 11413 | * This is called when a module is being unloaded. | ||
| 11414 | * | ||
| 11415 | * Returns the number of filters unregistered. */ | ||
| 11416 | int moduleUnregisterFilters(RedisModule *module) { | ||
| 11417 | listIter li; | ||
| 11418 | listNode *ln; | ||
| 11419 | int count = 0; | ||
| 11420 | |||
| 11421 | listRewind(module->filters,&li); | ||
| 11422 | while((ln = listNext(&li))) { | ||
| 11423 | RedisModuleCommandFilter *filter = ln->value; | ||
| 11424 | listNode *ln = listSearchKey(moduleCommandFilters,filter); | ||
| 11425 | if (ln) { | ||
| 11426 | listDelNode(moduleCommandFilters,ln); | ||
| 11427 | count++; | ||
| 11428 | } | ||
| 11429 | zfree(filter); | ||
| 11430 | } | ||
| 11431 | return count; | ||
| 11432 | } | ||
| 11433 | |||
| 11434 | /* -------------------------------------------------------------------------- | ||
| 11435 | * ## Module Command Filter API | ||
| 11436 | * -------------------------------------------------------------------------- */ | ||
| 11437 | |||
| 11438 | /* Register a new command filter function. | ||
| 11439 | * | ||
| 11440 | * Command filtering makes it possible for modules to extend Redis by plugging | ||
| 11441 | * into the execution flow of all commands. | ||
| 11442 | * | ||
| 11443 | * A registered filter gets called before Redis executes *any* command. This | ||
| 11444 | * includes both core Redis commands and commands registered by any module. The | ||
| 11445 | * filter applies in all execution paths including: | ||
| 11446 | * | ||
| 11447 | * 1. Invocation by a client. | ||
| 11448 | * 2. Invocation through `RedisModule_Call()` by any module. | ||
| 11449 | * 3. Invocation through Lua `redis.call()`. | ||
| 11450 | * 4. Replication of a command from a master. | ||
| 11451 | * | ||
| 11452 | * The filter executes in a special filter context, which is different and more | ||
| 11453 | * limited than a RedisModuleCtx. Because the filter affects any command, it | ||
| 11454 | * must be implemented in a very efficient way to reduce the performance impact | ||
| 11455 | * on Redis. All Redis Module API calls that require a valid context (such as | ||
| 11456 | * `RedisModule_Call()`, `RedisModule_OpenKey()`, etc.) are not supported in a | ||
| 11457 | * filter context. | ||
| 11458 | * | ||
| 11459 | * The `RedisModuleCommandFilterCtx` can be used to inspect or modify the | ||
| 11460 | * executed command and its arguments. As the filter executes before Redis | ||
| 11461 | * begins processing the command, any change will affect the way the command is | ||
| 11462 | * processed. For example, a module can override Redis commands this way: | ||
| 11463 | * | ||
| 11464 | * 1. Register a `MODULE.SET` command which implements an extended version of | ||
| 11465 | * the Redis `SET` command. | ||
| 11466 | * 2. Register a command filter which detects invocation of `SET` on a specific | ||
| 11467 | * pattern of keys. Once detected, the filter will replace the first | ||
| 11468 | * argument from `SET` to `MODULE.SET`. | ||
| 11469 | * 3. When filter execution is complete, Redis considers the new command name | ||
| 11470 | * and therefore executes the module's own command. | ||
| 11471 | * | ||
| 11472 | * Note that in the above use case, if `MODULE.SET` itself uses | ||
| 11473 | * `RedisModule_Call()` the filter will be applied on that call as well. If | ||
| 11474 | * that is not desired, the `REDISMODULE_CMDFILTER_NOSELF` flag can be set when | ||
| 11475 | * registering the filter. | ||
| 11476 | * | ||
| 11477 | * The `REDISMODULE_CMDFILTER_NOSELF` flag prevents execution flows that | ||
| 11478 | * originate from the module's own `RM_Call()` from reaching the filter. This | ||
| 11479 | * flag is effective for all execution flows, including nested ones, as long as | ||
| 11480 | * the execution begins from the module's command context or a thread-safe | ||
| 11481 | * context that is associated with a blocking command. | ||
| 11482 | * | ||
| 11483 | * Detached thread-safe contexts are *not* associated with the module and cannot | ||
| 11484 | * be protected by this flag. | ||
| 11485 | * | ||
| 11486 | * If multiple filters are registered (by the same or different modules), they | ||
| 11487 | * are executed in the order of registration. | ||
| 11488 | */ | ||
| 11489 | RedisModuleCommandFilter *RM_RegisterCommandFilter(RedisModuleCtx *ctx, RedisModuleCommandFilterFunc callback, int flags) { | ||
| 11490 | RedisModuleCommandFilter *filter = zmalloc(sizeof(*filter)); | ||
| 11491 | filter->module = ctx->module; | ||
| 11492 | filter->callback = callback; | ||
| 11493 | filter->flags = flags; | ||
| 11494 | |||
| 11495 | listAddNodeTail(moduleCommandFilters, filter); | ||
| 11496 | listAddNodeTail(ctx->module->filters, filter); | ||
| 11497 | return filter; | ||
| 11498 | } | ||
| 11499 | |||
| 11500 | /* Unregister a command filter. | ||
| 11501 | */ | ||
| 11502 | int RM_UnregisterCommandFilter(RedisModuleCtx *ctx, RedisModuleCommandFilter *filter) { | ||
| 11503 | listNode *ln; | ||
| 11504 | |||
| 11505 | /* A module can only remove its own filters */ | ||
| 11506 | if (filter->module != ctx->module) return REDISMODULE_ERR; | ||
| 11507 | |||
| 11508 | ln = listSearchKey(moduleCommandFilters,filter); | ||
| 11509 | if (!ln) return REDISMODULE_ERR; | ||
| 11510 | listDelNode(moduleCommandFilters,ln); | ||
| 11511 | |||
| 11512 | ln = listSearchKey(ctx->module->filters,filter); | ||
| 11513 | if (!ln) return REDISMODULE_ERR; /* Shouldn't happen */ | ||
| 11514 | listDelNode(ctx->module->filters,ln); | ||
| 11515 | |||
| 11516 | zfree(filter); | ||
| 11517 | |||
| 11518 | return REDISMODULE_OK; | ||
| 11519 | } | ||
| 11520 | |||
| 11521 | void moduleCallCommandFilters(client *c) { | ||
| 11522 | if (listLength(moduleCommandFilters) == 0) return; | ||
| 11523 | |||
| 11524 | listIter li; | ||
| 11525 | listNode *ln; | ||
| 11526 | listRewind(moduleCommandFilters,&li); | ||
| 11527 | |||
| 11528 | RedisModuleCommandFilterCtx filter = { | ||
| 11529 | .argv = c->argv, | ||
| 11530 | .argv_len = c->argv_len, | ||
| 11531 | .argc = c->argc, | ||
| 11532 | .c = c | ||
| 11533 | }; | ||
| 11534 | |||
| 11535 | while((ln = listNext(&li))) { | ||
| 11536 | RedisModuleCommandFilter *f = ln->value; | ||
| 11537 | |||
| 11538 | /* Skip filter if REDISMODULE_CMDFILTER_NOSELF is set and module is | ||
| 11539 | * currently processing a command. | ||
| 11540 | */ | ||
| 11541 | if ((f->flags & REDISMODULE_CMDFILTER_NOSELF) && f->module->in_call) continue; | ||
| 11542 | |||
| 11543 | /* Call filter */ | ||
| 11544 | f->callback(&filter); | ||
| 11545 | } | ||
| 11546 | |||
| 11547 | /* If the filter sets a new command, including command or subcommand, | ||
| 11548 | * the command looked up will be invalid. */ | ||
| 11549 | c->lookedcmd = NULL; | ||
| 11550 | |||
| 11551 | c->argv = filter.argv; | ||
| 11552 | c->argv_len = filter.argv_len; | ||
| 11553 | c->argc = filter.argc; | ||
| 11554 | |||
| 11555 | /* Update pending command if it exists. */ | ||
| 11556 | pendingCommand *pcmd = c->current_pending_cmd; | ||
| 11557 | if (pcmd) { | ||
| 11558 | pcmd->argv = filter.argv; | ||
| 11559 | pcmd->argc = filter.argc; | ||
| 11560 | pcmd->argv_len = filter.argv_len; | ||
| 11561 | pcmd->cmd = NULL; | ||
| 11562 | pcmd->slot = INVALID_CLUSTER_SLOT; | ||
| 11563 | pcmd->flags = 0; | ||
| 11564 | |||
| 11565 | /* Reset keys result */ | ||
| 11566 | getKeysFreeResult(&pcmd->keys_result); | ||
| 11567 | pcmd->keys_result = (getKeysResult)GETKEYS_RESULT_INIT; | ||
| 11568 | } | ||
| 11569 | } | ||
| 11570 | |||
| 11571 | /* Return the number of arguments a filtered command has. The number of | ||
| 11572 | * arguments include the command itself. | ||
| 11573 | */ | ||
| 11574 | int RM_CommandFilterArgsCount(RedisModuleCommandFilterCtx *fctx) | ||
| 11575 | { | ||
| 11576 | return fctx->argc; | ||
| 11577 | } | ||
| 11578 | |||
| 11579 | /* Return the specified command argument. The first argument (position 0) is | ||
| 11580 | * the command itself, and the rest are user-provided args. | ||
| 11581 | */ | ||
| 11582 | RedisModuleString *RM_CommandFilterArgGet(RedisModuleCommandFilterCtx *fctx, int pos) | ||
| 11583 | { | ||
| 11584 | if (pos < 0 || pos >= fctx->argc) return NULL; | ||
| 11585 | return fctx->argv[pos]; | ||
| 11586 | } | ||
| 11587 | |||
| 11588 | /* Modify the filtered command by inserting a new argument at the specified | ||
| 11589 | * position. The specified RedisModuleString argument may be used by Redis | ||
| 11590 | * after the filter context is destroyed, so it must not be auto-memory | ||
| 11591 | * allocated, freed or used elsewhere. | ||
| 11592 | */ | ||
| 11593 | int RM_CommandFilterArgInsert(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg) | ||
| 11594 | { | ||
| 11595 | int i; | ||
| 11596 | |||
| 11597 | if (pos < 0 || pos > fctx->argc) return REDISMODULE_ERR; | ||
| 11598 | |||
| 11599 | if (fctx->argv_len < fctx->argc+1) { | ||
| 11600 | fctx->argv_len = fctx->argc+1; | ||
| 11601 | fctx->argv = zrealloc(fctx->argv, fctx->argv_len*sizeof(RedisModuleString *)); | ||
| 11602 | } | ||
| 11603 | for (i = fctx->argc; i > pos; i--) { | ||
| 11604 | fctx->argv[i] = fctx->argv[i-1]; | ||
| 11605 | } | ||
| 11606 | fctx->argv[pos] = arg; | ||
| 11607 | fctx->argc++; | ||
| 11608 | |||
| 11609 | return REDISMODULE_OK; | ||
| 11610 | } | ||
| 11611 | |||
| 11612 | /* Modify the filtered command by replacing an existing argument with a new one. | ||
| 11613 | * The specified RedisModuleString argument may be used by Redis after the | ||
| 11614 | * filter context is destroyed, so it must not be auto-memory allocated, freed | ||
| 11615 | * or used elsewhere. | ||
| 11616 | */ | ||
| 11617 | int RM_CommandFilterArgReplace(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg) | ||
| 11618 | { | ||
| 11619 | if (pos < 0 || pos >= fctx->argc) return REDISMODULE_ERR; | ||
| 11620 | |||
| 11621 | decrRefCount(fctx->argv[pos]); | ||
| 11622 | fctx->argv[pos] = arg; | ||
| 11623 | |||
| 11624 | return REDISMODULE_OK; | ||
| 11625 | } | ||
| 11626 | |||
| 11627 | /* Modify the filtered command by deleting an argument at the specified | ||
| 11628 | * position. | ||
| 11629 | */ | ||
| 11630 | int RM_CommandFilterArgDelete(RedisModuleCommandFilterCtx *fctx, int pos) | ||
| 11631 | { | ||
| 11632 | int i; | ||
| 11633 | if (pos < 0 || pos >= fctx->argc) return REDISMODULE_ERR; | ||
| 11634 | |||
| 11635 | decrRefCount(fctx->argv[pos]); | ||
| 11636 | for (i = pos; i < fctx->argc-1; i++) { | ||
| 11637 | fctx->argv[i] = fctx->argv[i+1]; | ||
| 11638 | } | ||
| 11639 | fctx->argc--; | ||
| 11640 | |||
| 11641 | return REDISMODULE_OK; | ||
| 11642 | } | ||
| 11643 | |||
| 11644 | /* Get Client ID for client that issued the command we are filtering */ | ||
| 11645 | unsigned long long RM_CommandFilterGetClientId(RedisModuleCommandFilterCtx *fctx) { | ||
| 11646 | return fctx->c->id; | ||
| 11647 | } | ||
| 11648 | |||
| 11649 | /* For a given pointer allocated via RedisModule_Alloc() or | ||
| 11650 | * RedisModule_Realloc(), return the amount of memory allocated for it. | ||
| 11651 | * Note that this may be different (larger) than the memory we allocated | ||
| 11652 | * with the allocation calls, since sometimes the underlying allocator | ||
| 11653 | * will allocate more memory. | ||
| 11654 | */ | ||
| 11655 | size_t RM_MallocSize(void* ptr) { | ||
| 11656 | return zmalloc_size(ptr); | ||
| 11657 | } | ||
| 11658 | |||
| 11659 | /* Similar to RM_MallocSize, the difference is that RM_MallocUsableSize | ||
| 11660 | * returns the usable size of memory by the module. */ | ||
| 11661 | size_t RM_MallocUsableSize(void *ptr) { | ||
| 11662 | /* It is safe to use 'zmalloc_usable_size()' to manipulate additional | ||
| 11663 | * memory space, as we guarantee that the compiler can recognize this | ||
| 11664 | * after 'RM_Alloc', 'RM_TryAlloc', 'RM_Realloc', or 'RM_Calloc'. */ | ||
| 11665 | return zmalloc_usable_size(ptr); | ||
| 11666 | } | ||
| 11667 | |||
| 11668 | /* Same as RM_MallocSize, except it works on RedisModuleString pointers. | ||
| 11669 | */ | ||
| 11670 | size_t RM_MallocSizeString(RedisModuleString* str) { | ||
| 11671 | serverAssert(str->type == OBJ_STRING); | ||
| 11672 | return sizeof(*str) + getStringObjectSdsUsedMemory(str); | ||
| 11673 | } | ||
| 11674 | |||
| 11675 | /* Same as RM_MallocSize, except it works on RedisModuleDict pointers. | ||
| 11676 | * Note that the returned value is only the overhead of the underlying structures, | ||
| 11677 | * it does not include the allocation size of the keys and values. | ||
| 11678 | */ | ||
| 11679 | size_t RM_MallocSizeDict(RedisModuleDict* dict) { | ||
| 11680 | size_t size = sizeof(RedisModuleDict) + sizeof(rax); | ||
| 11681 | size += dict->rax->numnodes * sizeof(raxNode); | ||
| 11682 | /* For more info about this weird line, see streamRadixTreeMemoryUsage */ | ||
| 11683 | size += dict->rax->numnodes * sizeof(long)*30; | ||
| 11684 | return size; | ||
| 11685 | } | ||
| 11686 | |||
| 11687 | /* Return the a number between 0 to 1 indicating the amount of memory | ||
| 11688 | * currently used, relative to the Redis "maxmemory" configuration. | ||
| 11689 | * | ||
| 11690 | * * 0 - No memory limit configured. | ||
| 11691 | * * Between 0 and 1 - The percentage of the memory used normalized in 0-1 range. | ||
| 11692 | * * Exactly 1 - Memory limit reached. | ||
| 11693 | * * Greater 1 - More memory used than the configured limit. | ||
| 11694 | */ | ||
| 11695 | float RM_GetUsedMemoryRatio(void){ | ||
| 11696 | float level; | ||
| 11697 | getMaxmemoryState(NULL, NULL, NULL, &level); | ||
| 11698 | return level; | ||
| 11699 | } | ||
| 11700 | |||
| 11701 | /* -------------------------------------------------------------------------- | ||
| 11702 | * ## Scanning keyspace and hashes | ||
| 11703 | * -------------------------------------------------------------------------- */ | ||
| 11704 | |||
| 11705 | typedef void (*RedisModuleScanCB)(RedisModuleCtx *ctx, RedisModuleString *keyname, RedisModuleKey *key, void *privdata); | ||
| 11706 | typedef struct { | ||
| 11707 | RedisModuleCtx *ctx; | ||
| 11708 | void* user_data; | ||
| 11709 | RedisModuleScanCB fn; | ||
| 11710 | } ScanCBData; | ||
| 11711 | |||
| 11712 | typedef struct RedisModuleScanCursor{ | ||
| 11713 | unsigned long long cursor; | ||
| 11714 | int done; | ||
| 11715 | }RedisModuleScanCursor; | ||
| 11716 | |||
| 11717 | static void moduleScanCallback(void *privdata, const dictEntry *de, dictEntryLink plink) { | ||
| 11718 | UNUSED(plink); | ||
| 11719 | ScanCBData *data = privdata; | ||
| 11720 | kvobj *keyvalObj = dictGetKey(de); | ||
| 11721 | sds key = kvobjGetKey(keyvalObj); | ||
| 11722 | RedisModuleString *keyname = createObject(OBJ_STRING,sdsdup(key)); | ||
| 11723 | |||
| 11724 | /* Setup the key handle. */ | ||
| 11725 | RedisModuleKey kp = {0}; | ||
| 11726 | moduleInitKey(&kp, data->ctx, keyname, keyvalObj, REDISMODULE_READ); | ||
| 11727 | |||
| 11728 | data->fn(data->ctx, keyname, &kp, data->user_data); | ||
| 11729 | |||
| 11730 | moduleCloseKey(&kp); | ||
| 11731 | decrRefCount(keyname); | ||
| 11732 | } | ||
| 11733 | |||
| 11734 | /* Create a new cursor to be used with RedisModule_Scan */ | ||
| 11735 | RedisModuleScanCursor *RM_ScanCursorCreate(void) { | ||
| 11736 | RedisModuleScanCursor* cursor = zmalloc(sizeof(*cursor)); | ||
| 11737 | cursor->cursor = 0; | ||
| 11738 | cursor->done = 0; | ||
| 11739 | return cursor; | ||
| 11740 | } | ||
| 11741 | |||
| 11742 | /* Restart an existing cursor. The keys will be rescanned. */ | ||
| 11743 | void RM_ScanCursorRestart(RedisModuleScanCursor *cursor) { | ||
| 11744 | cursor->cursor = 0; | ||
| 11745 | cursor->done = 0; | ||
| 11746 | } | ||
| 11747 | |||
| 11748 | /* Destroy the cursor struct. */ | ||
| 11749 | void RM_ScanCursorDestroy(RedisModuleScanCursor *cursor) { | ||
| 11750 | zfree(cursor); | ||
| 11751 | } | ||
| 11752 | |||
| 11753 | /* Scan API that allows a module to scan all the keys and value in | ||
| 11754 | * the selected db. | ||
| 11755 | * | ||
| 11756 | * Callback for scan implementation. | ||
| 11757 | * | ||
| 11758 | * void scan_callback(RedisModuleCtx *ctx, RedisModuleString *keyname, | ||
| 11759 | * RedisModuleKey *key, void *privdata); | ||
| 11760 | * | ||
| 11761 | * - `ctx`: the redis module context provided to for the scan. | ||
| 11762 | * - `keyname`: owned by the caller and need to be retained if used after this | ||
| 11763 | * function. | ||
| 11764 | * - `key`: holds info on the key and value, it is provided as best effort, in | ||
| 11765 | * some cases it might be NULL, in which case the user should (can) use | ||
| 11766 | * RedisModule_OpenKey() (and CloseKey too). | ||
| 11767 | * when it is provided, it is owned by the caller and will be free when the | ||
| 11768 | * callback returns. | ||
| 11769 | * - `privdata`: the user data provided to RedisModule_Scan(). | ||
| 11770 | * | ||
| 11771 | * The way it should be used: | ||
| 11772 | * | ||
| 11773 | * RedisModuleScanCursor *c = RedisModule_ScanCursorCreate(); | ||
| 11774 | * while(RedisModule_Scan(ctx, c, callback, privateData)); | ||
| 11775 | * RedisModule_ScanCursorDestroy(c); | ||
| 11776 | * | ||
| 11777 | * It is also possible to use this API from another thread while the lock | ||
| 11778 | * is acquired during the actual call to RM_Scan: | ||
| 11779 | * | ||
| 11780 | * RedisModuleScanCursor *c = RedisModule_ScanCursorCreate(); | ||
| 11781 | * RedisModule_ThreadSafeContextLock(ctx); | ||
| 11782 | * while(RedisModule_Scan(ctx, c, callback, privateData)){ | ||
| 11783 | * RedisModule_ThreadSafeContextUnlock(ctx); | ||
| 11784 | * // do some background job | ||
| 11785 | * RedisModule_ThreadSafeContextLock(ctx); | ||
| 11786 | * } | ||
| 11787 | * RedisModule_ScanCursorDestroy(c); | ||
| 11788 | * | ||
| 11789 | * The function will return 1 if there are more elements to scan and | ||
| 11790 | * 0 otherwise, possibly setting errno if the call failed. | ||
| 11791 | * | ||
| 11792 | * It is also possible to restart an existing cursor using RM_ScanCursorRestart. | ||
| 11793 | * | ||
| 11794 | * IMPORTANT: This API is very similar to the Redis SCAN command from the | ||
| 11795 | * point of view of the guarantees it provides. This means that the API | ||
| 11796 | * may report duplicated keys, but guarantees to report at least one time | ||
| 11797 | * every key that was there from the start to the end of the scanning process. | ||
| 11798 | * | ||
| 11799 | * NOTE: If you do database changes within the callback, you should be aware | ||
| 11800 | * that the internal state of the database may change. For instance it is safe | ||
| 11801 | * to delete or modify the current key, but may not be safe to delete any | ||
| 11802 | * other key. | ||
| 11803 | * Moreover playing with the Redis keyspace while iterating may have the | ||
| 11804 | * effect of returning more duplicates. A safe pattern is to store the keys | ||
| 11805 | * names you want to modify elsewhere, and perform the actions on the keys | ||
| 11806 | * later when the iteration is complete. However this can cost a lot of | ||
| 11807 | * memory, so it may make sense to just operate on the current key when | ||
| 11808 | * possible during the iteration, given that this is safe. */ | ||
| 11809 | int RM_Scan(RedisModuleCtx *ctx, RedisModuleScanCursor *cursor, RedisModuleScanCB fn, void *privdata) { | ||
| 11810 | if (cursor->done) { | ||
| 11811 | errno = ENOENT; | ||
| 11812 | return 0; | ||
| 11813 | } | ||
| 11814 | int ret = 1; | ||
| 11815 | ScanCBData data = { ctx, privdata, fn }; | ||
| 11816 | cursor->cursor = dbScan(ctx->client->db, cursor->cursor, moduleScanCallback, &data); | ||
| 11817 | if (cursor->cursor == 0) { | ||
| 11818 | cursor->done = 1; | ||
| 11819 | ret = 0; | ||
| 11820 | } | ||
| 11821 | errno = 0; | ||
| 11822 | return ret; | ||
| 11823 | } | ||
| 11824 | |||
| 11825 | typedef void (*RedisModuleScanKeyCB)(RedisModuleKey *key, RedisModuleString *field, RedisModuleString *value, void *privdata); | ||
| 11826 | typedef struct { | ||
| 11827 | RedisModuleKey *key; | ||
| 11828 | void* user_data; | ||
| 11829 | RedisModuleScanKeyCB fn; | ||
| 11830 | } ScanKeyCBData; | ||
| 11831 | |||
| 11832 | static void moduleScanKeyCallback(void *privdata, const dictEntry *de, dictEntryLink plink) { | ||
| 11833 | UNUSED(plink); | ||
| 11834 | ScanKeyCBData *data = privdata; | ||
| 11835 | sds key = dictGetKey(de); | ||
| 11836 | kvobj *kv = data->key->kv; | ||
| 11837 | robj *field = NULL; | ||
| 11838 | robj *value = NULL; | ||
| 11839 | if (kv->type == OBJ_SET) { | ||
| 11840 | field = createStringObject(key, sdslen(key)); | ||
| 11841 | value = NULL; | ||
| 11842 | } else if (kv->type == OBJ_HASH) { | ||
| 11843 | Entry *e = (Entry *) key; | ||
| 11844 | |||
| 11845 | /* If field is expired and not indicated to access expired, then ignore */ | ||
| 11846 | if ((!(data->key->mode & REDISMODULE_OPEN_KEY_ACCESS_EXPIRED)) && | ||
| 11847 | (entryIsExpired(e))) | ||
| 11848 | return; | ||
| 11849 | |||
| 11850 | /* For hash, the value is stored in the entry (field), not in the dict entry */ | ||
| 11851 | sds fieldStr = entryGetField(e); | ||
| 11852 | sds val = entryGetValue(e); | ||
| 11853 | |||
| 11854 | field = createStringObject(fieldStr, sdslen(fieldStr)); | ||
| 11855 | value = createStringObject(val, sdslen(val)); | ||
| 11856 | } else if (kv->type == OBJ_ZSET) { | ||
| 11857 | zskiplistNode *znode = (zskiplistNode *) key; | ||
| 11858 | sds fieldStr = zslGetNodeElement(znode); | ||
| 11859 | field = createStringObject(fieldStr, sdslen(fieldStr)); | ||
| 11860 | value = createStringObjectFromLongDouble(znode->score, 0); | ||
| 11861 | } | ||
| 11862 | |||
| 11863 | serverAssert(field != NULL); | ||
| 11864 | data->fn(data->key, field, value, data->user_data); | ||
| 11865 | decrRefCount(field); | ||
| 11866 | if (value) decrRefCount(value); | ||
| 11867 | } | ||
| 11868 | |||
| 11869 | /* Scan api that allows a module to scan the elements in a hash, set or sorted set key | ||
| 11870 | * | ||
| 11871 | * Callback for scan implementation. | ||
| 11872 | * | ||
| 11873 | * void scan_callback(RedisModuleKey *key, RedisModuleString* field, RedisModuleString* value, void *privdata); | ||
| 11874 | * | ||
| 11875 | * - key - the redis key context provided to for the scan. | ||
| 11876 | * - field - field name, owned by the caller and need to be retained if used | ||
| 11877 | * after this function. | ||
| 11878 | * - value - value string or NULL for set type, owned by the caller and need to | ||
| 11879 | * be retained if used after this function. | ||
| 11880 | * - privdata - the user data provided to RedisModule_ScanKey. | ||
| 11881 | * | ||
| 11882 | * The way it should be used: | ||
| 11883 | * | ||
| 11884 | * RedisModuleScanCursor *c = RedisModule_ScanCursorCreate(); | ||
| 11885 | * RedisModuleKey *key = RedisModule_OpenKey(...); | ||
| 11886 | * while(RedisModule_ScanKey(key, c, callback, privateData)); | ||
| 11887 | * RedisModule_CloseKey(key); | ||
| 11888 | * RedisModule_ScanCursorDestroy(c); | ||
| 11889 | * | ||
| 11890 | * It is also possible to use this API from another thread while the lock is acquired during | ||
| 11891 | * the actual call to RM_ScanKey, and re-opening the key each time: | ||
| 11892 | * | ||
| 11893 | * RedisModuleScanCursor *c = RedisModule_ScanCursorCreate(); | ||
| 11894 | * RedisModule_ThreadSafeContextLock(ctx); | ||
| 11895 | * RedisModuleKey *key = RedisModule_OpenKey(...); | ||
| 11896 | * while(RedisModule_ScanKey(ctx, c, callback, privateData)){ | ||
| 11897 | * RedisModule_CloseKey(key); | ||
| 11898 | * RedisModule_ThreadSafeContextUnlock(ctx); | ||
| 11899 | * // do some background job | ||
| 11900 | * RedisModule_ThreadSafeContextLock(ctx); | ||
| 11901 | * key = RedisModule_OpenKey(...); | ||
| 11902 | * } | ||
| 11903 | * RedisModule_CloseKey(key); | ||
| 11904 | * RedisModule_ScanCursorDestroy(c); | ||
| 11905 | * | ||
| 11906 | * The function will return 1 if there are more elements to scan and 0 otherwise, | ||
| 11907 | * possibly setting errno if the call failed. | ||
| 11908 | * It is also possible to restart an existing cursor using RM_ScanCursorRestart. | ||
| 11909 | * | ||
| 11910 | * NOTE: Certain operations are unsafe while iterating the object. For instance | ||
| 11911 | * while the API guarantees to return at least one time all the elements that | ||
| 11912 | * are present in the data structure consistently from the start to the end | ||
| 11913 | * of the iteration (see HSCAN and similar commands documentation), the more | ||
| 11914 | * you play with the elements, the more duplicates you may get. In general | ||
| 11915 | * deleting the current element of the data structure is safe, while removing | ||
| 11916 | * the key you are iterating is not safe. */ | ||
| 11917 | int RM_ScanKey(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleScanKeyCB fn, void *privdata) { | ||
| 11918 | if (key == NULL || key->kv == NULL) { | ||
| 11919 | errno = EINVAL; | ||
| 11920 | return 0; | ||
| 11921 | } | ||
| 11922 | dict *ht = NULL; | ||
| 11923 | kvobj *kv = key->kv; | ||
| 11924 | if (kv->type == OBJ_SET) { | ||
| 11925 | if (kv->encoding == OBJ_ENCODING_HT) | ||
| 11926 | ht = kv->ptr; | ||
| 11927 | } else if (kv->type == OBJ_HASH) { | ||
| 11928 | if (kv->encoding == OBJ_ENCODING_HT) | ||
| 11929 | ht = kv->ptr; | ||
| 11930 | } else if (kv->type == OBJ_ZSET) { | ||
| 11931 | if (kv->encoding == OBJ_ENCODING_SKIPLIST) | ||
| 11932 | ht = ((zset *)kv->ptr)->dict; | ||
| 11933 | } else { | ||
| 11934 | errno = EINVAL; | ||
| 11935 | return 0; | ||
| 11936 | } | ||
| 11937 | if (cursor->done) { | ||
| 11938 | errno = ENOENT; | ||
| 11939 | return 0; | ||
| 11940 | } | ||
| 11941 | int ret = 1; | ||
| 11942 | if (ht) { | ||
| 11943 | ScanKeyCBData data = { key, privdata, fn }; | ||
| 11944 | cursor->cursor = dictScan(ht, cursor->cursor, moduleScanKeyCallback, &data); | ||
| 11945 | if (cursor->cursor == 0) { | ||
| 11946 | cursor->done = 1; | ||
| 11947 | ret = 0; | ||
| 11948 | } | ||
| 11949 | } else if (kv->type == OBJ_SET) { | ||
| 11950 | setTypeIterator si; | ||
| 11951 | sds sdsele; | ||
| 11952 | setTypeInitIterator(&si, kv); | ||
| 11953 | while ((sdsele = setTypeNextObject(&si)) != NULL) { | ||
| 11954 | robj *field = createObject(OBJ_STRING, sdsele); | ||
| 11955 | fn(key, field, NULL, privdata); | ||
| 11956 | decrRefCount(field); | ||
| 11957 | } | ||
| 11958 | setTypeResetIterator(&si); | ||
| 11959 | cursor->cursor = 1; | ||
| 11960 | cursor->done = 1; | ||
| 11961 | ret = 0; | ||
| 11962 | } else if (kv->type == OBJ_ZSET || kv->type == OBJ_HASH) { | ||
| 11963 | unsigned char *lp, *p; | ||
| 11964 | /* is hash with expiry on fields, then lp tuples are [field][value][expire] */ | ||
| 11965 | int hfe = kv->type == OBJ_HASH && kv->encoding == OBJ_ENCODING_LISTPACK_EX; | ||
| 11966 | |||
| 11967 | if (kv->type == OBJ_HASH) | ||
| 11968 | lp = hashTypeListpackGetLp(kv); | ||
| 11969 | else | ||
| 11970 | lp = kv->ptr; | ||
| 11971 | |||
| 11972 | p = lpSeek(lp,0); | ||
| 11973 | while(p) { | ||
| 11974 | long long vllField, vllValue, vllExpire; | ||
| 11975 | unsigned int lenField, lenValue; | ||
| 11976 | unsigned char *pField, *pValue; | ||
| 11977 | |||
| 11978 | pField = lpGetValue(p,&lenField,&vllField); | ||
| 11979 | p = lpNext(lp,p); | ||
| 11980 | pValue = lpGetValue(p,&lenValue,&vllValue); | ||
| 11981 | p = lpNext(lp,p); | ||
| 11982 | |||
| 11983 | if (hfe) { | ||
| 11984 | serverAssert(lpGetIntegerValue(p, &vllExpire)); | ||
| 11985 | p = lpNext(lp, p); | ||
| 11986 | |||
| 11987 | /* Skip expired fields */ | ||
| 11988 | if ((!(key->mode & REDISMODULE_OPEN_KEY_ACCESS_EXPIRED)) && | ||
| 11989 | (hashTypeIsExpired(kv, vllExpire))) | ||
| 11990 | continue; | ||
| 11991 | } | ||
| 11992 | |||
| 11993 | robj *value = (pValue != NULL) ? | ||
| 11994 | createStringObject((char*)pValue,lenValue) : | ||
| 11995 | createStringObjectFromLongLongWithSds(vllValue); | ||
| 11996 | |||
| 11997 | robj *field = (pField != NULL) ? | ||
| 11998 | createStringObject((char*)pField,lenField) : | ||
| 11999 | createStringObjectFromLongLongWithSds(vllField); | ||
| 12000 | fn(key, field, value, privdata); | ||
| 12001 | |||
| 12002 | decrRefCount(field); | ||
| 12003 | decrRefCount(value); | ||
| 12004 | } | ||
| 12005 | cursor->cursor = 1; | ||
| 12006 | cursor->done = 1; | ||
| 12007 | ret = 0; | ||
| 12008 | } | ||
| 12009 | errno = 0; | ||
| 12010 | return ret; | ||
| 12011 | } | ||
| 12012 | |||
| 12013 | /* -------------------------------------------------------------------------- | ||
| 12014 | * ## Module fork API | ||
| 12015 | * -------------------------------------------------------------------------- */ | ||
| 12016 | |||
| 12017 | /* Create a background child process with the current frozen snapshot of the | ||
| 12018 | * main process where you can do some processing in the background without | ||
| 12019 | * affecting / freezing the traffic and no need for threads and GIL locking. | ||
| 12020 | * Note that Redis allows for only one concurrent fork. | ||
| 12021 | * When the child wants to exit, it should call RedisModule_ExitFromChild. | ||
| 12022 | * If the parent wants to kill the child it should call RedisModule_KillForkChild | ||
| 12023 | * The done handler callback will be executed on the parent process when the | ||
| 12024 | * child existed (but not when killed) | ||
| 12025 | * Return: -1 on failure, on success the parent process will get a positive PID | ||
| 12026 | * of the child, and the child process will get 0. | ||
| 12027 | */ | ||
| 12028 | int RM_Fork(RedisModuleForkDoneHandler cb, void *user_data) { | ||
| 12029 | pid_t childpid; | ||
| 12030 | |||
| 12031 | if ((childpid = redisFork(CHILD_TYPE_MODULE)) == 0) { | ||
| 12032 | /* Child */ | ||
| 12033 | redisSetProcTitle("redis-module-fork"); | ||
| 12034 | } else if (childpid == -1) { | ||
| 12035 | serverLog(LL_WARNING,"Can't fork for module: %s", strerror(errno)); | ||
| 12036 | } else { | ||
| 12037 | /* Parent */ | ||
| 12038 | moduleForkInfo.done_handler = cb; | ||
| 12039 | moduleForkInfo.done_handler_user_data = user_data; | ||
| 12040 | serverLog(LL_VERBOSE, "Module fork started pid: %ld ", (long) childpid); | ||
| 12041 | } | ||
| 12042 | return childpid; | ||
| 12043 | } | ||
| 12044 | |||
| 12045 | /* The module is advised to call this function from the fork child once in a while, | ||
| 12046 | * so that it can report progress and COW memory to the parent which will be | ||
| 12047 | * reported in INFO. | ||
| 12048 | * The `progress` argument should between 0 and 1, or -1 when not available. */ | ||
| 12049 | void RM_SendChildHeartbeat(double progress) { | ||
| 12050 | sendChildInfoGeneric(CHILD_INFO_TYPE_CURRENT_INFO, 0, progress, "Module fork"); | ||
| 12051 | } | ||
| 12052 | |||
| 12053 | /* Call from the child process when you want to terminate it. | ||
| 12054 | * retcode will be provided to the done handler executed on the parent process. | ||
| 12055 | */ | ||
| 12056 | int RM_ExitFromChild(int retcode) { | ||
| 12057 | sendChildCowInfo(CHILD_INFO_TYPE_MODULE_COW_SIZE, "Module fork"); | ||
| 12058 | exitFromChild(retcode, 0); | ||
| 12059 | return REDISMODULE_OK; | ||
| 12060 | } | ||
| 12061 | |||
| 12062 | /* Kill the active module forked child, if there is one active and the | ||
| 12063 | * pid matches, and returns C_OK. Otherwise if there is no active module | ||
| 12064 | * child or the pid does not match, return C_ERR without doing anything. */ | ||
| 12065 | int TerminateModuleForkChild(int child_pid, int wait) { | ||
| 12066 | /* Module child should be active and pid should match. */ | ||
| 12067 | if (server.child_type != CHILD_TYPE_MODULE || | ||
| 12068 | server.child_pid != child_pid) return C_ERR; | ||
| 12069 | |||
| 12070 | int statloc; | ||
| 12071 | serverLog(LL_VERBOSE,"Killing running module fork child: %ld", | ||
| 12072 | (long) server.child_pid); | ||
| 12073 | if (kill(server.child_pid,SIGUSR1) != -1 && wait) { | ||
| 12074 | while(waitpid(server.child_pid, &statloc, 0) != | ||
| 12075 | server.child_pid); | ||
| 12076 | } | ||
| 12077 | /* Reset the buffer accumulating changes while the child saves. */ | ||
| 12078 | resetChildState(); | ||
| 12079 | moduleForkInfo.done_handler = NULL; | ||
| 12080 | moduleForkInfo.done_handler_user_data = NULL; | ||
| 12081 | return C_OK; | ||
| 12082 | } | ||
| 12083 | |||
| 12084 | /* Can be used to kill the forked child process from the parent process. | ||
| 12085 | * child_pid would be the return value of RedisModule_Fork. */ | ||
| 12086 | int RM_KillForkChild(int child_pid) { | ||
| 12087 | /* Kill module child, wait for child exit. */ | ||
| 12088 | if (TerminateModuleForkChild(child_pid,1) == C_OK) | ||
| 12089 | return REDISMODULE_OK; | ||
| 12090 | else | ||
| 12091 | return REDISMODULE_ERR; | ||
| 12092 | } | ||
| 12093 | |||
| 12094 | void ModuleForkDoneHandler(int exitcode, int bysignal) { | ||
| 12095 | serverLog(LL_NOTICE, | ||
| 12096 | "Module fork exited pid: %ld, retcode: %d, bysignal: %d", | ||
| 12097 | (long) server.child_pid, exitcode, bysignal); | ||
| 12098 | if (moduleForkInfo.done_handler) { | ||
| 12099 | moduleForkInfo.done_handler(exitcode, bysignal, | ||
| 12100 | moduleForkInfo.done_handler_user_data); | ||
| 12101 | } | ||
| 12102 | |||
| 12103 | moduleForkInfo.done_handler = NULL; | ||
| 12104 | moduleForkInfo.done_handler_user_data = NULL; | ||
| 12105 | } | ||
| 12106 | |||
| 12107 | /* -------------------------------------------------------------------------- | ||
| 12108 | * ## Server hooks implementation | ||
| 12109 | * -------------------------------------------------------------------------- */ | ||
| 12110 | |||
| 12111 | /* This must be synced with REDISMODULE_EVENT_* | ||
| 12112 | * We use -1 (MAX_UINT64) to denote that this event doesn't have | ||
| 12113 | * a data structure associated with it. We use MAX_UINT64 on purpose, | ||
| 12114 | * in order to pass the check in RedisModule_SubscribeToServerEvent. */ | ||
| 12115 | static uint64_t moduleEventVersions[] = { | ||
| 12116 | REDISMODULE_REPLICATIONINFO_VERSION, /* REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED */ | ||
| 12117 | -1, /* REDISMODULE_EVENT_PERSISTENCE */ | ||
| 12118 | REDISMODULE_FLUSHINFO_VERSION, /* REDISMODULE_EVENT_FLUSHDB */ | ||
| 12119 | -1, /* REDISMODULE_EVENT_LOADING */ | ||
| 12120 | REDISMODULE_CLIENTINFO_VERSION, /* REDISMODULE_EVENT_CLIENT_CHANGE */ | ||
| 12121 | -1, /* REDISMODULE_EVENT_SHUTDOWN */ | ||
| 12122 | -1, /* REDISMODULE_EVENT_REPLICA_CHANGE */ | ||
| 12123 | -1, /* REDISMODULE_EVENT_MASTER_LINK_CHANGE */ | ||
| 12124 | REDISMODULE_CRON_LOOP_VERSION, /* REDISMODULE_EVENT_CRON_LOOP */ | ||
| 12125 | REDISMODULE_MODULE_CHANGE_VERSION, /* REDISMODULE_EVENT_MODULE_CHANGE */ | ||
| 12126 | REDISMODULE_LOADING_PROGRESS_VERSION, /* REDISMODULE_EVENT_LOADING_PROGRESS */ | ||
| 12127 | REDISMODULE_SWAPDBINFO_VERSION, /* REDISMODULE_EVENT_SWAPDB */ | ||
| 12128 | -1, /* REDISMODULE_EVENT_REPL_BACKUP */ | ||
| 12129 | -1, /* REDISMODULE_EVENT_FORK_CHILD */ | ||
| 12130 | -1, /* REDISMODULE_EVENT_REPL_ASYNC_LOAD */ | ||
| 12131 | -1, /* REDISMODULE_EVENT_EVENTLOOP */ | ||
| 12132 | -1, /* REDISMODULE_EVENT_CONFIG */ | ||
| 12133 | REDISMODULE_KEYINFO_VERSION, /* REDISMODULE_EVENT_KEY */ | ||
| 12134 | REDISMODULE_CLUSTER_SLOT_MIGRATION_INFO_VERSION, /* REDISMODULE_EVENT_CLUSTER_SLOT_MIGRATION */ | ||
| 12135 | REDISMODULE_CLUSTER_SLOT_MIGRATION_TRIMINFO_VERSION, /* REDISMODULE_EVENT_CLUSTER_SLOT_MIGRATION_TRIM */ | ||
| 12136 | }; | ||
| 12137 | |||
| 12138 | /* Register to be notified, via a callback, when the specified server event | ||
| 12139 | * happens. The callback is called with the event as argument, and an additional | ||
| 12140 | * argument which is a void pointer and should be cased to a specific type | ||
| 12141 | * that is event-specific (but many events will just use NULL since they do not | ||
| 12142 | * have additional information to pass to the callback). | ||
| 12143 | * | ||
| 12144 | * If the callback is NULL and there was a previous subscription, the module | ||
| 12145 | * will be unsubscribed. If there was a previous subscription and the callback | ||
| 12146 | * is not null, the old callback will be replaced with the new one. | ||
| 12147 | * | ||
| 12148 | * The callback must be of this type: | ||
| 12149 | * | ||
| 12150 | * int (*RedisModuleEventCallback)(RedisModuleCtx *ctx, | ||
| 12151 | * RedisModuleEvent eid, | ||
| 12152 | * uint64_t subevent, | ||
| 12153 | * void *data); | ||
| 12154 | * | ||
| 12155 | * The 'ctx' is a normal Redis module context that the callback can use in | ||
| 12156 | * order to call other modules APIs. The 'eid' is the event itself, this | ||
| 12157 | * is only useful in the case the module subscribed to multiple events: using | ||
| 12158 | * the 'id' field of this structure it is possible to check if the event | ||
| 12159 | * is one of the events we registered with this callback. The 'subevent' field | ||
| 12160 | * depends on the event that fired. | ||
| 12161 | * | ||
| 12162 | * Finally the 'data' pointer may be populated, only for certain events, with | ||
| 12163 | * more relevant data. | ||
| 12164 | * | ||
| 12165 | * Here is a list of events you can use as 'eid' and related sub events: | ||
| 12166 | * | ||
| 12167 | * * RedisModuleEvent_ReplicationRoleChanged: | ||
| 12168 | * | ||
| 12169 | * This event is called when the instance switches from master | ||
| 12170 | * to replica or the other way around, however the event is | ||
| 12171 | * also called when the replica remains a replica but starts to | ||
| 12172 | * replicate with a different master. | ||
| 12173 | * | ||
| 12174 | * The following sub events are available: | ||
| 12175 | * | ||
| 12176 | * * `REDISMODULE_SUBEVENT_REPLROLECHANGED_NOW_MASTER` | ||
| 12177 | * * `REDISMODULE_SUBEVENT_REPLROLECHANGED_NOW_REPLICA` | ||
| 12178 | * | ||
| 12179 | * The 'data' field can be casted by the callback to a | ||
| 12180 | * `RedisModuleReplicationInfo` structure with the following fields: | ||
| 12181 | * | ||
| 12182 | * int master; // true if master, false if replica | ||
| 12183 | * char *masterhost; // master instance hostname for NOW_REPLICA | ||
| 12184 | * int masterport; // master instance port for NOW_REPLICA | ||
| 12185 | * char *replid1; // Main replication ID | ||
| 12186 | * char *replid2; // Secondary replication ID | ||
| 12187 | * uint64_t repl1_offset; // Main replication offset | ||
| 12188 | * uint64_t repl2_offset; // Offset of replid2 validity | ||
| 12189 | * | ||
| 12190 | * * RedisModuleEvent_Persistence | ||
| 12191 | * | ||
| 12192 | * This event is called when RDB saving or AOF rewriting starts | ||
| 12193 | * and ends. The following sub events are available: | ||
| 12194 | * | ||
| 12195 | * * `REDISMODULE_SUBEVENT_PERSISTENCE_RDB_START` | ||
| 12196 | * * `REDISMODULE_SUBEVENT_PERSISTENCE_AOF_START` | ||
| 12197 | * * `REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_START` | ||
| 12198 | * * `REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_AOF_START` | ||
| 12199 | * * `REDISMODULE_SUBEVENT_PERSISTENCE_ENDED` | ||
| 12200 | * * `REDISMODULE_SUBEVENT_PERSISTENCE_FAILED` | ||
| 12201 | * | ||
| 12202 | * The above events are triggered not just when the user calls the | ||
| 12203 | * relevant commands like BGSAVE, but also when a saving operation | ||
| 12204 | * or AOF rewriting occurs because of internal server triggers. | ||
| 12205 | * The SYNC_RDB_START sub events are happening in the foreground due to | ||
| 12206 | * SAVE command, FLUSHALL, or server shutdown, and the other RDB and | ||
| 12207 | * AOF sub events are executed in a background fork child, so any | ||
| 12208 | * action the module takes can only affect the generated AOF or RDB, | ||
| 12209 | * but will not be reflected in the parent process and affect connected | ||
| 12210 | * clients and commands. Also note that the AOF_START sub event may end | ||
| 12211 | * up saving RDB content in case of an AOF with rdb-preamble. | ||
| 12212 | * | ||
| 12213 | * * RedisModuleEvent_FlushDB | ||
| 12214 | * | ||
| 12215 | * The FLUSHALL, FLUSHDB or an internal flush (for instance | ||
| 12216 | * because of replication, after the replica synchronization) | ||
| 12217 | * happened. The following sub events are available: | ||
| 12218 | * | ||
| 12219 | * * `REDISMODULE_SUBEVENT_FLUSHDB_START` | ||
| 12220 | * * `REDISMODULE_SUBEVENT_FLUSHDB_END` | ||
| 12221 | * | ||
| 12222 | * The data pointer can be casted to a RedisModuleFlushInfo | ||
| 12223 | * structure with the following fields: | ||
| 12224 | * | ||
| 12225 | * int32_t async; // True if the flush is done in a thread. | ||
| 12226 | * // See for instance FLUSHALL ASYNC. | ||
| 12227 | * // In this case the END callback is invoked | ||
| 12228 | * // immediately after the database is put | ||
| 12229 | * // in the free list of the thread. | ||
| 12230 | * int32_t dbnum; // Flushed database number, -1 for all the DBs | ||
| 12231 | * // in the case of the FLUSHALL operation. | ||
| 12232 | * | ||
| 12233 | * The start event is called *before* the operation is initiated, thus | ||
| 12234 | * allowing the callback to call DBSIZE or other operation on the | ||
| 12235 | * yet-to-free keyspace. | ||
| 12236 | * | ||
| 12237 | * * RedisModuleEvent_Loading | ||
| 12238 | * | ||
| 12239 | * Called on loading operations: at startup when the server is | ||
| 12240 | * started, but also after a first synchronization when the | ||
| 12241 | * replica is loading the RDB file from the master. | ||
| 12242 | * The following sub events are available: | ||
| 12243 | * | ||
| 12244 | * * `REDISMODULE_SUBEVENT_LOADING_RDB_START` | ||
| 12245 | * * `REDISMODULE_SUBEVENT_LOADING_AOF_START` | ||
| 12246 | * * `REDISMODULE_SUBEVENT_LOADING_REPL_START` | ||
| 12247 | * * `REDISMODULE_SUBEVENT_LOADING_ENDED` | ||
| 12248 | * * `REDISMODULE_SUBEVENT_LOADING_FAILED` | ||
| 12249 | * | ||
| 12250 | * Note that AOF loading may start with an RDB data in case of | ||
| 12251 | * rdb-preamble, in which case you'll only receive an AOF_START event. | ||
| 12252 | * | ||
| 12253 | * * RedisModuleEvent_ClientChange | ||
| 12254 | * | ||
| 12255 | * Called when a client connects or disconnects. | ||
| 12256 | * The data pointer can be casted to a RedisModuleClientInfo | ||
| 12257 | * structure, documented in RedisModule_GetClientInfoById(). | ||
| 12258 | * The following sub events are available: | ||
| 12259 | * | ||
| 12260 | * * `REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED` | ||
| 12261 | * * `REDISMODULE_SUBEVENT_CLIENT_CHANGE_DISCONNECTED` | ||
| 12262 | * | ||
| 12263 | * * RedisModuleEvent_Shutdown | ||
| 12264 | * | ||
| 12265 | * The server is shutting down. No subevents are available. | ||
| 12266 | * | ||
| 12267 | * * RedisModuleEvent_ReplicaChange | ||
| 12268 | * | ||
| 12269 | * This event is called when the instance (that can be both a | ||
| 12270 | * master or a replica) get a new online replica, or lose a | ||
| 12271 | * replica since it gets disconnected. | ||
| 12272 | * The following sub events are available: | ||
| 12273 | * | ||
| 12274 | * * `REDISMODULE_SUBEVENT_REPLICA_CHANGE_ONLINE` | ||
| 12275 | * * `REDISMODULE_SUBEVENT_REPLICA_CHANGE_OFFLINE` | ||
| 12276 | * | ||
| 12277 | * No additional information is available so far: future versions | ||
| 12278 | * of Redis will have an API in order to enumerate the replicas | ||
| 12279 | * connected and their state. | ||
| 12280 | * | ||
| 12281 | * * RedisModuleEvent_CronLoop | ||
| 12282 | * | ||
| 12283 | * This event is called every time Redis calls the serverCron() | ||
| 12284 | * function in order to do certain bookkeeping. Modules that are | ||
| 12285 | * required to do operations from time to time may use this callback. | ||
| 12286 | * Normally Redis calls this function 10 times per second, but | ||
| 12287 | * this changes depending on the "hz" configuration. | ||
| 12288 | * No sub events are available. | ||
| 12289 | * | ||
| 12290 | * The data pointer can be casted to a RedisModuleCronLoop | ||
| 12291 | * structure with the following fields: | ||
| 12292 | * | ||
| 12293 | * int32_t hz; // Approximate number of events per second. | ||
| 12294 | * | ||
| 12295 | * * RedisModuleEvent_MasterLinkChange | ||
| 12296 | * | ||
| 12297 | * This is called for replicas in order to notify when the | ||
| 12298 | * replication link becomes functional (up) with our master, | ||
| 12299 | * or when it goes down. Note that the link is not considered | ||
| 12300 | * up when we just connected to the master, but only if the | ||
| 12301 | * replication is happening correctly. | ||
| 12302 | * The following sub events are available: | ||
| 12303 | * | ||
| 12304 | * * `REDISMODULE_SUBEVENT_MASTER_LINK_UP` | ||
| 12305 | * * `REDISMODULE_SUBEVENT_MASTER_LINK_DOWN` | ||
| 12306 | * | ||
| 12307 | * * RedisModuleEvent_ModuleChange | ||
| 12308 | * | ||
| 12309 | * This event is called when a new module is loaded or one is unloaded. | ||
| 12310 | * The following sub events are available: | ||
| 12311 | * | ||
| 12312 | * * `REDISMODULE_SUBEVENT_MODULE_LOADED` | ||
| 12313 | * * `REDISMODULE_SUBEVENT_MODULE_UNLOADED` | ||
| 12314 | * | ||
| 12315 | * The data pointer can be casted to a RedisModuleModuleChange | ||
| 12316 | * structure with the following fields: | ||
| 12317 | * | ||
| 12318 | * const char* module_name; // Name of module loaded or unloaded. | ||
| 12319 | * int32_t module_version; // Module version. | ||
| 12320 | * | ||
| 12321 | * * RedisModuleEvent_LoadingProgress | ||
| 12322 | * | ||
| 12323 | * This event is called repeatedly called while an RDB or AOF file | ||
| 12324 | * is being loaded. | ||
| 12325 | * The following sub events are available: | ||
| 12326 | * | ||
| 12327 | * * `REDISMODULE_SUBEVENT_LOADING_PROGRESS_RDB` | ||
| 12328 | * * `REDISMODULE_SUBEVENT_LOADING_PROGRESS_AOF` | ||
| 12329 | * | ||
| 12330 | * The data pointer can be casted to a RedisModuleLoadingProgress | ||
| 12331 | * structure with the following fields: | ||
| 12332 | * | ||
| 12333 | * int32_t hz; // Approximate number of events per second. | ||
| 12334 | * int32_t progress; // Approximate progress between 0 and 1024, | ||
| 12335 | * // or -1 if unknown. | ||
| 12336 | * | ||
| 12337 | * * RedisModuleEvent_SwapDB | ||
| 12338 | * | ||
| 12339 | * This event is called when a SWAPDB command has been successfully | ||
| 12340 | * Executed. | ||
| 12341 | * For this event call currently there is no subevents available. | ||
| 12342 | * | ||
| 12343 | * The data pointer can be casted to a RedisModuleSwapDbInfo | ||
| 12344 | * structure with the following fields: | ||
| 12345 | * | ||
| 12346 | * int32_t dbnum_first; // Swap Db first dbnum | ||
| 12347 | * int32_t dbnum_second; // Swap Db second dbnum | ||
| 12348 | * | ||
| 12349 | * * RedisModuleEvent_ReplBackup | ||
| 12350 | * | ||
| 12351 | * WARNING: Replication Backup events are deprecated since Redis 7.0 and are never fired. | ||
| 12352 | * See RedisModuleEvent_ReplAsyncLoad for understanding how Async Replication Loading events | ||
| 12353 | * are now triggered when repl-diskless-load is set to swapdb. | ||
| 12354 | * | ||
| 12355 | * Called when repl-diskless-load config is set to swapdb, | ||
| 12356 | * And redis needs to backup the current database for the | ||
| 12357 | * possibility to be restored later. A module with global data and | ||
| 12358 | * maybe with aux_load and aux_save callbacks may need to use this | ||
| 12359 | * notification to backup / restore / discard its globals. | ||
| 12360 | * The following sub events are available: | ||
| 12361 | * | ||
| 12362 | * * `REDISMODULE_SUBEVENT_REPL_BACKUP_CREATE` | ||
| 12363 | * * `REDISMODULE_SUBEVENT_REPL_BACKUP_RESTORE` | ||
| 12364 | * * `REDISMODULE_SUBEVENT_REPL_BACKUP_DISCARD` | ||
| 12365 | * | ||
| 12366 | * * RedisModuleEvent_ReplAsyncLoad | ||
| 12367 | * | ||
| 12368 | * Called when repl-diskless-load config is set to swapdb and a replication with a master of same | ||
| 12369 | * data set history (matching replication ID) occurs. | ||
| 12370 | * In which case redis serves current data set while loading new database in memory from socket. | ||
| 12371 | * Modules must have declared they support this mechanism in order to activate it, through | ||
| 12372 | * REDISMODULE_OPTIONS_HANDLE_REPL_ASYNC_LOAD flag. | ||
| 12373 | * The following sub events are available: | ||
| 12374 | * | ||
| 12375 | * * `REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_STARTED` | ||
| 12376 | * * `REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_ABORTED` | ||
| 12377 | * * `REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_COMPLETED` | ||
| 12378 | * | ||
| 12379 | * * RedisModuleEvent_ForkChild | ||
| 12380 | * | ||
| 12381 | * Called when a fork child (AOFRW, RDBSAVE, module fork...) is born/dies | ||
| 12382 | * The following sub events are available: | ||
| 12383 | * | ||
| 12384 | * * `REDISMODULE_SUBEVENT_FORK_CHILD_BORN` | ||
| 12385 | * * `REDISMODULE_SUBEVENT_FORK_CHILD_DIED` | ||
| 12386 | * | ||
| 12387 | * * RedisModuleEvent_EventLoop | ||
| 12388 | * | ||
| 12389 | * Called on each event loop iteration, once just before the event loop goes | ||
| 12390 | * to sleep or just after it wakes up. | ||
| 12391 | * The following sub events are available: | ||
| 12392 | * | ||
| 12393 | * * `REDISMODULE_SUBEVENT_EVENTLOOP_BEFORE_SLEEP` | ||
| 12394 | * * `REDISMODULE_SUBEVENT_EVENTLOOP_AFTER_SLEEP` | ||
| 12395 | * | ||
| 12396 | * * RedisModule_Event_Config | ||
| 12397 | * | ||
| 12398 | * Called when a configuration event happens | ||
| 12399 | * The following sub events are available: | ||
| 12400 | * | ||
| 12401 | * * `REDISMODULE_SUBEVENT_CONFIG_CHANGE` | ||
| 12402 | * | ||
| 12403 | * The data pointer can be casted to a RedisModuleConfigChange | ||
| 12404 | * structure with the following fields: | ||
| 12405 | * | ||
| 12406 | * const char **config_names; // An array of C string pointers containing the | ||
| 12407 | * // name of each modified configuration item | ||
| 12408 | * uint32_t num_changes; // The number of elements in the config_names array | ||
| 12409 | * | ||
| 12410 | * * RedisModule_Event_Key | ||
| 12411 | * | ||
| 12412 | * Called when a key is removed from the keyspace. We can't modify any key in | ||
| 12413 | * the event. | ||
| 12414 | * The following sub events are available: | ||
| 12415 | * | ||
| 12416 | * * `REDISMODULE_SUBEVENT_KEY_DELETED` | ||
| 12417 | * * `REDISMODULE_SUBEVENT_KEY_EXPIRED` | ||
| 12418 | * * `REDISMODULE_SUBEVENT_KEY_EVICTED` | ||
| 12419 | * * `REDISMODULE_SUBEVENT_KEY_OVERWRITTEN` | ||
| 12420 | * | ||
| 12421 | * The data pointer can be casted to a RedisModuleKeyInfo | ||
| 12422 | * structure with the following fields: | ||
| 12423 | * | ||
| 12424 | * RedisModuleKey *key; // Key name | ||
| 12425 | * | ||
| 12426 | * * RedisModuleEvent_ClusterSlotMigration | ||
| 12427 | * | ||
| 12428 | * Called when an atomic slot migration (ASM) event happens. | ||
| 12429 | * IMPORT events are triggered on the destination side of a slot migration | ||
| 12430 | * operation. These notifications let modules prepare for the upcoming | ||
| 12431 | * ownership change, observe successful completion once the cluster config | ||
| 12432 | * reflects the new owner, or detect a failure in which case slot ownership | ||
| 12433 | * remains with the source. | ||
| 12434 | * | ||
| 12435 | * Similarly, MIGRATE events triggered on the source side of a slot | ||
| 12436 | * migration operation to let modules prepare for the ownership change and | ||
| 12437 | * observe the completion of the slot migration. MIGRATE_MODULE_PROPAGATE | ||
| 12438 | * event is triggered in the fork just before snapshot delivery; modules may | ||
| 12439 | * use it to enqueue commands that will be delivered first. See | ||
| 12440 | * RedisModule_ClusterPropagateForSlotMigration() for details. | ||
| 12441 | * | ||
| 12442 | * * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_IMPORT_STARTED` | ||
| 12443 | * * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_IMPORT_FAILED` | ||
| 12444 | * * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_IMPORT_COMPLETED` | ||
| 12445 | * * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_STARTED` | ||
| 12446 | * * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_FAILED` | ||
| 12447 | * * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_COMPLETED` | ||
| 12448 | * * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_MODULE_PROPAGATE` | ||
| 12449 | * | ||
| 12450 | * The data pointer can be casted to a RedisModuleClusterSlotMigrationInfo | ||
| 12451 | * structure with the following fields: | ||
| 12452 | * | ||
| 12453 | * char source_node_id[REDISMODULE_NODE_ID_LEN + 1]; | ||
| 12454 | * char destination_node_id[REDISMODULE_NODE_ID_LEN + 1]; | ||
| 12455 | * const char *task_id; // Task ID | ||
| 12456 | * RedisModuleSlotRangeArray *slots; // Slot ranges | ||
| 12457 | * | ||
| 12458 | * * RedisModuleEvent_ClusterSlotMigrationTrim | ||
| 12459 | * | ||
| 12460 | * Called when trimming keys after a slot migration. Fires on the source | ||
| 12461 | * after a successful migration to clean up migrated keys, or on the | ||
| 12462 | * destination after a failed import to discard partial imports. Two methods | ||
| 12463 | * are supported. In the first method, keys are deleted in a background | ||
| 12464 | * thread; this is reported via the TRIM_BACKGROUND event. In the second | ||
| 12465 | * method, Redis performs incremental deletions on the main thread via the | ||
| 12466 | * cron loop to avoid stalls; this is reported via the TRIM_STARTED and | ||
| 12467 | * TRIM_COMPLETED events. Each deletion emits REDISMODULE_NOTIFY_KEY_TRIMMED | ||
| 12468 | * so modules can react to individual key deletions. Redis selects the | ||
| 12469 | * method automatically: background by default; switches to main thread | ||
| 12470 | * trimming when a module subscribes to REDISMODULE_NOTIFY_KEY_TRIMMED. | ||
| 12471 | * | ||
| 12472 | * The following sub events are available: | ||
| 12473 | * | ||
| 12474 | * * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_TRIM_STARTED` | ||
| 12475 | * * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_TRIM_COMPLETED` | ||
| 12476 | * * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_TRIM_BACKGROUND` | ||
| 12477 | * | ||
| 12478 | * The data pointer can be casted to a RedisModuleClusterSlotMigrationTrimInfo | ||
| 12479 | * structure with the following fields: | ||
| 12480 | * | ||
| 12481 | * RedisModuleSlotRangeArray *slots; // Slot ranges | ||
| 12482 | * | ||
| 12483 | * The function returns REDISMODULE_OK if the module was successfully subscribed | ||
| 12484 | * for the specified event. If the API is called from a wrong context or unsupported event | ||
| 12485 | * is given then REDISMODULE_ERR is returned. */ | ||
| 12486 | int RM_SubscribeToServerEvent(RedisModuleCtx *ctx, RedisModuleEvent event, RedisModuleEventCallback callback) { | ||
| 12487 | RedisModuleEventListener *el; | ||
| 12488 | |||
| 12489 | /* Protect in case of calls from contexts without a module reference. */ | ||
| 12490 | if (ctx->module == NULL) return REDISMODULE_ERR; | ||
| 12491 | if (event.id >= _REDISMODULE_EVENT_NEXT) return REDISMODULE_ERR; | ||
| 12492 | if (event.dataver > moduleEventVersions[event.id]) return REDISMODULE_ERR; /* Module compiled with a newer redismodule.h than we support */ | ||
| 12493 | |||
| 12494 | /* Search an event matching this module and event ID. */ | ||
| 12495 | listIter li; | ||
| 12496 | listNode *ln; | ||
| 12497 | listRewind(RedisModule_EventListeners,&li); | ||
| 12498 | while((ln = listNext(&li))) { | ||
| 12499 | el = ln->value; | ||
| 12500 | if (el->module == ctx->module && el->event.id == event.id) | ||
| 12501 | break; /* Matching event found. */ | ||
| 12502 | } | ||
| 12503 | |||
| 12504 | /* Modify or remove the event listener if we already had one. */ | ||
| 12505 | if (ln) { | ||
| 12506 | if (callback == NULL) { | ||
| 12507 | listDelNode(RedisModule_EventListeners,ln); | ||
| 12508 | zfree(el); | ||
| 12509 | } else { | ||
| 12510 | el->callback = callback; /* Update the callback with the new one. */ | ||
| 12511 | } | ||
| 12512 | return REDISMODULE_OK; | ||
| 12513 | } | ||
| 12514 | |||
| 12515 | /* No event found, we need to add a new one. */ | ||
| 12516 | el = zmalloc(sizeof(*el)); | ||
| 12517 | el->module = ctx->module; | ||
| 12518 | el->event = event; | ||
| 12519 | el->callback = callback; | ||
| 12520 | listAddNodeTail(RedisModule_EventListeners,el); | ||
| 12521 | return REDISMODULE_OK; | ||
| 12522 | } | ||
| 12523 | |||
| 12524 | /** | ||
| 12525 | * For a given server event and subevent, return zero if the | ||
| 12526 | * subevent is not supported and non-zero otherwise. | ||
| 12527 | */ | ||
| 12528 | int RM_IsSubEventSupported(RedisModuleEvent event, int64_t subevent) { | ||
| 12529 | switch (event.id) { | ||
| 12530 | case REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED: | ||
| 12531 | return subevent < _REDISMODULE_EVENT_REPLROLECHANGED_NEXT; | ||
| 12532 | case REDISMODULE_EVENT_PERSISTENCE: | ||
| 12533 | return subevent < _REDISMODULE_SUBEVENT_PERSISTENCE_NEXT; | ||
| 12534 | case REDISMODULE_EVENT_FLUSHDB: | ||
| 12535 | return subevent < _REDISMODULE_SUBEVENT_FLUSHDB_NEXT; | ||
| 12536 | case REDISMODULE_EVENT_LOADING: | ||
| 12537 | return subevent < _REDISMODULE_SUBEVENT_LOADING_NEXT; | ||
| 12538 | case REDISMODULE_EVENT_CLIENT_CHANGE: | ||
| 12539 | return subevent < _REDISMODULE_SUBEVENT_CLIENT_CHANGE_NEXT; | ||
| 12540 | case REDISMODULE_EVENT_SHUTDOWN: | ||
| 12541 | return subevent < _REDISMODULE_SUBEVENT_SHUTDOWN_NEXT; | ||
| 12542 | case REDISMODULE_EVENT_REPLICA_CHANGE: | ||
| 12543 | return subevent < _REDISMODULE_EVENT_REPLROLECHANGED_NEXT; | ||
| 12544 | case REDISMODULE_EVENT_MASTER_LINK_CHANGE: | ||
| 12545 | return subevent < _REDISMODULE_SUBEVENT_MASTER_NEXT; | ||
| 12546 | case REDISMODULE_EVENT_CRON_LOOP: | ||
| 12547 | return subevent < _REDISMODULE_SUBEVENT_CRON_LOOP_NEXT; | ||
| 12548 | case REDISMODULE_EVENT_MODULE_CHANGE: | ||
| 12549 | return subevent < _REDISMODULE_SUBEVENT_MODULE_NEXT; | ||
| 12550 | case REDISMODULE_EVENT_LOADING_PROGRESS: | ||
| 12551 | return subevent < _REDISMODULE_SUBEVENT_LOADING_PROGRESS_NEXT; | ||
| 12552 | case REDISMODULE_EVENT_SWAPDB: | ||
| 12553 | return subevent < _REDISMODULE_SUBEVENT_SWAPDB_NEXT; | ||
| 12554 | case REDISMODULE_EVENT_REPL_ASYNC_LOAD: | ||
| 12555 | return subevent < _REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_NEXT; | ||
| 12556 | case REDISMODULE_EVENT_FORK_CHILD: | ||
| 12557 | return subevent < _REDISMODULE_SUBEVENT_FORK_CHILD_NEXT; | ||
| 12558 | case REDISMODULE_EVENT_EVENTLOOP: | ||
| 12559 | return subevent < _REDISMODULE_SUBEVENT_EVENTLOOP_NEXT; | ||
| 12560 | case REDISMODULE_EVENT_CONFIG: | ||
| 12561 | return subevent < _REDISMODULE_SUBEVENT_CONFIG_NEXT; | ||
| 12562 | case REDISMODULE_EVENT_KEY: | ||
| 12563 | return subevent < _REDISMODULE_SUBEVENT_KEY_NEXT; | ||
| 12564 | case REDISMODULE_EVENT_CLUSTER_SLOT_MIGRATION: | ||
| 12565 | return subevent < _REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_NEXT; | ||
| 12566 | case REDISMODULE_EVENT_CLUSTER_SLOT_MIGRATION_TRIM: | ||
| 12567 | return subevent < _REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_TRIM_NEXT; | ||
| 12568 | default: | ||
| 12569 | break; | ||
| 12570 | } | ||
| 12571 | return 0; | ||
| 12572 | } | ||
| 12573 | |||
| 12574 | typedef struct KeyInfo { | ||
| 12575 | int32_t dbnum; | ||
| 12576 | RedisModuleString *key; | ||
| 12577 | kvobj *kv; /* key-value object */ | ||
| 12578 | int mode; | ||
| 12579 | } KeyInfo; | ||
| 12580 | |||
| 12581 | /* This is called by the Redis internals every time we want to fire an | ||
| 12582 | * event that can be intercepted by some module. The pointer 'data' is useful | ||
| 12583 | * in order to populate the event-specific structure when needed, in order | ||
| 12584 | * to return the structure with more information to the callback. | ||
| 12585 | * | ||
| 12586 | * 'eid' and 'subid' are just the main event ID and the sub event associated | ||
| 12587 | * with the event, depending on what exactly happened. */ | ||
| 12588 | void moduleFireServerEvent(uint64_t eid, int subid, void *data) { | ||
| 12589 | /* Fast path to return ASAP if there is nothing to do, avoiding to | ||
| 12590 | * setup the iterator and so forth: we want this call to be extremely | ||
| 12591 | * cheap if there are no registered modules. */ | ||
| 12592 | if (listLength(RedisModule_EventListeners) == 0) return; | ||
| 12593 | |||
| 12594 | listIter li; | ||
| 12595 | listNode *ln; | ||
| 12596 | listRewind(RedisModule_EventListeners,&li); | ||
| 12597 | while((ln = listNext(&li))) { | ||
| 12598 | RedisModuleEventListener *el = ln->value; | ||
| 12599 | if (el->event.id == eid) { | ||
| 12600 | RedisModuleCtx ctx; | ||
| 12601 | if (eid == REDISMODULE_EVENT_CLIENT_CHANGE) { | ||
| 12602 | /* In the case of client changes, we're pushing the real client | ||
| 12603 | * so the event handler can mutate it if needed. For example, | ||
| 12604 | * to change its authentication state in a way that does not | ||
| 12605 | * depend on specific commands executed later. | ||
| 12606 | */ | ||
| 12607 | moduleCreateContext(&ctx,el->module,REDISMODULE_CTX_NONE); | ||
| 12608 | ctx.client = (client *) data; | ||
| 12609 | } else { | ||
| 12610 | moduleCreateContext(&ctx,el->module,REDISMODULE_CTX_TEMP_CLIENT); | ||
| 12611 | } | ||
| 12612 | |||
| 12613 | void *moduledata = NULL; | ||
| 12614 | RedisModuleClientInfoV1 civ1; | ||
| 12615 | RedisModuleReplicationInfoV1 riv1; | ||
| 12616 | RedisModuleModuleChangeV1 mcv1; | ||
| 12617 | RedisModuleKey key; | ||
| 12618 | RedisModuleKeyInfoV1 ki = {REDISMODULE_KEYINFO_VERSION, &key}; | ||
| 12619 | |||
| 12620 | /* Event specific context and data pointer setup. */ | ||
| 12621 | if (eid == REDISMODULE_EVENT_CLIENT_CHANGE) { | ||
| 12622 | serverAssert(modulePopulateClientInfoStructure(&civ1,data, el->event.dataver) == REDISMODULE_OK); | ||
| 12623 | moduledata = &civ1; | ||
| 12624 | } else if (eid == REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED) { | ||
| 12625 | serverAssert(modulePopulateReplicationInfoStructure(&riv1,el->event.dataver) == REDISMODULE_OK); | ||
| 12626 | moduledata = &riv1; | ||
| 12627 | } else if (eid == REDISMODULE_EVENT_FLUSHDB) { | ||
| 12628 | moduledata = data; | ||
| 12629 | RedisModuleFlushInfoV1 *fi = data; | ||
| 12630 | if (fi->dbnum != -1) | ||
| 12631 | selectDb(ctx.client, fi->dbnum); | ||
| 12632 | } else if (eid == REDISMODULE_EVENT_MODULE_CHANGE) { | ||
| 12633 | RedisModule *m = data; | ||
| 12634 | if (m == el->module) { | ||
| 12635 | moduleFreeContext(&ctx); | ||
| 12636 | continue; | ||
| 12637 | } | ||
| 12638 | mcv1.version = REDISMODULE_MODULE_CHANGE_VERSION; | ||
| 12639 | mcv1.module_name = m->name; | ||
| 12640 | mcv1.module_version = m->ver; | ||
| 12641 | moduledata = &mcv1; | ||
| 12642 | } else if (eid == REDISMODULE_EVENT_LOADING_PROGRESS) { | ||
| 12643 | moduledata = data; | ||
| 12644 | } else if (eid == REDISMODULE_EVENT_CRON_LOOP) { | ||
| 12645 | moduledata = data; | ||
| 12646 | } else if (eid == REDISMODULE_EVENT_SWAPDB) { | ||
| 12647 | moduledata = data; | ||
| 12648 | } else if (eid == REDISMODULE_EVENT_CONFIG) { | ||
| 12649 | moduledata = data; | ||
| 12650 | } else if (eid == REDISMODULE_EVENT_KEY) { | ||
| 12651 | KeyInfo *info = data; | ||
| 12652 | selectDb(ctx.client, info->dbnum); | ||
| 12653 | moduleInitKey(&key, &ctx, info->key, info->kv, info->mode); | ||
| 12654 | moduledata = &ki; | ||
| 12655 | } else if (eid == REDISMODULE_EVENT_CLUSTER_SLOT_MIGRATION) { | ||
| 12656 | moduledata = data; | ||
| 12657 | } else if (eid == REDISMODULE_EVENT_CLUSTER_SLOT_MIGRATION_TRIM) { | ||
| 12658 | moduledata = data; | ||
| 12659 | } | ||
| 12660 | |||
| 12661 | el->module->in_hook++; | ||
| 12662 | el->callback(&ctx,el->event,subid,moduledata); | ||
| 12663 | el->module->in_hook--; | ||
| 12664 | |||
| 12665 | if (eid == REDISMODULE_EVENT_KEY) { | ||
| 12666 | moduleCloseKey(&key); | ||
| 12667 | } | ||
| 12668 | |||
| 12669 | moduleFreeContext(&ctx); | ||
| 12670 | } | ||
| 12671 | } | ||
| 12672 | } | ||
| 12673 | |||
| 12674 | /* Remove all the listeners for this module: this is used before unloading | ||
| 12675 | * a module. */ | ||
| 12676 | void moduleUnsubscribeAllServerEvents(RedisModule *module) { | ||
| 12677 | RedisModuleEventListener *el; | ||
| 12678 | listIter li; | ||
| 12679 | listNode *ln; | ||
| 12680 | listRewind(RedisModule_EventListeners,&li); | ||
| 12681 | |||
| 12682 | while((ln = listNext(&li))) { | ||
| 12683 | el = ln->value; | ||
| 12684 | if (el->module == module) { | ||
| 12685 | listDelNode(RedisModule_EventListeners,ln); | ||
| 12686 | zfree(el); | ||
| 12687 | } | ||
| 12688 | } | ||
| 12689 | } | ||
| 12690 | |||
| 12691 | void processModuleLoadingProgressEvent(int is_aof) { | ||
| 12692 | long long now = server.ustime; | ||
| 12693 | static long long next_event = 0; | ||
| 12694 | if (now >= next_event) { | ||
| 12695 | /* Fire the loading progress modules end event. */ | ||
| 12696 | int progress = -1; | ||
| 12697 | if (server.loading_total_bytes) | ||
| 12698 | progress = (server.loading_loaded_bytes<<10) / server.loading_total_bytes; | ||
| 12699 | RedisModuleLoadingProgressV1 fi = {REDISMODULE_LOADING_PROGRESS_VERSION, | ||
| 12700 | server.hz, | ||
| 12701 | progress}; | ||
| 12702 | moduleFireServerEvent(REDISMODULE_EVENT_LOADING_PROGRESS, | ||
| 12703 | is_aof? | ||
| 12704 | REDISMODULE_SUBEVENT_LOADING_PROGRESS_AOF: | ||
| 12705 | REDISMODULE_SUBEVENT_LOADING_PROGRESS_RDB, | ||
| 12706 | &fi); | ||
| 12707 | /* decide when the next event should fire. */ | ||
| 12708 | next_event = now + 1000000 / server.hz; | ||
| 12709 | } | ||
| 12710 | } | ||
| 12711 | |||
| 12712 | /* When a key is deleted (in dbAsyncDelete/dbSyncDelete/setKey), it | ||
| 12713 | * will be called to tell the module which key is about to be released. */ | ||
| 12714 | void moduleNotifyKeyUnlink(robj *key, kvobj *kv, int dbid, int flags) { | ||
| 12715 | server.allow_access_expired++; | ||
| 12716 | server.allow_access_trimmed++; | ||
| 12717 | int subevent = REDISMODULE_SUBEVENT_KEY_DELETED; | ||
| 12718 | if (flags & DB_FLAG_KEY_EXPIRED) { | ||
| 12719 | subevent = REDISMODULE_SUBEVENT_KEY_EXPIRED; | ||
| 12720 | } else if (flags & DB_FLAG_KEY_EVICTED) { | ||
| 12721 | subevent = REDISMODULE_SUBEVENT_KEY_EVICTED; | ||
| 12722 | } else if (flags & DB_FLAG_KEY_OVERWRITE) { | ||
| 12723 | subevent = REDISMODULE_SUBEVENT_KEY_OVERWRITTEN; | ||
| 12724 | } | ||
| 12725 | KeyInfo info = {dbid, key, kv, REDISMODULE_READ}; | ||
| 12726 | moduleFireServerEvent(REDISMODULE_EVENT_KEY, subevent, &info); | ||
| 12727 | |||
| 12728 | if (kv->type == OBJ_MODULE) { | ||
| 12729 | moduleValue *mv = kv->ptr; | ||
| 12730 | moduleType *mt = mv->type; | ||
| 12731 | /* We prefer to use the enhanced version. */ | ||
| 12732 | if (mt->unlink2 != NULL) { | ||
| 12733 | RedisModuleKeyOptCtx ctx = {key, NULL, dbid, -1}; | ||
| 12734 | mt->unlink2(&ctx,mv->value); | ||
| 12735 | } else if (mt->unlink != NULL) { | ||
| 12736 | mt->unlink(key,mv->value); | ||
| 12737 | } | ||
| 12738 | } | ||
| 12739 | server.allow_access_expired--; | ||
| 12740 | server.allow_access_trimmed--; | ||
| 12741 | } | ||
| 12742 | |||
| 12743 | /* Return the free_effort of the module, it will automatically choose to call | ||
| 12744 | * `free_effort` or `free_effort2`, and the default return value is 1. | ||
| 12745 | * value of 0 means very high effort (always asynchronous freeing). */ | ||
| 12746 | size_t moduleGetFreeEffort(robj *key, robj *val, int dbid) { | ||
| 12747 | moduleValue *mv = val->ptr; | ||
| 12748 | moduleType *mt = mv->type; | ||
| 12749 | size_t effort = 1; | ||
| 12750 | /* We prefer to use the enhanced version. */ | ||
| 12751 | if (mt->free_effort2 != NULL) { | ||
| 12752 | RedisModuleKeyOptCtx ctx = {key, NULL, dbid, -1}; | ||
| 12753 | effort = mt->free_effort2(&ctx,mv->value); | ||
| 12754 | } else if (mt->free_effort != NULL) { | ||
| 12755 | effort = mt->free_effort(key,mv->value); | ||
| 12756 | } | ||
| 12757 | |||
| 12758 | return effort; | ||
| 12759 | } | ||
| 12760 | |||
| 12761 | /* Return the memory usage of the module, it will automatically choose to call | ||
| 12762 | * `mem_usage` or `mem_usage2`, and the default return value is 0. */ | ||
| 12763 | size_t moduleGetMemUsage(robj *key, robj *val, size_t sample_size, int dbid) { | ||
| 12764 | moduleValue *mv = val->ptr; | ||
| 12765 | moduleType *mt = mv->type; | ||
| 12766 | size_t size = 0; | ||
| 12767 | /* We prefer to use the enhanced version. */ | ||
| 12768 | if (mt->mem_usage2 != NULL) { | ||
| 12769 | RedisModuleKeyOptCtx ctx = {key, NULL, dbid, -1}; | ||
| 12770 | size = mt->mem_usage2(&ctx, mv->value, sample_size); | ||
| 12771 | } else if (mt->mem_usage != NULL) { | ||
| 12772 | size = mt->mem_usage(mv->value); | ||
| 12773 | } | ||
| 12774 | |||
| 12775 | return size; | ||
| 12776 | } | ||
| 12777 | |||
| 12778 | /* -------------------------------------------------------------------------- | ||
| 12779 | * Modules API internals | ||
| 12780 | * -------------------------------------------------------------------------- */ | ||
| 12781 | |||
| 12782 | /* server.moduleapi dictionary type. Only uses plain C strings since | ||
| 12783 | * this gets queries from modules. */ | ||
| 12784 | |||
| 12785 | uint64_t dictCStringKeyHash(const void *key) { | ||
| 12786 | return dictGenHashFunction((unsigned char*)key, strlen((char*)key)); | ||
| 12787 | } | ||
| 12788 | |||
| 12789 | int dictCStringKeyCompare(dictCmpCache *cache, const void *key1, const void *key2) { | ||
| 12790 | UNUSED(cache); | ||
| 12791 | return strcmp(key1,key2) == 0; | ||
| 12792 | } | ||
| 12793 | |||
| 12794 | dictType moduleAPIDictType = { | ||
| 12795 | dictCStringKeyHash, /* hash function */ | ||
| 12796 | NULL, /* key dup */ | ||
| 12797 | NULL, /* val dup */ | ||
| 12798 | dictCStringKeyCompare, /* key compare */ | ||
| 12799 | NULL, /* key destructor */ | ||
| 12800 | NULL, /* val destructor */ | ||
| 12801 | NULL /* allow to expand */ | ||
| 12802 | }; | ||
| 12803 | |||
| 12804 | int moduleRegisterApi(const char *funcname, void *funcptr) { | ||
| 12805 | return dictAdd(server.moduleapi, (char*)funcname, funcptr); | ||
| 12806 | } | ||
| 12807 | |||
| 12808 | #define REGISTER_API(name) \ | ||
| 12809 | moduleRegisterApi("RedisModule_" #name, (void *)(unsigned long)RM_ ## name) | ||
| 12810 | |||
| 12811 | /* Global initialization at Redis startup. */ | ||
| 12812 | void moduleRegisterCoreAPI(void); | ||
| 12813 | |||
| 12814 | /* Currently, this function is just a placeholder for the module system | ||
| 12815 | * initialization steps that need to be run after server initialization. | ||
| 12816 | * A previous issue, selectDb() in createClient() requires that server.db has | ||
| 12817 | * been initialized, see #7323. */ | ||
| 12818 | void moduleInitModulesSystemLast(void) { | ||
| 12819 | } | ||
| 12820 | |||
| 12821 | |||
| 12822 | dictType sdsKeyValueHashDictType = { | ||
| 12823 | dictSdsCaseHash, /* hash function */ | ||
| 12824 | NULL, /* key dup */ | ||
| 12825 | NULL, /* val dup */ | ||
| 12826 | dictSdsKeyCaseCompare, /* key compare */ | ||
| 12827 | dictSdsDestructor, /* key destructor */ | ||
| 12828 | dictSdsDestructor, /* val destructor */ | ||
| 12829 | NULL /* allow to expand */ | ||
| 12830 | }; | ||
| 12831 | |||
| 12832 | void moduleInitModulesSystem(void) { | ||
| 12833 | moduleUnblockedClients = listCreate(); | ||
| 12834 | server.loadmodule_queue = listCreate(); | ||
| 12835 | server.module_configs_queue = dictCreate(&sdsKeyValueHashDictType); | ||
| 12836 | server.module_gil_acquring = 0; | ||
| 12837 | modules = dictCreate(&modulesDictType); | ||
| 12838 | moduleAuthCallbacks = listCreate(); | ||
| 12839 | |||
| 12840 | /* Set up the keyspace notification subscriber list and static client */ | ||
| 12841 | moduleKeyspaceSubscribers = listCreate(); | ||
| 12842 | |||
| 12843 | modulePostExecUnitJobs = listCreate(); | ||
| 12844 | |||
| 12845 | /* Set up filter list */ | ||
| 12846 | moduleCommandFilters = listCreate(); | ||
| 12847 | |||
| 12848 | moduleRegisterCoreAPI(); | ||
| 12849 | |||
| 12850 | /* Create a pipe for module threads to be able to wake up the redis main thread. | ||
| 12851 | * Make the pipe non blocking. This is just a best effort aware mechanism | ||
| 12852 | * and we do not want to block not in the read nor in the write half. | ||
| 12853 | * Enable close-on-exec flag on pipes in case of the fork-exec system calls in | ||
| 12854 | * sentinels or redis servers. */ | ||
| 12855 | if (anetPipe(server.module_pipe, O_CLOEXEC|O_NONBLOCK, O_CLOEXEC|O_NONBLOCK) == -1) { | ||
| 12856 | serverLog(LL_WARNING, | ||
| 12857 | "Can't create the pipe for module threads: %s", strerror(errno)); | ||
| 12858 | exit(1); | ||
| 12859 | } | ||
| 12860 | |||
| 12861 | /* Create the timers radix tree. */ | ||
| 12862 | Timers = raxNew(); | ||
| 12863 | |||
| 12864 | /* Setup the event listeners data structures. */ | ||
| 12865 | RedisModule_EventListeners = listCreate(); | ||
| 12866 | |||
| 12867 | /* Making sure moduleEventVersions is synced with the number of events. */ | ||
| 12868 | serverAssert(sizeof(moduleEventVersions)/sizeof(moduleEventVersions[0]) == _REDISMODULE_EVENT_NEXT); | ||
| 12869 | |||
| 12870 | /* Our thread-safe contexts GIL must start with already locked: | ||
| 12871 | * it is just unlocked when it's safe. */ | ||
| 12872 | pthread_mutex_lock(&moduleGIL); | ||
| 12873 | } | ||
| 12874 | |||
| 12875 | void modulesCron(void) { | ||
| 12876 | /* Check number of temporary clients in the pool and free the unused ones | ||
| 12877 | * since the last cron. moduleTempClientMinCount tracks minimum count of | ||
| 12878 | * clients in the pool since the last cron. This is the number of clients | ||
| 12879 | * that we didn't use for the last cron period. */ | ||
| 12880 | |||
| 12881 | /* Limit the max client count to be freed at once to avoid latency spikes.*/ | ||
| 12882 | int iteration = 50; | ||
| 12883 | /* We are freeing clients if we have more than 8 unused clients. Keeping | ||
| 12884 | * small amount of clients to avoid client allocation costs if temporary | ||
| 12885 | * clients are required after some idle period. */ | ||
| 12886 | const unsigned int min_client = 8; | ||
| 12887 | while (iteration > 0 && moduleTempClientCount > 0 && moduleTempClientMinCount > min_client) { | ||
| 12888 | client *c = moduleTempClients[--moduleTempClientCount]; | ||
| 12889 | freeClient(c); | ||
| 12890 | iteration--; | ||
| 12891 | moduleTempClientMinCount--; | ||
| 12892 | } | ||
| 12893 | moduleTempClientMinCount = moduleTempClientCount; | ||
| 12894 | |||
| 12895 | /* Shrink moduleTempClients array itself if it is wasting some space */ | ||
| 12896 | if (moduleTempClientCap > 32 && moduleTempClientCap > moduleTempClientCount * 4) { | ||
| 12897 | moduleTempClientCap /= 4; | ||
| 12898 | moduleTempClients = zrealloc(moduleTempClients,sizeof(client*)*moduleTempClientCap); | ||
| 12899 | } | ||
| 12900 | } | ||
| 12901 | |||
| 12902 | void moduleLoadQueueEntryFree(struct moduleLoadQueueEntry *loadmod) { | ||
| 12903 | if (!loadmod) return; | ||
| 12904 | sdsfree(loadmod->path); | ||
| 12905 | for (int i = 0; i < loadmod->argc; i++) { | ||
| 12906 | decrRefCount(loadmod->argv[i]); | ||
| 12907 | } | ||
| 12908 | zfree(loadmod->argv); | ||
| 12909 | zfree(loadmod); | ||
| 12910 | } | ||
| 12911 | |||
| 12912 | /* Remove Module Configs from standardConfig array in config.c */ | ||
| 12913 | void moduleRemoveConfigs(RedisModule *module) { | ||
| 12914 | listIter li; | ||
| 12915 | listNode *ln; | ||
| 12916 | listRewind(module->module_configs, &li); | ||
| 12917 | while ((ln = listNext(&li))) { | ||
| 12918 | ModuleConfig *config = listNodeValue(ln); | ||
| 12919 | removeConfig(config->name); | ||
| 12920 | if (config->alias) | ||
| 12921 | removeConfig(config->alias); | ||
| 12922 | } | ||
| 12923 | } | ||
| 12924 | |||
| 12925 | /* Remove ACL categories added by the module when it fails to load. */ | ||
| 12926 | void moduleRemoveCateogires(RedisModule *module) { | ||
| 12927 | if (module->num_acl_categories_added) { | ||
| 12928 | ACLCleanupCategoriesOnFailure(module->num_acl_categories_added); | ||
| 12929 | } | ||
| 12930 | } | ||
| 12931 | |||
| 12932 | int VectorSets_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc); | ||
| 12933 | /* Load internal data types that bundled as modules */ | ||
| 12934 | void moduleLoadInternalModules(void) { | ||
| 12935 | #ifdef INCLUDE_VEC_SETS | ||
| 12936 | int retval = moduleOnLoad((int (*)(void *, void **, int)) VectorSets_OnLoad, NULL, NULL, NULL, 0, 0); | ||
| 12937 | serverAssert(retval == C_OK); | ||
| 12938 | #endif | ||
| 12939 | } | ||
| 12940 | |||
| 12941 | /* Load all the modules in the server.loadmodule_queue list, which is | ||
| 12942 | * populated by `loadmodule` directives in the configuration file. | ||
| 12943 | * We can't load modules directly when processing the configuration file | ||
| 12944 | * because the server must be fully initialized before loading modules. | ||
| 12945 | * | ||
| 12946 | * The function aborts the server on errors, since to start with missing | ||
| 12947 | * modules is not considered sane: clients may rely on the existence of | ||
| 12948 | * given commands, loading AOF also may need some modules to exist, and | ||
| 12949 | * if this instance is a slave, it must understand commands from master. */ | ||
| 12950 | void moduleLoadFromQueue(void) { | ||
| 12951 | listIter li; | ||
| 12952 | listNode *ln; | ||
| 12953 | |||
| 12954 | listRewind(server.loadmodule_queue,&li); | ||
| 12955 | while((ln = listNext(&li))) { | ||
| 12956 | struct moduleLoadQueueEntry *loadmod = ln->value; | ||
| 12957 | if (moduleLoad(loadmod->path,(void **)loadmod->argv,loadmod->argc, 0) | ||
| 12958 | == C_ERR) | ||
| 12959 | { | ||
| 12960 | serverLog(LL_WARNING, | ||
| 12961 | "Can't load module from %s: server aborting", | ||
| 12962 | loadmod->path); | ||
| 12963 | exit(1); | ||
| 12964 | } | ||
| 12965 | moduleLoadQueueEntryFree(loadmod); | ||
| 12966 | listDelNode(server.loadmodule_queue, ln); | ||
| 12967 | } | ||
| 12968 | if (dictSize(server.module_configs_queue)) { | ||
| 12969 | serverLog(LL_WARNING, "Unresolved Configuration(s) Detected:"); | ||
| 12970 | dictIterator di; | ||
| 12971 | dictEntry *de; | ||
| 12972 | dictInitIterator(&di, server.module_configs_queue); | ||
| 12973 | while ((de = dictNext(&di)) != NULL) { | ||
| 12974 | serverLog(LL_WARNING, ">>> '%s %s'", (char *)dictGetKey(de), (char *)dictGetVal(de)); | ||
| 12975 | } | ||
| 12976 | dictResetIterator(&di); | ||
| 12977 | serverLog(LL_WARNING, "Module Configuration detected without loadmodule directive or no ApplyConfig call: aborting"); | ||
| 12978 | exit(1); | ||
| 12979 | } | ||
| 12980 | } | ||
| 12981 | |||
| 12982 | void moduleFreeModuleStructure(struct RedisModule *module) { | ||
| 12983 | listRelease(module->types); | ||
| 12984 | listRelease(module->filters); | ||
| 12985 | listRelease(module->usedby); | ||
| 12986 | listRelease(module->using); | ||
| 12987 | listRelease(module->module_configs); | ||
| 12988 | sdsfree(module->name); | ||
| 12989 | moduleLoadQueueEntryFree(module->loadmod); | ||
| 12990 | zfree(module); | ||
| 12991 | } | ||
| 12992 | |||
| 12993 | void moduleFreeArgs(struct redisCommandArg *args, int num_args) { | ||
| 12994 | for (int j = 0; j < num_args; j++) { | ||
| 12995 | zfree((char *)args[j].name); | ||
| 12996 | zfree((char *)args[j].token); | ||
| 12997 | zfree((char *)args[j].summary); | ||
| 12998 | zfree((char *)args[j].since); | ||
| 12999 | zfree((char *)args[j].deprecated_since); | ||
| 13000 | zfree((char *)args[j].display_text); | ||
| 13001 | |||
| 13002 | if (args[j].subargs) { | ||
| 13003 | moduleFreeArgs(args[j].subargs, args[j].num_args); | ||
| 13004 | } | ||
| 13005 | } | ||
| 13006 | zfree(args); | ||
| 13007 | } | ||
| 13008 | |||
| 13009 | /* Free the command registered with the specified module. | ||
| 13010 | * On success C_OK is returned, otherwise C_ERR is returned. | ||
| 13011 | * | ||
| 13012 | * Note that caller needs to handle the deletion of the command table dict, | ||
| 13013 | * and after that needs to free the command->fullname and the command itself. | ||
| 13014 | */ | ||
| 13015 | int moduleFreeCommand(struct RedisModule *module, struct redisCommand *cmd) { | ||
| 13016 | if (cmd->proc != RedisModuleCommandDispatcher) | ||
| 13017 | return C_ERR; | ||
| 13018 | |||
| 13019 | RedisModuleCommand *cp = cmd->module_cmd; | ||
| 13020 | if (cp->module != module) | ||
| 13021 | return C_ERR; | ||
| 13022 | |||
| 13023 | /* Free everything except cmd->fullname and cmd itself. */ | ||
| 13024 | for (int j = 0; j < cmd->key_specs_num; j++) { | ||
| 13025 | if (cmd->key_specs[j].notes) | ||
| 13026 | zfree((char *)cmd->key_specs[j].notes); | ||
| 13027 | if (cmd->key_specs[j].begin_search_type == KSPEC_BS_KEYWORD) | ||
| 13028 | zfree((char *)cmd->key_specs[j].bs.keyword.keyword); | ||
| 13029 | } | ||
| 13030 | zfree(cmd->key_specs); | ||
| 13031 | for (int j = 0; cmd->tips && cmd->tips[j]; j++) | ||
| 13032 | zfree((char *)cmd->tips[j]); | ||
| 13033 | zfree(cmd->tips); | ||
| 13034 | for (int j = 0; cmd->history && cmd->history[j].since; j++) { | ||
| 13035 | zfree((char *)cmd->history[j].since); | ||
| 13036 | zfree((char *)cmd->history[j].changes); | ||
| 13037 | } | ||
| 13038 | zfree(cmd->history); | ||
| 13039 | zfree((char *)cmd->summary); | ||
| 13040 | zfree((char *)cmd->since); | ||
| 13041 | zfree((char *)cmd->deprecated_since); | ||
| 13042 | zfree((char *)cmd->complexity); | ||
| 13043 | if (cmd->latency_histogram) { | ||
| 13044 | hdr_close(cmd->latency_histogram); | ||
| 13045 | cmd->latency_histogram = NULL; | ||
| 13046 | } | ||
| 13047 | moduleFreeArgs(cmd->args, cmd->num_args); | ||
| 13048 | zfree(cp); | ||
| 13049 | |||
| 13050 | if (cmd->subcommands_dict) { | ||
| 13051 | dictEntry *de; | ||
| 13052 | dictIterator di; | ||
| 13053 | dictInitSafeIterator(&di, cmd->subcommands_dict); | ||
| 13054 | while ((de = dictNext(&di)) != NULL) { | ||
| 13055 | struct redisCommand *sub = dictGetVal(de); | ||
| 13056 | if (moduleFreeCommand(module, sub) != C_OK) continue; | ||
| 13057 | |||
| 13058 | serverAssert(dictDelete(cmd->subcommands_dict, sub->declared_name) == DICT_OK); | ||
| 13059 | sdsfree((sds)sub->declared_name); | ||
| 13060 | sdsfree(sub->fullname); | ||
| 13061 | zfree(sub); | ||
| 13062 | } | ||
| 13063 | dictResetIterator(&di); | ||
| 13064 | dictRelease(cmd->subcommands_dict); | ||
| 13065 | } | ||
| 13066 | |||
| 13067 | return C_OK; | ||
| 13068 | } | ||
| 13069 | |||
| 13070 | void moduleUnregisterCommands(struct RedisModule *module) { | ||
| 13071 | pauseAllIOThreads(); | ||
| 13072 | /* Unregister all the commands registered by this module. */ | ||
| 13073 | dictIterator di; | ||
| 13074 | dictEntry *de; | ||
| 13075 | dictInitSafeIterator(&di, server.commands); | ||
| 13076 | while ((de = dictNext(&di)) != NULL) { | ||
| 13077 | struct redisCommand *cmd = dictGetVal(de); | ||
| 13078 | if (moduleFreeCommand(module, cmd) != C_OK) continue; | ||
| 13079 | |||
| 13080 | serverAssert(dictDelete(server.commands, cmd->fullname) == DICT_OK); | ||
| 13081 | serverAssert(dictDelete(server.orig_commands, cmd->fullname) == DICT_OK); | ||
| 13082 | sdsfree((sds)cmd->declared_name); | ||
| 13083 | sdsfree(cmd->fullname); | ||
| 13084 | zfree(cmd); | ||
| 13085 | } | ||
| 13086 | dictResetIterator(&di); | ||
| 13087 | resumeAllIOThreads(); | ||
| 13088 | } | ||
| 13089 | |||
| 13090 | /* We parse argv to add sds "NAME VALUE" pairs to the server.module_configs_queue list of configs. | ||
| 13091 | * We also increment the module_argv pointer to just after ARGS if there are args, otherwise | ||
| 13092 | * we set it to NULL */ | ||
| 13093 | int parseLoadexArguments(RedisModuleString ***module_argv, int *module_argc) { | ||
| 13094 | int args_specified = 0; | ||
| 13095 | RedisModuleString **argv = *module_argv; | ||
| 13096 | int argc = *module_argc; | ||
| 13097 | for (int i = 0; i < argc; i++) { | ||
| 13098 | char *arg_val = argv[i]->ptr; | ||
| 13099 | if (!strcasecmp(arg_val, "CONFIG")) { | ||
| 13100 | if (i + 2 >= argc) { | ||
| 13101 | serverLog(LL_NOTICE, "CONFIG specified without name value pair"); | ||
| 13102 | return REDISMODULE_ERR; | ||
| 13103 | } | ||
| 13104 | sds name = sdsdup(argv[i + 1]->ptr); | ||
| 13105 | sds value = sdsdup(argv[i + 2]->ptr); | ||
| 13106 | if (!dictReplace(server.module_configs_queue, name, value)) sdsfree(name); | ||
| 13107 | i += 2; | ||
| 13108 | } else if (!strcasecmp(arg_val, "ARGS")) { | ||
| 13109 | args_specified = 1; | ||
| 13110 | i++; | ||
| 13111 | if (i >= argc) { | ||
| 13112 | *module_argv = NULL; | ||
| 13113 | *module_argc = 0; | ||
| 13114 | } else { | ||
| 13115 | *module_argv = argv + i; | ||
| 13116 | *module_argc = argc - i; | ||
| 13117 | } | ||
| 13118 | break; | ||
| 13119 | } else { | ||
| 13120 | serverLog(LL_NOTICE, "Syntax Error from arguments to loadex around %s.", arg_val); | ||
| 13121 | return REDISMODULE_ERR; | ||
| 13122 | } | ||
| 13123 | } | ||
| 13124 | if (!args_specified) { | ||
| 13125 | *module_argv = NULL; | ||
| 13126 | *module_argc = 0; | ||
| 13127 | } | ||
| 13128 | return REDISMODULE_OK; | ||
| 13129 | } | ||
| 13130 | |||
| 13131 | /* Unregister module-related things, called when moduleLoad fails or moduleUnload. */ | ||
| 13132 | void moduleUnregisterCleanup(RedisModule *module) { | ||
| 13133 | moduleFreeAuthenticatedClients(module); | ||
| 13134 | moduleUnregisterCommands(module); | ||
| 13135 | moduleUnsubscribeNotifications(module); | ||
| 13136 | moduleUnregisterSharedAPI(module); | ||
| 13137 | moduleUnregisterUsedAPI(module); | ||
| 13138 | moduleUnregisterFilters(module); | ||
| 13139 | moduleUnsubscribeAllServerEvents(module); | ||
| 13140 | moduleRemoveConfigs(module); | ||
| 13141 | moduleUnregisterAuthCBs(module); | ||
| 13142 | } | ||
| 13143 | |||
| 13144 | /* Load a module by path and initialize it. On success C_OK is returned, otherwise | ||
| 13145 | * C_ERR is returned. */ | ||
| 13146 | int moduleLoad(const char *path, void **module_argv, int module_argc, int is_loadex) { | ||
| 13147 | int (*onload)(void *, void **, int); | ||
| 13148 | void *handle; | ||
| 13149 | |||
| 13150 | struct stat st; | ||
| 13151 | if (stat(path, &st) == 0) { | ||
| 13152 | /* This check is best effort */ | ||
| 13153 | if (!(st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) { | ||
| 13154 | serverLog(LL_WARNING, "Module %s failed to load: It does not have execute permissions.", path); | ||
| 13155 | return C_ERR; | ||
| 13156 | } | ||
| 13157 | } | ||
| 13158 | |||
| 13159 | handle = dlopen(path,RTLD_NOW|RTLD_LOCAL); | ||
| 13160 | if (handle == NULL) { | ||
| 13161 | serverLog(LL_WARNING, "Module %s failed to load: %s", path, dlerror()); | ||
| 13162 | return C_ERR; | ||
| 13163 | } | ||
| 13164 | onload = (int (*)(void *, void **, int))(unsigned long) dlsym(handle,"RedisModule_OnLoad"); | ||
| 13165 | if (onload == NULL) { | ||
| 13166 | dlclose(handle); | ||
| 13167 | serverLog(LL_WARNING, | ||
| 13168 | "Module %s does not export RedisModule_OnLoad() " | ||
| 13169 | "symbol. Module not loaded.",path); | ||
| 13170 | return C_ERR; | ||
| 13171 | } | ||
| 13172 | |||
| 13173 | return moduleOnLoad(onload, path, handle, module_argv, module_argc, is_loadex); | ||
| 13174 | } | ||
| 13175 | |||
| 13176 | /* Load a module by its 'onload' callback and initialize it. On success C_OK is returned, otherwise | ||
| 13177 | * C_ERR is returned. */ | ||
| 13178 | int moduleOnLoad(int (*onload)(void *, void **, int), const char *path, void *handle, void **module_argv, int module_argc, int is_loadex) { | ||
| 13179 | RedisModuleCtx ctx; | ||
| 13180 | moduleCreateContext(&ctx, NULL, REDISMODULE_CTX_TEMP_CLIENT); /* We pass NULL since we don't have a module yet. */ | ||
| 13181 | if (onload((void*)&ctx,module_argv,module_argc) == REDISMODULE_ERR) { | ||
| 13182 | serverLog(LL_WARNING, | ||
| 13183 | "Module %s initialization failed. Module not loaded",path); | ||
| 13184 | if (ctx.module) { | ||
| 13185 | moduleUnregisterCleanup(ctx.module); | ||
| 13186 | moduleRemoveCateogires(ctx.module); | ||
| 13187 | moduleFreeModuleStructure(ctx.module); | ||
| 13188 | } | ||
| 13189 | moduleFreeContext(&ctx); | ||
| 13190 | if (handle) dlclose(handle); | ||
| 13191 | return C_ERR; | ||
| 13192 | } | ||
| 13193 | |||
| 13194 | /* Redis module loaded! Register it. */ | ||
| 13195 | dictAdd(modules,ctx.module->name,ctx.module); | ||
| 13196 | ctx.module->blocked_clients = 0; | ||
| 13197 | ctx.module->handle = handle; | ||
| 13198 | ctx.module->loadmod = zmalloc(sizeof(struct moduleLoadQueueEntry)); | ||
| 13199 | ctx.module->loadmod->path = sdsnew(path); | ||
| 13200 | ctx.module->loadmod->argv = module_argc ? zmalloc(sizeof(robj*)*module_argc) : NULL; | ||
| 13201 | ctx.module->loadmod->argc = module_argc; | ||
| 13202 | for (int i = 0; i < module_argc; i++) { | ||
| 13203 | ctx.module->loadmod->argv[i] = module_argv[i]; | ||
| 13204 | incrRefCount(ctx.module->loadmod->argv[i]); | ||
| 13205 | } | ||
| 13206 | |||
| 13207 | /* If module commands have ACL categories, recompute command bits | ||
| 13208 | * for all existing users once the modules has been registered. */ | ||
| 13209 | if (ctx.module->num_commands_with_acl_categories) { | ||
| 13210 | ACLRecomputeCommandBitsFromCommandRulesAllUsers(); | ||
| 13211 | } | ||
| 13212 | if (path) serverLog(LL_NOTICE,"Module '%s' loaded from %s",ctx.module->name,path); | ||
| 13213 | ctx.module->onload = 0; | ||
| 13214 | |||
| 13215 | int post_load_err = 0; | ||
| 13216 | if (listLength(ctx.module->module_configs) && !(ctx.module->configs_initialized & MODULE_CONFIGS_USER_VALS)) { | ||
| 13217 | serverLogRaw(LL_WARNING, "Module Configurations were not set, missing LoadConfigs call. Unloading the module."); | ||
| 13218 | post_load_err = 1; | ||
| 13219 | } | ||
| 13220 | |||
| 13221 | if (is_loadex && dictSize(server.module_configs_queue)) { | ||
| 13222 | serverLogRaw(LL_WARNING, "Loadex configurations were not applied, likely due to invalid arguments. Unloading the module."); | ||
| 13223 | post_load_err = 1; | ||
| 13224 | } | ||
| 13225 | |||
| 13226 | if (post_load_err) { | ||
| 13227 | serverAssert(moduleUnload(ctx.module->name, NULL, 1) == C_OK); | ||
| 13228 | moduleFreeContext(&ctx); | ||
| 13229 | return C_ERR; | ||
| 13230 | } | ||
| 13231 | |||
| 13232 | /* Fire the loaded modules event. */ | ||
| 13233 | moduleFireServerEvent(REDISMODULE_EVENT_MODULE_CHANGE, | ||
| 13234 | REDISMODULE_SUBEVENT_MODULE_LOADED, | ||
| 13235 | ctx.module); | ||
| 13236 | |||
| 13237 | moduleFreeContext(&ctx); | ||
| 13238 | return C_OK; | ||
| 13239 | } | ||
| 13240 | |||
| 13241 | /* Unload the module registered with the specified name. On success | ||
| 13242 | * C_OK is returned, otherwise C_ERR is returned and errmsg is set | ||
| 13243 | * with an appropriate message. | ||
| 13244 | * Only forcefully unload this module, passing forced_unload != 0, | ||
| 13245 | * if it is certain that it has not yet been in use (e.g., immediate | ||
| 13246 | * unload on failed load). */ | ||
| 13247 | int moduleUnload(sds name, const char **errmsg, int forced_unload) { | ||
| 13248 | struct RedisModule *module = dictFetchValue(modules,name); | ||
| 13249 | |||
| 13250 | if (module == NULL) { | ||
| 13251 | *errmsg = "no such module with that name"; | ||
| 13252 | return C_ERR; | ||
| 13253 | } else if (sdslen(module->loadmod->path) == 0) { | ||
| 13254 | *errmsg = "the module can't be unloaded"; | ||
| 13255 | return C_ERR; | ||
| 13256 | } else if (listLength(module->types) && !forced_unload) { | ||
| 13257 | *errmsg = "the module exports one or more module-side data " | ||
| 13258 | "types, can't unload"; | ||
| 13259 | return C_ERR; | ||
| 13260 | } else if (listLength(module->usedby)) { | ||
| 13261 | *errmsg = "the module exports APIs used by other modules. " | ||
| 13262 | "Please unload them first and try again"; | ||
| 13263 | return C_ERR; | ||
| 13264 | } else if (module->blocked_clients) { | ||
| 13265 | *errmsg = "the module has blocked clients. " | ||
| 13266 | "Please wait for them to be unblocked and try again"; | ||
| 13267 | return C_ERR; | ||
| 13268 | } else if (moduleHoldsTimer(module)) { | ||
| 13269 | *errmsg = "the module holds timer that is not fired. " | ||
| 13270 | "Please stop the timer or wait until it fires."; | ||
| 13271 | return C_ERR; | ||
| 13272 | } | ||
| 13273 | |||
| 13274 | /* Give module a chance to clean up. */ | ||
| 13275 | int (*onunload)(void *); | ||
| 13276 | onunload = (int (*)(void *))(unsigned long) dlsym(module->handle, "RedisModule_OnUnload"); | ||
| 13277 | if (onunload) { | ||
| 13278 | RedisModuleCtx ctx; | ||
| 13279 | moduleCreateContext(&ctx, module, REDISMODULE_CTX_TEMP_CLIENT); | ||
| 13280 | int unload_status = onunload((void*)&ctx); | ||
| 13281 | moduleFreeContext(&ctx); | ||
| 13282 | |||
| 13283 | if (unload_status == REDISMODULE_ERR) { | ||
| 13284 | serverLog(LL_WARNING, "Module %s OnUnload failed. Unload canceled.", name); | ||
| 13285 | errno = ECANCELED; | ||
| 13286 | return C_ERR; | ||
| 13287 | } | ||
| 13288 | } | ||
| 13289 | |||
| 13290 | moduleUnregisterCleanup(module); | ||
| 13291 | |||
| 13292 | /* Unload the dynamic library. */ | ||
| 13293 | if (dlclose(module->handle) == -1) { | ||
| 13294 | char *error = dlerror(); | ||
| 13295 | if (error == NULL) error = "Unknown error"; | ||
| 13296 | serverLog(LL_WARNING,"Error when trying to close the %s module: %s", | ||
| 13297 | module->name, error); | ||
| 13298 | } | ||
| 13299 | |||
| 13300 | /* Fire the unloaded modules event. */ | ||
| 13301 | moduleFireServerEvent(REDISMODULE_EVENT_MODULE_CHANGE, | ||
| 13302 | REDISMODULE_SUBEVENT_MODULE_UNLOADED, | ||
| 13303 | module); | ||
| 13304 | |||
| 13305 | /* Remove from list of modules. */ | ||
| 13306 | serverLog(LL_NOTICE,"Module %s unloaded",module->name); | ||
| 13307 | dictDelete(modules,module->name); | ||
| 13308 | module->name = NULL; /* The name was already freed by dictDelete(). */ | ||
| 13309 | moduleFreeModuleStructure(module); | ||
| 13310 | |||
| 13311 | /* Recompute command bits for all users once the modules has been completely unloaded. */ | ||
| 13312 | ACLRecomputeCommandBitsFromCommandRulesAllUsers(); | ||
| 13313 | return C_OK; | ||
| 13314 | } | ||
| 13315 | |||
| 13316 | void modulePipeReadable(aeEventLoop *el, int fd, void *privdata, int mask) { | ||
| 13317 | UNUSED(el); | ||
| 13318 | UNUSED(fd); | ||
| 13319 | UNUSED(mask); | ||
| 13320 | UNUSED(privdata); | ||
| 13321 | |||
| 13322 | char buf[128]; | ||
| 13323 | while (read(fd, buf, sizeof(buf)) == sizeof(buf)); | ||
| 13324 | |||
| 13325 | /* Handle event loop events if pipe was written from event loop API */ | ||
| 13326 | eventLoopHandleOneShotEvents(); | ||
| 13327 | } | ||
| 13328 | |||
| 13329 | /* Helper function for the MODULE and HELLO command: send the list of the | ||
| 13330 | * loaded modules to the client. */ | ||
| 13331 | void addReplyLoadedModules(client *c) { | ||
| 13332 | const long ln = dictSize(modules); | ||
| 13333 | /* In case no module is load we avoid iterator creation */ | ||
| 13334 | addReplyArrayLen(c,ln); | ||
| 13335 | if (ln == 0) { | ||
| 13336 | return; | ||
| 13337 | } | ||
| 13338 | dictIterator di; | ||
| 13339 | dictEntry *de; | ||
| 13340 | |||
| 13341 | dictInitIterator(&di, modules); | ||
| 13342 | while ((de = dictNext(&di)) != NULL) { | ||
| 13343 | sds name = dictGetKey(de); | ||
| 13344 | struct RedisModule *module = dictGetVal(de); | ||
| 13345 | sds path = module->loadmod->path; | ||
| 13346 | addReplyMapLen(c,4); | ||
| 13347 | addReplyBulkCString(c,"name"); | ||
| 13348 | addReplyBulkCBuffer(c,name,sdslen(name)); | ||
| 13349 | addReplyBulkCString(c,"ver"); | ||
| 13350 | addReplyLongLong(c,module->ver); | ||
| 13351 | addReplyBulkCString(c,"path"); | ||
| 13352 | addReplyBulkCBuffer(c,path,sdslen(path)); | ||
| 13353 | addReplyBulkCString(c,"args"); | ||
| 13354 | addReplyArrayLen(c,module->loadmod->argc); | ||
| 13355 | for (int i = 0; i < module->loadmod->argc; i++) { | ||
| 13356 | addReplyBulk(c,module->loadmod->argv[i]); | ||
| 13357 | } | ||
| 13358 | } | ||
| 13359 | dictResetIterator(&di); | ||
| 13360 | } | ||
| 13361 | |||
| 13362 | /* Helper for genModulesInfoString(): given a list of modules, return | ||
| 13363 | * an SDS string in the form "[modulename|modulename2|...]" */ | ||
| 13364 | sds genModulesInfoStringRenderModulesList(list *l) { | ||
| 13365 | listIter li; | ||
| 13366 | listNode *ln; | ||
| 13367 | listRewind(l,&li); | ||
| 13368 | sds output = sdsnew("["); | ||
| 13369 | while((ln = listNext(&li))) { | ||
| 13370 | RedisModule *module = ln->value; | ||
| 13371 | output = sdscat(output,module->name); | ||
| 13372 | if (ln != listLast(l)) | ||
| 13373 | output = sdscat(output,"|"); | ||
| 13374 | } | ||
| 13375 | output = sdscat(output,"]"); | ||
| 13376 | return output; | ||
| 13377 | } | ||
| 13378 | |||
| 13379 | /* Helper for genModulesInfoString(): render module options as an SDS string. */ | ||
| 13380 | sds genModulesInfoStringRenderModuleOptions(struct RedisModule *module) { | ||
| 13381 | sds output = sdsnew("["); | ||
| 13382 | if (module->options & REDISMODULE_OPTIONS_HANDLE_IO_ERRORS) | ||
| 13383 | output = sdscat(output,"handle-io-errors|"); | ||
| 13384 | if (module->options & REDISMODULE_OPTIONS_HANDLE_REPL_ASYNC_LOAD) | ||
| 13385 | output = sdscat(output,"handle-repl-async-load|"); | ||
| 13386 | if (module->options & REDISMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED) | ||
| 13387 | output = sdscat(output,"no-implicit-signal-modified|"); | ||
| 13388 | output = sdstrim(output,"|"); | ||
| 13389 | output = sdscat(output,"]"); | ||
| 13390 | return output; | ||
| 13391 | } | ||
| 13392 | |||
| 13393 | |||
| 13394 | /* Helper function for the INFO command: adds loaded modules as to info's | ||
| 13395 | * output. | ||
| 13396 | * | ||
| 13397 | * After the call, the passed sds info string is no longer valid and all the | ||
| 13398 | * references must be substituted with the new pointer returned by the call. */ | ||
| 13399 | sds genModulesInfoString(sds info) { | ||
| 13400 | dictIterator di; | ||
| 13401 | dictEntry *de; | ||
| 13402 | |||
| 13403 | dictInitIterator(&di, modules); | ||
| 13404 | while ((de = dictNext(&di)) != NULL) { | ||
| 13405 | sds name = dictGetKey(de); | ||
| 13406 | struct RedisModule *module = dictGetVal(de); | ||
| 13407 | |||
| 13408 | sds usedby = genModulesInfoStringRenderModulesList(module->usedby); | ||
| 13409 | sds using = genModulesInfoStringRenderModulesList(module->using); | ||
| 13410 | sds options = genModulesInfoStringRenderModuleOptions(module); | ||
| 13411 | info = sdscatfmt(info, | ||
| 13412 | "module:name=%S,ver=%i,api=%i,filters=%i," | ||
| 13413 | "usedby=%S,using=%S,options=%S\r\n", | ||
| 13414 | name, module->ver, module->apiver, | ||
| 13415 | (int)listLength(module->filters), usedby, using, options); | ||
| 13416 | sdsfree(usedby); | ||
| 13417 | sdsfree(using); | ||
| 13418 | sdsfree(options); | ||
| 13419 | } | ||
| 13420 | dictResetIterator(&di); | ||
| 13421 | return info; | ||
| 13422 | } | ||
| 13423 | |||
| 13424 | /* -------------------------------------------------------------------------- | ||
| 13425 | * Module Configurations API internals | ||
| 13426 | * -------------------------------------------------------------------------- */ | ||
| 13427 | |||
| 13428 | /* Check if the configuration name is already registered */ | ||
| 13429 | int isModuleConfigNameRegistered(RedisModule *module, const char *name) { | ||
| 13430 | listNode *match = listSearchKey(module->module_configs, (void *) name); | ||
| 13431 | return match != NULL; | ||
| 13432 | } | ||
| 13433 | |||
| 13434 | /* Assert that the flags passed into the RM_RegisterConfig Suite are valid */ | ||
| 13435 | int moduleVerifyConfigFlags(unsigned int flags, configType type) { | ||
| 13436 | if ((flags & ~(REDISMODULE_CONFIG_DEFAULT | ||
| 13437 | | REDISMODULE_CONFIG_IMMUTABLE | ||
| 13438 | | REDISMODULE_CONFIG_SENSITIVE | ||
| 13439 | | REDISMODULE_CONFIG_HIDDEN | ||
| 13440 | | REDISMODULE_CONFIG_PROTECTED | ||
| 13441 | | REDISMODULE_CONFIG_DENY_LOADING | ||
| 13442 | | REDISMODULE_CONFIG_BITFLAGS | ||
| 13443 | | REDISMODULE_CONFIG_MEMORY | ||
| 13444 | | REDISMODULE_CONFIG_UNPREFIXED))) { | ||
| 13445 | serverLogRaw(LL_WARNING, "Invalid flag(s) for configuration"); | ||
| 13446 | return REDISMODULE_ERR; | ||
| 13447 | } | ||
| 13448 | if (type != NUMERIC_CONFIG && flags & REDISMODULE_CONFIG_MEMORY) { | ||
| 13449 | serverLogRaw(LL_WARNING, "Numeric flag provided for non-numeric configuration."); | ||
| 13450 | return REDISMODULE_ERR; | ||
| 13451 | } | ||
| 13452 | if (type != ENUM_CONFIG && flags & REDISMODULE_CONFIG_BITFLAGS) { | ||
| 13453 | serverLogRaw(LL_WARNING, "Enum flag provided for non-enum configuration."); | ||
| 13454 | return REDISMODULE_ERR; | ||
| 13455 | } | ||
| 13456 | return REDISMODULE_OK; | ||
| 13457 | } | ||
| 13458 | |||
| 13459 | /* Verify a module resource or name has only alphanumeric characters, underscores | ||
| 13460 | * or dashes. */ | ||
| 13461 | int moduleVerifyResourceName(const char *name) { | ||
| 13462 | if (name[0] == '\0') { | ||
| 13463 | return REDISMODULE_ERR; | ||
| 13464 | } | ||
| 13465 | |||
| 13466 | for (size_t i = 0; name[i] != '\0'; i++) { | ||
| 13467 | char curr_char = name[i]; | ||
| 13468 | if ((curr_char >= 'a' && curr_char <= 'z') || | ||
| 13469 | (curr_char >= 'A' && curr_char <= 'Z') || | ||
| 13470 | (curr_char >= '0' && curr_char <= '9') || | ||
| 13471 | (curr_char == '_') || (curr_char == '-')) | ||
| 13472 | { | ||
| 13473 | continue; | ||
| 13474 | } | ||
| 13475 | serverLog(LL_WARNING, "Invalid character %c in Module resource name %s.", curr_char, name); | ||
| 13476 | return REDISMODULE_ERR; | ||
| 13477 | } | ||
| 13478 | return REDISMODULE_OK; | ||
| 13479 | } | ||
| 13480 | |||
| 13481 | /* Verify unprefixed name config might be a single "<name>" or in the form | ||
| 13482 | * "<name>|<alias>". Unlike moduleVerifyResourceName(), unprefixed name config | ||
| 13483 | * allows a single dot in the name or alias. | ||
| 13484 | * | ||
| 13485 | * delim - Updates to point to "|" if it exists, NULL otherwise. | ||
| 13486 | */ | ||
| 13487 | int moduleVerifyUnprefixedName(const char *nameAlias, const char **delim) { | ||
| 13488 | if (nameAlias[0] == '\0') | ||
| 13489 | return REDISMODULE_ERR; | ||
| 13490 | |||
| 13491 | *delim = NULL; | ||
| 13492 | int dot_count = 0, lname = 0; | ||
| 13493 | |||
| 13494 | for (size_t i = 0; nameAlias[i] != '\0'; i++) { | ||
| 13495 | char ch = nameAlias[i]; | ||
| 13496 | |||
| 13497 | if (((*delim) == NULL) && (ch == '|')) { | ||
| 13498 | /* Handle single separator between name and alias */ | ||
| 13499 | if (!lname) { | ||
| 13500 | serverLog(LL_WARNING, "Module configuration name is empty: %s", nameAlias); | ||
| 13501 | return REDISMODULE_ERR; | ||
| 13502 | } | ||
| 13503 | *delim = &nameAlias[i]; | ||
| 13504 | dot_count = lname = 0; | ||
| 13505 | } else if ( (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || | ||
| 13506 | (ch >= '0' && ch <= '9') || (ch == '_') || (ch == '-') ) | ||
| 13507 | { | ||
| 13508 | ++lname; | ||
| 13509 | } else if (ch == '.') { | ||
| 13510 | /* Allow only one dot per section (name or alias) */ | ||
| 13511 | if (++dot_count > 1) { | ||
| 13512 | serverLog(LL_WARNING, "Invalid character sequence in Module configuration name or alias: %s", nameAlias); | ||
| 13513 | return REDISMODULE_ERR; | ||
| 13514 | } | ||
| 13515 | } else { | ||
| 13516 | serverLog(LL_WARNING, "Invalid character %c in Module configuration name or alias %s.", ch, nameAlias); | ||
| 13517 | return REDISMODULE_ERR; | ||
| 13518 | } | ||
| 13519 | } | ||
| 13520 | |||
| 13521 | if (!lname) { | ||
| 13522 | serverLog(LL_WARNING, "Module configuration name or alias is empty : %s", nameAlias); | ||
| 13523 | return REDISMODULE_ERR; | ||
| 13524 | } | ||
| 13525 | |||
| 13526 | return REDISMODULE_OK; | ||
| 13527 | } | ||
| 13528 | |||
| 13529 | /* This is a series of set functions for each type that act as dispatchers for | ||
| 13530 | * config.c to call module set callbacks. */ | ||
| 13531 | #define CONFIG_ERR_SIZE 256 | ||
| 13532 | static char configerr[CONFIG_ERR_SIZE]; | ||
| 13533 | static void propagateErrorString(RedisModuleString *err_in, const char **err) { | ||
| 13534 | if (err_in) { | ||
| 13535 | redis_strlcpy(configerr, err_in->ptr, CONFIG_ERR_SIZE); | ||
| 13536 | decrRefCount(err_in); | ||
| 13537 | *err = configerr; | ||
| 13538 | } | ||
| 13539 | } | ||
| 13540 | |||
| 13541 | /* If configuration was originally registered with indication to prefix the name, | ||
| 13542 | * return the name without the prefix by skipping prefix "<MODULE-NAME>.". | ||
| 13543 | * Otherwise, return the stored name as is. */ | ||
| 13544 | static char *getRegisteredConfigName(ModuleConfig *config) { | ||
| 13545 | if (config->unprefixedFlag) | ||
| 13546 | return config->name; | ||
| 13547 | |||
| 13548 | /* For prefixed configuration, find the '.' indicating the end of the prefix */ | ||
| 13549 | char *endOfPrefix = strchr(config->name, '.'); | ||
| 13550 | serverAssert(endOfPrefix != NULL); | ||
| 13551 | return endOfPrefix + 1; | ||
| 13552 | } | ||
| 13553 | |||
| 13554 | int setModuleBoolConfig(ModuleConfig *config, int val, const char **err) { | ||
| 13555 | RedisModuleString *error = NULL; | ||
| 13556 | |||
| 13557 | char *rname = getRegisteredConfigName(config); | ||
| 13558 | int return_code = config->set_fn.set_bool(rname, val, config->privdata, &error); | ||
| 13559 | propagateErrorString(error, err); | ||
| 13560 | return return_code == REDISMODULE_OK ? 1 : 0; | ||
| 13561 | } | ||
| 13562 | |||
| 13563 | int setModuleStringConfig(ModuleConfig *config, sds strval, const char **err) { | ||
| 13564 | RedisModuleString *error = NULL; | ||
| 13565 | RedisModuleString *new = createStringObject(strval, sdslen(strval)); | ||
| 13566 | |||
| 13567 | char *rname = getRegisteredConfigName(config); | ||
| 13568 | int return_code = config->set_fn.set_string(rname, new, config->privdata, &error); | ||
| 13569 | propagateErrorString(error, err); | ||
| 13570 | decrRefCount(new); | ||
| 13571 | return return_code == REDISMODULE_OK ? 1 : 0; | ||
| 13572 | } | ||
| 13573 | |||
| 13574 | int setModuleEnumConfig(ModuleConfig *config, int val, const char **err) { | ||
| 13575 | RedisModuleString *error = NULL; | ||
| 13576 | int return_code = config->set_fn.set_enum(config->name, val, config->privdata, &error); | ||
| 13577 | propagateErrorString(error, err); | ||
| 13578 | return return_code == REDISMODULE_OK ? 1 : 0; | ||
| 13579 | } | ||
| 13580 | |||
| 13581 | int setModuleNumericConfig(ModuleConfig *config, long long val, const char **err) { | ||
| 13582 | RedisModuleString *error = NULL; | ||
| 13583 | char *rname = getRegisteredConfigName(config); | ||
| 13584 | int return_code = config->set_fn.set_numeric(rname, val, config->privdata, &error); | ||
| 13585 | propagateErrorString(error, err); | ||
| 13586 | return return_code == REDISMODULE_OK ? 1 : 0; | ||
| 13587 | } | ||
| 13588 | |||
| 13589 | /* This is a series of get functions for each type that act as dispatchers for | ||
| 13590 | * config.c to call module set callbacks. */ | ||
| 13591 | int getModuleBoolConfig(ModuleConfig *module_config) { | ||
| 13592 | char *rname = getRegisteredConfigName(module_config); | ||
| 13593 | return module_config->get_fn.get_bool(rname, module_config->privdata); | ||
| 13594 | } | ||
| 13595 | |||
| 13596 | sds getModuleStringConfig(ModuleConfig *module_config) { | ||
| 13597 | char *rname = getRegisteredConfigName(module_config); | ||
| 13598 | RedisModuleString *val = module_config->get_fn.get_string(rname, module_config->privdata); | ||
| 13599 | return val ? sdsdup(val->ptr) : NULL; | ||
| 13600 | } | ||
| 13601 | |||
| 13602 | int getModuleEnumConfig(ModuleConfig *module_config) { | ||
| 13603 | char *rname = getRegisteredConfigName(module_config); | ||
| 13604 | return module_config->get_fn.get_enum(rname, module_config->privdata); | ||
| 13605 | } | ||
| 13606 | |||
| 13607 | long long getModuleNumericConfig(ModuleConfig *module_config) { | ||
| 13608 | char *rname = getRegisteredConfigName(module_config); | ||
| 13609 | return module_config->get_fn.get_numeric(rname, module_config->privdata); | ||
| 13610 | } | ||
| 13611 | |||
| 13612 | int loadModuleDefaultConfigs(RedisModule *module) { | ||
| 13613 | listIter li; | ||
| 13614 | listNode *ln; | ||
| 13615 | const char *err = NULL; | ||
| 13616 | listRewind(module->module_configs, &li); | ||
| 13617 | while ((ln = listNext(&li))) { | ||
| 13618 | ModuleConfig *module_config = listNodeValue(ln); | ||
| 13619 | if (!performModuleConfigSetDefaultFromName(module_config->name, &err)) { | ||
| 13620 | serverLog(LL_WARNING, "Issue attempting to set default value of configuration %s : %s", module_config->name, err); | ||
| 13621 | return REDISMODULE_ERR; | ||
| 13622 | } | ||
| 13623 | } | ||
| 13624 | module->configs_initialized |= MODULE_CONFIGS_DEFAULTS; | ||
| 13625 | return REDISMODULE_OK; | ||
| 13626 | } | ||
| 13627 | |||
| 13628 | /* This function takes a module and a list of configs stored as sds NAME VALUE pairs. | ||
| 13629 | * It attempts to call set on each of these configs. */ | ||
| 13630 | int loadModuleConfigs(RedisModule *module) { | ||
| 13631 | listIter li; | ||
| 13632 | listNode *ln; | ||
| 13633 | const char *err = NULL; | ||
| 13634 | listRewind(module->module_configs, &li); | ||
| 13635 | const int set_default_if_missing = !(module->configs_initialized & MODULE_CONFIGS_DEFAULTS); | ||
| 13636 | while ((ln = listNext(&li))) { | ||
| 13637 | ModuleConfig *module_config = listNodeValue(ln); | ||
| 13638 | dictEntry *de = dictUnlink(server.module_configs_queue, module_config->name); | ||
| 13639 | if ((!de) && (module_config->alias)) | ||
| 13640 | de = dictUnlink(server.module_configs_queue, module_config->alias); | ||
| 13641 | |||
| 13642 | /* If found in the queue, set the value. Otherwise, set the default value. */ | ||
| 13643 | if (de) { | ||
| 13644 | if (!performModuleConfigSetFromName(dictGetKey(de), dictGetVal(de), &err)) { | ||
| 13645 | serverLog(LL_WARNING, "Issue during loading of configuration %s : %s", (sds) dictGetKey(de), err); | ||
| 13646 | dictFreeUnlinkedEntry(server.module_configs_queue, de); | ||
| 13647 | dictEmpty(server.module_configs_queue, NULL); | ||
| 13648 | return REDISMODULE_ERR; | ||
| 13649 | } | ||
| 13650 | dictFreeUnlinkedEntry(server.module_configs_queue, de); | ||
| 13651 | } else if (set_default_if_missing) { | ||
| 13652 | if (!performModuleConfigSetDefaultFromName(module_config->name, &err)) { | ||
| 13653 | serverLog(LL_WARNING, "Issue attempting to set default value of configuration %s : %s", module_config->name, err); | ||
| 13654 | dictEmpty(server.module_configs_queue, NULL); | ||
| 13655 | return REDISMODULE_ERR; | ||
| 13656 | } | ||
| 13657 | } | ||
| 13658 | } | ||
| 13659 | module->configs_initialized = MODULE_CONFIGS_ALL_APPLIED; | ||
| 13660 | return REDISMODULE_OK; | ||
| 13661 | } | ||
| 13662 | |||
| 13663 | /* Add module_config to the list if the apply and privdata do not match one already in it. */ | ||
| 13664 | void addModuleConfigApply(list *module_configs, ModuleConfig *module_config) { | ||
| 13665 | if (!module_config->apply_fn) return; | ||
| 13666 | listIter li; | ||
| 13667 | listNode *ln; | ||
| 13668 | ModuleConfig *pending_apply; | ||
| 13669 | listRewind(module_configs, &li); | ||
| 13670 | while ((ln = listNext(&li))) { | ||
| 13671 | pending_apply = listNodeValue(ln); | ||
| 13672 | if (pending_apply->apply_fn == module_config->apply_fn && pending_apply->privdata == module_config->privdata) { | ||
| 13673 | return; | ||
| 13674 | } | ||
| 13675 | } | ||
| 13676 | listAddNodeTail(module_configs, module_config); | ||
| 13677 | } | ||
| 13678 | |||
| 13679 | /* Call apply on a module config. Assumes module_config->apply_fn != NULL! */ | ||
| 13680 | int moduleConfigApplyInternal(ModuleConfig *module_config, const char **err) { | ||
| 13681 | RedisModuleCtx ctx; | ||
| 13682 | RedisModuleString *error = NULL; | ||
| 13683 | |||
| 13684 | moduleCreateContext(&ctx, module_config->module, REDISMODULE_CTX_NONE); | ||
| 13685 | if (module_config->apply_fn(&ctx, module_config->privdata, &error)) { | ||
| 13686 | propagateErrorString(error, err); | ||
| 13687 | moduleFreeContext(&ctx); | ||
| 13688 | return 0; | ||
| 13689 | } | ||
| 13690 | moduleFreeContext(&ctx); | ||
| 13691 | return 1; | ||
| 13692 | } | ||
| 13693 | |||
| 13694 | /* Call apply on a single module config. */ | ||
| 13695 | int moduleConfigApply(ModuleConfig *module_config, const char **err) { | ||
| 13696 | if (module_config->apply_fn == NULL) return 1; | ||
| 13697 | return moduleConfigApplyInternal(module_config, err); | ||
| 13698 | } | ||
| 13699 | |||
| 13700 | int moduleConfigNeedsApply(ModuleConfig *config) { | ||
| 13701 | return config->apply_fn != NULL; | ||
| 13702 | } | ||
| 13703 | |||
| 13704 | /* Call apply on all module configs specified in set, if an apply function was specified at registration time. */ | ||
| 13705 | int moduleConfigApplyConfig(list *module_configs, const char **err, const char **err_arg_name) { | ||
| 13706 | if (!listLength(module_configs)) return 1; | ||
| 13707 | listIter li; | ||
| 13708 | listNode *ln; | ||
| 13709 | ModuleConfig *module_config; | ||
| 13710 | |||
| 13711 | listRewind(module_configs, &li); | ||
| 13712 | while ((ln = listNext(&li))) { | ||
| 13713 | module_config = listNodeValue(ln); | ||
| 13714 | /* We know apply_fn is not NULL so skip the check */ | ||
| 13715 | if (!moduleConfigApplyInternal(module_config, err)) { | ||
| 13716 | if (err_arg_name) *err_arg_name = module_config->name; | ||
| 13717 | return 0; | ||
| 13718 | } | ||
| 13719 | } | ||
| 13720 | return 1; | ||
| 13721 | } | ||
| 13722 | |||
| 13723 | /* -------------------------------------------------------------------------- | ||
| 13724 | * ## Module Configurations API | ||
| 13725 | * -------------------------------------------------------------------------- */ | ||
| 13726 | |||
| 13727 | /* Resolve config name and create a module config object */ | ||
| 13728 | ModuleConfig *createModuleConfig(const char *name, RedisModuleConfigApplyFunc apply_fn, | ||
| 13729 | void *privdata, RedisModule *module, unsigned int flags) | ||
| 13730 | { | ||
| 13731 | sds cname, alias = NULL; | ||
| 13732 | |||
| 13733 | /* Determine the configuration name: | ||
| 13734 | * - If the unprefixed flag is set, the "<MODULE-NAME>." prefix is omitted. | ||
| 13735 | * - An optional alias can be specified using "<NAME>|<ALIAS>". | ||
| 13736 | * | ||
| 13737 | * Examples: | ||
| 13738 | * - Unprefixed: "bf.initial_size" or "bf-initial-size|bf.initial_size". | ||
| 13739 | * - Prefixed: "initial_size" becomes "<MODULE-NAME>.initial_size". | ||
| 13740 | */ | ||
| 13741 | if (flags & REDISMODULE_CONFIG_UNPREFIXED) { | ||
| 13742 | const char *delim = strchr(name, '|'); | ||
| 13743 | cname = sdsnew(name); | ||
| 13744 | if (delim) { /* Handle "<NAME>|<ALIAS>" format */ | ||
| 13745 | sdssubstr(cname, 0, delim - name); | ||
| 13746 | alias = sdsnew(delim + 1); | ||
| 13747 | } | ||
| 13748 | } else { | ||
| 13749 | /* Add the module name prefix */ | ||
| 13750 | cname = sdscatfmt(sdsempty(), "%s.%s", module->name, name); | ||
| 13751 | } | ||
| 13752 | |||
| 13753 | ModuleConfig *new_config = zmalloc(sizeof(ModuleConfig)); | ||
| 13754 | new_config->unprefixedFlag = flags & REDISMODULE_CONFIG_UNPREFIXED; | ||
| 13755 | new_config->name = cname; | ||
| 13756 | new_config->alias = alias; | ||
| 13757 | new_config->apply_fn = apply_fn; | ||
| 13758 | new_config->privdata = privdata; | ||
| 13759 | new_config->module = module; | ||
| 13760 | return new_config; | ||
| 13761 | } | ||
| 13762 | |||
| 13763 | /* Verify the configuration name and check for duplicates. | ||
| 13764 | * | ||
| 13765 | * - If the configuration is flagged as unprefixed, it checks for duplicate | ||
| 13766 | * names and optional aliases in the format <NAME>|<ALIAS>. | ||
| 13767 | * - If the configuration is prefixed, it ensures the name is unique with | ||
| 13768 | * the module name prepended (<MODULE_NAME>.<NAME>). | ||
| 13769 | */ | ||
| 13770 | int moduleConfigValidityCheck(RedisModule *module, const char *name, unsigned int flags, configType type) { | ||
| 13771 | if (!module->onload) { | ||
| 13772 | errno = EBUSY; | ||
| 13773 | return REDISMODULE_ERR; | ||
| 13774 | } | ||
| 13775 | if (moduleVerifyConfigFlags(flags, type)) { | ||
| 13776 | errno = EINVAL; | ||
| 13777 | return REDISMODULE_ERR; | ||
| 13778 | } | ||
| 13779 | |||
| 13780 | int isdup = 0; | ||
| 13781 | if (flags & REDISMODULE_CONFIG_UNPREFIXED) { | ||
| 13782 | const char *delim = NULL; /* Pointer to the '|' delimiter in <NAME>|<ALIAS> */ | ||
| 13783 | if (moduleVerifyUnprefixedName(name, &delim)){ | ||
| 13784 | errno = EINVAL; | ||
| 13785 | return REDISMODULE_ERR; | ||
| 13786 | } | ||
| 13787 | |||
| 13788 | if (delim) { | ||
| 13789 | /* Temporary split the "<NAME>|<ALIAS>" for the check */ | ||
| 13790 | int count; | ||
| 13791 | sds *ar = sdssplitlen(name, strlen(name), "|", 1, &count); | ||
| 13792 | serverAssert(count == 2); /* Already validated */ | ||
| 13793 | isdup = configExists(ar[0]) || | ||
| 13794 | configExists(ar[1]) || | ||
| 13795 | (sdscmp(ar[0], ar[1]) == 0); | ||
| 13796 | sdsfreesplitres(ar, count); | ||
| 13797 | } else { | ||
| 13798 | sds _name = sdsnew(name); | ||
| 13799 | isdup = configExists(_name); | ||
| 13800 | sdsfree(_name); | ||
| 13801 | } | ||
| 13802 | } else { | ||
| 13803 | if (moduleVerifyResourceName(name)) { | ||
| 13804 | errno = EINVAL; | ||
| 13805 | return REDISMODULE_ERR; | ||
| 13806 | } | ||
| 13807 | |||
| 13808 | sds fullname = sdscatfmt(sdsempty(), "%s.%s", module->name, name); | ||
| 13809 | isdup = configExists(fullname); | ||
| 13810 | sdsfree(fullname); | ||
| 13811 | } | ||
| 13812 | |||
| 13813 | if (isdup) { | ||
| 13814 | serverLog(LL_WARNING, "Configuration by the name: %s already registered", name); | ||
| 13815 | errno = EALREADY; | ||
| 13816 | return REDISMODULE_ERR; | ||
| 13817 | } | ||
| 13818 | return REDISMODULE_OK; | ||
| 13819 | } | ||
| 13820 | |||
| 13821 | unsigned int maskModuleConfigFlags(unsigned int flags) { | ||
| 13822 | unsigned int new_flags = 0; | ||
| 13823 | if (flags & REDISMODULE_CONFIG_DEFAULT) new_flags |= MODIFIABLE_CONFIG; | ||
| 13824 | if (flags & REDISMODULE_CONFIG_IMMUTABLE) new_flags |= IMMUTABLE_CONFIG; | ||
| 13825 | if (flags & REDISMODULE_CONFIG_HIDDEN) new_flags |= HIDDEN_CONFIG; | ||
| 13826 | if (flags & REDISMODULE_CONFIG_PROTECTED) new_flags |= PROTECTED_CONFIG; | ||
| 13827 | if (flags & REDISMODULE_CONFIG_DENY_LOADING) new_flags |= DENY_LOADING_CONFIG; | ||
| 13828 | return new_flags; | ||
| 13829 | } | ||
| 13830 | |||
| 13831 | unsigned int maskModuleNumericConfigFlags(unsigned int flags) { | ||
| 13832 | unsigned int new_flags = 0; | ||
| 13833 | if (flags & REDISMODULE_CONFIG_MEMORY) new_flags |= MEMORY_CONFIG; | ||
| 13834 | return new_flags; | ||
| 13835 | } | ||
| 13836 | |||
| 13837 | unsigned int maskModuleEnumConfigFlags(unsigned int flags) { | ||
| 13838 | unsigned int new_flags = 0; | ||
| 13839 | if (flags & REDISMODULE_CONFIG_BITFLAGS) new_flags |= MULTI_ARG_CONFIG; | ||
| 13840 | return new_flags; | ||
| 13841 | } | ||
| 13842 | |||
| 13843 | /* Create a string config that Redis users can interact with via the Redis config file, | ||
| 13844 | * `CONFIG SET`, `CONFIG GET`, and `CONFIG REWRITE` commands. | ||
| 13845 | * | ||
| 13846 | * The actual config value is owned by the module, and the `getfn`, `setfn` and optional | ||
| 13847 | * `applyfn` callbacks that are provided to Redis in order to access or manipulate the | ||
| 13848 | * value. The `getfn` callback retrieves the value from the module, while the `setfn` | ||
| 13849 | * callback provides a value to be stored into the module config. | ||
| 13850 | * The optional `applyfn` callback is called after a `CONFIG SET` command modified one or | ||
| 13851 | * more configs using the `setfn` callback and can be used to atomically apply a config | ||
| 13852 | * after several configs were changed together. | ||
| 13853 | * If there are multiple configs with `applyfn` callbacks set by a single `CONFIG SET` | ||
| 13854 | * command, they will be deduplicated if their `applyfn` function and `privdata` pointers | ||
| 13855 | * are identical, and the callback will only be run once. | ||
| 13856 | * Both the `setfn` and `applyfn` can return an error if the provided value is invalid or | ||
| 13857 | * cannot be used. | ||
| 13858 | * The config also declares a type for the value that is validated by Redis and | ||
| 13859 | * provided to the module. The config system provides the following types: | ||
| 13860 | * | ||
| 13861 | * * Redis String: Binary safe string data. | ||
| 13862 | * * Enum: One of a finite number of string tokens, provided during registration. | ||
| 13863 | * * Numeric: 64 bit signed integer, which also supports min and max values. | ||
| 13864 | * * Bool: Yes or no value. | ||
| 13865 | * | ||
| 13866 | * The `setfn` callback is expected to return REDISMODULE_OK when the value is successfully | ||
| 13867 | * applied. It can also return REDISMODULE_ERR if the value can't be applied, and the | ||
| 13868 | * *err pointer can be set with a RedisModuleString error message to provide to the client. | ||
| 13869 | * This RedisModuleString will be freed by redis after returning from the set callback. | ||
| 13870 | * | ||
| 13871 | * All configs are registered with a name, a type, a default value, private data that is made | ||
| 13872 | * available in the callbacks, as well as several flags that modify the behavior of the config. | ||
| 13873 | * The name must only contain alphanumeric characters or dashes. The supported flags are: | ||
| 13874 | * | ||
| 13875 | * * REDISMODULE_CONFIG_DEFAULT: The default flags for a config. This creates a config that can be modified after startup. | ||
| 13876 | * * REDISMODULE_CONFIG_IMMUTABLE: This config can only be provided loading time. | ||
| 13877 | * * REDISMODULE_CONFIG_SENSITIVE: The value stored in this config is redacted from all logging. | ||
| 13878 | * * REDISMODULE_CONFIG_HIDDEN: The name is hidden from `CONFIG GET` with pattern matching. | ||
| 13879 | * * REDISMODULE_CONFIG_PROTECTED: This config will be only be modifiable based off the value of enable-protected-configs. | ||
| 13880 | * * REDISMODULE_CONFIG_DENY_LOADING: This config is not modifiable while the server is loading data. | ||
| 13881 | * * REDISMODULE_CONFIG_MEMORY: For numeric configs, this config will convert data unit notations into their byte equivalent. | ||
| 13882 | * * REDISMODULE_CONFIG_BITFLAGS: For enum configs, this config will allow multiple entries to be combined as bit flags. | ||
| 13883 | * | ||
| 13884 | * Default values are used on startup to set the value if it is not provided via the config file | ||
| 13885 | * or command line. Default values are also used to compare to on a config rewrite. | ||
| 13886 | * | ||
| 13887 | * Notes: | ||
| 13888 | * | ||
| 13889 | * 1. On string config sets that the string passed to the set callback will be freed after execution and the module must retain it. | ||
| 13890 | * 2. On string config gets the string will not be consumed and will be valid after execution. | ||
| 13891 | * | ||
| 13892 | * Example implementation: | ||
| 13893 | * | ||
| 13894 | * RedisModuleString *strval; | ||
| 13895 | * int adjustable = 1; | ||
| 13896 | * RedisModuleString *getStringConfigCommand(const char *name, void *privdata) { | ||
| 13897 | * return strval; | ||
| 13898 | * } | ||
| 13899 | * | ||
| 13900 | * int setStringConfigCommand(const char *name, RedisModuleString *new, void *privdata, RedisModuleString **err) { | ||
| 13901 | * if (adjustable) { | ||
| 13902 | * RedisModule_Free(strval); | ||
| 13903 | * RedisModule_RetainString(NULL, new); | ||
| 13904 | * strval = new; | ||
| 13905 | * return REDISMODULE_OK; | ||
| 13906 | * } | ||
| 13907 | * *err = RedisModule_CreateString(NULL, "Not adjustable.", 15); | ||
| 13908 | * return REDISMODULE_ERR; | ||
| 13909 | * } | ||
| 13910 | * ... | ||
| 13911 | * RedisModule_RegisterStringConfig(ctx, "string", NULL, REDISMODULE_CONFIG_DEFAULT, getStringConfigCommand, setStringConfigCommand, NULL, NULL); | ||
| 13912 | * | ||
| 13913 | * If the registration fails, REDISMODULE_ERR is returned and one of the following | ||
| 13914 | * errno is set: | ||
| 13915 | * * EBUSY: Registering the Config outside of RedisModule_OnLoad. | ||
| 13916 | * * EINVAL: The provided flags are invalid for the registration or the name of the config contains invalid characters. | ||
| 13917 | * * EALREADY: The provided configuration name is already used. */ | ||
| 13918 | int RM_RegisterStringConfig(RedisModuleCtx *ctx, const char *name, const char *default_val, unsigned int flags, RedisModuleConfigGetStringFunc getfn, RedisModuleConfigSetStringFunc setfn, RedisModuleConfigApplyFunc applyfn, void *privdata) { | ||
| 13919 | RedisModule *module = ctx->module; | ||
| 13920 | if (moduleConfigValidityCheck(module, name, flags, NUMERIC_CONFIG)) { | ||
| 13921 | return REDISMODULE_ERR; | ||
| 13922 | } | ||
| 13923 | |||
| 13924 | ModuleConfig *mc = createModuleConfig(name, applyfn, privdata, module, flags); | ||
| 13925 | mc->get_fn.get_string = getfn; | ||
| 13926 | mc->set_fn.set_string = setfn; | ||
| 13927 | listAddNodeTail(module->module_configs, mc); | ||
| 13928 | unsigned int cflags = maskModuleConfigFlags(flags); | ||
| 13929 | addModuleStringConfig(sdsdup(mc->name), (mc->alias) ? sdsdup(mc->alias) : NULL, | ||
| 13930 | cflags, mc, default_val ? sdsnew(default_val) : NULL); | ||
| 13931 | return REDISMODULE_OK; | ||
| 13932 | } | ||
| 13933 | |||
| 13934 | /* Create a bool config that server clients can interact with via the | ||
| 13935 | * `CONFIG SET`, `CONFIG GET`, and `CONFIG REWRITE` commands. See | ||
| 13936 | * RedisModule_RegisterStringConfig for detailed information about configs. */ | ||
| 13937 | int RM_RegisterBoolConfig(RedisModuleCtx *ctx, const char *name, int default_val, unsigned int flags, RedisModuleConfigGetBoolFunc getfn, RedisModuleConfigSetBoolFunc setfn, RedisModuleConfigApplyFunc applyfn, void *privdata) { | ||
| 13938 | RedisModule *module = ctx->module; | ||
| 13939 | if (moduleConfigValidityCheck(module, name, flags, BOOL_CONFIG)) { | ||
| 13940 | return REDISMODULE_ERR; | ||
| 13941 | } | ||
| 13942 | ModuleConfig *mc = createModuleConfig(name, applyfn, privdata, module, flags); | ||
| 13943 | mc->get_fn.get_bool = getfn; | ||
| 13944 | mc->set_fn.set_bool = setfn; | ||
| 13945 | listAddNodeTail(module->module_configs, mc); | ||
| 13946 | unsigned int cflags = maskModuleConfigFlags(flags); | ||
| 13947 | addModuleBoolConfig(sdsdup(mc->name), (mc->alias) ? sdsdup(mc->alias) : NULL, | ||
| 13948 | cflags, mc, default_val); | ||
| 13949 | return REDISMODULE_OK; | ||
| 13950 | } | ||
| 13951 | |||
| 13952 | /* | ||
| 13953 | * Create an enum config that server clients can interact with via the | ||
| 13954 | * `CONFIG SET`, `CONFIG GET`, and `CONFIG REWRITE` commands. | ||
| 13955 | * Enum configs are a set of string tokens to corresponding integer values, where | ||
| 13956 | * the string value is exposed to Redis clients but the value passed Redis and the | ||
| 13957 | * module is the integer value. These values are defined in enum_values, an array | ||
| 13958 | * of null-terminated c strings, and int_vals, an array of enum values who has an | ||
| 13959 | * index partner in enum_values. | ||
| 13960 | * Example Implementation: | ||
| 13961 | * const char *enum_vals[3] = {"first", "second", "third"}; | ||
| 13962 | * const int int_vals[3] = {0, 2, 4}; | ||
| 13963 | * int enum_val = 0; | ||
| 13964 | * | ||
| 13965 | * int getEnumConfigCommand(const char *name, void *privdata) { | ||
| 13966 | * return enum_val; | ||
| 13967 | * } | ||
| 13968 | * | ||
| 13969 | * int setEnumConfigCommand(const char *name, int val, void *privdata, const char **err) { | ||
| 13970 | * enum_val = val; | ||
| 13971 | * return REDISMODULE_OK; | ||
| 13972 | * } | ||
| 13973 | * ... | ||
| 13974 | * RedisModule_RegisterEnumConfig(ctx, "enum", 0, REDISMODULE_CONFIG_DEFAULT, enum_vals, int_vals, 3, getEnumConfigCommand, setEnumConfigCommand, NULL, NULL); | ||
| 13975 | * | ||
| 13976 | * Note that you can use REDISMODULE_CONFIG_BITFLAGS so that multiple enum string | ||
| 13977 | * can be combined into one integer as bit flags, in which case you may want to | ||
| 13978 | * sort your enums so that the preferred combinations are present first. | ||
| 13979 | * | ||
| 13980 | * See RedisModule_RegisterStringConfig for detailed general information about configs. */ | ||
| 13981 | int RM_RegisterEnumConfig(RedisModuleCtx *ctx, const char *name, int default_val, unsigned int flags, const char **enum_values, const int *int_values, int num_enum_vals, RedisModuleConfigGetEnumFunc getfn, RedisModuleConfigSetEnumFunc setfn, RedisModuleConfigApplyFunc applyfn, void *privdata) { | ||
| 13982 | RedisModule *module = ctx->module; | ||
| 13983 | if (moduleConfigValidityCheck(module, name, flags, ENUM_CONFIG)) { | ||
| 13984 | return REDISMODULE_ERR; | ||
| 13985 | } | ||
| 13986 | ModuleConfig *mc = createModuleConfig(name, applyfn, privdata, module, flags); | ||
| 13987 | mc->get_fn.get_enum = getfn; | ||
| 13988 | mc->set_fn.set_enum = setfn; | ||
| 13989 | configEnum *enum_vals = zmalloc((num_enum_vals + 1) * sizeof(configEnum)); | ||
| 13990 | for (int i = 0; i < num_enum_vals; i++) { | ||
| 13991 | enum_vals[i].name = zstrdup(enum_values[i]); | ||
| 13992 | enum_vals[i].val = int_values[i]; | ||
| 13993 | } | ||
| 13994 | enum_vals[num_enum_vals].name = NULL; | ||
| 13995 | enum_vals[num_enum_vals].val = 0; | ||
| 13996 | listAddNodeTail(module->module_configs, mc); | ||
| 13997 | |||
| 13998 | unsigned int cflags = maskModuleConfigFlags(flags) | maskModuleEnumConfigFlags(flags); | ||
| 13999 | addModuleEnumConfig(sdsdup(mc->name), (mc->alias) ? sdsdup(mc->alias) : NULL, | ||
| 14000 | cflags, mc, default_val, enum_vals, num_enum_vals); | ||
| 14001 | return REDISMODULE_OK; | ||
| 14002 | } | ||
| 14003 | |||
| 14004 | /* | ||
| 14005 | * Create an integer config that server clients can interact with via the | ||
| 14006 | * `CONFIG SET`, `CONFIG GET`, and `CONFIG REWRITE` commands. See | ||
| 14007 | * RedisModule_RegisterStringConfig for detailed information about configs. */ | ||
| 14008 | int RM_RegisterNumericConfig(RedisModuleCtx *ctx, const char *name, long long default_val, unsigned int flags, long long min, long long max, RedisModuleConfigGetNumericFunc getfn, RedisModuleConfigSetNumericFunc setfn, RedisModuleConfigApplyFunc applyfn, void *privdata) { | ||
| 14009 | RedisModule *module = ctx->module; | ||
| 14010 | if (moduleConfigValidityCheck(module, name, flags, NUMERIC_CONFIG)) { | ||
| 14011 | return REDISMODULE_ERR; | ||
| 14012 | } | ||
| 14013 | ModuleConfig *mc = createModuleConfig(name, applyfn, privdata, module, flags); | ||
| 14014 | mc->get_fn.get_numeric = getfn; | ||
| 14015 | mc->set_fn.set_numeric = setfn; | ||
| 14016 | listAddNodeTail(module->module_configs, mc); | ||
| 14017 | unsigned int numeric_flags = maskModuleNumericConfigFlags(flags); | ||
| 14018 | |||
| 14019 | unsigned int cflags = maskModuleConfigFlags(flags); | ||
| 14020 | addModuleNumericConfig(sdsdup(mc->name), (mc->alias) ? sdsdup(mc->alias) : NULL, | ||
| 14021 | cflags, mc, default_val, numeric_flags, min, max); | ||
| 14022 | return REDISMODULE_OK; | ||
| 14023 | } | ||
| 14024 | |||
| 14025 | /* Applies all default configurations for the parameters the module registered. | ||
| 14026 | * Only call this function if the module would like to make changes to the | ||
| 14027 | * configuration values before the actual values are applied by RedisModule_LoadConfigs. | ||
| 14028 | * Otherwise it's sufficient to call RedisModule_LoadConfigs, it should already set the default values if needed. | ||
| 14029 | * This makes it possible to distinguish between default values and user provided values and apply other changes between setting the defaults and the user values. | ||
| 14030 | * This will return REDISMODULE_ERR if it is called: | ||
| 14031 | * 1. outside RedisModule_OnLoad | ||
| 14032 | * 2. more than once | ||
| 14033 | * 3. after the RedisModule_LoadConfigs call */ | ||
| 14034 | int RM_LoadDefaultConfigs(RedisModuleCtx *ctx) { | ||
| 14035 | if (!ctx || !ctx->module || !ctx->module->onload || ctx->module->configs_initialized) { | ||
| 14036 | return REDISMODULE_ERR; | ||
| 14037 | } | ||
| 14038 | RedisModule *module = ctx->module; | ||
| 14039 | /* Load default configs of the module */ | ||
| 14040 | return loadModuleDefaultConfigs(module); | ||
| 14041 | } | ||
| 14042 | |||
| 14043 | /* Applies all pending configurations on the module load. This should be called | ||
| 14044 | * after all of the configurations have been registered for the module inside of RedisModule_OnLoad. | ||
| 14045 | * This will return REDISMODULE_ERR if it is called outside RedisModule_OnLoad. | ||
| 14046 | * This API needs to be called when configurations are provided in either `MODULE LOADEX` | ||
| 14047 | * or provided as startup arguments. */ | ||
| 14048 | int RM_LoadConfigs(RedisModuleCtx *ctx) { | ||
| 14049 | if (!ctx || !ctx->module || !ctx->module->onload) { | ||
| 14050 | return REDISMODULE_ERR; | ||
| 14051 | } | ||
| 14052 | RedisModule *module = ctx->module; | ||
| 14053 | /* Load configs from conf file or arguments from loadex */ | ||
| 14054 | return loadModuleConfigs(module); | ||
| 14055 | } | ||
| 14056 | |||
| 14057 | /* -------------------------------------------------------------------------- | ||
| 14058 | * ## RDB load/save API | ||
| 14059 | * -------------------------------------------------------------------------- */ | ||
| 14060 | |||
| 14061 | #define REDISMODULE_RDB_STREAM_FILE 1 | ||
| 14062 | |||
| 14063 | typedef struct RedisModuleRdbStream { | ||
| 14064 | int type; | ||
| 14065 | |||
| 14066 | union { | ||
| 14067 | char *filename; | ||
| 14068 | } data; | ||
| 14069 | } RedisModuleRdbStream; | ||
| 14070 | |||
| 14071 | /* Create a stream object to save/load RDB to/from a file. | ||
| 14072 | * | ||
| 14073 | * This function returns a pointer to RedisModuleRdbStream which is owned | ||
| 14074 | * by the caller. It requires a call to RM_RdbStreamFree() to free | ||
| 14075 | * the object. */ | ||
| 14076 | RedisModuleRdbStream *RM_RdbStreamCreateFromFile(const char *filename) { | ||
| 14077 | RedisModuleRdbStream *stream = zmalloc(sizeof(*stream)); | ||
| 14078 | stream->type = REDISMODULE_RDB_STREAM_FILE; | ||
| 14079 | stream->data.filename = zstrdup(filename); | ||
| 14080 | return stream; | ||
| 14081 | } | ||
| 14082 | |||
| 14083 | /* Release an RDB stream object. */ | ||
| 14084 | void RM_RdbStreamFree(RedisModuleRdbStream *stream) { | ||
| 14085 | switch (stream->type) { | ||
| 14086 | case REDISMODULE_RDB_STREAM_FILE: | ||
| 14087 | zfree(stream->data.filename); | ||
| 14088 | break; | ||
| 14089 | default: | ||
| 14090 | serverAssert(0); | ||
| 14091 | break; | ||
| 14092 | } | ||
| 14093 | zfree(stream); | ||
| 14094 | } | ||
| 14095 | |||
| 14096 | /* Load RDB file from the `stream`. Dataset will be cleared first and then RDB | ||
| 14097 | * file will be loaded. | ||
| 14098 | * | ||
| 14099 | * `flags` must be zero. This parameter is for future use. | ||
| 14100 | * | ||
| 14101 | * On success REDISMODULE_OK is returned, otherwise REDISMODULE_ERR is returned | ||
| 14102 | * and errno is set accordingly. | ||
| 14103 | * | ||
| 14104 | * Example: | ||
| 14105 | * | ||
| 14106 | * RedisModuleRdbStream *s = RedisModule_RdbStreamCreateFromFile("exp.rdb"); | ||
| 14107 | * RedisModule_RdbLoad(ctx, s, 0); | ||
| 14108 | * RedisModule_RdbStreamFree(s); | ||
| 14109 | */ | ||
| 14110 | int RM_RdbLoad(RedisModuleCtx *ctx, RedisModuleRdbStream *stream, int flags) { | ||
| 14111 | UNUSED(ctx); | ||
| 14112 | |||
| 14113 | if (!stream || flags != 0) { | ||
| 14114 | errno = EINVAL; | ||
| 14115 | return REDISMODULE_ERR; | ||
| 14116 | } | ||
| 14117 | |||
| 14118 | /* Not allowed on replicas. */ | ||
| 14119 | if (server.masterhost != NULL) { | ||
| 14120 | errno = ENOTSUP; | ||
| 14121 | return REDISMODULE_ERR; | ||
| 14122 | } | ||
| 14123 | |||
| 14124 | /* Drop replicas if exist. */ | ||
| 14125 | disconnectSlaves(); | ||
| 14126 | freeReplicationBacklog(); | ||
| 14127 | |||
| 14128 | if (server.aof_state != AOF_OFF) stopAppendOnly(); | ||
| 14129 | |||
| 14130 | /* Kill existing RDB fork as it is saving outdated data. Also killing it | ||
| 14131 | * will prevent COW memory issue. */ | ||
| 14132 | if (server.child_type == CHILD_TYPE_RDB) killRDBChild(); | ||
| 14133 | |||
| 14134 | emptyData(-1,EMPTYDB_NO_FLAGS,NULL); | ||
| 14135 | |||
| 14136 | /* rdbLoad() can go back to the networking and process network events. If | ||
| 14137 | * RM_RdbLoad() is called inside a command callback, we don't want to | ||
| 14138 | * process the current client. Otherwise, we may free the client or try to | ||
| 14139 | * process next message while we are already in the command callback. */ | ||
| 14140 | if (server.current_client) protectClient(server.current_client); | ||
| 14141 | |||
| 14142 | serverAssert(stream->type == REDISMODULE_RDB_STREAM_FILE); | ||
| 14143 | int ret = rdbLoad(stream->data.filename,NULL,RDBFLAGS_NONE); | ||
| 14144 | |||
| 14145 | if (server.current_client) unprotectClient(server.current_client); | ||
| 14146 | if (server.aof_enabled) startAppendOnlyWithRetry(); | ||
| 14147 | |||
| 14148 | if (ret != RDB_OK) { | ||
| 14149 | errno = (ret == RDB_NOT_EXIST) ? ENOENT : EIO; | ||
| 14150 | return REDISMODULE_ERR; | ||
| 14151 | } | ||
| 14152 | |||
| 14153 | errno = 0; | ||
| 14154 | return REDISMODULE_OK; | ||
| 14155 | } | ||
| 14156 | |||
| 14157 | /* Save dataset to the RDB stream. | ||
| 14158 | * | ||
| 14159 | * `flags` must be zero. This parameter is for future use. | ||
| 14160 | * | ||
| 14161 | * On success REDISMODULE_OK is returned, otherwise REDISMODULE_ERR is returned | ||
| 14162 | * and errno is set accordingly. | ||
| 14163 | * | ||
| 14164 | * Example: | ||
| 14165 | * | ||
| 14166 | * RedisModuleRdbStream *s = RedisModule_RdbStreamCreateFromFile("exp.rdb"); | ||
| 14167 | * RedisModule_RdbSave(ctx, s, 0); | ||
| 14168 | * RedisModule_RdbStreamFree(s); | ||
| 14169 | */ | ||
| 14170 | int RM_RdbSave(RedisModuleCtx *ctx, RedisModuleRdbStream *stream, int flags) { | ||
| 14171 | UNUSED(ctx); | ||
| 14172 | |||
| 14173 | if (!stream || flags != 0) { | ||
| 14174 | errno = EINVAL; | ||
| 14175 | return REDISMODULE_ERR; | ||
| 14176 | } | ||
| 14177 | |||
| 14178 | serverAssert(stream->type == REDISMODULE_RDB_STREAM_FILE); | ||
| 14179 | |||
| 14180 | if (rdbSaveToFile(stream->data.filename) != C_OK) { | ||
| 14181 | return REDISMODULE_ERR; | ||
| 14182 | } | ||
| 14183 | |||
| 14184 | errno = 0; | ||
| 14185 | return REDISMODULE_OK; | ||
| 14186 | } | ||
| 14187 | |||
| 14188 | /* Returns the internal secret of the cluster. | ||
| 14189 | * Should be used to authenticate as an internal connection to a node in the | ||
| 14190 | * cluster, and by that gain the permissions to execute internal commands. | ||
| 14191 | */ | ||
| 14192 | const char* RM_GetInternalSecret(RedisModuleCtx *ctx, size_t *len) { | ||
| 14193 | UNUSED(ctx); | ||
| 14194 | serverAssert(len != NULL); | ||
| 14195 | const char *secret = clusterGetSecret(len); | ||
| 14196 | return secret; | ||
| 14197 | } | ||
| 14198 | |||
| 14199 | |||
| 14200 | /* -------------------------------------------------------------------------- | ||
| 14201 | * ## Config access API | ||
| 14202 | * -------------------------------------------------------------------------- */ | ||
| 14203 | |||
| 14204 | /* Get an iterator to all configs. | ||
| 14205 | * Optional `ctx` can be provided if use of auto-memory is desired. | ||
| 14206 | * Optional `pattern` can be provided to filter configs by name. If `pattern` is | ||
| 14207 | * NULL all configs will be returned. | ||
| 14208 | * | ||
| 14209 | * The returned iterator can be used to iterate over all configs using | ||
| 14210 | * RedisModule_ConfigIteratorNext(). | ||
| 14211 | * | ||
| 14212 | * Example usage: | ||
| 14213 | * ``` | ||
| 14214 | * // Below is same as RedisModule_ConfigIteratorCreate(ctx, NULL) | ||
| 14215 | * RedisModuleConfigIterator *iter = RedisModule_ConfigIteratorCreate(ctx, "*"); | ||
| 14216 | * const char *config_name = NULL; | ||
| 14217 | * while ((config_name = RedisModule_ConfigIteratorNext(iter)) != NULL) { | ||
| 14218 | * RedisModuleString *value = NULL; | ||
| 14219 | * if (RedisModule_ConfigGet(ctx, config_name, &value) == REDISMODULE_OK) { | ||
| 14220 | * // Do something with `value`... | ||
| 14221 | * RedisModule_FreeString(ctx, value); | ||
| 14222 | * } | ||
| 14223 | * } | ||
| 14224 | * RedisModule_ConfigIteratorRelease(ctx, iter); | ||
| 14225 | * | ||
| 14226 | * // Or optionally one can check the type to get the config value directly | ||
| 14227 | * // via the appropriate API in case performance is of consideration | ||
| 14228 | * iter = RedisModule_ConfigIteratorCreate(ctx, "*"); | ||
| 14229 | * while ((config_name = RedisModule_ConfigIteratorNext(iter)) != NULL) { | ||
| 14230 | * RedisModuleConfigType type = RedisModule_ConfigGetType(config_name); | ||
| 14231 | * if (type == REDISMODULE_CONFIG_TYPE_STRING) { | ||
| 14232 | * RedisModuleString *value; | ||
| 14233 | * RedisModule_ConfigGet(ctx, config_name, &value); | ||
| 14234 | * // Do something with `value`... | ||
| 14235 | * RedisModule_FreeString(ctx, value); | ||
| 14236 | * } if (type == REDISMODULE_CONFIG_TYPE_NUMERIC) { | ||
| 14237 | * long long value; | ||
| 14238 | * RedisModule_ConfigGetNumeric(ctx, config_name, &value); | ||
| 14239 | * // Do something with `value`... | ||
| 14240 | * } else if (type == REDISMODULE_CONFIG_TYPE_BOOL) { | ||
| 14241 | * int value; | ||
| 14242 | * RedisModule_ConfigGetBool(ctx, config_name, &value); | ||
| 14243 | * // Do something with `value`... | ||
| 14244 | * } else if (type == REDISMODULE_CONFIG_TYPE_ENUM) { | ||
| 14245 | * RedisModuleString *value; | ||
| 14246 | * RedisModule_ConfigGetEnum(ctx, config_name, &value); | ||
| 14247 | * // Do something with `value`... | ||
| 14248 | * RedisModule_Free(value); | ||
| 14249 | * } | ||
| 14250 | * } | ||
| 14251 | * RedisModule_ConfigIteratorRelease(ctx, iter); | ||
| 14252 | * ``` | ||
| 14253 | * | ||
| 14254 | * Returns a pointer to RedisModuleConfigIterator. Unless auto-memory is enabled | ||
| 14255 | * the caller is responsible for freeing the iterator using | ||
| 14256 | * RedisModule_ConfigIteratorRelease(). */ | ||
| 14257 | RedisModuleConfigIterator *RM_ConfigIteratorCreate(RedisModuleCtx *ctx, const char *pattern) { | ||
| 14258 | RedisModuleConfigIterator *iter = RM_Alloc(sizeof(*iter)); | ||
| 14259 | |||
| 14260 | iter->di = moduleGetConfigIterator(); | ||
| 14261 | if (pattern != NULL) { | ||
| 14262 | iter->pattern = sdsnew(pattern); | ||
| 14263 | iter->is_glob = (strpbrk(pattern, "*?[") != NULL); | ||
| 14264 | } else { | ||
| 14265 | iter->pattern = NULL; | ||
| 14266 | iter->is_glob = false; | ||
| 14267 | } | ||
| 14268 | |||
| 14269 | if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_CONFIG, iter); | ||
| 14270 | return iter; | ||
| 14271 | } | ||
| 14272 | |||
| 14273 | /* Release the iterator returned by RedisModule_ConfigIteratorCreate(). If auto-memory | ||
| 14274 | * is enabled and manual release is needed one must pass the same RedisModuleCtx | ||
| 14275 | * that was used to create the iterator. */ | ||
| 14276 | void RM_ConfigIteratorRelease(RedisModuleCtx *ctx, RedisModuleConfigIterator *iter) { | ||
| 14277 | if (ctx != NULL) autoMemoryFreed(ctx,REDISMODULE_AM_CONFIG,iter); | ||
| 14278 | if (iter->di) dictReleaseIterator(iter->di); | ||
| 14279 | sdsfree(iter->pattern); | ||
| 14280 | RM_Free(iter); | ||
| 14281 | } | ||
| 14282 | |||
| 14283 | static RedisModuleConfigType convertToRedisModuleConfigType(configType type) { | ||
| 14284 | switch (type) { | ||
| 14285 | case BOOL_CONFIG: | ||
| 14286 | return REDISMODULE_CONFIG_TYPE_BOOL; | ||
| 14287 | case NUMERIC_CONFIG: | ||
| 14288 | return REDISMODULE_CONFIG_TYPE_NUMERIC; | ||
| 14289 | case STRING_CONFIG: | ||
| 14290 | case SDS_CONFIG: | ||
| 14291 | case SPECIAL_CONFIG: | ||
| 14292 | return REDISMODULE_CONFIG_TYPE_STRING; | ||
| 14293 | case ENUM_CONFIG: | ||
| 14294 | return REDISMODULE_CONFIG_TYPE_ENUM; | ||
| 14295 | default: | ||
| 14296 | serverAssert(0); | ||
| 14297 | break; | ||
| 14298 | } | ||
| 14299 | } | ||
| 14300 | |||
| 14301 | /* Get the type of a config as RedisModuleConfigType. One may use this in order | ||
| 14302 | * to get or set the values of the config with the appropriate function if the | ||
| 14303 | * generic RedisModule_ConfigGet and RedisModule_ConfigSet APIs are performing | ||
| 14304 | * poorly. | ||
| 14305 | * | ||
| 14306 | * Intended usage of this function is when iteration over the configs is | ||
| 14307 | * performed. See RedisModule_ConfigIteratorNext() for example usage. If setting | ||
| 14308 | * or getting individual configs one can check the config type by hand in | ||
| 14309 | * redis.conf (or via other sources if config is added by a module) and use the | ||
| 14310 | * appropriate function without the need to call this function. | ||
| 14311 | * | ||
| 14312 | * Explanation of config types: | ||
| 14313 | * - REDISMODULE_CONFIG_TYPE_BOOL: Config is a boolean. One can use RedisModule_Config(Get/Set)Bool | ||
| 14314 | * - REDISMODULE_CONFIG_TYPE_NUMERIC: Config is a numeric value. One can use RedisModule_Config(Get/Set)Numeric | ||
| 14315 | * - REDISMODULE_CONFIG_TYPE_STRING: Config is a string. One can use the generic RedisModule_Config(Get/Set) | ||
| 14316 | * - REDISMODULE_CONFIG_TYPE_ENUM: Config is an enum. One can use RedisModule_Config(Get/Set)Enum | ||
| 14317 | * | ||
| 14318 | * If a config with the given name exists `res` is populated with its type, else | ||
| 14319 | * REDISMODULE_ERR is returned. */ | ||
| 14320 | int RM_ConfigGetType(const char *name, RedisModuleConfigType *res) { | ||
| 14321 | sds config_name = sdsnew(name); | ||
| 14322 | configType type; | ||
| 14323 | int ret = moduleGetConfigType(config_name, &type); | ||
| 14324 | sdsfree(config_name); | ||
| 14325 | |||
| 14326 | if (!ret) | ||
| 14327 | return REDISMODULE_ERR; | ||
| 14328 | |||
| 14329 | *res = convertToRedisModuleConfigType(type); | ||
| 14330 | return REDISMODULE_OK; | ||
| 14331 | } | ||
| 14332 | |||
| 14333 | /* Go to the next element of the config iterator. | ||
| 14334 | * | ||
| 14335 | * Returns the name of the next config, or NULL if there are no more configs. | ||
| 14336 | * Returned string is non-owning and thus should not be freed. | ||
| 14337 | * If a pattern was provided when creating the iterator, only configs matching | ||
| 14338 | * the pattern will be returned. | ||
| 14339 | * | ||
| 14340 | * See RedisModule_ConfigIteratorCreate() for example usage. */ | ||
| 14341 | const char *RM_ConfigIteratorNext(RedisModuleConfigIterator *iter) { | ||
| 14342 | return moduleConfigIteratorNext(&iter->di, iter->pattern, iter->is_glob, NULL); | ||
| 14343 | } | ||
| 14344 | |||
| 14345 | /* Get the value of a config as a string. This function can be used to get the | ||
| 14346 | * value of any config, regardless of its type. | ||
| 14347 | * | ||
| 14348 | * The string is allocated by the module and must be freed by the caller unless | ||
| 14349 | * auto memory is enabled. | ||
| 14350 | * | ||
| 14351 | * If the config does not exist, REDISMODULE_ERR is returned, else REDISMODULE_OK | ||
| 14352 | * is returned and `res` is populated with the value. */ | ||
| 14353 | int RM_ConfigGet(RedisModuleCtx *ctx, const char *name, RedisModuleString **res) { | ||
| 14354 | sds config_name = sdsnew(name); | ||
| 14355 | sds res_sds = NULL; | ||
| 14356 | int ret = moduleGetStringConfig(config_name, &res_sds); | ||
| 14357 | sdsfree(config_name); | ||
| 14358 | if (ret) | ||
| 14359 | *res = RM_CreateString(ctx, res_sds, sdslen(res_sds)); | ||
| 14360 | sdsfree(res_sds); | ||
| 14361 | return ret ? REDISMODULE_OK : REDISMODULE_ERR; | ||
| 14362 | } | ||
| 14363 | |||
| 14364 | /* Get the value of a bool config. | ||
| 14365 | * | ||
| 14366 | * If the config does not exist or is not a bool config, REDISMODULE_ERR is | ||
| 14367 | * returned, else REDISMODULE_OK is returned and `res` is populated with the | ||
| 14368 | * value. */ | ||
| 14369 | int RM_ConfigGetBool(RedisModuleCtx *ctx, const char *name, int *res) { | ||
| 14370 | UNUSED(ctx); | ||
| 14371 | sds config_name = sdsnew(name); | ||
| 14372 | int ret = moduleGetBoolConfig(config_name, res); | ||
| 14373 | sdsfree(config_name); | ||
| 14374 | return ret ? REDISMODULE_OK : REDISMODULE_ERR; | ||
| 14375 | } | ||
| 14376 | |||
| 14377 | /* Get the value of an enum config. | ||
| 14378 | * | ||
| 14379 | * If the config does not exist or is not an enum config, REDISMODULE_ERR is | ||
| 14380 | * returned, else REDISMODULE_OK is returned and `res` is populated with the value. | ||
| 14381 | * If the config has multiple arguments they are returned as a space-separated | ||
| 14382 | * string. */ | ||
| 14383 | int RM_ConfigGetEnum(RedisModuleCtx *ctx, const char *name, RedisModuleString **res) { | ||
| 14384 | sds config_name = sdsnew(name); | ||
| 14385 | sds res_sds = NULL; | ||
| 14386 | int ret = moduleGetEnumConfig(config_name, &res_sds); | ||
| 14387 | sdsfree(config_name); | ||
| 14388 | if (ret) | ||
| 14389 | *res = RM_CreateString(ctx, res_sds, sdslen(res_sds)); | ||
| 14390 | sdsfree(res_sds); | ||
| 14391 | return ret ? REDISMODULE_OK : REDISMODULE_ERR; | ||
| 14392 | } | ||
| 14393 | |||
| 14394 | /* Get the value of a numeric config. | ||
| 14395 | * | ||
| 14396 | * If the config does not exist or is not a numeric config, REDISMODULE_ERR is | ||
| 14397 | * returned, else REDISMODULE_OK is returned and `res` is populated with the | ||
| 14398 | * value. */ | ||
| 14399 | int RM_ConfigGetNumeric(RedisModuleCtx *ctx, const char *name, long long *res) { | ||
| 14400 | UNUSED(ctx); | ||
| 14401 | sds config_name = sdsnew(name); | ||
| 14402 | int ret = moduleGetNumericConfig(config_name, res); | ||
| 14403 | sdsfree(config_name); | ||
| 14404 | return ret ? REDISMODULE_OK : REDISMODULE_ERR; | ||
| 14405 | } | ||
| 14406 | |||
| 14407 | /* Set the value of a config. | ||
| 14408 | * | ||
| 14409 | * This function can be used to set the value of any config, regardless of its | ||
| 14410 | * type. If the config is multi-argument, the value must be a space-separated | ||
| 14411 | * string. | ||
| 14412 | * | ||
| 14413 | * If the value failed to be set REDISMODULE_ERR will be returned and if `err` | ||
| 14414 | * is not NULL, it will be populated with an error message. */ | ||
| 14415 | int RM_ConfigSet(RedisModuleCtx *ctx, const char *name, RedisModuleString *value, RedisModuleString **err) { | ||
| 14416 | sds config_name = sdsnew(name); | ||
| 14417 | const char *cerr = NULL; | ||
| 14418 | const char *val = RM_StringPtrLen(value, NULL); | ||
| 14419 | int res = moduleSetStringConfig(ctx->client, config_name, val, &cerr); | ||
| 14420 | sdsfree(config_name); | ||
| 14421 | if (err && cerr) | ||
| 14422 | *err = RM_CreateString(ctx, cerr, strlen(cerr)); | ||
| 14423 | return (res == 0 ? REDISMODULE_ERR : REDISMODULE_OK); | ||
| 14424 | } | ||
| 14425 | |||
| 14426 | /* Set the value of a bool config. | ||
| 14427 | * | ||
| 14428 | * See RedisModule_ConfigSet for return value. */ | ||
| 14429 | int RM_ConfigSetBool(RedisModuleCtx *ctx, const char *name, int value, RedisModuleString **err) { | ||
| 14430 | const char *cerr = NULL; | ||
| 14431 | sds config_name = sdsnew(name); | ||
| 14432 | int res = moduleSetBoolConfig(ctx->client, config_name, value, &cerr); | ||
| 14433 | sdsfree(config_name); | ||
| 14434 | if (err && cerr) | ||
| 14435 | *err = RM_CreateString(ctx, cerr, strlen(cerr)); | ||
| 14436 | return (res == 0 ? REDISMODULE_ERR : REDISMODULE_OK); | ||
| 14437 | } | ||
| 14438 | |||
| 14439 | /* Set the value of an enum config. | ||
| 14440 | * | ||
| 14441 | * If the config is multi-argument the value parameter must be a space-separated | ||
| 14442 | * string. | ||
| 14443 | * | ||
| 14444 | * See RedisModule_ConfigSet for return value. */ | ||
| 14445 | int RM_ConfigSetEnum(RedisModuleCtx *ctx, const char *name, RedisModuleString *value, RedisModuleString **err) { | ||
| 14446 | sds config_name = sdsnew(name); | ||
| 14447 | const char *cerr = NULL; | ||
| 14448 | |||
| 14449 | size_t len; | ||
| 14450 | const char *val = RM_StringPtrLen(value, &len); | ||
| 14451 | sds sds_val = sdsnewlen(val, len); | ||
| 14452 | |||
| 14453 | int vals_cnt = 0; | ||
| 14454 | sds *vals = sdssplitlen(val, sdslen(sds_val), " ", 1, &vals_cnt); | ||
| 14455 | |||
| 14456 | int res = moduleSetEnumConfig(ctx->client, config_name, vals, vals_cnt, &cerr); | ||
| 14457 | |||
| 14458 | sdsfreesplitres(vals, vals_cnt); | ||
| 14459 | sdsfree(sds_val); | ||
| 14460 | sdsfree(config_name); | ||
| 14461 | if (err && cerr) | ||
| 14462 | *err = RM_CreateString(ctx, cerr, strlen(cerr)); | ||
| 14463 | return (res == 0 ? REDISMODULE_ERR : REDISMODULE_OK); | ||
| 14464 | } | ||
| 14465 | |||
| 14466 | /* Set the value of a numeric config. | ||
| 14467 | * If the value passed is meant to be a percentage, it should be passed as a | ||
| 14468 | * negative value. | ||
| 14469 | * For unsigned configs pass the value and cast to (long long) - internal type | ||
| 14470 | * checks will handle it. | ||
| 14471 | * | ||
| 14472 | * See RedisModule_ConfigSet for return value. */ | ||
| 14473 | int RM_ConfigSetNumeric(RedisModuleCtx *ctx, const char *name, long long value, RedisModuleString **err) { | ||
| 14474 | sds config_name = sdsnew(name); | ||
| 14475 | const char *cerr = NULL; | ||
| 14476 | int res = moduleSetNumericConfig(ctx->client, config_name, value, &cerr); | ||
| 14477 | sdsfree(config_name); | ||
| 14478 | if (err && cerr) | ||
| 14479 | *err = RM_CreateString(ctx, cerr, strlen(cerr)); | ||
| 14480 | return (res == 0 ? REDISMODULE_ERR : REDISMODULE_OK); | ||
| 14481 | } | ||
| 14482 | |||
| 14483 | /* Redis MODULE command. | ||
| 14484 | * | ||
| 14485 | * MODULE LIST | ||
| 14486 | * MODULE LOAD <path> [args...] | ||
| 14487 | * MODULE LOADEX <path> [[CONFIG NAME VALUE] [CONFIG NAME VALUE]] [ARGS ...] | ||
| 14488 | * MODULE UNLOAD <name> | ||
| 14489 | */ | ||
| 14490 | void moduleCommand(client *c) { | ||
| 14491 | char *subcmd = c->argv[1]->ptr; | ||
| 14492 | |||
| 14493 | if (c->argc == 2 && !strcasecmp(subcmd,"help")) { | ||
| 14494 | const char *help[] = { | ||
| 14495 | "LIST", | ||
| 14496 | " Return a list of loaded modules.", | ||
| 14497 | "LOAD <path> [<arg> ...]", | ||
| 14498 | " Load a module library from <path>, passing to it any optional arguments.", | ||
| 14499 | "LOADEX <path> [[CONFIG NAME VALUE] [CONFIG NAME VALUE]] [ARGS ...]", | ||
| 14500 | " Load a module library from <path>, while passing it module configurations and optional arguments.", | ||
| 14501 | "UNLOAD <name>", | ||
| 14502 | " Unload a module.", | ||
| 14503 | NULL | ||
| 14504 | }; | ||
| 14505 | addReplyHelp(c, help); | ||
| 14506 | } else if (!strcasecmp(subcmd,"load") && c->argc >= 3) { | ||
| 14507 | robj **argv = NULL; | ||
| 14508 | int argc = 0; | ||
| 14509 | |||
| 14510 | if (c->argc > 3) { | ||
| 14511 | argc = c->argc - 3; | ||
| 14512 | argv = &c->argv[3]; | ||
| 14513 | } | ||
| 14514 | |||
| 14515 | if (moduleLoad(c->argv[2]->ptr,(void **)argv,argc, 0) == C_OK) | ||
| 14516 | addReply(c,shared.ok); | ||
| 14517 | else | ||
| 14518 | addReplyError(c, | ||
| 14519 | "Error loading the extension. Please check the server logs."); | ||
| 14520 | } else if (!strcasecmp(subcmd,"loadex") && c->argc >= 3) { | ||
| 14521 | robj **argv = NULL; | ||
| 14522 | int argc = 0; | ||
| 14523 | |||
| 14524 | if (c->argc > 3) { | ||
| 14525 | argc = c->argc - 3; | ||
| 14526 | argv = &c->argv[3]; | ||
| 14527 | } | ||
| 14528 | /* If this is a loadex command we want to populate server.module_configs_queue with | ||
| 14529 | * sds NAME VALUE pairs. We also want to increment argv to just after ARGS, if supplied. */ | ||
| 14530 | if (parseLoadexArguments((RedisModuleString ***) &argv, &argc) == REDISMODULE_OK && | ||
| 14531 | moduleLoad(c->argv[2]->ptr, (void **)argv, argc, 1) == C_OK) | ||
| 14532 | addReply(c,shared.ok); | ||
| 14533 | else { | ||
| 14534 | dictEmpty(server.module_configs_queue, NULL); | ||
| 14535 | addReplyError(c, | ||
| 14536 | "Error loading the extension. Please check the server logs."); | ||
| 14537 | } | ||
| 14538 | |||
| 14539 | } else if (!strcasecmp(subcmd,"unload") && c->argc == 3) { | ||
| 14540 | const char *errmsg = NULL; | ||
| 14541 | if (moduleUnload(c->argv[2]->ptr, &errmsg, 0) == C_OK) | ||
| 14542 | addReply(c,shared.ok); | ||
| 14543 | else { | ||
| 14544 | if (errmsg == NULL) errmsg = "operation not possible."; | ||
| 14545 | addReplyErrorFormat(c, "Error unloading module: %s", errmsg); | ||
| 14546 | serverLog(LL_WARNING, "Error unloading module %s: %s", (sds) c->argv[2]->ptr, errmsg); | ||
| 14547 | } | ||
| 14548 | } else if (!strcasecmp(subcmd,"list") && c->argc == 2) { | ||
| 14549 | addReplyLoadedModules(c); | ||
| 14550 | } else { | ||
| 14551 | addReplySubcommandSyntaxError(c); | ||
| 14552 | return; | ||
| 14553 | } | ||
| 14554 | } | ||
| 14555 | |||
| 14556 | /* Return the number of registered modules. */ | ||
| 14557 | size_t moduleCount(void) { | ||
| 14558 | return dictSize(modules); | ||
| 14559 | } | ||
| 14560 | |||
| 14561 | /* -------------------------------------------------------------------------- | ||
| 14562 | * ## Key eviction API | ||
| 14563 | * -------------------------------------------------------------------------- */ | ||
| 14564 | |||
| 14565 | /* Set the key last access time for LRU based eviction. not relevant if the | ||
| 14566 | * servers's maxmemory policy is LFU based. Value is idle time in milliseconds. | ||
| 14567 | * returns REDISMODULE_OK if the LRU was updated, REDISMODULE_ERR otherwise. */ | ||
| 14568 | int RM_SetLRU(RedisModuleKey *key, mstime_t lru_idle) { | ||
| 14569 | if (!key->kv) | ||
| 14570 | return REDISMODULE_ERR; | ||
| 14571 | if (objectSetLRUOrLFU(key->kv, -1, lru_idle, lru_idle>=0 ? LRU_CLOCK() : 0, 1)) | ||
| 14572 | return REDISMODULE_OK; | ||
| 14573 | return REDISMODULE_ERR; | ||
| 14574 | } | ||
| 14575 | |||
| 14576 | /* Gets the key last access time. | ||
| 14577 | * Value is idletime in milliseconds or -1 if the server's eviction policy is | ||
| 14578 | * LFU based. | ||
| 14579 | * returns REDISMODULE_OK if when key is valid. */ | ||
| 14580 | int RM_GetLRU(RedisModuleKey *key, mstime_t *lru_idle) { | ||
| 14581 | *lru_idle = -1; | ||
| 14582 | if (!key->kv) | ||
| 14583 | return REDISMODULE_ERR; | ||
| 14584 | if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) | ||
| 14585 | return REDISMODULE_OK; | ||
| 14586 | *lru_idle = estimateObjectIdleTime(key->kv); | ||
| 14587 | return REDISMODULE_OK; | ||
| 14588 | } | ||
| 14589 | |||
| 14590 | /* Set the key access frequency. only relevant if the server's maxmemory policy | ||
| 14591 | * is LFU based. | ||
| 14592 | * The frequency is a logarithmic counter that provides an indication of | ||
| 14593 | * the access frequencyonly (must be <= 255). | ||
| 14594 | * returns REDISMODULE_OK if the LFU was updated, REDISMODULE_ERR otherwise. */ | ||
| 14595 | int RM_SetLFU(RedisModuleKey *key, long long lfu_freq) { | ||
| 14596 | if (!key->kv) | ||
| 14597 | return REDISMODULE_ERR; | ||
| 14598 | if (objectSetLRUOrLFU(key->kv, lfu_freq, -1, 0, 1)) | ||
| 14599 | return REDISMODULE_OK; | ||
| 14600 | return REDISMODULE_ERR; | ||
| 14601 | } | ||
| 14602 | |||
| 14603 | /* Gets the key access frequency or -1 if the server's eviction policy is not | ||
| 14604 | * LFU based. | ||
| 14605 | * returns REDISMODULE_OK if when key is valid. */ | ||
| 14606 | int RM_GetLFU(RedisModuleKey *key, long long *lfu_freq) { | ||
| 14607 | *lfu_freq = -1; | ||
| 14608 | if (!key->kv) | ||
| 14609 | return REDISMODULE_ERR; | ||
| 14610 | if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) | ||
| 14611 | *lfu_freq = LFUDecrAndReturn(key->kv); | ||
| 14612 | return REDISMODULE_OK; | ||
| 14613 | } | ||
| 14614 | |||
| 14615 | /* -------------------------------------------------------------------------- | ||
| 14616 | * ## Miscellaneous APIs | ||
| 14617 | * -------------------------------------------------------------------------- */ | ||
| 14618 | |||
| 14619 | /** | ||
| 14620 | * Returns the full module options flags mask, using the return value | ||
| 14621 | * the module can check if a certain set of module options are supported | ||
| 14622 | * by the redis server version in use. | ||
| 14623 | * Example: | ||
| 14624 | * | ||
| 14625 | * int supportedFlags = RM_GetModuleOptionsAll(); | ||
| 14626 | * if (supportedFlags & REDISMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS) { | ||
| 14627 | * // REDISMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS is supported | ||
| 14628 | * } else{ | ||
| 14629 | * // REDISMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS is not supported | ||
| 14630 | * } | ||
| 14631 | */ | ||
| 14632 | int RM_GetModuleOptionsAll(void) { | ||
| 14633 | return _REDISMODULE_OPTIONS_FLAGS_NEXT - 1; | ||
| 14634 | } | ||
| 14635 | |||
| 14636 | /** | ||
| 14637 | * Returns the full ContextFlags mask, using the return value | ||
| 14638 | * the module can check if a certain set of flags are supported | ||
| 14639 | * by the redis server version in use. | ||
| 14640 | * Example: | ||
| 14641 | * | ||
| 14642 | * int supportedFlags = RM_GetContextFlagsAll(); | ||
| 14643 | * if (supportedFlags & REDISMODULE_CTX_FLAGS_MULTI) { | ||
| 14644 | * // REDISMODULE_CTX_FLAGS_MULTI is supported | ||
| 14645 | * } else{ | ||
| 14646 | * // REDISMODULE_CTX_FLAGS_MULTI is not supported | ||
| 14647 | * } | ||
| 14648 | */ | ||
| 14649 | int RM_GetContextFlagsAll(void) { | ||
| 14650 | return _REDISMODULE_CTX_FLAGS_NEXT - 1; | ||
| 14651 | } | ||
| 14652 | |||
| 14653 | /** | ||
| 14654 | * Returns the full KeyspaceNotification mask, using the return value | ||
| 14655 | * the module can check if a certain set of flags are supported | ||
| 14656 | * by the redis server version in use. | ||
| 14657 | * Example: | ||
| 14658 | * | ||
| 14659 | * int supportedFlags = RM_GetKeyspaceNotificationFlagsAll(); | ||
| 14660 | * if (supportedFlags & REDISMODULE_NOTIFY_LOADED) { | ||
| 14661 | * // REDISMODULE_NOTIFY_LOADED is supported | ||
| 14662 | * } else{ | ||
| 14663 | * // REDISMODULE_NOTIFY_LOADED is not supported | ||
| 14664 | * } | ||
| 14665 | */ | ||
| 14666 | int RM_GetKeyspaceNotificationFlagsAll(void) { | ||
| 14667 | return _REDISMODULE_NOTIFY_NEXT - 1; | ||
| 14668 | } | ||
| 14669 | |||
| 14670 | /** | ||
| 14671 | * Return the redis version in format of 0x00MMmmpp. | ||
| 14672 | * Example for 6.0.7 the return value will be 0x00060007. | ||
| 14673 | */ | ||
| 14674 | int RM_GetServerVersion(void) { | ||
| 14675 | return REDIS_VERSION_NUM; | ||
| 14676 | } | ||
| 14677 | |||
| 14678 | /** | ||
| 14679 | * Return the current redis-server runtime value of REDISMODULE_TYPE_METHOD_VERSION. | ||
| 14680 | * You can use that when calling RM_CreateDataType to know which fields of | ||
| 14681 | * RedisModuleTypeMethods are gonna be supported and which will be ignored. | ||
| 14682 | */ | ||
| 14683 | int RM_GetTypeMethodVersion(void) { | ||
| 14684 | return REDISMODULE_TYPE_METHOD_VERSION; | ||
| 14685 | } | ||
| 14686 | |||
| 14687 | /* Replace the value assigned to a module type. | ||
| 14688 | * | ||
| 14689 | * The key must be open for writing, have an existing value, and have a moduleType | ||
| 14690 | * that matches the one specified by the caller. | ||
| 14691 | * | ||
| 14692 | * Unlike RM_ModuleTypeSetValue() which will free the old value, this function | ||
| 14693 | * simply swaps the old value with the new value. | ||
| 14694 | * | ||
| 14695 | * The function returns REDISMODULE_OK on success, REDISMODULE_ERR on errors | ||
| 14696 | * such as: | ||
| 14697 | * | ||
| 14698 | * 1. Key is not opened for writing. | ||
| 14699 | * 2. Key is not a module data type key. | ||
| 14700 | * 3. Key is a module datatype other than 'mt'. | ||
| 14701 | * | ||
| 14702 | * If old_value is non-NULL, the old value is returned by reference. | ||
| 14703 | */ | ||
| 14704 | int RM_ModuleTypeReplaceValue(RedisModuleKey *key, moduleType *mt, void *new_value, void **old_value) { | ||
| 14705 | if (!(key->mode & REDISMODULE_WRITE) || key->iter) | ||
| 14706 | return REDISMODULE_ERR; | ||
| 14707 | if (!key->kv || key->kv->type != OBJ_MODULE) | ||
| 14708 | return REDISMODULE_ERR; | ||
| 14709 | |||
| 14710 | moduleValue *mv = key->kv->ptr; | ||
| 14711 | if (mv->type != mt) | ||
| 14712 | return REDISMODULE_ERR; | ||
| 14713 | |||
| 14714 | if (old_value) | ||
| 14715 | *old_value = mv->value; | ||
| 14716 | mv->value = new_value; | ||
| 14717 | |||
| 14718 | return REDISMODULE_OK; | ||
| 14719 | } | ||
| 14720 | |||
| 14721 | /* For a specified command, parse its arguments and return an array that | ||
| 14722 | * contains the indexes of all key name arguments. This function is | ||
| 14723 | * essentially a more efficient way to do `COMMAND GETKEYS`. | ||
| 14724 | * | ||
| 14725 | * The out_flags argument is optional, and can be set to NULL. | ||
| 14726 | * When provided it is filled with REDISMODULE_CMD_KEY_ flags in matching | ||
| 14727 | * indexes with the key indexes of the returned array. | ||
| 14728 | * | ||
| 14729 | * A NULL return value indicates the specified command has no keys, or | ||
| 14730 | * an error condition. Error conditions are indicated by setting errno | ||
| 14731 | * as follows: | ||
| 14732 | * | ||
| 14733 | * * ENOENT: Specified command does not exist. | ||
| 14734 | * * EINVAL: Invalid command arity specified. | ||
| 14735 | * | ||
| 14736 | * NOTE: The returned array is not a Redis Module object so it does not | ||
| 14737 | * get automatically freed even when auto-memory is used. The caller | ||
| 14738 | * must explicitly call RM_Free() to free it, same as the out_flags pointer if | ||
| 14739 | * used. | ||
| 14740 | */ | ||
| 14741 | int *RM_GetCommandKeysWithFlags(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, int *num_keys, int **out_flags) { | ||
| 14742 | UNUSED(ctx); | ||
| 14743 | struct redisCommand *cmd; | ||
| 14744 | int *res = NULL; | ||
| 14745 | |||
| 14746 | /* Find command */ | ||
| 14747 | if ((cmd = lookupCommand(argv,argc)) == NULL) { | ||
| 14748 | errno = ENOENT; | ||
| 14749 | return NULL; | ||
| 14750 | } | ||
| 14751 | |||
| 14752 | /* Bail out if command has no keys */ | ||
| 14753 | if (!doesCommandHaveKeys(cmd)) { | ||
| 14754 | errno = 0; | ||
| 14755 | return NULL; | ||
| 14756 | } | ||
| 14757 | |||
| 14758 | if ((cmd->arity > 0 && cmd->arity != argc) || (argc < -cmd->arity)) { | ||
| 14759 | errno = EINVAL; | ||
| 14760 | return NULL; | ||
| 14761 | } | ||
| 14762 | |||
| 14763 | getKeysResult result = GETKEYS_RESULT_INIT; | ||
| 14764 | getKeysFromCommand(cmd, argv, argc, &result); | ||
| 14765 | |||
| 14766 | *num_keys = result.numkeys; | ||
| 14767 | if (!result.numkeys) { | ||
| 14768 | errno = 0; | ||
| 14769 | getKeysFreeResult(&result); | ||
| 14770 | return NULL; | ||
| 14771 | } | ||
| 14772 | |||
| 14773 | /* The return value here expects an array of key positions */ | ||
| 14774 | unsigned long int size = sizeof(int) * result.numkeys; | ||
| 14775 | res = zmalloc(size); | ||
| 14776 | if (out_flags) | ||
| 14777 | *out_flags = zmalloc(size); | ||
| 14778 | for (int i = 0; i < result.numkeys; i++) { | ||
| 14779 | res[i] = result.keys[i].pos; | ||
| 14780 | if (out_flags) | ||
| 14781 | (*out_flags)[i] = moduleConvertKeySpecsFlags(result.keys[i].flags, 0); | ||
| 14782 | } | ||
| 14783 | |||
| 14784 | getKeysFreeResult(&result); | ||
| 14785 | return res; | ||
| 14786 | } | ||
| 14787 | |||
| 14788 | /* Identical to RM_GetCommandKeysWithFlags when flags are not needed. */ | ||
| 14789 | int *RM_GetCommandKeys(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, int *num_keys) { | ||
| 14790 | return RM_GetCommandKeysWithFlags(ctx, argv, argc, num_keys, NULL); | ||
| 14791 | } | ||
| 14792 | |||
| 14793 | /* Return the name of the command currently running */ | ||
| 14794 | const char *RM_GetCurrentCommandName(RedisModuleCtx *ctx) { | ||
| 14795 | if (!ctx || !ctx->client || !ctx->client->cmd) | ||
| 14796 | return NULL; | ||
| 14797 | |||
| 14798 | return (const char*)ctx->client->cmd->fullname; | ||
| 14799 | } | ||
| 14800 | |||
| 14801 | /* -------------------------------------------------------------------------- | ||
| 14802 | * ## Defrag API | ||
| 14803 | * -------------------------------------------------------------------------- */ | ||
| 14804 | |||
| 14805 | /* Register a defrag callback for global data, i.e. anything that the module | ||
| 14806 | * may allocate that is not tied to a specific data type. | ||
| 14807 | */ | ||
| 14808 | int RM_RegisterDefragFunc(RedisModuleCtx *ctx, RedisModuleDefragFunc cb) { | ||
| 14809 | ctx->module->defrag_cb = cb; | ||
| 14810 | return REDISMODULE_OK; | ||
| 14811 | } | ||
| 14812 | |||
| 14813 | /* Register a defrag callback for global data, i.e. anything that the module | ||
| 14814 | * may allocate that is not tied to a specific data type. | ||
| 14815 | * This is a more advanced version of RM_RegisterDefragFunc, in that it takes | ||
| 14816 | * a callbacks that has a return value, and can use RM_DefragShouldStop | ||
| 14817 | * in and indicate that it should be called again later, or is it done (returned 0). | ||
| 14818 | */ | ||
| 14819 | int RM_RegisterDefragFunc2(RedisModuleCtx *ctx, RedisModuleDefragFunc2 cb) { | ||
| 14820 | ctx->module->defrag_cb_2 = cb; | ||
| 14821 | return REDISMODULE_OK; | ||
| 14822 | } | ||
| 14823 | |||
| 14824 | /* Register a defrag callbacks that will be called when defrag operation starts and ends. | ||
| 14825 | * | ||
| 14826 | * The callbacks are the same as `RM_RegisterDefragFunc` but the user | ||
| 14827 | * can also assume the callbacks are called when the defrag operation starts and ends. */ | ||
| 14828 | int RM_RegisterDefragCallbacks(RedisModuleCtx *ctx, RedisModuleDefragFunc start, RedisModuleDefragFunc end) { | ||
| 14829 | ctx->module->defrag_start_cb = start; | ||
| 14830 | ctx->module->defrag_end_cb = end; | ||
| 14831 | return REDISMODULE_OK; | ||
| 14832 | } | ||
| 14833 | |||
| 14834 | /* When the data type defrag callback iterates complex structures, this | ||
| 14835 | * function should be called periodically. A zero (false) return | ||
| 14836 | * indicates the callback may continue its work. A non-zero value (true) | ||
| 14837 | * indicates it should stop. | ||
| 14838 | * | ||
| 14839 | * When stopped, the callback may use RM_DefragCursorSet() to store its | ||
| 14840 | * position so it can later use RM_DefragCursorGet() to resume defragging. | ||
| 14841 | * | ||
| 14842 | * When stopped and more work is left to be done, the callback should | ||
| 14843 | * return 1. Otherwise, it should return 0. | ||
| 14844 | */ | ||
| 14845 | int RM_DefragShouldStop(RedisModuleDefragCtx *ctx) { | ||
| 14846 | if (ctx->stopping) /* Return immediately if already stopping */ | ||
| 14847 | return 1; | ||
| 14848 | if (!ctx->endtime) /* Return if no time limit set */ | ||
| 14849 | return 0; | ||
| 14850 | |||
| 14851 | /* We use certain thresholds to avoid excessive system calls. | ||
| 14852 | * Time checks are only performed when any threshold is reached, | ||
| 14853 | * which means we might slightly exceed the expected end time. */ | ||
| 14854 | if (server.stat_active_defrag_hits - ctx->last_stop_check_hits >= 512 || | ||
| 14855 | server.stat_active_defrag_misses - ctx->last_stop_check_misses >= 1024) | ||
| 14856 | { | ||
| 14857 | if (ctx->endtime <= getMonotonicUs()) { | ||
| 14858 | ctx->stopping = 1; | ||
| 14859 | return 1; | ||
| 14860 | } | ||
| 14861 | ctx->last_stop_check_hits = server.stat_active_defrag_hits; | ||
| 14862 | ctx->last_stop_check_misses = server.stat_active_defrag_misses; | ||
| 14863 | } | ||
| 14864 | return 0; | ||
| 14865 | } | ||
| 14866 | |||
| 14867 | /* Store an arbitrary cursor value for future re-use. | ||
| 14868 | * | ||
| 14869 | * This should only be called if RM_DefragShouldStop() has returned a non-zero | ||
| 14870 | * value and the defrag callback is about to exit without fully iterating its | ||
| 14871 | * data type. | ||
| 14872 | * | ||
| 14873 | * This behavior is reserved to cases where late defrag is performed. Late | ||
| 14874 | * defrag is selected for keys that implement the `free_effort` callback and | ||
| 14875 | * return a `free_effort` value that is larger than the defrag | ||
| 14876 | * 'active-defrag-max-scan-fields' configuration directive. | ||
| 14877 | * | ||
| 14878 | * Smaller keys, keys that do not implement `free_effort` or the global | ||
| 14879 | * defrag callback are not called in late-defrag mode. In those cases, a | ||
| 14880 | * call to this function will return REDISMODULE_ERR. | ||
| 14881 | * | ||
| 14882 | * The cursor may be used by the module to represent some progress into the | ||
| 14883 | * module's data type. Modules may also store additional cursor-related | ||
| 14884 | * information locally and use the cursor as a flag that indicates when | ||
| 14885 | * traversal of a new key begins. This is possible because the API makes | ||
| 14886 | * a guarantee that concurrent defragmentation of multiple keys will | ||
| 14887 | * not be performed. | ||
| 14888 | */ | ||
| 14889 | int RM_DefragCursorSet(RedisModuleDefragCtx *ctx, unsigned long cursor) { | ||
| 14890 | if (!ctx->cursor) | ||
| 14891 | return REDISMODULE_ERR; | ||
| 14892 | |||
| 14893 | *ctx->cursor = cursor; | ||
| 14894 | return REDISMODULE_OK; | ||
| 14895 | } | ||
| 14896 | |||
| 14897 | /* Fetch a cursor value that has been previously stored using RM_DefragCursorSet(). | ||
| 14898 | * | ||
| 14899 | * If not called for a late defrag operation, REDISMODULE_ERR will be returned and | ||
| 14900 | * the cursor should be ignored. See RM_DefragCursorSet() for more details on | ||
| 14901 | * defrag cursors. | ||
| 14902 | */ | ||
| 14903 | int RM_DefragCursorGet(RedisModuleDefragCtx *ctx, unsigned long *cursor) { | ||
| 14904 | if (!ctx->cursor) | ||
| 14905 | return REDISMODULE_ERR; | ||
| 14906 | |||
| 14907 | *cursor = *ctx->cursor; | ||
| 14908 | return REDISMODULE_OK; | ||
| 14909 | } | ||
| 14910 | |||
| 14911 | /* Defrag a memory allocation previously allocated by RM_Alloc, RM_Calloc, etc. | ||
| 14912 | * The defragmentation process involves allocating a new memory block and copying | ||
| 14913 | * the contents to it, like realloc(). | ||
| 14914 | * | ||
| 14915 | * If defragmentation was not necessary, NULL is returned and the operation has | ||
| 14916 | * no other effect. | ||
| 14917 | * | ||
| 14918 | * If a non-NULL value is returned, the caller should use the new pointer instead | ||
| 14919 | * of the old one and update any reference to the old pointer, which must not | ||
| 14920 | * be used again. | ||
| 14921 | */ | ||
| 14922 | void *RM_DefragAlloc(RedisModuleDefragCtx *ctx, void *ptr) { | ||
| 14923 | UNUSED(ctx); | ||
| 14924 | return activeDefragAlloc(ptr); | ||
| 14925 | } | ||
| 14926 | |||
| 14927 | /* Allocate memory for defrag purposes | ||
| 14928 | * | ||
| 14929 | * On the common cases user simply want to reallocate a pointer with a single | ||
| 14930 | * owner. For such usecase RM_DefragAlloc is enough. But on some usecases the user | ||
| 14931 | * might want to replace a pointer with multiple owners in different keys. | ||
| 14932 | * In such case, an in place replacement can not work because the other key still | ||
| 14933 | * keep a pointer to the old value. | ||
| 14934 | * | ||
| 14935 | * RM_DefragAllocRaw and RM_DefragFreeRaw allows to control when the memory | ||
| 14936 | * for defrag purposes will be allocated and when it will be freed, | ||
| 14937 | * allow to support more complex defrag usecases. */ | ||
| 14938 | void *RM_DefragAllocRaw(RedisModuleDefragCtx *ctx, size_t size) { | ||
| 14939 | UNUSED(ctx); | ||
| 14940 | return activeDefragAllocRaw(size); | ||
| 14941 | } | ||
| 14942 | |||
| 14943 | /* Free memory for defrag purposes | ||
| 14944 | * | ||
| 14945 | * See RM_DefragAllocRaw for more information. */ | ||
| 14946 | void RM_DefragFreeRaw(RedisModuleDefragCtx *ctx, void *ptr) { | ||
| 14947 | UNUSED(ctx); | ||
| 14948 | activeDefragFreeRaw(ptr); | ||
| 14949 | } | ||
| 14950 | |||
| 14951 | /* Defrag a RedisModuleString previously allocated by RM_Alloc, RM_Calloc, etc. | ||
| 14952 | * See RM_DefragAlloc() for more information on how the defragmentation process | ||
| 14953 | * works. | ||
| 14954 | * | ||
| 14955 | * NOTE: It is only possible to defrag strings that have a single reference. | ||
| 14956 | * Typically this means strings retained with RM_RetainString or RM_HoldString | ||
| 14957 | * may not be defragmentable. One exception is command argvs which, if retained | ||
| 14958 | * by the module, will end up with a single reference (because the reference | ||
| 14959 | * on the Redis side is dropped as soon as the command callback returns). | ||
| 14960 | */ | ||
| 14961 | RedisModuleString *RM_DefragRedisModuleString(RedisModuleDefragCtx *ctx, RedisModuleString *str) { | ||
| 14962 | UNUSED(ctx); | ||
| 14963 | return activeDefragStringOb(str); | ||
| 14964 | } | ||
| 14965 | |||
| 14966 | /* Defrag callback for radix tree iterator, called for each node, | ||
| 14967 | * used in order to defrag the nodes allocations. */ | ||
| 14968 | int moduleDefragRaxNode(raxNode **noderef, void *privdata) { | ||
| 14969 | UNUSED(privdata); | ||
| 14970 | raxNode *newnode = activeDefragAlloc(*noderef); | ||
| 14971 | if (newnode) { | ||
| 14972 | *noderef = newnode; | ||
| 14973 | return 1; | ||
| 14974 | } | ||
| 14975 | return 0; | ||
| 14976 | } | ||
| 14977 | |||
| 14978 | /* Defragment a Redis Module Dictionary by scanning its contents and calling a value | ||
| 14979 | * callback for each value. | ||
| 14980 | * | ||
| 14981 | * The callback gets the current value in the dict, and should update newptr to the new pointer, | ||
| 14982 | * if the value was re-allocated to a different address. The callback also gets the key name just as a reference. | ||
| 14983 | * The callback returns 0 when defrag is complete for this node, 1 when node needs more work. | ||
| 14984 | * | ||
| 14985 | * The API can work incrementally by accepting a seek position to continue from, and | ||
| 14986 | * returning the next position to seek to on the next call (or return NULL when the iteration is completed). | ||
| 14987 | * | ||
| 14988 | * This API returns a new dict if it was re-allocated to a new address (will only | ||
| 14989 | * be attempted when *seekTo is NULL on entry). | ||
| 14990 | */ | ||
| 14991 | RedisModuleDict *RM_DefragRedisModuleDict(RedisModuleDefragCtx *ctx, RedisModuleDict *dict, RedisModuleDefragDictValueCallback valueCB, RedisModuleString **seekTo) { | ||
| 14992 | RedisModuleDict *newdict = NULL; | ||
| 14993 | raxIterator ri; | ||
| 14994 | |||
| 14995 | if (*seekTo == NULL) { | ||
| 14996 | /* if last seek is NULL, we start new iteration */ | ||
| 14997 | rax* newrax = NULL; | ||
| 14998 | if ((newdict = activeDefragAlloc(dict))) | ||
| 14999 | dict = newdict; | ||
| 15000 | if ((newrax = activeDefragAlloc(dict->rax))) | ||
| 15001 | dict->rax = newrax; | ||
| 15002 | } | ||
| 15003 | |||
| 15004 | raxStart(&ri,dict->rax); | ||
| 15005 | if (*seekTo == NULL) { | ||
| 15006 | /* if last seek is NULL, we start new iteration */ | ||
| 15007 | moduleDefragRaxNode(&dict->rax->head, NULL); | ||
| 15008 | /* assign the iterator node callback before the seek, so that the | ||
| 15009 | * initial nodes that are processed till the first item are covered */ | ||
| 15010 | ri.node_cb = moduleDefragRaxNode; | ||
| 15011 | raxSeek(&ri,"^",NULL,0); | ||
| 15012 | } else { | ||
| 15013 | /* Seek to the static 'seekTo'. */ | ||
| 15014 | if (!raxSeek(&ri,">=", (*seekTo)->ptr, sdslen((*seekTo)->ptr))) { | ||
| 15015 | goto cleanup; | ||
| 15016 | } | ||
| 15017 | /* assign the iterator node callback after the seek, so that the | ||
| 15018 | * initial nodes that are processed till now aren't covered */ | ||
| 15019 | ri.node_cb = moduleDefragRaxNode; | ||
| 15020 | } | ||
| 15021 | |||
| 15022 | while (raxNext(&ri)) { | ||
| 15023 | int ret = 0; | ||
| 15024 | void *newdata = NULL; | ||
| 15025 | |||
| 15026 | if (valueCB) { | ||
| 15027 | ret = valueCB(ctx, ri.data, ri.key, ri.key_len, &newdata); | ||
| 15028 | if (newdata) | ||
| 15029 | raxSetData(ri.node, ri.data=newdata); | ||
| 15030 | } | ||
| 15031 | |||
| 15032 | /* Check if we need to interrupt defragmentation. | ||
| 15033 | * - For explicit interruption, use current position | ||
| 15034 | * - For timeout interruption, try to advance to next node if possible */ | ||
| 15035 | if (ret == 1 || RM_DefragShouldStop(ctx)) { | ||
| 15036 | if (ret == 0 && !raxNext(&ri)) goto cleanup; /* Last node and no more work needed. */ | ||
| 15037 | if (*seekTo) RM_FreeString(NULL, *seekTo); | ||
| 15038 | *seekTo = RM_CreateString(NULL, (const char *)ri.key, ri.key_len); | ||
| 15039 | raxStop(&ri); | ||
| 15040 | return newdict; | ||
| 15041 | } | ||
| 15042 | } | ||
| 15043 | cleanup: | ||
| 15044 | if (*seekTo) RM_FreeString(NULL, *seekTo); | ||
| 15045 | *seekTo = NULL; | ||
| 15046 | raxStop(&ri); | ||
| 15047 | return newdict; | ||
| 15048 | } | ||
| 15049 | |||
| 15050 | /* Perform a late defrag of a module datatype key. | ||
| 15051 | * | ||
| 15052 | * Returns a zero value (and initializes the cursor) if no more needs to be done, | ||
| 15053 | * or a non-zero value otherwise. | ||
| 15054 | */ | ||
| 15055 | int moduleLateDefrag(robj *key, robj *value, unsigned long *cursor, monotime endtime, int dbid) { | ||
| 15056 | moduleValue *mv = value->ptr; | ||
| 15057 | moduleType *mt = mv->type; | ||
| 15058 | |||
| 15059 | /* Interval shouldn't exceed 1 hour. */ | ||
| 15060 | serverAssert(!endtime || llabs((long long)endtime - (long long)getMonotonicUs()) < 60*60*1000*1000LL); | ||
| 15061 | |||
| 15062 | RedisModuleDefragCtx defrag_ctx = INIT_MODULE_DEFRAG_CTX(endtime, cursor, key, dbid); | ||
| 15063 | |||
| 15064 | /* Invoke callback. Note that the callback may be missing if the key has been | ||
| 15065 | * replaced with a different type since our last visit. | ||
| 15066 | */ | ||
| 15067 | int ret = 0; | ||
| 15068 | if (mt->defrag) | ||
| 15069 | ret = mt->defrag(&defrag_ctx, key, &mv->value); | ||
| 15070 | |||
| 15071 | if (!ret) { | ||
| 15072 | *cursor = 0; /* No more work to do */ | ||
| 15073 | return 0; | ||
| 15074 | } | ||
| 15075 | |||
| 15076 | return 1; | ||
| 15077 | } | ||
| 15078 | |||
| 15079 | /* Attempt to defrag a module data type value. Depending on complexity, | ||
| 15080 | * the operation may happen immediately or be scheduled for later. | ||
| 15081 | * | ||
| 15082 | * Returns 1 if the operation has been completed or 0 if it needs to | ||
| 15083 | * be scheduled for late defrag. | ||
| 15084 | */ | ||
| 15085 | int moduleDefragValue(robj *key, robj *value, int dbid) { | ||
| 15086 | moduleValue *mv = value->ptr; | ||
| 15087 | moduleType *mt = mv->type; | ||
| 15088 | |||
| 15089 | /* Try to defrag moduleValue itself regardless of whether or not | ||
| 15090 | * defrag callbacks are provided. | ||
| 15091 | */ | ||
| 15092 | moduleValue *newmv = activeDefragAlloc(mv); | ||
| 15093 | if (newmv) { | ||
| 15094 | value->ptr = mv = newmv; | ||
| 15095 | } | ||
| 15096 | |||
| 15097 | if (!mt->defrag) | ||
| 15098 | return 1; | ||
| 15099 | |||
| 15100 | /* Use free_effort to determine complexity of module value, and if | ||
| 15101 | * necessary schedule it for defragLater instead of quick immediate | ||
| 15102 | * defrag. | ||
| 15103 | */ | ||
| 15104 | size_t effort = moduleGetFreeEffort(key, value, dbid); | ||
| 15105 | if (!effort) | ||
| 15106 | effort = SIZE_MAX; | ||
| 15107 | if (effort > server.active_defrag_max_scan_fields) { | ||
| 15108 | return 0; /* Defrag later */ | ||
| 15109 | } | ||
| 15110 | |||
| 15111 | RedisModuleDefragCtx defrag_ctx = INIT_MODULE_DEFRAG_CTX(0, NULL, key, dbid); | ||
| 15112 | mt->defrag(&defrag_ctx, key, &mv->value); | ||
| 15113 | return 1; | ||
| 15114 | } | ||
| 15115 | |||
| 15116 | /* Call registered module API defrag start functions */ | ||
| 15117 | void moduleDefragStart(void) { | ||
| 15118 | dictForEach(modules, struct RedisModule, module, | ||
| 15119 | if (module->defrag_start_cb) { | ||
| 15120 | RedisModuleDefragCtx defrag_ctx = INIT_MODULE_DEFRAG_CTX(0, NULL, NULL, -1); | ||
| 15121 | module->defrag_start_cb(&defrag_ctx); | ||
| 15122 | } | ||
| 15123 | ); | ||
| 15124 | } | ||
| 15125 | |||
| 15126 | /* Call registered module API defrag end functions */ | ||
| 15127 | void moduleDefragEnd(void) { | ||
| 15128 | dictForEach(modules, struct RedisModule, module, | ||
| 15129 | if (module->defrag_end_cb) { | ||
| 15130 | RedisModuleDefragCtx defrag_ctx = INIT_MODULE_DEFRAG_CTX(0, NULL, NULL, -1); | ||
| 15131 | module->defrag_end_cb(&defrag_ctx); | ||
| 15132 | } | ||
| 15133 | ); | ||
| 15134 | } | ||
| 15135 | |||
| 15136 | /* Returns the name of the key currently being processed. | ||
| 15137 | * There is no guarantee that the key name is always available, so this may return NULL. | ||
| 15138 | */ | ||
| 15139 | const RedisModuleString *RM_GetKeyNameFromDefragCtx(RedisModuleDefragCtx *ctx) { | ||
| 15140 | return ctx->key; | ||
| 15141 | } | ||
| 15142 | |||
| 15143 | /* Returns the database id of the key currently being processed. | ||
| 15144 | * There is no guarantee that this info is always available, so this may return -1. | ||
| 15145 | */ | ||
| 15146 | int RM_GetDbIdFromDefragCtx(RedisModuleDefragCtx *ctx) { | ||
| 15147 | return ctx->dbid; | ||
| 15148 | } | ||
| 15149 | |||
| 15150 | /* Register all the APIs we export. Keep this function at the end of the | ||
| 15151 | * file so that's easy to seek it to add new entries. */ | ||
| 15152 | void moduleRegisterCoreAPI(void) { | ||
| 15153 | server.moduleapi = dictCreate(&moduleAPIDictType); | ||
| 15154 | server.sharedapi = dictCreate(&moduleAPIDictType); | ||
| 15155 | REGISTER_API(Alloc); | ||
| 15156 | REGISTER_API(TryAlloc); | ||
| 15157 | REGISTER_API(Calloc); | ||
| 15158 | REGISTER_API(TryCalloc); | ||
| 15159 | REGISTER_API(Realloc); | ||
| 15160 | REGISTER_API(TryRealloc); | ||
| 15161 | REGISTER_API(Free); | ||
| 15162 | REGISTER_API(Strdup); | ||
| 15163 | REGISTER_API(CreateCommand); | ||
| 15164 | REGISTER_API(GetCommand); | ||
| 15165 | REGISTER_API(CreateSubcommand); | ||
| 15166 | REGISTER_API(SetCommandInfo); | ||
| 15167 | REGISTER_API(SetCommandACLCategories); | ||
| 15168 | REGISTER_API(AddACLCategory); | ||
| 15169 | REGISTER_API(SetModuleAttribs); | ||
| 15170 | REGISTER_API(IsModuleNameBusy); | ||
| 15171 | REGISTER_API(WrongArity); | ||
| 15172 | REGISTER_API(ReplyWithLongLong); | ||
| 15173 | REGISTER_API(ReplyWithError); | ||
| 15174 | REGISTER_API(ReplyWithErrorFormat); | ||
| 15175 | REGISTER_API(ReplyWithSimpleString); | ||
| 15176 | REGISTER_API(ReplyWithArray); | ||
| 15177 | REGISTER_API(ReplyWithMap); | ||
| 15178 | REGISTER_API(ReplyWithSet); | ||
| 15179 | REGISTER_API(ReplyWithAttribute); | ||
| 15180 | REGISTER_API(ReplyWithNullArray); | ||
| 15181 | REGISTER_API(ReplyWithEmptyArray); | ||
| 15182 | REGISTER_API(ReplySetArrayLength); | ||
| 15183 | REGISTER_API(ReplySetMapLength); | ||
| 15184 | REGISTER_API(ReplySetSetLength); | ||
| 15185 | REGISTER_API(ReleaseKeyMetaClass); | ||
| 15186 | REGISTER_API(ReplySetAttributeLength); | ||
| 15187 | REGISTER_API(ReplyWithString); | ||
| 15188 | REGISTER_API(ReplyWithEmptyString); | ||
| 15189 | REGISTER_API(ReplyWithVerbatimString); | ||
| 15190 | REGISTER_API(ReplyWithVerbatimStringType); | ||
| 15191 | REGISTER_API(ReplyWithStringBuffer); | ||
| 15192 | REGISTER_API(CreateKeyMetaClass); | ||
| 15193 | REGISTER_API(SetKeyMeta); | ||
| 15194 | REGISTER_API(GetKeyMeta); | ||
| 15195 | REGISTER_API(ReplyWithCString); | ||
| 15196 | REGISTER_API(ReplyWithNull); | ||
| 15197 | REGISTER_API(ReplyWithBool); | ||
| 15198 | REGISTER_API(ReplyWithCallReply); | ||
| 15199 | REGISTER_API(ReplyWithDouble); | ||
| 15200 | REGISTER_API(ReplyWithBigNumber); | ||
| 15201 | REGISTER_API(ReplyWithLongDouble); | ||
| 15202 | REGISTER_API(GetSelectedDb); | ||
| 15203 | REGISTER_API(SelectDb); | ||
| 15204 | REGISTER_API(KeyExists); | ||
| 15205 | REGISTER_API(OpenKey); | ||
| 15206 | REGISTER_API(GetOpenKeyModesAll); | ||
| 15207 | REGISTER_API(CloseKey); | ||
| 15208 | REGISTER_API(KeyType); | ||
| 15209 | REGISTER_API(ValueLength); | ||
| 15210 | REGISTER_API(ListPush); | ||
| 15211 | REGISTER_API(ListPop); | ||
| 15212 | REGISTER_API(ListGet); | ||
| 15213 | REGISTER_API(ListSet); | ||
| 15214 | REGISTER_API(ListInsert); | ||
| 15215 | REGISTER_API(ListDelete); | ||
| 15216 | REGISTER_API(StringToLongLong); | ||
| 15217 | REGISTER_API(StringToULongLong); | ||
| 15218 | REGISTER_API(StringToDouble); | ||
| 15219 | REGISTER_API(StringToLongDouble); | ||
| 15220 | REGISTER_API(StringToStreamID); | ||
| 15221 | REGISTER_API(Call); | ||
| 15222 | REGISTER_API(CallReplyProto); | ||
| 15223 | REGISTER_API(FreeCallReply); | ||
| 15224 | REGISTER_API(CallReplyInteger); | ||
| 15225 | REGISTER_API(CallReplyDouble); | ||
| 15226 | REGISTER_API(CallReplyBigNumber); | ||
| 15227 | REGISTER_API(CallReplyVerbatim); | ||
| 15228 | REGISTER_API(CallReplyBool); | ||
| 15229 | REGISTER_API(CallReplySetElement); | ||
| 15230 | REGISTER_API(CallReplyMapElement); | ||
| 15231 | REGISTER_API(CallReplyAttributeElement); | ||
| 15232 | REGISTER_API(CallReplyPromiseSetUnblockHandler); | ||
| 15233 | REGISTER_API(CallReplyPromiseAbort); | ||
| 15234 | REGISTER_API(CallReplyAttribute); | ||
| 15235 | REGISTER_API(CallReplyType); | ||
| 15236 | REGISTER_API(CallReplyLength); | ||
| 15237 | REGISTER_API(CallReplyArrayElement); | ||
| 15238 | REGISTER_API(CallReplyStringPtr); | ||
| 15239 | REGISTER_API(CreateStringFromCallReply); | ||
| 15240 | REGISTER_API(CreateString); | ||
| 15241 | REGISTER_API(CreateStringFromLongLong); | ||
| 15242 | REGISTER_API(CreateStringFromULongLong); | ||
| 15243 | REGISTER_API(CreateStringFromDouble); | ||
| 15244 | REGISTER_API(CreateStringFromLongDouble); | ||
| 15245 | REGISTER_API(CreateStringFromString); | ||
| 15246 | REGISTER_API(CreateStringFromStreamID); | ||
| 15247 | REGISTER_API(CreateStringPrintf); | ||
| 15248 | REGISTER_API(FreeString); | ||
| 15249 | REGISTER_API(StringPtrLen); | ||
| 15250 | REGISTER_API(AutoMemory); | ||
| 15251 | REGISTER_API(Replicate); | ||
| 15252 | REGISTER_API(ReplicateVerbatim); | ||
| 15253 | REGISTER_API(DeleteKey); | ||
| 15254 | REGISTER_API(UnlinkKey); | ||
| 15255 | REGISTER_API(StringSet); | ||
| 15256 | REGISTER_API(StringDMA); | ||
| 15257 | REGISTER_API(StringTruncate); | ||
| 15258 | REGISTER_API(SetExpire); | ||
| 15259 | REGISTER_API(GetExpire); | ||
| 15260 | REGISTER_API(SetAbsExpire); | ||
| 15261 | REGISTER_API(GetAbsExpire); | ||
| 15262 | REGISTER_API(ResetDataset); | ||
| 15263 | REGISTER_API(DbSize); | ||
| 15264 | REGISTER_API(RandomKey); | ||
| 15265 | REGISTER_API(ZsetAdd); | ||
| 15266 | REGISTER_API(ZsetIncrby); | ||
| 15267 | REGISTER_API(ZsetScore); | ||
| 15268 | REGISTER_API(ZsetRem); | ||
| 15269 | REGISTER_API(ZsetRangeStop); | ||
| 15270 | REGISTER_API(ZsetFirstInScoreRange); | ||
| 15271 | REGISTER_API(ZsetLastInScoreRange); | ||
| 15272 | REGISTER_API(ZsetFirstInLexRange); | ||
| 15273 | REGISTER_API(ZsetLastInLexRange); | ||
| 15274 | REGISTER_API(ZsetRangeCurrentElement); | ||
| 15275 | REGISTER_API(ZsetRangeNext); | ||
| 15276 | REGISTER_API(ZsetRangePrev); | ||
| 15277 | REGISTER_API(ZsetRangeEndReached); | ||
| 15278 | REGISTER_API(HashSet); | ||
| 15279 | REGISTER_API(HashGet); | ||
| 15280 | REGISTER_API(HashFieldMinExpire); | ||
| 15281 | REGISTER_API(StreamAdd); | ||
| 15282 | REGISTER_API(StreamDelete); | ||
| 15283 | REGISTER_API(StreamIteratorStart); | ||
| 15284 | REGISTER_API(StreamIteratorStop); | ||
| 15285 | REGISTER_API(StreamIteratorNextID); | ||
| 15286 | REGISTER_API(StreamIteratorNextField); | ||
| 15287 | REGISTER_API(StreamIteratorDelete); | ||
| 15288 | REGISTER_API(StreamTrimByLength); | ||
| 15289 | REGISTER_API(StreamTrimByID); | ||
| 15290 | REGISTER_API(IsKeysPositionRequest); | ||
| 15291 | REGISTER_API(KeyAtPos); | ||
| 15292 | REGISTER_API(KeyAtPosWithFlags); | ||
| 15293 | REGISTER_API(IsChannelsPositionRequest); | ||
| 15294 | REGISTER_API(ChannelAtPosWithFlags); | ||
| 15295 | REGISTER_API(GetClientId); | ||
| 15296 | REGISTER_API(GetClientUserNameById); | ||
| 15297 | REGISTER_API(GetContextFlags); | ||
| 15298 | REGISTER_API(AvoidReplicaTraffic); | ||
| 15299 | REGISTER_API(PoolAlloc); | ||
| 15300 | REGISTER_API(CreateDataType); | ||
| 15301 | REGISTER_API(ModuleTypeSetValue); | ||
| 15302 | REGISTER_API(ModuleTypeReplaceValue); | ||
| 15303 | REGISTER_API(ModuleTypeGetType); | ||
| 15304 | REGISTER_API(ModuleTypeGetValue); | ||
| 15305 | REGISTER_API(IsIOError); | ||
| 15306 | REGISTER_API(SetModuleOptions); | ||
| 15307 | REGISTER_API(SignalModifiedKey); | ||
| 15308 | REGISTER_API(SaveUnsigned); | ||
| 15309 | REGISTER_API(LoadUnsigned); | ||
| 15310 | REGISTER_API(SaveSigned); | ||
| 15311 | REGISTER_API(LoadSigned); | ||
| 15312 | REGISTER_API(SaveString); | ||
| 15313 | REGISTER_API(SaveStringBuffer); | ||
| 15314 | REGISTER_API(LoadString); | ||
| 15315 | REGISTER_API(LoadStringBuffer); | ||
| 15316 | REGISTER_API(SaveDouble); | ||
| 15317 | REGISTER_API(LoadDouble); | ||
| 15318 | REGISTER_API(SaveFloat); | ||
| 15319 | REGISTER_API(LoadFloat); | ||
| 15320 | REGISTER_API(SaveLongDouble); | ||
| 15321 | REGISTER_API(LoadLongDouble); | ||
| 15322 | REGISTER_API(SaveDataTypeToString); | ||
| 15323 | REGISTER_API(LoadDataTypeFromString); | ||
| 15324 | REGISTER_API(LoadDataTypeFromStringEncver); | ||
| 15325 | REGISTER_API(EmitAOF); | ||
| 15326 | REGISTER_API(Log); | ||
| 15327 | REGISTER_API(LogIOError); | ||
| 15328 | REGISTER_API(_Assert); | ||
| 15329 | REGISTER_API(LatencyAddSample); | ||
| 15330 | REGISTER_API(StringAppendBuffer); | ||
| 15331 | REGISTER_API(TrimStringAllocation); | ||
| 15332 | REGISTER_API(RetainString); | ||
| 15333 | REGISTER_API(HoldString); | ||
| 15334 | REGISTER_API(StringCompare); | ||
| 15335 | REGISTER_API(GetContextFromIO); | ||
| 15336 | REGISTER_API(GetKeyNameFromIO); | ||
| 15337 | REGISTER_API(GetKeyNameFromModuleKey); | ||
| 15338 | REGISTER_API(GetDbIdFromModuleKey); | ||
| 15339 | REGISTER_API(GetDbIdFromIO); | ||
| 15340 | REGISTER_API(GetKeyNameFromOptCtx); | ||
| 15341 | REGISTER_API(GetToKeyNameFromOptCtx); | ||
| 15342 | REGISTER_API(GetDbIdFromOptCtx); | ||
| 15343 | REGISTER_API(GetToDbIdFromOptCtx); | ||
| 15344 | REGISTER_API(GetKeyNameFromDefragCtx); | ||
| 15345 | REGISTER_API(GetDbIdFromDefragCtx); | ||
| 15346 | REGISTER_API(GetKeyNameFromDigest); | ||
| 15347 | REGISTER_API(GetDbIdFromDigest); | ||
| 15348 | REGISTER_API(BlockClient); | ||
| 15349 | REGISTER_API(BlockClientGetPrivateData); | ||
| 15350 | REGISTER_API(BlockClientSetPrivateData); | ||
| 15351 | REGISTER_API(BlockClientOnAuth); | ||
| 15352 | REGISTER_API(UnblockClient); | ||
| 15353 | REGISTER_API(IsBlockedReplyRequest); | ||
| 15354 | REGISTER_API(IsBlockedTimeoutRequest); | ||
| 15355 | REGISTER_API(GetBlockedClientPrivateData); | ||
| 15356 | REGISTER_API(AbortBlock); | ||
| 15357 | REGISTER_API(Milliseconds); | ||
| 15358 | REGISTER_API(MonotonicMicroseconds); | ||
| 15359 | REGISTER_API(Microseconds); | ||
| 15360 | REGISTER_API(CachedMicroseconds); | ||
| 15361 | REGISTER_API(BlockedClientMeasureTimeStart); | ||
| 15362 | REGISTER_API(BlockedClientMeasureTimeEnd); | ||
| 15363 | REGISTER_API(GetThreadSafeContext); | ||
| 15364 | REGISTER_API(GetDetachedThreadSafeContext); | ||
| 15365 | REGISTER_API(FreeThreadSafeContext); | ||
| 15366 | REGISTER_API(ThreadSafeContextLock); | ||
| 15367 | REGISTER_API(ThreadSafeContextTryLock); | ||
| 15368 | REGISTER_API(ThreadSafeContextUnlock); | ||
| 15369 | REGISTER_API(DigestAddStringBuffer); | ||
| 15370 | REGISTER_API(DigestAddLongLong); | ||
| 15371 | REGISTER_API(DigestEndSequence); | ||
| 15372 | REGISTER_API(NotifyKeyspaceEvent); | ||
| 15373 | REGISTER_API(GetNotifyKeyspaceEvents); | ||
| 15374 | REGISTER_API(SubscribeToKeyspaceEvents); | ||
| 15375 | REGISTER_API(UnsubscribeFromKeyspaceEvents); | ||
| 15376 | REGISTER_API(AddPostNotificationJob); | ||
| 15377 | REGISTER_API(RegisterClusterMessageReceiver); | ||
| 15378 | REGISTER_API(SendClusterMessage); | ||
| 15379 | REGISTER_API(GetClusterNodeInfo); | ||
| 15380 | REGISTER_API(GetClusterNodesList); | ||
| 15381 | REGISTER_API(FreeClusterNodesList); | ||
| 15382 | REGISTER_API(CreateTimer); | ||
| 15383 | REGISTER_API(StopTimer); | ||
| 15384 | REGISTER_API(GetTimerInfo); | ||
| 15385 | REGISTER_API(GetMyClusterID); | ||
| 15386 | REGISTER_API(GetClusterSize); | ||
| 15387 | REGISTER_API(GetRandomBytes); | ||
| 15388 | REGISTER_API(GetRandomHexChars); | ||
| 15389 | REGISTER_API(BlockedClientDisconnected); | ||
| 15390 | REGISTER_API(SetDisconnectCallback); | ||
| 15391 | REGISTER_API(GetBlockedClientHandle); | ||
| 15392 | REGISTER_API(SetClusterFlags); | ||
| 15393 | REGISTER_API(ClusterDisableTrim); | ||
| 15394 | REGISTER_API(ClusterEnableTrim); | ||
| 15395 | REGISTER_API(ClusterKeySlot); | ||
| 15396 | REGISTER_API(ClusterKeySlotC); | ||
| 15397 | REGISTER_API(ClusterCanonicalKeyNameInSlot); | ||
| 15398 | REGISTER_API(ClusterCanAccessKeysInSlot); | ||
| 15399 | REGISTER_API(ClusterPropagateForSlotMigration); | ||
| 15400 | REGISTER_API(ClusterGetLocalSlotRanges); | ||
| 15401 | REGISTER_API(ClusterFreeSlotRanges); | ||
| 15402 | REGISTER_API(CreateDict); | ||
| 15403 | REGISTER_API(FreeDict); | ||
| 15404 | REGISTER_API(DictSize); | ||
| 15405 | REGISTER_API(DictSetC); | ||
| 15406 | REGISTER_API(DictReplaceC); | ||
| 15407 | REGISTER_API(DictSet); | ||
| 15408 | REGISTER_API(DictReplace); | ||
| 15409 | REGISTER_API(DictGetC); | ||
| 15410 | REGISTER_API(DictGet); | ||
| 15411 | REGISTER_API(DictDelC); | ||
| 15412 | REGISTER_API(DictDel); | ||
| 15413 | REGISTER_API(DictIteratorStartC); | ||
| 15414 | REGISTER_API(DictIteratorStart); | ||
| 15415 | REGISTER_API(DictIteratorStop); | ||
| 15416 | REGISTER_API(DictIteratorReseekC); | ||
| 15417 | REGISTER_API(DictIteratorReseek); | ||
| 15418 | REGISTER_API(DictNextC); | ||
| 15419 | REGISTER_API(DictPrevC); | ||
| 15420 | REGISTER_API(DictNext); | ||
| 15421 | REGISTER_API(DictPrev); | ||
| 15422 | REGISTER_API(DictCompareC); | ||
| 15423 | REGISTER_API(DictCompare); | ||
| 15424 | REGISTER_API(ExportSharedAPI); | ||
| 15425 | REGISTER_API(GetSharedAPI); | ||
| 15426 | REGISTER_API(RegisterCommandFilter); | ||
| 15427 | REGISTER_API(UnregisterCommandFilter); | ||
| 15428 | REGISTER_API(CommandFilterArgsCount); | ||
| 15429 | REGISTER_API(CommandFilterArgGet); | ||
| 15430 | REGISTER_API(CommandFilterArgInsert); | ||
| 15431 | REGISTER_API(CommandFilterArgReplace); | ||
| 15432 | REGISTER_API(CommandFilterArgDelete); | ||
| 15433 | REGISTER_API(CommandFilterGetClientId); | ||
| 15434 | REGISTER_API(Fork); | ||
| 15435 | REGISTER_API(SendChildHeartbeat); | ||
| 15436 | REGISTER_API(ExitFromChild); | ||
| 15437 | REGISTER_API(KillForkChild); | ||
| 15438 | REGISTER_API(RegisterInfoFunc); | ||
| 15439 | REGISTER_API(InfoAddSection); | ||
| 15440 | REGISTER_API(InfoBeginDictField); | ||
| 15441 | REGISTER_API(InfoEndDictField); | ||
| 15442 | REGISTER_API(InfoAddFieldString); | ||
| 15443 | REGISTER_API(InfoAddFieldCString); | ||
| 15444 | REGISTER_API(InfoAddFieldDouble); | ||
| 15445 | REGISTER_API(InfoAddFieldLongLong); | ||
| 15446 | REGISTER_API(InfoAddFieldULongLong); | ||
| 15447 | REGISTER_API(GetServerInfo); | ||
| 15448 | REGISTER_API(FreeServerInfo); | ||
| 15449 | REGISTER_API(ServerInfoGetField); | ||
| 15450 | REGISTER_API(ServerInfoGetFieldC); | ||
| 15451 | REGISTER_API(ServerInfoGetFieldSigned); | ||
| 15452 | REGISTER_API(ServerInfoGetFieldUnsigned); | ||
| 15453 | REGISTER_API(ServerInfoGetFieldDouble); | ||
| 15454 | REGISTER_API(GetClientInfoById); | ||
| 15455 | REGISTER_API(GetClientNameById); | ||
| 15456 | REGISTER_API(SetClientNameById); | ||
| 15457 | REGISTER_API(PublishMessage); | ||
| 15458 | REGISTER_API(PublishMessageShard); | ||
| 15459 | REGISTER_API(SubscribeToServerEvent); | ||
| 15460 | REGISTER_API(SetLRU); | ||
| 15461 | REGISTER_API(GetLRU); | ||
| 15462 | REGISTER_API(SetLFU); | ||
| 15463 | REGISTER_API(GetLFU); | ||
| 15464 | REGISTER_API(BlockClientOnKeys); | ||
| 15465 | REGISTER_API(BlockClientOnKeysWithFlags); | ||
| 15466 | REGISTER_API(SignalKeyAsReady); | ||
| 15467 | REGISTER_API(GetBlockedClientReadyKey); | ||
| 15468 | REGISTER_API(GetUsedMemoryRatio); | ||
| 15469 | REGISTER_API(MallocSize); | ||
| 15470 | REGISTER_API(MallocUsableSize); | ||
| 15471 | REGISTER_API(MallocSizeString); | ||
| 15472 | REGISTER_API(MallocSizeDict); | ||
| 15473 | REGISTER_API(ScanCursorCreate); | ||
| 15474 | REGISTER_API(ScanCursorDestroy); | ||
| 15475 | REGISTER_API(ScanCursorRestart); | ||
| 15476 | REGISTER_API(Scan); | ||
| 15477 | REGISTER_API(ScanKey); | ||
| 15478 | REGISTER_API(CreateModuleUser); | ||
| 15479 | REGISTER_API(SetContextUser); | ||
| 15480 | REGISTER_API(SetModuleUserACL); | ||
| 15481 | REGISTER_API(SetModuleUserACLString); | ||
| 15482 | REGISTER_API(GetModuleUserACLString); | ||
| 15483 | REGISTER_API(GetCurrentUserName); | ||
| 15484 | REGISTER_API(GetModuleUserFromUserName); | ||
| 15485 | REGISTER_API(ACLCheckCommandPermissions); | ||
| 15486 | REGISTER_API(ACLCheckKeyPermissions); | ||
| 15487 | REGISTER_API(ACLCheckKeyPrefixPermissions); | ||
| 15488 | REGISTER_API(ACLCheckChannelPermissions); | ||
| 15489 | REGISTER_API(ACLAddLogEntry); | ||
| 15490 | REGISTER_API(ACLAddLogEntryByUserName); | ||
| 15491 | REGISTER_API(FreeModuleUser); | ||
| 15492 | REGISTER_API(DeauthenticateAndCloseClient); | ||
| 15493 | REGISTER_API(AuthenticateClientWithACLUser); | ||
| 15494 | REGISTER_API(AuthenticateClientWithUser); | ||
| 15495 | REGISTER_API(GetContextFlagsAll); | ||
| 15496 | REGISTER_API(GetModuleOptionsAll); | ||
| 15497 | REGISTER_API(GetKeyspaceNotificationFlagsAll); | ||
| 15498 | REGISTER_API(IsSubEventSupported); | ||
| 15499 | REGISTER_API(GetServerVersion); | ||
| 15500 | REGISTER_API(GetClientCertificate); | ||
| 15501 | REGISTER_API(RedactClientCommandArgument); | ||
| 15502 | REGISTER_API(GetCommandKeys); | ||
| 15503 | REGISTER_API(GetCommandKeysWithFlags); | ||
| 15504 | REGISTER_API(GetCurrentCommandName); | ||
| 15505 | REGISTER_API(GetTypeMethodVersion); | ||
| 15506 | REGISTER_API(RegisterDefragFunc); | ||
| 15507 | REGISTER_API(RegisterDefragFunc2); | ||
| 15508 | REGISTER_API(RegisterDefragCallbacks); | ||
| 15509 | REGISTER_API(DefragAlloc); | ||
| 15510 | REGISTER_API(DefragAllocRaw); | ||
| 15511 | REGISTER_API(DefragFreeRaw); | ||
| 15512 | REGISTER_API(DefragRedisModuleString); | ||
| 15513 | REGISTER_API(DefragRedisModuleDict); | ||
| 15514 | REGISTER_API(DefragShouldStop); | ||
| 15515 | REGISTER_API(DefragCursorSet); | ||
| 15516 | REGISTER_API(DefragCursorGet); | ||
| 15517 | REGISTER_API(EventLoopAdd); | ||
| 15518 | REGISTER_API(EventLoopDel); | ||
| 15519 | REGISTER_API(EventLoopAddOneShot); | ||
| 15520 | REGISTER_API(Yield); | ||
| 15521 | REGISTER_API(RegisterBoolConfig); | ||
| 15522 | REGISTER_API(RegisterNumericConfig); | ||
| 15523 | REGISTER_API(RegisterStringConfig); | ||
| 15524 | REGISTER_API(RegisterEnumConfig); | ||
| 15525 | REGISTER_API(LoadDefaultConfigs); | ||
| 15526 | REGISTER_API(LoadConfigs); | ||
| 15527 | REGISTER_API(RegisterAuthCallback); | ||
| 15528 | REGISTER_API(RdbStreamCreateFromFile); | ||
| 15529 | REGISTER_API(RdbStreamFree); | ||
| 15530 | REGISTER_API(RdbLoad); | ||
| 15531 | REGISTER_API(RdbSave); | ||
| 15532 | REGISTER_API(GetInternalSecret); | ||
| 15533 | REGISTER_API(ConfigIteratorCreate); | ||
| 15534 | REGISTER_API(ConfigIteratorRelease); | ||
| 15535 | REGISTER_API(ConfigIteratorNext); | ||
| 15536 | REGISTER_API(ConfigGetType); | ||
| 15537 | REGISTER_API(ConfigGet); | ||
| 15538 | REGISTER_API(ConfigGetBool); | ||
| 15539 | REGISTER_API(ConfigGetEnum); | ||
| 15540 | REGISTER_API(ConfigGetNumeric); | ||
| 15541 | REGISTER_API(ConfigSet); | ||
| 15542 | REGISTER_API(ConfigSetBool); | ||
| 15543 | REGISTER_API(ConfigSetEnum); | ||
| 15544 | REGISTER_API(ConfigSetNumeric); | ||
| 15545 | } | ||
