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
|