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