6
6
#include < math.h>
7
7
#include < fstream>
8
8
#include < streambuf>
9
+ #include < dlfcn.h>
10
+ #include < experimental/filesystem>
9
11
10
12
RedisModuleCtx *g_ctx = nullptr ;
11
13
JSContext *g_jscontext = nullptr ;
14
+ bool g_fInStartup = true ;
15
+
16
+ class KeyDBContext
17
+ {
18
+ RedisModuleCtx *m_ctxSave;
19
+ public:
20
+ KeyDBContext (RedisModuleCtx *ctxSet)
21
+ {
22
+ m_ctxSave = g_ctx;
23
+ g_ctx = ctxSet;
24
+ }
25
+
26
+ ~KeyDBContext ()
27
+ {
28
+ g_ctx = m_ctxSave;
29
+ }
30
+ };
12
31
13
32
static void processResult (RedisModuleCtx *ctx, v8::Isolate *isolate, v8::Local<v8::Context> &v8ctx, v8::Local<v8::Value> &result)
14
33
{
@@ -129,8 +148,27 @@ void KeyDBExecuteCallback(const v8::FunctionCallbackInfo<v8::Value>& args)
129
148
RedisModule_FreeString (g_ctx, str);
130
149
}
131
150
151
+ void LogCallback (const v8::FunctionCallbackInfo<v8::Value>& args)
152
+ {
153
+ v8::Isolate* isolate = args.GetIsolate ();
154
+ v8::HandleScope scope (isolate);
155
+
156
+ if (args.Length () != 2 )
157
+ {
158
+ isolate->ThrowException (v8::String::NewFromUtf8 (isolate, " Log expects two parameters" ).ToLocalChecked ());
159
+ return ;
160
+ }
161
+
162
+
163
+ v8::String::Utf8Value level (isolate, args[0 ]);
164
+ v8::String::Utf8Value message (isolate, args[1 ]);
165
+ RedisModule_Log (g_ctx, *level, " %s" , *message);
166
+ }
167
+
132
168
int js_command (RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
133
169
{
170
+ KeyDBContext ctxsav (ctx);
171
+
134
172
v8::Locker locker (g_jscontext->getIsolate ());
135
173
v8::HandleScope scope (g_jscontext->getIsolate ());
136
174
if (argc < 1 )
@@ -169,6 +207,7 @@ int js_command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
169
207
}
170
208
171
209
auto maybeResult = fnCall->Call (context, global, (int )vecargs.size (), vecargs.data ());
210
+
172
211
v8::Local<v8::Value> result;
173
212
if (!maybeResult.ToLocal (&result))
174
213
{
@@ -181,13 +220,11 @@ int js_command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
181
220
catch (std::string strerr)
182
221
{
183
222
RedisModule_ReplyWithError (ctx, strerr.c_str ());
184
- g_ctx = nullptr ;
185
223
return REDISMODULE_ERR;
186
224
}
187
225
catch (std::nullptr_t )
188
226
{
189
227
RedisModule_ReplyWithError (ctx, " Unknown Error" );
190
- g_ctx = nullptr ;
191
228
return REDISMODULE_ERR;
192
229
}
193
230
return REDISMODULE_OK;
@@ -199,6 +236,12 @@ void RegisterCommandCallback(const v8::FunctionCallbackInfo<v8::Value>& args)
199
236
v8::Isolate* isolate = args.GetIsolate ();
200
237
v8::HandleScope scope (isolate);
201
238
239
+ if (!g_fInStartup)
240
+ {
241
+ isolate->ThrowException (v8::String::NewFromUtf8 (isolate, " New commands may only be registered during startup" ).ToLocalChecked ());
242
+ return ;
243
+ }
244
+
202
245
v8::Local<v8::Value> vfn = args[0 ];
203
246
if (!vfn->IsFunction ())
204
247
return ;
@@ -250,6 +293,8 @@ void RegisterCommandCallback(const v8::FunctionCallbackInfo<v8::Value>& args)
250
293
251
294
int evaljs_command (RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
252
295
{
296
+ KeyDBContext ctxsav (ctx);
297
+
253
298
if (argc != 2 )
254
299
{
255
300
RedisModule_WrongArity (ctx);
@@ -268,7 +313,6 @@ int evaljs_command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
268
313
const char *rgch = RedisModule_StringPtrLen (argv[1 ], &cch);
269
314
try
270
315
{
271
- g_ctx = ctx;
272
316
v8::HandleScope scope (g_jscontext->getIsolate ());
273
317
v8::Local<v8::Value> result = g_jscontext->run (rgch, cch);
274
318
auto context = g_jscontext->getCurrentContext ();
@@ -277,16 +321,47 @@ int evaljs_command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
277
321
catch (std::string strerr)
278
322
{
279
323
RedisModule_ReplyWithError (ctx, strerr.c_str ());
280
- g_ctx = nullptr ;
281
324
return REDISMODULE_ERR;
282
325
}
283
326
catch (std::nullptr_t )
284
327
{
285
328
RedisModule_ReplyWithError (ctx, " Unknown Error" );
286
- g_ctx = nullptr ;
287
329
return REDISMODULE_ERR;
288
330
}
289
- g_ctx = nullptr ;
331
+ return REDISMODULE_OK;
332
+ }
333
+
334
+ int run_startup_script (RedisModuleCtx *ctx, const char *rgchPath, size_t cchPath)
335
+ {
336
+ KeyDBContext ctxsav (ctx);
337
+
338
+ std::ifstream file (rgchPath, std::ios::binary | std::ios::ate);
339
+ std::streamsize size = file.tellg ();
340
+ if (size == -1 )
341
+ {
342
+ RedisModule_Log (ctx, " warning" , " startup script does not exist" );
343
+ return REDISMODULE_ERR; // Failed to read file
344
+ }
345
+
346
+ file.seekg (0 , std::ios::beg);
347
+
348
+ std::vector<char > buffer (size);
349
+ if (!file.read (buffer.data (), size))
350
+ {
351
+ RedisModule_Log (ctx, " warning" , " failed to read startup script" );
352
+ return REDISMODULE_ERR; // Failed to read file
353
+ }
354
+
355
+ v8::HandleScope scope (g_jscontext->getIsolate ());
356
+ try
357
+ {
358
+ g_jscontext->run (buffer.data (), buffer.size (), true /* don't cache */ );
359
+ }
360
+ catch (std::string str)
361
+ {
362
+ RedisModule_Log (ctx, " warning" , " %s" , str.c_str ());
363
+ return REDISMODULE_ERR;
364
+ }
290
365
return REDISMODULE_OK;
291
366
}
292
367
@@ -304,42 +379,39 @@ extern "C" int __attribute__((visibility("default"))) RedisModule_OnLoad(RedisMo
304
379
g_jscontext = new JSContext ();
305
380
g_jscontext->initialize ();
306
381
307
- if (argc >= 1 )
382
+ // Run our bootstrap.js code
308
383
{
309
- // Process the startup script
310
- size_t cchPath;
311
- const char *rgchPath = RedisModule_StringPtrLen (argv[0 ], &cchPath);
312
-
313
- std::ifstream file (rgchPath, std::ios::binary | std::ios::ate);
314
- std::streamsize size = file.tellg ();
315
- if (size == -1 )
384
+ Dl_info dlInfo;
385
+ dladdr ((const void *)RedisModule_OnLoad, &dlInfo);
386
+ if (dlInfo.dli_sname != NULL && dlInfo.dli_saddr != NULL )
316
387
{
317
- RedisModule_Log (ctx, " warning" , " startup script does not exist" );
318
- return REDISMODULE_ERR; // Failed to read file
388
+ std::experimental::filesystem::path path (dlInfo.dli_fname );
389
+ path.remove_filename ();
390
+ path /= " bootstrap.js" ;
391
+ std::string strPath = path.string ();
392
+ if (run_startup_script (ctx, strPath.data (), strPath.size ()) == REDISMODULE_ERR)
393
+ {
394
+ RedisModule_Log (ctx, " warning" , " failed to run bootstrap.js, ensure this is located in the same location as the .so" );
395
+ return REDISMODULE_ERR;
396
+ }
319
397
}
320
-
321
- file.seekg (0 , std::ios::beg);
322
-
323
- std::vector<char > buffer (size);
324
- if (!file.read (buffer.data (), size))
398
+ else
325
399
{
326
- RedisModule_Log (ctx, " warning" , " failed to read startup script" );
327
- return REDISMODULE_ERR; // Failed to read file
400
+ RedisModule_Log (ctx, " warning" , " failed to locate bootstrap script" );
401
+ return REDISMODULE_ERR;
328
402
}
403
+ }
329
404
330
- v8::HandleScope scope (g_jscontext->getIsolate ());
331
- try
332
- {
333
- g_ctx = ctx;
334
- g_jscontext->run (buffer.data (), buffer.size (), true /* don't cache */ );
335
- g_ctx = nullptr ;
336
- }
337
- catch (std::string str)
338
- {
339
- RedisModule_Log (ctx, " warning" , " %s" , str.c_str ());
405
+ for (int iarg = 0 ; iarg < argc; ++iarg)
406
+ {
407
+ // Process the startup script
408
+ size_t cchPath;
409
+ const char *rgchPath = RedisModule_StringPtrLen (argv[iarg], &cchPath);
410
+
411
+ if (run_startup_script (ctx, rgchPath, cchPath) == REDISMODULE_ERR)
340
412
return REDISMODULE_ERR;
341
- }
342
413
}
343
414
415
+ g_fInStartup = false ;
344
416
return REDISMODULE_OK;
345
417
}
0 commit comments