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