]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/fd_plugins.c
Change JobFiles display from %f to %F in RunScript because %f was already affected...
[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    if (info->size != sizeof(pInfo)) {
793       Jmsg(NULL, M_ERROR, 0, _("Plugin size mismatch.\n"));
794       return false;
795    }
796    return true;
797 }
798
799
800 /**
801  * Create a new instance of each plugin for this Job
802  *   Note, plugin_list can exist but jcr->plugin_ctx_list can
803  *   be NULL if no plugins were loaded.
804  */
805 void new_plugins(JCR *jcr)
806 {
807    Plugin *plugin;
808    int i = 0;
809
810    if (!plugin_list) {
811       Dmsg0(dbglvl, "plugin list is NULL\n");
812       return;
813    }
814    if (jcr->is_job_canceled() || jcr->JobId == 0) {
815       return;
816    }
817
818    int num = plugin_list->size();
819
820    if (num == 0) {
821       Dmsg0(dbglvl, "No plugins loaded\n");
822       return;
823    }
824
825    jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
826
827    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
828    Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
829    foreach_alist(plugin, plugin_list) {
830       /* Start a new instance of each plugin */
831       bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
832       memset(b_ctx, 0, sizeof(bacula_ctx));
833       b_ctx->jcr = jcr;
834       plugin_ctx_list[i].bContext = (void *)b_ctx;   /* Bacula private context */
835       plugin_ctx_list[i].pContext = NULL;
836       if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
837          b_ctx->disabled = true;
838       }
839    }
840 }
841
842 /**
843  * Free the plugin instances for this Job
844  */
845 void free_plugins(JCR *jcr)
846 {
847    Plugin *plugin;
848    int i = 0;
849
850    if (!plugin_list || !jcr->plugin_ctx_list) {
851       return;                         /* no plugins, nothing to do */
852    }
853
854    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
855    Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
856    foreach_alist(plugin, plugin_list) {   
857       /* Free the plugin instance */
858       plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
859       free(plugin_ctx_list[i++].bContext);     /* free Bacula private context */
860    }
861    free(plugin_ctx_list);
862    jcr->plugin_ctx_list = NULL;
863 }
864
865 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
866 {
867    JCR *jcr = bfd->jcr;
868    Plugin *plugin = (Plugin *)jcr->plugin;
869    struct io_pkt io;
870
871    Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
872    if (!plugin || !jcr->plugin_ctx) {
873       return 0;
874    }
875    io.pkt_size = sizeof(io);
876    io.pkt_end = sizeof(io);
877    io.func = IO_OPEN;
878    io.count = 0;
879    io.buf = NULL;
880    io.fname = fname;
881    io.flags = flags;
882    io.mode = mode;
883    io.win32 = false;
884    io.lerror = 0;
885    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
886    bfd->berrno = io.io_errno;
887    if (io.win32) {
888       errno = b_errno_win32;
889    } else {
890       errno = io.io_errno;
891       bfd->lerror = io.lerror;
892    }
893    Dmsg1(50, "Return from plugin open status=%d\n", io.status);
894    return io.status;
895 }
896
897 static int my_plugin_bclose(BFILE *bfd)
898 {
899    JCR *jcr = bfd->jcr;
900    Plugin *plugin = (Plugin *)jcr->plugin;
901    struct io_pkt io;
902
903    Dmsg0(dbglvl, "===== plugin_bclose\n");
904    if (!plugin || !jcr->plugin_ctx) {
905       return 0;
906    }
907    io.pkt_size = sizeof(io);
908    io.pkt_end = sizeof(io);
909    io.func = IO_CLOSE;
910    io.count = 0;
911    io.buf = NULL;
912    io.win32 = false;
913    io.lerror = 0;
914    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
915    bfd->berrno = io.io_errno;
916    if (io.win32) {
917       errno = b_errno_win32;
918    } else {
919       errno = io.io_errno;
920       bfd->lerror = io.lerror;
921    }
922    Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
923    return io.status;
924 }
925
926 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
927 {
928    JCR *jcr = bfd->jcr;
929    Plugin *plugin = (Plugin *)jcr->plugin;
930    struct io_pkt io;
931
932    Dmsg0(dbglvl, "plugin_bread\n");
933    if (!plugin || !jcr->plugin_ctx) {
934       return 0;
935    }
936    io.pkt_size = sizeof(io);
937    io.pkt_end = sizeof(io);
938    io.func = IO_READ;
939    io.count = count;
940    io.buf = (char *)buf;
941    io.win32 = false;
942    io.offset = 0;
943    io.lerror = 0;
944    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
945    bfd->offset = io.offset;
946    bfd->berrno = io.io_errno;
947    if (io.win32) {
948       errno = b_errno_win32;
949    } else {
950       errno = io.io_errno;
951       bfd->lerror = io.lerror;
952    }
953    return (ssize_t)io.status;
954 }
955
956 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
957 {
958    JCR *jcr = bfd->jcr;
959    Plugin *plugin = (Plugin *)jcr->plugin;
960    struct io_pkt io;
961
962    Dmsg0(dbglvl, "plugin_bwrite\n");
963    if (!plugin || !jcr->plugin_ctx) {
964       return 0;
965    }
966    io.pkt_size = sizeof(io);
967    io.pkt_end = sizeof(io);
968    io.func = IO_WRITE;
969    io.count = count;
970    io.buf = (char *)buf;
971    io.win32 = false;
972    io.lerror = 0;
973    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
974    bfd->berrno = io.io_errno;
975    if (io.win32) {
976       errno = b_errno_win32;
977    } else {
978       errno = io.io_errno;
979       bfd->lerror = io.lerror;
980    }
981    return (ssize_t)io.status;
982 }
983
984 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
985 {
986    JCR *jcr = bfd->jcr;
987    Plugin *plugin = (Plugin *)jcr->plugin;
988    struct io_pkt io;
989
990    Dmsg0(dbglvl, "plugin_bseek\n");
991    if (!plugin || !jcr->plugin_ctx) {
992       return 0;
993    }
994    io.pkt_size = sizeof(io);
995    io.pkt_end = sizeof(io);
996    io.func = IO_SEEK;
997    io.offset = offset;
998    io.whence = whence;
999    io.win32 = false;
1000    io.lerror = 0;
1001    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1002    bfd->berrno = io.io_errno;
1003    if (io.win32) {
1004       errno = b_errno_win32;
1005    } else {
1006       errno = io.io_errno;
1007       bfd->lerror = io.lerror;
1008    }
1009    return (boffset_t)io.offset;
1010 }
1011
1012 /* ==============================================================
1013  *
1014  * Callbacks from the plugin
1015  *
1016  * ==============================================================
1017  */
1018 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
1019 {
1020    JCR *jcr;
1021    if (!value) {
1022       return bRC_Error;
1023    }
1024
1025    switch (var) {               /* General variables, no need of ctx */
1026    case bVarFDName:
1027       *((char **)value) = my_name;
1028       break;
1029    case bVarWorkingDir:
1030       *(void **)value = me->working_directory;
1031       break;
1032    case bVarExePath:
1033       *(char **)value = exepath;
1034       break;
1035    case bVarVersion:
1036       *(char **)value = version;
1037       break;
1038    case bVarDistName:
1039       *(char **)value = dist_name;
1040       break;
1041    case bVarBEEF:
1042       *((int *)value) = beef;
1043       break;
1044    default:
1045       break;
1046    }
1047
1048    if (!ctx) {                  /* Other variables need context */
1049       return bRC_Error;
1050    }
1051
1052    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1053    if (!jcr) {
1054       return bRC_Error;
1055    }
1056
1057    switch (var) {
1058    case bVarJobId:
1059       *((int *)value) = jcr->JobId;
1060       Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
1061       break;
1062    case bVarLevel:
1063       *((int *)value) = jcr->getJobLevel();
1064       Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
1065       break;
1066    case bVarType:
1067       *((int *)value) = jcr->getJobType();
1068       Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
1069       break;
1070    case bVarClient:
1071       *((char **)value) = jcr->client_name;
1072       Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
1073       break;
1074    case bVarJobName:
1075       *((char **)value) = jcr->Job;
1076       Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
1077       break;
1078    case bVarJobStatus:
1079       *((int *)value) = jcr->JobStatus;
1080       Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
1081       break;
1082    case bVarSinceTime:
1083       *((int *)value) = (int)jcr->mtime;
1084       Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
1085       break;
1086    case bVarAccurate:
1087       *((int *)value) = (int)jcr->accurate;
1088       Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1089       break;
1090    case bVarFileSeen:
1091       break;                 /* a write only variable, ignore read request */
1092    case bVarVssObject:
1093 #ifdef HAVE_WIN32
1094       if (g_pVSSClient) {
1095          *(void **)value = g_pVSSClient->GetVssObject();
1096          break;
1097        }
1098 #endif
1099        return bRC_Error;
1100    case bVarVssDllHandle:
1101 #ifdef HAVE_WIN32
1102       if (g_pVSSClient) {
1103          *(void **)value = g_pVSSClient->GetVssDllHandle();
1104          break;
1105        }
1106 #endif
1107        return bRC_Error;
1108    case bVarWhere:
1109       *(char **)value = jcr->where;
1110       break;
1111    case bVarRegexWhere:
1112       *(char **)value = jcr->RegexWhere;
1113       break;
1114
1115    case bVarFDName:             /* get warning with g++ if we missed one */
1116    case bVarWorkingDir:
1117    case bVarExePath:
1118    case bVarVersion:
1119    case bVarDistName:
1120    case bVarBEEF:
1121       break;
1122    }
1123    return bRC_OK;
1124 }
1125
1126 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1127 {
1128    JCR *jcr;
1129    if (!value || !ctx) {
1130       return bRC_Error;
1131    }
1132 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1133    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1134    if (!jcr) {
1135       return bRC_Error;
1136    }
1137 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
1138    switch (var) {
1139    case bVarFileSeen:
1140       if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1141          return bRC_Error;
1142       } 
1143       break;
1144    default:
1145       break;
1146    }
1147    return bRC_OK;
1148 }
1149
1150 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1151 {
1152    va_list args;
1153    uint32_t event;
1154
1155    if (!ctx) {
1156       return bRC_Error;
1157    }
1158
1159    va_start(args, ctx);
1160    while ((event = va_arg(args, uint32_t))) {
1161       Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1162    }
1163    va_end(args);
1164    return bRC_OK;
1165 }
1166
1167 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1168   int type, utime_t mtime, const char *fmt, ...)
1169 {
1170    va_list arg_ptr;
1171    char buf[2000];
1172    JCR *jcr;
1173
1174    if (ctx) {
1175       jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1176    } else {
1177       jcr = NULL;
1178    }
1179
1180    va_start(arg_ptr, fmt);
1181    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1182    va_end(arg_ptr);
1183    Jmsg(jcr, type, mtime, "%s", buf);
1184    return bRC_OK;
1185 }
1186
1187 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1188   int level, const char *fmt, ...)
1189 {
1190    va_list arg_ptr;
1191    char buf[2000];
1192
1193    va_start(arg_ptr, fmt);
1194    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1195    va_end(arg_ptr);
1196    d_msg(file, line, level, "%s", buf);
1197    return bRC_OK;
1198 }
1199
1200 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1201               size_t size)
1202 {
1203 #ifdef SMARTALLOC
1204    return sm_malloc(file, line, size);
1205 #else
1206    return malloc(size);
1207 #endif
1208 }
1209
1210 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1211 {
1212 #ifdef SMARTALLOC
1213    sm_free(file, line, mem);
1214 #else
1215    free(mem);
1216 #endif
1217 }
1218
1219 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1220 {
1221    if (!ctx) {
1222       return false;
1223    }
1224    bctx = (bacula_ctx *)ctx->bContext;
1225    if (!bctx) {
1226       return false;
1227    }
1228    jcr = bctx->jcr;
1229    if (!jcr) {
1230       return false;
1231    }
1232    return true;
1233 }
1234
1235 /**
1236  * Let the plugin define files/directories to be excluded
1237  *  from the main backup.
1238  */
1239 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1240 {
1241    JCR *jcr;
1242    bacula_ctx *bctx;
1243    if (!is_ctx_good(ctx, jcr, bctx)) {
1244       return bRC_Error;
1245    }
1246    if (!file) {
1247       return bRC_Error;
1248    }
1249    if (!bctx->exclude) {  
1250       bctx->exclude = new_exclude(jcr);
1251       new_options(jcr, bctx->exclude);
1252    }
1253    set_incexe(jcr, bctx->exclude);
1254    add_file_to_fileset(jcr, file, true);
1255    Dmsg1(100, "Add exclude file=%s\n", file);
1256    return bRC_OK;
1257 }
1258
1259 /**
1260  * Let the plugin define files/directories to be excluded
1261  *  from the main backup.
1262  */
1263 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1264 {
1265    JCR *jcr;
1266    bacula_ctx *bctx;
1267    if (!is_ctx_good(ctx, jcr, bctx)) {
1268       return bRC_Error;
1269    }
1270    if (!file) {
1271       return bRC_Error;
1272    }
1273    if (!bctx->include) {  
1274       bctx->include = new_preinclude(jcr);
1275       new_options(jcr, bctx->include);
1276    }
1277    set_incexe(jcr, bctx->include);
1278    add_file_to_fileset(jcr, file, true);
1279    Dmsg1(100, "Add include file=%s\n", file);
1280    return bRC_OK;
1281 }
1282
1283 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1284 {
1285    JCR *jcr;
1286    bacula_ctx *bctx;
1287    if (!is_ctx_good(ctx, jcr, bctx)) {
1288       return bRC_Error;
1289    }
1290    if (!opts) {
1291       return bRC_Error;
1292    }
1293    add_options_to_fileset(jcr, opts);
1294    Dmsg1(1000, "Add options=%s\n", opts);
1295    return bRC_OK;
1296 }
1297
1298 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1299 {
1300    JCR *jcr;
1301    bacula_ctx *bctx;
1302    if (!is_ctx_good(ctx, jcr, bctx)) {
1303       return bRC_Error;
1304    }
1305    if (!item) {
1306       return bRC_Error;
1307    }
1308    add_regex_to_fileset(jcr, item, type);
1309    Dmsg1(100, "Add regex=%s\n", item);
1310    return bRC_OK;
1311 }
1312
1313 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1314 {
1315    JCR *jcr;
1316    bacula_ctx *bctx;
1317    if (!is_ctx_good(ctx, jcr, bctx)) {
1318       return bRC_Error;
1319    }
1320    if (!item) {
1321       return bRC_Error;
1322    }
1323    add_wild_to_fileset(jcr, item, type);
1324    Dmsg1(100, "Add wild=%s\n", item);
1325    return bRC_OK;
1326 }
1327
1328 static bRC baculaNewOptions(bpContext *ctx)
1329 {
1330    JCR *jcr;
1331    bacula_ctx *bctx;
1332    if (!is_ctx_good(ctx, jcr, bctx)) {
1333       return bRC_Error;
1334    }
1335    (void)new_options(jcr, NULL);
1336    return bRC_OK;
1337 }
1338
1339 static bRC baculaNewInclude(bpContext *ctx)
1340 {
1341    JCR *jcr;
1342    bacula_ctx *bctx;
1343    if (!is_ctx_good(ctx, jcr, bctx)) {
1344       return bRC_Error;
1345    }
1346    (void)new_include(jcr);
1347    return bRC_OK;
1348 }
1349
1350
1351 /* 
1352  * Check if a file have to be backuped using Accurate code
1353  */
1354 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
1355 {
1356    JCR *jcr;
1357    bacula_ctx *bctx;
1358    FF_PKT *ff_pkt;
1359    bRC ret = bRC_Error;
1360
1361    if (!is_ctx_good(ctx, jcr, bctx)) {
1362       goto bail_out;
1363    }
1364    if (!sp) {
1365       goto bail_out;
1366    }
1367    
1368    ff_pkt = jcr->ff;
1369    /*
1370     * Copy fname and link because save_file() zaps them.  This 
1371     *  avoids zaping the plugin's strings.
1372     */
1373    ff_pkt->type = sp->type;
1374    if (!sp->fname) {
1375       Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n"));
1376       goto bail_out;
1377    }
1378
1379    ff_pkt->fname = sp->fname;
1380    ff_pkt->link = sp->link;
1381    memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp));
1382
1383    if (check_changes(jcr, ff_pkt))  {
1384       ret = bRC_OK;
1385    } else {
1386       ret = bRC_Seen;
1387    }
1388
1389    /* check_changes() can update delta sequence number, return it to the
1390     * plugin 
1391     */
1392    sp->delta_seq = ff_pkt->delta_seq;
1393
1394 bail_out:
1395    Dmsg1(100, "checkChanges=%i\n", ret);
1396    return ret;
1397 }
1398
1399
1400 #ifdef TEST_PROGRAM
1401
1402 int     (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1403 int     (*plugin_bclose)(JCR *jcr) = NULL;
1404 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1405 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1406 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1407
1408 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1409 {
1410    return 0;
1411 }
1412
1413 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1414 {
1415    return true;
1416 }
1417
1418 int main(int argc, char *argv[])
1419 {
1420    char plugin_dir[1000];
1421    JCR mjcr1, mjcr2;
1422    JCR *jcr1 = &mjcr1;
1423    JCR *jcr2 = &mjcr2;
1424
1425    strcpy(my_name, "test-fd");
1426     
1427    getcwd(plugin_dir, sizeof(plugin_dir)-1);
1428    load_fd_plugins(plugin_dir);
1429
1430    jcr1->JobId = 111;
1431    new_plugins(jcr1);
1432
1433    jcr2->JobId = 222;
1434    new_plugins(jcr2);
1435
1436    generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1437    generate_plugin_event(jcr1, bEventJobEnd);
1438    generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1439    free_plugins(jcr1);
1440    generate_plugin_event(jcr2, bEventJobEnd);
1441    free_plugins(jcr2);
1442
1443    unload_plugins();
1444
1445    Dmsg0(dbglvl, "bacula: OK ...\n");
1446    close_memory_pool();
1447    sm_dump(false);     /* unit test */
1448    return 0;
1449 }
1450
1451 #endif /* TEST_PROGRAM */