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