1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2011-2015 Planets Communications B.V.
5    Copyright (C) 2013-2018 Bareos GmbH & Co. KG
6 
7    This program is Free Software; you can redistribute it and/or
8    modify it under the terms of version three of the GNU Affero General Public
9    License as published by the Free Software Foundation, which is
10    listed in the file LICENSE.
11 
12    This program is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15    Affero General Public License for more details.
16 
17    You should have received a copy of the GNU Affero General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20    02110-1301, USA.
21 */
22 /*
23  * Marco van Wieringen, August 2012
24  */
25 /**
26  * @file
27  * Python plugin for the Bareos File Daemon
28  */
29 #define BUILD_PLUGIN
30 
31 #include "include/bareos.h"
32 #include <Python.h>
33 #include "filed/fd_plugins.h"
34 #include "plugins/filed/fd_common.h"
35 #include "python-fd.h"
36 
37 namespace filedaemon {
38 
39 #if (PY_VERSION_HEX <  0x02060000)
40 #error "Need at least Python version 2.6 or newer"
41 #endif
42 
43 #if (PY_VERSION_HEX >  0x03050000)
44 #define PyInt_AsLong PyLong_AsLong
45 #define PyInt_FromLong PyLong_FromLong
46 #define PyString_AsString PyUnicode_AsUTF8
47 #define PyString_FromString PyUnicode_FromString
48 #define PyString_Check PyBytes_Check
49 #endif
50 
51 static const int debuglevel = 150;
52 
53 #define PLUGIN_LICENSE      "Bareos AGPLv3"
54 #define PLUGIN_AUTHOR       "Marco van Wieringen"
55 #define PLUGIN_DATE         "May 2014"
56 #define PLUGIN_VERSION      "3"
57 #define PLUGIN_DESCRIPTION  "Python File Daemon Plugin"
58 #define PLUGIN_USAGE        "python:module_path=<path-to-python-modules>:module_name=<python-module-to-load>:..."
59 
60 /**
61  * Forward referenced functions
62  */
63 static bRC newPlugin(bpContext *ctx);
64 static bRC freePlugin(bpContext *ctx);
65 static bRC getPluginValue(bpContext *ctx, pVariable var, void *value);
66 static bRC setPluginValue(bpContext *ctx, pVariable var, void *value);
67 static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value);
68 static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp);
69 static bRC endBackupFile(bpContext *ctx);
70 static bRC pluginIO(bpContext *ctx, struct io_pkt *io);
71 static bRC startRestoreFile(bpContext *ctx, const char *cmd);
72 static bRC endRestoreFile(bpContext *ctx);
73 static bRC createFile(bpContext *ctx, struct restore_pkt *rp);
74 static bRC setFileAttributes(bpContext *ctx, struct restore_pkt *rp);
75 static bRC checkFile(bpContext *ctx, char *fname);
76 static bRC getAcl(bpContext *ctx, acl_pkt *ap);
77 static bRC setAcl(bpContext *ctx, acl_pkt *ap);
78 static bRC getXattr(bpContext *ctx, xattr_pkt *xp);
79 static bRC setXattr(bpContext *ctx, xattr_pkt *xp);
80 static bRC parse_plugin_definition(bpContext *ctx, void *value, PoolMem &plugin_options);
81 
82 static void PyErrorHandler(bpContext *ctx, int msgtype);
83 static bRC PyLoadModule(bpContext *ctx, void *value);
84 static bRC PyParsePluginDefinition(bpContext *ctx, void *value);
85 static bRC PyGetPluginValue(bpContext *ctx, pVariable var, void *value);
86 static bRC PySetPluginValue(bpContext *ctx, pVariable var, void *value);
87 static bRC PyHandlePluginEvent(bpContext *ctx, bEvent *event, void *value);
88 static bRC PyStartBackupFile(bpContext *ctx, struct save_pkt *sp);
89 static bRC PyEndBackupFile(bpContext *ctx);
90 static bRC PyPluginIO(bpContext *ctx, struct io_pkt *io);
91 static bRC PyStartRestoreFile(bpContext *ctx, const char *cmd);
92 static bRC PyEndRestoreFile(bpContext *ctx);
93 static bRC PyCreateFile(bpContext *ctx, struct restore_pkt *rp);
94 static bRC PySetFileAttributes(bpContext *ctx, struct restore_pkt *rp);
95 static bRC PyCheckFile(bpContext *ctx, char *fname);
96 static bRC PyGetAcl(bpContext *ctx, acl_pkt *ap);
97 static bRC PySetAcl(bpContext *ctx, acl_pkt *ap);
98 static bRC PyGetXattr(bpContext *ctx, xattr_pkt *xp);
99 static bRC PySetXattr(bpContext *ctx, xattr_pkt *xp);
100 static bRC PyRestoreObjectData(bpContext *ctx, struct restore_object_pkt *rop);
101 static bRC PyHandleBackupFile(bpContext *ctx, struct save_pkt *sp);
102 
103 /**
104  * Pointers to Bareos functions
105  */
106 static bFuncs *bfuncs = NULL;
107 static bInfo *binfo = NULL;
108 
109 static genpInfo pluginInfo = {
110    sizeof(pluginInfo),
111    FD_PLUGIN_INTERFACE_VERSION,
112    FD_PLUGIN_MAGIC,
113    PLUGIN_LICENSE,
114    PLUGIN_AUTHOR,
115    PLUGIN_DATE,
116    PLUGIN_VERSION,
117    PLUGIN_DESCRIPTION,
118    PLUGIN_USAGE
119 };
120 
121 static pFuncs pluginFuncs = {
122    sizeof(pluginFuncs),
123    FD_PLUGIN_INTERFACE_VERSION,
124 
125    /* Entry points into plugin */
126    newPlugin,                         /* new plugin instance */
127    freePlugin,                        /* free plugin instance */
128    getPluginValue,
129    setPluginValue,
130    handlePluginEvent,
131    startBackupFile,
132    endBackupFile,
133    startRestoreFile,
134    endRestoreFile,
135    pluginIO,
136    createFile,
137    setFileAttributes,
138    checkFile,
139    getAcl,
140    setAcl,
141    getXattr,
142    setXattr
143 };
144 
145 /**
146  * Plugin private context
147  */
148 struct plugin_ctx {
149    int32_t backup_level;              /* Backup level e.g. Full/Differential/Incremental */
150    utime_t since;                     /* Since time for Differential/Incremental */
151    bool python_loaded;                /* Plugin has python module loaded ? */
152    bool python_path_set;              /* Python plugin search path is set ? */
153    char *plugin_options;              /* Plugin Option string */
154    char *module_path;                 /* Plugin Module Path */
155    char *module_name;                 /* Plugin Module Name */
156    char *fname;                       /* Next filename to save */
157    char *link;                        /* Target symlink points to */
158    char *object_name;                 /* Restore Object Name */
159    char *object;                      /* Restore Object Content */
160    PyThreadState *interpreter;        /* Python interpreter for this instance of the plugin */
161    PyObject *pInstance;               /* Python Module instance */
162    PyObject *pModule;                 /* Python Module entry point */
163    PyObject *pDict;                   /* Python Dictionary */
164    PyObject *bpContext;               /* Python representation of plugin context */
165 };
166 
167 /**
168  * We don't actually use this but we need it to tear down the
169  * final python interpreter on unload of the plugin. Each instance of
170  * the plugin get its own interpreter.
171  */
172 static PyThreadState *mainThreadState;
173 
174 #if (PY_VERSION_HEX >  0x03050000)
175 static struct PyModuleDef BareosFDModuleDef = {
176   PyModuleDef_HEAD_INIT,
177   "bareosfd",
178   NULL,
179   -1,
180   BareosFDMethods,
181   NULL,
182   NULL,
183   NULL,
184   NULL
185 };
186 #endif
187 
188 #ifdef __cplusplus
189 extern "C" {
190 #endif
191 
192 /**
193  * Plugin called here when it is first loaded
194  */
loadPlugin(bInfo * lbinfo,bFuncs * lbfuncs,genpInfo ** pinfo,pFuncs ** pfuncs)195 bRC loadPlugin(bInfo *lbinfo,
196                            bFuncs *lbfuncs,
197                            genpInfo **pinfo,
198                            pFuncs **pfuncs)
199 {
200    bfuncs = lbfuncs;                  /* Set Bareos funct pointers */
201    binfo  = lbinfo;
202 
203    *pinfo  = &pluginInfo;             /* Return pointer to our info */
204    *pfuncs = &pluginFuncs;            /* Return pointer to our functions */
205 
206    /*
207     * Setup Python
208     */
209    Py_InitializeEx(0);
210    PyEval_InitThreads();
211    mainThreadState = PyEval_SaveThread();
212 
213    return bRC_OK;
214 }
215 
216 /**
217  * Plugin called here when it is unloaded, normally when Bareos is going to exit.
218  */
unloadPlugin()219 bRC unloadPlugin()
220 {
221    /*
222     * Terminate Python
223     */
224    PyEval_RestoreThread(mainThreadState);
225    Py_Finalize();
226 
227    return bRC_OK;
228 }
229 
230 #ifdef __cplusplus
231 }
232 #endif
233 
234 /**
235  * Called here to make a new instance of the plugin -- i.e. when
236  * a new Job is started. There can be multiple instances of
237  * each plugin that are running at the same time.  Your
238  * plugin instance must be thread safe and keep its own
239  * local data.
240  */
newPlugin(bpContext * ctx)241 static bRC newPlugin(bpContext *ctx)
242 {
243    struct plugin_ctx *p_ctx;
244 
245    p_ctx = (struct plugin_ctx *)malloc(sizeof(struct plugin_ctx));
246    if (!p_ctx) {
247       return bRC_Error;
248    }
249    memset(p_ctx, 0, sizeof(struct plugin_ctx));
250    ctx->pContext = (void *)p_ctx;        /* set our context pointer */
251 
252    /*
253     * For each plugin instance we instantiate a new Python interpreter.
254     */
255    PyEval_AcquireLock();
256    p_ctx->interpreter = Py_NewInterpreter();
257    PyEval_ReleaseThread(p_ctx->interpreter);
258 
259    /*
260     * Always register some events the python plugin itself can register
261     * any other events it is interested in.
262     */
263    bfuncs->registerBareosEvents(ctx,
264                                 9,
265                                 bEventLevel,
266                                 bEventSince,
267                                 bEventNewPluginOptions,
268                                 bEventPluginCommand,
269                                 bEventJobStart,
270                                 bEventRestoreCommand,
271                                 bEventEstimateCommand,
272                                 bEventBackupCommand,
273                                 bEventRestoreObject);
274 
275    return bRC_OK;
276 }
277 
278 /**
279  * Release everything concerning a particular instance of a
280  * plugin. Normally called when the Job terminates.
281  */
freePlugin(bpContext * ctx)282 static bRC freePlugin(bpContext *ctx)
283 {
284    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
285 
286    if (!p_ctx) {
287       return bRC_Error;
288    }
289 
290    if (p_ctx->plugin_options) {
291       free(p_ctx->plugin_options);
292    }
293 
294    if (p_ctx->module_path) {
295       free(p_ctx->module_path);
296    }
297 
298    if (p_ctx->module_name) {
299       free(p_ctx->module_name);
300    }
301 
302    if (p_ctx->fname) {
303       free(p_ctx->fname);
304    }
305 
306    if (p_ctx->link) {
307       free(p_ctx->link);
308    }
309 
310    if (p_ctx->object_name) {
311       free(p_ctx->object_name);
312    }
313 
314    if (p_ctx->object) {
315       free(p_ctx->object);
316    }
317 
318    /*
319     * Stop any sub interpreter started per plugin instance.
320     */
321    PyEval_AcquireThread(p_ctx->interpreter);
322 
323    /*
324     * Do python cleanup calls.
325     */
326    if (p_ctx->bpContext) {
327       Py_DECREF(p_ctx->bpContext);
328    }
329 
330    if (p_ctx->pModule) {
331       Py_DECREF(p_ctx->pModule);
332    }
333 
334    Py_EndInterpreter(p_ctx->interpreter);
335    PyEval_ReleaseLock();
336 
337    free(p_ctx);
338    ctx->pContext = NULL;
339 
340    return bRC_OK;
341 }
342 
343 /**
344  * Called by core code to get a variable from the plugin.
345  * Not currently used.
346  */
getPluginValue(bpContext * ctx,pVariable var,void * value)347 static bRC getPluginValue(bpContext *ctx, pVariable var, void *value)
348 {
349    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
350    bRC retval = bRC_Error;
351 
352    if (!p_ctx) {
353       goto bail_out;
354    }
355 
356    PyEval_AcquireThread(p_ctx->interpreter);
357    retval = PyGetPluginValue(ctx, var, value);
358    PyEval_ReleaseThread(p_ctx->interpreter);
359 
360 bail_out:
361    return retval;
362 }
363 
364 /**
365  * Called by core code to set a plugin variable.
366  * Not currently used.
367  */
setPluginValue(bpContext * ctx,pVariable var,void * value)368 static bRC setPluginValue(bpContext *ctx, pVariable var, void *value)
369 {
370    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
371    bRC retval = bRC_Error;
372 
373    if (!p_ctx) {
374       return bRC_Error;
375    }
376 
377    PyEval_AcquireThread(p_ctx->interpreter);
378    retval = PySetPluginValue(ctx, var, value);
379    PyEval_ReleaseThread(p_ctx->interpreter);
380 
381    return retval;
382 }
383 
384 /**
385  * Called by Bareos when there are certain events that the
386  * plugin might want to know.  The value depends on the event.
387  */
handlePluginEvent(bpContext * ctx,bEvent * event,void * value)388 static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value)
389 {
390    bRC retval = bRC_Error;
391    bool event_dispatched = false;
392    PoolMem plugin_options(PM_FNAME);
393    plugin_ctx *p_ctx = (plugin_ctx *)ctx->pContext;
394 
395    if (!p_ctx) {
396       goto bail_out;
397    }
398 
399    /*
400     * First handle some events internally before calling python if it
401     * want to do some special handling on the event triggered.
402     */
403    switch (event->eventType) {
404    case bEventLevel:
405       p_ctx->backup_level = (int64_t)value;
406       break;
407    case bEventSince:
408       p_ctx->since = (int64_t)value;
409       break;
410    case bEventBackupCommand:
411      /*
412       * Fall-through wanted
413       */
414    case bEventRestoreCommand:
415      /*
416       * Fall-through wanted
417       */
418    case bEventEstimateCommand:
419      /*
420       * Fall-through wanted
421       */
422    case bEventPluginCommand:
423       event_dispatched = true;
424       retval = parse_plugin_definition(ctx, value, plugin_options);
425       break;
426    case bEventNewPluginOptions:
427       /*
428        * Free any previous value.
429        */
430       if (p_ctx->plugin_options) {
431          free(p_ctx->plugin_options);
432          p_ctx->plugin_options = NULL;
433       }
434 
435       event_dispatched = true;
436       retval = parse_plugin_definition(ctx, value, plugin_options);
437 
438       /*
439        * Save that we got a plugin override.
440        */
441       p_ctx->plugin_options = bstrdup((char *)value);
442       break;
443    case bEventRestoreObject: {
444       struct restore_object_pkt *rop;
445 
446       rop = (struct restore_object_pkt *)value;
447 
448       /*
449        * Only use the plugin definition of a restore object if we
450        * didn't get any other plugin definition from some other source before.
451        */
452       if (!p_ctx->python_loaded) {
453          if (rop && *rop->plugin_name) {
454             event_dispatched = true;
455             retval = parse_plugin_definition(ctx, rop->plugin_name, plugin_options);
456          }
457       }
458       break;
459    }
460    default:
461       break;
462    }
463 
464    /*
465     * See if we have been triggered in the previous switch if not we have to
466     * always dispatch the event. If we already processed the event internally
467     * we only do a dispatch to the python entry point when that internal processing
468     * was successfull (e.g. retval == bRC_OK).
469     */
470    if (!event_dispatched || retval == bRC_OK) {
471       PyEval_AcquireThread(p_ctx->interpreter);
472 
473       /*
474        * Now dispatch the event to Python.
475        * First the calls that need special handling.
476        */
477       switch (event->eventType) {
478       case bEventBackupCommand:
479         /*
480          * Fall-through wanted
481          */
482       case bEventRestoreCommand:
483         /*
484          * Fall-through wanted
485          */
486       case bEventEstimateCommand:
487         /*
488          * Fall-through wanted
489          */
490       case bEventPluginCommand:
491         /*
492          * Fall-through wanted
493          */
494       case bEventNewPluginOptions:
495          /*
496           * See if we already loaded the Python modules.
497           */
498          if (!p_ctx->python_loaded) {
499             retval = PyLoadModule(ctx, plugin_options.c_str());
500          }
501 
502          /*
503           * Only try to call when the loading succeeded.
504           */
505          if (retval == bRC_OK) {
506             retval = PyParsePluginDefinition(ctx, plugin_options.c_str());
507          }
508          break;
509       case bEventRestoreObject: {
510          struct restore_object_pkt *rop;
511 
512          rop = (struct restore_object_pkt *)value;
513          if (!rop) {
514             /*
515              * If rop == NULL this means we got the last restore object.
516              * No need to call into python so just return.
517              */
518             retval = bRC_OK;
519          } else {
520             /*
521              * See if we already loaded the Python modules.
522              */
523             if (!p_ctx->python_loaded && *rop->plugin_name) {
524                retval = PyLoadModule(ctx, plugin_options.c_str());
525 
526                /*
527                 * Only try to call when the loading succeeded.
528                 */
529                if (retval == bRC_OK) {
530                   retval = PyParsePluginDefinition(ctx, plugin_options.c_str());
531                   if (retval == bRC_OK) {
532                      retval = PyRestoreObjectData(ctx, rop);
533                   }
534                }
535             } else {
536                retval = PyRestoreObjectData(ctx, rop);
537             }
538          }
539          break;
540       }
541       case bEventHandleBackupFile:
542          retval = PyHandleBackupFile(ctx, (struct save_pkt *)value);
543          break;
544       default:
545          /*
546           * Handle the generic events e.g. the ones which are just passed on.
547           * We only try to call Python when we loaded the right module until
548           * that time we pretend the call succeeded.
549           */
550          if (p_ctx->python_loaded) {
551             retval = PyHandlePluginEvent(ctx, event, value);
552          } else {
553             retval = bRC_OK;
554          }
555          break;
556       }
557 
558       PyEval_ReleaseThread(p_ctx->interpreter);
559    }
560 
561 bail_out:
562    return retval;
563 }
564 
565 /**
566  * Called when starting to backup a file. Here the plugin must
567  * return the "stat" packet for the directory/file and provide
568  * certain information so that Bareos knows what the file is.
569  * The plugin can create "Virtual" files by giving them a
570  * name that is not normally found on the file system.
571  */
startBackupFile(bpContext * ctx,struct save_pkt * sp)572 static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp)
573 {
574    bRC retval = bRC_Error;
575    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
576 
577    if (!p_ctx) {
578       goto bail_out;
579    }
580 
581    PyEval_AcquireThread(p_ctx->interpreter);
582    retval = PyStartBackupFile(ctx, sp);
583    PyEval_ReleaseThread(p_ctx->interpreter);
584 
585    /*
586     * For Incremental and Differential backups use checkChanges method to
587     * see if we need to backup this file.
588     */
589    switch (p_ctx->backup_level) {
590    case L_INCREMENTAL:
591    case L_DIFFERENTIAL:
592       /*
593        * If the plugin didn't set a save_time but we have a since time
594        * from the bEventSince event we use that as basis for the actual
595        * save_time to check.
596        */
597       if (sp->save_time == 0 && p_ctx->since) {
598          sp->save_time = p_ctx->since;
599       }
600 
601       switch (bfuncs->checkChanges(ctx, sp)) {
602       case bRC_Seen:
603          switch (sp->type) {
604          case FT_DIRBEGIN:
605             sp->type = FT_DIRNOCHG;
606             break;
607          default:
608             sp->type = FT_NOCHG;
609             break;
610          }
611          break;
612       default:
613          break;
614       }
615    }
616 
617 bail_out:
618    return retval;
619 }
620 
621 /**
622  * Done backing up a file.
623  */
endBackupFile(bpContext * ctx)624 static bRC endBackupFile(bpContext *ctx)
625 {
626    bRC retval = bRC_Error;
627    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
628 
629    if (!p_ctx) {
630       goto bail_out;
631    }
632 
633    PyEval_AcquireThread(p_ctx->interpreter);
634    retval = PyEndBackupFile(ctx);
635    PyEval_ReleaseThread(p_ctx->interpreter);
636 
637 bail_out:
638    return retval;
639 }
640 
641 /**
642  * Do actual I/O. Bareos calls this after startBackupFile
643  * or after startRestoreFile to do the actual file
644  * input or output.
645  */
pluginIO(bpContext * ctx,struct io_pkt * io)646 static bRC pluginIO(bpContext *ctx, struct io_pkt *io)
647 {
648    bRC retval = bRC_Error;
649    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
650 
651    if (!p_ctx) {
652       goto bail_out;
653    }
654 
655    if (!p_ctx->python_loaded) {
656       goto bail_out;
657    }
658 
659    PyEval_AcquireThread(p_ctx->interpreter);
660    retval = PyPluginIO(ctx, io);
661    PyEval_ReleaseThread(p_ctx->interpreter);
662 
663 bail_out:
664    return retval;
665 }
666 
667 /**
668  * Start restore of a file.
669  */
startRestoreFile(bpContext * ctx,const char * cmd)670 static bRC startRestoreFile(bpContext *ctx, const char *cmd)
671 {
672    bRC retval = bRC_Error;
673    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
674 
675    if (!p_ctx) {
676       goto bail_out;
677    }
678 
679    PyEval_AcquireThread(p_ctx->interpreter);
680    retval = PyStartRestoreFile(ctx, cmd);
681    PyEval_ReleaseThread(p_ctx->interpreter);
682 
683 bail_out:
684    return retval;
685 }
686 
687 /**
688  * Done restoring a file.
689  */
endRestoreFile(bpContext * ctx)690 static bRC endRestoreFile(bpContext *ctx)
691 {
692    bRC retval = bRC_Error;
693    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
694 
695    if (!p_ctx) {
696       goto bail_out;
697    }
698 
699    PyEval_AcquireThread(p_ctx->interpreter);
700    retval = PyEndRestoreFile(ctx);
701    PyEval_ReleaseThread(p_ctx->interpreter);
702 
703 bail_out:
704    return retval;
705 }
706 
707 /**
708  * Called here to give the plugin the information needed to
709  * re-create the file on a restore.  It basically gets the
710  * stat packet that was created during the backup phase.
711  * This data is what is needed to create the file, but does
712  * not contain actual file data.
713  */
createFile(bpContext * ctx,struct restore_pkt * rp)714 static bRC createFile(bpContext *ctx, struct restore_pkt *rp)
715 {
716    bRC retval = bRC_Error;
717    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
718 
719    if (!p_ctx) {
720       goto bail_out;
721    }
722 
723    PyEval_AcquireThread(p_ctx->interpreter);
724    retval = PyCreateFile(ctx, rp);
725    PyEval_ReleaseThread(p_ctx->interpreter);
726 
727 bail_out:
728    return retval;
729 }
730 
731 /**
732  * Called after the file has been restored. This can be used to
733  * set directory permissions, ...
734  */
setFileAttributes(bpContext * ctx,struct restore_pkt * rp)735 static bRC setFileAttributes(bpContext *ctx, struct restore_pkt *rp)
736 {
737    bRC retval = bRC_Error;
738    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
739 
740    if (!p_ctx) {
741       goto bail_out;
742    }
743 
744    PyEval_AcquireThread(p_ctx->interpreter);
745    retval = PySetFileAttributes(ctx, rp);
746    PyEval_ReleaseThread(p_ctx->interpreter);
747 
748 bail_out:
749    return retval;
750 }
751 
752 /**
753  * When using Incremental dump, all previous dumps are necessary
754  */
checkFile(bpContext * ctx,char * fname)755 static bRC checkFile(bpContext *ctx, char *fname)
756 {
757    bRC retval = bRC_Error;
758    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
759 
760    if (!p_ctx) {
761       goto bail_out;
762    }
763 
764    if (!p_ctx->python_loaded) {
765       return bRC_OK;
766    }
767 
768    PyEval_AcquireThread(p_ctx->interpreter);
769    retval = PyCheckFile(ctx, fname);
770    PyEval_ReleaseThread(p_ctx->interpreter);
771 
772 bail_out:
773    return retval;
774 }
775 
776 /**
777  */
getAcl(bpContext * ctx,acl_pkt * ap)778 static bRC getAcl(bpContext *ctx, acl_pkt *ap)
779 {
780    bRC retval = bRC_Error;
781    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
782 
783    if (!p_ctx) {
784       goto bail_out;
785    }
786 
787    PyEval_AcquireThread(p_ctx->interpreter);
788    retval = PyGetAcl(ctx, ap);
789    PyEval_ReleaseThread(p_ctx->interpreter);
790 
791 bail_out:
792    return retval;
793 }
794 
795 /**
796  */
setAcl(bpContext * ctx,acl_pkt * ap)797 static bRC setAcl(bpContext *ctx, acl_pkt *ap)
798 {
799    bRC retval = bRC_Error;
800    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
801 
802    if (!p_ctx) {
803       goto bail_out;
804    }
805 
806    PyEval_AcquireThread(p_ctx->interpreter);
807    retval = PySetAcl(ctx, ap);
808    PyEval_ReleaseThread(p_ctx->interpreter);
809 
810 bail_out:
811    return retval;
812 }
813 
814 /**
815  */
getXattr(bpContext * ctx,xattr_pkt * xp)816 static bRC getXattr(bpContext *ctx, xattr_pkt *xp)
817 {
818    bRC retval = bRC_Error;
819    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
820 
821    if (!p_ctx) {
822       goto bail_out;
823    }
824 
825    PyEval_AcquireThread(p_ctx->interpreter);
826    retval = PyGetXattr(ctx, xp);
827    PyEval_ReleaseThread(p_ctx->interpreter);
828 
829 bail_out:
830    return retval;
831 }
832 
833 /**
834  */
setXattr(bpContext * ctx,xattr_pkt * xp)835 static bRC setXattr(bpContext *ctx, xattr_pkt *xp)
836 {
837    bRC retval = bRC_Error;
838    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
839 
840    if (!p_ctx) {
841       goto bail_out;
842    }
843 
844    PyEval_AcquireThread(p_ctx->interpreter);
845    retval = PySetXattr(ctx, xp);
846    PyEval_ReleaseThread(p_ctx->interpreter);
847 
848 bail_out:
849    return retval;
850 }
851 
852 /**
853  * Strip any backslashes in the string.
854  */
StripBackSlashes(char * value)855 static inline void StripBackSlashes(char *value)
856 {
857    char *bp;
858 
859    bp = value;
860    while (*bp) {
861       switch (*bp) {
862       case '\\':
863          bstrinlinecpy(bp, bp + 1);
864          break;
865       default:
866          break;
867       }
868 
869       bp++;
870    }
871 }
872 
873 /**
874  * Parse a boolean value e.g. check if its yes or true anything else translates to false.
875  */
ParseBoolean(const char * argument_value)876 static inline bool ParseBoolean(const char *argument_value)
877 {
878    if (Bstrcasecmp(argument_value, "yes") ||
879        Bstrcasecmp(argument_value, "true")) {
880       return true;
881    } else {
882       return false;
883    }
884 }
885 
886 /**
887  * Only set destination to value when it has no previous setting.
888  */
SetStringIfNull(char ** destination,char * value)889 static inline void SetStringIfNull(char **destination, char *value)
890 {
891    if (!*destination) {
892       *destination = bstrdup(value);
893       StripBackSlashes(*destination);
894    }
895 }
896 
897 /**
898  * Always set destination to value and clean any previous one.
899  */
SetString(char ** destination,char * value)900 static inline void SetString(char **destination, char *value)
901 {
902    if (*destination) {
903       free(*destination);
904    }
905 
906    *destination = bstrdup(value);
907    StripBackSlashes(*destination);
908 }
909 
910 /**
911  * Parse the plugin definition passed in.
912  *
913  * The definition is in this form:
914  *
915  * python:module_path=<path>:module_name=<python_module_name>:...
916  */
parse_plugin_definition(bpContext * ctx,void * value,PoolMem & plugin_options)917 static bRC parse_plugin_definition(bpContext *ctx, void *value, PoolMem &plugin_options)
918 {
919    bool found;
920    int i, cnt;
921    bool keep_existing;
922    PoolMem plugin_definition(PM_FNAME);
923    char *bp, *argument, *argument_value;
924    plugin_ctx *p_ctx = (plugin_ctx *)ctx->pContext;
925 
926    if (!value) {
927       return bRC_Error;
928    }
929 
930    /*
931     * Skip this plugin when getting plugin definition "*all*"
932     * This allows to restore a Windows Backup on a Linux FD with
933     * Python Plugins enabled.
934     */
935    if (bstrcmp((char *)value, "*all*")) {
936       Dmsg(ctx, debuglevel, "python-fd: Got plugin definition %s, skipping to ignore\n", (char *)value);
937       return bRC_Skip;
938    }
939 
940    keep_existing = (p_ctx->plugin_options) ? true : false;
941 
942    /*
943     * Parse the plugin definition.
944     * Make a private copy of the whole string.
945     */
946    if (!p_ctx->python_loaded && p_ctx->plugin_options) {
947       int len;
948 
949       /*
950        * We got some option string which got pushed before we actual were able to send
951        * it to the python module as the entry point was not instantiated. So we prepend
952        * that now in the option string and append the new option string with the first
953        * argument being the pluginname removed as that is already part of the other
954        * plugin option string.
955        */
956       len = strlen(p_ctx->plugin_options);
957       PmStrcpy(plugin_definition, p_ctx->plugin_options);
958 
959       bp = strchr((char *)value, ':');
960       if (!bp) {
961          Jmsg(ctx, M_FATAL, "python-fd: Illegal plugin definition %s\n", (char *)value);
962          Dmsg(ctx, debuglevel, "python-fd: Illegal plugin definition %s\n", (char *)value);
963          goto bail_out;
964       }
965 
966       /*
967        * See if option string end with ':'
968        */
969       if (p_ctx->plugin_options[len - 1] != ':') {
970          PmStrcat(plugin_definition, (char *)bp);
971       } else {
972          PmStrcat(plugin_definition, (char *)bp + 1);
973       }
974    } else {
975       PmStrcpy(plugin_definition, (char *)value);
976    }
977 
978    bp = strchr(plugin_definition.c_str(), ':');
979    if (!bp) {
980       Jmsg(ctx, M_FATAL, "python-fd: Illegal plugin definition %s\n", plugin_definition.c_str());
981       Dmsg(ctx, debuglevel, "python-fd: Illegal plugin definition %s\n", plugin_definition.c_str());
982       goto bail_out;
983    }
984 
985    /*
986     * Skip the first ':'
987     */
988    bp++;
989 
990    cnt = 0;
991    while (bp) {
992       if (strlen(bp) == 0) {
993          break;
994       }
995 
996       /*
997        * Each argument is in the form:
998        *    <argument> = <argument_value>
999        *
1000        * So we setup the right pointers here, argument to the beginning
1001        * of the argument, argument_value to the beginning of the argument_value.
1002        */
1003       argument = bp;
1004       argument_value = strchr(bp, '=');
1005       if (!argument_value) {
1006          Jmsg(ctx, M_FATAL, "python-fd: Illegal argument %s without value\n", argument);
1007          Dmsg(ctx, debuglevel, "python-fd: Illegal argument %s without value\n", argument);
1008          goto bail_out;
1009       }
1010       *argument_value++ = '\0';
1011 
1012       /*
1013        * See if there are more arguments and setup for the next run.
1014        */
1015       bp = argument_value;
1016       do {
1017          bp = strchr(bp, ':');
1018          if (bp) {
1019             if (*(bp - 1) != '\\') {
1020                *bp++ = '\0';
1021                break;
1022             } else {
1023                bp++;
1024             }
1025          }
1026       } while (bp);
1027 
1028       found = false;
1029       for (i = 0; plugin_arguments[i].name; i++) {
1030          if (Bstrcasecmp(argument, plugin_arguments[i].name)) {
1031             char **str_destination = NULL;
1032             bool *bool_destination = NULL;
1033 
1034             switch (plugin_arguments[i].type) {
1035             case argument_module_path:
1036                str_destination = &p_ctx->module_path;
1037                break;
1038             case argument_module_name:
1039                str_destination = &p_ctx->module_name;
1040                break;
1041             default:
1042                break;
1043             }
1044 
1045             /*
1046              * Keep the first value, ignore any next setting.
1047              */
1048             if (str_destination) {
1049                if (keep_existing) {
1050                   SetStringIfNull(str_destination, argument_value);
1051                } else {
1052                   SetString(str_destination, argument_value);
1053                }
1054             }
1055 
1056             /*
1057              * Set any boolean variable.
1058              */
1059             if (bool_destination) {
1060                *bool_destination = ParseBoolean(argument_value);
1061             }
1062 
1063             /*
1064              * When we have a match break the loop.
1065              */
1066             found = true;
1067             break;
1068          }
1069       }
1070 
1071       /*
1072        * If we didn't consume this parameter we add it to the plugin_options list.
1073        */
1074       if (!found) {
1075          PoolMem option(PM_FNAME);
1076 
1077          if (cnt) {
1078             Mmsg(option, ":%s=%s", argument, argument_value);
1079             PmStrcat(plugin_options, option.c_str());
1080          } else {
1081             Mmsg(option, "%s=%s", argument, argument_value);
1082             PmStrcat(plugin_options, option.c_str());
1083          }
1084          cnt++;
1085       }
1086    }
1087 
1088    if (cnt > 0) {
1089       PmStrcat(plugin_options, ":");
1090    }
1091 
1092    return bRC_OK;
1093 
1094 bail_out:
1095    return bRC_Error;
1096 }
1097 
1098 /**
1099  * Work around API changes in Python versions.
1100  * These function abstract the storage and retrieval of the bpContext
1101  * which is passed to the Python methods and which the method can pass
1102  * back and which allow the callback function to understand what bpContext
1103  * its talking about.
1104  */
1105 #if ((PY_VERSION_HEX < 0x02070000) || \
1106      ((PY_VERSION_HEX >= 0x03000000) && \
1107       (PY_VERSION_HEX < 0x03010000)))
1108 /**
1109  * Python version before 2.7 and 3.0.
1110  */
PyCreatebpContext(bpContext * ctx)1111 static PyObject *PyCreatebpContext(bpContext *ctx)
1112 {
1113    /*
1114     * Setup a new CObject which holds the bpContext structure used here internally.
1115     */
1116    return PyCObject_FromVoidPtr((void *)ctx, NULL);
1117 }
1118 
PyGetbpContext(PyObject * pyCtx)1119 static bpContext *PyGetbpContext(PyObject *pyCtx)
1120 {
1121    return (bpContext *)PyCObject_AsVoidPtr(pyCtx);
1122 }
1123 #else
1124 /**
1125  * Python version after 2.6 and 3.1.
1126  */
PyCreatebpContext(bpContext * ctx)1127 static PyObject *PyCreatebpContext(bpContext *ctx)
1128 {
1129    /*
1130     * Setup a new Capsule which holds the bpContext structure used here internally.
1131     */
1132    return PyCapsule_New((void *)ctx, "bareos.bpContext", NULL);
1133 }
1134 
PyGetbpContext(PyObject * pyCtx)1135 static bpContext *PyGetbpContext(PyObject *pyCtx)
1136 {
1137    return (bpContext *)PyCapsule_GetPointer(pyCtx, "bareos.bpContext");
1138 }
1139 #endif
1140 
1141 /**
1142  * Convert a return value into a bRC enum value.
1143  */
conv_python_retval(PyObject * pRetVal)1144 static inline bRC conv_python_retval(PyObject *pRetVal)
1145 {
1146    return (bRC)PyInt_AsLong(pRetVal);
1147 }
1148 
1149 /**
1150  * Convert a return value from bRC enum value into Python Object.
1151  */
conv_retval_python(bRC retval)1152 static inline PyObject *conv_retval_python(bRC retval)
1153 {
1154    return (PyObject *)PyInt_FromLong((int)retval);
1155 }
1156 
1157 /**
1158  * Handle a Python error.
1159  *
1160  * Python equivalent:
1161  *
1162  * import traceback, sys
1163  * return "".join(traceback.format_exception(sys.exc_type,
1164  *    sys.exc_value, sys.exc_traceback))
1165  */
PyErrorHandler(bpContext * ctx,int msgtype)1166 static void PyErrorHandler(bpContext *ctx, int msgtype)
1167 {
1168    PyObject *type, *value, *traceback;
1169    PyObject *tracebackModule;
1170    char *error_string;
1171 
1172    PyErr_Fetch(&type, &value, &traceback);
1173 
1174    tracebackModule = PyImport_ImportModule("traceback");
1175    if (tracebackModule != NULL) {
1176       PyObject *tbList, *emptyString, *strRetval;
1177 
1178       tbList = PyObject_CallMethod(tracebackModule,
1179                                    (char *)"format_exception",
1180                                    (char *)"OOO",
1181                                    type,
1182                                    value == NULL ? Py_None : value,
1183                                    traceback == NULL ? Py_None : traceback);
1184 
1185       emptyString = PyString_FromString("");
1186       strRetval = PyObject_CallMethod(emptyString,
1187                                       (char *)"join",
1188                                       (char *)"O", tbList);
1189 
1190       error_string = bstrdup(PyString_AsString(strRetval));
1191 
1192       Py_DECREF(tbList);
1193       Py_DECREF(emptyString);
1194       Py_DECREF(strRetval);
1195       Py_DECREF(tracebackModule);
1196    } else {
1197       error_string = bstrdup("Unable to import traceback module.");
1198    }
1199 
1200    Py_DECREF(type);
1201    Py_XDECREF(value);
1202    Py_XDECREF(traceback);
1203 
1204    Dmsg(ctx, debuglevel, "python-fd: %s\n", error_string);
1205    if (msgtype) {
1206       Jmsg(ctx, msgtype, "python-fd: %s\n", error_string);
1207    }
1208 
1209    free(error_string);
1210 }
1211 
1212 /**
1213  * Initial load of the Python module.
1214  *
1215  * Based on the parsed plugin options we set some prerequisits like the
1216  * module path and the module to load. We also load the dictionary used
1217  * for looking up the Python methods.
1218  */
PyLoadModule(bpContext * ctx,void * value)1219 static bRC PyLoadModule(bpContext *ctx, void *value)
1220 {
1221    bRC retval = bRC_Error;
1222    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
1223    PyObject *sysPath,
1224             *mPath,
1225             *pName,
1226             *pFunc;
1227 
1228    /*
1229     * See if we already setup the python search path.
1230     */
1231    if (!p_ctx->python_path_set) {
1232       /*
1233        * Extend the Python search path with the given module_path.
1234        */
1235       if (p_ctx->module_path) {
1236          sysPath = PySys_GetObject((char *)"path");
1237          mPath = PyString_FromString(p_ctx->module_path);
1238          PyList_Append(sysPath, mPath);
1239          Py_DECREF(mPath);
1240          p_ctx->python_path_set = true;
1241       }
1242    }
1243 
1244    /*
1245     * See if we already setup the module structure.
1246     */
1247    if (!p_ctx->pInstance) {
1248       /*
1249        * Make our callback methods available for Python.
1250        */
1251 #if (PY_VERSION_HEX >  0x03050000)
1252       p_ctx->pInstance = PyModule_Create(&BareosFDModuleDef);
1253 #else
1254       p_ctx->pInstance = Py_InitModule("bareosfd", BareosFDMethods);
1255 #endif
1256 
1257       /*
1258        * Fill in the slots of PyRestoreObject
1259        */
1260       PyRestoreObjectType.tp_new = PyType_GenericNew;
1261       if (PyType_Ready(&PyRestoreObjectType) < 0) {
1262          goto cleanup;
1263       }
1264 
1265       /*
1266        * Fill in the slots of PyStatPacket
1267        */
1268       PyStatPacketType.tp_new = PyType_GenericNew;
1269       if (PyType_Ready(&PyStatPacketType) < 0) {
1270          goto cleanup;
1271       }
1272 
1273       /*
1274        * Fill in the slots of PySavePacket
1275        */
1276       PySavePacketType.tp_new = PyType_GenericNew;
1277       if (PyType_Ready(&PySavePacketType) < 0) {
1278          goto cleanup;
1279       }
1280 
1281       /*
1282        * Fill in the slots of PyRestorePacket
1283        */
1284       PyRestorePacketType.tp_new = PyType_GenericNew;
1285       if (PyType_Ready(&PyRestorePacketType) < 0) {
1286          goto cleanup;
1287       }
1288 
1289       /*
1290        * Fill in the slots of PyIoPacketType
1291        */
1292       PyIoPacketType.tp_new = PyType_GenericNew;
1293       if (PyType_Ready(&PyIoPacketType) < 0) {
1294          goto cleanup;
1295       }
1296 
1297       /*
1298        * Fill in the slots of PyAclPacketType
1299        */
1300       PyAclPacketType.tp_new = PyType_GenericNew;
1301       if (PyType_Ready(&PyAclPacketType) < 0) {
1302          goto cleanup;
1303       }
1304 
1305       /*
1306        * Fill in the slots of PyXattrPacketType
1307        */
1308       PyXattrPacketType.tp_new = PyType_GenericNew;
1309       if (PyType_Ready(&PyXattrPacketType) < 0) {
1310          goto cleanup;
1311       }
1312 
1313       /*
1314        * Add the types to the module
1315        */
1316       Py_INCREF(&PyRestoreObjectType);
1317       PyModule_AddObject(p_ctx->pInstance, "RestoreObject", (PyObject *)&PyRestoreObjectType);
1318 
1319       Py_INCREF(&PyStatPacketType);
1320       PyModule_AddObject(p_ctx->pInstance, "StatPacket", (PyObject *)&PyStatPacketType);
1321 
1322       Py_INCREF(&PySavePacketType);
1323       PyModule_AddObject(p_ctx->pInstance, "SavePacket", (PyObject *)&PySavePacketType);
1324 
1325       Py_INCREF(&PyRestorePacketType);
1326       PyModule_AddObject(p_ctx->pInstance, "RestorePacket", (PyObject *)&PyRestorePacketType);
1327 
1328       Py_INCREF(&PyIoPacketType);
1329       PyModule_AddObject(p_ctx->pInstance, "IoPacket", (PyObject *)&PyIoPacketType);
1330 
1331       Py_INCREF(&PyAclPacketType);
1332       PyModule_AddObject(p_ctx->pInstance, "AclPacket", (PyObject *)&PyAclPacketType);
1333 
1334       Py_INCREF(&PyXattrPacketType);
1335       PyModule_AddObject(p_ctx->pInstance, "XattrPacket", (PyObject *)&PyXattrPacketType);
1336    }
1337 
1338    /*
1339     * Try to load the Python module by name.
1340     */
1341    if (p_ctx->module_name) {
1342       Dmsg(ctx, debuglevel, "python-fd: Trying to load module with name %s\n", p_ctx->module_name);
1343       pName = PyString_FromString(p_ctx->module_name);
1344       p_ctx->pModule = PyImport_Import(pName);
1345       Py_DECREF(pName);
1346 
1347       if (!p_ctx->pModule) {
1348          Dmsg(ctx, debuglevel, "python-fd: Failed to load module with name %s\n", p_ctx->module_name);
1349          goto bail_out;
1350       }
1351 
1352       Dmsg(ctx, debuglevel, "python-fd: Successfully loaded module with name %s\n", p_ctx->module_name);
1353 
1354       /*
1355        * Get the Python dictionary for lookups in the Python namespace.
1356        */
1357       p_ctx->pDict = PyModule_GetDict(p_ctx->pModule); /* Borrowed reference */
1358 
1359       /*
1360        * Encode the bpContext so a Python method can pass it in on calling back.
1361        */
1362       p_ctx->bpContext = PyCreatebpContext(ctx);
1363 
1364       /*
1365        * Lookup the load_bareos_plugin() function in the python module.
1366        */
1367       pFunc = PyDict_GetItemString(p_ctx->pDict, "load_bareos_plugin"); /* Borrowed reference */
1368       if (pFunc && PyCallable_Check(pFunc)) {
1369          PyObject *pPluginDefinition,
1370                   *pRetVal;
1371 
1372          pPluginDefinition = PyString_FromString((char *)value);
1373          if (!pPluginDefinition) {
1374             goto bail_out;
1375          }
1376 
1377          pRetVal = PyObject_CallFunctionObjArgs(pFunc, p_ctx->bpContext, pPluginDefinition, NULL);
1378          Py_DECREF(pPluginDefinition);
1379 
1380          if (!pRetVal) {
1381             goto bail_out;
1382          } else {
1383             retval = conv_python_retval(pRetVal);
1384             Py_DECREF(pRetVal);
1385          }
1386       } else {
1387          Dmsg(ctx, debuglevel, "python-fd: Failed to find function named load_bareos_plugins()\n");
1388          goto bail_out;
1389       }
1390 
1391       /*
1392        * Keep track we successfully loaded.
1393        */
1394       p_ctx->python_loaded = true;
1395    }
1396 
1397    return retval;
1398 
1399 cleanup:
1400    p_ctx->pInstance = NULL;
1401 
1402 bail_out:
1403    if (PyErr_Occurred()) {
1404       PyErrorHandler(ctx, M_FATAL);
1405    }
1406 
1407    return retval;
1408 }
1409 
1410 /**
1411  * Any plugin options which are passed in are dispatched here to a Python method and it
1412  * can parse the plugin options. This function is also called after PyLoadModule() has
1413  * loaded the Python module and made sure things are operational. Normally you would only get
1414  * one set of plugin options but for a restore overrides can be passed in before the actual
1415  * plugin options are restored as part of the restore stream handling.
1416  */
PyParsePluginDefinition(bpContext * ctx,void * value)1417 static bRC PyParsePluginDefinition(bpContext *ctx, void *value)
1418 {
1419    bRC retval = bRC_Error;
1420    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
1421    PyObject *pFunc;
1422 
1423    /*
1424     * Lookup the parse_plugin_definition() function in the python module.
1425     */
1426    pFunc = PyDict_GetItemString(p_ctx->pDict, "parse_plugin_definition"); /* Borrowed reference */
1427    if (pFunc && PyCallable_Check(pFunc)) {
1428       PyObject *pPluginDefinition,
1429                *pRetVal;
1430 
1431       pPluginDefinition = PyString_FromString((char *)value);
1432       if (!pPluginDefinition) {
1433          goto bail_out;
1434       }
1435 
1436       pRetVal = PyObject_CallFunctionObjArgs(pFunc, p_ctx->bpContext, pPluginDefinition, NULL);
1437       Py_DECREF(pPluginDefinition);
1438 
1439       if (!pRetVal) {
1440          goto bail_out;
1441       } else {
1442          retval = conv_python_retval(pRetVal);
1443          Py_DECREF(pRetVal);
1444       }
1445 
1446       return retval;
1447    } else {
1448       Dmsg(ctx, debuglevel, "python-fd: Failed to find function named parse_plugin_definition()\n");
1449       return bRC_Error;
1450    }
1451 
1452 bail_out:
1453    if (PyErr_Occurred()) {
1454       PyErrorHandler(ctx, M_FATAL);
1455    }
1456 
1457    return retval;
1458 }
1459 
PyGetPluginValue(bpContext * ctx,pVariable var,void * value)1460 static bRC PyGetPluginValue(bpContext *ctx, pVariable var, void *value)
1461 {
1462    return bRC_OK;
1463 }
1464 
PySetPluginValue(bpContext * ctx,pVariable var,void * value)1465 static bRC PySetPluginValue(bpContext *ctx, pVariable var, void *value)
1466 {
1467    return bRC_OK;
1468 }
1469 
PyHandlePluginEvent(bpContext * ctx,bEvent * event,void * value)1470 static bRC PyHandlePluginEvent(bpContext *ctx, bEvent *event, void *value)
1471 {
1472    bRC retval = bRC_Error;
1473    plugin_ctx *p_ctx = (plugin_ctx *)ctx->pContext;
1474    PyObject *pFunc;
1475 
1476    /*
1477     * Lookup the handle_plugin_event() function in the python module.
1478     */
1479    pFunc = PyDict_GetItemString(p_ctx->pDict, "handle_plugin_event"); /* Borrowed reference */
1480    if (pFunc && PyCallable_Check(pFunc)) {
1481       PyObject *pEventType,
1482                *pRetVal;
1483 
1484       pEventType = PyInt_FromLong(event->eventType);
1485 
1486       pRetVal = PyObject_CallFunctionObjArgs(pFunc, p_ctx->bpContext, pEventType, NULL);
1487       Py_DECREF(pEventType);
1488 
1489       if (!pRetVal) {
1490          goto bail_out;
1491       } else {
1492          retval = conv_python_retval(pRetVal);
1493          Py_DECREF(pRetVal);
1494       }
1495    } else {
1496       Dmsg(ctx, debuglevel, "python-fd: Failed to find function named handle_plugin_event()\n");
1497    }
1498 
1499    return retval;
1500 
1501 bail_out:
1502    if (PyErr_Occurred()) {
1503       PyErrorHandler(ctx, M_FATAL);
1504    }
1505 
1506    return retval;
1507 }
1508 
NativeToPyStatPacket(struct stat * statp)1509 static inline PyStatPacket *NativeToPyStatPacket(struct stat *statp)
1510 {
1511    PyStatPacket *pStatp = PyObject_New(PyStatPacket, &PyStatPacketType);
1512 
1513    if (pStatp) {
1514       pStatp->dev = statp->st_dev;
1515       pStatp->ino = statp->st_ino;
1516       pStatp->mode = statp->st_mode;
1517       pStatp->nlink = statp->st_nlink;
1518       pStatp->uid = statp->st_uid;
1519       pStatp->gid = statp->st_gid;
1520       pStatp->rdev = statp->st_rdev;
1521       pStatp->size = statp->st_size;
1522       pStatp->atime = statp->st_atime;
1523       pStatp->mtime = statp->st_mtime;
1524       pStatp->ctime = statp->st_ctime;
1525       pStatp->blksize = statp->st_blksize;
1526       pStatp->blocks = statp->st_blocks;
1527    }
1528 
1529    return pStatp;
1530 }
1531 
PyStatPacketToNative(PyStatPacket * pStatp,struct stat * statp)1532 static inline void PyStatPacketToNative(PyStatPacket *pStatp, struct stat *statp)
1533 {
1534    statp->st_dev = pStatp->dev;
1535    statp->st_ino = pStatp->ino;
1536    statp->st_mode = pStatp->mode;
1537    statp->st_nlink = pStatp->nlink;
1538    statp->st_uid = pStatp->uid;
1539    statp->st_gid = pStatp->gid;
1540    statp->st_rdev = pStatp->rdev;
1541    statp->st_size = pStatp->size;
1542    statp->st_atime = pStatp->atime;
1543    statp->st_mtime = pStatp->mtime;
1544    statp->st_ctime = pStatp->ctime;
1545    statp->st_blksize = pStatp->blksize;
1546    statp->st_blocks = pStatp->blocks;
1547 }
1548 
NativeToPySavePacket(struct save_pkt * sp)1549 static inline PySavePacket *NativeToPySavePacket(struct save_pkt *sp)
1550 {
1551    PySavePacket *pSavePkt = PyObject_New(PySavePacket, &PySavePacketType);
1552 
1553    if (pSavePkt) {
1554       /*
1555        * Initialize the Python SavePkt with the data we got passed in.
1556        */
1557       if (sp->fname) {
1558          pSavePkt->fname = PyString_FromString(sp->fname);
1559       } else {
1560          pSavePkt->fname = NULL;
1561       }
1562 
1563       if (sp->link) {
1564          pSavePkt->link = PyString_FromString(sp->link);
1565       } else {
1566          pSavePkt->link = NULL;
1567       }
1568 
1569       if (sp->statp.st_mode) {
1570          pSavePkt->statp = (PyObject *)NativeToPyStatPacket(&sp->statp);
1571       } else {
1572          pSavePkt->statp = NULL;
1573       }
1574 
1575       pSavePkt->type = sp->type;
1576       pSavePkt->flags = PyByteArray_FromStringAndSize(sp->flags, sizeof(sp->flags));
1577       pSavePkt->no_read = sp->no_read;
1578       pSavePkt->portable = sp->portable;
1579       pSavePkt->accurate_found = sp->accurate_found;
1580       pSavePkt->cmd = sp->cmd;
1581       pSavePkt->save_time = sp->save_time;
1582       pSavePkt->delta_seq = sp->delta_seq;
1583       pSavePkt->object_name = NULL;
1584       pSavePkt->object = NULL;
1585       pSavePkt->object_len = sp->object_len;
1586       pSavePkt->object_index = sp->index;
1587    }
1588 
1589    return pSavePkt;
1590 }
1591 
PySavePacketToNative(PySavePacket * pSavePkt,struct save_pkt * sp,struct plugin_ctx * p_ctx,bool is_options_plugin)1592 static inline bool PySavePacketToNative(PySavePacket *pSavePkt, struct save_pkt *sp,
1593                                         struct plugin_ctx *p_ctx, bool is_options_plugin)
1594 {
1595    /*
1596     * See if this is for an Options Plugin.
1597     */
1598    if (!is_options_plugin) {
1599       /*
1600        * Only copy back the arguments that are allowed to change.
1601        */
1602       if (pSavePkt->fname) {
1603          /*
1604           * As this has to linger as long as the backup is running we save it in our plugin context.
1605           */
1606          if (PyString_Check(pSavePkt->fname)) {
1607             if (p_ctx->fname) {
1608                free(p_ctx->fname);
1609             }
1610             p_ctx->fname = bstrdup(PyString_AsString(pSavePkt->fname));
1611             sp->fname = p_ctx->fname;
1612          }
1613       } else {
1614          goto bail_out;
1615       }
1616 
1617       /*
1618        * Optional field.
1619        */
1620       if (pSavePkt->link) {
1621          /*
1622           * As this has to linger as long as the backup is running we save it in our plugin context.
1623           */
1624          if (PyString_Check(pSavePkt->link)) {
1625             if (p_ctx->link) {
1626                free(p_ctx->link);
1627             }
1628             p_ctx->link = bstrdup(PyString_AsString(pSavePkt->link));
1629             sp->link = p_ctx->link;
1630          }
1631       }
1632 
1633       /*
1634        * Handle the stat structure.
1635        */
1636       if (pSavePkt->statp) {
1637          PyStatPacketToNative((PyStatPacket *)pSavePkt->statp, &sp->statp);
1638       } else {
1639          goto bail_out;
1640       }
1641 
1642       sp->type = pSavePkt->type;
1643 
1644       if (PyByteArray_Check(pSavePkt->flags)) {
1645          char *flags;
1646 
1647          if (PyByteArray_Size(pSavePkt->flags) != sizeof(sp->flags)) {
1648             goto bail_out;
1649          }
1650 
1651          if ((flags = PyByteArray_AsString(pSavePkt->flags))) {
1652             memcpy(sp->flags, flags, sizeof(sp->flags));
1653          } else {
1654             goto bail_out;
1655          }
1656       } else {
1657          goto bail_out;
1658       }
1659 
1660       /*
1661        * Special code for handling restore objects.
1662        */
1663       if (IS_FT_OBJECT(sp->type)) {
1664          /*
1665           * See if a proper restore object was created.
1666           */
1667          if (pSavePkt->object_len > 0) {
1668             /*
1669              * As this has to linger as long as the backup is running we save it in our plugin context.
1670              */
1671             if (pSavePkt->object_name &&
1672                 pSavePkt->object &&
1673                 PyString_Check(pSavePkt->object_name) &&
1674                 PyByteArray_Check(pSavePkt->object)) {
1675                char *buf;
1676 
1677                if (p_ctx->object_name) {
1678                   free(p_ctx->object_name);
1679                }
1680                p_ctx->object_name = bstrdup(PyString_AsString(pSavePkt->object_name));
1681                sp->object_name = p_ctx->object_name;
1682 
1683                sp->object_len = pSavePkt->object_len;
1684                sp->index = pSavePkt->object_index;
1685 
1686                if ((buf = PyByteArray_AsString(pSavePkt->object))) {
1687                   if (p_ctx->object) {
1688                      free(p_ctx->object);
1689                   }
1690                   p_ctx->object = (char *)malloc(pSavePkt->object_len);
1691                   memcpy(p_ctx->object, buf, pSavePkt->object_len);
1692                   sp->object = p_ctx->object;
1693                } else {
1694                   goto bail_out;
1695                }
1696             } else {
1697                goto bail_out;
1698             }
1699          } else {
1700             goto bail_out;
1701          }
1702       } else {
1703          sp->no_read = pSavePkt->no_read;
1704          sp->delta_seq = pSavePkt->delta_seq;
1705       }
1706    } else {
1707       sp->no_read = pSavePkt->no_read;
1708       sp->delta_seq = pSavePkt->delta_seq;
1709 
1710       if (PyByteArray_Check(pSavePkt->flags)) {
1711          char *flags;
1712 
1713          if (PyByteArray_Size(pSavePkt->flags) != sizeof(sp->flags)) {
1714             goto bail_out;
1715          }
1716 
1717          if ((flags = PyByteArray_AsString(pSavePkt->flags))) {
1718             memcpy(sp->flags, flags, sizeof(sp->flags));
1719          } else {
1720             goto bail_out;
1721          }
1722       } else {
1723          goto bail_out;
1724       }
1725    }
1726 
1727    return true;
1728 
1729 bail_out:
1730    return false;
1731 }
1732 
1733 /**
1734  * Called when starting to backup a file. Here the plugin must
1735  * return the "stat" packet for the directory/file and provide
1736  * certain information so that Bareos knows what the file is.
1737  * The plugin can create "Virtual" files by giving them a
1738  * name that is not normally found on the file system.
1739  */
PyStartBackupFile(bpContext * ctx,struct save_pkt * sp)1740 static bRC PyStartBackupFile(bpContext *ctx, struct save_pkt *sp)
1741 {
1742    bRC retval = bRC_Error;
1743    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
1744    PyObject *pFunc;
1745 
1746    /*
1747     * Lookup the start_backup_file() function in the python module.
1748     */
1749    pFunc = PyDict_GetItemString(p_ctx->pDict, "start_backup_file"); /* Borrowed reference */
1750    if (pFunc && PyCallable_Check(pFunc)) {
1751       PySavePacket *pSavePkt;
1752       PyObject *pRetVal;
1753 
1754       pSavePkt = NativeToPySavePacket(sp);
1755       if (!pSavePkt) {
1756          goto bail_out;
1757       }
1758 
1759       pRetVal = PyObject_CallFunctionObjArgs(pFunc, p_ctx->bpContext, (PyObject *)pSavePkt, NULL);
1760       if (!pRetVal) {
1761          Py_DECREF((PyObject *)pSavePkt);
1762          goto bail_out;
1763       } else {
1764          retval = conv_python_retval(pRetVal);
1765          Py_DECREF(pRetVal);
1766 
1767          if (!PySavePacketToNative(pSavePkt, sp, p_ctx, false)) {
1768             Py_DECREF((PyObject *)pSavePkt);
1769             goto bail_out;
1770          }
1771          Py_DECREF((PyObject *)pSavePkt);
1772       }
1773    } else {
1774       Dmsg(ctx, debuglevel, "python-fd: Failed to find function named start_backup_file()\n");
1775    }
1776 
1777    return retval;
1778 
1779 bail_out:
1780    if (PyErr_Occurred()) {
1781       PyErrorHandler(ctx, M_FATAL);
1782    }
1783 
1784    return retval;
1785 }
1786 
1787 /**
1788  * Called at the end of backing up a file for a command plugin.
1789  * If the plugin's work is done, it should return bRC_OK.
1790  * If the plugin wishes to create another file and back it up,
1791  * then it must return bRC_More (not yet implemented).
1792  */
PyEndBackupFile(bpContext * ctx)1793 static bRC PyEndBackupFile(bpContext *ctx)
1794 {
1795    bRC retval = bRC_Error;
1796    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
1797    PyObject *pFunc;
1798 
1799    /*
1800     * Lookup the end_backup_file() function in the python module.
1801     */
1802    pFunc = PyDict_GetItemString(p_ctx->pDict, "end_backup_file"); /* Borrowed reference */
1803    if (pFunc && PyCallable_Check(pFunc)) {
1804       PyObject *pRetVal;
1805 
1806       pRetVal = PyObject_CallFunctionObjArgs(pFunc, p_ctx->bpContext, NULL);
1807       if (!pRetVal) {
1808          goto bail_out;
1809       } else {
1810          retval = conv_python_retval(pRetVal);
1811       }
1812    } else {
1813       Dmsg(ctx, debuglevel, "python-fd: Failed to find function named end_backup_file()\n");
1814    }
1815 
1816    return retval;
1817 
1818 bail_out:
1819    if (PyErr_Occurred()) {
1820       PyErrorHandler(ctx, M_FATAL);
1821    }
1822 
1823    return retval;
1824 }
1825 
NativeToPyIoPacket(struct io_pkt * io)1826 static inline PyIoPacket *NativeToPyIoPacket(struct io_pkt *io)
1827 {
1828    PyIoPacket *pIoPkt = PyObject_New(PyIoPacket, &PyIoPacketType);
1829 
1830    if (pIoPkt) {
1831       /*
1832        * Initialize the Python IoPkt with the data we got passed in.
1833        */
1834       pIoPkt->func = io->func;
1835       pIoPkt->count = io->count;
1836       pIoPkt->flags = io->flags;
1837       pIoPkt->mode = io->mode;
1838       pIoPkt->fname = io->fname;
1839       pIoPkt->whence = io->whence;
1840       pIoPkt->offset = io->offset;
1841       if (io->func == IO_WRITE && io->count > 0) {
1842          /*
1843           * Only initialize the buffer with read data when we are writing and there is data.
1844           */
1845          pIoPkt->buf = PyByteArray_FromStringAndSize(io->buf, io->count);
1846          if (!pIoPkt->buf) {
1847             Py_DECREF((PyObject *)pIoPkt);
1848             return (PyIoPacket *)NULL;
1849          }
1850       } else {
1851          pIoPkt->buf = NULL;
1852       }
1853 
1854       /*
1855        * These must be set by the Python function but we initialize them to zero
1856        * to be sure they have some valid setting an not random data.
1857        */
1858       pIoPkt->io_errno = 0;
1859       pIoPkt->lerror = 0;
1860       pIoPkt->win32 = false;
1861       pIoPkt->status = 0;
1862    }
1863 
1864    return pIoPkt;
1865 }
1866 
PyIoPacketToNative(PyIoPacket * pIoPkt,struct io_pkt * io)1867 static inline bool PyIoPacketToNative(PyIoPacket *pIoPkt, struct io_pkt *io)
1868 {
1869    /*
1870     * Only copy back the arguments that are allowed to change.
1871     */
1872    io->io_errno = pIoPkt->io_errno;
1873    io->lerror = pIoPkt->lerror;
1874    io->win32 = pIoPkt->win32;
1875    io->status = pIoPkt->status;
1876    if (io->func == IO_READ && io->status > 0) {
1877       /*
1878        * Only copy back the data when doing a read and there is data.
1879        */
1880       if (PyByteArray_Check(pIoPkt->buf)) {
1881          char *buf;
1882 
1883          if (PyByteArray_Size(pIoPkt->buf) > io->count || io->status > io->count) {
1884             return false;
1885          }
1886 
1887          if (!(buf = PyByteArray_AsString(pIoPkt->buf))) {
1888             return false;
1889          }
1890          memcpy(io->buf, buf, io->status);
1891       }
1892    }
1893 
1894    return true;
1895 }
1896 
1897 /**
1898  * Do actual I/O. Bareos calls this after startBackupFile
1899  * or after startRestoreFile to do the actual file
1900  * input or output.
1901  */
PyPluginIO(bpContext * ctx,struct io_pkt * io)1902 static bRC PyPluginIO(bpContext *ctx, struct io_pkt *io)
1903 {
1904    bRC retval = bRC_Error;
1905    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
1906    PyObject *pFunc;
1907 
1908    /*
1909     * Lookup the plugin_io() function in the python module.
1910     */
1911    pFunc = PyDict_GetItemString(p_ctx->pDict, "plugin_io"); /* Borrowed reference */
1912    if (pFunc && PyCallable_Check(pFunc)) {
1913       PyIoPacket *pIoPkt;
1914       PyObject *pRetVal;
1915 
1916       pIoPkt = NativeToPyIoPacket(io);
1917       if (!pIoPkt) {
1918          goto bail_out;
1919       }
1920 
1921       pRetVal = PyObject_CallFunctionObjArgs(pFunc, p_ctx->bpContext, (PyObject *)pIoPkt, NULL);
1922       if (!pRetVal) {
1923          Py_DECREF((PyObject *)pIoPkt);
1924          goto bail_out;
1925       } else {
1926          retval = conv_python_retval(pRetVal);
1927          Py_DECREF(pRetVal);
1928 
1929          if (!PyIoPacketToNative(pIoPkt, io)) {
1930             Py_DECREF((PyObject *)pIoPkt);
1931             goto bail_out;
1932          }
1933       }
1934       Py_DECREF((PyObject *)pIoPkt);
1935    } else {
1936       Dmsg(ctx, debuglevel, "python-fd: Failed to find function named plugin_io()\n");
1937    }
1938 
1939    return retval;
1940 
1941 bail_out:
1942    if (PyErr_Occurred()) {
1943       PyErrorHandler(ctx, M_FATAL);
1944    }
1945 
1946    io->status = -1;
1947 
1948    return retval;
1949 }
1950 
1951 /**
1952  * Called when the first record is read from the Volume that was previously written by the command plugin.
1953  */
PyStartRestoreFile(bpContext * ctx,const char * cmd)1954 static bRC PyStartRestoreFile(bpContext *ctx, const char *cmd)
1955 {
1956    bRC retval = bRC_Error;
1957    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
1958    PyObject *pFunc;
1959 
1960    /*
1961     * Lookup the start_restore_file() function in the python module.
1962     */
1963    pFunc = PyDict_GetItemString(p_ctx->pDict, "start_restore_file"); /* Borrowed reference */
1964    if (pFunc && PyCallable_Check(pFunc)) {
1965       PyObject *pCmd,
1966                *pRetVal;
1967 
1968       pCmd = PyString_FromString(cmd);
1969       if (!pCmd) {
1970          goto bail_out;
1971       }
1972 
1973       pRetVal = PyObject_CallFunctionObjArgs(pFunc, p_ctx->bpContext, pCmd, NULL);
1974       Py_DECREF(pCmd);
1975 
1976       if (!pRetVal) {
1977          goto bail_out;
1978       } else {
1979          retval = conv_python_retval(pRetVal);
1980          Py_DECREF(pRetVal);
1981       }
1982    } else {
1983       Dmsg(ctx, debuglevel, "python-fd: Failed to find function named start_restore_file()\n");
1984    }
1985 
1986    return retval;
1987 
1988 bail_out:
1989    if (PyErr_Occurred()) {
1990       PyErrorHandler(ctx, M_FATAL);
1991    }
1992 
1993    return retval;
1994 }
1995 
1996 /**
1997  * Called when a command plugin is done restoring a file.
1998  */
PyEndRestoreFile(bpContext * ctx)1999 static bRC PyEndRestoreFile(bpContext *ctx)
2000 {
2001    bRC retval = bRC_Error;
2002    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
2003    PyObject *pFunc;
2004 
2005    /*
2006     * Lookup the end_restore_file() function in the python module.
2007     */
2008    pFunc = PyDict_GetItemString(p_ctx->pDict, "end_restore_file"); /* Borrowed reference */
2009    if (pFunc && PyCallable_Check(pFunc)) {
2010       PyObject *pRetVal;
2011 
2012       pRetVal = PyObject_CallFunctionObjArgs(pFunc, p_ctx->bpContext, NULL);
2013       if (!pRetVal) {
2014          goto bail_out;
2015       } else {
2016          retval = conv_python_retval(pRetVal);
2017       }
2018    } else {
2019       Dmsg(ctx, debuglevel, "python-fd: Failed to find function named end_restore_file()\n");
2020    }
2021 
2022    return retval;
2023 
2024 bail_out:
2025    if (PyErr_Occurred()) {
2026       PyErrorHandler(ctx, M_FATAL);
2027    }
2028 
2029    return retval;
2030 }
2031 
NativeToPyRestorePacket(struct restore_pkt * rp)2032 static inline PyRestorePacket *NativeToPyRestorePacket(struct restore_pkt *rp)
2033 {
2034    PyRestorePacket *pRestorePacket = PyObject_New(PyRestorePacket, &PyRestorePacketType);
2035 
2036    if (pRestorePacket) {
2037       pRestorePacket->stream = rp->stream;
2038       pRestorePacket->data_stream = rp->data_stream;
2039       pRestorePacket->type = rp->type;
2040       pRestorePacket->file_index = rp->file_index;
2041       pRestorePacket->LinkFI = rp->LinkFI;
2042       pRestorePacket->uid = rp->uid;
2043       pRestorePacket->statp = (PyObject *)NativeToPyStatPacket(&rp->statp);
2044       pRestorePacket->attrEx = rp->attrEx;
2045       pRestorePacket->ofname = rp->ofname;
2046       pRestorePacket->olname = rp->olname;
2047       pRestorePacket->where = rp->where;
2048       pRestorePacket->RegexWhere = rp->RegexWhere;
2049       pRestorePacket->replace = rp->replace;
2050       pRestorePacket->create_status = rp->create_status;
2051    }
2052 
2053    return pRestorePacket;
2054 }
2055 
PyRestorePacketToNative(PyRestorePacket * pRestorePacket,struct restore_pkt * rp)2056 static inline void PyRestorePacketToNative(PyRestorePacket *pRestorePacket, struct restore_pkt *rp)
2057 {
2058    /*
2059     * Only copy back the fields that are allowed to be changed.
2060     */
2061    rp->create_status = pRestorePacket->create_status;
2062 }
2063 
2064 /**
2065  * Called for a command plugin to create a file during a Restore job before restoring the data.
2066  * This entry point is called before any I/O is done on the file. After this call,
2067  * Bareos will call pluginIO() to open the file for write.
2068  *
2069  * We must return in rp->create_status:
2070  *
2071  * CF_ERROR    -- error
2072  * CF_SKIP     -- skip processing this file
2073  * CF_EXTRACT  -- extract the file (i.e.call i/o routines)
2074  * CF_CREATED  -- created, but no content to extract (typically directories)
2075  */
PyCreateFile(bpContext * ctx,struct restore_pkt * rp)2076 static bRC PyCreateFile(bpContext *ctx, struct restore_pkt *rp)
2077 {
2078    bRC retval = bRC_Error;
2079    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
2080    PyObject *pFunc;
2081 
2082    if (!rp) {
2083       return bRC_Error;
2084    }
2085 
2086    /*
2087     * Lookup the create_file() function in the python module.
2088     */
2089    pFunc = PyDict_GetItemString(p_ctx->pDict, "create_file"); /* Borrowed reference */
2090    if (pFunc && PyCallable_Check(pFunc)) {
2091       PyRestorePacket *pRestorePacket;
2092       PyObject *pRetVal;
2093 
2094       pRestorePacket = NativeToPyRestorePacket(rp);
2095       if (!pRestorePacket) {
2096          goto bail_out;
2097       }
2098 
2099       pRetVal = PyObject_CallFunctionObjArgs(pFunc, p_ctx->bpContext, pRestorePacket, NULL);
2100       if (!pRetVal) {
2101          Py_DECREF(pRestorePacket);
2102          goto bail_out;
2103       } else {
2104          retval = conv_python_retval(pRetVal);
2105          Py_DECREF(pRetVal);
2106 
2107          PyRestorePacketToNative(pRestorePacket, rp);
2108          Py_DECREF(pRestorePacket);
2109       }
2110    } else {
2111       Dmsg(ctx, debuglevel, "python-fd: Failed to find function named create_file()\n");
2112    }
2113 
2114    return retval;
2115 
2116 bail_out:
2117    if (PyErr_Occurred()) {
2118       PyErrorHandler(ctx, M_FATAL);
2119    }
2120 
2121    return retval;
2122 }
2123 
PySetFileAttributes(bpContext * ctx,struct restore_pkt * rp)2124 static bRC PySetFileAttributes(bpContext *ctx, struct restore_pkt *rp)
2125 {
2126    bRC retval = bRC_Error;
2127    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
2128    PyObject *pFunc;
2129 
2130    if (!rp) {
2131       return bRC_Error;
2132    }
2133 
2134    /*
2135     * Lookup the set_file_attributes() function in the python module.
2136     */
2137    pFunc = PyDict_GetItemString(p_ctx->pDict, "set_file_attributes"); /* Borrowed reference */
2138    if (pFunc && PyCallable_Check(pFunc)) {
2139       PyRestorePacket *pRestorePacket;
2140       PyObject *pRetVal;
2141 
2142       pRestorePacket = NativeToPyRestorePacket(rp);
2143       if (!pRestorePacket) {
2144          goto bail_out;
2145       }
2146 
2147       pRetVal = PyObject_CallFunctionObjArgs(pFunc, p_ctx->bpContext, pRestorePacket, NULL);
2148       if (!pRetVal) {
2149          Py_DECREF(pRestorePacket);
2150          goto bail_out;
2151       } else {
2152          retval = conv_python_retval(pRetVal);
2153          Py_DECREF(pRetVal);
2154          Py_DECREF(pRestorePacket);
2155       }
2156    } else {
2157       Dmsg(ctx, debuglevel, "python-fd: Failed to find function named set_file_attributes()\n");
2158    }
2159 
2160    return retval;
2161 
2162 bail_out:
2163    if (PyErr_Occurred()) {
2164       PyErrorHandler(ctx, M_FATAL);
2165    }
2166 
2167    return retval;
2168 }
2169 
PyCheckFile(bpContext * ctx,char * fname)2170 static bRC PyCheckFile(bpContext *ctx, char *fname)
2171 {
2172    bRC retval = bRC_Error;
2173    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
2174    PyObject *pFunc;
2175 
2176    if (!fname) {
2177       return bRC_Error;
2178    }
2179 
2180    /*
2181     * Lookup the check_file() function in the python module.
2182     */
2183    pFunc = PyDict_GetItemString(p_ctx->pDict, "check_file"); /* Borrowed reference */
2184    if (pFunc && PyCallable_Check(pFunc)) {
2185       PyObject *pFname,
2186                *pRetVal;
2187 
2188       pFname = PyString_FromString(fname);
2189       pRetVal = PyObject_CallFunctionObjArgs(pFunc, p_ctx->bpContext, pFname, NULL);
2190       Py_DECREF(pFname);
2191 
2192       if (!pRetVal) {
2193          goto bail_out;
2194       } else {
2195          retval = conv_python_retval(pRetVal);
2196          Py_DECREF(pRetVal);
2197       }
2198    } else {
2199       Dmsg(ctx, debuglevel, "python-fd: Failed to find function named check_file()\n");
2200    }
2201 
2202    return retval;
2203 
2204 bail_out:
2205    if (PyErr_Occurred()) {
2206       PyErrorHandler(ctx, M_FATAL);
2207    }
2208 
2209    return retval;
2210 }
2211 
NativeToPyAclPacket(struct acl_pkt * ap)2212 static inline PyAclPacket *NativeToPyAclPacket(struct acl_pkt *ap)
2213 {
2214    PyAclPacket *pAclPacket = PyObject_New(PyAclPacket, &PyAclPacketType);
2215 
2216    if (pAclPacket) {
2217       pAclPacket->fname = ap->fname;
2218 
2219       if (ap->content_length && ap->content) {
2220          pAclPacket->content = PyByteArray_FromStringAndSize(ap->content, ap->content_length);
2221       } else {
2222          pAclPacket->content = NULL;
2223       }
2224    }
2225 
2226    return pAclPacket;
2227 }
2228 
PyAclPacketToNative(PyAclPacket * pAclPacket,struct acl_pkt * ap)2229 static inline bool PyAclPacketToNative(PyAclPacket *pAclPacket, struct acl_pkt *ap)
2230 {
2231    if (!pAclPacket->content) {
2232       return true;
2233    }
2234 
2235    if (PyByteArray_Check(pAclPacket->content)) {
2236       char *buf;
2237 
2238       ap->content_length = PyByteArray_Size(pAclPacket->content);
2239       if (ap->content_length <= 0 || !(buf = PyByteArray_AsString(pAclPacket->content))) {
2240          return false;
2241       }
2242 
2243       if (ap->content) {
2244          free(ap->content);
2245       }
2246       ap->content = (char *)malloc(ap->content_length);
2247       memcpy(ap->content, buf, ap->content_length);
2248    }
2249 
2250    return true;
2251 }
2252 
PyGetAcl(bpContext * ctx,acl_pkt * ap)2253 static bRC PyGetAcl(bpContext *ctx, acl_pkt *ap)
2254 {
2255    bRC retval = bRC_Error;
2256    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
2257    PyObject *pFunc;
2258 
2259    if (!ap) {
2260       return bRC_Error;
2261    }
2262 
2263    /*
2264     * Lookup the get_acl() function in the python module.
2265     */
2266    pFunc = PyDict_GetItemString(p_ctx->pDict, "get_acl"); /* Borrowed reference */
2267    if (pFunc && PyCallable_Check(pFunc)) {
2268       PyAclPacket *pAclPkt;
2269       PyObject *pRetVal;
2270 
2271       pAclPkt = NativeToPyAclPacket(ap);
2272       if (!pAclPkt) {
2273          goto bail_out;
2274       }
2275 
2276       pRetVal = PyObject_CallFunctionObjArgs(pFunc, p_ctx->bpContext, pAclPkt, NULL);
2277       if (!pRetVal) {
2278          Py_DECREF((PyObject *)pAclPkt);
2279          goto bail_out;
2280       } else {
2281          retval = conv_python_retval(pRetVal);
2282          Py_DECREF(pRetVal);
2283 
2284          if (!PyAclPacketToNative(pAclPkt, ap)) {
2285             Py_DECREF((PyObject *)pAclPkt);
2286             goto bail_out;
2287          }
2288          Py_DECREF(pAclPkt);
2289       }
2290    } else {
2291       Dmsg(ctx, debuglevel, "python-fd: Failed to find function named get_acl()\n");
2292    }
2293 
2294    return retval;
2295 
2296 bail_out:
2297    if (PyErr_Occurred()) {
2298       PyErrorHandler(ctx, M_FATAL);
2299    }
2300 
2301    return retval;
2302 }
2303 
PySetAcl(bpContext * ctx,acl_pkt * ap)2304 static bRC PySetAcl(bpContext *ctx, acl_pkt *ap)
2305 {
2306    bRC retval = bRC_Error;
2307    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
2308    PyObject *pFunc;
2309 
2310    if (!ap) {
2311       return bRC_Error;
2312    }
2313 
2314    /*
2315     * Lookup the set_acl() function in the python module.
2316     */
2317    pFunc = PyDict_GetItemString(p_ctx->pDict, "set_acl"); /* Borrowed reference */
2318    if (pFunc && PyCallable_Check(pFunc)) {
2319       PyAclPacket *pAclPkt;
2320       PyObject *pRetVal;
2321 
2322       pAclPkt = NativeToPyAclPacket(ap);
2323       if (!pAclPkt) {
2324          goto bail_out;
2325       }
2326 
2327       pRetVal = PyObject_CallFunctionObjArgs(pFunc, p_ctx->bpContext, pAclPkt, NULL);
2328       Py_DECREF(pAclPkt);
2329 
2330       if (!pRetVal) {
2331          goto bail_out;
2332       } else {
2333          retval = conv_python_retval(pRetVal);
2334          Py_DECREF(pRetVal);
2335       }
2336    } else {
2337       Dmsg(ctx, debuglevel, "python-fd: Failed to find function named set_acl()\n");
2338    }
2339 
2340    return retval;
2341 
2342 bail_out:
2343    if (PyErr_Occurred()) {
2344       PyErrorHandler(ctx, M_FATAL);
2345    }
2346 
2347    return retval;
2348 }
2349 
NativeToPyXattrPacket(struct xattr_pkt * xp)2350 static inline PyXattrPacket *NativeToPyXattrPacket(struct xattr_pkt *xp)
2351 {
2352    PyXattrPacket *pXattrPacket = PyObject_New(PyXattrPacket, &PyXattrPacketType);
2353 
2354    if (pXattrPacket) {
2355       pXattrPacket->fname = xp->fname;
2356 
2357       if (xp->name_length && xp->name) {
2358          pXattrPacket->name = PyByteArray_FromStringAndSize(xp->name, xp->name_length);
2359       } else {
2360          pXattrPacket->name = NULL;
2361       }
2362       if (xp->value_length && xp->value) {
2363          pXattrPacket->value = PyByteArray_FromStringAndSize(xp->value, xp->value_length);
2364       } else {
2365          pXattrPacket->value = NULL;
2366       }
2367    }
2368 
2369    return pXattrPacket;
2370 }
2371 
PyXattrPacketToNative(PyXattrPacket * pXattrPacket,struct xattr_pkt * xp)2372 static inline bool PyXattrPacketToNative(PyXattrPacket *pXattrPacket, struct xattr_pkt *xp)
2373 {
2374    if (!pXattrPacket->name) {
2375       return true;
2376    }
2377 
2378    if (PyByteArray_Check(pXattrPacket->name)) {
2379       char *buf;
2380 
2381       xp->name_length = PyByteArray_Size(pXattrPacket->name);
2382       if (xp->name_length <= 0 || !(buf = PyByteArray_AsString(pXattrPacket->name))) {
2383          return false;
2384       }
2385 
2386       if (xp->name) {
2387          free(xp->name);
2388       }
2389       xp->name = (char *)malloc(xp->name_length);
2390       memcpy(xp->name, buf, xp->name_length);
2391    }
2392 
2393    if (pXattrPacket->value && PyByteArray_Check(pXattrPacket->value)) {
2394       char *buf;
2395 
2396       xp->value_length = PyByteArray_Size(pXattrPacket->value);
2397       if (xp->name_length <= 0 || !(buf = PyByteArray_AsString(pXattrPacket->name))) {
2398          return false;
2399       }
2400 
2401       if (xp->value) {
2402          free(xp->value);
2403       }
2404       xp->value = (char *)malloc(xp->value_length);
2405       memcpy(xp->value, buf, xp->value_length);
2406    } else {
2407       if (xp->value) {
2408          free(xp->value);
2409       }
2410       xp->value = NULL;
2411    }
2412 
2413    return true;
2414 }
2415 
PyGetXattr(bpContext * ctx,xattr_pkt * xp)2416 static bRC PyGetXattr(bpContext *ctx, xattr_pkt *xp)
2417 {
2418    bRC retval = bRC_Error;
2419    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
2420    PyObject *pFunc;
2421 
2422    if (!xp) {
2423       return bRC_Error;
2424    }
2425 
2426    /*
2427     * Lookup the get_xattr() function in the python module.
2428     */
2429    pFunc = PyDict_GetItemString(p_ctx->pDict, "get_xattr"); /* Borrowed reference */
2430    if (pFunc && PyCallable_Check(pFunc)) {
2431       PyXattrPacket *pXattrPkt;
2432       PyObject *pRetVal;
2433 
2434       pXattrPkt = NativeToPyXattrPacket(xp);
2435       if (!pXattrPkt) {
2436          goto bail_out;
2437       }
2438 
2439       pRetVal = PyObject_CallFunctionObjArgs(pFunc, p_ctx->bpContext, pXattrPkt, NULL);
2440       if (!pRetVal) {
2441          Py_DECREF((PyObject *)pXattrPkt);
2442          goto bail_out;
2443       } else {
2444          retval = conv_python_retval(pRetVal);
2445          Py_DECREF(pRetVal);
2446 
2447          if (!PyXattrPacketToNative(pXattrPkt, xp)) {
2448             Py_DECREF((PyObject *)pXattrPkt);
2449             goto bail_out;
2450          }
2451          Py_DECREF(pXattrPkt);
2452       }
2453    } else {
2454       Dmsg(ctx, debuglevel, "python-fd: Failed to find function named get_xattr()\n");
2455    }
2456 
2457    return retval;
2458 
2459 bail_out:
2460    if (PyErr_Occurred()) {
2461       PyErrorHandler(ctx, M_FATAL);
2462    }
2463 
2464    return retval;
2465 }
2466 
PySetXattr(bpContext * ctx,xattr_pkt * xp)2467 static bRC PySetXattr(bpContext *ctx, xattr_pkt *xp)
2468 {
2469    bRC retval = bRC_Error;
2470    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
2471    PyObject *pFunc;
2472 
2473    if (!xp) {
2474       return bRC_Error;
2475    }
2476 
2477    /*
2478     * Lookup the set_acl() function in the python module.
2479     */
2480    pFunc = PyDict_GetItemString(p_ctx->pDict, "set_xattr"); /* Borrowed reference */
2481    if (pFunc && PyCallable_Check(pFunc)) {
2482       PyXattrPacket *pXattrPkt;
2483       PyObject *pRetVal;
2484 
2485       pXattrPkt = NativeToPyXattrPacket(xp);
2486       if (!pXattrPkt) {
2487          goto bail_out;
2488       }
2489 
2490       pRetVal = PyObject_CallFunctionObjArgs(pFunc, p_ctx->bpContext, pXattrPkt, NULL);
2491       Py_DECREF(pXattrPkt);
2492 
2493       if (!pRetVal) {
2494          goto bail_out;
2495       } else {
2496          retval = conv_python_retval(pRetVal);
2497          Py_DECREF(pRetVal);
2498       }
2499    } else {
2500       Dmsg(ctx, debuglevel, "python-fd: Failed to find function named set_xattr()\n");
2501    }
2502 
2503    return retval;
2504 bail_out:
2505    if (PyErr_Occurred()) {
2506       PyErrorHandler(ctx, M_FATAL);
2507    }
2508 
2509    return retval;
2510 }
2511 
NativeToPyRestoreObject(struct restore_object_pkt * rop)2512 static inline PyRestoreObject *NativeToPyRestoreObject(struct restore_object_pkt *rop)
2513 {
2514    PyRestoreObject *pRestoreObject = PyObject_New(PyRestoreObject, &PyRestoreObjectType);
2515 
2516    if (pRestoreObject) {
2517       pRestoreObject->object_name = PyString_FromString(rop->object_name);
2518       pRestoreObject->object = PyByteArray_FromStringAndSize(rop->object, rop->object_len);
2519       pRestoreObject->plugin_name = rop->plugin_name;
2520       pRestoreObject->object_type = rop->object_type;
2521       pRestoreObject->object_len = rop->object_len;
2522       pRestoreObject->object_full_len = rop->object_full_len;
2523       pRestoreObject->object_index = rop->object_index;
2524       pRestoreObject->object_compression = rop->object_compression;
2525       pRestoreObject->stream = rop->stream;
2526       pRestoreObject->JobId = rop->JobId;
2527    }
2528 
2529    return pRestoreObject;
2530 }
2531 
PyRestoreObjectData(bpContext * ctx,struct restore_object_pkt * rop)2532 static bRC PyRestoreObjectData(bpContext *ctx, struct restore_object_pkt *rop)
2533 {
2534    bRC retval = bRC_Error;
2535    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
2536    PyObject *pFunc;
2537 
2538    if (!rop) {
2539       return bRC_OK;
2540    }
2541 
2542    /*
2543     * Lookup the restore_object_data() function in the python module.
2544     */
2545    pFunc = PyDict_GetItemString(p_ctx->pDict, "restore_object_data"); /* Borrowed reference */
2546    if (pFunc && PyCallable_Check(pFunc)) {
2547       PyRestoreObject *pRestoreObject;
2548       PyObject *pRetVal;
2549 
2550       pRestoreObject = NativeToPyRestoreObject(rop);
2551       if (!pRestoreObject) {
2552          goto bail_out;
2553       }
2554 
2555       pRetVal = PyObject_CallFunctionObjArgs(pFunc, p_ctx->bpContext, pRestoreObject, NULL);
2556       Py_DECREF(pRestoreObject);
2557 
2558       if (!pRetVal) {
2559          goto bail_out;
2560       } else {
2561          retval = conv_python_retval(pRetVal);
2562          Py_DECREF(pRetVal);
2563       }
2564    } else {
2565       Dmsg(ctx, debuglevel, "python-fd: Failed to find function named start_restore_file()\n");
2566    }
2567 
2568    return retval;
2569 
2570 bail_out:
2571    if (PyErr_Occurred()) {
2572       PyErrorHandler(ctx, M_FATAL);
2573    }
2574 
2575    return retval;
2576 }
2577 
PyHandleBackupFile(bpContext * ctx,struct save_pkt * sp)2578 static bRC PyHandleBackupFile(bpContext *ctx, struct save_pkt *sp)
2579 {
2580    bRC retval = bRC_Error;
2581    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
2582    PyObject *pFunc;
2583 
2584    if (!sp) {
2585       return bRC_Error;
2586    }
2587 
2588    /*
2589     * Lookup the handle_backup_file() function in the python module.
2590     */
2591    pFunc = PyDict_GetItemString(p_ctx->pDict, "handle_backup_file"); /* Borrowed reference */
2592    if (pFunc && PyCallable_Check(pFunc)) {
2593       PySavePacket *pSavePkt;
2594       PyObject *pRetVal;
2595 
2596       pSavePkt = NativeToPySavePacket(sp);
2597       if (!pSavePkt) {
2598          goto bail_out;
2599       }
2600 
2601       pRetVal = PyObject_CallFunctionObjArgs(pFunc, p_ctx->bpContext, pSavePkt, NULL);
2602       if (!pRetVal) {
2603          Py_DECREF((PyObject *)pSavePkt);
2604          goto bail_out;
2605       } else {
2606          retval = conv_python_retval(pRetVal);
2607          Py_DECREF(pRetVal);
2608 
2609          if (!PySavePacketToNative(pSavePkt, sp, p_ctx, true)) {
2610             Py_DECREF((PyObject *)pSavePkt);
2611             goto bail_out;
2612          }
2613          Py_DECREF(pSavePkt);
2614       }
2615    } else {
2616       Dmsg(ctx, debuglevel, "python-fd: Failed to find function named handle_backup_file()\n");
2617    }
2618 
2619    return retval;
2620 
2621 bail_out:
2622    if (PyErr_Occurred()) {
2623       PyErrorHandler(ctx, M_FATAL);
2624    }
2625 
2626    return retval;
2627 }
2628 
2629 /**
2630  * Callback function which is exposed as a part of the additional methods which allow
2631  * a Python plugin to get certain internal values of the current Job.
2632  */
PyBareosGetValue(PyObject * self,PyObject * args)2633 static PyObject *PyBareosGetValue(PyObject *self, PyObject *args)
2634 {
2635    int var;
2636    bpContext *ctx = NULL;
2637    PyObject *pyCtx;
2638    PyObject *pRetVal = NULL;
2639 
2640    if (!PyArg_ParseTuple(args, "Oi:BareosGetValue", &pyCtx, &var)) {
2641       return NULL;
2642    }
2643 
2644    switch (var) {
2645    case bVarFDName:
2646    case bVarWorkingDir:
2647    case bVarExePath:
2648    case bVarVersion:
2649    case bVarDistName: {
2650       char *value = NULL;
2651 
2652       if (bfuncs->getBareosValue(ctx, (bVariable)var, &value) == bRC_OK) {
2653          if (value) {
2654             pRetVal = PyString_FromString(value);
2655          }
2656       }
2657       break;
2658    }
2659    case bVarJobId:
2660    case bVarLevel:
2661    case bVarType:
2662    case bVarJobStatus:
2663    case bVarSinceTime:
2664    case bVarAccurate:
2665    case bVarPrefixLinks: {
2666       int value = 0;
2667 
2668       ctx = PyGetbpContext(pyCtx);
2669       if (bfuncs->getBareosValue(ctx, (bVariable)var, &value) == bRC_OK) {
2670          pRetVal = PyInt_FromLong(value);
2671       }
2672       break;
2673    }
2674    case bVarClient:
2675    case bVarJobName:
2676    case bVarPrevJobName:
2677    case bVarWhere:
2678    case bVarRegexWhere: {
2679       char *value = NULL;
2680 
2681       ctx = PyGetbpContext(pyCtx);
2682       if (bfuncs->getBareosValue(ctx, (bVariable)var, &value) == bRC_OK) {
2683          if (value) {
2684             pRetVal = PyString_FromString(value);
2685          }
2686       }
2687       break;
2688    }
2689    case bVarFileSeen:
2690       break;                 /* a write only variable, ignore read request */
2691    default:
2692       ctx = PyGetbpContext(pyCtx);
2693       Dmsg(ctx, debuglevel, "python-fd: PyBareosGetValue unknown variable requested %d\n", var);
2694       break;
2695    }
2696 
2697    if (!pRetVal) {
2698       Py_INCREF(Py_None);
2699       pRetVal = Py_None;
2700    }
2701 
2702    return pRetVal;
2703 }
2704 
2705 /**
2706  * Callback function which is exposed as a part of the additional methods which allow
2707  * a Python plugin to get certain internal values of the current Job.
2708  */
PyBareosSetValue(PyObject * self,PyObject * args)2709 static PyObject *PyBareosSetValue(PyObject *self, PyObject *args)
2710 {
2711    int var;
2712    bpContext *ctx = NULL;
2713    bRC retval = bRC_Error;
2714    PyObject *pyCtx, *pyValue;
2715 
2716    if (!PyArg_ParseTuple(args, "OiO:BareosSetValue", &pyCtx, &var, &pyValue)) {
2717       goto bail_out;
2718    }
2719 
2720    switch (var) {
2721    case bVarLevel: {
2722       int value = 0;
2723 
2724       value = PyInt_AsLong(pyValue);
2725       if (value) {
2726          retval = bfuncs->setBareosValue(ctx, (bVariable)var, &value);
2727       }
2728       break;
2729    }
2730    case bVarFileSeen: {
2731       char *value;
2732 
2733 #if (PY_VERSION_HEX >  0x03050000)
2734       value = bstrdup(PyString_AsString(pyValue));
2735 #else
2736       value = PyString_AsString(pyValue);
2737 #endif
2738       if (value) {
2739          retval = bfuncs->setBareosValue(ctx, (bVariable)var, value);
2740       }
2741       break;
2742    }
2743    default:
2744       ctx = PyGetbpContext(pyCtx);
2745       Dmsg(ctx, debuglevel, "python-fd: PyBareosSetValue unknown variable requested %d\n", var);
2746       break;
2747    }
2748 
2749 bail_out:
2750    return conv_retval_python(retval);
2751 }
2752 
2753 /**
2754  * Callback function which is exposed as a part of the additional methods which allow
2755  * a Python plugin to issue debug messages using the Bareos debug message facility.
2756  */
PyBareosDebugMessage(PyObject * self,PyObject * args)2757 static PyObject *PyBareosDebugMessage(PyObject *self, PyObject *args)
2758 {
2759    int level;
2760    char *dbgmsg = NULL;
2761    bpContext *ctx;
2762    PyObject *pyCtx;
2763 
2764    if (!PyArg_ParseTuple(args, "Oi|z:BareosDebugMessage", &pyCtx, &level, &dbgmsg)) {
2765       return NULL;
2766    }
2767 
2768    if (dbgmsg) {
2769       ctx = PyGetbpContext(pyCtx);
2770       Dmsg(ctx, level, "python-fd: %s", dbgmsg);
2771    }
2772 
2773    Py_INCREF(Py_None);
2774    return Py_None;
2775 }
2776 
2777 /**
2778  * Callback function which is exposed as a part of the additional methods which allow
2779  * a Python plugin to issue Job messages using the Bareos Job message facility.
2780  */
PyBareosJobMessage(PyObject * self,PyObject * args)2781 static PyObject *PyBareosJobMessage(PyObject *self, PyObject *args)
2782 {
2783    int type;
2784    char *jobmsg = NULL;
2785    bpContext *ctx;
2786    PyObject *pyCtx;
2787 
2788    if (!PyArg_ParseTuple(args, "Oi|z:BareosJobMessage", &pyCtx, &type, &jobmsg)) {
2789       return NULL;
2790    }
2791 
2792    if (jobmsg) {
2793       ctx = PyGetbpContext(pyCtx);
2794       Jmsg(ctx, type, "python-fd: %s", jobmsg);
2795    }
2796 
2797    Py_INCREF(Py_None);
2798    return Py_None;
2799 }
2800 
2801 /**
2802  * Callback function which is exposed as a part of the additional methods which allow
2803  * a Python plugin to issue a Register Event to register additional events it wants
2804  * to receive.
2805  */
PyBareosRegisterEvents(PyObject * self,PyObject * args)2806 static PyObject *PyBareosRegisterEvents(PyObject *self, PyObject *args)
2807 {
2808    int len, event;
2809    bpContext *ctx;
2810    bRC retval = bRC_Error;
2811    PyObject *pyCtx, *pyEvents, *pySeq, *pyEvent;
2812 
2813    if (!PyArg_ParseTuple(args, "OO:BareosRegisterEvents", &pyCtx, &pyEvents)) {
2814       goto bail_out;
2815    }
2816 
2817    pySeq = PySequence_Fast(pyEvents, "Expected a sequence of events");
2818    if (!pySeq) {
2819       goto bail_out;
2820    }
2821 
2822    len = PySequence_Fast_GET_SIZE(pySeq);
2823 
2824    ctx = PyGetbpContext(pyCtx);
2825    for (int i = 0; i < len; i++) {
2826       pyEvent = PySequence_Fast_GET_ITEM(pySeq, i);
2827       event = PyInt_AsLong(pyEvent);
2828 
2829       if (event >= bEventJobStart && event <= FD_NR_EVENTS) {
2830          Dmsg(ctx, debuglevel, "python-fd: PyBareosRegisterEvents registering event %d\n", event);
2831          retval = bfuncs->registerBareosEvents(ctx, 1, event);
2832 
2833          if (retval != bRC_OK) {
2834             break;
2835          }
2836       }
2837    }
2838 
2839    Py_DECREF(pySeq);
2840 
2841 bail_out:
2842    return conv_retval_python(retval);
2843 }
2844 
2845 /**
2846  * Callback function which is exposed as a part of the additional methods which allow
2847  * a Python plugin to issue an Unregister Event to unregister events it doesn't want to
2848  * receive anymore.
2849  */
PyBareosUnRegisterEvents(PyObject * self,PyObject * args)2850 static PyObject *PyBareosUnRegisterEvents(PyObject *self, PyObject *args)
2851 {
2852    int len, event;
2853    bpContext *ctx;
2854    bRC retval = bRC_Error;
2855    PyObject *pyCtx, *pyEvents, *pySeq, *pyEvent;
2856 
2857    if (!PyArg_ParseTuple(args, "OO:BareosUnRegisterEvents", &pyCtx, &pyEvents)) {
2858       goto bail_out;
2859    }
2860 
2861    pySeq = PySequence_Fast(pyEvents, "Expected a sequence of events");
2862    if (!pySeq) {
2863       goto bail_out;
2864    }
2865 
2866    len = PySequence_Fast_GET_SIZE(pySeq);
2867 
2868    ctx = PyGetbpContext(pyCtx);
2869    for (int i = 0; i < len; i++) {
2870       pyEvent = PySequence_Fast_GET_ITEM(pySeq, i);
2871       event = PyInt_AsLong(pyEvent);
2872 
2873       if (event >= bEventJobStart && event <= FD_NR_EVENTS) {
2874          Dmsg(ctx, debuglevel, "PyBareosUnRegisterEvents: unregistering event %d\n", event);
2875          retval = bfuncs->unregisterBareosEvents(ctx, 1, event);
2876       }
2877    }
2878 
2879    Py_DECREF(pySeq);
2880 
2881 bail_out:
2882    return conv_retval_python(retval);
2883 }
2884 
2885 /**
2886  * Callback function which is exposed as a part of the additional methods which allow
2887  * a Python plugin to issue a GetInstanceCount to retrieve the number of instances of
2888  * the current plugin being loaded into the daemon.
2889  */
PyBareosGetInstanceCount(PyObject * self,PyObject * args)2890 static PyObject *PyBareosGetInstanceCount(PyObject *self, PyObject *args)
2891 {
2892    int value;
2893    bpContext *ctx = NULL;
2894    PyObject *pyCtx;
2895    PyObject *pRetVal = NULL;
2896 
2897    if (!PyArg_ParseTuple(args, "O:BareosGetInstanceCount", &pyCtx)) {
2898       return NULL;
2899    }
2900 
2901    ctx = PyGetbpContext(pyCtx);
2902    if (bfuncs->getInstanceCount(ctx, &value) == bRC_OK) {
2903       pRetVal = PyInt_FromLong(value);
2904    }
2905 
2906    if (!pRetVal) {
2907       Py_INCREF(Py_None);
2908       pRetVal = Py_None;
2909    }
2910 
2911    return pRetVal;
2912 }
2913 
2914 /**
2915  * Callback function which is exposed as a part of the additional methods which allow
2916  * a Python plugin to issue a Add Exclude pattern to the fileset.
2917  */
PyBareosAddExclude(PyObject * self,PyObject * args)2918 static PyObject *PyBareosAddExclude(PyObject *self, PyObject *args)
2919 {
2920    char *file = NULL;
2921    bpContext *ctx;
2922    PyObject *pyCtx;
2923    bRC retval = bRC_Error;
2924 
2925    if (!PyArg_ParseTuple(args, "O|z:BareosAddExclude", &pyCtx, &file)) {
2926       goto bail_out;
2927    }
2928 
2929    if (file) {
2930       ctx = PyGetbpContext(pyCtx);
2931       retval = bfuncs->AddExclude(ctx, file);
2932    }
2933 
2934 bail_out:
2935    return conv_retval_python(retval);
2936 }
2937 
2938 /**
2939  * Callback function which is exposed as a part of the additional methods which allow
2940  * a Python plugin to issue a Add Include pattern to the fileset.
2941  */
PyBareosAddInclude(PyObject * self,PyObject * args)2942 static PyObject *PyBareosAddInclude(PyObject *self, PyObject *args)
2943 {
2944    char *file = NULL;
2945    bpContext *ctx;
2946    PyObject *pyCtx;
2947    bRC retval = bRC_Error;
2948 
2949    if (!PyArg_ParseTuple(args, "O|z:BareosAddInclude", &pyCtx, &file)) {
2950       goto bail_out;
2951    }
2952 
2953    if (file) {
2954       ctx = PyGetbpContext(pyCtx);
2955       retval = bfuncs->AddInclude(ctx, file);
2956    }
2957 
2958 bail_out:
2959    return conv_retval_python(retval);
2960 }
2961 
2962 /**
2963  * Callback function which is exposed as a part of the additional methods which allow
2964  * a Python plugin to issue a Add Include Options to the fileset.
2965  */
PyBareosAddOptions(PyObject * self,PyObject * args)2966 static PyObject *PyBareosAddOptions(PyObject *self, PyObject *args)
2967 {
2968    char *opts = NULL;
2969    bpContext *ctx;
2970    PyObject *pyCtx;
2971    bRC retval = bRC_Error;
2972 
2973    if (!PyArg_ParseTuple(args, "O|z:BareosAddOptions", &pyCtx, &opts)) {
2974       goto bail_out;
2975    }
2976 
2977    if (opts) {
2978       ctx = PyGetbpContext(pyCtx);
2979       retval = bfuncs->AddOptions(ctx, opts);
2980    }
2981 
2982 bail_out:
2983    return conv_retval_python(retval);
2984 }
2985 
2986 /**
2987  * Callback function which is exposed as a part of the additional methods which allow
2988  * a Python plugin to issue a Add Regex to the fileset.
2989  */
PyBareosAddRegex(PyObject * self,PyObject * args)2990 static PyObject *PyBareosAddRegex(PyObject *self, PyObject *args)
2991 {
2992    int type;
2993    char *item = NULL;
2994    bpContext *ctx;
2995    PyObject *pyCtx;
2996    bRC retval = bRC_Error;
2997 
2998    if (!PyArg_ParseTuple(args, "O|zi:BareosAddRegex", &pyCtx, &item, &type)) {
2999       goto bail_out;
3000    }
3001 
3002    if (item) {
3003       ctx = PyGetbpContext(pyCtx);
3004       retval = bfuncs->AddRegex(ctx, item, type);
3005    }
3006 
3007 bail_out:
3008    return conv_retval_python(retval);
3009 }
3010 
3011 /**
3012  * Callback function which is exposed as a part of the additional methods which allow
3013  * a Python plugin to issue a Add Wildcard to the fileset.
3014  */
PyBareosAddWild(PyObject * self,PyObject * args)3015 static PyObject *PyBareosAddWild(PyObject *self, PyObject *args)
3016 {
3017    int type;
3018    char *item = NULL;
3019    bpContext *ctx;
3020    PyObject *pyCtx;
3021    bRC retval = bRC_Error;
3022 
3023    if (!PyArg_ParseTuple(args, "O|zi:BareosAddWild", &pyCtx, &item, &type)) {
3024       goto bail_out;
3025    }
3026 
3027    if (item) {
3028       ctx = PyGetbpContext(pyCtx);
3029       retval = bfuncs->AddWild(ctx, item, type);
3030    }
3031 
3032 bail_out:
3033    return conv_retval_python(retval);
3034 }
3035 
3036 /**
3037  * Callback function which is exposed as a part of the additional methods which allow
3038  * a Python plugin to issue a Add New Option block.
3039  */
PyBareosNewOptions(PyObject * self,PyObject * args)3040 static PyObject *PyBareosNewOptions(PyObject *self, PyObject *args)
3041 {
3042    bpContext *ctx;
3043    PyObject *pyCtx;
3044    bRC retval = bRC_Error;
3045 
3046    if (!PyArg_ParseTuple(args, "O:BareosNewOptions", &pyCtx)) {
3047       goto bail_out;
3048    }
3049 
3050    ctx = PyGetbpContext(pyCtx);
3051    retval = bfuncs->NewOptions(ctx);
3052 
3053 bail_out:
3054    return conv_retval_python(retval);
3055 }
3056 
3057 /**
3058  * Callback function which is exposed as a part of the additional methods which allow
3059  * a Python plugin to issue a Add New Include block.
3060  */
PyBareosNewInclude(PyObject * self,PyObject * args)3061 static PyObject *PyBareosNewInclude(PyObject *self, PyObject *args)
3062 {
3063    bpContext *ctx;
3064    PyObject *pyCtx;
3065    bRC retval = bRC_Error;
3066 
3067    if (!PyArg_ParseTuple(args, "O:BareosNewInclude", &pyCtx)) {
3068       goto bail_out;
3069    }
3070 
3071    ctx = PyGetbpContext(pyCtx);
3072    retval = bfuncs->NewInclude(ctx);
3073 
3074 bail_out:
3075    return conv_retval_python(retval);
3076 }
3077 
3078 /**
3079  * Callback function which is exposed as a part of the additional methods which allow
3080  * a Python plugin to issue a Add New Pre Include block.
3081  */
PyBareosNewPreInclude(PyObject * self,PyObject * args)3082 static PyObject *PyBareosNewPreInclude(PyObject *self, PyObject *args)
3083 {
3084    bpContext *ctx;
3085    PyObject *pyCtx;
3086    bRC retval = bRC_Error;
3087 
3088    if (!PyArg_ParseTuple(args, "O:BareosNewPreInclude", &pyCtx)) {
3089       goto bail_out;
3090    }
3091 
3092    ctx = PyGetbpContext(pyCtx);
3093    retval = bfuncs->NewPreInclude(ctx);
3094 
3095 bail_out:
3096    return conv_retval_python(retval);
3097 }
3098 
3099 /**
3100  * Callback function which is exposed as a part of the additional methods which allow
3101  * a Python plugin to issue a check if a file have to be backuped using Accurate code.
3102  */
PyBareosCheckChanges(PyObject * self,PyObject * args)3103 static PyObject *PyBareosCheckChanges(PyObject *self, PyObject *args)
3104 {
3105    bpContext *ctx;
3106    PyObject *pyCtx;
3107    struct save_pkt sp;
3108    bRC retval = bRC_Error;
3109    PySavePacket *pSavePkt;
3110 
3111    if (!PyArg_ParseTuple(args, "OO:BareosCheckChanges", &pyCtx, &pSavePkt)) {
3112       goto bail_out;
3113    }
3114 
3115    ctx = PyGetbpContext(pyCtx);
3116 
3117    /*
3118     * CheckFile only has a need for a limited version of the PySavePacket so we handle
3119     * that here separately and don't call PySavePacketToNative().
3120     */
3121    sp.type = pSavePkt->type;
3122    if (pSavePkt->fname) {
3123       if (PyString_Check(pSavePkt->fname)) {
3124 #if (PY_VERSION_HEX >  0x03050000)
3125          sp.fname = bstrdup(PyString_AsString(pSavePkt->fname));
3126 #else
3127          sp.fname = PyString_AsString(pSavePkt->fname);
3128 #endif
3129       } else {
3130          goto bail_out;
3131       }
3132    } else {
3133       goto bail_out;
3134    }
3135    if (pSavePkt->link) {
3136       if (PyString_Check(pSavePkt->link)) {
3137 #if (PY_VERSION_HEX >  0x03050000)
3138          sp.link = bstrdup(PyString_AsString(pSavePkt->link));
3139 #else
3140          sp.link = PyString_AsString(pSavePkt->link);
3141 #endif
3142       } else {
3143          goto bail_out;
3144       }
3145    }
3146    sp.save_time = pSavePkt->save_time;
3147 
3148    retval = bfuncs->checkChanges(ctx, &sp);
3149 
3150    /*
3151     * Copy back the two fields that are changed by checkChanges().
3152     */
3153    pSavePkt->delta_seq = sp.delta_seq;
3154    pSavePkt->accurate_found = sp.accurate_found;
3155 
3156 bail_out:
3157    return conv_retval_python(retval);
3158 }
3159 
3160 /**
3161  * Callback function which is exposed as a part of the additional methods which allow
3162  * a Python plugin to issue a check if a file would be saved using current Include/Exclude code.
3163  */
PyBareosAcceptFile(PyObject * self,PyObject * args)3164 static PyObject *PyBareosAcceptFile(PyObject *self, PyObject *args)
3165 {
3166    bpContext *ctx;
3167    PyObject *pyCtx;
3168    struct save_pkt sp;
3169    bRC retval = bRC_Error;
3170    PySavePacket *pSavePkt;
3171 
3172    if (!PyArg_ParseTuple(args, "OO:BareosAcceptFile", &pyCtx, &pSavePkt)) {
3173       goto bail_out;
3174    }
3175 
3176    ctx = PyGetbpContext(pyCtx);
3177 
3178    /*
3179     * Acceptfile only needs fname and statp from PySavePacket so we handle
3180     * that here separately and don't call PySavePacketToNative().
3181     */
3182    if (pSavePkt->fname) {
3183       if (PyString_Check(pSavePkt->fname)) {
3184 #if (PY_VERSION_HEX >  0x03050000)
3185          sp.fname = bstrdup(PyString_AsString(pSavePkt->fname));
3186 #else
3187          sp.fname = PyString_AsString(pSavePkt->fname);
3188 #endif
3189       } else {
3190          goto bail_out;
3191       }
3192    } else {
3193       goto bail_out;
3194    }
3195 
3196    if (pSavePkt->statp) {
3197       PyStatPacketToNative((PyStatPacket *)pSavePkt->statp, &sp.statp);
3198    } else {
3199       goto bail_out;
3200    }
3201 
3202    retval = bfuncs->AcceptFile(ctx, &sp);
3203 
3204 bail_out:
3205    return conv_retval_python(retval);
3206 }
3207 
3208 /**
3209  * Callback function which is exposed as a part of the additional methods which allow
3210  * a Python plugin to issue a Set bit in the Accurate Seen bitmap.
3211  */
PyBareosSetSeenBitmap(PyObject * self,PyObject * args)3212 static PyObject *PyBareosSetSeenBitmap(PyObject *self, PyObject *args)
3213 {
3214    bool all;
3215    bpContext *ctx;
3216    char *fname = NULL;
3217    bRC retval = bRC_Error;
3218    PyObject *pyCtx, *pyBool;
3219 
3220    if (!PyArg_ParseTuple(args, "OO|s:BareosSetSeenBitmap", &pyCtx, &pyBool, &fname)) {
3221       goto bail_out;
3222    }
3223 
3224    ctx = PyGetbpContext(pyCtx);
3225    all = PyObject_IsTrue(pyBool);
3226    retval = bfuncs->SetSeenBitmap(ctx, all, fname);
3227 
3228 bail_out:
3229    return conv_retval_python(retval);
3230 }
3231 
3232 /**
3233  * Callback function which is exposed as a part of the additional methods which allow
3234  * a Python plugin to issue a Clear bit in the Accurate Seen bitmap.
3235  */
PyBareosClearSeenBitmap(PyObject * self,PyObject * args)3236 static PyObject *PyBareosClearSeenBitmap(PyObject *self, PyObject *args)
3237 {
3238    bool all;
3239    bpContext *ctx;
3240    char *fname = NULL;
3241    bRC retval = bRC_Error;
3242    PyObject *pyCtx, *pyBool;
3243 
3244    if (!PyArg_ParseTuple(args, "OO|s:BareosClearSeenBitmap", &pyCtx, &pyBool, &fname)) {
3245       goto bail_out;
3246    }
3247 
3248    ctx = PyGetbpContext(pyCtx);
3249    all = PyObject_IsTrue(pyBool);
3250    retval = bfuncs->ClearSeenBitmap(ctx, all, fname);
3251 
3252 bail_out:
3253    return conv_retval_python(retval);
3254 }
3255 
3256 /**
3257  * Some helper functions.
3258  */
PyGetStringValue(PyObject * object)3259 static inline char *PyGetStringValue(PyObject *object)
3260 {
3261    if (!object || !PyString_Check(object)) {
3262       return (char *)"";
3263    }
3264 #if (PY_VERSION_HEX >  0x03050000)
3265    return bstrdup(PyString_AsString(object));
3266 #else
3267    return PyString_AsString(object);
3268 #endif
3269 }
3270 
PyGetByteArrayValue(PyObject * object)3271 static inline char *PyGetByteArrayValue(PyObject *object)
3272 {
3273    if (!object || !PyByteArray_Check(object)) {
3274       return (char *)"";
3275    }
3276 
3277    return PyByteArray_AsString(object);
3278 }
3279 
3280 /**
3281  * Python specific handlers for PyRestoreObject structure mapping.
3282  */
3283 
3284 /**
3285  * Representation.
3286  */
PyRestoreObject_repr(PyRestoreObject * self)3287 static PyObject *PyRestoreObject_repr(PyRestoreObject *self)
3288 {
3289    PyObject *s;
3290    PoolMem buf(PM_MESSAGE);
3291 
3292    Mmsg(buf, "RestoreObject(object_name=\"%s\", object=\"%s\", plugin_name=\"%s\", "
3293              "object_type=%d, object_len=%d, object_full_len=%d, "
3294              "object_index=%d, object_compression=%d, stream=%d, jobid=%u)",
3295         PyGetStringValue(self->object_name), PyGetByteArrayValue(self->object), self->plugin_name,
3296         self->object_type, self->object_len, self->object_full_len,
3297         self->object_index, self->object_compression, self->stream, self->JobId);
3298    s = PyString_FromString(buf.c_str());
3299 
3300    return s;
3301 }
3302 
3303 /**
3304  * Initialization.
3305  */
PyRestoreObject_init(PyRestoreObject * self,PyObject * args,PyObject * kwds)3306 static int PyRestoreObject_init(PyRestoreObject *self, PyObject *args, PyObject *kwds)
3307 {
3308    static char *kwlist[] = {
3309       (char *)"object_name",
3310       (char *)"object",
3311       (char *)"plugin_name",
3312       (char *)"object_type",
3313       (char *)"object_len",
3314       (char *)"object_full_len",
3315       (char *)"object_index",
3316       (char *)"object_compression",
3317       (char *)"stream",
3318       (char *)"jobid",
3319       NULL
3320    };
3321 
3322    self->object_name = NULL;
3323    self->object = NULL;
3324    self->plugin_name = NULL;
3325    self->object_type = 0;
3326    self->object_len = 0;
3327    self->object_full_len = 0;
3328    self->object_index = 0;
3329    self->object_compression = 0;
3330    self->stream = 0;
3331    self->JobId = 0;
3332 
3333    if (!PyArg_ParseTupleAndKeywords(args,
3334                                     kwds,
3335                                     "|oosiiiiiiI",
3336                                     kwlist,
3337                                     &self->object_name,
3338                                     &self->object,
3339                                     &self->plugin_name,
3340                                     &self->object_type,
3341                                     &self->object_len,
3342                                     &self->object_full_len,
3343                                     &self->object_index,
3344                                     &self->object_compression,
3345                                     &self->stream,
3346                                     &self->JobId)) {
3347       return -1;
3348    }
3349 
3350    return 0;
3351 }
3352 
3353 /**
3354  * Destructor.
3355  */
PyRestoreObject_dealloc(PyRestoreObject * self)3356 static void PyRestoreObject_dealloc(PyRestoreObject *self)
3357 {
3358    if (self->object_name) {
3359       Py_XDECREF(self->object_name);
3360    }
3361    if (self->object) {
3362       Py_XDECREF(self->object);
3363    }
3364    PyObject_Del(self);
3365 }
3366 
3367 /**
3368  * Python specific handlers for PyStatPacket structure mapping.
3369  */
3370 
3371 /**
3372  * Representation.
3373  */
PyStatPacket_repr(PyStatPacket * self)3374 static PyObject *PyStatPacket_repr(PyStatPacket *self)
3375 {
3376    PyObject *s;
3377    PoolMem buf(PM_MESSAGE);
3378 
3379    Mmsg(buf, "StatPacket(dev=%ld, ino=%lld, mode=%04o, nlink=%d, "
3380              "uid=%ld, gid=%ld, rdev=%ld, size=%lld, "
3381              "atime=%ld, mtime=%ld, ctime=%ld, blksize=%ld, blocks=%lld)",
3382         self->dev, self->ino, (self->mode & ~S_IFMT), self->nlink,
3383         self->uid, self->gid, self->rdev, self->size,
3384         self->atime, self->mtime, self->ctime, self->blksize, self->blocks);
3385 
3386    s = PyString_FromString(buf.c_str());
3387 
3388    return s;
3389 }
3390 
3391 /**
3392  * Initialization.
3393  */
PyStatPacket_init(PyStatPacket * self,PyObject * args,PyObject * kwds)3394 static int PyStatPacket_init(PyStatPacket *self, PyObject *args, PyObject *kwds)
3395 {
3396    time_t now;
3397    static char *kwlist[] = {
3398       (char *)"dev",
3399       (char *)"ino",
3400       (char *)"mode",
3401       (char *)"nlink",
3402       (char *)"uid",
3403       (char *)"gid",
3404       (char *)"rdev",
3405       (char *)"size",
3406       (char *)"atime",
3407       (char *)"mtime",
3408       (char *)"ctime",
3409       (char *)"blksize",
3410       (char *)"blocks",
3411       NULL
3412    };
3413 
3414    now = time(NULL);
3415    self->dev = 0;
3416    self->ino = 0;
3417    self->mode = 0700 | S_IFREG;
3418    self->nlink = 0;
3419    self->uid = 0;
3420    self->gid = 0;
3421    self->rdev = 0;
3422    self->size = -1;
3423    self->atime = now;
3424    self->mtime = now;
3425    self->ctime = now;
3426    self->blksize = 4096;
3427    self->blocks = 1;
3428 
3429    if (!PyArg_ParseTupleAndKeywords(args,
3430                                     kwds,
3431                                     "|IKHHIIIKIIIIK",
3432                                     kwlist,
3433                                     &self->dev,
3434                                     &self->ino,
3435                                     &self->mode,
3436                                     &self->nlink,
3437                                     &self->uid,
3438                                     &self->gid,
3439                                     &self->rdev,
3440                                     &self->size,
3441                                     &self->atime,
3442                                     &self->mtime,
3443                                     &self->ctime,
3444                                     &self->blksize,
3445                                     &self->blocks)) {
3446       return -1;
3447    }
3448 
3449    return 0;
3450 }
3451 
3452 /**
3453  * Destructor.
3454  */
PyStatPacket_dealloc(PyStatPacket * self)3455 static void PyStatPacket_dealloc(PyStatPacket *self)
3456 {
3457    PyObject_Del(self);
3458 }
3459 
3460 /**
3461  * Python specific handlers for PySavePacket structure mapping.
3462  */
3463 
3464 /**
3465  * Representation.
3466  */
print_flags_bitmap(PyObject * bitmap)3467 static inline const char *print_flags_bitmap(PyObject *bitmap)
3468 {
3469    static char visual_bitmap[FO_MAX + 1];
3470 
3471    if (PyByteArray_Check(bitmap)) {
3472       int cnt;
3473       char *flags;
3474 
3475       if (PyByteArray_Size(bitmap) == FOPTS_BYTES) {
3476          if ((flags = PyByteArray_AsString(bitmap))) {
3477             memset(visual_bitmap, 0, sizeof(visual_bitmap));
3478             for (cnt = 0; cnt <= FO_MAX; cnt++) {
3479                if (BitIsSet(cnt, flags)) {
3480                   visual_bitmap[cnt] = '1';
3481                } else {
3482                   visual_bitmap[cnt] = '0';
3483                }
3484             }
3485 
3486             return visual_bitmap;
3487          }
3488       }
3489    }
3490 
3491    return "Unknown";
3492 }
3493 
PySavePacket_repr(PySavePacket * self)3494 static PyObject *PySavePacket_repr(PySavePacket *self)
3495 {
3496    PyObject *s;
3497    PoolMem buf(PM_MESSAGE);
3498 
3499    Mmsg(buf, "SavePacket(fname=\"%s\", link=\"%s\", type=%ld, flags=%s, "
3500              "no_read=%d, portable=%d, accurate_found=%d, "
3501              "cmd=\"%s\", save_time=%ld, delta_seq=%ld, object_name=\"%s\", "
3502              "object=\"%s\", object_len=%ld, object_index=%ld)",
3503         PyGetStringValue(self->fname), PyGetStringValue(self->link),
3504         self->type, print_flags_bitmap(self->flags), self->no_read, self->portable,
3505         self->accurate_found, self->cmd, self->save_time, self->delta_seq,
3506         PyGetStringValue(self->object_name), PyGetByteArrayValue(self->object),
3507         self->object_len, self->object_index);
3508 
3509    s = PyString_FromString(buf.c_str());
3510 
3511    return s;
3512 }
3513 
3514 /**
3515  * Initialization.
3516  */
PySavePacket_init(PySavePacket * self,PyObject * args,PyObject * kwds)3517 static int PySavePacket_init(PySavePacket *self, PyObject *args, PyObject *kwds)
3518 {
3519    static char *kwlist[] = {
3520       (char *)"fname",
3521       (char *)"link",
3522       (char *)"type",
3523       (char *)"flags",
3524       (char *)"no_read",
3525       (char *)"portable",
3526       (char *)"accurate_found",
3527       (char *)"cmd",
3528       (char *)"save_time",
3529       (char *)"delta_seq",
3530       (char *)"object_name",
3531       (char *)"object",
3532       (char *)"object_len",
3533       (char *)"object_index",
3534       NULL
3535    };
3536 
3537    self->fname = NULL;
3538    self->link = NULL;
3539    self->type = 0;
3540    self->flags = NULL;
3541    self->no_read = false;
3542    self->portable = false;
3543    self->accurate_found = false;
3544    self->cmd = NULL;
3545    self->save_time = 0;
3546    self->delta_seq = 0;
3547    self->object_name = NULL;
3548    self->object = NULL;
3549    self->object_len = 0;
3550    self->object_index = 0;
3551 
3552    if (!PyArg_ParseTupleAndKeywords(args,
3553                                     kwds,
3554                                     "|ooiocccsiiooii",
3555                                     kwlist,
3556                                     &self->fname,
3557                                     &self->link,
3558                                     &self->type,
3559                                     &self->flags,
3560                                     &self->no_read,
3561                                     &self->portable,
3562                                     &self->accurate_found,
3563                                     &self->cmd,
3564                                     &self->save_time,
3565                                     &self->delta_seq,
3566                                     &self->object_name,
3567                                     &self->object,
3568                                     &self->object_len,
3569                                     &self->object_index)) {
3570       return -1;
3571    }
3572 
3573    return 0;
3574 }
3575 
3576 /**
3577  * Destructor.
3578  */
PySavePacket_dealloc(PySavePacket * self)3579 static void PySavePacket_dealloc(PySavePacket *self)
3580 {
3581    if (self->fname) {
3582       Py_XDECREF(self->fname);
3583    }
3584    if (self->link) {
3585       Py_XDECREF(self->link);
3586    }
3587    if (self->flags) {
3588       Py_XDECREF(self->flags);
3589    }
3590    if (self->object_name) {
3591       Py_XDECREF(self->object_name);
3592    }
3593    if (self->object) {
3594       Py_XDECREF(self->object);
3595    }
3596    PyObject_Del(self);
3597 }
3598 
3599 /**
3600  * Python specific handlers for PyRestorePacket structure mapping.
3601  */
3602 
3603 /**
3604  * Representation.
3605  */
PyRestorePacket_repr(PyRestorePacket * self)3606 static PyObject *PyRestorePacket_repr(PyRestorePacket *self)
3607 {
3608    PyObject *stat_repr, *s;
3609    PoolMem buf(PM_MESSAGE);
3610 
3611    stat_repr = PyObject_Repr(self->statp);
3612    Mmsg(buf, "RestorePacket(stream=%d, data_stream=%ld, type=%ld, file_index=%ld, "
3613              "linkFI=%ld, uid=%ld, statp=\"%s\", attrEx=\"%s\", ofname=\"%s\", "
3614              "olname=\"%s\", where=\"%s\", RegexWhere=\"%s\", replace=%d, create_status=%d)",
3615         self->stream, self->data_stream, self->type, self->file_index,
3616         self->LinkFI, self->uid, PyGetStringValue(stat_repr), self->attrEx, self->ofname,
3617         self->olname, self->where, self->RegexWhere, self->replace, self->create_status);
3618 
3619    s = PyString_FromString(buf.c_str());
3620    Py_DECREF(stat_repr);
3621 
3622    return s;
3623 }
3624 
3625 /**
3626  * Initialization.
3627  */
PyRestorePacket_init(PyRestorePacket * self,PyObject * args,PyObject * kwds)3628 static int PyRestorePacket_init(PyRestorePacket *self, PyObject *args, PyObject *kwds)
3629 {
3630    static char *kwlist[] = {
3631       (char *)"stream",
3632       (char *)"data_stream",
3633       (char *)"type",
3634       (char *)"file_index",
3635       (char *)"linkFI",
3636       (char *)"uid",
3637       (char *)"statp",
3638       (char *)"attrEX",
3639       (char *)"ofname",
3640       (char *)"olname",
3641       (char *)"where",
3642       (char *)"regexwhere",
3643       (char *)"replace",
3644       (char *)"create_status",
3645       NULL
3646    };
3647 
3648    self->stream = 0;
3649    self->data_stream = 0;
3650    self->type = 0;
3651    self->file_index = 0;
3652    self->LinkFI = 0;
3653    self->uid = 0;
3654    self->statp = NULL;
3655    self->attrEx = NULL;
3656    self->ofname = NULL;
3657    self->olname = NULL;
3658    self->where = NULL;
3659    self->RegexWhere = NULL;
3660    self->replace = 0;
3661    self->create_status = 0;
3662 
3663    if (!PyArg_ParseTupleAndKeywords(args,
3664                                     kwds,
3665                                     "|iiiiiIosssssii",
3666                                     kwlist,
3667                                     &self->stream,
3668                                     &self->data_stream,
3669                                     &self->type,
3670                                     &self->file_index,
3671                                     &self->LinkFI,
3672                                     &self->uid,
3673                                     &self->statp,
3674                                     &self->attrEx,
3675                                     &self->ofname,
3676                                     &self->olname,
3677                                     &self->where,
3678                                     &self->RegexWhere,
3679                                     &self->replace,
3680                                     &self->create_status)) {
3681       return -1;
3682    }
3683 
3684    return 0;
3685 }
3686 
3687 /**
3688  * Destructor.
3689  */
PyRestorePacket_dealloc(PyRestorePacket * self)3690 static void PyRestorePacket_dealloc(PyRestorePacket *self)
3691 {
3692    PyObject_Del(self);
3693 }
3694 
3695 /**
3696  * Python specific handlers for PyIoPacket structure mapping.
3697  */
3698 
3699 /**
3700  * Representation.
3701  */
PyIoPacket_repr(PyIoPacket * self)3702 static PyObject *PyIoPacket_repr(PyIoPacket *self)
3703 {
3704    PyObject *s;
3705    PoolMem buf(PM_MESSAGE);
3706 
3707    Mmsg(buf, "IoPacket(func=%d, count=%ld, flags=%ld, mode=%04o, "
3708              "buf=\"%s\", fname=\"%s\", status=%ld, io_errno=%ld, lerror=%ld, "
3709              "whence=%ld, offset=%lld, win32=%d)",
3710         self->func, self->count, self->flags, (self->mode & ~S_IFMT),
3711         PyGetByteArrayValue(self->buf), self->fname, self->status,
3712         self->io_errno, self->lerror,
3713         self->whence, self->offset, self->win32);
3714    s = PyString_FromString(buf.c_str());
3715 
3716    return s;
3717 }
3718 
3719 /**
3720  * Initialization.
3721  */
PyIoPacket_init(PyIoPacket * self,PyObject * args,PyObject * kwds)3722 static int PyIoPacket_init(PyIoPacket *self, PyObject *args, PyObject *kwds)
3723 {
3724    static char *kwlist[] = {
3725       (char *)"func",
3726       (char *)"count",
3727       (char *)"flags",
3728       (char *)"mode",
3729       (char *)"buf",
3730       (char *)"fname",
3731       (char *)"status",
3732       (char *)"io_errno",
3733       (char *)"lerror",
3734       (char *)"whence",
3735       (char *)"offset",
3736       (char *)"win32",
3737       NULL
3738    };
3739 
3740    self->func = 0;
3741    self->count = 0;
3742    self->flags = 0;
3743    self->mode = 0;
3744    self->buf = NULL;
3745    self->fname = NULL;
3746    self->status = 0;
3747    self->io_errno = 0;
3748    self->lerror = 0;
3749    self->whence = 0;
3750    self->offset = 0;
3751    self->win32 = false;
3752 
3753    if (!PyArg_ParseTupleAndKeywords(args,
3754                                     kwds,
3755                                     "|Hiiiosiiiilc",
3756                                     kwlist,
3757                                     &self->func,
3758                                     &self->count,
3759                                     &self->flags,
3760                                     &self->mode,
3761                                     &self->buf,
3762                                     &self->fname,
3763                                     &self->status,
3764                                     &self->io_errno,
3765                                     &self->lerror,
3766                                     &self->whence,
3767                                     &self->offset,
3768                                     &self->win32)) {
3769       return -1;
3770    }
3771 
3772    return 0;
3773 }
3774 
3775 /**
3776  * Destructor.
3777  */
PyIoPacket_dealloc(PyIoPacket * self)3778 static void PyIoPacket_dealloc(PyIoPacket *self)
3779 {
3780    if (self->buf) {
3781       Py_XDECREF(self->buf);
3782    }
3783    PyObject_Del(self);
3784 }
3785 
3786 /**
3787  * Python specific handlers for PyAclPacket structure mapping.
3788  */
3789 
3790 /**
3791  * Representation.
3792  */
PyAclPacket_repr(PyAclPacket * self)3793 static PyObject *PyAclPacket_repr(PyAclPacket *self)
3794 {
3795    PyObject *s;
3796    PoolMem buf(PM_MESSAGE);
3797 
3798    Mmsg(buf, "AclPacket(fname=\"%s\", content=\"%s\")",
3799         self->fname, PyGetByteArrayValue(self->content));
3800    s = PyString_FromString(buf.c_str());
3801 
3802    return s;
3803 }
3804 
3805 /**
3806  * Initialization.
3807  */
PyAclPacket_init(PyAclPacket * self,PyObject * args,PyObject * kwds)3808 static int PyAclPacket_init(PyAclPacket *self, PyObject *args, PyObject *kwds)
3809 {
3810    static char *kwlist[] = {
3811       (char *)"fname",
3812       (char *)"content",
3813       NULL
3814    };
3815 
3816    self->fname = NULL;
3817    self->content = NULL;
3818 
3819    if (!PyArg_ParseTupleAndKeywords(args,
3820                                     kwds,
3821                                     "|so",
3822                                     kwlist,
3823                                     &self->fname,
3824                                     &self->content)) {
3825       return -1;
3826    }
3827 
3828    return 0;
3829 }
3830 
3831 /**
3832  * Destructor.
3833  */
PyAclPacket_dealloc(PyAclPacket * self)3834 static void PyAclPacket_dealloc(PyAclPacket *self)
3835 {
3836    if (self->content) {
3837       Py_XDECREF(self->content);
3838    }
3839    PyObject_Del(self);
3840 }
3841 
3842 /**
3843  * Python specific handlers for PyIOPacket structure mapping.
3844  */
3845 
3846 /**
3847  * Representation.
3848  */
PyXattrPacket_repr(PyXattrPacket * self)3849 static PyObject *PyXattrPacket_repr(PyXattrPacket *self)
3850 {
3851    PyObject *s;
3852    PoolMem buf(PM_MESSAGE);
3853 
3854    Mmsg(buf, "XattrPacket(fname=\"%s\", name=\"%s\", value=\"%s\")",
3855         self->fname, PyGetByteArrayValue(self->name), PyGetByteArrayValue(self->value));
3856    s = PyString_FromString(buf.c_str());
3857 
3858    return s;
3859 }
3860 
3861 /**
3862  * Initialization.
3863  */
PyXattrPacket_init(PyXattrPacket * self,PyObject * args,PyObject * kwds)3864 static int PyXattrPacket_init(PyXattrPacket *self, PyObject *args, PyObject *kwds)
3865 {
3866    static char *kwlist[] = {
3867       (char *)"fname",
3868       (char *)"name",
3869       (char *)"value",
3870       NULL
3871    };
3872 
3873    self->fname = NULL;
3874    self->name = NULL;
3875    self->value = NULL;
3876 
3877    if (!PyArg_ParseTupleAndKeywords(args,
3878                                     kwds,
3879                                     "|soo",
3880                                     kwlist,
3881                                     &self->fname,
3882                                     &self->name,
3883                                     &self->value)) {
3884       return -1;
3885    }
3886 
3887    return 0;
3888 }
3889 
3890 /**
3891  * Destructor.
3892  */
PyXattrPacket_dealloc(PyXattrPacket * self)3893 static void PyXattrPacket_dealloc(PyXattrPacket *self)
3894 {
3895    if (self->value) {
3896       Py_XDECREF(self->value);
3897    }
3898    if (self->name) {
3899       Py_XDECREF(self->name);
3900    }
3901    PyObject_Del(self);
3902 }
3903 } /* namespace filedaemon */
3904