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 : }
|