1 #define REDISMODULE_EXPERIMENTAL_API
2 #include "redismodule.h"
3 
4 #include <string.h>
5 #include <assert.h>
6 #include <unistd.h>
7 #include <errno.h>
8 
9 #define UNUSED(x) (void)(x)
10 
test_call_generic(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)11 int test_call_generic(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
12 {
13     if (argc<2) {
14         RedisModule_WrongArity(ctx);
15         return REDISMODULE_OK;
16     }
17 
18     const char* cmdname = RedisModule_StringPtrLen(argv[1], NULL);
19     RedisModuleCallReply *reply = RedisModule_Call(ctx, cmdname, "v", argv+2, argc-2);
20     if (reply) {
21         RedisModule_ReplyWithCallReply(ctx, reply);
22         RedisModule_FreeCallReply(reply);
23     } else {
24         RedisModule_ReplyWithError(ctx, strerror(errno));
25     }
26     return REDISMODULE_OK;
27 }
28 
test_call_info(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)29 int test_call_info(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
30 {
31     RedisModuleCallReply *reply;
32     if (argc>1)
33         reply = RedisModule_Call(ctx, "info", "s", argv[1]);
34     else
35         reply = RedisModule_Call(ctx, "info", "");
36     if (reply) {
37         RedisModule_ReplyWithCallReply(ctx, reply);
38         RedisModule_FreeCallReply(reply);
39     } else {
40         RedisModule_ReplyWithError(ctx, strerror(errno));
41     }
42     return REDISMODULE_OK;
43 }
44 
test_ld_conv(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)45 int test_ld_conv(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
46     UNUSED(argv);
47     UNUSED(argc);
48     long double ld = 0.00000000000000001L;
49     const char *ldstr = "0.00000000000000001";
50     RedisModuleString *s1 = RedisModule_CreateStringFromLongDouble(ctx, ld, 1);
51     RedisModuleString *s2 =
52         RedisModule_CreateString(ctx, ldstr, strlen(ldstr));
53     if (RedisModule_StringCompare(s1, s2) != 0) {
54         char err[4096];
55         snprintf(err, 4096,
56             "Failed to convert long double to string ('%s' != '%s')",
57             RedisModule_StringPtrLen(s1, NULL),
58             RedisModule_StringPtrLen(s2, NULL));
59         RedisModule_ReplyWithError(ctx, err);
60         goto final;
61     }
62     long double ld2 = 0;
63     if (RedisModule_StringToLongDouble(s2, &ld2) == REDISMODULE_ERR) {
64         RedisModule_ReplyWithError(ctx,
65             "Failed to convert string to long double");
66         goto final;
67     }
68     if (ld2 != ld) {
69         char err[4096];
70         snprintf(err, 4096,
71             "Failed to convert string to long double (%.40Lf != %.40Lf)",
72             ld2,
73             ld);
74         RedisModule_ReplyWithError(ctx, err);
75         goto final;
76     }
77 
78     /* Make sure we can't convert a string that has \0 in it */
79     char buf[4] = "123";
80     buf[1] = '\0';
81     RedisModuleString *s3 = RedisModule_CreateString(ctx, buf, 3);
82     long double ld3;
83     if (RedisModule_StringToLongDouble(s3, &ld3) == REDISMODULE_OK) {
84         RedisModule_ReplyWithError(ctx, "Invalid string successfully converted to long double");
85         RedisModule_FreeString(ctx, s3);
86         goto final;
87     }
88     RedisModule_FreeString(ctx, s3);
89 
90     RedisModule_ReplyWithLongDouble(ctx, ld2);
91 final:
92     RedisModule_FreeString(ctx, s1);
93     RedisModule_FreeString(ctx, s2);
94     return REDISMODULE_OK;
95 }
96 
test_flushall(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)97 int test_flushall(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
98 {
99     REDISMODULE_NOT_USED(argv);
100     REDISMODULE_NOT_USED(argc);
101     RedisModule_ResetDataset(1, 0);
102     RedisModule_ReplyWithCString(ctx, "Ok");
103     return REDISMODULE_OK;
104 }
105 
test_dbsize(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)106 int test_dbsize(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
107 {
108     REDISMODULE_NOT_USED(argv);
109     REDISMODULE_NOT_USED(argc);
110     long long ll = RedisModule_DbSize(ctx);
111     RedisModule_ReplyWithLongLong(ctx, ll);
112     return REDISMODULE_OK;
113 }
114 
test_randomkey(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)115 int test_randomkey(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
116 {
117     REDISMODULE_NOT_USED(argv);
118     REDISMODULE_NOT_USED(argc);
119     RedisModuleString *str = RedisModule_RandomKey(ctx);
120     RedisModule_ReplyWithString(ctx, str);
121     RedisModule_FreeString(ctx, str);
122     return REDISMODULE_OK;
123 }
124 
test_keyexists(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)125 int test_keyexists(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
126     if (argc < 2) return RedisModule_WrongArity(ctx);
127     RedisModuleString *key = argv[1];
128     int exists = RedisModule_KeyExists(ctx, key);
129     return RedisModule_ReplyWithBool(ctx, exists);
130 }
131 
open_key_or_reply(RedisModuleCtx * ctx,RedisModuleString * keyname,int mode)132 RedisModuleKey *open_key_or_reply(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode) {
133     RedisModuleKey *key = RedisModule_OpenKey(ctx, keyname, mode);
134     if (!key) {
135         RedisModule_ReplyWithError(ctx, "key not found");
136         return NULL;
137     }
138     return key;
139 }
140 
test_getlru(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)141 int test_getlru(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
142 {
143     if (argc<2) {
144         RedisModule_WrongArity(ctx);
145         return REDISMODULE_OK;
146     }
147     RedisModuleKey *key = open_key_or_reply(ctx, argv[1], REDISMODULE_READ|REDISMODULE_OPEN_KEY_NOTOUCH);
148     mstime_t lru;
149     RedisModule_GetLRU(key, &lru);
150     RedisModule_ReplyWithLongLong(ctx, lru);
151     RedisModule_CloseKey(key);
152     return REDISMODULE_OK;
153 }
154 
test_setlru(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)155 int test_setlru(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
156 {
157     if (argc<3) {
158         RedisModule_WrongArity(ctx);
159         return REDISMODULE_OK;
160     }
161     RedisModuleKey *key = open_key_or_reply(ctx, argv[1], REDISMODULE_READ|REDISMODULE_OPEN_KEY_NOTOUCH);
162     mstime_t lru;
163     if (RedisModule_StringToLongLong(argv[2], &lru) != REDISMODULE_OK) {
164         RedisModule_ReplyWithError(ctx, "invalid idle time");
165         return REDISMODULE_OK;
166     }
167     int was_set = RedisModule_SetLRU(key, lru)==REDISMODULE_OK;
168     RedisModule_ReplyWithLongLong(ctx, was_set);
169     RedisModule_CloseKey(key);
170     return REDISMODULE_OK;
171 }
172 
test_getlfu(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)173 int test_getlfu(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
174 {
175     if (argc<2) {
176         RedisModule_WrongArity(ctx);
177         return REDISMODULE_OK;
178     }
179     RedisModuleKey *key = open_key_or_reply(ctx, argv[1], REDISMODULE_READ|REDISMODULE_OPEN_KEY_NOTOUCH);
180     mstime_t lfu;
181     RedisModule_GetLFU(key, &lfu);
182     RedisModule_ReplyWithLongLong(ctx, lfu);
183     RedisModule_CloseKey(key);
184     return REDISMODULE_OK;
185 }
186 
test_setlfu(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)187 int test_setlfu(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
188 {
189     if (argc<3) {
190         RedisModule_WrongArity(ctx);
191         return REDISMODULE_OK;
192     }
193     RedisModuleKey *key = open_key_or_reply(ctx, argv[1], REDISMODULE_READ|REDISMODULE_OPEN_KEY_NOTOUCH);
194     mstime_t lfu;
195     if (RedisModule_StringToLongLong(argv[2], &lfu) != REDISMODULE_OK) {
196         RedisModule_ReplyWithError(ctx, "invalid freq");
197         return REDISMODULE_OK;
198     }
199     int was_set = RedisModule_SetLFU(key, lfu)==REDISMODULE_OK;
200     RedisModule_ReplyWithLongLong(ctx, was_set);
201     RedisModule_CloseKey(key);
202     return REDISMODULE_OK;
203 }
204 
test_redisversion(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)205 int test_redisversion(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){
206     (void) argv;
207     (void) argc;
208 
209     int version = RedisModule_GetServerVersion();
210     int patch = version & 0x000000ff;
211     int minor = (version & 0x0000ff00) >> 8;
212     int major = (version & 0x00ff0000) >> 16;
213 
214     RedisModuleString* vStr = RedisModule_CreateStringPrintf(ctx, "%d.%d.%d", major, minor, patch);
215     RedisModule_ReplyWithString(ctx, vStr);
216     RedisModule_FreeString(ctx, vStr);
217 
218     return REDISMODULE_OK;
219 }
220 
test_getclientcert(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)221 int test_getclientcert(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
222 {
223     (void) argv;
224     (void) argc;
225 
226     RedisModuleString *cert = RedisModule_GetClientCertificate(ctx,
227             RedisModule_GetClientId(ctx));
228     if (!cert) {
229         RedisModule_ReplyWithNull(ctx);
230     } else {
231         RedisModule_ReplyWithString(ctx, cert);
232         RedisModule_FreeString(ctx, cert);
233     }
234 
235     return REDISMODULE_OK;
236 }
237 
test_clientinfo(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)238 int test_clientinfo(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
239 {
240     (void) argv;
241     (void) argc;
242 
243     RedisModuleClientInfo ci = { .version = REDISMODULE_CLIENTINFO_VERSION };
244 
245     if (RedisModule_GetClientInfoById(&ci, RedisModule_GetClientId(ctx)) == REDISMODULE_ERR) {
246             RedisModule_ReplyWithError(ctx, "failed to get client info");
247             return REDISMODULE_OK;
248     }
249 
250     RedisModule_ReplyWithArray(ctx, 10);
251     char flags[512];
252     snprintf(flags, sizeof(flags) - 1, "%s:%s:%s:%s:%s:%s",
253         ci.flags & REDISMODULE_CLIENTINFO_FLAG_SSL ? "ssl" : "",
254         ci.flags & REDISMODULE_CLIENTINFO_FLAG_PUBSUB ? "pubsub" : "",
255         ci.flags & REDISMODULE_CLIENTINFO_FLAG_BLOCKED ? "blocked" : "",
256         ci.flags & REDISMODULE_CLIENTINFO_FLAG_TRACKING ? "tracking" : "",
257         ci.flags & REDISMODULE_CLIENTINFO_FLAG_UNIXSOCKET ? "unixsocket" : "",
258         ci.flags & REDISMODULE_CLIENTINFO_FLAG_MULTI ? "multi" : "");
259 
260     RedisModule_ReplyWithCString(ctx, "flags");
261     RedisModule_ReplyWithCString(ctx, flags);
262     RedisModule_ReplyWithCString(ctx, "id");
263     RedisModule_ReplyWithLongLong(ctx, ci.id);
264     RedisModule_ReplyWithCString(ctx, "addr");
265     RedisModule_ReplyWithCString(ctx, ci.addr);
266     RedisModule_ReplyWithCString(ctx, "port");
267     RedisModule_ReplyWithLongLong(ctx, ci.port);
268     RedisModule_ReplyWithCString(ctx, "db");
269     RedisModule_ReplyWithLongLong(ctx, ci.db);
270 
271     return REDISMODULE_OK;
272 }
273 
test_log_tsctx(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)274 int test_log_tsctx(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
275 {
276     RedisModuleCtx *tsctx = RedisModule_GetDetachedThreadSafeContext(ctx);
277 
278     if (argc != 3) {
279         RedisModule_WrongArity(ctx);
280         return REDISMODULE_OK;
281     }
282 
283     char level[50];
284     size_t level_len;
285     const char *level_str = RedisModule_StringPtrLen(argv[1], &level_len);
286     snprintf(level, sizeof(level) - 1, "%.*s", (int) level_len, level_str);
287 
288     size_t msg_len;
289     const char *msg_str = RedisModule_StringPtrLen(argv[2], &msg_len);
290 
291     RedisModule_Log(tsctx, level, "%.*s", (int) msg_len, msg_str);
292     RedisModule_FreeThreadSafeContext(tsctx);
293 
294     RedisModule_ReplyWithSimpleString(ctx, "OK");
295     return REDISMODULE_OK;
296 }
297 
test_weird_cmd(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)298 int test_weird_cmd(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
299     REDISMODULE_NOT_USED(argv);
300     REDISMODULE_NOT_USED(argc);
301 
302     RedisModule_ReplyWithSimpleString(ctx, "OK");
303     return REDISMODULE_OK;
304 }
305 
RedisModule_OnLoad(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)306 int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
307     REDISMODULE_NOT_USED(argv);
308     REDISMODULE_NOT_USED(argc);
309     if (RedisModule_Init(ctx,"misc",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR)
310         return REDISMODULE_ERR;
311 
312     if (RedisModule_CreateCommand(ctx,"test.call_generic", test_call_generic,"",0,0,0) == REDISMODULE_ERR)
313         return REDISMODULE_ERR;
314     if (RedisModule_CreateCommand(ctx,"test.call_info", test_call_info,"",0,0,0) == REDISMODULE_ERR)
315         return REDISMODULE_ERR;
316     if (RedisModule_CreateCommand(ctx,"test.ld_conversion", test_ld_conv, "",0,0,0) == REDISMODULE_ERR)
317         return REDISMODULE_ERR;
318     if (RedisModule_CreateCommand(ctx,"test.flushall", test_flushall,"",0,0,0) == REDISMODULE_ERR)
319         return REDISMODULE_ERR;
320     if (RedisModule_CreateCommand(ctx,"test.dbsize", test_dbsize,"",0,0,0) == REDISMODULE_ERR)
321         return REDISMODULE_ERR;
322     if (RedisModule_CreateCommand(ctx,"test.randomkey", test_randomkey,"",0,0,0) == REDISMODULE_ERR)
323         return REDISMODULE_ERR;
324     if (RedisModule_CreateCommand(ctx,"test.keyexists", test_keyexists,"",1,1,1) == REDISMODULE_ERR)
325         return REDISMODULE_ERR;
326     if (RedisModule_CreateCommand(ctx,"test.setlru", test_setlru,"",0,0,0) == REDISMODULE_ERR)
327         return REDISMODULE_ERR;
328     if (RedisModule_CreateCommand(ctx,"test.getlru", test_getlru,"",0,0,0) == REDISMODULE_ERR)
329         return REDISMODULE_ERR;
330     if (RedisModule_CreateCommand(ctx,"test.setlfu", test_setlfu,"",0,0,0) == REDISMODULE_ERR)
331         return REDISMODULE_ERR;
332     if (RedisModule_CreateCommand(ctx,"test.getlfu", test_getlfu,"",0,0,0) == REDISMODULE_ERR)
333         return REDISMODULE_ERR;
334     if (RedisModule_CreateCommand(ctx,"test.clientinfo", test_clientinfo,"",0,0,0) == REDISMODULE_ERR)
335         return REDISMODULE_ERR;
336     if (RedisModule_CreateCommand(ctx,"test.redisversion", test_redisversion,"",0,0,0) == REDISMODULE_ERR)
337         return REDISMODULE_ERR;
338     if (RedisModule_CreateCommand(ctx,"test.getclientcert", test_getclientcert,"",0,0,0) == REDISMODULE_ERR)
339         return REDISMODULE_ERR;
340     if (RedisModule_CreateCommand(ctx,"test.log_tsctx", test_log_tsctx,"",0,0,0) == REDISMODULE_ERR)
341         return REDISMODULE_ERR;
342     /* Add a command with ':' in it's name, so that we can check commandstats sanitization. */
343     if (RedisModule_CreateCommand(ctx,"test.weird:cmd", test_weird_cmd,"readonly",0,0,0) == REDISMODULE_ERR)
344         return REDISMODULE_ERR;
345 
346     return REDISMODULE_OK;
347 }
348