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