]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/fd_plugins.c
Use new FO_xxx bits in backup/restore and plugins
[bacula/bacula] / bacula / src / filed / fd_plugins.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2010 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation, which is 
11    listed in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /**
29  * Main program to test loading and running Bacula plugins.
30  *   Destined to become Bacula pluginloader, ...
31  *
32  * Kern Sibbald, October 2007
33  */
34 #include "bacula.h"
35 #include "filed.h"
36
37 extern CLIENT *me;
38 extern DLL_IMP_EXP char *exepath;
39
40 const int dbglvl = 150;
41 #ifdef HAVE_WIN32
42 const char *plugin_type = "-fd.dll";
43 #else
44 const char *plugin_type = "-fd.so";
45 #endif
46
47 extern int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
48 extern bool check_changes(JCR *jcr, FF_PKT *ff_pkt);
49
50 /* Function pointers to be set here */
51 extern DLL_IMP_EXP int     (*plugin_bopen)(BFILE *bfd, const char *fname, int flags, mode_t mode);
52 extern DLL_IMP_EXP int     (*plugin_bclose)(BFILE *bfd);
53 extern DLL_IMP_EXP ssize_t (*plugin_bread)(BFILE *bfd, void *buf, size_t count);
54 extern DLL_IMP_EXP ssize_t (*plugin_bwrite)(BFILE *bfd, void *buf, size_t count);
55 extern DLL_IMP_EXP boffset_t (*plugin_blseek)(BFILE *bfd, boffset_t offset, int whence);
56
57
58 /* Forward referenced functions */
59 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value);
60 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value);
61 static bRC baculaRegisterEvents(bpContext *ctx, ...);
62 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
63   int type, utime_t mtime, const char *fmt, ...);
64 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
65   int level, const char *fmt, ...);
66 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
67               size_t size);
68 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem);
69 static bRC  baculaAddExclude(bpContext *ctx, const char *file);
70 static bRC baculaAddInclude(bpContext *ctx, const char *file);
71 static bRC baculaAddOptions(bpContext *ctx, const char *opts);
72 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type);
73 static bRC baculaAddWild(bpContext *ctx, const char *item, int type);
74 static bRC baculaNewOptions(bpContext *ctx);
75 static bRC baculaNewInclude(bpContext *ctx);
76 static bool is_plugin_compatible(Plugin *plugin);
77 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret);
78 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp);
79
80 /*
81  * These will be plugged into the global pointer structure for
82  *  the findlib.
83  */
84 static int     my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
85 static int     my_plugin_bclose(BFILE *bfd);
86 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
87 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
88 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
89
90 #define for_this_plug(plugin, str, len) (((len) == (plugin)->file_len) && strncmp((plugin)->file, str, len) == 0)
91
92 /* Bacula info */
93 static bInfo binfo = {
94    sizeof(bInfo),
95    FD_PLUGIN_INTERFACE_VERSION 
96 };
97
98 /* Bacula entry points */
99 static bFuncs bfuncs = {
100    sizeof(bFuncs),
101    FD_PLUGIN_INTERFACE_VERSION,
102    baculaRegisterEvents,
103    baculaGetValue,
104    baculaSetValue,
105    baculaJobMsg,
106    baculaDebugMsg,
107    baculaMalloc,
108    baculaFree,
109    baculaAddExclude,
110    baculaAddInclude,
111    baculaAddOptions,
112    baculaAddRegex,
113    baculaAddWild,
114    baculaNewOptions,
115    baculaNewInclude,
116    baculaCheckChanges
117 };
118
119 /* 
120  * Bacula private context
121  */
122 struct bacula_ctx {
123    JCR *jcr;                             /* jcr for plugin */
124    bRC  rc;                              /* last return code */
125    bool disabled;                        /* set if plugin disabled */
126    findINCEXE *exclude;                  /* pointer to exclude files */
127    findINCEXE *include;                  /* pointer to include/exclude files */
128 };
129
130 static bool is_plugin_disabled(bpContext *plugin_ctx)
131 {
132    bacula_ctx *b_ctx;
133    if (!plugin_ctx) {
134       return true;
135    }
136    b_ctx = (bacula_ctx *)plugin_ctx->bContext;
137    return b_ctx->disabled;
138 }
139
140 static bool is_plugin_disabled(JCR *jcr)
141 {
142    return is_plugin_disabled(jcr->plugin_ctx);
143 }
144
145 /**
146  * Create a plugin event 
147  * When receiving bEventCancelCommand, this function is called by an other thread. 
148  */
149 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)     
150 {
151    bpContext *plugin_ctx;
152    bEvent event;
153    Plugin *plugin;
154    int i = 0;
155    char *name = NULL;
156    int len = 0;
157    bool call_if_canceled = false;
158    bRC rc;
159
160    if (!plugin_list || !jcr || !jcr->plugin_ctx_list) {
161       return;                         /* Return if no plugins loaded */
162    }
163    
164    /*
165     * Some events are sent to only a particular plugin or must be
166     *  called even if the job is canceled
167     */
168    switch(eventType) {
169    case bEventPluginCommand:
170       name = (char *)value;
171       if (!get_plugin_name(jcr, name, &len)) {
172          return;
173       }
174       break;
175    case bEventEndBackupJob:
176    case bEventEndVerifyJob:
177       call_if_canceled = true;
178       break;
179    case bEventEndRestoreJob:
180       call_if_canceled = true;
181       if (jcr->plugin && jcr->plugin->restoreFileStarted) {
182          plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
183          jcr->plugin->restoreFileStarted = false;
184       }
185       break;
186    default:
187       break;
188    }
189
190    if (!call_if_canceled && jcr->is_job_canceled()) {
191       return;
192    }
193
194    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
195    event.eventType = eventType;
196
197    Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
198
199    /* Pass event to every plugin (except if name is set) */
200    foreach_alist(plugin, plugin_list) {
201       if (name && !for_this_plug(plugin, name, len)) {
202          i++;
203          continue;
204       }
205       plugin_ctx = &plugin_ctx_list[i++];
206       if (is_plugin_disabled(plugin_ctx)) {
207          continue;
208       }
209       rc = plug_func(plugin)->handlePluginEvent(plugin_ctx, &event, value);
210       if (rc != bRC_OK) {
211          break;
212       }
213    }
214    return;
215 }
216
217 /**
218  * Check if file was seen for accurate
219  */
220 bool plugin_check_file(JCR *jcr, char *fname)
221 {
222    Plugin *plugin;
223    int rc = bRC_OK;
224    int i = 0;
225
226    if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
227       return false;                      /* Return if no plugins loaded */
228    }
229
230    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
231
232    Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
233
234    /* Pass event to every plugin */
235    foreach_alist(plugin, plugin_list) {
236       jcr->plugin_ctx = &plugin_ctx_list[i++];
237       jcr->plugin = plugin;
238       if (is_plugin_disabled(jcr)) {
239          continue;
240       }
241       if (plug_func(plugin)->checkFile == NULL) {
242          continue;
243       }
244       rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
245       if (rc == bRC_Seen) {
246          break;
247       }
248    }
249
250    jcr->plugin = NULL;
251    jcr->plugin_ctx = NULL;
252    return rc == bRC_Seen;
253 }
254
255 /* Get the first part of the the plugin command
256  *  systemstate:/@SYSTEMSTATE/ 
257  * => ret = 11
258  * => can use for_this_plug(plug, cmd, ret);
259  *
260  * The plugin command can contain only the plugin name
261  *  Plugin = alldrives
262  * => ret = 9
263  */
264 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret)
265 {
266    char *p;
267    int len;
268    if (!cmd || (*cmd == '\0')) {
269       return false;
270    }
271    /* Handle plugin command here backup */
272    Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
273    if ((p = strchr(cmd, ':')) == NULL) {
274       if (strchr(cmd, ' ') == NULL) { /* we have just the plugin name */
275          len = strlen(cmd);
276       } else {
277          Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
278          return false;
279       }
280    } else {                     /* plugin:argument */
281       len = p - cmd;
282       if (len <= 0) {
283          return false;
284       }
285    }
286    *ret = len;
287    return true;
288 }
289
290 /**  
291  * Sequence of calls for a backup:
292  * 1. plugin_save() here is called with ff_pkt
293  * 2. we find the plugin requested on the command string
294  * 3. we generate a bEventBackupCommand event to the specified plugin
295  *    and pass it the command string.
296  * 4. we make a startPluginBackup call to the plugin, which gives
297  *    us the data we need in save_pkt
298  * 5. we call Bacula's save_file() subroutine to save the specified
299  *    file.  The plugin will be called at pluginIO() to supply the
300  *    file data.
301  *
302  * Sequence of calls for restore:
303  *   See subroutine plugin_name_stream() below.
304  */
305 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
306 {
307    Plugin *plugin;
308    int i = 0;
309    int len;
310    char *cmd = ff_pkt->top_fname;
311    struct save_pkt sp;
312    bEvent event;
313    POOL_MEM fname(PM_FNAME);
314    POOL_MEM link(PM_FNAME);
315
316    if (!plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
317       Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
318       return 1;                            /* Return if no plugins loaded */
319    }
320
321    jcr->cmd_plugin = true;
322    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
323    event.eventType = bEventBackupCommand;
324
325    if (!get_plugin_name(jcr, cmd, &len)) {
326       goto bail_out;
327    }
328
329    /* Note, we stop the loop on the first plugin that matches the name */
330    foreach_alist(plugin, plugin_list) {
331       Dmsg4(0, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
332       if (!for_this_plug(plugin, cmd, len)) {
333          i++;
334          continue;
335       }
336       /* 
337        * We put the current plugin pointer, and the plugin context
338        *  into the jcr, because during save_file(), the plugin
339        *  will be called many times and these values are needed.
340        */
341       jcr->plugin_ctx = &plugin_ctx_list[i];
342       jcr->plugin = plugin;
343       if (is_plugin_disabled(jcr)) {
344          goto bail_out;
345       }
346
347       Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
348       /* Send the backup command to the right plugin*/
349       if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
350          goto bail_out;
351       }
352       /* Loop getting filenames to backup then saving them */
353       while (!jcr->is_job_canceled()) { 
354          memset(&sp, 0, sizeof(sp));
355          sp.pkt_size = sizeof(sp);
356          sp.pkt_end = sizeof(sp);
357          sp.portable = true;
358          sp.flags = 0;
359          sp.cmd = cmd;
360          Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
361                 &sp);
362          /* Get the file save parameters. I.e. the stat pkt ... */
363          if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
364             goto bail_out;
365          }
366          if (sp.type == 0) {
367             Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
368                cmd);
369             goto bail_out;
370          }
371          jcr->plugin_sp = &sp;
372          ff_pkt = jcr->ff;
373          /*
374           * Copy fname and link because save_file() zaps them.  This 
375           *  avoids zaping the plugin's strings.
376           */
377          ff_pkt->type = sp.type;
378          if (sp.type == FT_RESTORE_FIRST) {
379             if (!sp.object_name) {
380                Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
381                   cmd);
382                goto bail_out;
383             }
384             ff_pkt->fname = cmd;                 /* full plugin string */
385             ff_pkt->object_name = sp.object_name;
386             ff_pkt->object_index = sp.index;     /* restore object index */
387             ff_pkt->object_compression = 0;      /* no compression for now */
388             ff_pkt->object = sp.object;
389             ff_pkt->object_len = sp.object_len;
390          } else {
391             if (!sp.fname) {
392                Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
393                   cmd);
394                goto bail_out;
395             }
396             pm_strcpy(fname, sp.fname);
397             pm_strcpy(link, sp.link);
398             ff_pkt->fname = fname.c_str();
399             ff_pkt->link = link.c_str();
400             ff_pkt->delta_seq = sp.delta_seq;
401             if (sp.flags & FO_DELTA) {
402                ff_pkt->flags |= FO_DELTA;
403                ff_pkt->delta_seq++;          /* make new delta sequence number */
404             } else {
405                ff_pkt->flags &= ~FO_DELTA;   /* clean delta sequence number */
406                ff_pkt->delta_seq = 0;
407             }
408             if (sp.flags & FO_OFFSETS) {
409                ff_pkt->flags |= FO_OFFSETS;
410             }
411             if (sp.flags & FO_PORTABLE_DATA) {
412                ff_pkt->flags |= FO_PORTABLE_DATA;
413             }
414             ff_pkt->flags |= FO_PLUGIN;       /* data from plugin */
415          }
416
417          memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
418          Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
419          if (sp.object) {
420             Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
421          }   
422          /* Call Bacula core code to backup the plugin's file */
423          save_file(jcr, ff_pkt, true);
424          bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
425          if (rc == bRC_More || rc == bRC_OK) {
426             accurate_mark_file_as_seen(jcr, fname.c_str());
427          }
428          if (rc == bRC_More) {
429             continue;
430          }
431          goto bail_out;
432       } /* end while loop */
433       goto bail_out;
434    } /* end loop over all plugins */
435    Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
436
437 bail_out:
438    jcr->cmd_plugin = false;
439    jcr->plugin = NULL;
440    jcr->plugin_ctx = NULL;
441    return 1;
442 }
443
444 /**
445  * Send plugin name start/end record to SD
446  */
447 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
448 {
449    int stat;
450    int index = jcr->JobFiles;
451    struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
452
453    if (!sp) {
454       Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
455       return false;
456    }
457    if (jcr->is_job_canceled()) {
458       return false;
459    }
460   
461    if (start) {
462       index++;                  /* JobFiles not incremented yet */
463    }
464    Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
465    /* Send stream header */
466    if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
467      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
468            sd->bstrerror());
469      return false;
470    }
471    Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
472
473    if (start) {
474       /* Send data -- not much */
475       stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
476    } else {
477       /* Send end of data */
478       stat = sd->fsend("0 0");
479    }
480    if (!stat) {
481       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
482             sd->bstrerror());
483          return false;
484    }
485    Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
486    sd->signal(BNET_EOD);            /* indicate end of plugin name data */
487    return true;
488 }
489
490 /**
491  * Plugin name stream found during restore.  The record passed in
492  *  argument name was generated in send_plugin_name() above.
493  *
494  * Returns: true  if start of stream
495  *          false if end of steam
496  */
497 bool plugin_name_stream(JCR *jcr, char *name)    
498 {
499    char *p = name;
500    char *cmd;
501    bool start, portable;
502    Plugin *plugin;
503    int len;
504    int i = 0;
505    bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
506
507    Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
508    skip_nonspaces(&p);             /* skip over jcr->JobFiles */
509    skip_spaces(&p);
510    start = *p == '1';
511    if (start) {
512       /* Start of plugin data */
513       skip_nonspaces(&p);          /* skip start/end flag */
514       skip_spaces(&p);
515       portable = *p == '1';
516       skip_nonspaces(&p);          /* skip portable flag */
517       skip_spaces(&p);
518       cmd = p;
519    } else {
520       /*
521        * End of plugin data, notify plugin, then clear flags   
522        */
523       Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
524       if (jcr->plugin && jcr->plugin->restoreFileStarted) {
525          plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
526          jcr->plugin->restoreFileStarted = false;
527       }
528       jcr->plugin_ctx = NULL;
529       jcr->plugin = NULL;
530       goto bail_out;
531    }
532    if (!plugin_ctx_list) {
533       goto bail_out;
534    }
535       
536    /*
537     * After this point, we are dealing with a restore start
538     */
539    if (!get_plugin_name(jcr, cmd, &len)) {
540       goto bail_out;
541    }
542
543    /*
544     * Search for correct plugin as specified on the command 
545     */
546    foreach_alist(plugin, plugin_list) {
547       bEvent event;
548       Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
549       if (!for_this_plug(plugin, cmd, len)) {
550          i++;
551          continue;
552       }
553       jcr->plugin_ctx = &plugin_ctx_list[i];
554       jcr->plugin = plugin;
555       if (is_plugin_disabled(jcr)) {
556          goto bail_out;
557       }
558       Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
559       event.eventType = bEventRestoreCommand;     
560       if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, 
561             &event, cmd) != bRC_OK) {
562          goto bail_out;
563       }
564       /* ***FIXME**** check error code */
565       if (plugin->restoreFileStarted) {
566          plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
567       }
568       plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
569       plugin->restoreFileStarted = true;
570       goto bail_out;
571    }
572    Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
573
574 bail_out:
575    return start;
576 }
577
578 /**
579  * Tell the plugin to create the file.  Return values are
580  *   This is called only during Restore
581  *
582  *  CF_ERROR    -- error
583  *  CF_SKIP     -- skip processing this file
584  *  CF_EXTRACT  -- extract the file (i.e.call i/o routines)
585  *  CF_CREATED  -- created, but no content to extract (typically directories)
586  *
587  */
588 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
589 {
590    bpContext *plugin_ctx = jcr->plugin_ctx;
591    Plugin *plugin = jcr->plugin;
592    struct restore_pkt rp;
593    int flags;
594    int rc;
595
596    if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
597       return CF_ERROR;
598    }
599
600    rp.pkt_size = sizeof(rp);
601    rp.pkt_end = sizeof(rp);
602    rp.stream = attr->stream;
603    rp.data_stream = attr->data_stream;
604    rp.type = attr->type;
605    rp.file_index = attr->file_index;
606    rp.LinkFI = attr->LinkFI;
607    rp.uid = attr->uid;
608    rp.statp = attr->statp;                /* structure assignment */
609    rp.attrEx = attr->attrEx;
610    rp.ofname = attr->ofname;
611    rp.olname = attr->olname;
612    rp.where = jcr->where;
613    rp.RegexWhere = jcr->RegexWhere;
614    rp.replace = jcr->replace;
615    rp.create_status = CF_ERROR;
616    Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n", 
617          rp.stream, rp.type, rp.LinkFI, rp.ofname);
618    if (rp.attrEx) {
619       Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
620    }
621    rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
622    if (rc != bRC_OK) {
623       Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
624             rc, attr->ofname);
625       return CF_ERROR;
626    }
627    if (rp.create_status == CF_SKIP) {
628       return CF_SKIP;
629    }
630    if (rp.create_status == CF_ERROR) {
631       Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
632             attr->ofname);
633       return CF_ERROR;
634    }
635    /* Created link or directory? */
636    if (rp.create_status == CF_CREATED) {
637       return rp.create_status;        /* yes, no need to bopen */
638    }
639
640    flags =  O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
641    Dmsg0(dbglvl, "call bopen\n");
642    int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
643    Dmsg1(50, "bopen status=%d\n", stat);
644    if (stat < 0) {
645       berrno be;
646       be.set_errno(bfd->berrno);
647       Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
648             attr->ofname, be.bstrerror());
649       Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
650       return CF_ERROR;
651    }
652
653    if (!is_bopen(bfd)) {
654       Dmsg0(000, "===== BFD is not open!!!!\n");
655    }
656    return CF_EXTRACT;
657 }
658
659 /**
660  * Reset the file attributes after all file I/O is done -- this allows
661  *  the previous access time/dates to be set properly, and it also allows
662  *  us to properly set directory permissions.
663  *  Not currently Implemented.
664  */
665 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
666 {
667    Dmsg0(dbglvl, "plugin_set_attributes\n");
668    if (is_bopen(ofd)) {
669       bclose(ofd);
670    }
671    pm_strcpy(attr->ofname, "*none*");
672    return true;
673 }
674
675 /*
676  * Print to file the plugin info.
677  */
678 void dump_fd_plugin(Plugin *plugin, FILE *fp)
679 {
680    if (!plugin) {
681       return ;
682    }
683    pInfo *info = (pInfo *)plugin->pinfo;
684    fprintf(fp, "\tversion=%d\n", info->version);
685    fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
686    fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
687    fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
688    fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
689    fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
690    fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
691 }
692
693 /**
694  * This entry point is called internally by Bacula to ensure
695  *  that the plugin IO calls come into this code.
696  */
697 void load_fd_plugins(const char *plugin_dir)
698 {
699    Plugin *plugin;
700
701    if (!plugin_dir) {
702       Dmsg0(dbglvl, "plugin dir is NULL\n");
703       return;
704    }
705
706    plugin_list = New(alist(10, not_owned_by_alist));
707    if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
708                      is_plugin_compatible)) {
709       /* Either none found, or some error */
710       if (plugin_list->size() == 0) {
711          delete plugin_list;
712          plugin_list = NULL;
713          Dmsg0(dbglvl, "No plugins loaded\n");
714          return;
715       }
716    }
717
718    /* Plug entry points called from findlib */
719    plugin_bopen  = my_plugin_bopen;
720    plugin_bclose = my_plugin_bclose;
721    plugin_bread  = my_plugin_bread;
722    plugin_bwrite = my_plugin_bwrite;
723    plugin_blseek = my_plugin_blseek;
724
725    /* 
726     * Verify that the plugin is acceptable, and print information
727     *  about it.
728     */
729    foreach_alist(plugin, plugin_list) {
730       Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
731       Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
732    }
733
734    dbg_plugin_add_hook(dump_fd_plugin);
735 }
736
737 /**
738  * Check if a plugin is compatible.  Called by the load_plugin function
739  *  to allow us to verify the plugin.
740  */
741 static bool is_plugin_compatible(Plugin *plugin)
742 {
743    pInfo *info = (pInfo *)plugin->pinfo;
744    Dmsg0(50, "is_plugin_compatible called\n");
745    if (debug_level >= 50) {
746       dump_fd_plugin(plugin, stdin);
747    }
748    if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
749       Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
750            plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
751       Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
752            plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
753
754       return false;
755    }
756    if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
757       Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
758            plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
759       Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
760            plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
761       return false;
762    }
763    if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
764        strcmp(info->plugin_license, "AGPLv3") != 0) {
765       Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
766            plugin->file, info->plugin_license);
767       Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
768            plugin->file, info->plugin_license);
769       return false;
770    }
771       
772    return true;
773 }
774
775
776 /**
777  * Create a new instance of each plugin for this Job
778  *   Note, plugin_list can exist but jcr->plugin_ctx_list can
779  *   be NULL if no plugins were loaded.
780  */
781 void new_plugins(JCR *jcr)
782 {
783    Plugin *plugin;
784    int i = 0;
785
786    if (!plugin_list) {
787       Dmsg0(dbglvl, "plugin list is NULL\n");
788       return;
789    }
790    if (jcr->is_job_canceled() || jcr->JobId == 0) {
791       return;
792    }
793
794    int num = plugin_list->size();
795
796    if (num == 0) {
797       Dmsg0(dbglvl, "No plugins loaded\n");
798       return;
799    }
800
801    jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
802
803    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
804    Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
805    foreach_alist(plugin, plugin_list) {
806       /* Start a new instance of each plugin */
807       bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
808       memset(b_ctx, 0, sizeof(bacula_ctx));
809       b_ctx->jcr = jcr;
810       plugin_ctx_list[i].bContext = (void *)b_ctx;   /* Bacula private context */
811       plugin_ctx_list[i].pContext = NULL;
812       if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
813          b_ctx->disabled = true;
814       }
815    }
816 }
817
818 /**
819  * Free the plugin instances for this Job
820  */
821 void free_plugins(JCR *jcr)
822 {
823    Plugin *plugin;
824    int i = 0;
825
826    if (!plugin_list || !jcr->plugin_ctx_list) {
827       return;                         /* no plugins, nothing to do */
828    }
829
830    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
831    Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
832    foreach_alist(plugin, plugin_list) {   
833       /* Free the plugin instance */
834       plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
835       free(plugin_ctx_list[i++].bContext);     /* free Bacula private context */
836    }
837    free(plugin_ctx_list);
838    jcr->plugin_ctx_list = NULL;
839 }
840
841 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
842 {
843    JCR *jcr = bfd->jcr;
844    Plugin *plugin = (Plugin *)jcr->plugin;
845    struct io_pkt io;
846
847    Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
848    if (!plugin || !jcr->plugin_ctx) {
849       return 0;
850    }
851    io.pkt_size = sizeof(io);
852    io.pkt_end = sizeof(io);
853    io.func = IO_OPEN;
854    io.count = 0;
855    io.buf = NULL;
856    io.fname = fname;
857    io.flags = flags;
858    io.mode = mode;
859    io.win32 = false;
860    io.lerror = 0;
861    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
862    bfd->berrno = io.io_errno;
863    if (io.win32) {
864       errno = b_errno_win32;
865    } else {
866       errno = io.io_errno;
867       bfd->lerror = io.lerror;
868    }
869    Dmsg1(50, "Return from plugin open status=%d\n", io.status);
870    return io.status;
871 }
872
873 static int my_plugin_bclose(BFILE *bfd)
874 {
875    JCR *jcr = bfd->jcr;
876    Plugin *plugin = (Plugin *)jcr->plugin;
877    struct io_pkt io;
878
879    Dmsg0(dbglvl, "===== plugin_bclose\n");
880    if (!plugin || !jcr->plugin_ctx) {
881       return 0;
882    }
883    io.pkt_size = sizeof(io);
884    io.pkt_end = sizeof(io);
885    io.func = IO_CLOSE;
886    io.count = 0;
887    io.buf = NULL;
888    io.win32 = false;
889    io.lerror = 0;
890    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
891    bfd->berrno = io.io_errno;
892    if (io.win32) {
893       errno = b_errno_win32;
894    } else {
895       errno = io.io_errno;
896       bfd->lerror = io.lerror;
897    }
898    Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
899    return io.status;
900 }
901
902 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
903 {
904    JCR *jcr = bfd->jcr;
905    Plugin *plugin = (Plugin *)jcr->plugin;
906    struct io_pkt io;
907
908    Dmsg0(dbglvl, "plugin_bread\n");
909    if (!plugin || !jcr->plugin_ctx) {
910       return 0;
911    }
912    io.pkt_size = sizeof(io);
913    io.pkt_end = sizeof(io);
914    io.func = IO_READ;
915    io.count = count;
916    io.buf = (char *)buf;
917    io.win32 = false;
918    io.offset = 0;
919    io.lerror = 0;
920    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
921    bfd->offset = io.offset;
922    bfd->berrno = io.io_errno;
923    if (io.win32) {
924       errno = b_errno_win32;
925    } else {
926       errno = io.io_errno;
927       bfd->lerror = io.lerror;
928    }
929    return (ssize_t)io.status;
930 }
931
932 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
933 {
934    JCR *jcr = bfd->jcr;
935    Plugin *plugin = (Plugin *)jcr->plugin;
936    struct io_pkt io;
937
938    Dmsg0(dbglvl, "plugin_bwrite\n");
939    if (!plugin || !jcr->plugin_ctx) {
940       return 0;
941    }
942    io.pkt_size = sizeof(io);
943    io.pkt_end = sizeof(io);
944    io.func = IO_WRITE;
945    io.count = count;
946    io.buf = (char *)buf;
947    io.win32 = false;
948    io.lerror = 0;
949    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
950    bfd->berrno = io.io_errno;
951    if (io.win32) {
952       errno = b_errno_win32;
953    } else {
954       errno = io.io_errno;
955       bfd->lerror = io.lerror;
956    }
957    return (ssize_t)io.status;
958 }
959
960 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
961 {
962    JCR *jcr = bfd->jcr;
963    Plugin *plugin = (Plugin *)jcr->plugin;
964    struct io_pkt io;
965
966    Dmsg0(dbglvl, "plugin_bseek\n");
967    if (!plugin || !jcr->plugin_ctx) {
968       return 0;
969    }
970    io.pkt_size = sizeof(io);
971    io.pkt_end = sizeof(io);
972    io.func = IO_SEEK;
973    io.offset = offset;
974    io.whence = whence;
975    io.win32 = false;
976    io.lerror = 0;
977    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
978    bfd->berrno = io.io_errno;
979    if (io.win32) {
980       errno = b_errno_win32;
981    } else {
982       errno = io.io_errno;
983       bfd->lerror = io.lerror;
984    }
985    return (boffset_t)io.offset;
986 }
987
988 /* ==============================================================
989  *
990  * Callbacks from the plugin
991  *
992  * ==============================================================
993  */
994 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
995 {
996    JCR *jcr;
997    if (!value) {
998       return bRC_Error;
999    }
1000
1001    switch (var) {               /* General variables, no need of ctx */
1002    case bVarFDName:
1003       *((char **)value) = my_name;
1004       break;
1005    case bVarWorkingDir:
1006       *(void **)value = me->working_directory;
1007       break;
1008    case bVarExePath:
1009       *(char **)value = exepath;
1010       break;
1011    default:
1012       break;
1013    }
1014
1015    if (!ctx) {                  /* Other variables need context */
1016       return bRC_Error;
1017    }
1018
1019    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1020    if (!jcr) {
1021       return bRC_Error;
1022    }
1023
1024    switch (var) {
1025    case bVarJobId:
1026       *((int *)value) = jcr->JobId;
1027       Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
1028       break;
1029    case bVarLevel:
1030       *((int *)value) = jcr->getJobLevel();
1031       Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
1032       break;
1033    case bVarType:
1034       *((int *)value) = jcr->getJobType();
1035       Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
1036       break;
1037    case bVarClient:
1038       *((char **)value) = jcr->client_name;
1039       Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
1040       break;
1041    case bVarJobName:
1042       *((char **)value) = jcr->Job;
1043       Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
1044       break;
1045    case bVarJobStatus:
1046       *((int *)value) = jcr->JobStatus;
1047       Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
1048       break;
1049    case bVarSinceTime:
1050       *((int *)value) = (int)jcr->mtime;
1051       Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
1052       break;
1053    case bVarAccurate:
1054       *((int *)value) = (int)jcr->accurate;
1055       Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1056       break;
1057    case bVarFileSeen:
1058       break;                 /* a write only variable, ignore read request */
1059    case bVarVssObject:
1060 #ifdef HAVE_WIN32
1061       if (g_pVSSClient) {
1062          *(void **)value = g_pVSSClient->GetVssObject();
1063          break;
1064        }
1065 #endif
1066        return bRC_Error;
1067    case bVarVssDllHandle:
1068 #ifdef HAVE_WIN32
1069       if (g_pVSSClient) {
1070          *(void **)value = g_pVSSClient->GetVssDllHandle();
1071          break;
1072        }
1073 #endif
1074        return bRC_Error;
1075    case bVarWhere:
1076       *(char **)value = jcr->where;
1077       break;
1078    case bVarRegexWhere:
1079       *(char **)value = jcr->RegexWhere;
1080       break;
1081
1082    case bVarFDName:             /* get warning with g++ if we missed one */
1083    case bVarWorkingDir:
1084    case bVarExePath:
1085       break;
1086    }
1087    return bRC_OK;
1088 }
1089
1090 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1091 {
1092    JCR *jcr;
1093    if (!value || !ctx) {
1094       return bRC_Error;
1095    }
1096 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1097    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1098    if (!jcr) {
1099       return bRC_Error;
1100    }
1101 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
1102    switch (var) {
1103    case bVarFileSeen:
1104       if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1105          return bRC_Error;
1106       } 
1107       break;
1108    default:
1109       break;
1110    }
1111    return bRC_OK;
1112 }
1113
1114 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1115 {
1116    va_list args;
1117    uint32_t event;
1118
1119    if (!ctx) {
1120       return bRC_Error;
1121    }
1122
1123    va_start(args, ctx);
1124    while ((event = va_arg(args, uint32_t))) {
1125       Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1126    }
1127    va_end(args);
1128    return bRC_OK;
1129 }
1130
1131 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1132   int type, utime_t mtime, const char *fmt, ...)
1133 {
1134    va_list arg_ptr;
1135    char buf[2000];
1136    JCR *jcr;
1137
1138    if (ctx) {
1139       jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1140    } else {
1141       jcr = NULL;
1142    }
1143
1144    va_start(arg_ptr, fmt);
1145    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1146    va_end(arg_ptr);
1147    Jmsg(jcr, type, mtime, "%s", buf);
1148    return bRC_OK;
1149 }
1150
1151 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1152   int level, const char *fmt, ...)
1153 {
1154    va_list arg_ptr;
1155    char buf[2000];
1156
1157    va_start(arg_ptr, fmt);
1158    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1159    va_end(arg_ptr);
1160    d_msg(file, line, level, "%s", buf);
1161    return bRC_OK;
1162 }
1163
1164 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1165               size_t size)
1166 {
1167 #ifdef SMARTALLOC
1168    return sm_malloc(file, line, size);
1169 #else
1170    return malloc(size);
1171 #endif
1172 }
1173
1174 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1175 {
1176 #ifdef SMARTALLOC
1177    sm_free(file, line, mem);
1178 #else
1179    free(mem);
1180 #endif
1181 }
1182
1183 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1184 {
1185    if (!ctx) {
1186       return false;
1187    }
1188    bctx = (bacula_ctx *)ctx->bContext;
1189    if (!bctx) {
1190       return false;
1191    }
1192    jcr = bctx->jcr;
1193    if (!jcr) {
1194       return false;
1195    }
1196    return true;
1197 }
1198
1199 /**
1200  * Let the plugin define files/directories to be excluded
1201  *  from the main backup.
1202  */
1203 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1204 {
1205    JCR *jcr;
1206    bacula_ctx *bctx;
1207    if (!is_ctx_good(ctx, jcr, bctx)) {
1208       return bRC_Error;
1209    }
1210    if (!file) {
1211       return bRC_Error;
1212    }
1213    if (!bctx->exclude) {  
1214       bctx->exclude = new_exclude(jcr);
1215       new_options(jcr, bctx->exclude);
1216    }
1217    set_incexe(jcr, bctx->exclude);
1218    add_file_to_fileset(jcr, file, true);
1219    Dmsg1(100, "Add exclude file=%s\n", file);
1220    return bRC_OK;
1221 }
1222
1223 /**
1224  * Let the plugin define files/directories to be excluded
1225  *  from the main backup.
1226  */
1227 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1228 {
1229    JCR *jcr;
1230    bacula_ctx *bctx;
1231    if (!is_ctx_good(ctx, jcr, bctx)) {
1232       return bRC_Error;
1233    }
1234    if (!file) {
1235       return bRC_Error;
1236    }
1237    if (!bctx->include) {  
1238       bctx->include = new_preinclude(jcr);
1239       new_options(jcr, bctx->include);
1240    }
1241    set_incexe(jcr, bctx->include);
1242    add_file_to_fileset(jcr, file, true);
1243    Dmsg1(100, "Add include file=%s\n", file);
1244    return bRC_OK;
1245 }
1246
1247 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1248 {
1249    JCR *jcr;
1250    bacula_ctx *bctx;
1251    if (!is_ctx_good(ctx, jcr, bctx)) {
1252       return bRC_Error;
1253    }
1254    if (!opts) {
1255       return bRC_Error;
1256    }
1257    add_options_to_fileset(jcr, opts);
1258    Dmsg1(1000, "Add options=%s\n", opts);
1259    return bRC_OK;
1260 }
1261
1262 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1263 {
1264    JCR *jcr;
1265    bacula_ctx *bctx;
1266    if (!is_ctx_good(ctx, jcr, bctx)) {
1267       return bRC_Error;
1268    }
1269    if (!item) {
1270       return bRC_Error;
1271    }
1272    add_regex_to_fileset(jcr, item, type);
1273    Dmsg1(100, "Add regex=%s\n", item);
1274    return bRC_OK;
1275 }
1276
1277 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1278 {
1279    JCR *jcr;
1280    bacula_ctx *bctx;
1281    if (!is_ctx_good(ctx, jcr, bctx)) {
1282       return bRC_Error;
1283    }
1284    if (!item) {
1285       return bRC_Error;
1286    }
1287    add_wild_to_fileset(jcr, item, type);
1288    Dmsg1(100, "Add wild=%s\n", item);
1289    return bRC_OK;
1290 }
1291
1292 static bRC baculaNewOptions(bpContext *ctx)
1293 {
1294    JCR *jcr;
1295    bacula_ctx *bctx;
1296    if (!is_ctx_good(ctx, jcr, bctx)) {
1297       return bRC_Error;
1298    }
1299    (void)new_options(jcr, NULL);
1300    return bRC_OK;
1301 }
1302
1303 static bRC baculaNewInclude(bpContext *ctx)
1304 {
1305    JCR *jcr;
1306    bacula_ctx *bctx;
1307    if (!is_ctx_good(ctx, jcr, bctx)) {
1308       return bRC_Error;
1309    }
1310    (void)new_include(jcr);
1311    return bRC_OK;
1312 }
1313
1314
1315 /* 
1316  * Check if a file have to be backuped using Accurate code
1317  */
1318 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
1319 {
1320    JCR *jcr;
1321    bacula_ctx *bctx;
1322    FF_PKT *ff_pkt;
1323    bRC ret = bRC_Error;
1324
1325    if (!is_ctx_good(ctx, jcr, bctx)) {
1326       goto bail_out;
1327    }
1328    if (!sp) {
1329       goto bail_out;
1330    }
1331    
1332    ff_pkt = jcr->ff;
1333    /*
1334     * Copy fname and link because save_file() zaps them.  This 
1335     *  avoids zaping the plugin's strings.
1336     */
1337    ff_pkt->type = sp->type;
1338    if (!sp->fname) {
1339       Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n"));
1340       goto bail_out;
1341    }
1342
1343    ff_pkt->fname = sp->fname;
1344    ff_pkt->link = sp->link;
1345    memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp));
1346
1347    if (check_changes(jcr, ff_pkt))  {
1348       ret = bRC_OK;
1349    } else {
1350       ret = bRC_Seen;
1351    }
1352
1353    /* check_changes() can update delta sequence number, return it to the
1354     * plugin 
1355     */
1356    sp->delta_seq = ff_pkt->delta_seq;
1357
1358 bail_out:
1359    Dmsg1(100, "checkChanges=%i\n", ret);
1360    return ret;
1361 }
1362
1363
1364 #ifdef TEST_PROGRAM
1365
1366 int     (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1367 int     (*plugin_bclose)(JCR *jcr) = NULL;
1368 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1369 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1370 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1371
1372 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1373 {
1374    return 0;
1375 }
1376
1377 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1378 {
1379    return true;
1380 }
1381
1382 int main(int argc, char *argv[])
1383 {
1384    char plugin_dir[1000];
1385    JCR mjcr1, mjcr2;
1386    JCR *jcr1 = &mjcr1;
1387    JCR *jcr2 = &mjcr2;
1388
1389    strcpy(my_name, "test-fd");
1390     
1391    getcwd(plugin_dir, sizeof(plugin_dir)-1);
1392    load_fd_plugins(plugin_dir);
1393
1394    jcr1->JobId = 111;
1395    new_plugins(jcr1);
1396
1397    jcr2->JobId = 222;
1398    new_plugins(jcr2);
1399
1400    generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1401    generate_plugin_event(jcr1, bEventJobEnd);
1402    generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1403    free_plugins(jcr1);
1404    generate_plugin_event(jcr2, bEventJobEnd);
1405    free_plugins(jcr2);
1406
1407    unload_plugins();
1408
1409    Dmsg0(dbglvl, "bacula: OK ...\n");
1410    close_memory_pool();
1411    sm_dump(false);     /* unit test */
1412    return 0;
1413 }
1414
1415 #endif /* TEST_PROGRAM */