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