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