Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief
5 : *
6 : * @copyright BSD License (see doc/LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 :
10 : #ifndef SWIG_TYPE_TABLE
11 : #error Build system error, SWIG_TYPE_TABLE is not defined
12 : #endif
13 :
14 : #include <ruby.h>
15 :
16 : #ifndef HAVE_KDBCONFIG
17 : #include <kdbconfig.h>
18 : #endif
19 :
20 : #include <kdbmacros.h>
21 :
22 : #include SWIG_RUNTIME
23 : #include "ruby.hpp"
24 :
25 : #include <key.hpp>
26 : #include <keyset.hpp>
27 :
28 : /* for global variable access */
29 : #include <mutex>
30 :
31 : using namespace ckdb;
32 : #include <kdbassert.h>
33 : #include <kdberrors.h>
34 : #include <kdblogger.h>
35 :
36 : #include <stdarg.h>
37 :
38 : typedef struct
39 : {
40 : VALUE rbInstance = Qnil;
41 :
42 : } moduleData;
43 :
44 :
45 : /*
46 : * CPP helper function to create new Ruby objects
47 : */
48 :
49 : /* create a new Ruby Kdb::Key object from the given ckdb::Key
50 : * will be deleted by the Ruby gc. */
51 16 : static inline VALUE newRubyObject (ckdb::Key * key)
52 : {
53 32 : return SWIG_NewPointerObj (new kdb::Key (key), SWIG_TypeQuery ("kdb::Key *"), 1);
54 : }
55 :
56 : /* create a new Ruby object and take ownership of this object, thus given keySet will be deleted by
57 : * the Ruby gc. */
58 19 : static inline VALUE newRubyObject (kdb::KeySet * keySet)
59 : {
60 19 : return SWIG_NewPointerObj (keySet, SWIG_TypeQuery ("kdb::KeySet *"), 1);
61 : }
62 :
63 : /*
64 : * Plugin global variables
65 : *
66 : * Although global variables are not allowed (and are not of good style)
67 : * this is required here.
68 : * Write access to these variables is guarded by a mutex
69 : */
70 :
71 : /* Plugin instance created by the Ruby-plugin */
72 : static VALUE global_plugin_instance = Qnil;
73 :
74 : /* mutex to guard write access to global variables */
75 : static std::mutex global_context_mutex;
76 :
77 : #define CONFIG_KEY_SCRIPT "user/script"
78 :
79 : /* global ruby variable: array of plugin instances to prevent them from being gc'ed */
80 : #define RB_GLOBAL_VAR_PLUGINS "Kdb_ruby_plugins"
81 : #define RB_GLOBAL_PLUGIN_KLASS "Kdb_Plugin_klass"
82 : #define RB_GLOBAL_KDB_MODULE "Kdb_Module_klass"
83 :
84 : extern "C" {
85 :
86 :
87 : /**
88 : * @brief generate string representation of exception
89 : */
90 3 : static VALUE get_exception_string (VALUE exception)
91 : {
92 :
93 : /* get backtrace array and join it to a string with '\n ' */
94 3 : ID bt_id = rb_intern ("backtrace");
95 3 : if (rb_respond_to (exception, bt_id))
96 : {
97 3 : VALUE backtrace = rb_funcall (exception, bt_id, 0);
98 3 : backtrace = rb_ary_join (backtrace, rb_str_new_cstr ("\n "));
99 :
100 3 : return rb_sprintf ("Ruby Exception: %" PRIsVALUE ": %" PRIsVALUE " \n %" PRIsVALUE, CLASS_OF (exception), exception,
101 3 : backtrace);
102 : }
103 : else
104 : {
105 0 : return rb_sprintf ("Ruby Exception: %" PRIsVALUE ": %" PRIsVALUE, CLASS_OF (exception), exception);
106 : }
107 : }
108 :
109 : /**
110 : * @brief clear ruby exception, log and return it
111 : *
112 : * this method should be called directly after rb_protect (or similar function), iff
113 : * that call issued an exception. This will clear the exception and log a warning
114 : * message.
115 : * (inline, to have better logs)
116 : */
117 : static inline VALUE clear_ruby_exception ()
118 : {
119 3 : VALUE exception = rb_errinfo ();
120 3 : rb_set_errinfo (Qnil);
121 :
122 : #ifdef HAVE_LOGGER
123 : VALUE msg = get_exception_string (exception);
124 : ELEKTRA_LOG_WARNING ("%s", StringValueCStr (msg));
125 : #endif
126 : return exception;
127 : }
128 :
129 : /**
130 : * @brief additional to 'clear_ruby_exception()' adds exception msg to warningsKey
131 : *
132 : */
133 2 : static VALUE clear_ruby_exception_add_warning (ckdb::Key * warningsKey)
134 : {
135 2 : VALUE exception = clear_ruby_exception ();
136 2 : VALUE msg = get_exception_string (exception);
137 :
138 2 : ELEKTRA_ADD_PLUGIN_MISBEHAVIOR_WARNING (warningsKey, StringValueCStr (msg));
139 :
140 2 : return exception;
141 : }
142 :
143 1 : static VALUE clear_ruby_exception_set_error (ckdb::Key * errorKey)
144 : {
145 1 : VALUE exception = clear_ruby_exception ();
146 1 : VALUE msg = get_exception_string (exception);
147 :
148 1 : ELEKTRA_SET_PLUGIN_MISBEHAVIOR_ERROR (errorKey, StringValueCStr (msg));
149 :
150 1 : return exception;
151 : }
152 :
153 : #define RUBY_INT_OR_DEFAULT(x) (RB_TYPE_P (x, T_FIXNUM) ? NUM2INT (x) : 1)
154 :
155 : /*
156 : * Ruby instance method call mechanism with exception handling
157 : */
158 : /* maximum number of arguments (avoids allocating VALUE array for rb_funcall2) */
159 : #define MAX_ARGS 3
160 :
161 : /**
162 : * @brief wrapper function to be called by rb_protect
163 : *
164 : * takes a Ruby array with arguments, method id and instance (in that order)
165 : * to be called.
166 : */
167 16 : static VALUE protected_ruby_call_wrapper (VALUE args)
168 : {
169 16 : VALUE instance = rb_ary_pop (args);
170 16 : ID method = SYM2ID (rb_ary_pop (args));
171 :
172 16 : const int n = RARRAY_LEN (args) > MAX_ARGS ? MAX_ARGS : RARRAY_LEN (args);
173 : VALUE _args[MAX_ARGS];
174 41 : for (int i = n - 1; 0 <= i; i--)
175 : {
176 25 : _args[i] = rb_ary_pop (args);
177 : }
178 :
179 16 : return rb_funcall2 (instance, method, n, _args);
180 : }
181 :
182 : /**
183 : * @brief wrapper function to rb_protect with variable argument list
184 : *
185 : * calls method 'method' of Ruby instance 'instance' with given arguments, whereas
186 : * 'nargs' defines the number of given arguments (all arguments have to be of type
187 : * 'VALUE').
188 : * 'state' will be set to a non-zero value, iff an exception was thrown (compare
189 : * rb_protect), otherwise 'state' will be set to 0.
190 : */
191 16 : static VALUE my_rb_protect (VALUE instance, ID method, int * state, int nargs, ...)
192 : {
193 : va_list ap;
194 16 : int i = 0;
195 : VALUE array;
196 :
197 : ELEKTRA_LOG_DEBUG ("call Plugin.%s with %d argument%s", rb_id2name (method), nargs, nargs > 1 ? "s" : "");
198 16 : ELEKTRA_ASSERT (nargs <= MAX_ARGS, "max number of arguments for wrapped ruby call reached, increase MAX_ARGS");
199 :
200 16 : array = rb_ary_new2 (nargs + 2); // num arguments + instance + method
201 :
202 16 : va_start (ap, nargs);
203 41 : for (i = 0; i < nargs; i++)
204 : {
205 25 : rb_ary_push (array, va_arg (ap, VALUE));
206 : }
207 16 : va_end (ap);
208 :
209 16 : rb_ary_push (array, ID2SYM (method));
210 16 : rb_ary_push (array, instance);
211 :
212 16 : return rb_protect (protected_ruby_call_wrapper, array, state);
213 : }
214 :
215 : static VALUE rb_kdb_plugin_define (VALUE self ELEKTRA_UNUSED, VALUE name);
216 :
217 : /**
218 : * @brief: define the Kdb::Plugin class
219 : */
220 10 : static VALUE define_kdb_plugin_class ()
221 : {
222 10 : VALUE module = Qnil;
223 10 : VALUE klass = Qnil;
224 :
225 10 : module = rb_define_module ("Kdb");
226 10 : if (!rb_const_defined (rb_cObject, rb_intern (RB_GLOBAL_KDB_MODULE)))
227 : {
228 1 : rb_define_const (rb_cObject, RB_GLOBAL_KDB_MODULE, module);
229 : }
230 :
231 10 : klass = rb_define_class_under (module, "Plugin", rb_cObject);
232 10 : if (!rb_const_defined (rb_cObject, rb_intern (RB_GLOBAL_PLUGIN_KLASS)))
233 : {
234 1 : rb_define_const (rb_cObject, RB_GLOBAL_PLUGIN_KLASS, klass);
235 : }
236 :
237 10 : rb_define_singleton_method (klass, "define", ((VALUE (*) (...)) rb_kdb_plugin_define), 1);
238 10 : return klass;
239 : }
240 :
241 :
242 : /**
243 : * @brief Kdb::Plugin.define(name): called by Ruby-plugin code to define a new plugin
244 : *
245 : * create a new Ruby-plugin
246 : */
247 7 : static VALUE rb_kdb_plugin_define (VALUE self, VALUE name)
248 : {
249 :
250 7 : if (RB_TYPE_P (name, T_SYMBOL))
251 : {
252 : // sym2name() not in 1.9
253 7 : name = rb_funcall (name, rb_intern ("to_s"), 0);
254 : }
255 :
256 : ELEKTRA_LOG ("creating new Ruby plugin plugin '%s'", StringValueCStr (name));
257 :
258 7 : VALUE instance = Qnil;
259 :
260 7 : instance = rb_funcall (self, rb_intern ("new"), 0);
261 7 : if (rb_block_given_p ())
262 : {
263 : /* call the given block in the context of the newly created instance */
264 7 : VALUE block = rb_block_proc ();
265 7 : rb_funcall_with_block (instance, rb_intern ("instance_eval"), 0, NULL, block);
266 : }
267 : else
268 : {
269 0 : rb_raise (rb_eArgError, "a block is required");
270 : }
271 :
272 7 : rb_iv_set (instance, "@plugin_name", name);
273 :
274 : /* store this plugin instance in our global variable */
275 7 : global_plugin_instance = instance;
276 : ELEKTRA_LOG_DEBUG ("Plugin called Kdb::Plugin.define, name: %s\n", StringValueCStr (name));
277 :
278 7 : return Qnil;
279 : }
280 :
281 : /* ensure this Ruby instance is not garbage collected
282 : * we simply put them in a global constant array, so the GC finds them and doesn't delete them
283 : */
284 7 : static void add_plugin_instance (VALUE instance)
285 : {
286 : ELEKTRA_LOG_DEBUG ("adding plugin instance to global plugins array: %ld", instance);
287 7 : VALUE ary = rb_const_get (rb_cObject, rb_intern (RB_GLOBAL_VAR_PLUGINS));
288 7 : rb_ary_push (ary, instance);
289 7 : }
290 :
291 : /* remove this instance from our global plugins array, now this instance can be GCed */
292 : // static void remove_plugin_instance (VALUE instance)
293 : // {
294 : // ELEKTRA_LOG_DEBUG ("removing plugin instance to global plugins array: %ld", instance);
295 : // VALUE ary = rb_const_get (rb_cObject, rb_intern (RB_GLOBAL_VAR_PLUGINS));
296 : // rb_funcall (ary, rb_intern ("delete"), 1, instance);
297 : // }
298 :
299 :
300 27 : static VALUE require_kdb (VALUE v ELEKTRA_UNUSED)
301 : {
302 27 : rb_require ("kdb");
303 27 : return Qnil;
304 : }
305 :
306 :
307 27 : static int init_ruby_environment (ckdb::Key * warningsKey)
308 : {
309 : /*
310 : * init and start Ruby-VM
311 : * does nothing, if it is already running
312 : */
313 : ELEKTRA_LOG ("init and start Ruby-VM");
314 27 : if (ruby_setup ())
315 : {
316 0 : ELEKTRA_ADD_INSTALLATION_WARNING (warningsKey, "Could not initialize Ruby-VM");
317 : return -1;
318 : }
319 :
320 : /* if the global constant TMP_RUBY_PREFIX is already defined
321 : * ruby_init_loadpath() was already called */
322 27 : if (!rb_const_defined (rb_cObject, rb_intern ("TMP_RUBY_PREFIX")))
323 : {
324 : ELEKTRA_LOG ("init Ruby environment");
325 :
326 15 : ruby_init_loadpath ();
327 :
328 : /* NOT REQUIRED HERE -- define Plugin class */
329 : // VALUE klass_Plugin = define_kdb_plugin_class();
330 : // rb_require ("kdb");
331 :
332 : /* define our global plugins array: here we collect all active ruby plugin instances */
333 15 : if (!rb_const_defined (rb_cObject, rb_intern (RB_GLOBAL_VAR_PLUGINS)))
334 : {
335 15 : rb_define_const (rb_cObject, RB_GLOBAL_VAR_PLUGINS, rb_ary_new ());
336 : }
337 : }
338 :
339 27 : int state = 0;
340 27 : rb_protect (require_kdb, Qnil, &state);
341 27 : if (state)
342 : {
343 0 : ELEKTRA_ADD_INSTALLATION_WARNING (warningsKey, "Could not load Ruby module 'kdb'");
344 : return -1;
345 : }
346 :
347 : return 1;
348 : }
349 :
350 10 : static VALUE load_ruby_plugin (VALUE config ELEKTRA_UNUSED)
351 : {
352 :
353 10 : kdb::KeySet * conf = nullptr;
354 : /* get kdb::KeySet pointer from Ruby object */
355 10 : if (SWIG_ConvertPtr (config, (void **) &conf, SWIG_TypeQuery ("kdb::KeySet *"), 0) == -1)
356 : {
357 : /* failed to get pointer */
358 : ELEKTRA_LOG_WARNING ("could not convert plugin config");
359 : return Qnil;
360 : }
361 :
362 :
363 : /* check if user supplied a plugin script,
364 : * do not issue an error here, otherwise a 'kdb info ruby' will print a lot of error messages
365 : */
366 50 : kdb::Key script_key = conf->lookup (CONFIG_KEY_SCRIPT);
367 10 : if (!script_key)
368 : {
369 : // don't be too verbose here
370 : // ELEKTRA_LOG_WARNING("no 'script' plugin config defined");
371 : return Qnil;
372 : }
373 :
374 10 : std::string script = script_key->getString ();
375 :
376 : ELEKTRA_LOG ("load Ruby-plugin '%s'", script.c_str ());
377 :
378 10 : VALUE rb_script = rb_str_new_cstr (script.c_str ());
379 :
380 : /* be sure we have the Kdb::Plugin class with method 'define' */
381 10 : define_kdb_plugin_class ();
382 : /* load the given script */
383 10 : rb_load (rb_script, 0);
384 :
385 8 : return Qnil;
386 : }
387 :
388 : /*
389 : *
390 : * Elektra plugin API functions
391 : *
392 : */
393 :
394 :
395 0 : int RUBY_PLUGIN_FUNCTION (CheckConf) (ckdb::Key * errorKey, ckdb::KeySet * conf)
396 : {
397 :
398 : ELEKTRA_LOG_DEBUG ("ruby plugin checkConf");
399 :
400 : /*
401 : * check if given Ruby plugin script exists, done by
402 : * - try to load the plugin
403 : * - if the plugin defines a 'check_conf', pass the check to the plugin
404 : */
405 0 : if (!ksLookupByName (conf, CONFIG_KEY_SCRIPT, 0))
406 : {
407 : /* no script specified
408 : * do not issue an error or 'kdb info ruby' causes problems */
409 0 : ELEKTRA_SET_INTERFACE_ERROR (errorKey, "No 'script' config value specified");
410 : return -1;
411 : }
412 :
413 0 : VALUE config_instance = Qnil;
414 : /*
415 : * create fresh keySet, since the kdb::KeySet takes ownership and deletes the ks
416 : * once the RubyVM GC deletes the config object
417 : */
418 0 : VALUE config = newRubyObject (new kdb::KeySet (ksDup (conf)));
419 :
420 0 : global_context_mutex.lock ();
421 0 : global_plugin_instance = Qnil;
422 :
423 : int state;
424 0 : rb_protect (load_ruby_plugin, config, &state);
425 0 : if (state)
426 : {
427 0 : global_context_mutex.unlock ();
428 0 : clear_ruby_exception_set_error (errorKey);
429 :
430 : return -1;
431 : }
432 :
433 0 : if (global_plugin_instance == Qnil)
434 : {
435 0 : ELEKTRA_SET_PLUGIN_MISBEHAVIOR_ERROR (errorKey, "Invalid Ruby plugin. Plugin did not call Kdb::Plugin.define");
436 :
437 : global_context_mutex.unlock ();
438 : return -1;
439 : }
440 :
441 0 : config_instance = global_plugin_instance;
442 0 : global_context_mutex.unlock ();
443 :
444 : /* check if plugin has a 'check_conf' method and call it */
445 0 : ID mConfCheck = rb_intern ("check_conf");
446 0 : if (rb_respond_to (config_instance, mConfCheck))
447 : {
448 0 : int exception = 0;
449 0 : VALUE ret = my_rb_protect (config_instance, mConfCheck, &exception, 2, newRubyObject (errorKey), config);
450 0 : if (exception)
451 : {
452 0 : clear_ruby_exception_set_error (errorKey);
453 : return -1;
454 : }
455 0 : return RUBY_INT_OR_DEFAULT (ret);
456 : }
457 :
458 : /* its OK, if plugin has no 'check_conf' method */
459 : return 0;
460 : }
461 :
462 27 : int RUBY_PLUGIN_FUNCTION (Open) (ckdb::Plugin * handle, ckdb::Key * warningsKey)
463 : {
464 : /*
465 : * parse plugin config settings
466 : */
467 : ELEKTRA_LOG_DEBUG ("ruby plugin open");
468 :
469 : /*
470 : * setup data structure and start Ruby VM
471 : */
472 27 : global_context_mutex.lock ();
473 :
474 27 : if (init_ruby_environment (warningsKey) != 1)
475 : {
476 : global_context_mutex.unlock ();
477 : ELEKTRA_LOG_WARNING ("could not init ruby environment");
478 : return 0;
479 : }
480 :
481 27 : ckdb::KeySet * conf_ks = elektraPluginGetConfig (handle);
482 27 : if (!ksLookupByName (conf_ks, CONFIG_KEY_SCRIPT, 0))
483 : {
484 : /* no script specified
485 : * do not issue an error or 'kdb info ruby' causes problems */
486 : global_context_mutex.unlock ();
487 : ELEKTRA_LOG_DEBUG ("no 'script' config option specified");
488 : return 0;
489 : }
490 : /*
491 : * create fresh keySet, since the kdb::KeySet takes ownership and deletes the ks
492 : * once the RubyVM GC deletes the config object
493 : */
494 20 : VALUE config = newRubyObject (new kdb::KeySet (ksDup (conf_ks)));
495 :
496 10 : global_plugin_instance = Qnil;
497 : int state;
498 10 : rb_protect (load_ruby_plugin, config, &state);
499 10 : if (state)
500 : {
501 2 : global_context_mutex.unlock ();
502 2 : clear_ruby_exception_add_warning (warningsKey);
503 : ELEKTRA_LOG_DEBUG ("failed to load the ruby plugin");
504 :
505 : /* if we return -1, the module is unloaded and the Ruby VM crashes :( */
506 : return 0;
507 : }
508 :
509 :
510 8 : if (global_plugin_instance == Qnil)
511 : {
512 1 : global_context_mutex.unlock ();
513 :
514 : /* error, the Ruby-plugin did not call Kdb::Plugin.define
515 : * so we do not have a Plugin instance */
516 1 : ELEKTRA_ADD_PLUGIN_MISBEHAVIOR_WARNING (warningsKey, "Error in Ruby-plugin, didn't call Kdb::Plugin.define");
517 :
518 : return 0;
519 : }
520 :
521 : ELEKTRA_LOG_DEBUG ("have new Ruby-plugin: %s", rb_obj_classname (global_plugin_instance));
522 :
523 : /*
524 : * store data in plugin handle
525 : */
526 7 : moduleData * data = new moduleData ();
527 7 : data->rbInstance = global_plugin_instance;
528 7 : add_plugin_instance (data->rbInstance);
529 :
530 7 : global_context_mutex.unlock ();
531 :
532 7 : elektraPluginSetData (handle, data);
533 : /*
534 : * pass open call to ruby plugin class
535 : */
536 7 : ID mOpen = rb_intern ("open");
537 7 : if (rb_respond_to (data->rbInstance, mOpen))
538 : {
539 7 : int exception = 0;
540 7 : VALUE ret = my_rb_protect (data->rbInstance, mOpen, &exception, 1, newRubyObject (warningsKey));
541 7 : if (exception)
542 : {
543 0 : clear_ruby_exception_add_warning (warningsKey);
544 : ELEKTRA_LOG_WARNING ("exception during Plugin.open");
545 : // TODO
546 : return 0;
547 : }
548 7 : return RUBY_INT_OR_DEFAULT (ret);
549 : }
550 :
551 : return 0;
552 : }
553 :
554 :
555 26 : int RUBY_PLUGIN_FUNCTION (Close) (ckdb::Plugin * handle, ckdb::Key * warningsKey)
556 : {
557 26 : int returnValue = 0;
558 :
559 : ELEKTRA_LOG_DEBUG ("ruby plugin close");
560 :
561 : /*
562 : * first pass call to ruby plugin
563 : */
564 26 : moduleData * data = static_cast<moduleData *> (elektraPluginGetData (handle));
565 26 : ID method = rb_intern ("close");
566 26 : int state = 0;
567 26 : VALUE ret = Qnil;
568 :
569 : // if this plugin is used via the ruby bindings, the Ruby VM crashes during
570 : // 'finalize' here. Maybe we can't all the plugin.close method any more ???
571 : //
572 : // if (data != nullptr && rb_respond_to(data->rbInstance, method)) {
573 : // VALUE msg = get_exception_string(data->rbInstance);
574 : // printf("at clone we have instance: %s\n", StringValueCStr(msg));
575 : // ret = my_rb_protect(data->rbInstance, method, &state, 1,
576 : // newRubyObject(warningsKey)
577 : // );
578 : // remove_plugin_instance(data->rbInstance);
579 : // if (state) {
580 : // clear_ruby_exception_add_warning(warningsKey);
581 :
582 : // returnValue = -1;
583 : // goto free_and_clear;
584 : // }
585 :
586 : // returnValue = RUBY_INT_OR_DEFAULT(ret);
587 : // goto free_and_clear;
588 : // }
589 : //
590 : // free_and_clear:
591 : /*
592 : * delete plugin data structure
593 : */
594 26 : delete data;
595 :
596 : /* TODO
597 : * if we can determine, that this RubyVM is the only one, destroy it, otherwise leave it
598 : */
599 26 : return returnValue;
600 : }
601 :
602 :
603 21 : int RUBY_PLUGIN_FUNCTION (Get) (ckdb::Plugin * handle, ckdb::KeySet * returned, ckdb::Key * parentKey)
604 : {
605 : ELEKTRA_LOG_DEBUG ("ruby plugin get");
606 : /* TODO
607 : * how should we proceed here?
608 : * shall we get these values from the according ruby plugin ???
609 : * or just add static values from this 'static' plugin ???
610 : */
611 :
612 : #define _MODULE_CONFIG_PATH "system/elektra/modules/" RUBY_PLUGIN_NAME_STR
613 21 : if (!strcmp (keyName (parentKey), _MODULE_CONFIG_PATH))
614 : {
615 : KeySet * n;
616 : ksAppend (returned,
617 16 : n = ksNew (30, keyNew (_MODULE_CONFIG_PATH, KEY_VALUE, "Ruby plugin", KEY_END),
618 : keyNew (_MODULE_CONFIG_PATH "/exports", KEY_END),
619 : keyNew (_MODULE_CONFIG_PATH "/exports/get", KEY_FUNC, RUBY_PLUGIN_FUNCTION (Get), KEY_END),
620 : keyNew (_MODULE_CONFIG_PATH "/exports/set", KEY_FUNC, RUBY_PLUGIN_FUNCTION (Set), KEY_END),
621 : keyNew (_MODULE_CONFIG_PATH "/exports/error", KEY_FUNC, RUBY_PLUGIN_FUNCTION (Error), KEY_END),
622 : keyNew (_MODULE_CONFIG_PATH "/exports/open", KEY_FUNC, RUBY_PLUGIN_FUNCTION (Open), KEY_END),
623 : keyNew (_MODULE_CONFIG_PATH "/exports/close", KEY_FUNC, RUBY_PLUGIN_FUNCTION (Close), KEY_END),
624 : keyNew (_MODULE_CONFIG_PATH "/exports/checkconf", KEY_FUNC, RUBY_PLUGIN_FUNCTION (CheckConf), KEY_END),
625 : #include ELEKTRA_README
626 16 : keyNew (_MODULE_CONFIG_PATH "/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END));
627 16 : ksDel (n);
628 : return 0;
629 : }
630 :
631 : /*
632 : * pass call to Ruby plugin
633 : */
634 5 : moduleData * data = static_cast<moduleData *> (elektraPluginGetData (handle));
635 5 : ID method = rb_intern ("get");
636 5 : int state = 0;
637 5 : VALUE ret = Qnil;
638 :
639 5 : if (data != nullptr && rb_respond_to (data->rbInstance, method))
640 : {
641 : // this keySet will be deleted by the Ruby GC
642 10 : kdb::KeySet * wrap_returned = new kdb::KeySet (returned);
643 5 : ret = my_rb_protect (data->rbInstance, method, &state, 2, newRubyObject (wrap_returned), newRubyObject (parentKey));
644 : // release ks ownership
645 5 : wrap_returned->release ();
646 5 : if (state)
647 : {
648 1 : clear_ruby_exception_set_error (parentKey);
649 : return -1;
650 : }
651 4 : return RUBY_INT_OR_DEFAULT (ret);
652 : }
653 : else
654 : {
655 : /* if not 'get' method is available, this plugin is useless, therefore set and error */
656 0 : ELEKTRA_SET_RESOURCE_ERROR (parentKey, "Plugin does not have a 'get' method");
657 : return -1;
658 : }
659 : return -1;
660 : }
661 :
662 4 : int RUBY_PLUGIN_FUNCTION (Set) (ckdb::Plugin * handle, ckdb::KeySet * returned, ckdb::Key * parentKey)
663 : {
664 : /*
665 : * pass call to Ruby plugin
666 : */
667 4 : moduleData * data = static_cast<moduleData *> (elektraPluginGetData (handle));
668 4 : ID method = rb_intern ("set");
669 4 : int state = 0;
670 4 : VALUE ret = Qnil;
671 :
672 4 : if (data != nullptr && rb_respond_to (data->rbInstance, method))
673 : {
674 8 : kdb::KeySet * wrap_returned = new kdb::KeySet (returned);
675 4 : ret = my_rb_protect (data->rbInstance, method, &state, 2, newRubyObject (wrap_returned), newRubyObject (parentKey));
676 4 : wrap_returned->release ();
677 4 : if (state)
678 : {
679 0 : clear_ruby_exception_set_error (parentKey);
680 : return -1;
681 : }
682 4 : return RUBY_INT_OR_DEFAULT (ret);
683 : }
684 : /* no plugin data or method not implemented */
685 : return -1;
686 : }
687 :
688 0 : int RUBY_PLUGIN_FUNCTION (Error) (ckdb::Plugin * handle, ckdb::KeySet * returned, ckdb::Key * parentKey)
689 : {
690 : /*
691 : * pass call to Ruby plugin
692 : */
693 0 : moduleData * data = static_cast<moduleData *> (elektraPluginGetData (handle));
694 0 : ID method = rb_intern ("error");
695 0 : int state = 0;
696 0 : VALUE ret = Qnil;
697 :
698 0 : if (data != nullptr && rb_respond_to (data->rbInstance, method))
699 : {
700 0 : kdb::KeySet * wrap_returned = new kdb::KeySet (returned);
701 0 : ret = my_rb_protect (data->rbInstance, method, &state, 2, newRubyObject (wrap_returned), newRubyObject (parentKey));
702 0 : wrap_returned->release ();
703 0 : if (state)
704 : {
705 0 : clear_ruby_exception_add_warning (parentKey);
706 : return -1;
707 : }
708 0 : return RUBY_INT_OR_DEFAULT (ret);
709 : }
710 : return -1;
711 : }
712 :
713 :
714 27 : ckdb::Plugin * ELEKTRA_PLUGIN_EXPORT
715 : {
716 : // clang-format off
717 : return elektraPluginExport(RUBY_PLUGIN_NAME_STR,
718 : ELEKTRA_PLUGIN_OPEN, &RUBY_PLUGIN_FUNCTION(Open),
719 : ELEKTRA_PLUGIN_CLOSE, &RUBY_PLUGIN_FUNCTION(Close),
720 : ELEKTRA_PLUGIN_GET, &RUBY_PLUGIN_FUNCTION(Get),
721 : ELEKTRA_PLUGIN_SET, &RUBY_PLUGIN_FUNCTION(Set),
722 : ELEKTRA_PLUGIN_ERROR, &RUBY_PLUGIN_FUNCTION(Error),
723 27 : ELEKTRA_PLUGIN_END);
724 : }
725 : }
|