LCOV - code coverage report
Current view: top level - src/libs/tools/src - plugin.cpp (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 184 244 75.4 %
Date: 2019-09-12 12:28:41 Functions: 15 22 68.2 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Implementation of plugin
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  *
       8             :  */
       9             : 
      10             : 
      11             : #include <kdb.hpp>
      12             : 
      13             : #include <helper/keyhelper.hpp>
      14             : #include <kdb.h>
      15             : #include <kdbmodule.h>
      16             : #include <kdbplugin.h>
      17             : #include <kdbprivate.h> // currently needed for plugin handling (struct _Plugin)
      18             : #include <plugindatabase.hpp>
      19             : 
      20             : #include <algorithm>
      21             : #include <set>
      22             : 
      23             : #include <plugin.hpp>
      24             : 
      25             : // for stdout
      26             : #include <stdio.h>
      27             : 
      28             : using namespace std;
      29             : 
      30             : namespace kdb
      31             : {
      32             : 
      33             : namespace tools
      34             : {
      35             : 
      36       61748 : Plugin::Plugin (PluginSpec const & spec_, KeySet & modules) : spec (spec_), firstRef (true)
      37             : {
      38       30856 :         Key errorKey;
      39       92568 :         plugin = ckdb::elektraPluginOpen (spec.getName ().c_str (), modules.getKeySet (), spec.getConfig ().dup (), *errorKey);
      40             : 
      41       15428 :         if (!plugin)
      42             :         {
      43          27 :                 throw NoPlugin (errorKey);
      44             :         }
      45             : 
      46             :         // plugin->name might be different for default plugins:
      47       46257 :         if (spec.getName () != plugin->name)
      48             :         {
      49        7736 :                 spec.setRefName (spec.getName ()); // save virtual name as refname
      50       15472 :                 spec.setName (plugin->name);       // use actual name
      51             :         }
      52       15419 : }
      53             : 
      54        1622 : kdb::KeySet Plugin::getConfig ()
      55             : {
      56        3244 :         return ksDup (elektraPluginGetConfig (plugin));
      57             : }
      58             : 
      59           0 : Plugin::Plugin (Plugin const & other)
      60           0 : : plugin (other.plugin), spec (other.spec), info (other.info), symbols (other.symbols), infos (other.infos), firstRef (other.firstRef)
      61             : {
      62           0 :         ++plugin->refcounter;
      63           0 : }
      64             : 
      65           0 : Plugin & Plugin::operator= (Plugin const & other)
      66             : {
      67           0 :         if (this == &other) return *this;
      68             : 
      69           0 :         uninit ();
      70             : 
      71           0 :         plugin = other.plugin;
      72           0 :         spec = other.spec;
      73           0 :         info = other.info;
      74           0 :         symbols = other.symbols;
      75           0 :         infos = other.infos;
      76           0 :         firstRef = other.firstRef;
      77             : 
      78           0 :         ++plugin->refcounter;
      79             : 
      80           0 :         return *this;
      81             : }
      82             : 
      83       77095 : Plugin::~Plugin ()
      84             : {
      85       15419 :         uninit ();
      86       15419 : }
      87             : 
      88       15419 : void Plugin::uninit ()
      89             : {
      90             :         /* ref counting will avoid closing */
      91             : 
      92       30838 :         Key errorKey;
      93       15419 :         ckdb::elektraPluginClose (plugin, errorKey.getKey ());
      94       15419 : }
      95             : 
      96       15419 : void Plugin::loadInfo ()
      97             : #if defined(__clang__)
      98             :         // We disable the undefined behavior sanitizer here, because otherwise the last line in this function produces the following error:
      99             :         // `runtime error: call to function (unknown) through pointer to incorrect function type`.
     100             :         // - See also: https://github.com/ElektraInitiative/libelektra/pull/1728
     101             :         // - TODO: Fix the undefined behavior
     102             :         __attribute__ ((no_sanitize ("undefined")))
     103             : #endif
     104             : {
     105       30838 :         Key infoKey ("system/elektra/modules", KEY_END);
     106       30838 :         infoKey.addBaseName (spec.getName ());
     107             : 
     108       15419 :         if (!plugin->kdbGet)
     109             :         {
     110           0 :                 throw MissingSymbol ("kdbGet");
     111             :         }
     112       46257 :         plugin->kdbGet (plugin, info.getKeySet (), *infoKey);
     113       15419 : }
     114             : 
     115       15419 : void Plugin::parse ()
     116             : {
     117      107933 :         Key root (std::string ("system/elektra/modules/") + spec.getName (), KEY_END);
     118             : 
     119       46257 :         Key k = info.lookup (root);
     120       15419 :         if (!k)
     121             :         {
     122           0 :                 throw PluginNoContract ();
     123             :         }
     124             : 
     125      107933 :         root.setName (std::string ("system/elektra/modules/") + spec.getName () + "/exports");
     126             : 
     127       61676 :         k = info.lookup (root);
     128             : 
     129       15419 :         if (k)
     130             :         {
     131      643414 :                 while ((k = info.next ()) && k.isBelow (root))
     132             :                 {
     133      157400 :                         symbols[k.getBaseName ()] = (*k.getFunc ());
     134             :                 }
     135             :         }
     136             : 
     137      107933 :         root.setName (std::string ("system/elektra/modules/") + spec.getName () + "/infos");
     138       61676 :         k = info.lookup (root);
     139             : 
     140       15419 :         if (k)
     141             :         {
     142     1050739 :                 while ((k = info.next ()) && k.isBelow (root))
     143             :                 {
     144      556192 :                         infos[k.getBaseName ()] = k.getString ();
     145             :                 }
     146             :         }
     147             :         else
     148             :         {
     149           0 :                 throw PluginNoInfo ();
     150             :         }
     151       15419 : }
     152             : 
     153         102 : void Plugin::check (vector<string> & warnings)
     154             : {
     155         612 :         if (infos.find ("version") == infos.end ())
     156           0 :                 warnings.push_back ("no version found");
     157         510 :         else if (infos["version"] != PLUGINVERSION)
     158           0 :                 throw VersionInfoMismatch ();
     159             : 
     160         612 :         if (infos.find ("licence") == infos.end ())
     161           0 :                 warnings.push_back ("no licence information found");
     162         510 :         else if (infos["licence"] != "BSD")
     163           0 :                 warnings.push_back ("the licence is not BSD, it might change the overall licence of your elektra installation");
     164             : 
     165         612 :         if (infos.find ("status") == infos.end ())
     166           0 :                 warnings.push_back ("no status information found");
     167             :         else
     168             :         {
     169             :                 // check if status is correct
     170         612 :                 std::string statusString = infos["status"];
     171         204 :                 std::istringstream ss (statusString);
     172         102 :                 std::string status;
     173        1498 :                 while (ss >> status)
     174             :                 {
     175         647 :                         auto it = PluginDatabase::statusMap.find (status);
     176         647 :                         if (it == PluginDatabase::statusMap.end ())
     177             :                         {
     178             :                                 char * endptr;
     179           5 :                                 const char * str = status.c_str ();
     180           5 :                                 errno = 0;
     181           5 :                                 long val = strtol (str, &endptr, 10);
     182           5 :                                 if (((errno == ERANGE && (val > INT_MAX || val < INT_MIN)) || (errno != 0 && val == 0)) || endptr == str)
     183             :                                 {
     184           0 :                                         throw WrongStatus (status);
     185             :                                 }
     186             :                         }
     187             :                 }
     188             :         }
     189             : 
     190         612 :         if (infos.find ("description") == infos.end ()) warnings.push_back ("no description of the plugin found");
     191             : 
     192         612 :         if (infos.find ("provides") == infos.end ()) warnings.push_back ("no provides information found");
     193         612 :         if (infos.find ("placements") == infos.end ())
     194             :         {
     195           0 :                 warnings.push_back ("no placements information found");
     196             :         }
     197             :         else
     198             :         {
     199         612 :                 std::string placements = infos["placements"];
     200         102 :                 if (placements.empty ())
     201             :                 {
     202           0 :                         warnings.push_back ("placements are empty");
     203             :                 }
     204             : 
     205         204 :                 std::vector<std::string> pp;
     206         510 :                 pp.push_back ("prerollback");
     207         510 :                 pp.push_back ("rollback");
     208         510 :                 pp.push_back ("postrollback");
     209         510 :                 pp.push_back ("getresolver");
     210         510 :                 pp.push_back ("pregetcache");
     211         510 :                 pp.push_back ("pregetstorage");
     212         510 :                 pp.push_back ("getstorage");
     213         510 :                 pp.push_back ("procgetstorage");
     214         510 :                 pp.push_back ("postgetstorage");
     215         510 :                 pp.push_back ("postgetcache");
     216         510 :                 pp.push_back ("setresolver");
     217         510 :                 pp.push_back ("postgetcleanup");
     218         510 :                 pp.push_back ("presetstorage");
     219         510 :                 pp.push_back ("setstorage");
     220         510 :                 pp.push_back ("presetcleanup");
     221         510 :                 pp.push_back ("precommit");
     222         510 :                 pp.push_back ("commit");
     223         510 :                 pp.push_back ("postcommit");
     224         204 :                 istringstream is (placements);
     225         102 :                 std::string placement;
     226         724 :                 while (is >> placement)
     227             :                 {
     228        1040 :                         if (std::find (pp.begin (), pp.end (), placement) == pp.end ())
     229             :                         {
     230           0 :                                 warnings.push_back ("not supported placement " + placement + " found");
     231             :                         }
     232             :                 }
     233             :         }
     234         612 :         if (infos.find ("needs") == infos.end ()) warnings.push_back ("no needs information found");
     235             : 
     236         612 :         if (infos.find ("author") == infos.end ())
     237             :         {
     238           0 :                 warnings.push_back ("no author found");
     239             :         }
     240             :         else
     241             :         {
     242         612 :                 std::string author = infos["author"];
     243         102 :                 size_t ppos = 0;
     244         102 :                 ppos = author.find ('<', ppos);
     245         102 :                 if (ppos == string::npos) warnings.push_back ("Could not find \"<\" for authors e-mail address");
     246             : 
     247         102 :                 size_t pos = 0;
     248         102 :                 pos = author.find ('@', ppos);
     249         102 :                 if (pos == string::npos) warnings.push_back ("Could not find \"@\" for authors e-mail address");
     250         102 :                 if (pos < ppos) warnings.push_back ("@ found before <");
     251             : 
     252         102 :                 size_t lpos = 0;
     253         102 :                 lpos = author.find ('>', pos);
     254         102 :                 if (lpos == string::npos) warnings.push_back ("Could not find \">\" for authors e-mail address");
     255         102 :                 if (lpos < pos) warnings.push_back ("> found before @");
     256             :         }
     257             : 
     258         204 :         std::set<func_t> checkDups;
     259         102 :         std::pair<std::set<func_t>::iterator, bool> ret;
     260         102 :         if (plugin->kdbOpen)
     261             :         {
     262         306 :                 if (symbols.find ("open") == symbols.end ())
     263           0 :                         warnings.push_back ("no open symbol exported");
     264         204 :                 else if (symbols["open"] != reinterpret_cast<func_t> (plugin->kdbOpen))
     265           0 :                         throw SymbolMismatch ("open");
     266         306 :                 ret = checkDups.insert (symbols["open"]);
     267          51 :                 if (!ret.second) throw SymbolDuplicate ("open");
     268             :         }
     269         102 :         if (plugin->kdbClose)
     270             :         {
     271         324 :                 if (symbols.find ("close") == symbols.end ())
     272           0 :                         warnings.push_back ("no close symbol exported");
     273         216 :                 else if (symbols["close"] != reinterpret_cast<func_t> (plugin->kdbClose))
     274           0 :                         throw SymbolMismatch ("close");
     275         324 :                 ret = checkDups.insert (symbols["close"]);
     276          54 :                 if (!ret.second) throw SymbolDuplicate ("close");
     277             :         }
     278         102 :         if (plugin->kdbGet)
     279             :         {
     280         612 :                 if (symbols.find ("get") == symbols.end ())
     281           0 :                         warnings.push_back ("no get symbol exported");
     282         408 :                 else if (symbols["get"] != reinterpret_cast<func_t> (plugin->kdbGet))
     283           0 :                         throw SymbolMismatch ("get");
     284         612 :                 ret = checkDups.insert (symbols["get"]);
     285         102 :                 if (!ret.second) throw SymbolDuplicate ("get");
     286             :         }
     287         102 :         if (plugin->kdbSet)
     288             :         {
     289         588 :                 if (symbols.find ("set") == symbols.end ())
     290           0 :                         warnings.push_back ("no set symbol exported");
     291         392 :                 else if (symbols["set"] != reinterpret_cast<func_t> (plugin->kdbSet))
     292           0 :                         throw SymbolMismatch ("set");
     293         588 :                 ret = checkDups.insert (symbols["set"]);
     294          98 :                 if (!ret.second) throw SymbolDuplicate ("set");
     295             :         }
     296         102 :         if (plugin->kdbCommit)
     297             :         {
     298          96 :                 if (symbols.find ("commit") == symbols.end ())
     299           0 :                         warnings.push_back ("no commit symbol exported");
     300          64 :                 else if (symbols["commit"] != reinterpret_cast<func_t> (plugin->kdbCommit))
     301           0 :                         throw SymbolMismatch ("commit");
     302          96 :                 ret = checkDups.insert (symbols["commit"]);
     303          16 :                 if (!ret.second) throw SymbolDuplicate ("commit");
     304             :         }
     305         102 :         if (plugin->kdbError)
     306             :         {
     307         198 :                 if (symbols.find ("error") == symbols.end ())
     308           0 :                         warnings.push_back ("no error symbol exported");
     309         132 :                 else if (symbols["error"] != reinterpret_cast<func_t> (plugin->kdbError))
     310           0 :                         throw SymbolMismatch ("error");
     311         198 :                 ret = checkDups.insert (symbols["error"]);
     312          33 :                 if (!ret.second) throw SymbolDuplicate ("error");
     313             :         }
     314         612 :         if (symbols.find ("open") != symbols.end ())
     315             :         {
     316          51 :                 if (!plugin->kdbOpen) throw SymbolMismatch ("open");
     317             :         }
     318         612 :         if (symbols.find ("close") != symbols.end ())
     319             :         {
     320          54 :                 if (!plugin->kdbClose) throw SymbolMismatch ("close");
     321             :         }
     322         612 :         if (symbols.find ("get") != symbols.end ())
     323             :         {
     324         102 :                 if (!plugin->kdbGet) throw SymbolMismatch ("get");
     325             :         }
     326         612 :         if (symbols.find ("set") != symbols.end ())
     327             :         {
     328          98 :                 if (!plugin->kdbSet) throw SymbolMismatch ("set");
     329             :         }
     330         612 :         if (symbols.find ("commit") != symbols.end ())
     331             :         {
     332          16 :                 if (!plugin->kdbCommit) throw SymbolMismatch ("commit");
     333             :         }
     334         612 :         if (symbols.find ("error") != symbols.end ())
     335             :         {
     336          33 :                 if (!plugin->kdbError) throw SymbolMismatch ("error");
     337             :         }
     338         102 : }
     339             : 
     340           0 : ckdb::Plugin * Plugin::operator-> ()
     341             : {
     342           0 :         return plugin;
     343             : }
     344             : 
     345      106235 : std::string Plugin::lookupInfo (std::string item, std::string section)
     346             : {
     347      212470 :         Key k ("system/elektra/modules", KEY_END);
     348      212470 :         k.addBaseName (spec.getName ());
     349      106235 :         k.addBaseName (section);
     350      106235 :         k.addBaseName (item);
     351      318705 :         Key ret = info.lookup (k);
     352             : 
     353      130070 :         if (!ret) return ""; /* TODO Lets say missing info is ok for now */
     354             : 
     355       82400 :         return ret.getString ();
     356             : }
     357             : 
     358       69034 : bool Plugin::findInfo (std::string compare, std::string item, std::string section)
     359             : {
     360      345170 :         std::string str = lookupInfo (item, section);
     361             : 
     362      138068 :         std::istringstream istr (str);
     363             : 
     364       69034 :         std::string toCheck;
     365      409266 :         while (istr >> toCheck)
     366             :         {
     367      151017 :                 if (toCheck == compare) return true;
     368             :         }
     369             :         return false;
     370             : }
     371             : 
     372        2309 : kdb::KeySet Plugin::getNeededConfig ()
     373             : {
     374        4618 :         Key neededConfigKey ("system/elektra/modules", KEY_END);
     375        4618 :         neededConfigKey.addName (spec.getName ());
     376        9236 :         neededConfigKey.addName ("config/needs");
     377             : 
     378        9236 :         KeySet d (info.dup ());
     379        9236 :         KeySet config = d.cut (neededConfigKey);
     380             : 
     381        2309 :         KeySet ret;
     382        4618 :         Key oldParent = neededConfigKey;
     383        4618 :         Key newParent ("system", KEY_END);
     384        8317 :         for (KeySet::iterator i = config.begin (); i != config.end (); ++i)
     385             :         {
     386        3475 :                 Key k (i->dup ());
     387        2085 :                 ret.append (kdb::tools::helper::rebaseKey (k, oldParent, newParent));
     388             :         }
     389        2309 :         return ret;
     390             : }
     391             : 
     392           0 : int Plugin::open (kdb::Key & errorKey)
     393             : {
     394           0 :         if (!plugin->kdbOpen)
     395             :         {
     396           0 :                 throw MissingSymbol ("kdbOpen");
     397             :         }
     398             : 
     399           0 :         return plugin->kdbOpen (plugin, errorKey.getKey ());
     400             : }
     401             : 
     402           0 : int Plugin::close (kdb::Key & errorKey)
     403             : {
     404           0 :         if (!plugin->kdbClose)
     405             :         {
     406           0 :                 throw MissingSymbol ("kdbClose");
     407             :         }
     408             : 
     409           0 :         return plugin->kdbClose (plugin, errorKey.getKey ());
     410             : }
     411             : 
     412          80 : int Plugin::get (kdb::KeySet & ks, kdb::Key & parentKey)
     413             : #if defined(__clang__)
     414             :         // See `Plugin::loadInfo`
     415             :         __attribute__ ((no_sanitize ("undefined")))
     416             : #endif
     417             : {
     418          80 :         if (!plugin->kdbGet)
     419             :         {
     420           0 :                 throw MissingSymbol ("kdbGet");
     421             :         }
     422             : 
     423         240 :         return plugin->kdbGet (plugin, ks.getKeySet (), parentKey.getKey ());
     424             : }
     425             : 
     426         834 : int Plugin::set (kdb::KeySet & ks, kdb::Key & parentKey)
     427             : #if defined(__clang__)
     428             :         // See `Plugin::loadInfo`
     429             :         __attribute__ ((no_sanitize ("undefined")))
     430             : #endif
     431             : {
     432         834 :         if (!plugin->kdbSet)
     433             :         {
     434           0 :                 throw MissingSymbol ("kdbSet");
     435             :         }
     436             : 
     437        2502 :         return plugin->kdbSet (plugin, ks.getKeySet (), parentKey.getKey ());
     438             : }
     439             : 
     440           0 : int Plugin::commit (kdb::KeySet & ks, kdb::Key & parentKey)
     441             : {
     442           0 :         if (!plugin->kdbCommit)
     443             :         {
     444           0 :                 throw MissingSymbol ("kdbCommit");
     445             :         }
     446             : 
     447           0 :         return plugin->kdbCommit (plugin, ks.getKeySet (), parentKey.getKey ());
     448             : }
     449             : 
     450           0 : int Plugin::error (kdb::KeySet & ks, kdb::Key & parentKey)
     451             : {
     452           0 :         if (!plugin->kdbError)
     453             :         {
     454           0 :                 throw MissingSymbol ("kdbError");
     455             :         }
     456             : 
     457           0 :         return plugin->kdbError (plugin, ks.getKeySet (), parentKey.getKey ());
     458             : }
     459             : 
     460             : 
     461        5042 : std::string Plugin::name () // TODO: rename + use internally
     462             : {
     463        5042 :         return spec.getName ();
     464             : }
     465             : 
     466        4468 : std::string Plugin::getFullName ()
     467             : {
     468        4468 :         return spec.getFullName ();
     469             : }
     470             : 
     471        3715 : std::string Plugin::refname ()
     472             : {
     473        3715 :         if (firstRef)
     474             :         {
     475        1616 :                 firstRef = false;
     476       14544 :                 return std::string ("#") + spec.getName () + "#" + spec.getRefName () + "#";
     477             :         }
     478             :         else
     479             :         {
     480       10495 :                 return std::string ("#") + spec.getRefName ();
     481             :         }
     482             : }
     483             : } // namespace tools
     484             : } // namespace kdb

Generated by: LCOV version 1.13