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 director 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-dir-mod: "
40 #else
41 # define LOGPREFIX "python3-dir-mod: "
42 #endif
43
44 #include "dird/dird.h"
45 #include "dird/dir_plugins.h"
46
47
48 #include "plugins/include/common.h" // for Dmsg Macro
49
50 #include "plugins/dird/python/plugin_private_context.h"
51
52 #include "plugins/include/python3compat.h"
53
54 #define BAREOSDIR_MODULE
55 #include "bareosdir.h"
56 #include "lib/edit.h"
57
58 namespace directordaemon {
59 static const int debuglevel = 150;
60
61 static bRC set_bareos_core_functions(CoreFunctions* new_bareos_core_functions);
62 static bRC set_plugin_context(PluginContext* new_plugin_context);
63
64 static bRC PyParsePluginDefinition(PluginContext* plugin_ctx, void* value);
65 static bRC PyGetPluginValue(PluginContext* plugin_ctx,
66 pVariable var,
67 void* value);
68 static bRC PySetPluginValue(PluginContext* plugin_ctx,
69 pVariable var,
70 void* value);
71 static bRC PyHandlePluginEvent(PluginContext* plugin_ctx,
72 bDirEvent* event,
73 void* value);
74
75 /* Pointers to Bareos functions */
76 static CoreFunctions* bareos_core_functions = NULL;
77
78
79 #define NOPLUGINSETGETVALUE 1
80 /* functions common to all plugins */
81 #include "plugins/include/python_plugins_common.inc"
82
83
84 /* set the bareos_core_functions pointer to the given value */
set_bareos_core_functions(CoreFunctions * new_bareos_core_functions)85 static bRC set_bareos_core_functions(CoreFunctions* new_bareos_core_functions)
86 {
87 bareos_core_functions = new_bareos_core_functions;
88 return bRC_OK;
89 }
90
91 /* set the plugin context pointer to the given value */
set_plugin_context(PluginContext * new_plugin_context)92 static bRC set_plugin_context(PluginContext* new_plugin_context)
93 {
94 plugin_context = new_plugin_context;
95 return bRC_OK;
96 }
97
98
99 /**
100 * Any plugin options which are passed in are dispatched here to a Python
101 * method and it can parse the plugin options. This function is also called
102 * after PyLoadModule() has loaded the Python module and made sure things are
103 * operational.
104 */
PyParsePluginDefinition(PluginContext * plugin_ctx,void * value)105 static bRC PyParsePluginDefinition(PluginContext* plugin_ctx, void* value)
106 {
107 bRC retval = bRC_Error;
108 struct plugin_private_context* plugin_priv_ctx
109 = (struct plugin_private_context*)plugin_ctx->plugin_private_context;
110 PyObject* pFunc;
111
112 /*
113 * Lookup the parse_plugin_definition() function in the python module.
114 */
115 pFunc = PyDict_GetItemString(
116 plugin_priv_ctx->pyModuleFunctionsDict,
117 "parse_plugin_definition"); /* Borrowed reference */
118 if (pFunc && PyCallable_Check(pFunc)) {
119 PyObject *pPluginDefinition, *pRetVal;
120
121 pPluginDefinition = PyUnicode_FromString((char*)value);
122 if (!pPluginDefinition) { goto bail_out; }
123
124 pRetVal = PyObject_CallFunctionObjArgs(pFunc, pPluginDefinition, NULL);
125 Py_DECREF(pPluginDefinition);
126
127 if (!pRetVal) {
128 goto bail_out;
129 } else {
130 retval = ConvertPythonRetvalTobRCRetval(pRetVal);
131 Py_DECREF(pRetVal);
132 }
133
134 return retval;
135 } else {
136 Dmsg(plugin_ctx, debuglevel,
137 LOGPREFIX
138 "Failed to find function named "
139 "parse_plugin_definition()\n");
140 return bRC_Error;
141 }
142
143 bail_out:
144 if (PyErr_Occurred()) { PyErrorHandler(plugin_ctx, M_FATAL); }
145
146 return retval;
147 }
148
PyGetPluginValue(PluginContext * plugin_ctx,pVariable var,void * value)149 static bRC PyGetPluginValue(PluginContext* plugin_ctx,
150 pVariable var,
151 void* value)
152 {
153 return bRC_OK;
154 }
155
PySetPluginValue(PluginContext * plugin_ctx,pVariable var,void * value)156 static bRC PySetPluginValue(PluginContext* plugin_ctx,
157 pVariable var,
158 void* value)
159 {
160 return bRC_OK;
161 }
162
PyHandlePluginEvent(PluginContext * plugin_ctx,bDirEvent * event,void * value)163 static bRC PyHandlePluginEvent(PluginContext* plugin_ctx,
164 bDirEvent* event,
165 void* value)
166 {
167 bRC retval = bRC_Error;
168 struct plugin_private_context* plugin_priv_ctx
169 = (struct plugin_private_context*)plugin_ctx->plugin_private_context;
170 PyObject* pFunc;
171
172 /*
173 * Lookup the handle_plugin_event() function in the python module.
174 */
175 pFunc = PyDict_GetItemString(plugin_priv_ctx->pyModuleFunctionsDict,
176 "handle_plugin_event"); /* Borrowed reference */
177 if (pFunc && PyCallable_Check(pFunc)) {
178 PyObject *pEventType, *pRetVal;
179
180 pEventType = PyLong_FromLong(event->eventType);
181
182 pRetVal = PyObject_CallFunctionObjArgs(pFunc, pEventType, NULL);
183 Py_DECREF(pEventType);
184
185 if (!pRetVal) {
186 goto bail_out;
187 } else {
188 retval = ConvertPythonRetvalTobRCRetval(pRetVal);
189 Py_DECREF(pRetVal);
190 }
191 } else {
192 Dmsg(plugin_ctx, debuglevel,
193 LOGPREFIX "Failed to find function named handle_plugin_event()\n");
194 }
195
196 return retval;
197
198 bail_out:
199 if (PyErr_Occurred()) { PyErrorHandler(plugin_ctx, M_FATAL); }
200
201 return retval;
202 }
203
204 /**
205 * Callback function which is exposed as a part of the additional methods
206 * which allow a Python plugin to get certain internal values of the current
207 * Job.
208 */
PyBareosGetValue(PyObject * self,PyObject * args)209 static PyObject* PyBareosGetValue(PyObject* self, PyObject* args)
210 {
211 int var;
212 PluginContext* plugin_ctx = plugin_context;
213 PyObject* pRetVal = NULL;
214
215 if (!PyArg_ParseTuple(args, "i:BareosGetValue", &var)) { return NULL; }
216 RETURN_RUNTIME_ERROR_IF_BFUNC_OR_BAREOS_PLUGIN_CTX_UNSET()
217
218 switch (var) {
219 case bDirVarJobId:
220 case bDirVarLevel:
221 case bDirVarType:
222 case bDirVarNumVols:
223 case bDirVarJobStatus:
224 case bDirVarPriority:
225 case bDirVarFDJobStatus:
226 case bDirVarSDJobStatus: {
227 int value = 0;
228
229 if (bareos_core_functions->getBareosValue(plugin_ctx, (brDirVariable)var,
230 &value)
231 == bRC_OK) {
232 pRetVal = PyLong_FromLong(value);
233 }
234 break;
235 }
236 case bDirVarJobErrors:
237 case bDirVarSDErrors:
238 case bDirVarJobFiles:
239 case bDirVarSDJobFiles:
240 case bDirVarLastRate:
241 case bDirVarJobBytes:
242 case bDirVarReadBytes: {
243 uint64_t value = 0;
244
245 if (bareos_core_functions->getBareosValue(plugin_ctx, (brDirVariable)var,
246 &value)
247 == bRC_OK) {
248 pRetVal = PyLong_FromUnsignedLong(value);
249 }
250 break;
251 }
252 case bDirVarJobName:
253 case bDirVarJob:
254 case bDirVarClient:
255 case bDirVarPool:
256 case bDirVarStorage:
257 case bDirVarWriteStorage:
258 case bDirVarReadStorage:
259 case bDirVarCatalog:
260 case bDirVarMediaType:
261 case bDirVarVolumeName: {
262 char* value = NULL;
263
264 if (bareos_core_functions->getBareosValue(plugin_ctx, (brDirVariable)var,
265 &value)
266 == bRC_OK) {
267 if (value) { pRetVal = PyUnicode_FromString(value); }
268 }
269 break;
270 }
271 case bDirVarPluginDir: {
272 char* value = NULL;
273
274 if (bareos_core_functions->getBareosValue(NULL, (brDirVariable)var,
275 &value)
276 == bRC_OK) {
277 if (value) { pRetVal = PyUnicode_FromString(value); }
278 }
279 break;
280 }
281 default:
282 Dmsg(plugin_ctx, debuglevel,
283 LOGPREFIX "PyBareosGetValue unknown variable requested %d\n", var);
284 break;
285 }
286
287 if (!pRetVal) { Py_RETURN_NONE; }
288
289 return pRetVal;
290 }
291
292 /**
293 * Callback function which is exposed as a part of the additional methods
294 * which allow a Python plugin to set certain internal values of the current
295 * Job.
296 */
PyBareosSetValue(PyObject * self,PyObject * args)297 static PyObject* PyBareosSetValue(PyObject* self, PyObject* args)
298 {
299 int var;
300 PluginContext* plugin_ctx = plugin_context;
301 bRC retval = bRC_Error;
302 PyObject* pyValue;
303
304 if (!PyArg_ParseTuple(args, "iO:BareosSetValue", &var, &pyValue)) {
305 goto bail_out;
306 }
307 RETURN_RUNTIME_ERROR_IF_BFUNC_OR_BAREOS_PLUGIN_CTX_UNSET()
308
309 switch (var) {
310 case bwDirVarVolumeName: {
311 const char* value;
312
313 value = PyUnicode_AsUTF8(pyValue);
314 if (value) {
315 retval = bareos_core_functions->setBareosValue(
316 plugin_ctx, (bwDirVariable)var,
317 static_cast<void*>(const_cast<char*>(value)));
318 }
319
320 break;
321 }
322 case bwDirVarPriority:
323 case bwDirVarJobLevel: {
324 int value;
325
326 value = PyLong_AsLong(pyValue);
327 if (value >= 0) {
328 retval = bareos_core_functions->setBareosValue(
329 plugin_ctx, (bwDirVariable)var, &value);
330 }
331 break;
332 }
333 default:
334 Dmsg(plugin_ctx, debuglevel,
335 LOGPREFIX "PyBareosSetValue unknown variable requested %d\n", var);
336 break;
337 }
338
339 bail_out:
340 return ConvertbRCRetvalToPythonRetval(retval);
341 }
342
343 /**
344 * Callback function which is exposed as a part of the additional methods
345 * which allow a Python plugin to issue debug messages using the Bareos debug
346 * message facility.
347 */
PyBareosDebugMessage(PyObject * self,PyObject * args)348 static PyObject* PyBareosDebugMessage(PyObject* self, PyObject* args)
349 {
350 int level;
351 char* dbgmsg = NULL;
352 PluginContext* plugin_ctx = plugin_context;
353
354 if (!PyArg_ParseTuple(args, "i|z:BareosDebugMessage", &level, &dbgmsg)) {
355 return NULL;
356 }
357 RETURN_RUNTIME_ERROR_IF_BFUNC_OR_BAREOS_PLUGIN_CTX_UNSET()
358
359 if (dbgmsg) { Dmsg(plugin_ctx, level, LOGPREFIX "%s", dbgmsg); }
360
361 Py_RETURN_NONE;
362 }
363
364 /**
365 * Callback function which is exposed as a part of the additional methods
366 * which allow a Python plugin to issue Job messages using the Bareos Job
367 * message facility.
368 */
PyBareosJobMessage(PyObject * self,PyObject * args)369 static PyObject* PyBareosJobMessage(PyObject* self, PyObject* args)
370 {
371 int type;
372 char* jobmsg = NULL;
373 PluginContext* plugin_ctx = plugin_context;
374
375 if (!PyArg_ParseTuple(args, "i|z:BareosJobMessage", &type, &jobmsg)) {
376 return NULL;
377 }
378 RETURN_RUNTIME_ERROR_IF_BFUNC_OR_BAREOS_PLUGIN_CTX_UNSET()
379
380 if (jobmsg) { Jmsg(plugin_ctx, type, LOGPREFIX "%s", jobmsg); }
381
382 Py_RETURN_NONE;
383 }
384
385 /**
386 * Callback function which is exposed as a part of the additional methods
387 * which allow a Python plugin to issue a Register Event to register
388 * additional events it wants to receive.
389 */
PyBareosRegisterEvents(PyObject * self,PyObject * args)390 static PyObject* PyBareosRegisterEvents(PyObject* self, PyObject* args)
391 {
392 int len, event;
393 PluginContext* plugin_ctx = plugin_context;
394 bRC retval = bRC_Error;
395 PyObject *pyEvents, *pySeq, *pyEvent;
396
397 if (!PyArg_ParseTuple(args, "O:BareosRegisterEvents", &pyEvents)) {
398 goto bail_out;
399 }
400 RETURN_RUNTIME_ERROR_IF_BFUNC_OR_BAREOS_PLUGIN_CTX_UNSET()
401
402 pySeq = PySequence_Fast(pyEvents, "Expected a sequence of events");
403 if (!pySeq) { goto bail_out; }
404
405 len = PySequence_Fast_GET_SIZE(pySeq);
406
407 for (int i = 0; i < len; i++) {
408 pyEvent = PySequence_Fast_GET_ITEM(pySeq, i);
409 event = PyLong_AsLong(pyEvent);
410
411 if (event >= bDirEventJobStart && event <= bDirEventGetScratch) {
412 Dmsg(plugin_ctx, debuglevel,
413 LOGPREFIX "PyBareosRegisterEvents registering event %d\n", event);
414 retval
415 = bareos_core_functions->registerBareosEvents(plugin_ctx, 1, event);
416
417 if (retval != bRC_OK) { break; }
418 }
419 }
420
421 Py_DECREF(pySeq);
422
423 bail_out:
424 return ConvertbRCRetvalToPythonRetval(retval);
425 }
426
427 /**
428 * Callback function which is exposed as a part of the additional methods
429 * which allow a Python plugin to issue an Unregister Event to unregister
430 * events it doesn't want to receive anymore.
431 */
PyBareosUnRegisterEvents(PyObject * self,PyObject * args)432 static PyObject* PyBareosUnRegisterEvents(PyObject* self, PyObject* args)
433 {
434 int len, event;
435 PluginContext* plugin_ctx = plugin_context;
436 bRC retval = bRC_Error;
437 PyObject *pyEvents, *pySeq, *pyEvent;
438
439 if (!PyArg_ParseTuple(args, "O:BareosUnRegisterEvents", &pyEvents)) {
440 goto bail_out;
441 }
442 RETURN_RUNTIME_ERROR_IF_BFUNC_OR_BAREOS_PLUGIN_CTX_UNSET()
443
444 pySeq = PySequence_Fast(pyEvents, "Expected a sequence of events");
445 if (!pySeq) { goto bail_out; }
446
447 len = PySequence_Fast_GET_SIZE(pySeq);
448
449 for (int i = 0; i < len; i++) {
450 pyEvent = PySequence_Fast_GET_ITEM(pySeq, i);
451 event = PyLong_AsLong(pyEvent);
452
453 if (event >= bDirEventJobStart && event <= bDirEventGetScratch) {
454 Dmsg(plugin_ctx, debuglevel,
455 "PyBareosUnRegisterEvents: registering event %d\n", event);
456 retval
457 = bareos_core_functions->unregisterBareosEvents(plugin_ctx, 1, event);
458
459 if (retval != bRC_OK) { break; }
460 }
461 }
462
463 Py_DECREF(pySeq);
464
465 bail_out:
466 return ConvertbRCRetvalToPythonRetval(retval);
467 }
468
469 /**
470 * Callback function which is exposed as a part of the additional methods
471 * which allow a Python plugin to issue a GetInstanceCount to retrieve the
472 * number of instances of the current plugin being loaded into the daemon.
473 */
PyBareosGetInstanceCount(PyObject * self,PyObject * args)474 static PyObject* PyBareosGetInstanceCount(PyObject* self, PyObject* args)
475 {
476 int value;
477 PluginContext* plugin_ctx = plugin_context;
478 PyObject* pRetVal = NULL;
479
480 if (!PyArg_ParseTuple(args, ":BareosGetInstanceCount")) { return NULL; }
481 RETURN_RUNTIME_ERROR_IF_BFUNC_OR_BAREOS_PLUGIN_CTX_UNSET()
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 } /* namespace directordaemon */
493