Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Interna of plugin functionality.
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : */
8 :
9 : #ifdef HAVE_KDBCONFIG_H
10 : #include "kdbconfig.h"
11 : #endif
12 :
13 : #if DEBUG && defined(HAVE_STDIO_H)
14 : #include <stdio.h>
15 : #endif
16 :
17 : #ifdef HAVE_LOCALE_H
18 : #include <locale.h>
19 : #endif
20 :
21 : #ifdef HAVE_STDLIB_H
22 : #include <stdlib.h>
23 : #endif
24 :
25 : #ifdef HAVE_STDARG_H
26 : #include <stdarg.h>
27 : #endif
28 :
29 : #ifdef HAVE_CTYPE_H
30 : #include <ctype.h>
31 : #endif
32 :
33 : #ifdef HAVE_STRING_H
34 : #include <string.h>
35 : #endif
36 :
37 : #ifdef HAVE_STDIO_H
38 : #include <stdio.h>
39 : #endif
40 :
41 : #include <kdbassert.h>
42 : #include <kdberrors.h>
43 : #include <kdbinternal.h>
44 : #include <kdbversion.h>
45 :
46 : /**
47 : * @retval 1 and an allocated string of the pluginName if a new plugins should be created.
48 : * @retval 2 and an allocated string of the referenceName if an old plugin should be used
49 : * @retval 3 and both if a new plugin should be created and made available for later
50 : * back referencing.
51 : * @retval -1 on error
52 : */
53 53785 : int elektraProcessPlugin (Key * cur, int * pluginNumber, char ** pluginName, char ** referenceName, Key * errorKey)
54 : {
55 53785 : const char * fullname = keyBaseName (cur);
56 53785 : size_t fullsize = keyGetBaseNameSize (cur);
57 :
58 53785 : if (fullname[0] != '#')
59 : {
60 2 : ELEKTRA_ADD_INSTALLATION_WARNINGF (errorKey, "Names of Plugins must start with a #. Pluginname: %s", fullname);
61 2 : return -1;
62 : }
63 53783 : if (fullname[1] < '0' || fullname[1] > '9')
64 : {
65 2 : ELEKTRA_ADD_INSTALLATION_WARNINGF (
66 : errorKey, "Names of Plugins must start with the position number as second char. Pluginname: %s", fullname);
67 2 : return -1;
68 : }
69 53781 : *pluginNumber = fullname[1] - '0';
70 53781 : if (*pluginNumber > NR_OF_PLUGINS)
71 : {
72 0 : ELEKTRA_ADD_INSTALLATION_WARNINGF (errorKey, "Tried to set more plugins than %d (NR_OF_PLUGINS). Pluginname: %s",
73 : NR_OF_PLUGINS, fullname);
74 0 : return -1;
75 : }
76 :
77 53781 : if (fullname[2] == '#')
78 : {
79 53753 : char prefixReferenceName[] = "system/elektra/plugins/";
80 :
81 : /* We have a back reference here */
82 53753 : if (fullname[fullsize - 2] == '#')
83 : {
84 23627 : const char * iter = &fullname[3];
85 23627 : size_t pluginNameSize = 1; /* For null character */
86 23627 : size_t referenceNameSize = 0;
87 : /* We will introduce a new plugin */
88 247088 : while (*iter != '#')
89 : {
90 199834 : ++iter;
91 199834 : ++pluginNameSize;
92 : }
93 :
94 23627 : *pluginName = elektraMalloc (pluginNameSize);
95 23627 : strncpy (*pluginName, &fullname[3], pluginNameSize);
96 23627 : (*pluginName)[pluginNameSize - 1] = 0;
97 :
98 23627 : referenceNameSize = fullsize - pluginNameSize - 4;
99 23627 : ++iter; /* advance to one after hash */
100 23627 : *referenceName = elektraMalloc (referenceNameSize + sizeof (prefixReferenceName));
101 23627 : strcpy (*referenceName, prefixReferenceName);
102 23627 : strncat (*referenceName, iter, referenceNameSize);
103 23627 : (*referenceName)[referenceNameSize + sizeof (prefixReferenceName) - 2] = 0;
104 :
105 23627 : return 3;
106 : }
107 : else
108 : {
109 : /* We reference back to a plugin */
110 :
111 30126 : *referenceName = elektraMalloc (fullsize - 3 + sizeof (prefixReferenceName) - 1);
112 30126 : strcpy (*referenceName, prefixReferenceName);
113 30126 : strncat (*referenceName, &fullname[3], fullsize - 3);
114 :
115 30126 : return 2;
116 : }
117 : }
118 : else
119 : {
120 28 : *pluginName = elektraMalloc (fullsize - 2); /* don't alloc for #n */
121 28 : strncpy (*pluginName, &fullname[2], fullsize - 2);
122 :
123 28 : return 1;
124 : }
125 :
126 : /* Should not be reached */
127 : return 0;
128 : }
129 :
130 : /**
131 : * Load a plugin.
132 : *
133 : * The array of plugins must be set to 0.
134 : * Its length is NR_OF_PLUGINS.
135 : *
136 : * systemConfig will only be used, not deleted.
137 : *
138 : * @param config the config with the information how the
139 : * plugins should be put together
140 : * @param systemConfig the shared (system) config for the plugins.
141 : * Every plugin additional get this config.
142 : * @param global the global keyset of the KDB instance
143 : *
144 : * @retval -1 on failure
145 : */
146 21125 : int elektraProcessPlugins (Plugin ** plugins, KeySet * modules, KeySet * referencePlugins, KeySet * config, KeySet * systemConfig,
147 : KeySet * global, Key * errorKey)
148 : {
149 : Key * root;
150 : Key * cur;
151 :
152 21125 : ksRewind (config);
153 :
154 21125 : root = ksNext (config);
155 :
156 96011 : while ((cur = ksNext (config)) != 0)
157 : {
158 53761 : if (keyRel (root, cur) == 1)
159 : {
160 53761 : char * pluginName = 0;
161 53761 : char * referenceName = 0;
162 53761 : int pluginNumber = 0;
163 :
164 53761 : if (elektraProcessPlugin (cur, &pluginNumber, &pluginName, &referenceName, errorKey) == -1)
165 : {
166 0 : elektraFree (pluginName);
167 0 : elektraFree (referenceName);
168 0 : ksDel (config);
169 0 : return -1;
170 : }
171 :
172 53761 : if (pluginName)
173 : {
174 23641 : Key * key = keyDup (cur);
175 23641 : keyAddBaseName (key, "config");
176 23641 : KeySet * cutConfig = ksCut (config, key);
177 23641 : keyDel (key);
178 :
179 23641 : KeySet * pluginConfig = elektraRenameKeys (cutConfig, "user");
180 23641 : ksDel (cutConfig);
181 23641 : if (!pluginConfig) return -1;
182 23641 : ksAppend (pluginConfig, systemConfig);
183 23641 : ksRewind (pluginConfig); /* TODO: bug ksAppend invalidates cursor */
184 :
185 : /* case 1, we create a new plugin,
186 : note that errorKey is not passed here, because it would set error information
187 : but we only want a warning instead. */
188 23641 : plugins[pluginNumber] = elektraPluginOpen (pluginName, modules, pluginConfig, errorKey);
189 23641 : if (!plugins[pluginNumber])
190 : {
191 0 : ELEKTRA_ADD_INSTALLATION_WARNINGF (errorKey, "Could not load plugin %s in process plugin",
192 : pluginName);
193 : /* Loading plugin did not work */
194 0 : elektraFree (pluginName);
195 0 : elektraFree (referenceName);
196 0 : ksDel (config);
197 0 : return -1;
198 : }
199 23641 : plugins[pluginNumber]->global = global;
200 :
201 : /* case 2, we label it for later use */
202 23641 : if (referenceName)
203 23619 : ksAppendKey (referencePlugins,
204 : keyNew (referenceName, KEY_BINARY, KEY_SIZE, sizeof (plugins[pluginNumber]), KEY_VALUE,
205 : &plugins[pluginNumber], KEY_END));
206 : }
207 : else
208 : {
209 : /* case 3, we use an existing plugin */
210 30120 : Key * lookup = ksLookup (referencePlugins, keyNew (referenceName, KEY_END), KDB_O_DEL);
211 30120 : if (!lookup)
212 : {
213 0 : ELEKTRA_ADD_INTERNAL_WARNINGF (errorKey, "Could not reference back to plugin %s", referenceName);
214 : /* Getting a reference plugin at a previous stage did not work.
215 : Note that this check is necessary, because loading the plugin could
216 : fail for example at errorplugins and at a later point, for example
217 : at setplugins it is tried to refer to that.*/
218 0 : elektraFree (referenceName);
219 0 : ksDel (config);
220 0 : return -1;
221 : }
222 30120 : plugins[pluginNumber] = *(Plugin **) keyValue (lookup);
223 30120 : ++plugins[pluginNumber]->refcounter;
224 : }
225 53761 : elektraFree (pluginName);
226 53761 : elektraFree (referenceName);
227 : }
228 : else
229 : {
230 0 : ELEKTRA_ADD_INSTALLATION_WARNINGF (errorKey, "Unknown additional entries in plugin configuration: %s",
231 : keyString (cur));
232 : }
233 : }
234 :
235 21125 : ksDel (config);
236 21125 : return 0;
237 : }
238 :
239 : /**
240 : * Opens a plugin.
241 : *
242 : * The config will be used as is. So be sure to transfer ownership
243 : * of the config to it, with e.g. ksDup().
244 : * elektraPluginClose() will delete the config.
245 : *
246 : * @return a pointer to a new created plugin or 0 on error
247 : */
248 190574 : Plugin * elektraPluginOpen (const char * name, KeySet * modules, KeySet * config, Key * errorKey)
249 : {
250 190574 : Plugin * handle = 0;
251 : const char * n;
252 :
253 190574 : elektraPluginFactory pluginFactory = 0;
254 :
255 190574 : if (!name || name[0] == '\0')
256 : {
257 4 : ELEKTRA_ADD_INSTALLATION_WARNING (errorKey, "Not a valid name supplied for a plugin: name is null or empty");
258 4 : goto err_clup;
259 : }
260 :
261 : n = name;
262 190602 : while (*n != '\0')
263 : {
264 190596 : if (*n == '/')
265 32 : ++n;
266 : else
267 : break;
268 : }
269 :
270 190570 : if (*n == '\0')
271 : {
272 6 : ELEKTRA_ADD_INSTALLATION_WARNING (errorKey, "Not a valid name supplied for a plugin: name contained slashes only");
273 6 : goto err_clup;
274 : }
275 :
276 190564 : pluginFactory = elektraModulesLoad (modules, name, errorKey);
277 190564 : if (pluginFactory == 0)
278 : {
279 : /* warning already set by elektraModulesLoad */
280 : goto err_clup;
281 : }
282 :
283 169593 : handle = pluginFactory ();
284 169593 : if (handle == 0)
285 : {
286 0 : ELEKTRA_ADD_INSTALLATION_WARNINGF (errorKey, "Could not call function exported by ELEKTRA_PLUGIN_EXPORT: %s", name);
287 0 : goto err_clup;
288 : }
289 :
290 : /* init reference counting */
291 169593 : handle->refcounter = 1;
292 169593 : handle->config = config;
293 169593 : config = 0; // for err_clup case
294 :
295 : /* let the plugin initialize itself */
296 169593 : if (handle->kdbOpen)
297 : {
298 134257 : if ((handle->kdbOpen (handle, errorKey)) == -1)
299 : {
300 15 : ELEKTRA_ADD_PLUGIN_MISBEHAVIOR_WARNINGF (
301 : errorKey,
302 : "Open of plugin returned unsuccessfully: %s. Reason contains plugin, see other warnings for details", name);
303 15 : elektraPluginClose (handle, errorKey);
304 15 : goto err_clup;
305 : }
306 : }
307 :
308 : ELEKTRA_LOG_DEBUG ("Finished loading plugin %s", name);
309 : return handle;
310 :
311 : err_clup:
312 : ELEKTRA_LOG ("Failed to load plugin %s\n", name);
313 20996 : ksDel (config);
314 20996 : return 0;
315 : }
316 :
317 3704309 : int elektraPluginClose (Plugin * handle, Key * errorKey)
318 : {
319 3704309 : int rc = 0;
320 :
321 3704309 : if (!handle) return 0;
322 :
323 441871 : --handle->refcounter;
324 :
325 : /* Check if we have the last reference on the plugin (unsigned!) */
326 441871 : if (handle->refcounter > 0) return 0;
327 :
328 180115 : if (handle->kdbClose)
329 : {
330 131054 : rc = handle->kdbClose (handle, errorKey);
331 131054 : if (rc == -1) ELEKTRA_ADD_RESOURCE_WARNING (errorKey, "Method 'kdbClose()' failed");
332 : }
333 :
334 180115 : ksDel (handle->config);
335 180115 : elektraFree (handle);
336 :
337 180115 : return rc;
338 : }
339 :
340 :
341 : /**
342 : * Retrieves a function exported by a plugin.
343 : *
344 : * @param plugin Plugin handle
345 : * @param name Function name
346 : * @return Pointer to function. NULL if function not found or not enough memory available
347 : */
348 1474 : size_t elektraPluginGetFunction (Plugin * plugin, const char * name)
349 : {
350 1474 : ELEKTRA_NOT_NULL (plugin);
351 1474 : ELEKTRA_NOT_NULL (name);
352 :
353 1474 : KeySet * exports = ksNew (0, KS_END);
354 1474 : Key * pk = keyNew ("system/elektra/modules", KEY_END);
355 1474 : keyAddBaseName (pk, plugin->name);
356 1474 : plugin->kdbGet (plugin, exports, pk);
357 1474 : ksRewind (exports);
358 1474 : keyAddBaseName (pk, "exports");
359 1474 : keyAddBaseName (pk, name);
360 1474 : Key * keyFunction = ksLookup (exports, pk, 0);
361 1474 : if (!keyFunction)
362 : {
363 : ELEKTRA_LOG_DEBUG ("function \"%s\" from plugin \"%s\" not found", name, plugin->name);
364 1080 : ksDel (exports);
365 1080 : keyDel (pk);
366 1080 : return 0;
367 : }
368 :
369 : size_t * buffer;
370 394 : size_t bufferSize = keyGetValueSize (keyFunction);
371 394 : buffer = elektraMalloc (bufferSize);
372 394 : if (buffer)
373 : {
374 394 : int result = keyGetBinary (keyFunction, buffer, bufferSize);
375 394 : if (result == -1 || buffer == NULL)
376 : {
377 : ELEKTRA_LOG_WARNING ("could not get function \"%s\" from plugin \"%s\"", name, plugin->name);
378 : return 0;
379 : }
380 : }
381 :
382 394 : size_t func = *buffer;
383 :
384 394 : elektraFree (buffer);
385 394 : ksDel (exports);
386 394 : keyDel (pk);
387 :
388 394 : return func;
389 : }
390 :
391 0 : static int elektraMissingGet (Plugin * plugin ELEKTRA_UNUSED, KeySet * ks ELEKTRA_UNUSED, Key * error)
392 : {
393 0 : ELEKTRA_SET_INSTALLATION_ERRORF (error, "Tried to get a key from a missing backend: %s", keyName (error));
394 0 : return -1;
395 : }
396 :
397 0 : static int elektraMissingSet (Plugin * plugin ELEKTRA_UNUSED, KeySet * ks ELEKTRA_UNUSED, Key * error)
398 : {
399 0 : ELEKTRA_SET_INSTALLATION_ERRORF (error, "Tried to set a key from a missing backend: %s", keyName (error));
400 0 : return -1;
401 : }
402 :
403 :
404 0 : Plugin * elektraPluginMissing (void)
405 : {
406 : Plugin * returned;
407 :
408 0 : returned = elektraCalloc (sizeof (struct _Plugin));
409 0 : if (!returned) return 0;
410 :
411 0 : returned->name = "missing";
412 0 : returned->kdbGet = elektraMissingGet;
413 0 : returned->kdbSet = elektraMissingSet;
414 0 : return returned;
415 : }
416 :
417 196 : static int elektraVersionGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned, Key * error ELEKTRA_UNUSED)
418 : {
419 196 : KeySet * info = elektraVersionKeySet ();
420 196 : keySetMeta (info->array[0], "restrict/write", "1");
421 196 : keySetMeta (info->array[0], "restrict/remove", "1");
422 2352 : for (size_t i = 1; i < info->size; i++)
423 : {
424 2156 : keyCopyAllMeta (info->array[i], info->array[0]);
425 : }
426 196 : ksAppend (returned, info);
427 196 : ksDel (info);
428 196 : return 1;
429 : }
430 :
431 4 : static int elektraVersionSet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned, Key * error)
432 : {
433 4 : KeySet * info = elektraVersionKeySet ();
434 4 : ELEKTRA_SET_ERROR_READ_ONLY (info, returned, error);
435 0 : return 0;
436 : }
437 :
438 10523 : Plugin * elektraPluginVersion (void)
439 : {
440 : Plugin * returned;
441 :
442 10523 : returned = elektraCalloc (sizeof (struct _Plugin));
443 10523 : if (!returned) return 0;
444 :
445 10523 : returned->name = "version";
446 10523 : returned->kdbGet = elektraVersionGet;
447 10523 : returned->kdbSet = elektraVersionSet;
448 10523 : return returned;
449 : }
450 :
451 : /**
452 : * Searches the global plugins for a given plugin name.
453 : *
454 : * NOTE: if the list plugin occupies the prerollback position,
455 : * this queries the list plugin first, and only if we don't find
456 : * anything there, we look directly in the global plugins array
457 : *
458 : * @param handle The KDB handle to search
459 : * @param pluginName The plugin name to look for
460 : *
461 : * @return the plugin handle, if found or NULL otherwise
462 : */
463 36 : Plugin * elektraPluginFindGlobal (KDB * handle, const char * pluginName)
464 : {
465 36 : Plugin * listPlugin = handle->globalPlugins[PREROLLBACK][MAXONCE]; // take any position
466 36 : if (listPlugin != NULL && strcmp (listPlugin->name, "list") == 0)
467 : {
468 : typedef Plugin * (*findPluginFun) (Plugin *, const char *);
469 36 : findPluginFun listFindPlugin = (findPluginFun) elektraPluginGetFunction (listPlugin, "findplugin");
470 36 : Plugin * plugin = listFindPlugin (listPlugin, pluginName);
471 36 : if (plugin != NULL)
472 : {
473 : return plugin;
474 : }
475 : }
476 :
477 266 : for (GlobalpluginPositions pos = 0; pos < NR_GLOBAL_POSITIONS; ++pos)
478 : {
479 1008 : for (GlobalpluginSubPositions sub = 0; sub < NR_GLOBAL_SUBPOSITIONS; ++sub)
480 : {
481 1008 : Plugin * plugin = handle->globalPlugins[pos][sub];
482 1008 : if (plugin != NULL && strcmp (plugin->name, pluginName) == 0)
483 : {
484 : return plugin;
485 : }
486 : }
487 : }
488 :
489 : return NULL;
490 : }
|