Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Library for invoking exported plugin functions
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : */
8 : #include <kdbassert.h>
9 : #include <kdbinvoke.h>
10 : #include <kdbmodule.h>
11 : #include <kdbprivate.h> // for elektraPluginOpen/Close
12 :
13 : #include <stdio.h>
14 :
15 : struct _ElektraInvokeHandle
16 : {
17 : Plugin * plugin;
18 : KeySet * modules;
19 : KeySet * exports;
20 : };
21 :
22 : /**
23 : * Structure for deferred calls
24 : * @internal
25 : */
26 : typedef struct _ElektraDeferredCall
27 : {
28 : char * name;
29 : KeySet * parameters;
30 : struct _ElektraDeferredCall * next;
31 : } _ElektraDeferredCall;
32 :
33 : /**
34 : * Structure for internal plugin state
35 : * @internal
36 : */
37 : struct _ElektraDeferredCallList
38 : {
39 : _ElektraDeferredCall * head;
40 : _ElektraDeferredCall * last;
41 : };
42 :
43 :
44 : /**
45 : * @defgroup invoke
46 : * @brief Functionality to use plugins and invoke functions
47 : *
48 : * Allows invoking functions of plugins as needed within applications
49 : * and plugins inside and outside of the KDB.
50 : *
51 : * To use this library, you need to include:
52 : *
53 : * @code
54 : * #include <kdbinvoke.h>`
55 : * @endcode
56 : *
57 : * and link against `libelektra-invoke`.
58 : * Then you can use it:
59 : *
60 : * @code
61 : * ElektraInvokeHandle * handle = elektraInvokeOpen ("dini", 0);
62 : * elektraInvoke2Args (handle, "get", ks, k);
63 : * elektraInvokeClose (handle);
64 : * @endcode
65 : *
66 : * @{
67 : *
68 : */
69 :
70 : /**
71 : * @deprecated Do not use.
72 : *
73 : * Use `elektraInvokeOpen (name, 0, 0)` instead.
74 : *
75 : * @see elektraInvokeOpen()
76 : */
77 0 : ElektraInvokeHandle * elektraInvokeInitialize (const char * elektraPluginName)
78 : {
79 0 : return elektraInvokeOpen (elektraPluginName, 0, 0);
80 : }
81 :
82 : /**
83 : * @brief Opens a new handle to invoke functions for a plugin.
84 : *
85 : * When opening the plugin, it calls the "open" function of the plugin.
86 : *
87 : * @param elektraPluginName the plugin on which we want to invoke functions.
88 : * @param config the config to be passed to the plugin.
89 : * @param errorKey a key where error messages will be stored
90 : *
91 : * @return the handle
92 : * @retval 0 on errors
93 : */
94 380 : ElektraInvokeHandle * elektraInvokeOpen (const char * elektraPluginName, KeySet * config, Key * errorKey)
95 : {
96 380 : if (!elektraPluginName)
97 : {
98 : return NULL;
99 : }
100 380 : ElektraInvokeHandle * handle = elektraCalloc (sizeof (struct _ElektraInvokeHandle));
101 380 : if (!handle)
102 : {
103 : return NULL;
104 : }
105 380 : KeySet * modules = ksNew (0, KS_END);
106 380 : handle->modules = modules;
107 380 : elektraModulesInit (modules, NULL);
108 :
109 380 : if (!config)
110 : {
111 231 : config = ksNew (0, KS_END);
112 : }
113 : else
114 : {
115 149 : config = ksDup (config);
116 : }
117 :
118 380 : int errorKeyMissing = !errorKey;
119 380 : if (errorKeyMissing)
120 : {
121 16 : errorKey = keyNew (0, KEY_END);
122 : }
123 :
124 380 : Plugin * plugin = elektraPluginOpen (elektraPluginName, modules, config, errorKey);
125 380 : if (errorKeyMissing)
126 : {
127 16 : keyDel (errorKey);
128 : }
129 380 : if (!plugin)
130 : {
131 0 : elektraModulesClose (modules, NULL);
132 0 : ksDel (modules);
133 0 : elektraFree (handle);
134 0 : return NULL;
135 : }
136 380 : handle->plugin = plugin;
137 380 : return handle;
138 : }
139 :
140 : /**
141 : * @brief Get a function pointer.
142 : *
143 : * @param handle the handle to use
144 : * @param elektraPluginFunctionName the name of the function to use, e.g., get/set
145 : *
146 : * @pre handle must be as returned from elektraInvokeOpen()
147 : *
148 : * Example:
149 : *
150 : * @code
151 : typedef int (*elektra2Args) (KeySet*, Key *);
152 : elektra2Args func = *(elektra2Args *)elektraInvokeGetFunction (handle, elektraPluginFunctionName);
153 : if (!func) exit(1); // no function found, handle error
154 : func (ks, k); // otherwise, call function
155 : * @endcode
156 : *
157 : * @see elektraInvoke2Args() a convenience function to be used for KeySet,Key arguments.
158 : *
159 : * @return a function pointer for the specified function.
160 : */
161 1479 : const void * elektraInvokeGetFunction (ElektraInvokeHandle * handle, const char * elektraPluginFunctionName)
162 : {
163 1479 : if (!handle || !elektraPluginFunctionName)
164 : {
165 : return NULL;
166 : }
167 1479 : Plugin * plugin = handle->plugin;
168 1479 : KeySet * exports = NULL;
169 :
170 1479 : Key * exportParent = keyNew ("system/elektra/modules", KEY_END);
171 1479 : keyAddBaseName (exportParent, plugin->name);
172 :
173 1479 : if (handle->exports)
174 : {
175 : exports = handle->exports;
176 : }
177 : else
178 : {
179 364 : exports = ksNew (0, KS_END);
180 364 : handle->exports = exports;
181 364 : plugin->kdbGet (plugin, exports, exportParent);
182 : }
183 1479 : keyAddBaseName (exportParent, "exports");
184 1479 : keyAddBaseName (exportParent, elektraPluginFunctionName);
185 1479 : Key * functionKey = ksLookup (exports, exportParent, 0);
186 1479 : keyDel (exportParent);
187 1479 : if (!functionKey)
188 : {
189 : return NULL;
190 : }
191 : else
192 : {
193 1479 : return keyValue (functionKey);
194 : }
195 : }
196 :
197 : /**
198 : * @brief Get the configuration the plugin uses.
199 : *
200 : * @param handle the handle to use
201 : *
202 : * @pre handle must be as returned from elektraInvokeOpen()
203 : *
204 : * @return the config of the plugin.
205 : */
206 0 : KeySet * elektraInvokeGetPluginConfig (ElektraInvokeHandle * handle)
207 : {
208 0 : if (!handle) return NULL;
209 0 : return handle->plugin->config;
210 : }
211 :
212 : /**
213 : * @brief Get the name of the plugin.
214 : *
215 : * The name might differ from the name as passed with elektraInvokeOpen
216 : * when symlinks are used.
217 : *
218 : * @param handle the handle to work with.
219 : *
220 : * @pre handle must be as returned from elektraInvokeOpen()
221 : *
222 : * @return the name of the plugin
223 : */
224 0 : const char * elektraInvokeGetPluginName (ElektraInvokeHandle * handle)
225 : {
226 0 : if (!handle) return NULL;
227 0 : return handle->plugin->name;
228 : }
229 :
230 : /**
231 : * @brief Get the data of the plugin.
232 : *
233 : * @param handle the handle to work with.
234 : *
235 : * @pre handle must be as returned from elektraInvokeOpen()
236 : *
237 : * @return a pointer to the plugin's data.
238 : */
239 0 : void * elektraInvokeGetPluginData (ElektraInvokeHandle * handle)
240 : {
241 0 : if (!handle) return NULL;
242 0 : return handle->plugin->data;
243 : }
244 :
245 : /**
246 : * @brief Get the modules used for invoking.
247 : *
248 : * @warning The modules are closed within elektraInvokeClose().
249 : * It is *not* enough to ksDup() the keyset, you must not call
250 : * elektraInvokeClose() if you want to reuse the modules.
251 : *
252 : * @param handle the handle to work with.
253 : *
254 : * @pre handle must be as returned from elektraInvokeOpen()
255 : *
256 : * @return the modules used for invoking.
257 : */
258 0 : KeySet * elektraInvokeGetModules (ElektraInvokeHandle * handle)
259 : {
260 0 : if (!handle) return NULL;
261 0 : return handle->modules;
262 : }
263 :
264 : /**
265 : * @brief Get the exports from the plugin.
266 : *
267 : * @param handle the handle to work with.
268 : *
269 : * @pre handle must be as returned from elektraInvokeOpen()
270 : *
271 : * @return the exports of the plugin.
272 : */
273 0 : KeySet * elektraInvokeGetExports (ElektraInvokeHandle * handle)
274 : {
275 0 : if (!handle) return NULL;
276 0 : return handle->exports;
277 : }
278 :
279 :
280 : /**
281 : * @brief A convenience function to call a function with two arguments.
282 : *
283 : * @param handle the handle to work with
284 : * @param elektraPluginFunctionName the function to call, e.g. "get"
285 : * @param ks the keyset to be used as first parameter
286 : * @param k the key to be used as second parameter
287 : *
288 : * @pre handle must be as returned from elektraInvokeOpen()
289 : *
290 : * @return the return value of the invoked function (i.e. -1, 0, or 1)
291 : * @retval -2 if the function was not found.
292 : */
293 1431 : int elektraInvoke2Args (ElektraInvokeHandle * handle, const char * elektraPluginFunctionName, KeySet * ks, Key * k)
294 : {
295 1431 : if (!handle || !elektraPluginFunctionName) return -2;
296 :
297 : // If we cast this right away although the function wasn't found it will cause a deadlock
298 1431 : const void * rawFunc = elektraInvokeGetFunction (handle, elektraPluginFunctionName);
299 :
300 1431 : if (!rawFunc) return -2;
301 :
302 : typedef int (*elektra2Args) (Plugin *, KeySet *, Key *);
303 1431 : elektra2Args func = *(elektra2Args *) rawFunc;
304 :
305 1431 : return func (handle->plugin, ks, k);
306 : }
307 :
308 : /**
309 : * @brief Closes all affairs with the handle.
310 : *
311 : * The close function of the plugin will be called.
312 : *
313 : * @param handle the handle to work with
314 : * @param errorKey a key where error messages will be stored
315 : *
316 : * @pre handle must be as returned from elektraInvokeOpen()
317 : */
318 380 : void elektraInvokeClose (ElektraInvokeHandle * handle, Key * errorKey)
319 : {
320 380 : if (!handle)
321 : {
322 : return;
323 : }
324 380 : int errorKeyMissing = !errorKey;
325 380 : if (errorKeyMissing)
326 : {
327 71 : errorKey = keyNew (0, KEY_END);
328 : }
329 380 : elektraPluginClose (handle->plugin, errorKey);
330 380 : if (errorKeyMissing)
331 : {
332 71 : keyDel (errorKey);
333 : }
334 380 : elektraModulesClose (handle->modules, NULL);
335 380 : ksDel (handle->modules);
336 380 : ksDel (handle->exports);
337 380 : elektraFree (handle);
338 : }
339 :
340 : /**
341 : * Invokes a deferable function on an invoke handle.
342 : * If the function is exported by the plugin it is directly invoked,
343 : * if the plugin supports deferring calls, the call is deferred.
344 : *
345 : * The parameters key set can be freed afterwards.
346 : *
347 : * @param handle invoke handle
348 : * @param elektraPluginFunctionName function name
349 : * @param parameters parameter key set
350 : * @retval 0 on success
351 : * @retval -1 when the call failed (direct call and deferring not available)
352 : */
353 0 : int elektraInvokeCallDeferable (ElektraInvokeHandle * handle, const char * elektraPluginFunctionName, KeySet * parameters)
354 : {
355 0 : if (!handle)
356 : {
357 : return -1;
358 : }
359 0 : return elektraDeferredCall (handle->plugin, elektraPluginFunctionName, parameters);
360 : }
361 :
362 : /**
363 : * Execute deferred calls from list on given invoke handle.
364 : *
365 : * Used internally by plugins holding invoke handles.
366 : *
367 : * @param handle invoke handle
368 : * @param list list
369 : */
370 0 : void elektraInvokeExecuteDeferredCalls (ElektraInvokeHandle * handle, ElektraDeferredCallList * list)
371 : {
372 0 : if (!handle)
373 : {
374 : return;
375 : }
376 0 : elektraDeferredCallsExecute (handle->plugin, list);
377 : }
378 :
379 : /**
380 : * Call a deferable function on a plugin handle.
381 : * If the function is exported by the plugin it is directly invoked,
382 : * if the plugin supports deferring calls, the call is deferred.
383 : * If both is possible (function is exported and deferred calls are supported),
384 : * the function is directly called and the call is deferred (i.e. for nested plugins).
385 : *
386 : * @param handle invoke handle
387 : * @param elektraPluginFunctionName function name
388 : * @param parameters parameter key set. Can bee freed afterwards.
389 : * @retval 0 on success
390 : * @retval -1 when the call failed (direct call and deferring not available)
391 : */
392 160 : int elektraDeferredCall (Plugin * handle, const char * elektraPluginFunctionName, KeySet * parameters)
393 : {
394 160 : ELEKTRA_NOT_NULL (handle);
395 160 : ELEKTRA_NOT_NULL (elektraPluginFunctionName);
396 :
397 : int result;
398 160 : size_t direct = elektraPluginGetFunction (handle, elektraPluginFunctionName);
399 160 : if (direct)
400 : {
401 0 : ElektraDeferredCallable directFn = (ElektraDeferredCallable) direct;
402 0 : directFn (handle, parameters);
403 0 : result = 0; // success
404 : }
405 : else
406 : {
407 : // no direct call possible
408 : result = -1;
409 : }
410 :
411 160 : size_t deferredCall = elektraPluginGetFunction (handle, "deferredCall");
412 160 : if (deferredCall)
413 : {
414 160 : ElektraDeferredCall deferredCallFn = (ElektraDeferredCall) deferredCall;
415 160 : deferredCallFn (handle, elektraPluginFunctionName, parameters);
416 160 : result = 0; // success
417 : }
418 : else
419 : {
420 : // deferred calls not possible
421 : result = -1;
422 : }
423 :
424 160 : return result;
425 : }
426 :
427 : /**
428 : * Add a new deferred call to the deferred call list.
429 : *
430 : * Used internally by plugins.
431 : *
432 : * @param list deferred call list
433 : * @param name function name
434 : * @param parameters function parameters
435 : * @retval 1 on success
436 : * @retval 0 when malloc failed
437 : */
438 160 : int elektraDeferredCallAdd (ElektraDeferredCallList * list, const char * name, KeySet * parameters)
439 : {
440 160 : ELEKTRA_NOT_NULL (list);
441 160 : ELEKTRA_NOT_NULL (name);
442 160 : _ElektraDeferredCall * item = elektraMalloc (sizeof *item);
443 160 : if (item == NULL)
444 : {
445 : return 0;
446 : }
447 160 : item->name = elektraStrDup (name);
448 160 : item->parameters = ksDup (parameters);
449 160 : item->next = NULL;
450 :
451 160 : if (list->head == NULL)
452 : {
453 : // Initialize list
454 6 : list->head = list->last = item;
455 : }
456 : else
457 : {
458 : // Make new item end of list
459 154 : list->last->next = item;
460 154 : list->last = item;
461 : }
462 :
463 : return 1;
464 : }
465 :
466 : /**
467 : * Create new deferred call list.
468 : *
469 : * The list needs to be deleted with elektraDeferredCallDeleteList().
470 : * Used internally by plugins.
471 : *
472 : * @return new list
473 : */
474 21064 : ElektraDeferredCallList * elektraDeferredCallCreateList (void)
475 : {
476 21064 : ElektraDeferredCallList * list = elektraMalloc (sizeof *list);
477 21064 : if (list == NULL)
478 : {
479 : return NULL;
480 : }
481 21064 : list->head = NULL;
482 21064 : list->last = NULL;
483 21064 : return list;
484 : }
485 :
486 : /**
487 : * Delete deferred call list.
488 : *
489 : * Used internally by plugins.
490 : *
491 : * @param list list
492 : */
493 21064 : void elektraDeferredCallDeleteList (ElektraDeferredCallList * list)
494 : {
495 21064 : ELEKTRA_NOT_NULL (list);
496 21064 : _ElektraDeferredCall * item = list->head;
497 42288 : while (item != NULL)
498 : {
499 160 : elektraFree (item->name);
500 160 : ksDel (item->parameters);
501 :
502 160 : _ElektraDeferredCall * next = item->next;
503 160 : elektraFree (item);
504 :
505 160 : item = next;
506 : }
507 :
508 21064 : elektraFree (list);
509 21064 : }
510 :
511 : /**
512 : * Execute deferred calls on given plugin.
513 : *
514 : * Used internally by plugins.
515 : *
516 : * @param plugin plugin handle
517 : * @param list list
518 : */
519 8083 : void elektraDeferredCallsExecute (Plugin * plugin, ElektraDeferredCallList * list)
520 : {
521 8083 : ELEKTRA_NOT_NULL (plugin);
522 8083 : ELEKTRA_NOT_NULL (list);
523 8083 : _ElektraDeferredCall * item = list->head;
524 17086 : while (item != NULL)
525 : {
526 920 : size_t func = elektraPluginGetFunction (plugin, item->name);
527 920 : if (!func)
528 : {
529 920 : item = item->next;
530 920 : continue;
531 : }
532 0 : ElektraDeferredCallable callable = (ElektraDeferredCallable) func;
533 0 : callable (plugin, item->parameters);
534 :
535 0 : item = item->next;
536 : }
537 8083 : }
538 :
539 : /**
540 : * @}
541 : */
|