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