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