LCOV - code coverage report
Current view: top level - src/plugins/process - process.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 53 126 42.1 %
Date: 2019-09-12 12:28:41 Functions: 9 13 69.2 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Source for process plugin
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  */
       8             : 
       9             : #include "process.h"
      10             : 
      11             : #include <kdberrors.h>
      12             : #include <kdbhelper.h>
      13             : #include <kdbinvoke.h>
      14             : #include <kdbpluginprocess.h>
      15             : 
      16             : #include <stdio.h>
      17             : 
      18             : typedef struct
      19             : {
      20             :         ElektraInvokeHandle * plugin;
      21             :         Key * pluginName;
      22             :         KeySet * pluginConfig;
      23             : } Process;
      24             : 
      25           1 : static int validPluginName (Key * pluginNameKey, Key * errorKey)
      26             : {
      27           1 :         if (pluginNameKey == NULL)
      28             :         {
      29           0 :                 ELEKTRA_ADD_INSTALLATION_WARNING (errorKey, "Missing plugin configuration parameter plugin=<name of plugin to be proxied>");
      30           0 :                 return 0;
      31             :         }
      32             : 
      33             :         // and this key should obviously contain the plugin's name, so check for any name
      34             :         // furthermore by invoking process in a process we'd create a deadloop, check that too
      35           1 :         const char * pluginName = keyString (pluginNameKey);
      36           1 :         if (elektraStrCmp (pluginName, "(null)") == 0 || keyIsBinary (pluginNameKey))
      37             :         {
      38           0 :                 ELEKTRA_ADD_VALIDATION_SEMANTIC_WARNINGF (errorKey, "Plugin configuration parameter plugin has an invalid value: %s",
      39             :                                                           pluginName);
      40           0 :                 return 0;
      41             :         }
      42           1 :         else if (elektraStrCmp (pluginName, "process") == 0)
      43             :         {
      44           0 :                 ELEKTRA_ADD_INTERFACE_WARNING (errorKey, "Cannot proxy the process plugin itself");
      45           0 :                 return 0;
      46             :         }
      47             :         return 1;
      48             : }
      49             : 
      50          53 : static void cleanup (Process * process, Key * errorKey)
      51             : {
      52          53 :         if (process->plugin) elektraInvokeClose (process->plugin, errorKey);
      53          53 :         if (process->pluginName) keyDel (process->pluginName);
      54          53 :         ksDel (process->pluginConfig);
      55          53 :         elektraFree (process);
      56          53 : }
      57             : 
      58           0 : int elektraInvoke1Arg (ElektraInvokeHandle * handle, const char * elektraPluginFunctionName, Key * k)
      59             : {
      60           0 :         if (!handle || !elektraPluginFunctionName) return -2;
      61             : 
      62             :         // If we cast this right away although the function wasn't found it will cause a deadlock
      63           0 :         const void * rawFunc = elektraInvokeGetFunction (handle, elektraPluginFunctionName);
      64             : 
      65           0 :         if (!rawFunc) return -2;
      66             : 
      67             :         typedef int (*elektra1Arg) (Key *);
      68           0 :         elektra1Arg func = *(elektra1Arg *) rawFunc;
      69           0 :         return func (k);
      70             : }
      71             : 
      72           0 : static int isContractKey (Key * key)
      73             : {
      74           0 :         return !elektraStrCmp (keyName (key), "system/elektra/modules/process");
      75             : }
      76             : 
      77          55 : int elektraProcessOpen (Plugin * handle, Key * errorKey)
      78             : {
      79          55 :         ElektraPluginProcess * pp = elektraPluginGetData (handle);
      80          55 :         if (pp == NULL)
      81             :         {
      82             :                 // process initialization
      83          53 :                 Process * process = elektraMalloc (sizeof (Process));
      84          53 :                 KeySet * processConfig = elektraPluginGetConfig (handle);
      85          53 :                 process->pluginName = ksLookupByName (processConfig, "/plugin", KDB_O_POP);
      86          53 :                 process->pluginConfig = ksDup (processConfig);
      87          53 :                 ksAppendKey (processConfig, process->pluginName);
      88          53 :                 process->plugin = NULL;
      89             : 
      90          53 :                 if ((pp = elektraPluginProcessInit (errorKey)) == NULL) return ELEKTRA_PLUGIN_STATUS_ERROR;
      91             : 
      92          53 :                 elektraPluginProcessSetData (pp, process);
      93          53 :                 elektraPluginSetData (handle, pp);
      94          53 :                 if (!elektraPluginProcessIsParent (pp)) elektraPluginProcessStart (handle, pp);
      95             :         }
      96             : 
      97          55 :         if (elektraPluginProcessIsParent (pp)) return elektraPluginProcessOpen (pp, errorKey);
      98             : 
      99           0 :         Process * process = elektraPluginProcessGetData (pp);
     100             : 
     101             :         // First time child initialization
     102             :         // elektraInvokeOpen will call the plugin's open function, this has to happen in the other process
     103             : 
     104           0 :         if (process->plugin == NULL && !isContractKey (errorKey) && validPluginName (process->pluginName, errorKey))
     105             :         {
     106           0 :                 process->plugin = elektraInvokeOpen (keyString (process->pluginName), process->pluginConfig, errorKey);
     107           0 :                 if (!process->plugin)
     108             :                 {
     109           0 :                         ELEKTRA_SET_RESOURCE_ERRORF (errorKey, "Failed to open the proxied plugin %s", keyString (process->pluginName));
     110           0 :                         return ELEKTRA_PLUGIN_STATUS_ERROR;
     111             :                 }
     112             :                 return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     113             :         }
     114             : 
     115           0 :         if (process->plugin == NULL) return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     116             : 
     117             :         // Otherwise just call the open function if it exists and don't reinitialize with elektraInvokeOpen
     118           0 :         int ret = elektraInvoke1Arg (process->plugin, "open", errorKey);
     119           0 :         if (ret == -2) return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     120           0 :         return ret;
     121             : }
     122             : 
     123          55 : int elektraProcessClose (Plugin * handle, Key * errorKey)
     124             : {
     125          55 :         ElektraPluginProcess * pp = elektraPluginGetData (handle);
     126          55 :         if (!pp) return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     127          55 :         Process * process = elektraPluginProcessGetData (pp);
     128          55 :         if (elektraPluginProcessIsParent (pp))
     129             :         {
     130          55 :                 ElektraPluginProcessCloseResult result = elektraPluginProcessClose (pp, errorKey);
     131          55 :                 if (result.cleanedUp)
     132             :                 {
     133          53 :                         cleanup (process, errorKey);
     134          53 :                         elektraPluginSetData (handle, NULL);
     135             :                 }
     136          55 :                 return result.result;
     137             :         }
     138             : 
     139             : 
     140             :         // Handle may be null if initialization failed in the child, ignore that it will exit anyway afterwards
     141           0 :         if (process->plugin == NULL) return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     142             : 
     143           0 :         int ret = elektraInvoke1Arg (process->plugin, "close", errorKey);
     144             :         if (ret == -2) return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     145             :         return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     146             : }
     147             : 
     148           0 : static void adjustContract (KeySet * pluginContract, KeySet * contract)
     149             : {
     150           0 :         ksRewind (pluginContract);
     151             :         Key * cur;
     152           0 :         while ((cur = ksNext (pluginContract)) != NULL)
     153             :         {
     154           0 :                 Key * cpy = keyDup (cur);
     155           0 :                 keySetBaseName (cpy, NULL);
     156           0 :                 if (!elektraStrCmp ("infos", keyBaseName (cpy)))
     157             :                 {
     158           0 :                         keySetBaseName (cpy, NULL);
     159           0 :                         keySetBaseName (cpy, NULL);
     160           0 :                         keyAddBaseName (cpy, "process");
     161           0 :                         keyAddBaseName (cpy, "infos");
     162           0 :                         keyAddBaseName (cpy, keyBaseName (cur));
     163           0 :                         Key * infoKey = ksLookup (contract, cpy, KDB_O_NONE);
     164           0 :                         keySetString (infoKey, keyString (cpy));
     165             :                 }
     166           0 :                 keyDel (cpy);
     167             :         }
     168           0 : }
     169             : 
     170             : 
     171          37 : int elektraProcessGet (Plugin * handle, KeySet * returned, Key * parentKey)
     172             : {
     173          37 :         ElektraPluginProcess * pp = elektraPluginGetData (handle);
     174          37 :         Process * process = elektraPluginProcessGetData (pp);
     175             : 
     176          37 :         if (elektraPluginProcessIsParent (pp)) return elektraPluginProcessSend (pp, ELEKTRA_PLUGINPROCESS_GET, returned, parentKey);
     177             : 
     178           0 :         if (isContractKey (parentKey))
     179             :         {
     180           0 :                 KeySet * processConfig = elektraPluginGetConfig (handle);
     181           0 :                 Key * pluginName = ksLookupByName (processConfig, "/plugin", KDB_O_NONE);
     182             : 
     183           0 :                 KeySet * contract =
     184           0 :                         ksNew (30, keyNew ("system/elektra/modules/process", KEY_VALUE, "process plugin waits for your orders", KEY_END),
     185             :                                keyNew ("system/elektra/modules/process/exports", KEY_END),
     186             :                                keyNew ("system/elektra/modules/process/exports/open", KEY_FUNC, elektraProcessOpen, KEY_END),
     187             :                                keyNew ("system/elektra/modules/process/exports/close", KEY_FUNC, elektraProcessClose, KEY_END),
     188             :                                keyNew ("system/elektra/modules/process/exports/get", KEY_FUNC, elektraProcessGet, KEY_END),
     189             :                                keyNew ("system/elektra/modules/process/exports/set", KEY_FUNC, elektraProcessSet, KEY_END),
     190             :                                keyNew ("system/elektra/modules/process/exports/error", KEY_FUNC, elektraProcessError, KEY_END),
     191             :                                keyNew ("system/elektra/modules/process/exports/checkconf", KEY_FUNC, elektraProcessCheckConfig, KEY_END),
     192             : #include ELEKTRA_README
     193             :                                keyNew ("system/elektra/modules/process/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END);
     194           0 :                 ksAppend (returned, contract);
     195           0 :                 ksDel (contract);
     196             : 
     197           0 :                 if (!validPluginName (pluginName, parentKey) || !process->plugin) return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     198             : 
     199           0 :                 Key * pluginParentKey = keyDup (parentKey);
     200           0 :                 keySetBaseName (pluginParentKey, keyString (pluginName));
     201             : 
     202           0 :                 KeySet * pluginContract = ksNew (30, KS_END);
     203           0 :                 elektraInvoke2Args (process->plugin, "get", pluginContract, pluginParentKey);
     204           0 :                 keyDel (pluginParentKey);
     205           0 :                 if (ksGetSize (pluginContract) == 0)
     206             :                 {
     207           0 :                         ELEKTRA_SET_INTERFACE_ERRORF (parentKey, "Failed to get the contract for %s", keyString (pluginName));
     208           0 :                         ksDel (pluginContract);
     209           0 :                         return ELEKTRA_PLUGIN_STATUS_ERROR;
     210             :                 }
     211             : 
     212           0 :                 adjustContract (pluginContract, returned);
     213           0 :                 ksDel (pluginContract);
     214             : 
     215           0 :                 return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     216             :         }
     217             : 
     218           0 :         if (!process->plugin) return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     219             : 
     220           0 :         int ret = elektraInvoke2Args (process->plugin, "get", returned, parentKey);
     221           0 :         if (ret == -2) return ELEKTRA_PLUGIN_STATUS_NO_UPDATE;
     222           0 :         return ret;
     223             : }
     224             : 
     225           3 : int elektraProcessSet (Plugin * handle, KeySet * returned, Key * parentKey)
     226             : {
     227           3 :         ElektraPluginProcess * pp = elektraPluginGetData (handle);
     228           3 :         Process * process = elektraPluginProcessGetData (pp);
     229             : 
     230           3 :         if (elektraPluginProcessIsParent (pp)) return elektraPluginProcessSend (pp, ELEKTRA_PLUGINPROCESS_SET, returned, parentKey);
     231             : 
     232           0 :         if (!process->plugin) return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     233             : 
     234           0 :         int ret = elektraInvoke2Args (process->plugin, "set", returned, parentKey);
     235           0 :         if (ret == -2) return ELEKTRA_PLUGIN_STATUS_NO_UPDATE;
     236           0 :         return ret;
     237             : }
     238             : 
     239           2 : int elektraProcessError (Plugin * handle, KeySet * returned, Key * parentKey)
     240             : {
     241           2 :         ElektraPluginProcess * pp = elektraPluginGetData (handle);
     242           2 :         Process * process = elektraPluginProcessGetData (pp);
     243             : 
     244           2 :         if (elektraPluginProcessIsParent (pp)) return elektraPluginProcessSend (pp, ELEKTRA_PLUGINPROCESS_ERROR, returned, parentKey);
     245             : 
     246           0 :         if (!process->plugin) return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     247             : 
     248           0 :         int ret = elektraInvoke2Args (process->plugin, "error", returned, parentKey);
     249           0 :         if (ret == -2) return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     250           0 :         return ret;
     251             : }
     252             : 
     253           1 : int elektraProcessCheckConfig (Key * errorKey, KeySet * conf)
     254             : {
     255             :         // We need the plugin key to know which plugin we should proxy
     256           1 :         Key * pluginNameKey = ksLookupByName (conf, "/plugin", KDB_O_NONE);
     257           1 :         if (!validPluginName (pluginNameKey, errorKey))
     258             :         {
     259             :                 return ELEKTRA_PLUGIN_STATUS_ERROR;
     260             :         }
     261             : 
     262           1 :         return ELEKTRA_PLUGIN_STATUS_NO_UPDATE;
     263             : }
     264             : 
     265          53 : Plugin * ELEKTRA_PLUGIN_EXPORT
     266             : {
     267             :         // clang-format off
     268          53 :         return elektraPluginExport ("process",
     269             :                 ELEKTRA_PLUGIN_OPEN,    &elektraProcessOpen,
     270             :                 ELEKTRA_PLUGIN_CLOSE,   &elektraProcessClose,
     271             :                 ELEKTRA_PLUGIN_GET,     &elektraProcessGet,
     272             :                 ELEKTRA_PLUGIN_SET,             &elektraProcessSet,
     273             :                 ELEKTRA_PLUGIN_ERROR,   &elektraProcessError,
     274             :                 ELEKTRA_PLUGIN_END);
     275             : }

Generated by: LCOV version 1.13