LCOV - code coverage report
Current view: top level - src/plugins/ruby - ruby.cpp (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 177 235 75.3 %
Date: 2022-05-21 16:19:22 Functions: 18 21 85.7 %

          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             : }

Generated by: LCOV version 1.13