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