/* HELLO.LEFTPAD str len ch * This is an implementation of the infamous LEFTPAD function, that * was at the center of an issue with the npm modules system in March 2016. * * LEFTPAD is a good example of using a Redis Modules API called * "pool allocator", that was a famous way to allocate memory in yet another * open source project, the Apache web server. * * The concept is very simple: there is memory that is useful to allocate * only in the context of serving a request, and must be freed anyway when * the callback implementing the command returns. So in that case the module * does not need to retain a reference to these allocations, it is just * required to free the memory before returning. When this is the case the * module can call RedisModule_PoolAlloc() instead, that works like malloc() * but will automatically free the memory when the module callback returns. * * Note that PoolAlloc() does not necessarily require AutoMemory to be * active. */ int HelloLeftPad_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ long long padlen; if (argc != 4) return RedisModule_WrongArity(ctx); if ((RedisModule_StringToLongLong(argv[2],&padlen) != REDISMODULE_OK) || (padlen< 0)) { return RedisModule_ReplyWithError(ctx,"ERR invalid padding length"); } size_t strlen, chlen; const char *str = RedisModule_StringPtrLen(argv[1], &strlen); const char *ch = RedisModule_StringPtrLen(argv[3], &chlen); /* If the string is already larger than the target len, just return * the string itself. */ if (strlen >= padlen) return RedisModule_ReplyWithString(ctx,argv[1]); /* Padding must be a single character in this simple implementation. */ if (chlen != 1) return RedisModule_ReplyWithError(ctx, "ERR padding must be a single char"); /* Here we use our pool allocator, for our throw-away allocation. */ padlen -= strlen; char *buf = RedisModule_PoolAlloc(ctx,padlen+strlen); for (size_t j = 0; j < padlen; j++) buf[j] = *ch; memcpy(buf+padlen,str,strlen); RedisModule_ReplyWithStringBuffer(ctx,buf,padlen+strlen); return REDISMODULE_OK; }
static void validate(RedisModuleCtx *ctx, RedisModuleCallReply *reply, RedisModuleString *password) { const char *reply_str; size_t reply_len; const char *pass; size_t pass_len; const char *crypt_pass; size_t crypt_pass_len; char crypt_buf[64]; if (RedisModule_CallReplyType(reply) == REDISMODULE_REPLY_NULL) { RedisModule_ReplyWithLongLong(ctx, 0); return; } if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_STRING) { RedisModule_ReplyWithError(ctx, "WRONGTYPE Operation against a key holding the wrong kind of value"); return; } reply_str = RedisModule_CallReplyStringPtr(reply, &reply_len); pass = RedisModule_StringPtrLen(password, &pass_len); crypt_pass = crypt_rn(pass, reply_str, crypt_buf, sizeof(crypt_buf)); if (!crypt_pass) { RedisModule_ReplyWithError(ctx, "ERR hash error"); return; } crypt_pass_len = strlen(crypt_pass); if (crypt_pass_len == reply_len && !memcmp(reply_str, crypt_pass, crypt_pass_len)) { RedisModule_ReplyWithLongLong(ctx, 1); } else { RedisModule_ReplyWithLongLong(ctx, 0); } }
static int UniquePushCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, mergefn fn) { if (argc != 4) { return RedisModule_WrongArity(ctx); } RedisModule_AutoMemory(ctx); // open the key and make sure it is indeed a Hash and not empty RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ | REDISMODULE_WRITE); int type = RedisModule_KeyType(key); if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != UniqueType) { return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } unique *unique; if (type == REDISMODULE_KEYTYPE_EMPTY) { unique = uniqueCreate(); RedisModule_ModuleTypeSetValue(key,UniqueType,unique); } else { unique = RedisModule_ModuleTypeGetValue(key); } sds skey, sval; size_t skeylen, svallen; const char *pkey, *pval; pkey = RedisModule_StringPtrLen(argv[2], &skeylen); pval = RedisModule_StringPtrLen(argv[3], &svallen); skey = sdsnewlen(pkey, skeylen); sval = sdsnewlen(pval, svallen); int n = uniquePush(unique, skey, sval, fn); if (n == -1) { RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } else { RedisModule_ReplyWithLongLong(ctx, n); } RedisModule_ReplicateVerbatim(ctx); return REDISMODULE_OK; }
// Internal function that parses arguments based on the format described above int rmutil_vparseArgs(RedisModuleString **argv, int argc, int offset, const char *fmt, va_list ap) { int i = offset; char *c = (char *)fmt; while (*c && i < argc) { // read c string if (*c == 'c') { char **p = va_arg(ap, char**); *p = (char *)RedisModule_StringPtrLen(argv[i], NULL); } else if (*c == 's') { //read redis string
/** Check if an argument exists in an argument list (argv,argc), starting at offset. @return 0 if it doesn't exist, otherwise the offset it exists in */ int RMUtil_ArgExists(const char *arg, RedisModuleString **argv, int argc, int offset) { for (; offset < argc; offset++) { size_t l; const char *carg = RedisModule_StringPtrLen(argv[offset], &l); if (carg != NULL && strcasecmp(carg, arg) == 0) { return offset; } } return 0; }
int UniquePushIVCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc < 4) { return RedisModule_WrongArity(ctx); } RedisModule_AutoMemory(ctx); // open the key and make sure it is indeed a Hash and not empty RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ | REDISMODULE_WRITE); int type = RedisModule_KeyType(key); if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != UniqueType) { return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } unique *unique; if (type == REDISMODULE_KEYTYPE_EMPTY) { unique = uniqueCreate(); RedisModule_ModuleTypeSetValue(key,UniqueType,unique); } else { unique = RedisModule_ModuleTypeGetValue(key); } sds skey, sval; size_t skeylen, svallen; const char *pkey; unsigned int lvec = argc-3; pkey = RedisModule_StringPtrLen(argv[2], &skeylen); svallen = lvec *8; skey = sdsnewlen(pkey, skeylen); sval = sdsnewlen(NULL, svallen); long long *vec = (long long*)sval; int i; for (i=3; i<argc; i++) { if (REDISMODULE_OK != RedisModule_StringToLongLong(argv[i], vec+i-3)) { return RedisModule_WrongArity(ctx); } } int n = uniquePush(unique, skey, sval, merge_int64); if (n == -1) { RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); } else { RedisModule_ReplyWithLongLong(ctx, n); } RedisModule_ReplicateVerbatim(ctx); return REDISMODULE_OK; }
static int router_simple_command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RedisModule_AutoMemory(ctx); if (argc < 2) { RedisModule_WrongArity(ctx); return REDISMODULE_OK; } RedisModuleCallReply *reply; size_t len; const char *realCmd = RedisModule_StringPtrLen(argv[1], &len); if (argc == 2) { reply = RedisModule_Call(ctx, realCmd, ""); } else { char *fmt = (char *) malloc(argc-1); memset(fmt, 's', argc-2); fmt[argc-2] = '\0'; ffi_cif cif; ffi_type **ffi_argv = (ffi_type **)malloc(sizeof(ffi_type *) * (argc + 1)); void *result; int i; for (i = 0; i < argc+1; i++) { ffi_argv[i] = &ffi_type_pointer; } void **values = (void **) malloc(sizeof(void *) * (argc + 1)); values[0] = &ctx; values[1] = &realCmd; values[2] = &fmt; for (i = 3; i < argc + 1; i++) { values[i] = &argv[i-1]; } if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, (argc + 1), &ffi_type_pointer, ffi_argv) == FFI_OK) { ffi_call(&cif, FFI_FN(RedisModule_Call), &result, values); } reply = (RedisModuleCallReply *) result; } if (reply == NULL) { const char *err = "command error"; RedisModule_ReplyWithError(ctx, err); } else { RedisModule_ReplyWithCallReply(ctx, reply); } return REDISMODULE_OK; }
/* ## FT.SUGDEL key str Delete a string from a suggestion index. ### Parameters: - key: the suggestion dictionary key. - str: the string to delete ### Returns: Integer reply: 1 if the string was found and deleted, 0 otherwise. */ int RSSuggestDelCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ if (argc != 3) return RedisModule_WrongArity(ctx); RedisModule_ReplicateVerbatim(ctx); RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ); int type = RedisModule_KeyType(key); if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != TrieType) { return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); } Trie *tree = RedisModule_ModuleTypeGetValue(key); if (!tree) { return RedisModule_ReplyWithLongLong(ctx, 0); } size_t len; const char *str = RedisModule_StringPtrLen(argv[2], &len); return RedisModule_ReplyWithLongLong(ctx, Trie_Delete(tree, str, len)); }
static int cmd_password_hset(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RedisModuleCallReply *reply; size_t len; char crypt_buf[64]; char *hash; if (argc != 4) { RedisModule_WrongArity(ctx); return REDISMODULE_OK; } hash = do_crypt(RedisModule_StringPtrLen(argv[3], &len), crypt_buf, sizeof(crypt_buf)); if (!hash) { RedisModule_ReplyWithError(ctx, "ERR hash error"); return REDISMODULE_ERR; } reply = RedisModule_Call(ctx, "HSET", "ssc!", argv[1], argv[2], hash); RedisModule_ReplyWithCallReply(ctx, reply); return REDISMODULE_OK; }
RMUtilInfo *RMUtil_GetRedisInfo(RedisModuleCtx *ctx) { RedisModuleCallReply *r = RedisModule_Call(ctx, "INFO", "c", "all"); if (r == NULL || RedisModule_CallReplyType(r) == REDISMODULE_REPLY_ERROR) { return NULL; } int cap = 100; // rough estimate of info lines RMUtilInfo *info = malloc(sizeof(RMUtilInfo)); info->entries = calloc(cap, sizeof(RMUtilInfoEntry)); int i = 0; char *text = (char *)RedisModule_StringPtrLen(RedisModule_CreateStringFromCallReply(r), NULL); char *line = text; while (line) { char *line = strsep(&text, "\r\n"); if (line == NULL) break; if (!(*line >= 'a' && *line <= 'z')) { //skip non entry lines continue; } char *key = strsep(&line, ":"); info->entries[i].key = key; info->entries[i].val = line; printf("Got info '%s' = '%s'\n", key, line); i++; if (i >= cap) { cap *=2; info->entries = realloc(info->entries, cap*sizeof(RMUtilInfoEntry)); } } info->numEntries = i; return info; }
/* This function must be present on each Redis module. It is used in order to * register the commands into the Redis server. */ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (RedisModule_Init(ctx,"helloworld",1,REDISMODULE_APIVER_1) == REDISMODULE_ERR) return REDISMODULE_ERR; /* Log the list of parameters passing loading the module. */ for (int j = 0; j < argc; j++) { const char *s = RedisModule_StringPtrLen(argv[j],NULL); printf("Module loaded with ARGV[%d] = %s\n", j, s); } if (RedisModule_CreateCommand(ctx,"hello.simple", HelloSimple_RedisCommand,"readonly",0,0,0) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"hello.push.native", HelloPushNative_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"hello.push.call", HelloPushCall_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"hello.push.call2", HelloPushCall2_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"hello.list.sum.len", HelloListSumLen_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"hello.list.splice", HelloListSplice_RedisCommand,"write deny-oom",1,2,1) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"hello.list.splice.auto", HelloListSpliceAuto_RedisCommand, "write deny-oom",1,2,1) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"hello.rand.array", HelloRandArray_RedisCommand,"readonly",0,0,0) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"hello.repl1", HelloRepl1_RedisCommand,"write",0,0,0) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"hello.repl2", HelloRepl2_RedisCommand,"write",1,1,1) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"hello.toggle.case", HelloToggleCase_RedisCommand,"write",1,1,1) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"hello.more.expire", HelloMoreExpire_RedisCommand,"write",1,1,1) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"hello.zsumrange", HelloZsumRange_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"hello.lexrange", HelloLexRange_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"hello.hcopy", HelloHCopy_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"hello.leftpad", HelloLeftPad_RedisCommand,"",1,1,1) == REDISMODULE_ERR) return REDISMODULE_ERR; return REDISMODULE_OK; }