1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2020-2020 Bareos GmbH & Co. KG
5 
6    This program is Free Software; you can redistribute it and/or
7    modify it under the terms of version three of the GNU Affero General Public
8    License as published by the Free Software Foundation, which is
9    listed in the file LICENSE.
10 
11    This program is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14    Affero General Public License for more details.
15 
16    You should have received a copy of the GNU Affero General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19    02110-1301, USA.
20 */
21 /**
22  * @file
23  * Python module for the Bareos storagedaemon plugin
24  */
25 #define PY_SSIZE_T_CLEAN
26 #define BUILD_PLUGIN
27 
28 #if defined(HAVE_WIN32)
29 #  include "include/bareos.h"
30 #  include <Python.h>
31 #else
32 #  include <Python.h>
33 #  include "include/bareos.h"
34 #endif
35 
36 #include "include/version_hex.h"
37 
38 #if PY_VERSION_HEX < VERSION_HEX(3, 0, 0)
39 #  define LOGPREFIX "python-sd-mod: "
40 #else
41 #  define LOGPREFIX "python3-sd-mod: "
42 #endif
43 
44 
45 #include "filed/fd_plugins.h"
46 
47 #include "stored/sd_plugins.h"
48 
49 #define BAREOSSD_MODULE
50 #include "bareossd.h"
51 #include "plugins/include/python3compat.h"
52 #include "lib/edit.h"
53 
54 namespace storagedaemon {
55 
56 static const int debuglevel = 150;
57 
58 static bRC set_bareos_core_functions(CoreFunctions* new_bareos_core_functions);
59 static bRC set_plugin_context(PluginContext* new_plugin_context);
60 static bRC PyParsePluginDefinition(PluginContext* plugin_ctx, void* value);
61 
62 static bRC PyGetPluginValue(PluginContext* plugin_ctx,
63                             pVariable var,
64                             void* value);
65 static bRC PySetPluginValue(PluginContext* plugin_ctx,
66                             pVariable var,
67                             void* value);
68 static bRC PyHandlePluginEvent(PluginContext* plugin_ctx,
69                                bSdEvent* event,
70                                void* value);
71 
72 /* Pointers to Bareos functions */
73 static CoreFunctions* bareos_core_functions = NULL;
74 
75 #include "plugins/stored/python/plugin_private_context.h"
76 
77 #define NOPLUGINSETGETVALUE 1
78 /* functions common to all plugins */
79 #include "plugins/include/python_plugins_common.inc"
80 
81 
82 /* set the bareos_core_functions pointer to the given value */
set_bareos_core_functions(CoreFunctions * new_bareos_core_functions)83 static bRC set_bareos_core_functions(CoreFunctions* new_bareos_core_functions)
84 {
85   bareos_core_functions = new_bareos_core_functions;
86   return bRC_OK;
87 }
88 
89 /* set the plugin context pointer to the given value */
set_plugin_context(PluginContext * new_plugin_context)90 static bRC set_plugin_context(PluginContext* new_plugin_context)
91 {
92   plugin_context = new_plugin_context;
93   return bRC_OK;
94 }
95 
96 /**
97  * Any plugin options which are passed in are dispatched here to a Python method
98  * and it can parse the plugin options. This function is also called after
99  * PyLoadModule() has loaded the Python module and made sure things are
100  * operational.
101  */
PyParsePluginDefinition(PluginContext * plugin_ctx,void * value)102 static bRC PyParsePluginDefinition(PluginContext* plugin_ctx, void* value)
103 {
104   bRC retval = bRC_Error;
105   struct plugin_private_context* plugin_priv_ctx
106       = (struct plugin_private_context*)plugin_ctx->plugin_private_context;
107   PyObject* pFunc;
108 
109   /*
110    * Lookup the parse_plugin_definition() function in the python module.
111    */
112   pFunc = PyDict_GetItemString(
113       plugin_priv_ctx->pyModuleFunctionsDict,
114       "parse_plugin_definition"); /* Borrowed reference */
115   if (pFunc && PyCallable_Check(pFunc)) {
116     PyObject *pPluginDefinition, *pRetVal;
117 
118     pPluginDefinition = PyUnicode_FromString((char*)value);
119     if (!pPluginDefinition) { goto bail_out; }
120 
121     pRetVal = PyObject_CallFunctionObjArgs(pFunc, pPluginDefinition, NULL);
122     Py_DECREF(pPluginDefinition);
123 
124     if (!pRetVal) {
125       goto bail_out;
126     } else {
127       retval = ConvertPythonRetvalTobRCRetval(pRetVal);
128       Py_DECREF(pRetVal);
129     }
130 
131     return retval;
132   } else {
133     Dmsg(plugin_ctx, debuglevel,
134          LOGPREFIX "Failed to find function named parse_plugin_definition()\n");
135     return bRC_Error;
136   }
137 
138 bail_out:
139   if (PyErr_Occurred()) { PyErrorHandler(plugin_ctx, M_FATAL); }
140 
141   return retval;
142 }
143 
PyGetPluginValue(PluginContext * plugin_ctx,pVariable var,void * value)144 static bRC PyGetPluginValue(PluginContext* plugin_ctx,
145                             pVariable var,
146                             void* value)
147 {
148   return bRC_OK;
149 }
150 
PySetPluginValue(PluginContext * plugin_ctx,pVariable var,void * value)151 static bRC PySetPluginValue(PluginContext* plugin_ctx,
152                             pVariable var,
153                             void* value)
154 {
155   return bRC_OK;
156 }
157 
PyHandlePluginEvent(PluginContext * plugin_ctx,bSdEvent * event,void * value)158 static bRC PyHandlePluginEvent(PluginContext* plugin_ctx,
159                                bSdEvent* event,
160                                void* value)
161 {
162   bRC retval = bRC_Error;
163   plugin_private_context* plugin_priv_ctx
164       = (plugin_private_context*)plugin_ctx->plugin_private_context;
165   PyObject* pFunc;
166 
167   /*
168    * Lookup the handle_plugin_event() function in the python module.
169    */
170   pFunc = PyDict_GetItemString(plugin_priv_ctx->pyModuleFunctionsDict,
171                                "handle_plugin_event"); /* Borrowed reference */
172   if (pFunc && PyCallable_Check(pFunc)) {
173     PyObject *pEventType, *pRetVal;
174 
175     pEventType = PyLong_FromLong(event->eventType);
176 
177     pRetVal = PyObject_CallFunctionObjArgs(pFunc, pEventType, NULL);
178     Py_DECREF(pEventType);
179 
180     if (!pRetVal) {
181       goto bail_out;
182     } else {
183       retval = ConvertPythonRetvalTobRCRetval(pRetVal);
184       Py_DECREF(pRetVal);
185     }
186   } else {
187     Dmsg(plugin_ctx, debuglevel,
188          LOGPREFIX "Failed to find function named handle_plugin_event()\n");
189   }
190 
191   return retval;
192 
193 bail_out:
194   if (PyErr_Occurred()) { PyErrorHandler(plugin_ctx, M_FATAL); }
195 
196   return retval;
197 }
198 
199 /**
200  * Callback function which is exposed as a part of the additional methods which
201  * allow a Python plugin to get certain internal values of the current Job.
202  */
PyBareosGetValue(PyObject * self,PyObject * args)203 static PyObject* PyBareosGetValue(PyObject* self, PyObject* args)
204 {
205   int var;
206   PluginContext* plugin_ctx = plugin_context;
207   PyObject* pRetVal = NULL;
208 
209   if (!PyArg_ParseTuple(args, "i:BareosGetValue", &var)) { return NULL; }
210   RETURN_RUNTIME_ERROR_IF_BFUNC_OR_BAREOS_PLUGIN_CTX_UNSET()
211 
212   switch (var) {
213     case bsdVarJobId:
214     case bsdVarLevel:
215     case bsdVarType:
216     case bsdVarJobStatus: {
217       int value;
218 
219       if (bareos_core_functions->getBareosValue(plugin_ctx, (bsdrVariable)var,
220                                                 &value)
221           == bRC_OK) {
222         pRetVal = PyLong_FromLong(value);
223       }
224       break;
225     }
226     case bsdVarJobErrors:
227     case bsdVarJobFiles:
228     case bsdVarJobBytes: {
229       uint64_t value = 0;
230 
231       if (bareos_core_functions->getBareosValue(plugin_ctx, (bsdrVariable)var,
232                                                 &value)
233           == bRC_OK) {
234         pRetVal = PyLong_FromUnsignedLong(value);
235       }
236       break;
237     }
238     case bsdVarJobName:
239     case bsdVarJob:
240     case bsdVarClient:
241     case bsdVarPool:
242     case bsdVarPoolType:
243     case bsdVarStorage:
244     case bsdVarMediaType:
245     case bsdVarVolumeName: {
246       char* value = NULL;
247 
248       if (bareos_core_functions->getBareosValue(plugin_ctx, (bsdrVariable)var,
249                                                 &value)
250           == bRC_OK) {
251         if (value) { pRetVal = PyUnicode_FromString(value); }
252       }
253       break;
254     }
255     case bsdVarCompatible: {
256       bool value;
257 
258       if (bareos_core_functions->getBareosValue(NULL, (bsdrVariable)var, &value)
259           == bRC_OK) {
260         long bool_value;
261 
262         bool_value = (value) ? 1 : 0;
263         pRetVal = PyBool_FromLong(bool_value);
264       }
265       break;
266     }
267     case bsdVarPluginDir: {
268       char* value = NULL;
269 
270       if (bareos_core_functions->getBareosValue(NULL, (bsdrVariable)var, &value)
271           == bRC_OK) {
272         if (value) { pRetVal = PyUnicode_FromString(value); }
273       }
274       break;
275     }
276     default:
277       Dmsg(plugin_ctx, debuglevel,
278            LOGPREFIX "PyBareosGetValue unknown variable requested %d\n", var);
279       break;
280   }
281 
282   if (!pRetVal) { Py_RETURN_NONE; }
283 
284   return pRetVal;
285 }
286 
287 /**
288  * Callback function which is exposed as a part of the additional methods which
289  * allow a Python plugin to get certain internal values of the current Job.
290  */
PyBareosSetValue(PyObject * self,PyObject * args)291 static PyObject* PyBareosSetValue(PyObject* self, PyObject* args)
292 {
293   int var;
294   PluginContext* plugin_ctx = plugin_context;
295   bRC retval = bRC_Error;
296   PyObject* pyValue;
297 
298   if (!PyArg_ParseTuple(args, "iO:BareosSetValue", &var, &pyValue)) {
299     goto bail_out;
300   }
301   RETURN_RUNTIME_ERROR_IF_BFUNC_OR_BAREOS_PLUGIN_CTX_UNSET()
302 
303   switch (var) {
304     case bsdwVarVolumeName: {
305       const char* value = PyUnicode_AsUTF8(pyValue);
306       if (value) {
307         bareos_core_functions->setBareosValue(
308             plugin_ctx, (bsdwVariable)var,
309             static_cast<void*>(const_cast<char*>(value)));
310       }
311 
312       break;
313     }
314     case bsdwVarPriority:
315     case bsdwVarJobLevel: {
316       int value;
317 
318       value = PyLong_AsLong(pyValue);
319       if (value >= 0) {
320         retval = bareos_core_functions->setBareosValue(
321             plugin_ctx, (bsdwVariable)var, &value);
322       }
323       break;
324     }
325     default:
326       Dmsg(plugin_ctx, debuglevel,
327            LOGPREFIX "PyBareosSetValue unknown variable requested %d\n", var);
328       break;
329   }
330 
331 bail_out:
332   return ConvertbRCRetvalToPythonRetval(retval);
333 }
334 
335 /**
336  * Callback function which is exposed as a part of the additional methods which
337  * allow a Python plugin to issue debug messages using the Bareos debug message
338  * facility.
339  */
PyBareosDebugMessage(PyObject * self,PyObject * args)340 static PyObject* PyBareosDebugMessage(PyObject* self, PyObject* args)
341 {
342   int level;
343   char* dbgmsg = NULL;
344   PluginContext* plugin_ctx = plugin_context;
345 
346   if (!PyArg_ParseTuple(args, "i|z:BareosDebugMessage", &level, &dbgmsg)) {
347     return NULL;
348   }
349   RETURN_RUNTIME_ERROR_IF_BFUNC_OR_BAREOS_PLUGIN_CTX_UNSET()
350 
351   if (dbgmsg) { Dmsg(plugin_ctx, level, LOGPREFIX "%s", dbgmsg); }
352 
353   Py_RETURN_NONE;
354 }
355 
356 /**
357  * Callback function which is exposed as a part of the additional methods which
358  * allow a Python plugin to issue Job messages using the Bareos Job message
359  * facility.
360  */
PyBareosJobMessage(PyObject * self,PyObject * args)361 static PyObject* PyBareosJobMessage(PyObject* self, PyObject* args)
362 {
363   int type;
364   char* jobmsg = NULL;
365   PluginContext* plugin_ctx = plugin_context;
366 
367   if (!PyArg_ParseTuple(args, "i|z:BareosJobMessage", &type, &jobmsg)) {
368     return NULL;
369   }
370   RETURN_RUNTIME_ERROR_IF_BFUNC_OR_BAREOS_PLUGIN_CTX_UNSET()
371 
372   if (jobmsg) { Jmsg(plugin_ctx, type, LOGPREFIX "%s", jobmsg); }
373 
374   Py_RETURN_NONE;
375 }
376 
377 /**
378  * Callback function which is exposed as a part of the additional methods which
379  * allow a Python plugin to issue a Register Event to register additional events
380  * it wants to receive.
381  */
PyBareosRegisterEvents(PyObject * self,PyObject * args)382 static PyObject* PyBareosRegisterEvents(PyObject* self, PyObject* args)
383 {
384   int len, event;
385   PluginContext* plugin_ctx = plugin_context;
386   bRC retval = bRC_Error;
387   PyObject *pyEvents, *pySeq, *pyEvent;
388 
389   if (!PyArg_ParseTuple(args, "O:BareosRegisterEvents", &pyEvents)) {
390     goto bail_out;
391   }
392   RETURN_RUNTIME_ERROR_IF_BFUNC_OR_BAREOS_PLUGIN_CTX_UNSET()
393 
394   pySeq = PySequence_Fast(pyEvents, "Expected a sequence of events");
395   if (!pySeq) { goto bail_out; }
396 
397   len = PySequence_Fast_GET_SIZE(pySeq);
398 
399   for (int i = 0; i < len; i++) {
400     pyEvent = PySequence_Fast_GET_ITEM(pySeq, i);
401     event = PyLong_AsLong(pyEvent);
402 
403     if (event >= bSdEventJobStart && event <= bSdEventWriteRecordTranslation) {
404       Dmsg(plugin_ctx, debuglevel,
405            LOGPREFIX "PyBareosRegisterEvents registering event %d\n", event);
406       retval
407           = bareos_core_functions->registerBareosEvents(plugin_ctx, 1, event);
408 
409       if (retval != bRC_OK) { break; }
410     }
411   }
412 
413   Py_DECREF(pySeq);
414 
415 bail_out:
416   return ConvertbRCRetvalToPythonRetval(retval);
417 }
418 
419 /**
420  * Callback function which is exposed as a part of the additional methods which
421  * allow a Python plugin to issue an Unregister Event to unregister events it
422  * doesn't want to receive anymore.
423  */
PyBareosUnRegisterEvents(PyObject * self,PyObject * args)424 static PyObject* PyBareosUnRegisterEvents(PyObject* self, PyObject* args)
425 {
426   int len, event;
427   PluginContext* plugin_ctx = plugin_context;
428   bRC retval = bRC_Error;
429   PyObject *pyEvents, *pySeq, *pyEvent;
430 
431   if (!PyArg_ParseTuple(args, "O:BareosUnRegisterEvents", &pyEvents)) {
432     goto bail_out;
433   }
434   RETURN_RUNTIME_ERROR_IF_BFUNC_OR_BAREOS_PLUGIN_CTX_UNSET()
435 
436   pySeq = PySequence_Fast(pyEvents, "Expected a sequence of events");
437   if (!pySeq) { goto bail_out; }
438 
439   len = PySequence_Fast_GET_SIZE(pySeq);
440 
441   for (int i = 0; i < len; i++) {
442     pyEvent = PySequence_Fast_GET_ITEM(pySeq, i);
443     event = PyLong_AsLong(pyEvent);
444 
445     if (event >= bSdEventJobStart && event <= bSdEventWriteRecordTranslation) {
446       Dmsg(plugin_ctx, debuglevel,
447            "PyBareosUnRegisterEvents: registering event %d\n", event);
448       retval
449           = bareos_core_functions->unregisterBareosEvents(plugin_ctx, 1, event);
450 
451       if (retval != bRC_OK) { break; }
452     }
453   }
454 
455   Py_DECREF(pySeq);
456 
457 bail_out:
458   return ConvertbRCRetvalToPythonRetval(retval);
459 }
460 
461 /**
462  * Callback function which is exposed as a part of the additional methods which
463  * allow a Python plugin to issue a GetInstanceCount to retrieve the number of
464  * instances of the current plugin being loaded into the daemon.
465  */
PyBareosGetInstanceCount(PyObject * self,PyObject * args)466 static PyObject* PyBareosGetInstanceCount(PyObject* self, PyObject* args)
467 {
468   int value;
469   PluginContext* plugin_ctx = plugin_context;
470   PyObject* pRetVal = NULL;
471 
472   if (!PyArg_ParseTuple(args, ":BareosGetInstanceCount")) { return NULL; }
473   RETURN_RUNTIME_ERROR_IF_BFUNC_OR_BAREOS_PLUGIN_CTX_UNSET()
474 
475   if (!plugin_ctx) {
476     PyErr_SetString(PyExc_ValueError, "plugin_ctx is unset");
477     return NULL;
478   }
479   if (!bareos_core_functions) {
480     PyErr_SetString(PyExc_ValueError, "bareos_core_functions is unset");
481     return NULL;
482   }
483   if (bareos_core_functions->getInstanceCount(plugin_ctx, &value) == bRC_OK) {
484     pRetVal = PyLong_FromLong(value);
485   }
486 
487   if (!pRetVal) { Py_RETURN_NONE; }
488 
489   return pRetVal;
490 }
491 
492 
493 } /* namespace storagedaemon*/
494