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