Beispiel #1
0
/* 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;
}
Beispiel #2
0
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);
    }
}
Beispiel #3
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;
}
Beispiel #4
0
// 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
Beispiel #5
0
/**
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;
}
Beispiel #6
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;
}
Beispiel #7
0
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;
}
Beispiel #8
0
/*
## 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));
}
Beispiel #9
0
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;
}
Beispiel #10
0
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;
    
}
Beispiel #11
0
/* 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;
}