]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/fd_plugins.c
89f174b3578e3cf92813bfb2adce3786faab84e2
[bacula/bacula] / bacula / src / filed / fd_plugins.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /**
20  * Main program to test loading and running Bacula plugins.
21  *   Destined to become Bacula pluginloader, ...
22  *
23  * Kern Sibbald, October 2007
24  */
25 #include "bacula.h"
26 #include "filed.h"
27
28 extern CLIENT *me;
29 extern DLL_IMP_EXP char *exepath;
30 extern DLL_IMP_EXP char *version;
31 extern DLL_IMP_EXP char *dist_name;
32
33 const int dbglvl = 150;
34 const char *plugin_type = "-fd.so";
35
36 extern int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
37 extern bool check_changes(JCR *jcr, FF_PKT *ff_pkt);
38
39 /* Function pointers to be set here */
40 extern DLL_IMP_EXP int     (*plugin_bopen)(BFILE *bfd, const char *fname, uint64_t flags, mode_t mode);
41 extern DLL_IMP_EXP int     (*plugin_bclose)(BFILE *bfd);
42 extern DLL_IMP_EXP ssize_t (*plugin_bread)(BFILE *bfd, void *buf, size_t count);
43 extern DLL_IMP_EXP ssize_t (*plugin_bwrite)(BFILE *bfd, void *buf, size_t count);
44 extern DLL_IMP_EXP boffset_t (*plugin_blseek)(BFILE *bfd, boffset_t offset, int whence);
45
46
47 /* Forward referenced functions */
48 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value);
49 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value);
50 static bRC baculaRegisterEvents(bpContext *ctx, ...);
51 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
52   int type, utime_t mtime, const char *fmt, ...);
53 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
54   int level, const char *fmt, ...);
55 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
56               size_t size);
57 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem);
58 static bRC  baculaAddExclude(bpContext *ctx, const char *file);
59 static bRC baculaAddInclude(bpContext *ctx, const char *file);
60 static bRC baculaAddOptions(bpContext *ctx, const char *opts);
61 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type);
62 static bRC baculaAddWild(bpContext *ctx, const char *item, int type);
63 static bRC baculaNewOptions(bpContext *ctx);
64 static bRC baculaNewInclude(bpContext *ctx);
65 static bRC baculaNewPreInclude(bpContext *ctx);
66 static bool is_plugin_compatible(Plugin *plugin);
67 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret);
68 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp);
69 static bRC baculaAcceptFile(bpContext *ctx, struct save_pkt *sp);
70
71 /*
72  * These will be plugged into the global pointer structure for
73  *  the findlib.
74  */
75 static int     my_plugin_bopen(BFILE *bfd, const char *fname, uint64_t flags, mode_t mode);
76 static int     my_plugin_bclose(BFILE *bfd);
77 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
78 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
79 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
80
81
82 /* Bacula info */
83 static bInfo binfo = {
84    sizeof(bInfo),
85    FD_PLUGIN_INTERFACE_VERSION
86 };
87
88 /* Bacula entry points */
89 static bFuncs bfuncs = {
90    sizeof(bFuncs),
91    FD_PLUGIN_INTERFACE_VERSION,
92    baculaRegisterEvents,
93    baculaGetValue,
94    baculaSetValue,
95    baculaJobMsg,
96    baculaDebugMsg,
97    baculaMalloc,
98    baculaFree,
99    baculaAddExclude,
100    baculaAddInclude,
101    baculaAddOptions,
102    baculaAddRegex,
103    baculaAddWild,
104    baculaNewOptions,
105    baculaNewInclude,
106    baculaNewPreInclude,
107    baculaCheckChanges,
108    baculaAcceptFile
109 };
110
111 /*
112  * Bacula private context
113  */
114 struct bacula_ctx {
115    JCR *jcr;                             /* jcr for plugin */
116    bRC  rc;                              /* last return code */
117    bool disabled;                        /* set if plugin disabled */
118    findINCEXE *exclude;                  /* pointer to exclude files */
119    findINCEXE *include;                  /* pointer to include/exclude files */
120 };
121
122 /*
123  * Test if event is for this plugin
124  */
125 static bool for_this_plugin(Plugin *plugin, char *name, int len)
126 {
127    Dmsg4(dbglvl, "name=%s len=%d plugin=%s plen=%d\n", name, len, plugin->file, plugin->file_len);
128    if (!name) {   /* if no plugin name, all plugins get it */
129       return true;
130    }
131    /* Return global VSS job metadata to all plugins */
132    if (strcmp("job", name) == 0) {  /* old V4.0 name for VSS job metadata */
133       return true;
134    }
135    if (strcmp("*all*", name) == 0) { /* new v6.0 name for VSS job metadata */
136       return true;
137    }
138    /* Check if this is the correct plugin */
139    if (len == plugin->file_len && strncmp(plugin->file, name, len) == 0) {
140       return true;
141    }
142    return false;
143 }
144
145
146 bool is_plugin_disabled(bpContext *plugin_ctx)
147 {
148    bacula_ctx *b_ctx;
149    Dsm_check(999);
150    if (!plugin_ctx) {
151       return true;
152    }
153    b_ctx = (bacula_ctx *)plugin_ctx->bContext;
154    if (!b_ctx) {
155       return true;
156    }
157    return b_ctx->disabled;
158 }
159
160 bool is_plugin_disabled(JCR *jcr)
161 {
162    return is_plugin_disabled(jcr->plugin_ctx);
163 }
164
165 /**
166  * Create a plugin event When receiving bEventCancelCommand, this function is
167  * called by an other thread.
168  */
169 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
170 {
171    bpContext *plugin_ctx;
172    bEvent event;
173    Plugin *plugin;
174    char *name = NULL;
175    int i;
176    int len = 0;
177    bool call_if_canceled = false;
178    restore_object_pkt *rop;
179
180    Dsm_check(999);
181    if (!b_plugin_list || !jcr || !jcr->plugin_ctx_list) {
182       return;                         /* Return if no plugins loaded */
183    }
184
185    /*
186     * Some events are sent to only a particular plugin or must be
187     *  called even if the job is canceled
188     */
189    switch(eventType) {
190    case bEventPluginCommand:
191    case bEventOptionPlugin:
192       name = (char *)value;
193       if (!get_plugin_name(jcr, name, &len)) {
194          return;
195       }
196       break;
197    case bEventRestoreObject:
198       /* After all RestoreObject, we have it one more time with value=NULL */
199       if (value) {
200          /* Some RestoreObjects may not have a plugin name */
201          rop = (restore_object_pkt *)value;
202          if (*rop->plugin_name) {
203             name = rop->plugin_name;
204             get_plugin_name(jcr, name, &len);
205          }
206       }
207
208       break;
209    case bEventEndBackupJob:
210    case bEventEndVerifyJob:
211       call_if_canceled = true; /* plugin *must* see this call */
212       break;
213    case bEventStartRestoreJob:
214       foreach_alist_index(i, plugin, b_plugin_list) {
215          plugin->restoreFileStarted = false;
216          plugin->createFileCalled = false;
217       }
218       break;
219    case bEventEndRestoreJob:
220       call_if_canceled = true; /* plugin *must* see this call */
221       break;
222    default:
223       break;
224    }
225
226    /* If call_if_canceled is set, we call the plugin anyway */
227    if (!call_if_canceled && jcr->is_job_canceled()) {
228       return;
229    }
230
231    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
232    event.eventType = eventType;
233
234    Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
235
236    /*
237     * Pass event to every plugin (except if name is set). If name
238     *   is set, we pass it only to the plugin with that name.
239     */
240    foreach_alist_index(i, plugin, b_plugin_list) {
241       if (!for_this_plugin(plugin, name, len)) {
242          Dmsg2(dbglvl, "Not for this plugin name=%s NULL=%d\n",
243             name, name==NULL?1:0);
244          continue;
245       }
246       /*
247        * Note, at this point do not change
248        *   jcr->plugin or jcr->plugin_ctx
249        */
250       Dsm_check(999);
251       plugin_ctx = &plugin_ctx_list[i];
252       if (is_plugin_disabled(plugin_ctx)) {
253          Dmsg1(50, "Plugin %s disabled\n", plugin->file);
254          continue;
255       }
256       if (eventType == bEventEndRestoreJob) {
257          Dmsg0(50, "eventType==bEventEndRestoreJob\n");
258          if (jcr->plugin && jcr->plugin->restoreFileStarted) {
259             plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
260          }
261          if (jcr->plugin) {
262             jcr->plugin->restoreFileStarted = false;
263             jcr->plugin->createFileCalled = false;
264          }
265       }
266       plug_func(plugin)->handlePluginEvent(plugin_ctx, &event, value);
267    }
268    return;
269 }
270
271 /**
272  * Check if file was seen for accurate
273  */
274 bool plugin_check_file(JCR *jcr, char *fname)
275 {
276    Plugin *plugin;
277    int rc = bRC_OK;
278    int i;
279
280    Dsm_check(999);
281    if (!b_plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
282       return false;                      /* Return if no plugins loaded */
283    }
284
285    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
286
287    Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
288
289    /* Pass event to every plugin */
290    foreach_alist_index(i, plugin, b_plugin_list) {
291       jcr->plugin_ctx = &plugin_ctx_list[i];
292       jcr->plugin = plugin;
293       if (is_plugin_disabled(jcr)) {
294          continue;
295       }
296       if (plug_func(plugin)->checkFile == NULL) {
297          continue;
298       }
299       rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
300       if (rc == bRC_Seen) {
301          break;
302       }
303    }
304
305    Dsm_check(999);
306    jcr->plugin = NULL;
307    jcr->plugin_ctx = NULL;
308    return rc == bRC_Seen;
309 }
310
311 /* Get the first part of the the plugin command
312  *  systemstate:/@SYSTEMSTATE/
313  * => ret = 11
314  * => can use for_this_plugin(plug, cmd, ret);
315  *
316  * The plugin command can contain only the plugin name
317  *  Plugin = alldrives
318  * => ret = 9
319  */
320 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret)
321 {
322    char *p;
323    int len;
324    Dsm_check(999);
325    if (!cmd || (*cmd == '\0')) {
326       return false;
327    }
328    /* Handle plugin command here backup */
329    Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
330    if ((p = strchr(cmd, ':')) == NULL) {
331       if (strchr(cmd, ' ') == NULL) { /* we have just the plugin name */
332          len = strlen(cmd);
333       } else {
334          Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
335          return false;
336       }
337    } else {                     /* plugin:argument */
338       len = p - cmd;
339       if (len <= 0) {
340          return false;
341       }
342    }
343    *ret = len;
344    Dsm_check(999);
345    return true;
346 }
347
348
349 static void update_ff_pkt(FF_PKT *ff_pkt, struct save_pkt *sp)
350 {
351    Dsm_check(999);
352    ff_pkt->no_read = sp->no_read;
353    ff_pkt->delta_seq = sp->delta_seq;
354    if (sp->flags & FO_DELTA) {
355       ff_pkt->flags |= FO_DELTA;
356       ff_pkt->delta_seq++;          /* make new delta sequence number */
357    } else {
358       ff_pkt->flags &= ~FO_DELTA;   /* clean delta sequence number */
359       ff_pkt->delta_seq = 0;
360    }
361
362    if (sp->flags & FO_OFFSETS) {
363       ff_pkt->flags |= FO_OFFSETS;
364    } else {
365       ff_pkt->flags &= ~FO_OFFSETS;
366    }
367    /* Sparse code doesn't work with plugins
368     * that use FIFO or STDOUT/IN to communicate
369     */
370    if (sp->flags & FO_SPARSE) {
371       ff_pkt->flags |= FO_SPARSE;
372    } else {
373       ff_pkt->flags &= ~FO_SPARSE;
374    }
375    if (sp->flags & FO_PORTABLE_DATA) {
376       ff_pkt->flags |= FO_PORTABLE_DATA;
377    } else {
378       ff_pkt->flags &= ~FO_PORTABLE_DATA;
379    }
380    ff_pkt->flags |= FO_PLUGIN;       /* data from plugin */
381    Dsm_check(999);
382 }
383
384 /* Ask to a Option Plugin what to do with the current file */
385 bRC plugin_option_handle_file(JCR *jcr, FF_PKT *ff_pkt, struct save_pkt *sp)
386 {
387    Plugin *plugin;
388    bRC ret = bRC_Error;
389    bool found=false;
390    char *cmd = ff_pkt->plugin;
391    int len;
392    int i=0;
393    bEvent event;
394    event.eventType = bEventHandleBackupFile;
395
396    Dsm_check(999);
397    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
398    memset(sp, 0, sizeof(struct save_pkt));
399    sp->pkt_size = sp->pkt_end = sizeof(struct save_pkt);
400    sp->portable = true;
401    sp->cmd = cmd;
402    sp->link = ff_pkt->link;
403    sp->cmd = ff_pkt->plugin;
404    sp->statp = ff_pkt->statp;
405    sp->fname = ff_pkt->fname;
406    sp->delta_seq = ff_pkt->delta_seq;
407    sp->accurate_found = ff_pkt->accurate_found;
408
409    if (!b_plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
410       Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
411       goto bail_out;         /* Return if no plugins loaded */
412    }
413
414    if (!get_plugin_name(jcr, cmd, &len)) {
415       goto bail_out;
416    }
417
418    /* Note, we stop the loop on the first plugin that matches the name */
419    foreach_alist_index(i, plugin, b_plugin_list) {
420       Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
421       if (!for_this_plugin(plugin, cmd, len)) {
422          continue;
423       }
424
425       found=true;
426
427       Dsm_check(999);
428       if (is_plugin_disabled(&plugin_ctx_list[i])) {
429          goto bail_out;
430       }
431
432       jcr->plugin_ctx = &plugin_ctx_list[i];
433       jcr->plugin = plugin;
434
435       ret = plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i],
436                                                  &event, sp);
437
438       /* TODO: would be better to set this in save_file() */
439       if (ret == bRC_OK) {
440          jcr->opt_plugin = true;
441          jcr->plugin = plugin;
442          jcr->plugin_sp = sp;      /* Unset sp in save_file */
443          jcr->plugin_ctx = &plugin_ctx_list[i];
444
445          update_ff_pkt(ff_pkt, sp);
446
447       /* reset plugin in JCR if not used this time */
448       } else {
449          jcr->plugin_ctx = NULL;
450          jcr->plugin = NULL;
451       }
452
453       goto bail_out;
454    } /* end foreach loop */
455 bail_out:
456    if (!found) {
457       Jmsg1(jcr, M_FATAL, 0, "Options plugin \"%s\" not found.\n", cmd);
458    }
459    Dsm_check(999);
460    return ret;
461 }
462
463 /**
464  * Sequence of calls for a backup:
465  * 1. plugin_save() here is called with ff_pkt
466  * 2. we find the plugin requested on the command string
467  * 3. we generate a bEventBackupCommand event to the specified plugin
468  *    and pass it the command string.
469  * 4. we make a startPluginBackup call to the plugin, which gives
470  *    us the data we need in save_pkt
471  * 5. we call Bacula's save_file() subroutine to save the specified
472  *    file.  The plugin will be called at pluginIO() to supply the
473  *    file data.
474  *
475  * Sequence of calls for restore:
476  *   See subroutine plugin_name_stream() below.
477  */
478 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
479 {
480    Plugin *plugin;
481    int len;
482    int i;
483    char *cmd = ff_pkt->top_fname;
484    struct save_pkt sp;
485    bEvent event;
486    POOL_MEM fname(PM_FNAME);
487    POOL_MEM link(PM_FNAME);
488
489    Dsm_check(999);
490    if (!b_plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
491       Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
492       return 1;                            /* Return if no plugins loaded */
493    }
494
495    jcr->cmd_plugin = true;
496    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
497    event.eventType = bEventBackupCommand;
498
499    if (!get_plugin_name(jcr, cmd, &len)) {
500       goto bail_out;
501    }
502
503    /* Note, we stop the loop on the first plugin that matches the name */
504    foreach_alist_index(i, plugin, b_plugin_list) {
505       Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
506       if (!for_this_plugin(plugin, cmd, len)) {
507          continue;
508       }
509       /*
510        * We put the current plugin pointer, and the plugin context
511        *  into the jcr, because during save_file(), the plugin
512        *  will be called many times and these values are needed.
513        */
514       Dsm_check(999);
515       jcr->plugin_ctx = &plugin_ctx_list[i];
516       jcr->plugin = plugin;
517       if (is_plugin_disabled(jcr)) {
518          goto bail_out;
519       }
520
521       Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
522       /* Send the backup command to the right plugin*/
523       if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
524          goto bail_out;
525       }
526       /* Loop getting filenames to backup then saving them */
527       while (!jcr->is_job_canceled()) {
528          memset(&sp, 0, sizeof(sp));
529          sp.pkt_size = sizeof(sp);
530          sp.pkt_end = sizeof(sp);
531          sp.portable = true;
532          sp.no_read = false;
533          sp.flags = 0;
534          sp.cmd = cmd;
535          Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
536                 &sp);
537          Dsm_check(999);
538          /* Get the file save parameters. I.e. the stat pkt ... */
539          if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
540             goto bail_out;
541          }
542          if (sp.type == 0) {
543             Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
544                cmd);
545             goto bail_out;
546          }
547          jcr->plugin_sp = &sp;
548          ff_pkt = jcr->ff;
549          /*
550           * Copy fname and link because save_file() zaps them.  This
551           *  avoids zaping the plugin's strings.
552           */
553          ff_pkt->type = sp.type;
554          if (IS_FT_OBJECT(sp.type)) {
555             if (!sp.object_name) {
556                Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
557                   cmd);
558                goto bail_out;
559             }
560             ff_pkt->fname = cmd;                 /* full plugin string */
561             ff_pkt->object_name = sp.object_name;
562             ff_pkt->object_index = sp.index;     /* restore object index */
563             ff_pkt->object_compression = 0;      /* no compression for now */
564             ff_pkt->object = sp.object;
565             ff_pkt->object_len = sp.object_len;
566          } else {
567             Dsm_check(999);
568             if (!sp.fname) {
569                Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
570                   cmd);
571                goto bail_out;
572             }
573             pm_strcpy(fname, sp.fname);
574             pm_strcpy(link, sp.link);
575
576
577             ff_pkt->fname = fname.c_str();
578             ff_pkt->link = link.c_str();
579             update_ff_pkt(ff_pkt, &sp);
580          }
581
582          memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
583          Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
584          if (sp.object) {
585             Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
586          }
587          /* Call Bacula core code to backup the plugin's file */
588          save_file(jcr, ff_pkt, true);
589          bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
590          if (rc == bRC_More || rc == bRC_OK) {
591             accurate_mark_file_as_seen(jcr, fname.c_str());
592          }
593          Dsm_check(999);
594          if (rc == bRC_More) {
595             continue;
596          }
597          goto bail_out;
598       } /* end while loop */
599       goto bail_out;
600    } /* end loop over all plugins */
601    Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
602
603 bail_out:
604    Dsm_check(999);
605    jcr->cmd_plugin = false;
606    jcr->plugin = NULL;
607    jcr->plugin_ctx = NULL;
608    return 1;
609 }
610
611
612 /**
613  * Sequence of calls for a estimate:
614  * 1. plugin_estimate() here is called with ff_pkt
615  * 2. we find the plugin requested on the command string
616  * 3. we generate a bEventEstimateCommand event to the specified plugin
617  *    and pass it the command string.
618  * 4. we make a startPluginBackup call to the plugin, which gives
619  *    us the data we need in save_pkt
620  *
621  */
622 int plugin_estimate(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
623 {
624    Plugin *plugin;
625    int len;
626    int i;
627    char *cmd = ff_pkt->top_fname;
628    struct save_pkt sp;
629    bEvent event;
630    POOL_MEM fname(PM_FNAME);
631    POOL_MEM link(PM_FNAME);
632    ATTR attr;
633
634    Dsm_check(999);
635    if (!b_plugin_list || !jcr->plugin_ctx_list) {
636       Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
637       return 1;                            /* Return if no plugins loaded */
638    }
639
640    jcr->cmd_plugin = true;
641    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
642    event.eventType = bEventEstimateCommand;
643
644    if (!get_plugin_name(jcr, cmd, &len)) {
645       goto bail_out;
646    }
647
648    /* Note, we stop the loop on the first plugin that matches the name */
649    foreach_alist_index(i, plugin, b_plugin_list) {
650       Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
651       if (!for_this_plugin(plugin, cmd, len)) {
652          continue;
653       }
654       /*
655        * We put the current plugin pointer, and the plugin context
656        *  into the jcr, because during save_file(), the plugin
657        *  will be called many times and these values are needed.
658        */
659       Dsm_check(999);
660       jcr->plugin_ctx = &plugin_ctx_list[i];
661       jcr->plugin = plugin;
662       if (is_plugin_disabled(jcr)) {
663          goto bail_out;
664       }
665
666       Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
667       /* Send the backup command to the right plugin*/
668       if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
669          goto bail_out;
670       }
671       /* Loop getting filenames to backup then saving them */
672       while (!jcr->is_job_canceled()) {
673          Dsm_check(999);
674          memset(&sp, 0, sizeof(sp));
675          sp.pkt_size = sizeof(sp);
676          sp.pkt_end = sizeof(sp);
677          sp.portable = true;
678          sp.flags = 0;
679          sp.cmd = cmd;
680          Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
681                 &sp);
682          /* Get the file save parameters. I.e. the stat pkt ... */
683          if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
684             goto bail_out;
685          }
686          if (sp.type == 0) {
687             Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
688                cmd);
689             goto bail_out;
690          }
691
692          if (!IS_FT_OBJECT(sp.type)) {
693             if (!sp.fname) {
694                Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
695                   cmd);
696                goto bail_out;
697             }
698
699             /* Count only files backed up */
700             switch (sp.type) {
701             case FT_REGE:
702             case FT_REG:
703             case FT_LNK:
704             case FT_DIREND:
705             case FT_SPEC:
706             case FT_RAW:
707             case FT_FIFO:
708             case FT_LNKSAVED:
709                jcr->JobFiles++;        /* increment number of files backed up */
710                break;
711             default:
712                break;
713             }
714             jcr->num_files_examined++;
715
716             if (sp.type != FT_LNKSAVED && S_ISREG(sp.statp.st_mode)) {
717                if (sp.statp.st_size > 0) {
718                   jcr->JobBytes += sp.statp.st_size;
719                }
720             }
721
722             if (jcr->listing) {
723                memcpy(&attr.statp, &sp.statp, sizeof(struct stat));
724                attr.type = sp.type;
725                attr.ofname = (POOLMEM *)sp.fname;
726                attr.olname = (POOLMEM *)sp.link;
727                print_ls_output(jcr, &attr);
728             }
729          }
730
731          Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
732          if (sp.object) {
733             Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
734          }
735          bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
736          if (rc == bRC_More || rc == bRC_OK) {
737             accurate_mark_file_as_seen(jcr, sp.fname);
738          }
739          Dsm_check(999);
740          if (rc == bRC_More) {
741             continue;
742          }
743          goto bail_out;
744       } /* end while loop */
745       goto bail_out;
746    } /* end loop over all plugins */
747    Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
748
749 bail_out:
750    Dsm_check(999);
751    jcr->cmd_plugin = false;
752    jcr->plugin = NULL;
753    jcr->plugin_ctx = NULL;
754    return 1;
755 }
756
757 /**
758  * Send plugin name start/end record to SD
759  */
760 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
761 {
762    int stat;
763    int index = jcr->JobFiles;
764    struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
765
766    Dsm_check(999);
767    if (!sp) {
768       Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
769       return false;
770    }
771    if (jcr->is_job_canceled()) {
772       return false;
773    }
774
775    if (start) {
776       index++;                  /* JobFiles not incremented yet */
777    }
778    Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
779    /* Send stream header */
780    Dsm_check(999);
781    if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
782      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
783            sd->bstrerror());
784      return false;
785    }
786    Dmsg1(dbglvl, "send plugin name hdr: %s\n", sd->msg);
787
788    Dsm_check(999);
789    if (start) {
790       /* Send data -- not much */
791       stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
792    } else {
793       /* Send end of data */
794       stat = sd->fsend("%ld 0", jcr->JobFiles);
795    }
796    Dsm_check(999);
797    if (!stat) {
798       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
799             sd->bstrerror());
800          return false;
801    }
802    Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
803    sd->signal(BNET_EOD);            /* indicate end of plugin name data */
804    Dsm_check(999);
805    return true;
806 }
807
808 /**
809  * Plugin name stream found during restore.  The record passed in
810  *  argument name was generated in send_plugin_name() above.
811  *
812  * Returns: true  if start of stream
813  *          false if end of steam
814  */
815 bool plugin_name_stream(JCR *jcr, char *name)
816 {
817    char *p = name;
818    char *cmd;
819    bool start;
820    Plugin *plugin;
821    int len;
822    int i;
823    bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
824
825    Dsm_check(999);
826    Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
827    skip_nonspaces(&p);             /* skip over jcr->JobFiles */
828    skip_spaces(&p);
829    start = *p == '1';
830    if (start) {
831       /* Start of plugin data */
832       skip_nonspaces(&p);          /* skip start/end flag */
833       skip_spaces(&p);
834 //    portable = *p == '1';
835       skip_nonspaces(&p);          /* skip portable flag */
836       skip_spaces(&p);
837       cmd = p;
838    } else {
839       /*
840        * End of plugin data, notify plugin, then clear flags
841        */
842       Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
843       if (jcr->plugin && jcr->plugin->restoreFileStarted) {
844          plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
845       }
846       if (jcr->plugin) {
847          jcr->plugin->restoreFileStarted = false;
848          jcr->plugin->createFileCalled = false;
849       }
850       jcr->plugin_ctx = NULL;
851       jcr->plugin = NULL;
852       goto bail_out;
853    }
854    Dsm_check(999);
855    if (!plugin_ctx_list) {
856       goto bail_out;
857    }
858
859    /*
860     * After this point, we are dealing with a restore start
861     */
862    if (!get_plugin_name(jcr, cmd, &len)) {
863       goto bail_out;
864    }
865
866    /*
867     * Search for correct plugin as specified on the command
868     */
869    Dsm_check(999);
870    foreach_alist_index(i, plugin, b_plugin_list) {
871       bEvent event;
872       Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
873       if (!for_this_plugin(plugin, cmd, len)) {
874          continue;
875       }
876       Dsm_check(999);
877       jcr->plugin_ctx = &plugin_ctx_list[i];
878       jcr->plugin = plugin;
879       if (is_plugin_disabled(jcr)) {
880          Dmsg1(dbglvl, "Plugin %s disabled\n", cmd);
881          goto bail_out;
882       }
883       Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
884       event.eventType = bEventRestoreCommand;
885       if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx,
886             &event, cmd) != bRC_OK) {
887          Dmsg1(dbglvl, "Handle event failed. Plugin=%s\n", cmd);
888          goto bail_out;
889       }
890       if (plugin->restoreFileStarted) {
891          Jmsg2(jcr, M_FATAL, 0, "Second call to startRestoreFile. plugin=%s cmd=%s\n", plugin->file, cmd);
892          plugin->restoreFileStarted = false;
893          goto bail_out;
894       }
895       if (plug_func(plugin)->startRestoreFile(jcr->plugin_ctx, cmd) == bRC_OK) {
896          plugin->restoreFileStarted = true;
897          goto ok_out;
898       } else {
899          Dmsg1(dbglvl, "startRestoreFile failed. plugin=%s\n", cmd);
900       }
901       goto bail_out;
902    }
903    Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
904    goto bail_out;
905
906 ok_out:
907    return start;
908
909 bail_out:
910    Dsm_check(999);
911    jcr->plugin = NULL;
912    jcr->plugin_ctx = NULL;
913    return start;
914 }
915
916 /**
917  * Tell the plugin to create the file.  Return values are
918  *   This is called only during Restore
919  *
920  *  CF_ERROR    -- error
921  *  CF_SKIP     -- skip processing this file
922  *  CF_EXTRACT  -- extract the file (i.e.call i/o routines)
923  *  CF_CREATED  -- created, but no content to extract (typically directories)
924  *
925  */
926 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
927 {
928    bpContext *plugin_ctx = jcr->plugin_ctx;
929    Plugin *plugin = jcr->plugin;
930    struct restore_pkt rp;
931    int flags;
932    int rc;
933
934    Dsm_check(999);
935    if (!plugin || !plugin_ctx || jcr->is_job_canceled()) {
936       return CF_ERROR;
937    }
938
939    rp.pkt_size = sizeof(rp);
940    rp.pkt_end = sizeof(rp);
941    rp.delta_seq = attr->delta_seq;
942    rp.stream = attr->stream;
943    rp.data_stream = attr->data_stream;
944    rp.type = attr->type;
945    rp.file_index = attr->file_index;
946    rp.LinkFI = attr->LinkFI;
947    rp.uid = attr->uid;
948    rp.statp = attr->statp;                /* structure assignment */
949    rp.attrEx = attr->attrEx;
950    rp.ofname = attr->ofname;
951    rp.olname = attr->olname;
952    rp.where = jcr->where;
953    rp.RegexWhere = jcr->RegexWhere;
954    rp.replace = jcr->replace;
955    rp.create_status = CF_ERROR;
956    Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n",
957          rp.stream, rp.type, rp.LinkFI, rp.ofname);
958    if (rp.attrEx) {
959       Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
960    }
961    Dsm_check(999);
962    if (!plugin->restoreFileStarted || plugin->createFileCalled) {
963       Jmsg2(jcr, M_FATAL, 0, "Unbalanced call to createFile=%d %d\n",
964          plugin->createFileCalled, plugin->restoreFileStarted);
965       plugin->createFileCalled = false;
966       return CF_ERROR;
967    }
968    rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
969    if (rc != bRC_OK) {
970       Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
971             rc, attr->ofname);
972       return CF_ERROR;
973    }
974    if (rp.create_status == CF_ERROR) {
975       Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
976             attr->ofname);
977       return CF_ERROR;
978    }
979
980    if (rp.create_status == CF_SKIP) {
981       return CF_SKIP;
982    }
983
984    if (rp.create_status == CF_CORE) {
985       return CF_CORE;           /* Let Bacula core handle the file creation */
986    }
987
988    /* Use the bfile for plugin */
989    set_cmd_plugin(bfd, jcr);
990
991    /* Created link or directory? */
992    if (rp.create_status == CF_CREATED) {
993       return rp.create_status;        /* yes, no need to bopen */
994    }
995
996    Dsm_check(999);
997
998    flags =  O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
999    Dmsg0(dbglvl, "call bopen\n");
1000    int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
1001    Dmsg1(dbglvl, "bopen status=%d\n", stat);
1002    if (stat < 0) {
1003       berrno be;
1004       be.set_errno(bfd->berrno);
1005       Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
1006             attr->ofname, be.bstrerror());
1007       Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
1008       return CF_ERROR;
1009    }
1010
1011    if (!is_bopen(bfd)) {
1012       Dmsg0(000, "===== BFD is not open!!!!\n");
1013    }
1014    Dsm_check(999);
1015    return CF_EXTRACT;
1016 }
1017
1018 /**
1019  * Reset the file attributes after all file I/O is done -- this allows
1020  *  the previous access time/dates to be set properly, and it also allows
1021  *  us to properly set directory permissions.
1022  *  Not currently Implemented.
1023  */
1024 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
1025 {
1026    Plugin *plugin = (Plugin *)jcr->plugin;
1027    struct restore_pkt rp;
1028
1029    Dmsg0(dbglvl, "plugin_set_attributes\n");
1030
1031    if (!plugin || !jcr->plugin_ctx) {
1032       return false;
1033    }
1034
1035    memset(&rp, 0, sizeof(rp));
1036    rp.pkt_size = sizeof(rp);
1037    rp.pkt_end = sizeof(rp);
1038    rp.stream = attr->stream;
1039    rp.data_stream = attr->data_stream;
1040    rp.type = attr->type;
1041    rp.file_index = attr->file_index;
1042    rp.LinkFI = attr->LinkFI;
1043    rp.uid = attr->uid;
1044    rp.statp = attr->statp;                /* structure assignment */
1045    rp.attrEx = attr->attrEx;
1046    rp.ofname = attr->ofname;
1047    rp.olname = attr->olname;
1048    rp.where = jcr->where;
1049    rp.RegexWhere = jcr->RegexWhere;
1050    rp.replace = jcr->replace;
1051    rp.create_status = CF_ERROR;
1052
1053    plug_func(plugin)->setFileAttributes(jcr->plugin_ctx, &rp);
1054
1055    if (rp.create_status == CF_CORE) {
1056       set_attributes(jcr, attr, ofd);
1057    } else {
1058       if (is_bopen(ofd)) {
1059          bclose(ofd);
1060       }
1061       pm_strcpy(attr->ofname, "*none*");
1062    }
1063
1064    Dsm_check(999);
1065    return true;
1066 }
1067
1068 /*
1069  * The Plugin ACL data backup. We are using a new Plugin callback:
1070  *    handleXACLdata() for that. The new callback get a pointer to
1071  *    struct xacl_pkt as a main argument which consist of the following
1072  *    data:
1073  *       xacl.func - could be the one of BACL_BACKUP, BACL_RESTORE,
1074  *                   BXATTR_BACKUP, BXATTR_RESTORE
1075  *       xacl.count - the length of data at the content buffer
1076  *       xacl.content - the buffer itself
1077  *    The buffer (xacl.content) is supplied by Bacula during restore and has to
1078  *    be supplied by a Plugin during backup.
1079  *    The new callback should return bRC_OK on success and bRC_Error on
1080  *    any error.
1081  *
1082  * in:
1083  *    jcr - Job Control Record
1084  *    ff_pkt - file save packet
1085  *    data is a pointer to variable returned
1086  * out:
1087  *    data - the pointer to data buffer returned from plugin
1088  *    0 - Success, no more data to save
1089  *    > 0 - Success and the number of bytes returned in **data buffer
1090  *    -1 - Error, no acls data to backup
1091  */
1092 int plugin_backup_acl(JCR *jcr, FF_PKT *ff_pkt, char **data)
1093 {
1094    struct xacl_pkt xacl;
1095    Plugin *plugin = (Plugin *)jcr->plugin;
1096    bRC rc;
1097
1098    Dmsg0(dbglvl, "plugin_backup_acl\n");
1099
1100    /* check of input variables */
1101    if (!plugin || !jcr->plugin_ctx || !data) {
1102       return 0;
1103    }
1104
1105    /* prepare the xacl packet */
1106    memset(&xacl, 0, sizeof(xacl));
1107    xacl.pkt_size = sizeof(xacl);
1108    xacl.pkt_end = sizeof(xacl);
1109    xacl.func = BACL_BACKUP;
1110
1111    rc = plug_func(plugin)->handleXACLdata(jcr->plugin_ctx, &xacl);
1112
1113    /* check out status */
1114    if (rc != bRC_OK){
1115       Dmsg0(dbglvl, "plugin->handleXACLdata returned error\n");
1116       return -1;
1117    }
1118    if (xacl.count > 0){
1119       /* we have something to save, so prepare return data */
1120       *data = xacl.content;
1121       return xacl.count;
1122    }
1123
1124    return 0;
1125 }
1126
1127 /*
1128  * Called here when Bacula got ACL stream to restore but not every stream but
1129  *    a specific one: STREAM_XACL_PLUGIN_ACL which means a plugin has to
1130  *    be called.
1131  *
1132  * in:
1133  *    jcr - Job Control Record
1134  *    data - content to restore
1135  *    length - the length of the content to restore
1136  * out:
1137  *    true - when successful
1138  *    false - on any Error
1139  */
1140 bool plugin_restore_acl(JCR *jcr, char *data, uint32_t length)
1141 {
1142    struct xacl_pkt xacl;
1143    Plugin *plugin = (Plugin *)jcr->plugin;
1144    bRC rc;
1145
1146    Dmsg0(dbglvl, "plugin_restore_acl\n");
1147
1148    /* check of input variables */
1149    if (!plugin || !jcr->plugin_ctx || !data || length == 0) {
1150       return true;
1151    }
1152
1153    /* prepare the xacl packet */
1154    memset(&xacl, 0, sizeof(xacl));
1155    xacl.pkt_size = sizeof(xacl);
1156    xacl.pkt_end = sizeof(xacl);
1157    xacl.func = BACL_RESTORE;
1158    xacl.content = data;
1159    xacl.count = length;
1160
1161    rc = plug_func(plugin)->handleXACLdata(jcr->plugin_ctx, &xacl);
1162
1163    /* check out status */
1164    if (rc != bRC_OK){
1165       Dmsg0(dbglvl, "plugin->handleXACLdata returned error\n");
1166       return false;
1167    }
1168
1169    return true;
1170 }
1171
1172 /*
1173  * The Plugin XATTR data backup. We are using a new Plugin callback:
1174  *    handleXACLdata() for that. Check plugin_backup_acl for new callback
1175  *    description.
1176  *
1177  * in:
1178  *    jcr - Job Control Record
1179  *    ff_pkt - file save packet
1180  *    data is a pointer to variable returned
1181  * out:
1182  *    data - the pointer to data buffer returned from plugin
1183  *    0 - Success, no more data to save
1184  *    >0 - Success and the number of bytes returned in **data buffer
1185  *    <0 - Error
1186  */
1187 int plugin_backup_xattr(JCR *jcr, FF_PKT *ff_pkt, char **data)
1188 {
1189
1190    struct xacl_pkt xacl;
1191    Plugin *plugin = (Plugin *)jcr->plugin;
1192    bRC rc;
1193
1194    Dmsg0(dbglvl, "plugin_backup_xattr\n");
1195
1196    /* check of input variables */
1197    if (!plugin || !jcr->plugin_ctx || !data) {
1198       return 0;
1199    }
1200
1201    /* prepare the xacl packet */
1202    memset(&xacl, 0, sizeof(xacl));
1203    xacl.pkt_size = sizeof(xacl);
1204    xacl.pkt_end = sizeof(xacl);
1205    xacl.func = BXATTR_BACKUP;
1206
1207    rc = plug_func(plugin)->handleXACLdata(jcr->plugin_ctx, &xacl);
1208
1209    /* check out status */
1210    if (rc != bRC_OK){
1211       Dmsg0(dbglvl, "plugin->handleXACLdata returned error\n");
1212       return -1;
1213    }
1214    if (xacl.count > 0){
1215       /* we have something to save, so prepare return data */
1216       *data = xacl.content;
1217       return xacl.count;
1218    }
1219
1220    return 0;
1221 }
1222
1223 /*
1224  * Called here when Bacula got XATTR stream to restore but not every stream but
1225  *    a specific one: STREAM_XACL_PLUGIN_XATTR which means a plugin has to
1226  *    be called.
1227  *
1228  * in:
1229  *    jcr - Job Control Record
1230  *    data - content to restore
1231  *    length - the length of the content to restore
1232  * out:
1233  *    true - when successful
1234  *    false - on any Error
1235  */
1236 bool plugin_restore_xattr(JCR *jcr, char *data, uint32_t length)
1237 {
1238    struct xacl_pkt xacl;
1239    Plugin *plugin = (Plugin *)jcr->plugin;
1240    bRC rc;
1241
1242    Dmsg0(dbglvl, "plugin_restore_xattr\n");
1243
1244    /* check of input variables */
1245    if (!plugin || !jcr->plugin_ctx || !data || length == 0) {
1246       return true;
1247    }
1248
1249    /* prepare the xacl packet */
1250    memset(&xacl, 0, sizeof(xacl));
1251    xacl.pkt_size = sizeof(xacl);
1252    xacl.pkt_end = sizeof(xacl);
1253    xacl.func = BXATTR_RESTORE;
1254    xacl.content = data;
1255    xacl.count = length;
1256
1257    rc = plug_func(plugin)->handleXACLdata(jcr->plugin_ctx, &xacl);
1258
1259    /* check out status */
1260    if (rc != bRC_OK){
1261       Dmsg0(dbglvl, "plugin->handleXACLdata returned error\n");
1262       return false;
1263    }
1264
1265    return true;
1266 }
1267
1268 /*
1269  * Print to file the plugin info.
1270  */
1271 void dump_fd_plugin(Plugin *plugin, FILE *fp)
1272 {
1273    if (!plugin) {
1274       return ;
1275    }
1276    pInfo *info = (pInfo *)plugin->pinfo;
1277    fprintf(fp, "\tversion=%d\n", info->version);
1278    fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
1279    fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
1280    fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
1281    fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
1282    fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
1283    fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
1284 }
1285
1286 /**
1287  * This entry point is called internally by Bacula to ensure
1288  *  that the plugin IO calls come into this code.
1289  */
1290 void load_fd_plugins(const char *plugin_dir)
1291 {
1292    Plugin *plugin;
1293    int i;
1294
1295    if (!plugin_dir) {
1296       Dmsg0(dbglvl, "plugin dir is NULL\n");
1297       return;
1298    }
1299
1300    b_plugin_list = New(alist(10, not_owned_by_alist));
1301    Dsm_check(999);
1302    if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
1303                      is_plugin_compatible)) {
1304       /* Either none found, or some error */
1305       if (b_plugin_list->size() == 0) {
1306          delete b_plugin_list;
1307          b_plugin_list = NULL;
1308          Dmsg0(dbglvl, "No plugins loaded\n");
1309          return;
1310       }
1311    }
1312
1313    /* Plug entry points called from findlib */
1314    plugin_bopen  = my_plugin_bopen;
1315    plugin_bclose = my_plugin_bclose;
1316    plugin_bread  = my_plugin_bread;
1317    plugin_bwrite = my_plugin_bwrite;
1318    plugin_blseek = my_plugin_blseek;
1319    Dsm_check(999);
1320
1321    /*
1322     * Verify that the plugin is acceptable, and print information
1323     *  about it.
1324     */
1325    foreach_alist_index(i, plugin, b_plugin_list) {
1326       Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
1327       Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
1328    }
1329
1330    dbg_plugin_add_hook(dump_fd_plugin);
1331    Dsm_check(999);
1332 }
1333
1334 /**
1335  * Check if a plugin is compatible.  Called by the load_plugin function
1336  *  to allow us to verify the plugin.
1337  */
1338 static bool is_plugin_compatible(Plugin *plugin)
1339 {
1340    pInfo *info = (pInfo *)plugin->pinfo;
1341    Dmsg0(dbglvl, "is_plugin_compatible called\n");
1342    Dsm_check(999);
1343    if (chk_dbglvl(50)) {
1344       dump_fd_plugin(plugin, stdin);
1345    }
1346    if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
1347       Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
1348            plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
1349       Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
1350            plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
1351
1352       return false;
1353    }
1354    if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
1355       Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
1356            plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1357       Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
1358            plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1359       return false;
1360    }
1361    if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
1362        strcmp(info->plugin_license, "AGPLv3") != 0 &&
1363        strcmp(info->plugin_license, "Bacula Systems(R) SA") != 0) {
1364       Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
1365            plugin->file, info->plugin_license);
1366       Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
1367            plugin->file, info->plugin_license);
1368       return false;
1369    }
1370    if (info->size != sizeof(pInfo)) {
1371       Jmsg(NULL, M_ERROR, 0,
1372            _("Plugin size incorrect. Plugin=%s wanted=%d got=%d\n"),
1373            plugin->file, sizeof(pInfo), info->size);
1374       return false;
1375    }
1376
1377    Dsm_check(999);
1378    return true;
1379 }
1380
1381
1382 /**
1383  * Create a new instance of each plugin for this Job
1384  *   Note, b_plugin_list can exist but jcr->plugin_ctx_list can
1385  *   be NULL if no plugins were loaded.
1386  */
1387 void new_plugins(JCR *jcr)
1388 {
1389    Plugin *plugin;
1390    int i;
1391
1392    Dsm_check(999);
1393    if (!b_plugin_list) {
1394       Dmsg0(dbglvl, "plugin list is NULL\n");
1395       return;
1396    }
1397    if (jcr->is_job_canceled()) {
1398       return;
1399    }
1400
1401    int num = b_plugin_list->size();
1402
1403    if (num == 0) {
1404       Dmsg0(dbglvl, "No plugins loaded\n");
1405       return;
1406    }
1407
1408    jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
1409
1410    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1411    Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1412    foreach_alist_index(i, plugin, b_plugin_list) {
1413       Dsm_check(999);
1414       /* Start a new instance of each plugin */
1415       bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
1416       memset(b_ctx, 0, sizeof(bacula_ctx));
1417       b_ctx->jcr = jcr;
1418       plugin_ctx_list[i].bContext = (void *)b_ctx;   /* Bacula private context */
1419       plugin_ctx_list[i].pContext = NULL;
1420       if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i]) != bRC_OK) {
1421          Dmsg1(000, "Plugin %s will be disabled\n", plugin->file);
1422          b_ctx->disabled = true;
1423       }
1424    }
1425    if (i > num) {
1426       Jmsg2(jcr, M_ABORT, 0, "Num plugins=%d exceeds list size=%d\n",
1427             i, num);
1428    }
1429    Dsm_check(999);
1430 }
1431
1432 /**
1433  * Free the plugin instances for this Job
1434  */
1435 void free_plugins(JCR *jcr)
1436 {
1437    Plugin *plugin;
1438    int i;
1439
1440    if (!b_plugin_list || !jcr->plugin_ctx_list) {
1441       return;                         /* no plugins, nothing to do */
1442    }
1443
1444    Dsm_check(999);
1445    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1446    Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1447    foreach_alist_index(i, plugin, b_plugin_list) {
1448       /* Free the plugin instance */
1449       plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
1450       free(plugin_ctx_list[i].bContext);     /* free Bacula private context */
1451       Dsm_check(999);
1452    }
1453    Dsm_check(999);
1454    free(plugin_ctx_list);
1455    jcr->plugin_ctx_list = NULL;
1456 }
1457
1458 static int my_plugin_bopen(BFILE *bfd, const char *fname, uint64_t flags, mode_t mode)
1459 {
1460    JCR *jcr = bfd->jcr;
1461    Plugin *plugin = (Plugin *)jcr->plugin;
1462    struct io_pkt io;
1463
1464    Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
1465    Dsm_check(999);
1466    if (!plugin || !jcr->plugin_ctx) {
1467       return 0;
1468    }
1469    io.pkt_size = sizeof(io);
1470    io.pkt_end = sizeof(io);
1471    io.func = IO_OPEN;
1472    io.count = 0;
1473    io.buf = NULL;
1474    io.fname = fname;
1475    io.flags = flags;
1476    io.mode = mode;
1477    io.win32 = false;
1478    io.lerror = 0;
1479    io.status = -1;
1480    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1481    bfd->berrno = io.io_errno;
1482    if (io.win32) {
1483       errno = b_errno_win32;
1484    } else {
1485       errno = io.io_errno;
1486       bfd->lerror = io.lerror;
1487    }
1488    Dmsg1(dbglvl, "Return from plugin open status=%d\n", io.status);
1489    Dsm_check(999);
1490    return io.status;
1491 }
1492
1493 static int my_plugin_bclose(BFILE *bfd)
1494 {
1495    JCR *jcr = bfd->jcr;
1496    Plugin *plugin = (Plugin *)jcr->plugin;
1497    struct io_pkt io;
1498
1499    Dsm_check(999);
1500    Dmsg0(dbglvl, "===== plugin_bclose\n");
1501    if (!plugin || !jcr->plugin_ctx) {
1502       return 0;
1503    }
1504    io.pkt_size = sizeof(io);
1505    io.pkt_end = sizeof(io);
1506    io.func = IO_CLOSE;
1507    io.count = 0;
1508    io.buf = NULL;
1509    io.win32 = false;
1510    io.lerror = 0;
1511    io.status = -1;
1512    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1513    bfd->berrno = io.io_errno;
1514    if (io.win32) {
1515       errno = b_errno_win32;
1516    } else {
1517       errno = io.io_errno;
1518       bfd->lerror = io.lerror;
1519    }
1520    Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
1521    Dsm_check(999);
1522    return io.status;
1523 }
1524
1525 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
1526 {
1527    JCR *jcr = bfd->jcr;
1528    Plugin *plugin = (Plugin *)jcr->plugin;
1529    struct io_pkt io;
1530
1531    Dsm_check(999);
1532    Dmsg0(dbglvl, "plugin_bread\n");
1533    if (!plugin || !jcr->plugin_ctx) {
1534       return 0;
1535    }
1536    io.pkt_size = sizeof(io);
1537    io.pkt_end = sizeof(io);
1538    io.func = IO_READ;
1539    io.count = count;
1540    io.buf = (char *)buf;
1541    io.win32 = false;
1542    io.offset = 0;
1543    io.lerror = 0;
1544    io.status = -1;
1545    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1546    bfd->offset = io.offset;
1547    bfd->berrno = io.io_errno;
1548    if (io.win32) {
1549       errno = b_errno_win32;
1550    } else {
1551       errno = io.io_errno;
1552       bfd->lerror = io.lerror;
1553    }
1554    Dsm_check(999);
1555    return (ssize_t)io.status;
1556 }
1557
1558 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
1559 {
1560    JCR *jcr = bfd->jcr;
1561    Plugin *plugin = (Plugin *)jcr->plugin;
1562    struct io_pkt io;
1563
1564    Dsm_check(999);
1565    Dmsg0(dbglvl, "plugin_bwrite\n");
1566    if (!plugin || !jcr->plugin_ctx) {
1567       Dmsg0(0, "No plugin context\n");
1568       return 0;
1569    }
1570    io.pkt_size = sizeof(io);
1571    io.pkt_end = sizeof(io);
1572    io.func = IO_WRITE;
1573    io.count = count;
1574    io.buf = (char *)buf;
1575    io.win32 = false;
1576    io.lerror = 0;
1577    io.status = -1;
1578    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1579    bfd->berrno = io.io_errno;
1580    if (io.win32) {
1581       errno = b_errno_win32;
1582    } else {
1583       errno = io.io_errno;
1584       bfd->lerror = io.lerror;
1585    }
1586    Dsm_check(999);
1587    return (ssize_t)io.status;
1588 }
1589
1590 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
1591 {
1592    JCR *jcr = bfd->jcr;
1593    Plugin *plugin = (Plugin *)jcr->plugin;
1594    struct io_pkt io;
1595
1596    Dsm_check(999);
1597    Dmsg0(dbglvl, "plugin_bseek\n");
1598    if (!plugin || !jcr->plugin_ctx) {
1599       return 0;
1600    }
1601    io.pkt_size = sizeof(io);
1602    io.pkt_end = sizeof(io);
1603    io.func = IO_SEEK;
1604    io.offset = offset;
1605    io.whence = whence;
1606    io.win32 = false;
1607    io.lerror = 0;
1608    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1609    bfd->berrno = io.io_errno;
1610    if (io.win32) {
1611       errno = b_errno_win32;
1612    } else {
1613       errno = io.io_errno;
1614       bfd->lerror = io.lerror;
1615    }
1616    Dsm_check(999);
1617    return (boffset_t)io.offset;
1618 }
1619
1620 /* ==============================================================
1621  *
1622  * Callbacks from the plugin
1623  *
1624  * ==============================================================
1625  */
1626 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
1627 {
1628    JCR *jcr;
1629    if (!value) {
1630       return bRC_Error;
1631    }
1632
1633    Dsm_check(999);
1634    switch (var) {               /* General variables, no need of ctx */
1635    case bVarFDName:
1636       *((char **)value) = my_name;
1637       break;
1638    case bVarWorkingDir:
1639       *(void **)value = me->working_directory;
1640       break;
1641    case bVarExePath:
1642       *(char **)value = exepath;
1643       break;
1644    case bVarVersion:
1645       *(char **)value = version;
1646       break;
1647    case bVarDistName:
1648       *(char **)value = dist_name;
1649       break;
1650    case bVarPrevJobName:
1651       break;
1652    case bVarPrefixLinks:
1653       break;
1654    case bVarxxx:
1655       break;
1656    default:
1657       break;
1658    }
1659
1660    if (!ctx) {                  /* Other variables need context */
1661       return bRC_Error;
1662    }
1663
1664    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1665    if (!jcr) {
1666       return bRC_Error;
1667    }
1668
1669    switch (var) {
1670    case bVarJobId:
1671       *((int *)value) = jcr->JobId;
1672       Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
1673       break;
1674    case bVarLevel:
1675       *((int *)value) = jcr->getJobLevel();
1676       Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
1677       break;
1678    case bVarType:
1679       *((int *)value) = jcr->getJobType();
1680       Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
1681       break;
1682    case bVarClient:
1683       *((char **)value) = jcr->client_name;
1684       Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
1685       break;
1686    case bVarJobName:
1687       *((char **)value) = jcr->Job;
1688       Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
1689       break;
1690    case bVarPrevJobName:
1691       *((char **)value) = jcr->PrevJob;
1692       Dmsg1(dbglvl, "Bacula: return Previous Job name=%s\n", jcr->PrevJob);
1693       break;
1694    case bVarJobStatus:
1695       *((int *)value) = jcr->JobStatus;
1696       Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
1697       break;
1698    case bVarSinceTime:
1699       *((int *)value) = (int)jcr->mtime;
1700       Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
1701       break;
1702    case bVarAccurate:
1703       *((int *)value) = (int)jcr->accurate;
1704       Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1705       break;
1706    case bVarInteractiveSession:
1707       *(int *)value = (int)jcr->interactive_session;
1708       break;
1709    case bVarFileSeen:
1710       break;                 /* a write only variable, ignore read request */
1711    case bVarVssObject:
1712        return bRC_Error;
1713    case bVarVssDllHandle:
1714        return bRC_Error;
1715    case bVarWhere:
1716       *(char **)value = jcr->where;
1717       break;
1718    case bVarRegexWhere:
1719       *(char **)value = jcr->RegexWhere;
1720       break;
1721    case bVarPrefixLinks:
1722       *(int *)value = (int)jcr->prefix_links;
1723       break;
1724    case bVarReplace:
1725       *((int*)value) = jcr->replace;
1726       Dmsg1(dbglvl, "Bacula: return replace=%c\n", jcr->replace);
1727       break;
1728    case bVarFDName:             /* get warning with g++ if we missed one */
1729    case bVarWorkingDir:
1730    case bVarExePath:
1731    case bVarVersion:
1732    case bVarDistName:
1733    case bVarxxx:
1734       break;
1735    }
1736    Dsm_check(999);
1737    return bRC_OK;
1738 }
1739
1740 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1741 {
1742    JCR *jcr;
1743    Dsm_check(999);
1744    if (!value || !ctx) {
1745       return bRC_Error;
1746    }
1747 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1748    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1749    if (!jcr) {
1750       return bRC_Error;
1751    }
1752 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
1753    switch (var) {
1754    case bVarFileSeen:
1755       if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1756          return bRC_Error;
1757       }
1758       break;
1759    case bVarInteractiveSession:
1760       jcr->interactive_session = (((intptr_t) value) == 1);
1761       break;
1762    default:
1763       break;
1764    }
1765    Dsm_check(999);
1766    return bRC_OK;
1767 }
1768
1769 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1770 {
1771    va_list args;
1772    uint32_t event;
1773
1774    Dsm_check(999);
1775    if (!ctx) {
1776       return bRC_Error;
1777    }
1778
1779    va_start(args, ctx);
1780    while ((event = va_arg(args, uint32_t))) {
1781       Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1782    }
1783    va_end(args);
1784    Dsm_check(999);
1785    return bRC_OK;
1786 }
1787
1788 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1789   int type, utime_t mtime, const char *fmt, ...)
1790 {
1791    va_list arg_ptr;
1792    char buf[2000];
1793    JCR *jcr;
1794
1795    Dsm_check(999);
1796    if (ctx) {
1797       jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1798    } else {
1799       jcr = NULL;
1800    }
1801
1802    va_start(arg_ptr, fmt);
1803    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1804    va_end(arg_ptr);
1805    Jmsg(jcr, type, mtime, "%s", buf);
1806    Dsm_check(999);
1807    return bRC_OK;
1808 }
1809
1810 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1811   int level, const char *fmt, ...)
1812 {
1813    va_list arg_ptr;
1814    char buf[2000];
1815
1816    Dsm_check(999);
1817    va_start(arg_ptr, fmt);
1818    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1819    va_end(arg_ptr);
1820    d_msg(file, line, level, "%s", buf);
1821    Dsm_check(999);
1822    return bRC_OK;
1823 }
1824
1825 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1826               size_t size)
1827 {
1828 #ifdef SMARTALLOC
1829    return sm_malloc(file, line, size);
1830 #else
1831    return malloc(size);
1832 #endif
1833 }
1834
1835 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1836 {
1837 #ifdef SMARTALLOC
1838    sm_free(file, line, mem);
1839 #else
1840    free(mem);
1841 #endif
1842 }
1843
1844 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1845 {
1846    Dsm_check(999);
1847    if (!ctx) {
1848       return false;
1849    }
1850    bctx = (bacula_ctx *)ctx->bContext;
1851    if (!bctx) {
1852       return false;
1853    }
1854    jcr = bctx->jcr;
1855    if (!jcr) {
1856       return false;
1857    }
1858    return true;
1859 }
1860
1861 /**
1862  * Let the plugin define files/directories to be excluded
1863  *  from the main backup.
1864  */
1865 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1866 {
1867    JCR *jcr;
1868    findINCEXE *old;
1869    bacula_ctx *bctx;
1870    Dsm_check(999);
1871    if (!is_ctx_good(ctx, jcr, bctx)) {
1872       return bRC_Error;
1873    }
1874    if (!file) {
1875       return bRC_Error;
1876    }
1877
1878    /* Save the include context */
1879    old = get_incexe(jcr);
1880
1881    /* Not right time to add exlude */
1882    if (!old) {
1883       return bRC_Error;
1884    }
1885
1886    if (!bctx->exclude) {
1887       bctx->exclude = new_exclude(jcr);
1888    }
1889
1890    /* Set the Exclude context */
1891    set_incexe(jcr, bctx->exclude);
1892
1893    add_file_to_fileset(jcr, file, true);
1894
1895    /* Restore the current context */
1896    set_incexe(jcr, old);
1897
1898    Dmsg1(100, "Add exclude file=%s\n", file);
1899    Dsm_check(999);
1900    return bRC_OK;
1901 }
1902
1903 /**
1904  * Let the plugin define files/directories to be excluded
1905  *  from the main backup.
1906  */
1907 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1908 {
1909    JCR *jcr;
1910    findINCEXE *old;
1911    bacula_ctx *bctx;
1912
1913    Dsm_check(999);
1914    if (!is_ctx_good(ctx, jcr, bctx)) {
1915       return bRC_Error;
1916    }
1917    if (!file) {
1918       return bRC_Error;
1919    }
1920
1921    /* Save the include context */
1922    old = get_incexe(jcr);
1923
1924    /* Not right time to add include */
1925    if (!old) {
1926       return bRC_Error;
1927    }
1928    if (!bctx->include) {
1929       bctx->include = old;
1930    }
1931
1932    set_incexe(jcr, bctx->include);
1933    add_file_to_fileset(jcr, file, true);
1934
1935    /* Restore the current context */
1936    set_incexe(jcr, old);
1937
1938    Dmsg1(100, "Add include file=%s\n", file);
1939    Dsm_check(999);
1940    return bRC_OK;
1941 }
1942
1943 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1944 {
1945    JCR *jcr;
1946    bacula_ctx *bctx;
1947    Dsm_check(999);
1948    if (!is_ctx_good(ctx, jcr, bctx)) {
1949       return bRC_Error;
1950    }
1951    if (!opts) {
1952       return bRC_Error;
1953    }
1954    add_options_to_fileset(jcr, opts);
1955    Dsm_check(999);
1956    Dmsg1(1000, "Add options=%s\n", opts);
1957    return bRC_OK;
1958 }
1959
1960 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1961 {
1962    JCR *jcr;
1963    bacula_ctx *bctx;
1964    Dsm_check(999);
1965    if (!is_ctx_good(ctx, jcr, bctx)) {
1966       return bRC_Error;
1967    }
1968    if (!item) {
1969       return bRC_Error;
1970    }
1971    add_regex_to_fileset(jcr, item, type);
1972    Dmsg1(100, "Add regex=%s\n", item);
1973    Dsm_check(999);
1974    return bRC_OK;
1975 }
1976
1977 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1978 {
1979    JCR *jcr;
1980    bacula_ctx *bctx;
1981    Dsm_check(999);
1982    if (!is_ctx_good(ctx, jcr, bctx)) {
1983       return bRC_Error;
1984    }
1985    if (!item) {
1986       return bRC_Error;
1987    }
1988    add_wild_to_fileset(jcr, item, type);
1989    Dmsg1(100, "Add wild=%s\n", item);
1990    Dsm_check(999);
1991    return bRC_OK;
1992 }
1993
1994 static bRC baculaNewOptions(bpContext *ctx)
1995 {
1996    JCR *jcr;
1997    bacula_ctx *bctx;
1998    Dsm_check(999);
1999    if (!is_ctx_good(ctx, jcr, bctx)) {
2000       return bRC_Error;
2001    }
2002    (void)new_options(jcr, NULL);
2003    Dsm_check(999);
2004    return bRC_OK;
2005 }
2006
2007 static bRC baculaNewInclude(bpContext *ctx)
2008 {
2009    JCR *jcr;
2010    bacula_ctx *bctx;
2011    Dsm_check(999);
2012    if (!is_ctx_good(ctx, jcr, bctx)) {
2013       return bRC_Error;
2014    }
2015    (void)new_include(jcr);
2016    Dsm_check(999);
2017    return bRC_OK;
2018 }
2019
2020 static bRC baculaNewPreInclude(bpContext *ctx)
2021 {
2022    JCR *jcr;
2023    bacula_ctx *bctx;
2024    Dsm_check(999);
2025    if (!is_ctx_good(ctx, jcr, bctx)) {
2026       return bRC_Error;
2027    }
2028
2029    bctx->include = new_preinclude(jcr);
2030    new_options(jcr, bctx->include);
2031    set_incexe(jcr, bctx->include);
2032
2033    Dsm_check(999);
2034    return bRC_OK;
2035 }
2036
2037 /*
2038  * Check if a file have to be backuped using Accurate code
2039  */
2040 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
2041 {
2042    JCR *jcr;
2043    bacula_ctx *bctx;
2044    FF_PKT *ff_pkt;
2045    bRC ret = bRC_Error;
2046
2047    Dsm_check(999);
2048    if (!is_ctx_good(ctx, jcr, bctx)) {
2049       goto bail_out;
2050    }
2051    if (!sp) {
2052       goto bail_out;
2053    }
2054
2055    ff_pkt = jcr->ff;
2056    /*
2057     * Copy fname and link because save_file() zaps them.  This
2058     *  avoids zaping the plugin's strings.
2059     */
2060    ff_pkt->type = sp->type;
2061    if (!sp->fname) {
2062       Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n"));
2063       goto bail_out;
2064    }
2065
2066    ff_pkt->fname = sp->fname;
2067    ff_pkt->link = sp->link;
2068    memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp));
2069
2070    if (check_changes(jcr, ff_pkt))  {
2071       ret = bRC_OK;
2072    } else {
2073       ret = bRC_Seen;
2074    }
2075
2076    /* check_changes() can update delta sequence number, return it to the
2077     * plugin
2078     */
2079    sp->delta_seq = ff_pkt->delta_seq;
2080    sp->accurate_found = ff_pkt->accurate_found;
2081
2082 bail_out:
2083    Dsm_check(999);
2084    Dmsg1(100, "checkChanges=%i\n", ret);
2085    return ret;
2086 }
2087
2088 /*
2089  * Check if a file would be saved using current Include/Exclude code
2090  */
2091 static bRC baculaAcceptFile(bpContext *ctx, struct save_pkt *sp)
2092 {
2093    JCR *jcr;
2094    FF_PKT *ff_pkt;
2095    bacula_ctx *bctx;
2096
2097    char *old;
2098    struct stat oldstat;
2099    bRC ret = bRC_Error;
2100
2101    Dsm_check(999);
2102    if (!is_ctx_good(ctx, jcr, bctx)) {
2103       goto bail_out;
2104    }
2105    if (!sp) {
2106       goto bail_out;
2107    }
2108
2109    ff_pkt = jcr->ff;
2110
2111    /* Probably not needed, but keep a copy */
2112    old = ff_pkt->fname;
2113    oldstat = ff_pkt->statp;
2114
2115    ff_pkt->fname = sp->fname;
2116    ff_pkt->statp = sp->statp;
2117
2118    if (accept_file(ff_pkt)) {
2119       ret = bRC_OK;
2120    } else {
2121       ret = bRC_Skip;
2122    }
2123
2124    ff_pkt->fname = old;
2125    ff_pkt->statp = oldstat;
2126
2127 bail_out:
2128    return ret;
2129 }
2130
2131 #ifdef TEST_PROGRAM
2132
2133 int     (*plugin_bopen)(JCR *jcr, const char *fname, uint64_t flags, mode_t mode) = NULL;
2134 int     (*plugin_bclose)(JCR *jcr) = NULL;
2135 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
2136 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
2137 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
2138
2139 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
2140 {
2141    return 0;
2142 }
2143
2144 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
2145 {
2146    return true;
2147 }
2148
2149 int main(int argc, char *argv[])
2150 {
2151    char plugin_dir[1000];
2152    JCR mjcr1, mjcr2;
2153    JCR *jcr1 = &mjcr1;
2154    JCR *jcr2 = &mjcr2;
2155
2156    strcpy(my_name, "test-fd");
2157
2158    getcwd(plugin_dir, sizeof(plugin_dir)-1);
2159    load_fd_plugins(plugin_dir);
2160
2161    jcr1->JobId = 111;
2162    new_plugins(jcr1);
2163
2164    jcr2->JobId = 222;
2165    new_plugins(jcr2);
2166
2167    generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
2168    generate_plugin_event(jcr1, bEventJobEnd);
2169    generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
2170    free_plugins(jcr1);
2171    generate_plugin_event(jcr2, bEventJobEnd);
2172    free_plugins(jcr2);
2173
2174    unload_plugins();
2175
2176    Dmsg0(dbglvl, "bacula: OK ...\n");
2177    close_memory_pool();
2178    sm_dump(false);     /* unit test */
2179    return 0;
2180 }
2181
2182 #endif /* TEST_PROGRAM */