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.09 * (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the10 * GNU Affero General Public License v3 (AGPLv3).11 *12 * Portions of this file are available under BSD3 terms; see REDISCONTRIBUTIONS for more information.13 */1415/* --------------------------------------------------------------------------16 * Modules API documentation information17 *18 * The comments in this file are used to generate the API documentation on the19 * Redis website.20 *21 * Each function starting with RM_ and preceded by a block comment is included22 * in the API documentation. To hide an RM_ function, put a blank line between23 * the comment and the function definition or put the comment inside the24 * function body.25 *26 * The functions are divided into sections. Each section is preceded by a27 * documentation block, which is comment block starting with a markdown level 228 * heading, i.e. a line starting with ##, on the first line of the comment block29 * (with the exception of a ----- line which can appear first). Other comment30 * blocks, which are not intended for the modules API user, such as this comment31 * block, do NOT start with a markdown level 2 heading, so they are included in32 * the generated a API documentation.33 *34 * The documentation comments may contain markdown formatting. Some automatic35 * replacements are done, such as the replacement of RM with RedisModule in36 * function names. For details, see the script src/modules/gendoc.rb.37 * -------------------------------------------------------------------------- */3839#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>5455/* --------------------------------------------------------------------------56 * Private data structures used by the modules system. Those are data57 * structures that are never exposed to Redis Modules, if not as void58 * pointers that have an API the module can call with them)59 * -------------------------------------------------------------------------- */6061struct 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};6970/* This represents a shared API. Shared APIs will be used to populate71 * the server.sharedapi dictionary, mapping names of APIs exported by72 * modules for other modules to use, to their structure specifying the73 * function pointer that can be called. */74struct RedisModuleSharedAPI {75 void *func;76 RedisModule *module;77};78typedef struct RedisModuleSharedAPI RedisModuleSharedAPI;79typedef struct RedisModuleKeyOptCtx RedisModuleKeyOptCtx;8081dict *modules; /* Hash table of modules. SDS -> RedisModule ptr.*/8283/* Entries in the context->amqueue array, representing objects to free84 * when the callback returns. */85struct AutoMemEntry {86 void *ptr;87 int type;88};8990/* AutoMemEntry type field values. */91#define REDISMODULE_AM_KEY 092#define REDISMODULE_AM_STRING 193#define REDISMODULE_AM_REPLY 294#define REDISMODULE_AM_FREED 3 /* Explicitly freed by user already. */95#define REDISMODULE_AM_DICT 496#define REDISMODULE_AM_INFO 597#define REDISMODULE_AM_CONFIG 698#define REDISMODULE_AM_SLOTRANGEARRAY 799100/* The pool allocator block. Redis Modules can allocate memory via this special101 * allocator that will automatically release it all once the callback returns.102 * This means that it can only be used for ephemeral allocations. However103 * 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 blocks107 * are allocated, and small pieces returned to the caller just advancing108 * the index of the allocation.109 *110 * Allocations are always rounded to the size of the void pointer in order111 * to always return aligned memory chunks. */112113#define REDISMODULE_POOL_ALLOC_MIN_SIZE (1024*8)114#define REDISMODULE_POOL_ALLOC_ALIGN (sizeof(void*))115116typedef struct RedisModulePoolAllocBlock {117 uint32_t size;118 uint32_t used;119 struct RedisModulePoolAllocBlock *next;120 char memory[];121} RedisModulePoolAllocBlock;122123/* 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 API125 * implementation can hold state across calls, or remember what to free after126 * the call and so forth.127 *128 * Note that not all the context structure is always filled with actual values129 * but only the fields needed in a given context. */130131struct RedisModuleBlockedClient;132struct RedisModuleUser;133134struct 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 for139 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 callback148 gets called for clients blocked149 on keys. */150151 /* Used if there is the REDISMODULE_CTX_KEYS_POS_REQUEST or 152 * REDISMODULE_CTX_CHANNEL_POS_REQUEST flag set. */153 getKeysResult *keys_result;154155 struct RedisModulePoolAllocBlock *pa_head;156 long long next_yield_time;157158 const struct RedisModuleUser *user; /* RedisModuleUser commands executed via159 RM_Call should be executed as, if set */160};161typedef struct RedisModuleCtx RedisModuleCtx;162163#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 pool171 when the context is destroyed */172#define REDISMODULE_CTX_NEW_CLIENT (1<<7) /* Free client object when the173 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) */176177178/* This represents a Redis key opened with RM_OpenKey(). */179struct 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. */186187 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 flag202 (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};212213/* RedisModuleKey 'ztype' values. */214#define REDISMODULE_ZSET_RANGE_NONE 0 /* This must always be 0. */215#define REDISMODULE_ZSET_RANGE_LEX 1216#define REDISMODULE_ZSET_RANGE_SCORE 2217#define REDISMODULE_ZSET_RANGE_POS 3218219/* Function pointer type of a function representing a command inside220 * a Redis module. */221struct RedisModuleBlockedClient;222typedef int (*RedisModuleCmdFunc) (RedisModuleCtx *ctx, void **argv, int argc);223typedef int (*RedisModuleAuthCallback)(RedisModuleCtx *ctx, void *username, void *password, RedisModuleString **err);224typedef void (*RedisModuleDisconnectFunc) (RedisModuleCtx *ctx, struct RedisModuleBlockedClient *bc);225226/* This struct holds the information about a command registered by a module.*/227struct RedisModuleCommand {228 struct RedisModule *module;229 RedisModuleCmdFunc func;230 struct redisCommand *rediscmd;231};232typedef struct RedisModuleCommand RedisModuleCommand;233234#define REDISMODULE_REPLYFLAG_NONE 0235#define REDISMODULE_REPLYFLAG_TOPARSE (1<<0) /* Protocol must be parsed. */236#define REDISMODULE_REPLYFLAG_NESTED (1<<1) /* Nested reply object. No proto237 or struct free. */238239/* Reply of RM_Call() function. The function is filled in a lazy240 * way depending on the function called on the reply structure. By default241 * only the type, proto and protolen are filled. */242typedef struct CallReply RedisModuleCallReply;243244/* Structure to hold the module auth callback & the Module implementing it. */245typedef struct RedisModuleAuthCtx {246 struct RedisModule *module;247 RedisModuleAuthCallback auth_cb;248} RedisModuleAuthCtx;249250/* Structure representing a blocked client. We get a pointer to such251 * an object when blocking from modules. */252typedef struct RedisModuleBlockedClient {253 client *client; /* Pointer to the blocked client. or NULL if the client254 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 blocking258 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 reply263 or timeout callback. It is set via the264 RedisModule_UnblockClient() API. */265 client *thread_safe_ctx_client; /* Fake client to be used for thread safe266 context so that no lock is required. */267 client *reply_client; /* Fake client used to accumulate replies268 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 on276 * a client that is blocked on keys. In this case we will277 * call the timeout call back from within278 * moduleHandleBlockedClients which runs from the main thread */279} RedisModuleBlockedClient;280281/* This is a list of Module Auth Contexts. Each time a Module registers a callback, a new ctx is282 * added to this list. Multiple modules can register auth callbacks and the same Module can have283 * multiple auth callbacks. */284static list *moduleAuthCallbacks;285286static pthread_mutex_t moduleUnblockedClientsMutex = PTHREAD_MUTEX_INITIALIZER;287static list *moduleUnblockedClients;288289/* Pool for temporary client objects. Creating and destroying a client object is290 * costly. We manage a pool of clients to avoid this cost. Pool expands when291 * more clients are needed and shrinks when unused. Please see modulesCron()292 * for more details. */293static client **moduleTempClients;294static size_t moduleTempClientCap = 0;295static size_t moduleTempClientCount = 0; /* Client count in pool */296static size_t moduleTempClientMinCount = 0; /* Min client count in pool since297 the last cron. */298299/* We need a mutex that is unlocked / relocked in beforeSleep() in order to300 * allow thread safe contexts to execute commands at a safe moment. */301static pthread_mutex_t moduleGIL = PTHREAD_MUTEX_INITIALIZER;302303/* Function pointer type for keyspace event notification subscriptions from modules. */304typedef int (*RedisModuleNotificationFunc) (RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key);305306/* Function pointer type for keyspace event notifications with subkeys from modules. */307typedef void (*RedisModuleNotificationWithSubkeysFunc)(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key, RedisModuleString **subkeys, int count);308309/* Function pointer type for post jobs */310typedef void (*RedisModulePostNotifyJobFunc) (RedisModuleCtx *ctx, void *pd);311typedef void (*RedisModulePostNotifyJobPerKeyFunc) (RedisModuleCtx *ctx, RedisModuleString *key, void *pd);312313/* Keyspace notification subscriber information.314 * See RM_SubscribeToKeyspaceEvents() for more information. */315typedef struct RedisModuleKeyspaceSubscriber {316 /* The module subscribed to the event */317 RedisModule *module;318 /* Notification callback in the module*/319 RedisModuleNotificationFunc notify_callback;320 /* Extended notification callback with subkeys */321 RedisModuleNotificationWithSubkeysFunc notify_callback_with_subkeys;322 /* A bit mask of the events the module is interested in */323 int event_mask;324 /* Delivery flags for subkey notifications, controlling when the callback is invoked. */325 int flags;326 /* Active flag set on entry, to avoid reentrant subscribers327 * calling themselves */328 int active;329} RedisModuleKeyspaceSubscriber;330331/* A queued module post-notification job. A single queue holds both flavors:332 * - Regular jobs (RM_AddPostNotificationJob): key == NULL, `callback` is used.333 * They fire once at the end of the outermost execution unit and may write to334 * the keyspace (RM_Call).335 * - Per-key jobs (RM_AddPostNotificationJobForKey): key != NULL, `key_callback`336 * is used and receives the bound key. They may NOT write to the keyspace337 * (RM_Call is refused while they run), and they fire at the tail of every338 * call() (between MULTI/EXEC and script sub-commands) and during AOF replay,339 * as well as at the end of the execution unit. The key being non-NULL is what340 * marks a job as per-key; no separate flag is needed. */341typedef struct RedisModulePostExecUnitJob {342 /* The module subscribed to the event */343 RedisModule *module;344 union {345 RedisModulePostNotifyJobFunc callback; /* key == NULL */346 RedisModulePostNotifyJobPerKeyFunc key_callback; /* key != NULL */347 } cb;348 RedisModuleString *key; /* NULL for a regular job; an owned reference for a349 * per-key job, freed after the callback runs. */350 void *pd;351 void (*free_pd)(void*);352 int dbid;353} RedisModulePostExecUnitJob;354355/* The module keyspace notification subscribers list */356static list *moduleKeyspaceSubscribers;357358/* Cached event types that have at least one subscriber.359 * Updated on subscribe/unsubscribe to avoid traversing the list on every event. */360static int moduleKeyspaceSubscribersTypes = 0;361static int moduleKeyspaceSubscribersWithSubkeysTypes = 0;362363/* The module post-notification jobs list. Holds both regular jobs364 * (RM_AddPostNotificationJob) and per-key jobs (RM_AddPostNotificationJobForKey);365 * see RedisModulePostExecUnitJob for how the two are distinguished and drained. */366static list *modulePostExecUnitJobs;367368static int keyedPostNotifRMCallWarned = 0;369static int keyedPostNotifNotifyWarned = 0;370371/* Data structures related to the exported dictionary data structure. */372typedef struct RedisModuleDict {373 rax *rax; /* The radix tree. */374 size_t alloc_size; /* Total memory used (in bytes) by this dict. */375} RedisModuleDict;376377typedef struct RedisModuleDictIter {378 RedisModuleDict *dict;379 raxIterator ri;380} RedisModuleDictIter;381382typedef struct RedisModuleCommandFilterCtx {383 RedisModuleString **argv;384 int argv_len;385 int argc;386 client *c;387} RedisModuleCommandFilterCtx;388389typedef void (*RedisModuleCommandFilterFunc) (RedisModuleCommandFilterCtx *filter);390391typedef struct RedisModuleCommandFilter {392 /* The module that registered the filter */393 RedisModule *module;394 /* Filter callback function */395 RedisModuleCommandFilterFunc callback;396 /* REDISMODULE_CMDFILTER_* flags */397 int flags;398} RedisModuleCommandFilter;399400/* Registered filters */401static list *moduleCommandFilters;402403typedef void (*RedisModuleForkDoneHandler) (int exitcode, int bysignal, void *user_data);404405static struct RedisModuleForkInfo {406 RedisModuleForkDoneHandler done_handler;407 void* done_handler_user_data;408} moduleForkInfo = {0};409410typedef struct RedisModuleServerInfoData {411 rax *rax; /* parsed info data. */412} RedisModuleServerInfoData;413414typedef struct RedisModuleConfigIterator {415 dictIterator *di; /* Iterator for the configs dict. */416 sds pattern; /* Pattern to filter configs by name. */417 int is_glob; /* Is the pattern a glob-pattern or a fixed string? */418} RedisModuleConfigIterator;419420/* Flags for moduleCreateArgvFromUserFormat(). */421#define REDISMODULE_ARGV_REPLICATE (1<<0)422#define REDISMODULE_ARGV_NO_AOF (1<<1)423#define REDISMODULE_ARGV_NO_REPLICAS (1<<2)424#define REDISMODULE_ARGV_RESP_3 (1<<3)425#define REDISMODULE_ARGV_RESP_AUTO (1<<4)426#define REDISMODULE_ARGV_RUN_AS_USER (1<<5)427#define REDISMODULE_ARGV_SCRIPT_MODE (1<<6)428#define REDISMODULE_ARGV_NO_WRITES (1<<7)429#define REDISMODULE_ARGV_CALL_REPLIES_AS_ERRORS (1<<8)430#define REDISMODULE_ARGV_RESPECT_DENY_OOM (1<<9)431#define REDISMODULE_ARGV_DRY_RUN (1<<10)432#define REDISMODULE_ARGV_ALLOW_BLOCK (1<<11)433434/* Determine whether Redis should signal modified key implicitly.435 * In case 'ctx' has no 'module' member (and therefore no module->options),436 * we assume default behavior, that is, Redis signals.437 * (see RM_GetThreadSafeContext) */438#define SHOULD_SIGNAL_MODIFIED_KEYS(ctx) \439 ((ctx)->module? !((ctx)->module->options & REDISMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED) : 1)440441/* Server events hooks data structures and defines: this modules API442 * allow modules to subscribe to certain events in Redis, such as443 * the start and end of an RDB or AOF save, the change of role in replication,444 * and similar other events. */445446typedef struct RedisModuleEventListener {447 RedisModule *module;448 RedisModuleEvent event;449 RedisModuleEventCallback callback;450} RedisModuleEventListener;451452list *RedisModule_EventListeners; /* Global list of all the active events. */453454/* Data structures related to the redis module users */455456/* This is the object returned by RM_CreateModuleUser(). The module API is457 * able to create users, set ACLs to such users, and later authenticate458 * clients using such newly created users. */459typedef struct RedisModuleUser {460 user *user; /* Reference to the real redis user */461 int free_user; /* Indicates that user should also be freed when this object is freed */462} RedisModuleUser;463464/* Data structures related to redis module configurations */465/* The function signatures for module config get callbacks. These are identical to the ones exposed in redismodule.h. */466typedef RedisModuleString * (*RedisModuleConfigGetStringFunc)(const char *name, void *privdata);467typedef long long (*RedisModuleConfigGetNumericFunc)(const char *name, void *privdata);468typedef int (*RedisModuleConfigGetBoolFunc)(const char *name, void *privdata);469typedef int (*RedisModuleConfigGetEnumFunc)(const char *name, void *privdata);470/* The function signatures for module config set callbacks. These are identical to the ones exposed in redismodule.h. */471typedef int (*RedisModuleConfigSetStringFunc)(const char *name, RedisModuleString *val, void *privdata, RedisModuleString **err);472typedef int (*RedisModuleConfigSetNumericFunc)(const char *name, long long val, void *privdata, RedisModuleString **err);473typedef int (*RedisModuleConfigSetBoolFunc)(const char *name, int val, void *privdata, RedisModuleString **err);474typedef int (*RedisModuleConfigSetEnumFunc)(const char *name, int val, void *privdata, RedisModuleString **err);475/* Apply signature, identical to redismodule.h */476typedef int (*RedisModuleConfigApplyFunc)(RedisModuleCtx *ctx, void *privdata, RedisModuleString **err);477478/* Struct representing a module config. These are stored in a list in the module struct */479struct ModuleConfig {480 sds name; /* Fullname of the config (as it appears in the config file) */481 sds alias; /* Optional alias for the configuration. NULL if none exists */482483 int unprefixedFlag; /* Indicates if the REDISMODULE_CONFIG_UNPREFIXED flag was set. 484 * If the configuration name was prefixed,during get_fn/set_fn 485 * callbacks, it should be reported without the prefix */486487 void *privdata; /* Optional data passed into the module config callbacks */488 union get_fn { /* The get callback specified by the module */489 RedisModuleConfigGetStringFunc get_string;490 RedisModuleConfigGetNumericFunc get_numeric;491 RedisModuleConfigGetBoolFunc get_bool;492 RedisModuleConfigGetEnumFunc get_enum;493 } get_fn;494 union set_fn { /* The set callback specified by the module */495 RedisModuleConfigSetStringFunc set_string;496 RedisModuleConfigSetNumericFunc set_numeric;497 RedisModuleConfigSetBoolFunc set_bool;498 RedisModuleConfigSetEnumFunc set_enum;499 } set_fn;500 RedisModuleConfigApplyFunc apply_fn;501 RedisModule *module;502};503504typedef struct RedisModuleAsyncRMCallPromise{505 size_t ref_count;506 void *private_data;507 RedisModule *module;508 RedisModuleOnUnblocked on_unblocked;509 client *c;510 RedisModuleCtx *ctx;511} RedisModuleAsyncRMCallPromise;512513/* --------------------------------------------------------------------------514 * Prototypes515 * -------------------------------------------------------------------------- */516517void RM_FreeCallReply(RedisModuleCallReply *reply);518void RM_CloseKey(RedisModuleKey *key);519void autoMemoryCollect(RedisModuleCtx *ctx);520robj **moduleCreateArgvFromUserFormat(const char *cmdname, const char *fmt, int *argcp, int *flags, va_list ap);521void RM_ZsetRangeStop(RedisModuleKey *kp);522static void zsetKeyReset(RedisModuleKey *key);523static void moduleInitKeyTypeSpecific(RedisModuleKey *key);524void RM_FreeDict(RedisModuleCtx *ctx, RedisModuleDict *d);525void RM_FreeServerInfo(RedisModuleCtx *ctx, RedisModuleServerInfoData *data);526void RM_ConfigIteratorRelease(RedisModuleCtx *ctx, RedisModuleConfigIterator *iter);527void RM_ClusterFreeSlotRanges(RedisModuleCtx *ctx, RedisModuleSlotRangeArray *slots);528529/* Helpers for RM_SetCommandInfo. */530static int moduleValidateCommandInfo(const RedisModuleCommandInfo *info);531static int64_t moduleConvertKeySpecsFlags(int64_t flags, int from_api);532static int moduleValidateCommandArgs(RedisModuleCommandArg *args,533 const RedisModuleCommandInfoVersion *version);534static struct redisCommandArg *moduleCopyCommandArgs(RedisModuleCommandArg *args,535 const RedisModuleCommandInfoVersion *version);536static redisCommandArgType moduleConvertArgType(RedisModuleCommandArgType type, int *error);537static int moduleConvertArgFlags(int flags);538void moduleCreateContext(RedisModuleCtx *out_ctx, RedisModule *module, int ctx_flags);539540/* Common helper functions. */541int moduleVerifyResourceName(const char *name);542543/* --------------------------------------------------------------------------544 * ## Heap allocation raw functions545 *546 * Memory allocated with these functions are taken into account by Redis key547 * eviction algorithms and are reported in Redis memory usage information.548 * -------------------------------------------------------------------------- */549550/* Use like malloc(). Memory allocated with this function is reported in551 * Redis INFO memory, used for keys eviction according to maxmemory settings552 * and in general is taken into account as memory allocated by Redis.553 * You should avoid using malloc().554 * This function panics if unable to allocate enough memory. */555void *RM_Alloc(size_t bytes) {556 /* Use 'zmalloc_usable()' instead of 'zmalloc()' to allow the compiler557 * to recognize the additional memory size, which means that modules can558 * use the memory reported by 'RM_MallocUsableSize()' safely. In theory this559 * isn't really needed since this API can't be inlined (not even for embedded560 * modules like TLS (we use function pointers for module APIs), and the API doesn't561 * have the malloc_size attribute, but it's hard to predict how smart future compilers562 * will be, so better safe than sorry. */563 return zmalloc_usable(bytes,NULL);564}565566/* Similar to RM_Alloc, but returns NULL in case of allocation failure, instead567 * of panicking. */568void *RM_TryAlloc(size_t bytes) {569 return ztrymalloc_usable(bytes,NULL);570}571572/* Use like calloc(). Memory allocated with this function is reported in573 * Redis INFO memory, used for keys eviction according to maxmemory settings574 * and in general is taken into account as memory allocated by Redis.575 * You should avoid using calloc() directly. */576void *RM_Calloc(size_t nmemb, size_t size) {577 return zcalloc_usable(nmemb*size,NULL);578}579580/* Similar to RM_Calloc, but returns NULL in case of allocation failure, instead581 * of panicking. */582void *RM_TryCalloc(size_t nmemb, size_t size) {583 return ztrycalloc_usable(nmemb*size,NULL);584}585586/* Use like realloc() for memory obtained with RedisModule_Alloc(). */587void* RM_Realloc(void *ptr, size_t bytes) {588 return zrealloc_usable(ptr,bytes,NULL,NULL);589}590591/* Similar to RM_Realloc, but returns NULL in case of allocation failure,592 * instead of panicking. */593void *RM_TryRealloc(void *ptr, size_t bytes) {594 return ztryrealloc_usable(ptr,bytes,NULL,NULL);595}596597/* Use like free() for memory obtained by RedisModule_Alloc() and598 * RedisModule_Realloc(). However you should never try to free with599 * RedisModule_Free() memory allocated with malloc() inside your module. */600void RM_Free(void *ptr) {601 zfree(ptr);602}603604/* Like strdup() but returns memory allocated with RedisModule_Alloc(). */605char *RM_Strdup(const char *str) {606 return zstrdup(str);607}608609/* --------------------------------------------------------------------------610 * Pool allocator611 * -------------------------------------------------------------------------- */612613/* Release the chain of blocks used for pool allocations. */614void poolAllocRelease(RedisModuleCtx *ctx) {615 RedisModulePoolAllocBlock *head = ctx->pa_head, *next;616617 while(head != NULL) {618 next = head->next;619 zfree(head);620 head = next;621 }622 ctx->pa_head = NULL;623}624625/* Return heap allocated memory that will be freed automatically when the626 * module callback function returns. Mostly suitable for small allocations627 * that are short living and must be released when the callback returns628 * anyway. The returned memory is aligned to the architecture word size629 * if at least word size bytes are requested, otherwise it is just630 * aligned to the next power of two, so for example a 3 bytes request is631 * 4 bytes aligned while a 2 bytes request is 2 bytes aligned.632 *633 * There is no realloc style function since when this is needed to use the634 * pool allocator is not a good idea.635 *636 * The function returns NULL if `bytes` is 0. */637void *RM_PoolAlloc(RedisModuleCtx *ctx, size_t bytes) {638 if (bytes == 0) return NULL;639 RedisModulePoolAllocBlock *b = ctx->pa_head;640 size_t left = b ? b->size - b->used : 0;641642 /* Fix alignment. */643 if (left >= bytes) {644 size_t alignment = REDISMODULE_POOL_ALLOC_ALIGN;645 while (bytes < alignment && alignment/2 >= bytes) alignment /= 2;646 if (b->used % alignment)647 b->used += alignment - (b->used % alignment);648 left = (b->used > b->size) ? 0 : b->size - b->used;649 }650651 /* Create a new block if needed. */652 if (left < bytes) {653 size_t blocksize = REDISMODULE_POOL_ALLOC_MIN_SIZE;654 if (blocksize < bytes) blocksize = bytes;655 b = zmalloc(sizeof(*b) + blocksize);656 b->size = blocksize;657 b->used = 0;658 b->next = ctx->pa_head;659 ctx->pa_head = b;660 }661662 char *retval = b->memory + b->used;663 b->used += bytes;664 return retval;665}666667/* --------------------------------------------------------------------------668 * Helpers for modules API implementation669 * -------------------------------------------------------------------------- */670671client *moduleAllocTempClient(void) {672 client *c = NULL;673674 if (moduleTempClientCount > 0) {675 c = moduleTempClients[--moduleTempClientCount];676 if (moduleTempClientCount < moduleTempClientMinCount)677 moduleTempClientMinCount = moduleTempClientCount;678 } else {679 c = createClient(NULL);680 c->flags |= CLIENT_MODULE;681 c->user = NULL; /* Root user */682 }683 return c;684}685686static void freeRedisModuleAsyncRMCallPromise(RedisModuleAsyncRMCallPromise *promise) {687 if (--promise->ref_count > 0) {688 return;689 }690 /* When the promise is finally freed it can not have a client attached to it.691 * Either releasing the client or RM_CallReplyPromiseAbort would have removed it. */692 serverAssert(!promise->c);693 zfree(promise);694}695696void moduleReleaseTempClient(client *c) {697 if (moduleTempClientCount == moduleTempClientCap) {698 moduleTempClientCap = moduleTempClientCap ? moduleTempClientCap*2 : 32;699 moduleTempClients = zrealloc(moduleTempClients, sizeof(c)*moduleTempClientCap);700 }701 clearClientConnectionState(c);702 listEmpty(c->reply);703 c->reply_bytes = c->reply_bytes_shared = c->reply_bytes_unshared = 0;704 c->duration = 0;705 resetClient(c, -1);706 serverAssert(c->all_argv_len_sum == 0);707 c->bufpos = 0;708 c->flags = CLIENT_MODULE;709 c->user = NULL; /* Root user */710 c->cmd = c->lastcmd = c->realcmd = NULL;711 if (c->bstate.async_rm_call_handle) {712 RedisModuleAsyncRMCallPromise *promise = c->bstate.async_rm_call_handle;713 promise->c = NULL; /* Remove the client from the promise so it will no longer be possible to abort it. */714 freeRedisModuleAsyncRMCallPromise(promise);715 c->bstate.async_rm_call_handle = NULL;716 }717 moduleTempClients[moduleTempClientCount++] = c;718}719720/* Create an empty key of the specified type. `key` must point to a key object721 * opened for writing where the `.value` member is set to NULL because the722 * key was found to be non existing.723 *724 * On success REDISMODULE_OK is returned and the key is populated with725 * the value of the specified type. The function fails and returns726 * REDISMODULE_ERR if:727 *728 * 1. The key is not open for writing.729 * 2. The key is not empty.730 * 3. The specified type is unknown.731 */732int moduleCreateEmptyKey(RedisModuleKey *key, int type) {733 robj *obj;734735 /* The key must be open for writing and non existing to proceed. */736 if (!(key->mode & REDISMODULE_WRITE) || key->kv)737 return REDISMODULE_ERR;738739 switch(type) {740 case REDISMODULE_KEYTYPE_LIST:741 obj = createListListpackObject();742 break;743 case REDISMODULE_KEYTYPE_ZSET:744 obj = createZsetListpackObject();745 break;746 case REDISMODULE_KEYTYPE_HASH:747 obj = createHashObject();748 break;749 case REDISMODULE_KEYTYPE_STREAM:750 obj = createStreamObject();751 break;752 default: return REDISMODULE_ERR;753 }754755 key->kv = dbAdd(key->db, key->key, &obj);756 moduleInitKeyTypeSpecific(key);757 return REDISMODULE_OK;758}759760/* Frees key->iter and sets it to NULL. */761static void moduleFreeKeyIterator(RedisModuleKey *key) {762 serverAssert(key->iter != NULL);763 switch (key->kv->type) {764 case OBJ_LIST:765 listTypeResetIterator(key->iter);766 zfree(key->iter);767 break;768 case OBJ_STREAM:769 streamIteratorStop(key->iter);770 zfree(key->iter);771 break;772 default: serverAssert(0); /* No key->iter for other types. */773 }774 key->iter = NULL;775}776777/* Callback for listTypeTryConversion().778 * Frees list iterator and sets it to NULL. */779static void moduleFreeListIterator(void *data) {780 RedisModuleKey *key = (RedisModuleKey*)data;781 serverAssert(key->kv->type == OBJ_LIST);782 if (key->iter) moduleFreeKeyIterator(key);783}784785/* This function is called in low-level API implementation functions in order786 * to check if the value associated with the key remained empty after an787 * operation that removed elements from an aggregate data type.788 *789 * If this happens, the key is deleted from the DB and the key object state790 * is set to the right one in order to be targeted again by write operations791 * possibly recreating the key if needed.792 *793 * The function returns 1 if the key value object is found empty and is794 * deleted, otherwise 0 is returned. */795int moduleDelKeyIfEmpty(RedisModuleKey *key) {796 if (!(key->mode & REDISMODULE_WRITE) || key->kv == NULL) return 0;797 int isempty;798 robj *o = key->kv;799800 switch(o->type) {801 case OBJ_LIST: isempty = listTypeLength(o) == 0; break;802 case OBJ_SET: isempty = setTypeSize(o) == 0; break;803 case OBJ_ZSET: isempty = zsetLength(o) == 0; break;804 case OBJ_HASH: isempty = hashTypeLength(o, 0) == 0; break;805 case OBJ_STREAM: isempty = streamLength(o) == 0; break;806 default: isempty = 0;807 }808809 if (isempty) {810 if (key->iter) moduleFreeKeyIterator(key);811 dbDelete(key->db,key->key);812 key->kv = NULL;813 return 1;814 } else {815 return 0;816 }817}818819/* Update the cached subscriber types by walking the subscriber list.820 * Called after subscribe/unsubscribe operations. */821static void moduleUpdateKeyspaceSubscribersTypes(void) {822 int mask = 0, subkeys_mask = 0;823 listIter li;824 listNode *ln;825 listRewind(moduleKeyspaceSubscribers,&li);826 while((ln = listNext(&li))) {827 RedisModuleKeyspaceSubscriber *sub = ln->value;828 mask |= sub->event_mask;829 if (sub->notify_callback_with_subkeys)830 subkeys_mask |= sub->event_mask;831 }832 moduleKeyspaceSubscribersTypes = mask;833 moduleKeyspaceSubscribersWithSubkeysTypes = subkeys_mask;834}835836/* --------------------------------------------------------------------------837 * Service API exported to modules838 *839 * Note that all the exported APIs are called RM_<funcname> in the core840 * and RedisModule_<funcname> in the module side (defined as function841 * pointers in redismodule.h). In this way the dynamic linker does not842 * mess with our global function pointers, overriding it with the symbols843 * defined in the main executable having the same names.844 * -------------------------------------------------------------------------- */845846int RM_GetApi(const char *funcname, void **targetPtrPtr) {847 /* Lookup the requested module API and store the function pointer into the848 * target pointer. The function returns REDISMODULE_ERR if there is no such849 * named API, otherwise REDISMODULE_OK.850 *851 * This function is not meant to be used by modules developer, it is only852 * used implicitly by including redismodule.h. */853 dictEntry *he = dictFind(server.moduleapi, funcname);854 if (!he) return REDISMODULE_ERR;855 *targetPtrPtr = dictGetVal(he);856 return REDISMODULE_OK;857}858859void modulePostExecutionUnitOperations(void) {860 if (server.execution_nesting)861 return;862863 if (server.busy_module_yield_flags) {864 blockingOperationEnds();865 server.busy_module_yield_flags = BUSY_MODULE_YIELD_NONE;866 if (server.current_client)867 unprotectClient(server.current_client);868 unblockPostponedClients();869 }870}871872/* Free the context after the user function was called. */873void moduleFreeContext(RedisModuleCtx *ctx) {874 /* See comment in moduleCreateContext */875 if (!(ctx->flags & (REDISMODULE_CTX_THREAD_SAFE|REDISMODULE_CTX_COMMAND))) {876 exitExecutionUnit();877 postExecutionUnitOperations();878 }879 autoMemoryCollect(ctx);880 poolAllocRelease(ctx);881 if (ctx->postponed_arrays) {882 zfree(ctx->postponed_arrays);883 ctx->postponed_arrays_count = 0;884 serverLog(LL_WARNING,885 "API misuse detected in module %s: "886 "RedisModule_ReplyWith*(REDISMODULE_POSTPONED_LEN) "887 "not matched by the same number of RedisModule_SetReply*Len() "888 "calls.",889 ctx->module->name);890 }891 /* If this context has a temp client, we return it back to the pool.892 * If this context created a new client (e.g detached context), we free it.893 * If the client is assigned manually, e.g ctx->client = someClientInstance,894 * none of these flags will be set and we do not attempt to free it. */895 if (ctx->flags & REDISMODULE_CTX_TEMP_CLIENT)896 moduleReleaseTempClient(ctx->client);897 else if (ctx->flags & REDISMODULE_CTX_NEW_CLIENT)898 freeClient(ctx->client);899}900901static CallReply *moduleParseReply(client *c, RedisModuleCtx *ctx) {902 /* Convert the result of the Redis command into a module reply. */903 sds proto = sdsnewlen(c->buf,c->bufpos);904 c->bufpos = 0;905 while(listLength(c->reply)) {906 clientReplyBlock *o = listNodeValue(listFirst(c->reply));907908 proto = sdscatlen(proto,o->buf,o->used);909 listDelNode(c->reply,listFirst(c->reply));910 }911 CallReply *reply = callReplyCreate(proto, c->deferred_reply_errors, ctx);912 c->deferred_reply_errors = NULL; /* now the responsibility of the reply object. */913 return reply;914}915916void moduleCallCommandUnblockedHandler(client *c) {917 RedisModuleCtx ctx;918 RedisModuleAsyncRMCallPromise *promise = c->bstate.async_rm_call_handle;919 serverAssert(promise);920 RedisModule *module = promise->module;921 if (!promise->on_unblocked) {922 moduleReleaseTempClient(c);923 return; /* module did not set any unblock callback. */924 }925 moduleCreateContext(&ctx, module, REDISMODULE_CTX_TEMP_CLIENT);926 selectDb(ctx.client, c->db->id);927928 CallReply *reply = moduleParseReply(c, NULL);929 module->in_call++;930 promise->on_unblocked(&ctx, reply, promise->private_data);931 module->in_call--;932933 moduleFreeContext(&ctx);934 moduleReleaseTempClient(c);935}936937/* Create a module ctx and keep track of the nesting level.938 *939 * Note: When creating ctx for threads (RM_GetThreadSafeContext and940 * RM_GetDetachedThreadSafeContext) we do not bump up the nesting level941 * because we only need to track of nesting level in the main thread942 * (only the main thread uses propagatePendingCommands) */943void moduleCreateContext(RedisModuleCtx *out_ctx, RedisModule *module, int ctx_flags) {944 memset(out_ctx, 0 ,sizeof(RedisModuleCtx));945 out_ctx->getapifuncptr = (void*)(unsigned long)&RM_GetApi;946 out_ctx->module = module;947 out_ctx->flags = ctx_flags;948 if (ctx_flags & REDISMODULE_CTX_TEMP_CLIENT)949 out_ctx->client = moduleAllocTempClient();950 else if (ctx_flags & REDISMODULE_CTX_NEW_CLIENT)951 out_ctx->client = createClient(NULL);952953 /* Calculate the initial yield time for long blocked contexts.954 * in loading we depend on the server hz, but in other cases we also wait955 * for busy_reply_threshold.956 * Note that in theory we could have started processing BUSY_MODULE_YIELD_EVENTS957 * sooner, and only delay the processing for clients till the busy_reply_threshold,958 * but this carries some overheads of frequently marking clients with BLOCKED_POSTPONE959 * and releasing them, i.e. if modules only block for short periods. */960 if (server.loading)961 out_ctx->next_yield_time = getMonotonicUs() + 1000000 / server.hz;962 else963 out_ctx->next_yield_time = getMonotonicUs() + server.busy_reply_threshold * 1000;964965 /* Increment the execution_nesting counter (module is about to execute some code),966 * except in the following cases:967 * 1. We came here from cmd->proc (either call() or AOF load).968 * In the former, the counter has been already incremented from within969 * call() and in the latter we don't care about execution_nesting970 * 2. If we are running in a thread (execution_nesting will be dealt with971 * when locking/unlocking the GIL) */972 if (!(ctx_flags & (REDISMODULE_CTX_THREAD_SAFE|REDISMODULE_CTX_COMMAND))) {973 enterExecutionUnit(1, 0);974 }975}976977/* This Redis command binds the normal Redis command invocation with commands978 * exported by modules. */979void RedisModuleCommandDispatcher(client *c) {980 RedisModuleCommand *cp = c->cmd->module_cmd;981 RedisModuleCtx ctx;982 moduleCreateContext(&ctx, cp->module, REDISMODULE_CTX_COMMAND);983984 ctx.client = c;985 cp->func(&ctx,(void**)c->argv,c->argc);986 moduleFreeContext(&ctx);987988 /* In some cases processMultibulkBuffer uses sdsMakeRoomFor to989 * expand the query buffer, and in order to avoid a big object copy990 * the query buffer SDS may be used directly as the SDS string backing991 * the client argument vectors: sometimes this will result in the SDS992 * string having unused space at the end. Later if a module takes ownership993 * of the RedisString, such space will be wasted forever. Inside the994 * Redis core this is not a problem because tryObjectEncoding() is called995 * before storing strings in the key space. Here we need to do it996 * for the module. */997 for (int i = 0; i < c->argc; i++) {998 /* Only do the work if the module took ownership of the object:999 * in that case the refcount is no longer 1. */1000 if (c->argv[i]->refcount > 1)1001 trimStringObjectIfNeeded(c->argv[i], 0);1002 }1003}10041005/* This function returns the list of keys, with the same interface as the1006 * 'getkeys' function of the native commands, for module commands that exported1007 * the "getkeys-api" flag during the registration. This is done when the1008 * list of keys are not at fixed positions, so that first/last/step cannot1009 * be used.1010 *1011 * In order to accomplish its work, the module command is called, flagging1012 * the context in a way that the command can recognize this is a special1013 * "get keys" call by calling RedisModule_IsKeysPositionRequest(ctx). */1014int moduleGetCommandKeysViaAPI(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {1015 RedisModuleCommand *cp = cmd->module_cmd;1016 RedisModuleCtx ctx;1017 moduleCreateContext(&ctx, cp->module, REDISMODULE_CTX_KEYS_POS_REQUEST);10181019 /* Initialize getKeysResult */1020 getKeysPrepareResult(result, MAX_KEYS_BUFFER);1021 ctx.keys_result = result;10221023 cp->func(&ctx,(void**)argv,argc);1024 /* We currently always use the array allocated by RM_KeyAtPos() and don't try1025 * to optimize for the pre-allocated buffer.1026 */1027 moduleFreeContext(&ctx);1028 return result->numkeys;1029}10301031/* This function returns the list of channels, with the same interface as1032 * moduleGetCommandKeysViaAPI, for modules that declare "getchannels-api"1033 * during registration. Unlike keys, this is the only way to declare channels. */1034int moduleGetCommandChannelsViaAPI(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {1035 RedisModuleCommand *cp = cmd->module_cmd;1036 RedisModuleCtx ctx;1037 moduleCreateContext(&ctx, cp->module, REDISMODULE_CTX_CHANNELS_POS_REQUEST);10381039 /* Initialize getKeysResult */1040 getKeysPrepareResult(result, MAX_KEYS_BUFFER);1041 ctx.keys_result = result;10421043 cp->func(&ctx,(void**)argv,argc);1044 /* We currently always use the array allocated by RM_RM_ChannelAtPosWithFlags() and don't try1045 * to optimize for the pre-allocated buffer. */1046 moduleFreeContext(&ctx);1047 return result->numkeys;1048}10491050/* --------------------------------------------------------------------------1051 * ## Commands API1052 *1053 * These functions are used to implement custom Redis commands.1054 *1055 * For examples, see https://redis.io/docs/latest/develop/reference/modules/.1056 * -------------------------------------------------------------------------- */10571058/* Return non-zero if a module command, that was declared with the1059 * flag "getkeys-api", is called in a special way to get the keys positions1060 * and not to get executed. Otherwise zero is returned. */1061int RM_IsKeysPositionRequest(RedisModuleCtx *ctx) {1062 return (ctx->flags & REDISMODULE_CTX_KEYS_POS_REQUEST) != 0;1063}10641065/* When a module command is called in order to obtain the position of1066 * keys, since it was flagged as "getkeys-api" during the registration,1067 * the command implementation checks for this special call using the1068 * RedisModule_IsKeysPositionRequest() API and uses this function in1069 * order to report keys.1070 *1071 * The supported flags are the ones used by RM_SetCommandInfo, see REDISMODULE_CMD_KEY_*.1072 *1073 *1074 * The following is an example of how it could be used:1075 *1076 * if (RedisModule_IsKeysPositionRequest(ctx)) {1077 * RedisModule_KeyAtPosWithFlags(ctx, 2, REDISMODULE_CMD_KEY_RO | REDISMODULE_CMD_KEY_ACCESS);1078 * RedisModule_KeyAtPosWithFlags(ctx, 1, REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE | REDISMODULE_CMD_KEY_ACCESS);1079 * }1080 *1081 * Note: in the example above the get keys API could have been handled by key-specs (preferred).1082 * Implementing the getkeys-api is required only when is it not possible to declare key-specs that cover all keys.1083 *1084 */1085void RM_KeyAtPosWithFlags(RedisModuleCtx *ctx, int pos, int flags) {1086 if (!(ctx->flags & REDISMODULE_CTX_KEYS_POS_REQUEST) || !ctx->keys_result) return;1087 if (pos <= 0) return;10881089 getKeysResult *res = ctx->keys_result;10901091 /* Check overflow */1092 if (res->numkeys == res->size) {1093 int newsize = res->size + (res->size > 8192 ? 8192 : res->size);1094 getKeysPrepareResult(res, newsize);1095 }10961097 res->keys[res->numkeys].pos = pos;1098 res->keys[res->numkeys].flags = moduleConvertKeySpecsFlags(flags, 1);1099 res->numkeys++;1100}11011102/* This API existed before RM_KeyAtPosWithFlags was added, now deprecated and1103 * can be used for compatibility with older versions, before key-specs and flags1104 * were introduced. */1105void RM_KeyAtPos(RedisModuleCtx *ctx, int pos) {1106 /* Default flags require full access */1107 int flags = moduleConvertKeySpecsFlags(CMD_KEY_FULL_ACCESS, 0);1108 RM_KeyAtPosWithFlags(ctx, pos, flags);1109}11101111/* Return non-zero if a module command, that was declared with the1112 * flag "getchannels-api", is called in a special way to get the channel positions1113 * and not to get executed. Otherwise zero is returned. */1114int RM_IsChannelsPositionRequest(RedisModuleCtx *ctx) {1115 return (ctx->flags & REDISMODULE_CTX_CHANNELS_POS_REQUEST) != 0;1116}11171118/* When a module command is called in order to obtain the position of1119 * channels, since it was flagged as "getchannels-api" during the1120 * registration, the command implementation checks for this special call1121 * using the RedisModule_IsChannelsPositionRequest() API and uses this1122 * function in order to report the channels.1123 * 1124 * The supported flags are:1125 * * REDISMODULE_CMD_CHANNEL_SUBSCRIBE: This command will subscribe to the channel.1126 * * REDISMODULE_CMD_CHANNEL_UNSUBSCRIBE: This command will unsubscribe from this channel.1127 * * REDISMODULE_CMD_CHANNEL_PUBLISH: This command will publish to this channel.1128 * * REDISMODULE_CMD_CHANNEL_PATTERN: Instead of acting on a specific channel, will act on any 1129 * channel specified by the pattern. This is the same access1130 * used by the PSUBSCRIBE and PUNSUBSCRIBE commands available 1131 * in Redis. Not intended to be used with PUBLISH permissions.1132 *1133 * The following is an example of how it could be used:1134 *1135 * if (RedisModule_IsChannelsPositionRequest(ctx)) {1136 * RedisModule_ChannelAtPosWithFlags(ctx, 1, REDISMODULE_CMD_CHANNEL_SUBSCRIBE | REDISMODULE_CMD_CHANNEL_PATTERN);1137 * RedisModule_ChannelAtPosWithFlags(ctx, 1, REDISMODULE_CMD_CHANNEL_PUBLISH);1138 * }1139 *1140 * Note: One usage of declaring channels is for evaluating ACL permissions. In this context,1141 * unsubscribing is always allowed, so commands will only be checked against subscribe and1142 * publish permissions. This is preferred over using RM_ACLCheckChannelPermissions, since1143 * it allows the ACLs to be checked before the command is executed. */1144void RM_ChannelAtPosWithFlags(RedisModuleCtx *ctx, int pos, int flags) {1145 if (!(ctx->flags & REDISMODULE_CTX_CHANNELS_POS_REQUEST) || !ctx->keys_result) return;1146 if (pos <= 0) return;11471148 getKeysResult *res = ctx->keys_result;11491150 /* Check overflow */1151 if (res->numkeys == res->size) {1152 int newsize = res->size + (res->size > 8192 ? 8192 : res->size);1153 getKeysPrepareResult(res, newsize);1154 }11551156 int new_flags = 0;1157 if (flags & REDISMODULE_CMD_CHANNEL_SUBSCRIBE) new_flags |= CMD_CHANNEL_SUBSCRIBE;1158 if (flags & REDISMODULE_CMD_CHANNEL_UNSUBSCRIBE) new_flags |= CMD_CHANNEL_UNSUBSCRIBE;1159 if (flags & REDISMODULE_CMD_CHANNEL_PUBLISH) new_flags |= CMD_CHANNEL_PUBLISH;1160 if (flags & REDISMODULE_CMD_CHANNEL_PATTERN) new_flags |= CMD_CHANNEL_PATTERN;11611162 res->keys[res->numkeys].pos = pos;1163 res->keys[res->numkeys].flags = new_flags;1164 res->numkeys++;1165}11661167/* Returns 1 if name is valid, otherwise returns 0.1168 *1169 * We want to block some chars in module command names that we know can1170 * mess things up.1171 *1172 * There are these characters:1173 * ' ' (space) - issues with old inline protocol.1174 * '\r', '\n' (newline) - can mess up the protocol on acl error replies.1175 * '|' - sub-commands.1176 * '@' - ACL categories.1177 * '=', ',' - info and client list fields (':' handled by getSafeInfoString).1178 * */1179int isCommandNameValid(const char *name) {1180 const char *block_chars = " \r\n|@=,";11811182 if (strpbrk(name, block_chars))1183 return 0;1184 return 1;1185}11861187/* Helper for RM_CreateCommand(). Turns a string representing command1188 * flags into the command flags used by the Redis core.1189 *1190 * It returns the set of flags, or -1 if unknown flags are found. */1191int64_t commandFlagsFromString(char *s) {1192 int count, j;1193 int64_t flags = 0;1194 sds *tokens = sdssplitlen(s,strlen(s)," ",1,&count);1195 for (j = 0; j < count; j++) {1196 char *t = tokens[j];1197 if (!strcasecmp(t,"write")) flags |= CMD_WRITE;1198 else if (!strcasecmp(t,"readonly")) flags |= CMD_READONLY;1199 else if (!strcasecmp(t,"admin")) flags |= CMD_ADMIN;1200 else if (!strcasecmp(t,"deny-oom")) flags |= CMD_DENYOOM;1201 else if (!strcasecmp(t,"deny-script")) flags |= CMD_NOSCRIPT;1202 else if (!strcasecmp(t,"allow-loading")) flags |= CMD_LOADING;1203 else if (!strcasecmp(t,"pubsub")) flags |= CMD_PUBSUB;1204 else if (!strcasecmp(t,"random")) { /* Deprecated. Silently ignore. */ }1205 else if (!strcasecmp(t,"blocking")) flags |= CMD_BLOCKING;1206 else if (!strcasecmp(t,"allow-stale")) flags |= CMD_STALE;1207 else if (!strcasecmp(t,"no-monitor")) flags |= CMD_SKIP_MONITOR;1208 else if (!strcasecmp(t,"no-slowlog")) flags |= CMD_SKIP_SLOWLOG;1209 else if (!strcasecmp(t,"fast")) flags |= CMD_FAST;1210 else if (!strcasecmp(t,"no-auth")) flags |= CMD_NO_AUTH;1211 else if (!strcasecmp(t,"may-replicate")) flags |= CMD_MAY_REPLICATE;1212 else if (!strcasecmp(t,"getkeys-api")) flags |= CMD_MODULE_GETKEYS;1213 else if (!strcasecmp(t,"getchannels-api")) flags |= CMD_MODULE_GETCHANNELS;1214 else if (!strcasecmp(t,"no-cluster")) flags |= CMD_MODULE_NO_CLUSTER;1215 else if (!strcasecmp(t,"no-mandatory-keys")) flags |= CMD_NO_MANDATORY_KEYS;1216 else if (!strcasecmp(t,"allow-busy")) flags |= CMD_ALLOW_BUSY;1217 else if (!strcasecmp(t,"internal")) flags |= (CMD_INTERNAL|CMD_NOSCRIPT); /* We also disallow internal commands in scripts. */1218 else if (!strcasecmp(t,"touches-arbitrary-keys")) flags |= CMD_TOUCHES_ARBITRARY_KEYS;1219 else break;1220 }1221 sdsfreesplitres(tokens,count);1222 if (j != count) return -1; /* Some token not processed correctly. */1223 return flags;1224}12251226RedisModuleCommand *moduleCreateCommandProxy(struct RedisModule *module, sds declared_name, sds fullname, RedisModuleCmdFunc cmdfunc, int64_t flags, int firstkey, int lastkey, int keystep);12271228/* Register a new command in the Redis server, that will be handled by1229 * calling the function pointer 'cmdfunc' using the RedisModule calling1230 * convention.1231 *1232 * The function returns REDISMODULE_ERR in these cases:1233 * - If creation of module command is called outside the RedisModule_OnLoad.1234 * - The specified command is already busy.1235 * - The command name contains some chars that are not allowed.1236 * - A set of invalid flags were passed.1237 *1238 * Otherwise REDISMODULE_OK is returned and the new command is registered.1239 *1240 * This function must be called during the initialization of the module1241 * inside the RedisModule_OnLoad() function. Calling this function outside1242 * of the initialization function is not defined.1243 *1244 * The command function type is the following:1245 *1246 * int MyCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);1247 *1248 * And is supposed to always return REDISMODULE_OK.1249 *1250 * The set of flags 'strflags' specify the behavior of the command, and should1251 * be passed as a C string composed of space separated words, like for1252 * example "write deny-oom". The set of flags are:1253 *1254 * * **"write"**: The command may modify the data set (it may also read1255 * from it).1256 * * **"readonly"**: The command returns data from keys but never writes.1257 * * **"admin"**: The command is an administrative command (may change1258 * replication or perform similar tasks).1259 * * **"deny-oom"**: The command may use additional memory and should be1260 * denied during out of memory conditions.1261 * * **"deny-script"**: Don't allow this command in Lua scripts.1262 * * **"allow-loading"**: Allow this command while the server is loading data.1263 * Only commands not interacting with the data set1264 * should be allowed to run in this mode. If not sure1265 * don't use this flag.1266 * * **"pubsub"**: The command publishes things on Pub/Sub channels.1267 * * **"random"**: The command may have different outputs even starting1268 * from the same input arguments and key values.1269 * Starting from Redis 7.0 this flag has been deprecated.1270 * Declaring a command as "random" can be done using1271 * command tips, see https://redis.io/docs/latest/develop/reference/command-tips/.1272 * * **"allow-stale"**: The command is allowed to run on slaves that don't1273 * serve stale data. Don't use if you don't know what1274 * this means.1275 * * **"no-monitor"**: Don't propagate the command on monitor. Use this if1276 * the command has sensitive data among the arguments.1277 * * **"no-slowlog"**: Don't log this command in the slowlog. Use this if1278 * the command has sensitive data among the arguments.1279 * * **"fast"**: The command time complexity is not greater1280 * than O(log(N)) where N is the size of the collection or1281 * anything else representing the normal scalability1282 * issue with the command.1283 * * **"getkeys-api"**: The command implements the interface to return1284 * the arguments that are keys. Used when start/stop/step1285 * is not enough because of the command syntax.1286 * * **"no-cluster"**: The command should not register in Redis Cluster1287 * since is not designed to work with it because, for1288 * example, is unable to report the position of the1289 * keys, programmatically creates key names, or any1290 * other reason.1291 * * **"no-auth"**: This command can be run by an un-authenticated client.1292 * Normally this is used by a command that is used1293 * to authenticate a client.1294 * * **"may-replicate"**: This command may generate replication traffic, even1295 * though it's not a write command.1296 * * **"no-mandatory-keys"**: All the keys this command may take are optional1297 * * **"blocking"**: The command has the potential to block the client.1298 * * **"allow-busy"**: Permit the command while the server is blocked either by1299 * a script or by a slow module command, see1300 * RM_Yield.1301 * * **"getchannels-api"**: The command implements the interface to return1302 * the arguments that are channels.1303 * * **"internal"**: Internal command, one that should not be exposed to the user connections.1304 * For example, module commands that are called by the modules,1305 * commands that do not perform ACL validations (relying on earlier checks)1306 * * **"touches-arbitrary-keys"**: This command may modify arbitrary keys (i.e. not provided via argv).1307 * This flag is used so we don't wrap the replicated commands with MULTI/EXEC.1308 *1309 * The last three parameters specify which arguments of the new command are1310 * Redis keys. See https://redis.io/commands/command for more information.1311 *1312 * * `firstkey`: One-based index of the first argument that's a key.1313 * Position 0 is always the command name itself.1314 * 0 for commands with no keys.1315 * * `lastkey`: One-based index of the last argument that's a key.1316 * Negative numbers refer to counting backwards from the last1317 * argument (-1 means the last argument provided)1318 * 0 for commands with no keys.1319 * * `keystep`: Step between first and last key indexes.1320 * 0 for commands with no keys.1321 *1322 * This information is used by ACL, Cluster and the `COMMAND` command.1323 *1324 * NOTE: The scheme described above serves a limited purpose and can1325 * only be used to find keys that exist at constant indices.1326 * For non-trivial key arguments, you may pass 0,0,0 and use1327 * RedisModule_SetCommandInfo to set key specs using a more advanced scheme and use1328 * RedisModule_SetCommandACLCategories to set Redis ACL categories of the commands. */1329int RM_CreateCommand(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep) {1330 if (!ctx->module->onload)1331 return REDISMODULE_ERR;1332 int64_t flags = strflags ? commandFlagsFromString((char*)strflags) : 0;1333 if (flags == -1) return REDISMODULE_ERR;1334 if ((flags & CMD_MODULE_NO_CLUSTER) && server.cluster_enabled)1335 return REDISMODULE_ERR;13361337 /* We will encounter an error as above if cluster is enable */1338 if (flags & CMD_MODULE_NO_CLUSTER)1339 server.stat_cluster_incompatible_ops++;13401341 /* Check if the command name is valid. */1342 if (!isCommandNameValid(name))1343 return REDISMODULE_ERR;13441345 /* Check if the command name is busy. */1346 if (lookupCommandByCString(name) != NULL)1347 return REDISMODULE_ERR;13481349 sds declared_name = sdsnew(name);1350 RedisModuleCommand *cp = moduleCreateCommandProxy(ctx->module, declared_name, sdsdup(declared_name), cmdfunc, flags, firstkey, lastkey, keystep);1351 cp->rediscmd->arity = cmdfunc ? -1 : -2; /* Default value, can be changed later via dedicated API */13521353 pauseAllIOThreads();1354 serverAssert(dictAdd(server.commands, sdsdup(declared_name), cp->rediscmd) == DICT_OK);1355 serverAssert(dictAdd(server.orig_commands, sdsdup(declared_name), cp->rediscmd) == DICT_OK);1356 resumeAllIOThreads();13571358 cp->rediscmd->id = ACLGetCommandID(declared_name); /* ID used for ACL. */1359 return REDISMODULE_OK;1360}13611362/* A proxy that help create a module command / subcommand.1363 *1364 * 'declared_name': it contains the sub_name, which is just the fullname for non-subcommands.1365 * 'fullname': sds string representing the command fullname.1366 *1367 * Function will take the ownership of both 'declared_name' and 'fullname' SDS.1368 */1369RedisModuleCommand *moduleCreateCommandProxy(struct RedisModule *module, sds declared_name, sds fullname, RedisModuleCmdFunc cmdfunc, int64_t flags, int firstkey, int lastkey, int keystep) {1370 struct redisCommand *rediscmd;1371 RedisModuleCommand *cp;13721373 /* Create a command "proxy", which is a structure that is referenced1374 * in the command table, so that the generic command that works as1375 * binding between modules and Redis, can know what function to call1376 * and what the module is. */1377 cp = zcalloc(sizeof(*cp));1378 cp->module = module;1379 cp->func = cmdfunc;1380 cp->rediscmd = zcalloc(sizeof(*rediscmd));1381 cp->rediscmd->declared_name = declared_name; /* SDS for module commands */1382 cp->rediscmd->fullname = fullname;1383 cp->rediscmd->group = COMMAND_GROUP_MODULE;1384 cp->rediscmd->proc = RedisModuleCommandDispatcher;1385 cp->rediscmd->flags = flags | CMD_MODULE;1386 cp->rediscmd->module_cmd = cp;1387 if (firstkey != 0) {1388 cp->rediscmd->key_specs_num = 1;1389 cp->rediscmd->key_specs = zcalloc(sizeof(keySpec));1390 cp->rediscmd->key_specs[0].flags = CMD_KEY_FULL_ACCESS;1391 if (flags & CMD_MODULE_GETKEYS)1392 cp->rediscmd->key_specs[0].flags |= CMD_KEY_VARIABLE_FLAGS;1393 cp->rediscmd->key_specs[0].begin_search_type = KSPEC_BS_INDEX;1394 cp->rediscmd->key_specs[0].bs.index.pos = firstkey;1395 cp->rediscmd->key_specs[0].find_keys_type = KSPEC_FK_RANGE;1396 cp->rediscmd->key_specs[0].fk.range.lastkey = lastkey < 0 ? lastkey : (lastkey-firstkey);1397 cp->rediscmd->key_specs[0].fk.range.keystep = keystep;1398 cp->rediscmd->key_specs[0].fk.range.limit = 0;1399 } else {1400 cp->rediscmd->key_specs_num = 0;1401 cp->rediscmd->key_specs = NULL;1402 }1403 populateCommandLegacyRangeSpec(cp->rediscmd);1404 cp->rediscmd->microseconds = 0;1405 cp->rediscmd->calls = 0;1406 cp->rediscmd->rejected_calls = 0;1407 cp->rediscmd->failed_calls = 0;1408 return cp;1409}14101411/* Get an opaque structure, representing a module command, by command name.1412 * This structure is used in some of the command-related APIs.1413 *1414 * NULL is returned in case of the following errors:1415 *1416 * * Command not found1417 * * The command is not a module command1418 * * The command doesn't belong to the calling module1419 */1420RedisModuleCommand *RM_GetCommand(RedisModuleCtx *ctx, const char *name) {1421 struct redisCommand *cmd = lookupCommandByCString(name);14221423 if (!cmd || !(cmd->flags & CMD_MODULE))1424 return NULL;14251426 RedisModuleCommand *cp = cmd->module_cmd;1427 if (cp->module != ctx->module)1428 return NULL;14291430 return cp;1431}14321433/* Very similar to RedisModule_CreateCommand except that it is used to create1434 * a subcommand, associated with another, container, command.1435 *1436 * Example: If a module has a configuration command, MODULE.CONFIG, then1437 * GET and SET should be individual subcommands, while MODULE.CONFIG is1438 * a command, but should not be registered with a valid `funcptr`:1439 *1440 * if (RedisModule_CreateCommand(ctx,"module.config",NULL,"",0,0,0) == REDISMODULE_ERR)1441 * return REDISMODULE_ERR;1442 *1443 * RedisModuleCommand *parent = RedisModule_GetCommand(ctx,,"module.config");1444 *1445 * if (RedisModule_CreateSubcommand(parent,"set",cmd_config_set,"",0,0,0) == REDISMODULE_ERR)1446 * return REDISMODULE_ERR;1447 *1448 * if (RedisModule_CreateSubcommand(parent,"get",cmd_config_get,"",0,0,0) == REDISMODULE_ERR)1449 * return REDISMODULE_ERR;1450 *1451 * Returns REDISMODULE_OK on success and REDISMODULE_ERR in case of the following errors:1452 *1453 * * Error while parsing `strflags`1454 * * Command is marked as `no-cluster` but cluster mode is enabled1455 * * `parent` is already a subcommand (we do not allow more than one level of command nesting)1456 * * `parent` is a command with an implementation (RedisModuleCmdFunc) (A parent command should be a pure container of subcommands)1457 * * `parent` already has a subcommand called `name`1458 * * Creating a subcommand is called outside of RedisModule_OnLoad.1459 */1460int RM_CreateSubcommand(RedisModuleCommand *parent, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep) {1461 if (!parent->module->onload)1462 return REDISMODULE_ERR;1463 int64_t flags = strflags ? commandFlagsFromString((char*)strflags) : 0;1464 if (flags == -1) return REDISMODULE_ERR;1465 if ((flags & CMD_MODULE_NO_CLUSTER) && server.cluster_enabled)1466 return REDISMODULE_ERR;14671468 /* We will encounter an error as above if cluster is enable */1469 if (flags & CMD_MODULE_NO_CLUSTER)1470 server.stat_cluster_incompatible_ops++;14711472 struct redisCommand *parent_cmd = parent->rediscmd;14731474 if (parent_cmd->parent)1475 return REDISMODULE_ERR; /* We don't allow more than one level of subcommands */14761477 RedisModuleCommand *parent_cp = parent_cmd->module_cmd;1478 if (parent_cp->func)1479 return REDISMODULE_ERR; /* A parent command should be a pure container of subcommands */14801481 /* Check if the command name is valid. */1482 if (!isCommandNameValid(name))1483 return REDISMODULE_ERR;14841485 /* Check if the command name is busy within the parent command. */1486 sds declared_name = sdsnew(name);1487 if (parent_cmd->subcommands_dict && lookupSubcommand(parent_cmd, declared_name) != NULL) {1488 sdsfree(declared_name);1489 return REDISMODULE_ERR;1490 }14911492 sds fullname = catSubCommandFullname(parent_cmd->fullname, name);1493 RedisModuleCommand *cp = moduleCreateCommandProxy(parent->module, declared_name, fullname, cmdfunc, flags, firstkey, lastkey, keystep);1494 cp->rediscmd->arity = -2;14951496 commandAddSubcommand(parent_cmd, cp->rediscmd, name);1497 return REDISMODULE_OK;1498}14991500/* Accessors of array elements of structs where the element size is stored1501 * separately in the version struct. */1502static RedisModuleCommandHistoryEntry *1503moduleCmdHistoryEntryAt(const RedisModuleCommandInfoVersion *version,1504 RedisModuleCommandHistoryEntry *entries, int index) {1505 off_t offset = index * version->sizeof_historyentry;1506 return (RedisModuleCommandHistoryEntry *)((char *)(entries) + offset);1507}1508static RedisModuleCommandKeySpec *1509moduleCmdKeySpecAt(const RedisModuleCommandInfoVersion *version,1510 RedisModuleCommandKeySpec *keyspecs, int index) {1511 off_t offset = index * version->sizeof_keyspec;1512 return (RedisModuleCommandKeySpec *)((char *)(keyspecs) + offset);1513}1514static RedisModuleCommandArg *1515moduleCmdArgAt(const RedisModuleCommandInfoVersion *version,1516 const RedisModuleCommandArg *args, int index) {1517 off_t offset = index * version->sizeof_arg;1518 return (RedisModuleCommandArg *)((char *)(args) + offset);1519}15201521/* Recursively populate the args structure (setting num_args to the number of1522 * subargs) and return the number of args. */1523int populateArgsStructure(struct redisCommandArg *args) {1524 if (!args)1525 return 0;1526 int count = 0;1527 while (args->name) {1528 serverAssert(count < INT_MAX);1529 args->num_args = populateArgsStructure(args->subargs);1530 count++;1531 args++;1532 }1533 return count;1534}15351536/* RedisModule_AddACLCategory can be used to add new ACL command categories. Category names1537 * can only contain alphanumeric characters, underscores, or dashes. Categories can only be added1538 * during the RedisModule_OnLoad function. Once a category has been added, it can not be removed. 1539 * Any module can register a command to any added categories using RedisModule_SetCommandACLCategories.1540 * 1541 * Returns:1542 * - REDISMODULE_OK on successfully adding the new ACL category. 1543 * - REDISMODULE_ERR on failure.1544 * 1545 * On error the errno is set to:1546 * - EINVAL if the name contains invalid characters.1547 * - EBUSY if the category name already exists.1548 * - ENOMEM if the number of categories reached the max limit of 64 categories.1549 */1550int RM_AddACLCategory(RedisModuleCtx *ctx, const char *name) {1551 if (!ctx->module->onload) {1552 errno = EINVAL;1553 return REDISMODULE_ERR;1554 }15551556 if (moduleVerifyResourceName(name) == REDISMODULE_ERR) {1557 errno = EINVAL;1558 return REDISMODULE_ERR;1559 }15601561 if (ACLGetCommandCategoryFlagByName(name)) {1562 errno = EBUSY;1563 return REDISMODULE_ERR;1564 }15651566 if (ACLAddCommandCategory(name, 0)) {1567 ctx->module->num_acl_categories_added++;1568 return REDISMODULE_OK;1569 } else {1570 errno = ENOMEM;1571 return REDISMODULE_ERR;1572 }1573}15741575/* Helper for categoryFlagsFromString(). Attempts to find an acl flag representing the provided flag string1576 * and adds that flag to acl_categories_flags if a match is found.1577 *1578 * Returns '1' if acl category flag is recognized or1579 * returns '0' if not recognized */1580int matchAclCategoryFlag(char *flag, int64_t *acl_categories_flags) {1581 uint64_t this_flag = ACLGetCommandCategoryFlagByName(flag);1582 if (this_flag) {1583 *acl_categories_flags |= (int64_t) this_flag;1584 return 1;1585 }1586 return 0; /* Unrecognized */1587}15881589/* Helper for RM_SetCommandACLCategories(). Turns a string representing acl category1590 * flags into the acl category flags used by Redis ACL which allows users to access 1591 * the module commands by acl categories.1592 * 1593 * It returns the set of acl flags, or -1 if unknown flags are found. */1594int64_t categoryFlagsFromString(char *aclflags) {1595 int count, j;1596 int64_t acl_categories_flags = 0;1597 sds *tokens = sdssplitlen(aclflags,strlen(aclflags)," ",1,&count);1598 for (j = 0; j < count; j++) {1599 char *t = tokens[j];1600 if (!matchAclCategoryFlag(t, &acl_categories_flags)) {1601 serverLog(LL_WARNING,"Unrecognized categories flag %s on module load", t);1602 break;1603 }1604 }1605 sdsfreesplitres(tokens,count);1606 if (j != count) return -1; /* Some token not processed correctly. */1607 return acl_categories_flags;1608}16091610/* RedisModule_SetCommandACLCategories can be used to set ACL categories to module1611 * commands and subcommands. The set of ACL categories should be passed as1612 * a space separated C string 'aclflags'.1613 * 1614 * Example, the acl flags 'write slow' marks the command as part of the write and 1615 * slow ACL categories.1616 * 1617 * On success REDISMODULE_OK is returned. On error REDISMODULE_ERR is returned.1618 * 1619 * This function can only be called during the RedisModule_OnLoad function. If called1620 * outside of this function, an error is returned.1621 */1622int RM_SetCommandACLCategories(RedisModuleCommand *command, const char *aclflags) {1623 if (!command || !command->module || !command->module->onload) return REDISMODULE_ERR;1624 int64_t categories_flags = aclflags ? categoryFlagsFromString((char*)aclflags) : 0;1625 if (categories_flags == -1) return REDISMODULE_ERR;1626 struct redisCommand *rcmd = command->rediscmd;1627 rcmd->acl_categories = categories_flags; /* ACL categories flags for module command */1628 command->module->num_commands_with_acl_categories++;1629 return REDISMODULE_OK;1630}16311632/* Set additional command information.1633 *1634 * Affects the output of `COMMAND`, `COMMAND INFO` and `COMMAND DOCS`, Cluster,1635 * ACL and is used to filter commands with the wrong number of arguments before1636 * the call reaches the module code.1637 *1638 * This function can be called after creating a command using RM_CreateCommand1639 * and fetching the command pointer using RM_GetCommand. The information can1640 * only be set once for each command and has the following structure:1641 *1642 * typedef struct RedisModuleCommandInfo {1643 * const RedisModuleCommandInfoVersion *version;1644 * const char *summary;1645 * const char *complexity;1646 * const char *since;1647 * RedisModuleCommandHistoryEntry *history;1648 * const char *tips;1649 * int arity;1650 * RedisModuleCommandKeySpec *key_specs;1651 * RedisModuleCommandArg *args;1652 * } RedisModuleCommandInfo;1653 *1654 * All fields except `version` are optional. Explanation of the fields:1655 *1656 * - `version`: This field enables compatibility with different Redis versions.1657 * Always set this field to REDISMODULE_COMMAND_INFO_VERSION.1658 *1659 * - `summary`: A short description of the command (optional).1660 *1661 * - `complexity`: Complexity description (optional).1662 *1663 * - `since`: The version where the command was introduced (optional).1664 * Note: The version specified should be the module's, not Redis version.1665 *1666 * - `history`: An array of RedisModuleCommandHistoryEntry (optional), which is1667 * a struct with the following fields:1668 *1669 * const char *since;1670 * const char *changes;1671 *1672 * `since` is a version string and `changes` is a string describing the1673 * changes. The array is terminated by a zeroed entry, i.e. an entry with1674 * both strings set to NULL.1675 *1676 * - `tips`: A string of space-separated tips regarding this command, meant for1677 * clients and proxies. See https://redis.io/docs/latest/develop/reference/command-tips/.1678 *1679 * - `arity`: Number of arguments, including the command name itself. A positive1680 * number specifies an exact number of arguments and a negative number1681 * specifies a minimum number of arguments, so use -N to say >= N. Redis1682 * validates a call before passing it to a module, so this can replace an1683 * arity check inside the module command implementation. A value of 0 (or an1684 * omitted arity field) is equivalent to -2 if the command has sub commands1685 * and -1 otherwise.1686 *1687 * - `key_specs`: An array of RedisModuleCommandKeySpec, terminated by an1688 * element memset to zero. This is a scheme that tries to describe the1689 * positions of key arguments better than the old RM_CreateCommand arguments1690 * `firstkey`, `lastkey`, `keystep` and is needed if those three are not1691 * enough to describe the key positions. There are two steps to retrieve key1692 * positions: *begin search* (BS) in which index should find the first key and1693 * *find keys* (FK) which, relative to the output of BS, describes how can we1694 * will which arguments are keys. Additionally, there are key specific flags.1695 *1696 * Key-specs cause the triplet (firstkey, lastkey, keystep) given in1697 * RM_CreateCommand to be recomputed, but it is still useful to provide1698 * these three parameters in RM_CreateCommand, to better support old Redis1699 * versions where RM_SetCommandInfo is not available.1700 *1701 * Note that key-specs don't fully replace the "getkeys-api" (see1702 * RM_CreateCommand, RM_IsKeysPositionRequest and RM_KeyAtPosWithFlags) so1703 * it may be a good idea to supply both key-specs and implement the1704 * getkeys-api.1705 *1706 * A key-spec has the following structure:1707 *1708 * typedef struct RedisModuleCommandKeySpec {1709 * const char *notes;1710 * uint64_t flags;1711 * RedisModuleKeySpecBeginSearchType begin_search_type;1712 * union {1713 * struct {1714 * int pos;1715 * } index;1716 * struct {1717 * const char *keyword;1718 * int startfrom;1719 * } keyword;1720 * } bs;1721 * RedisModuleKeySpecFindKeysType find_keys_type;1722 * union {1723 * struct {1724 * int lastkey;1725 * int keystep;1726 * int limit;1727 * } range;1728 * struct {1729 * int keynumidx;1730 * int firstkey;1731 * int keystep;1732 * } keynum;1733 * } fk;1734 * } RedisModuleCommandKeySpec;1735 *1736 * Explanation of the fields of RedisModuleCommandKeySpec:1737 *1738 * * `notes`: Optional notes or clarifications about this key spec.1739 *1740 * * `flags`: A bitwise or of key-spec flags described below.1741 *1742 * * `begin_search_type`: This describes how the first key is discovered.1743 * There are two ways to determine the first key:1744 *1745 * * `REDISMODULE_KSPEC_BS_UNKNOWN`: There is no way to tell where the1746 * key args start.1747 * * `REDISMODULE_KSPEC_BS_INDEX`: Key args start at a constant index.1748 * * `REDISMODULE_KSPEC_BS_KEYWORD`: Key args start just after a1749 * specific keyword.1750 *1751 * * `bs`: This is a union in which the `index` or `keyword` branch is used1752 * depending on the value of the `begin_search_type` field.1753 *1754 * * `bs.index.pos`: The index from which we start the search for keys.1755 * (`REDISMODULE_KSPEC_BS_INDEX` only.)1756 *1757 * * `bs.keyword.keyword`: The keyword (string) that indicates the1758 * beginning of key arguments. (`REDISMODULE_KSPEC_BS_KEYWORD` only.)1759 *1760 * * `bs.keyword.startfrom`: An index in argv from which to start1761 * searching. Can be negative, which means start search from the end,1762 * in reverse. Example: -2 means to start in reverse from the1763 * penultimate argument. (`REDISMODULE_KSPEC_BS_KEYWORD` only.)1764 *1765 * * `find_keys_type`: After the "begin search", this describes which1766 * arguments are keys. The strategies are:1767 *1768 * * `REDISMODULE_KSPEC_BS_UNKNOWN`: There is no way to tell where the1769 * key args are located.1770 * * `REDISMODULE_KSPEC_FK_RANGE`: Keys end at a specific index (or1771 * relative to the last argument).1772 * * `REDISMODULE_KSPEC_FK_KEYNUM`: There's an argument that contains1773 * the number of key args somewhere before the keys themselves.1774 *1775 * `find_keys_type` and `fk` can be omitted if this keyspec describes1776 * exactly one key.1777 *1778 * * `fk`: This is a union in which the `range` or `keynum` branch is used1779 * depending on the value of the `find_keys_type` field.1780 *1781 * * `fk.range` (for `REDISMODULE_KSPEC_FK_RANGE`): A struct with the1782 * following fields:1783 *1784 * * `lastkey`: Index of the last key relative to the result of the1785 * begin search step. Can be negative, in which case it's not1786 * relative. -1 indicates the last argument, -2 one before the1787 * last and so on.1788 *1789 * * `keystep`: How many arguments should we skip after finding a1790 * key, in order to find the next one?1791 *1792 * * `limit`: If `lastkey` is -1, we use `limit` to stop the search1793 * by a factor. 0 and 1 mean no limit. 2 means 1/2 of the1794 * remaining args, 3 means 1/3, and so on.1795 *1796 * * `fk.keynum` (for `REDISMODULE_KSPEC_FK_KEYNUM`): A struct with the1797 * following fields:1798 *1799 * * `keynumidx`: Index of the argument containing the number of1800 * keys to come, relative to the result of the begin search step.1801 *1802 * * `firstkey`: Index of the fist key relative to the result of the1803 * begin search step. (Usually it's just after `keynumidx`, in1804 * which case it should be set to `keynumidx + 1`.)1805 *1806 * * `keystep`: How many arguments should we skip after finding a1807 * key, in order to find the next one?1808 *1809 * Key-spec flags:1810 *1811 * The first four refer to what the command actually does with the *value or1812 * metadata of the key*, and not necessarily the user data or how it affects1813 * it. Each key-spec may must have exactly one of these. Any operation1814 * that's not distinctly deletion, overwrite or read-only would be marked as1815 * RW.1816 *1817 * * `REDISMODULE_CMD_KEY_RO`: Read-Only. Reads the value of the key, but1818 * doesn't necessarily return it.1819 *1820 * * `REDISMODULE_CMD_KEY_RW`: Read-Write. Modifies the data stored in the1821 * value of the key or its metadata.1822 *1823 * * `REDISMODULE_CMD_KEY_OW`: Overwrite. Overwrites the data stored in the1824 * value of the key.1825 *1826 * * `REDISMODULE_CMD_KEY_RM`: Deletes the key.1827 *1828 * The next four refer to *user data inside the value of the key*, not the1829 * metadata like LRU, type, cardinality. It refers to the logical operation1830 * on the user's data (actual input strings or TTL), being1831 * used/returned/copied/changed. It doesn't refer to modification or1832 * returning of metadata (like type, count, presence of data). ACCESS can be1833 * combined with one of the write operations INSERT, DELETE or UPDATE. Any1834 * write that's not an INSERT or a DELETE would be UPDATE.1835 *1836 * * `REDISMODULE_CMD_KEY_ACCESS`: Returns, copies or uses the user data1837 * from the value of the key.1838 *1839 * * `REDISMODULE_CMD_KEY_UPDATE`: Updates data to the value, new value may1840 * depend on the old value.1841 *1842 * * `REDISMODULE_CMD_KEY_INSERT`: Adds data to the value with no chance of1843 * modification or deletion of existing data.1844 *1845 * * `REDISMODULE_CMD_KEY_DELETE`: Explicitly deletes some content from the1846 * value of the key.1847 *1848 * Other flags:1849 *1850 * * `REDISMODULE_CMD_KEY_NOT_KEY`: The key is not actually a key, but 1851 * should be routed in cluster mode as if it was a key.1852 *1853 * * `REDISMODULE_CMD_KEY_INCOMPLETE`: The keyspec might not point out all1854 * the keys it should cover.1855 *1856 * * `REDISMODULE_CMD_KEY_VARIABLE_FLAGS`: Some keys might have different1857 * flags depending on arguments.1858 *1859 * - `args`: An array of RedisModuleCommandArg, terminated by an element memset1860 * to zero. RedisModuleCommandArg is a structure with at the fields described1861 * below.1862 *1863 * typedef struct RedisModuleCommandArg {1864 * const char *name;1865 * RedisModuleCommandArgType type;1866 * int key_spec_index;1867 * const char *token;1868 * const char *summary;1869 * const char *since;1870 * int flags;1871 * struct RedisModuleCommandArg *subargs;1872 * } RedisModuleCommandArg;1873 *1874 * Explanation of the fields:1875 *1876 * * `name`: Name of the argument.1877 *1878 * * `type`: The type of the argument. See below for details. The types1879 * `REDISMODULE_ARG_TYPE_ONEOF` and `REDISMODULE_ARG_TYPE_BLOCK` require1880 * an argument to have sub-arguments, i.e. `subargs`.1881 *1882 * * `key_spec_index`: If the `type` is `REDISMODULE_ARG_TYPE_KEY` you must1883 * provide the index of the key-spec associated with this argument. See1884 * `key_specs` above. If the argument is not a key, you may specify -1.1885 *1886 * * `token`: The token preceding the argument (optional). Example: the1887 * argument `seconds` in `SET` has a token `EX`. If the argument consists1888 * of only a token (for example `NX` in `SET`) the type should be1889 * `REDISMODULE_ARG_TYPE_PURE_TOKEN` and `value` should be NULL.1890 *1891 * * `summary`: A short description of the argument (optional).1892 *1893 * * `since`: The first version which included this argument (optional).1894 *1895 * * `flags`: A bitwise or of the macros `REDISMODULE_CMD_ARG_*`. See below.1896 *1897 * * `value`: The display-value of the argument. This string is what should1898 * be displayed when creating the command syntax from the output of1899 * `COMMAND`. If `token` is not NULL, it should also be displayed.1900 *1901 * Explanation of `RedisModuleCommandArgType`:1902 *1903 * * `REDISMODULE_ARG_TYPE_STRING`: String argument.1904 * * `REDISMODULE_ARG_TYPE_INTEGER`: Integer argument.1905 * * `REDISMODULE_ARG_TYPE_DOUBLE`: Double-precision float argument.1906 * * `REDISMODULE_ARG_TYPE_KEY`: String argument representing a keyname.1907 * * `REDISMODULE_ARG_TYPE_PATTERN`: String, but regex pattern.1908 * * `REDISMODULE_ARG_TYPE_UNIX_TIME`: Integer, but Unix timestamp.1909 * * `REDISMODULE_ARG_TYPE_PURE_TOKEN`: Argument doesn't have a placeholder.1910 * It's just a token without a value. Example: the `KEEPTTL` option of the1911 * `SET` command.1912 * * `REDISMODULE_ARG_TYPE_ONEOF`: Used when the user can choose only one of1913 * a few sub-arguments. Requires `subargs`. Example: the `NX` and `XX`1914 * options of `SET`.1915 * * `REDISMODULE_ARG_TYPE_BLOCK`: Used when one wants to group together1916 * several sub-arguments, usually to apply something on all of them, like1917 * making the entire group "optional". Requires `subargs`. Example: the1918 * `LIMIT offset count` parameters in `ZRANGE`.1919 *1920 * Explanation of the command argument flags:1921 *1922 * * `REDISMODULE_CMD_ARG_OPTIONAL`: The argument is optional (like GET in1923 * the SET command).1924 * * `REDISMODULE_CMD_ARG_MULTIPLE`: The argument may repeat itself (like1925 * key in DEL).1926 * * `REDISMODULE_CMD_ARG_MULTIPLE_TOKEN`: The argument may repeat itself,1927 * and so does its token (like `GET pattern` in SORT).1928 *1929 * On success REDISMODULE_OK is returned. On error REDISMODULE_ERR is returned1930 * and `errno` is set to EINVAL if invalid info was provided or EEXIST if info1931 * has already been set. If the info is invalid, a warning is logged explaining1932 * which part of the info is invalid and why. */1933int RM_SetCommandInfo(RedisModuleCommand *command, const RedisModuleCommandInfo *info) {1934 if (!moduleValidateCommandInfo(info)) {1935 errno = EINVAL;1936 return REDISMODULE_ERR;1937 }19381939 struct redisCommand *cmd = command->rediscmd;19401941 /* Check if any info has already been set. Overwriting info involves freeing1942 * the old info, which is not implemented. */1943 if (cmd->summary || cmd->complexity || cmd->since || cmd->history ||1944 cmd->tips || cmd->args ||1945 !(cmd->key_specs_num == 0 ||1946 /* Allow key spec populated from legacy (first,last,step) to exist. */1947 (cmd->key_specs_num == 1 &&1948 cmd->key_specs[0].begin_search_type == KSPEC_BS_INDEX &&1949 cmd->key_specs[0].find_keys_type == KSPEC_FK_RANGE))) {1950 errno = EEXIST;1951 return REDISMODULE_ERR;1952 }19531954 if (info->summary) cmd->summary = zstrdup(info->summary);1955 if (info->complexity) cmd->complexity = zstrdup(info->complexity);1956 if (info->since) cmd->since = zstrdup(info->since);19571958 const RedisModuleCommandInfoVersion *version = info->version;1959 if (info->history) {1960 size_t count = 0;1961 while (moduleCmdHistoryEntryAt(version, info->history, count)->since)1962 count++;1963 serverAssert(count < SIZE_MAX / sizeof(commandHistory));1964 cmd->history = zmalloc(sizeof(commandHistory) * (count + 1));1965 for (size_t j = 0; j < count; j++) {1966 RedisModuleCommandHistoryEntry *entry =1967 moduleCmdHistoryEntryAt(version, info->history, j);1968 cmd->history[j].since = zstrdup(entry->since);1969 cmd->history[j].changes = zstrdup(entry->changes);1970 }1971 cmd->history[count].since = NULL;1972 cmd->history[count].changes = NULL;1973 cmd->num_history = count;1974 }19751976 if (info->tips) {1977 int count;1978 sds *tokens = sdssplitlen(info->tips, strlen(info->tips), " ", 1, &count);1979 if (tokens) {1980 cmd->tips = zmalloc(sizeof(char *) * (count + 1));1981 for (int j = 0; j < count; j++) {1982 cmd->tips[j] = zstrdup(tokens[j]);1983 }1984 cmd->tips[count] = NULL;1985 cmd->num_tips = count;1986 sdsfreesplitres(tokens, count);1987 }1988 }19891990 if (info->arity) cmd->arity = info->arity;19911992 if (info->key_specs) {1993 /* Count and allocate the key specs. */1994 size_t count = 0;1995 while (moduleCmdKeySpecAt(version, info->key_specs, count)->begin_search_type)1996 count++;1997 serverAssert(count < INT_MAX);1998 zfree(cmd->key_specs);1999 cmd->key_specs = zmalloc(sizeof(keySpec) * count);
Findings
✓ No findings reported for this file.