LCOV - code coverage report
Current view: top level - src/plugins/ruby - ruby.cpp (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 151 192 78.6 %
Date: 2019-09-12 12:28:41 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             : 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             : }

Generated by: LCOV version 1.13