src/module.c C 16,095 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 16,095.
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.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.