]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/fd_plugins.c
Make a missing plugin during backup fatal
[bacula/bacula] / bacula / src / filed / fd_plugins.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2010 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU 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 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 const int dbglvl = 150;
38 #ifdef HAVE_WIN32
39 const char *plugin_type = "-fd.dll";
40 #else
41 const char *plugin_type = "-fd.so";
42 #endif
43
44 extern int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
45
46 /* Function pointers to be set here */
47 extern DLL_IMP_EXP int     (*plugin_bopen)(BFILE *bfd, const char *fname, int flags, mode_t mode);
48 extern DLL_IMP_EXP int     (*plugin_bclose)(BFILE *bfd);
49 extern DLL_IMP_EXP ssize_t (*plugin_bread)(BFILE *bfd, void *buf, size_t count);
50 extern DLL_IMP_EXP ssize_t (*plugin_bwrite)(BFILE *bfd, void *buf, size_t count);
51 extern DLL_IMP_EXP boffset_t (*plugin_blseek)(BFILE *bfd, boffset_t offset, int whence);
52
53
54 /* Forward referenced functions */
55 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value);
56 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value);
57 static bRC baculaRegisterEvents(bpContext *ctx, ...);
58 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
59   int type, utime_t mtime, const char *fmt, ...);
60 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
61   int level, const char *fmt, ...);
62 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
63               size_t size);
64 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem);
65 static bool is_plugin_compatible(Plugin *plugin);
66
67 /*
68  * These will be plugged into the global pointer structure for
69  *  the findlib.
70  */
71 static int     my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
72 static int     my_plugin_bclose(BFILE *bfd);
73 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
74 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
75 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
76
77
78 /* Bacula info */
79 static bInfo binfo = {
80    sizeof(bInfo),
81    FD_PLUGIN_INTERFACE_VERSION 
82 };
83
84 /* Bacula entry points */
85 static bFuncs bfuncs = {
86    sizeof(bFuncs),
87    FD_PLUGIN_INTERFACE_VERSION,
88    baculaRegisterEvents,
89    baculaGetValue,
90    baculaSetValue,
91    baculaJobMsg,
92    baculaDebugMsg,
93    baculaMalloc,
94    baculaFree
95 };
96
97 /* 
98  * Bacula private context
99  */
100 struct bacula_ctx {
101    JCR *jcr;                             /* jcr for plugin */
102    bRC  rc;                              /* last return code */
103    bool disabled;                        /* set if plugin disabled */
104 };
105
106 static bool is_plugin_disabled(JCR *jcr)
107 {
108    bacula_ctx *b_ctx;
109    if (!jcr->plugin_ctx) {
110       return true;
111    }
112    b_ctx = (bacula_ctx *)jcr->plugin_ctx->bContext;
113    return b_ctx->disabled;
114 }
115
116
117 /*
118  * Create a plugin event 
119  */
120 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)     
121 {
122    bEvent event;
123    Plugin *plugin;
124    int i = 0;
125
126    if (!plugin_list || !jcr || !jcr->plugin_ctx_list || job_canceled(jcr)) {
127       return;                         /* Return if no plugins loaded */
128    }
129
130    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
131    event.eventType = eventType;
132
133    Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
134
135    /* Pass event to every plugin */
136    foreach_alist(plugin, plugin_list) {
137       bRC rc;
138       jcr->plugin_ctx = &plugin_ctx_list[i++];
139       jcr->plugin = plugin;
140       if (is_plugin_disabled(jcr)) {
141          continue;
142       }
143       rc = plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, value);
144       if (rc != bRC_OK) {
145          break;
146       }
147    }
148
149    jcr->plugin = NULL;
150    jcr->plugin_ctx = NULL;
151    return;
152 }
153
154 /*
155  * Check if file was seen for accurate
156  */
157 bool plugin_check_file(JCR *jcr, char *fname)
158 {
159    Plugin *plugin;
160    int rc = bRC_OK;
161    int i = 0;
162
163    if (!plugin_list || !jcr || !jcr->plugin_ctx_list || job_canceled(jcr)) {
164       return false;                      /* Return if no plugins loaded */
165    }
166
167    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
168
169    Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
170
171    /* Pass event to every plugin */
172    foreach_alist(plugin, plugin_list) {
173       jcr->plugin_ctx = &plugin_ctx_list[i++];
174       jcr->plugin = plugin;
175       if (is_plugin_disabled(jcr)) {
176          continue;
177       }
178       if (plug_func(plugin)->checkFile == NULL) {
179          continue;
180       }
181       rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
182       if (rc == bRC_Seen) {
183          break;
184       }
185    }
186
187    jcr->plugin = NULL;
188    jcr->plugin_ctx = NULL;
189    return rc == bRC_Seen;
190 }
191
192
193 /*   
194  * Sequence of calls for a backup:
195  * 1. plugin_save() here is called with ff_pkt
196  * 2. we find the plugin requested on the command string
197  * 3. we generate a bEventBackupCommand event to the specified plugin
198  *    and pass it the command string.
199  * 4. we make a startPluginBackup call to the plugin, which gives
200  *    us the data we need in save_pkt
201  * 5. we call Bacula's save_file() subroutine to save the specified
202  *    file.  The plugin will be called at pluginIO() to supply the
203  *    file data.
204  *
205  * Sequence of calls for restore:
206  *   See subroutine plugin_name_stream() below.
207  */
208 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
209 {
210    Plugin *plugin;
211    int i = 0;
212    int len;
213    char *p;
214    char *cmd = ff_pkt->top_fname;
215    struct save_pkt sp;
216    bEvent event;
217    POOL_MEM fname(PM_FNAME);
218    POOL_MEM link(PM_FNAME);
219
220    if (!plugin_list || !jcr->plugin_ctx_list || job_canceled(jcr)) {
221       Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not loaded.\n", cmd);
222       return 1;                            /* Return if no plugins loaded */
223    }
224
225    jcr->cmd_plugin = true;
226    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
227    event.eventType = bEventBackupCommand;
228
229    /* Handle plugin command here backup */
230    Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
231    if (!(p = strchr(cmd, ':'))) {
232       Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
233       goto bail_out;
234    }
235    len = p - cmd;
236    if (len <= 0) {
237       goto bail_out;
238    }
239
240    /* Note, we stop the loop on the first plugin that matches the name */
241    foreach_alist(plugin, plugin_list) {
242       Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
243       if (strncmp(plugin->file, cmd, len) != 0) {
244          i++;
245          continue;
246       }
247       /* 
248        * We put the current plugin pointer, and the plugin context
249        *  into the jcr, because during save_file(), the plugin
250        *  will be called many times and these values are needed.
251        */
252       jcr->plugin_ctx = &plugin_ctx_list[i];
253       jcr->plugin = plugin;
254       if (is_plugin_disabled(jcr)) {
255          goto bail_out;
256       }
257
258       Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
259       /* Send the backup command to the right plugin*/
260       if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
261          goto bail_out;
262       }
263       /* Loop getting filenames to backup then saving them */
264       while (!job_canceled(jcr)) { 
265          memset(&sp, 0, sizeof(sp));
266          sp.pkt_size = sizeof(sp);
267          sp.pkt_end = sizeof(sp);
268          sp.portable = true;
269          sp.cmd = cmd;
270          Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
271                 &sp);
272          /* Get the file save parameters */
273          if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
274             goto bail_out;
275          }
276          if (sp.type == 0 || sp.fname == NULL) {
277             Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\" returned bad startBackupFile packet.\n"),
278                cmd);
279             goto bail_out;
280          }
281          jcr->plugin_sp = &sp;
282          ff_pkt = jcr->ff;
283          /*
284           * Copy fname and link because save_file() zaps them.  This 
285           *  avoids zaping the plugin's strings.
286           */
287          pm_strcpy(fname, sp.fname);
288          pm_strcpy(link, sp.link);
289          ff_pkt->fname = fname.c_str();
290          ff_pkt->link = link.c_str();
291          ff_pkt->type = sp.type;
292          memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
293          Dmsg1(dbglvl, "Save_file: file=%s\n", fname.c_str());
294          save_file(jcr, ff_pkt, true);
295          bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
296          if (rc == bRC_More || rc == bRC_OK) {
297             accurate_mark_file_as_seen(jcr, fname.c_str());
298          }
299          if (rc == bRC_More) {
300             continue;
301          }
302          goto bail_out;
303       }
304       goto bail_out;
305    }
306    Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
307
308 bail_out:
309    jcr->cmd_plugin = false;
310    jcr->plugin = NULL;
311    jcr->plugin_ctx = NULL;
312    return 1;
313 }
314
315 /* 
316  * Send plugin name start/end record to SD
317  */
318 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
319 {
320    int stat;
321    int index = jcr->JobFiles;
322    struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
323
324    if (!sp) {
325       Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
326       return false;
327    }
328    if (job_canceled(jcr)) {
329       return false;
330    }
331   
332    if (start) {
333       index++;                  /* JobFiles not incremented yet */
334    }
335    Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
336    /* Send stream header */
337    if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
338      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
339            sd->bstrerror());
340      return false;
341    }
342    Dmsg1(50, "send: %s\n", sd->msg);
343
344    if (start) {
345       /* Send data -- not much */
346       stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
347    } else {
348       /* Send end of data */
349       stat = sd->fsend("0 0");
350    }
351    if (!stat) {
352       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
353             sd->bstrerror());
354          return false;
355    }
356    Dmsg1(dbglvl, "send: %s\n", sd->msg);
357    sd->signal(BNET_EOD);            /* indicate end of plugin name data */
358    return true;
359 }
360
361 /*
362  * Plugin name stream found during restore.  The record passed in
363  *  argument name was generated in send_plugin_name() above.
364  *
365  * Returns: true  if start of stream
366  *          false if end of steam
367  */
368 bool plugin_name_stream(JCR *jcr, char *name)    
369 {
370    char *p = name;
371    char *cmd;
372    bool start, portable;
373    Plugin *plugin;
374    int len;
375    int i = 0;
376    bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
377
378    Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
379    skip_nonspaces(&p);             /* skip over jcr->JobFiles */
380    skip_spaces(&p);
381    start = *p == '1';
382    if (start) {
383       /* Start of plugin data */
384       skip_nonspaces(&p);          /* skip start/end flag */
385       skip_spaces(&p);
386       portable = *p == '1';
387       skip_nonspaces(&p);          /* skip portable flag */
388       skip_spaces(&p);
389       cmd = p;
390    } else {
391       /*
392        * End of plugin data, notify plugin, then clear flags   
393        */
394       Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
395       if (jcr->plugin) {
396          plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
397       }
398       jcr->plugin_ctx = NULL;
399       jcr->plugin = NULL;
400       goto bail_out;
401    }
402    if (!plugin_ctx_list) {
403       goto bail_out;
404    }
405       
406    /*
407     * After this point, we are dealing with a restore start
408     */
409
410 // Dmsg1(dbglvl, "plugin restore cmd=%s\n", cmd);
411    if (!(p = strchr(cmd, ':'))) {
412       Jmsg1(jcr, M_ERROR, 0,
413            _("Malformed plugin command. Name not terminated by colon: %s\n"), cmd);
414       goto bail_out;
415    }
416    len = p - cmd;
417    if (len <= 0) {
418       goto bail_out;
419    }
420
421    /*
422     * Search for correct plugin as specified on the command 
423     */
424    foreach_alist(plugin, plugin_list) {
425       bEvent event;
426       Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
427       if (strncmp(plugin->file, cmd, len) != 0) {
428          i++;
429          continue;
430       }
431       jcr->plugin_ctx = &plugin_ctx_list[i];
432       jcr->plugin = plugin;
433       if (is_plugin_disabled(jcr)) {
434          goto bail_out;
435       }
436       Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
437       event.eventType = bEventRestoreCommand;     
438       if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, 
439             &event, cmd) != bRC_OK) {
440          goto bail_out;
441       }
442       /* ***FIXME**** check error code */
443       plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
444       goto bail_out;
445    }
446    Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
447
448 bail_out:
449    return start;
450 }
451
452 /*
453  * Tell the plugin to create the file.  Return values are
454  *   This is called only during Restore
455  *
456  *  CF_ERROR    -- error
457  *  CF_SKIP     -- skip processing this file
458  *  CF_EXTRACT  -- extract the file (i.e.call i/o routines)
459  *  CF_CREATED  -- created, but no content to extract (typically directories)
460  *
461  */
462 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
463 {
464    bpContext *plugin_ctx = jcr->plugin_ctx;
465    Plugin *plugin = jcr->plugin;
466    struct restore_pkt rp;
467    int flags;
468    int rc;
469
470    if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || job_canceled(jcr)) {
471       return CF_ERROR;
472    }
473    rp.pkt_size = sizeof(rp);
474    rp.pkt_end = sizeof(rp);
475    rp.stream = attr->stream;
476    rp.data_stream = attr->data_stream;
477    rp.type = attr->type;
478    rp.file_index = attr->file_index;
479    rp.LinkFI = attr->LinkFI;
480    rp.uid = attr->uid;
481    rp.statp = attr->statp;                /* structure assignment */
482    rp.attrEx = attr->attrEx;
483    rp.ofname = attr->ofname;
484    rp.olname = attr->olname;
485    rp.where = jcr->where;
486    rp.RegexWhere = jcr->RegexWhere;
487    rp.replace = jcr->replace;
488    rp.create_status = CF_ERROR;
489    Dmsg1(dbglvl, "call plugin createFile=%s\n", rp.ofname);
490    rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
491    if (rc != bRC_OK) {
492       Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
493             rc, attr->ofname);
494       return CF_ERROR;
495    }
496    if (rp.create_status == CF_ERROR) {
497       Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
498             attr->ofname);
499       return CF_ERROR;
500    }
501    /* Created link or directory? */
502    if (rp.create_status == CF_CREATED) {
503       return rp.create_status;        /* yes, no need to bopen */
504    }
505
506    flags =  O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
507    Dmsg0(dbglvl, "call bopen\n");
508    int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
509    Dmsg1(50, "bopen status=%d\n", stat);
510    if (stat < 0) {
511       berrno be;
512       be.set_errno(bfd->berrno);
513       Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
514             attr->ofname, be.bstrerror());
515       Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
516       return CF_ERROR;
517    }
518
519    if (!is_bopen(bfd)) {
520       Dmsg0(000, "===== BFD is not open!!!!\n");
521    }
522    return CF_EXTRACT;
523 }
524
525 /*
526  * Reset the file attributes after all file I/O is done -- this allows
527  *  the previous access time/dates to be set properly, and it also allows
528  *  us to properly set directory permissions.
529  *  Not currently Implemented.
530  */
531 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
532 {
533    Dmsg0(dbglvl, "plugin_set_attributes\n");
534    if (is_bopen(ofd)) {
535       bclose(ofd);
536    }
537    pm_strcpy(attr->ofname, "*none*");
538    return true;
539 }
540
541 /*
542  * Print to file the plugin info.
543  */
544 void dump_fd_plugin(Plugin *plugin, FILE *fp)
545 {
546    if (!plugin) {
547       return ;
548    }
549    pInfo *info = (pInfo *)plugin->pinfo;
550    fprintf(fp, "\tversion=%d\n", info->version);
551    fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
552    fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
553    fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
554    fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
555    fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
556    fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
557 }
558
559 /*
560  * This entry point is called internally by Bacula to ensure
561  *  that the plugin IO calls come into this code.
562  */
563 void load_fd_plugins(const char *plugin_dir)
564 {
565    Plugin *plugin;
566
567    if (!plugin_dir) {
568       Dmsg0(dbglvl, "plugin dir is NULL\n");
569       return;
570    }
571
572    plugin_list = New(alist(10, not_owned_by_alist));
573    if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
574                      is_plugin_compatible)) {
575       /* Either none found, or some error */
576       if (plugin_list->size() == 0) {
577          delete plugin_list;
578          plugin_list = NULL;
579          Dmsg0(dbglvl, "No plugins loaded\n");
580          return;
581       }
582    }
583
584    /* Plug entry points called from findlib */
585    plugin_bopen  = my_plugin_bopen;
586    plugin_bclose = my_plugin_bclose;
587    plugin_bread  = my_plugin_bread;
588    plugin_bwrite = my_plugin_bwrite;
589    plugin_blseek = my_plugin_blseek;
590
591    /* 
592     * Verify that the plugin is acceptable, and print information
593     *  about it.
594     */
595    foreach_alist(plugin, plugin_list) {
596       Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
597       Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
598    }
599
600    dbg_plugin_add_hook(dump_fd_plugin);
601 }
602
603 /*
604  * Check if a plugin is compatible.  Called by the load_plugin function
605  *  to allow us to verify the plugin.
606  */
607 static bool is_plugin_compatible(Plugin *plugin)
608 {
609    pInfo *info = (pInfo *)plugin->pinfo;
610    Dmsg0(50, "is_plugin_compatible called\n");
611    if (debug_level >= 50) {
612       dump_fd_plugin(plugin, stdin);
613    }
614    if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
615       Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
616            plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
617       Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
618            plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
619
620       return false;
621    }
622    if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
623       Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
624            plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
625       Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
626            plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
627       return false;
628    }
629    if (strcmp(info->plugin_license, "Bacula GPLv2") != 0 &&
630        strcmp(info->plugin_license, "GPLv2") != 0) {
631       Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
632            plugin->file, info->plugin_license);
633       Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
634            plugin->file, info->plugin_license);
635       return false;
636    }
637       
638    return true;
639 }
640
641
642 /*
643  * Create a new instance of each plugin for this Job
644  *   Note, plugin_list can exist but jcr->plugin_ctx_list can
645  *   be NULL if no plugins were loaded.
646  */
647 void new_plugins(JCR *jcr)
648 {
649    Plugin *plugin;
650    int i = 0;
651
652    if (!plugin_list) {
653       Dmsg0(dbglvl, "plugin list is NULL\n");
654       return;
655    }
656    if (job_canceled(jcr)) {
657       return;
658    }
659
660    int num = plugin_list->size();
661
662    if (num == 0) {
663       Dmsg0(dbglvl, "No plugins loaded\n");
664       return;
665    }
666
667    jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
668
669    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
670    Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
671    foreach_alist(plugin, plugin_list) {
672       /* Start a new instance of each plugin */
673       bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
674       memset(b_ctx, 0, sizeof(bacula_ctx));
675       b_ctx->jcr = jcr;
676       plugin_ctx_list[i].bContext = (void *)b_ctx;   /* Bacula private context */
677       plugin_ctx_list[i].pContext = NULL;
678       if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
679          b_ctx->disabled = true;
680       }
681    }
682 }
683
684 /*
685  * Free the plugin instances for this Job
686  */
687 void free_plugins(JCR *jcr)
688 {
689    Plugin *plugin;
690    int i = 0;
691
692    if (!plugin_list || !jcr->plugin_ctx_list) {
693       return;                         /* no plugins, nothing to do */
694    }
695
696    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
697    Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
698    foreach_alist(plugin, plugin_list) {   
699       /* Free the plugin instance */
700       plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
701       free(plugin_ctx_list[i++].bContext);     /* free Bacula private context */
702    }
703    free(plugin_ctx_list);
704    jcr->plugin_ctx_list = NULL;
705 }
706
707 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
708 {
709    JCR *jcr = bfd->jcr;
710    Plugin *plugin = (Plugin *)jcr->plugin;
711    struct io_pkt io;
712
713    Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
714    io.pkt_size = sizeof(io);
715    io.pkt_end = sizeof(io);
716    io.func = IO_OPEN;
717    io.count = 0;
718    io.buf = NULL;
719    io.fname = fname;
720    io.flags = flags;
721    io.mode = mode;
722    io.win32 = false;
723    io.lerror = 0;
724    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
725    bfd->berrno = io.io_errno;
726    if (io.win32) {
727       errno = b_errno_win32;
728    } else {
729       errno = io.io_errno;
730       bfd->lerror = io.lerror;
731    }
732    Dmsg1(50, "Return from plugin open status=%d\n", io.status);
733    return io.status;
734 }
735
736 static int my_plugin_bclose(BFILE *bfd)
737 {
738    JCR *jcr = bfd->jcr;
739    Plugin *plugin = (Plugin *)jcr->plugin;
740    struct io_pkt io;
741    Dmsg0(dbglvl, "===== plugin_bclose\n");
742    io.pkt_size = sizeof(io);
743    io.pkt_end = sizeof(io);
744    io.func = IO_CLOSE;
745    io.count = 0;
746    io.buf = NULL;
747    io.win32 = false;
748    io.lerror = 0;
749    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
750    bfd->berrno = io.io_errno;
751    if (io.win32) {
752       errno = b_errno_win32;
753    } else {
754       errno = io.io_errno;
755       bfd->lerror = io.lerror;
756    }
757    Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
758    return io.status;
759 }
760
761 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
762 {
763    JCR *jcr = bfd->jcr;
764    Plugin *plugin = (Plugin *)jcr->plugin;
765    struct io_pkt io;
766    Dmsg0(dbglvl, "plugin_bread\n");
767    io.pkt_size = sizeof(io);
768    io.pkt_end = sizeof(io);
769    io.func = IO_READ;
770    io.count = count;
771    io.buf = (char *)buf;
772    io.win32 = false;
773    io.lerror = 0;
774    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
775    bfd->berrno = io.io_errno;
776    if (io.win32) {
777       errno = b_errno_win32;
778    } else {
779       errno = io.io_errno;
780       bfd->lerror = io.lerror;
781    }
782    return (ssize_t)io.status;
783 }
784
785 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
786 {
787    JCR *jcr = bfd->jcr;
788    Plugin *plugin = (Plugin *)jcr->plugin;
789    struct io_pkt io;
790    Dmsg0(dbglvl, "plugin_bwrite\n");
791    io.pkt_size = sizeof(io);
792    io.pkt_end = sizeof(io);
793    io.func = IO_WRITE;
794    io.count = count;
795    io.buf = (char *)buf;
796    io.win32 = false;
797    io.lerror = 0;
798    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
799    bfd->berrno = io.io_errno;
800    if (io.win32) {
801       errno = b_errno_win32;
802    } else {
803       errno = io.io_errno;
804       bfd->lerror = io.lerror;
805    }
806    return (ssize_t)io.status;
807 }
808
809 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
810 {
811    JCR *jcr = bfd->jcr;
812    Plugin *plugin = (Plugin *)jcr->plugin;
813    struct io_pkt io;
814    Dmsg0(dbglvl, "plugin_bseek\n");
815    io.pkt_size = sizeof(io);
816    io.pkt_end = sizeof(io);
817    io.func = IO_SEEK;
818    io.offset = offset;
819    io.whence = whence;
820    io.win32 = false;
821    io.lerror = 0;
822    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
823    bfd->berrno = io.io_errno;
824    if (io.win32) {
825       errno = b_errno_win32;
826    } else {
827       errno = io.io_errno;
828       bfd->lerror = io.lerror;
829    }
830    return (boffset_t)io.offset;
831 }
832
833 /* ==============================================================
834  *
835  * Callbacks from the plugin
836  *
837  * ==============================================================
838  */
839 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
840 {
841    JCR *jcr;
842    if (!value || !ctx) {
843       return bRC_Error;
844    }
845    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
846 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
847    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
848    if (!jcr) {
849       return bRC_Error;
850    }
851 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
852    switch (var) {
853    case bVarJobId:
854       *((int *)value) = jcr->JobId;
855       Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
856       break;
857    case bVarFDName:
858       *((char **)value) = my_name;
859       Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
860       break;
861    case bVarLevel:
862       *((int *)value) = jcr->getJobLevel();
863       Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
864       break;
865    case bVarType:
866       *((int *)value) = jcr->getJobType();
867       Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
868       break;
869    case bVarClient:
870       *((char **)value) = jcr->client_name;
871       Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
872       break;
873    case bVarJobName:
874       *((char **)value) = jcr->Job;
875       Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
876       break;
877    case bVarJobStatus:
878       *((int *)value) = jcr->JobStatus;
879       Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
880       break;
881    case bVarSinceTime:
882       *((int *)value) = (int)jcr->mtime;
883       Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
884       break;
885    case bVarAccurate:
886       *((int *)value) = (int)jcr->accurate;
887       Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
888       break;
889    case bVarFileSeen:
890       break;                 /* a write only variable, ignore read request */
891    }
892    return bRC_OK;
893 }
894
895 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
896 {
897    JCR *jcr;
898    if (!value || !ctx) {
899       return bRC_Error;
900    }
901    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
902 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
903    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
904    if (!jcr) {
905       return bRC_Error;
906    }
907 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
908    switch (var) {
909    case bVarFileSeen:
910       if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
911          return bRC_Error;
912       } 
913       break;
914    default:
915       break;
916    }
917    return bRC_OK;
918 }
919
920 static bRC baculaRegisterEvents(bpContext *ctx, ...)
921 {
922    va_list args;
923    uint32_t event;
924
925    if (!ctx) {
926       return bRC_Error;
927    }
928
929    va_start(args, ctx);
930    while ((event = va_arg(args, uint32_t))) {
931       Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
932    }
933    va_end(args);
934    return bRC_OK;
935 }
936
937 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
938   int type, utime_t mtime, const char *fmt, ...)
939 {
940    va_list arg_ptr;
941    char buf[2000];
942    JCR *jcr;
943
944    if (ctx) {
945       jcr = ((bacula_ctx *)ctx->bContext)->jcr;
946    } else {
947       jcr = NULL;
948    }
949
950    va_start(arg_ptr, fmt);
951    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
952    va_end(arg_ptr);
953    Jmsg(jcr, type, mtime, "%s", buf);
954    return bRC_OK;
955 }
956
957 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
958   int level, const char *fmt, ...)
959 {
960    va_list arg_ptr;
961    char buf[2000];
962
963    va_start(arg_ptr, fmt);
964    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
965    va_end(arg_ptr);
966    d_msg(file, line, level, "%s", buf);
967    return bRC_OK;
968 }
969
970 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
971               size_t size)
972 {
973 #ifdef SMARTALLOC
974    return sm_malloc(file, line, size);
975 #else
976    return malloc(size);
977 #endif
978 }
979
980 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
981 {
982 #ifdef SMARTALLOC
983    sm_free(file, line, mem);
984 #else
985    free(mem);
986 #endif
987 }
988
989
990
991 #ifdef TEST_PROGRAM
992
993 int     (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
994 int     (*plugin_bclose)(JCR *jcr) = NULL;
995 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
996 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
997 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
998
999 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1000 {
1001    return 0;
1002 }
1003
1004 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1005 {
1006    return true;
1007 }
1008
1009 int main(int argc, char *argv[])
1010 {
1011    char plugin_dir[1000];
1012    JCR mjcr1, mjcr2;
1013    JCR *jcr1 = &mjcr1;
1014    JCR *jcr2 = &mjcr2;
1015
1016    strcpy(my_name, "test-fd");
1017     
1018    getcwd(plugin_dir, sizeof(plugin_dir)-1);
1019    load_fd_plugins(plugin_dir);
1020
1021    jcr1->JobId = 111;
1022    new_plugins(jcr1);
1023
1024    jcr2->JobId = 222;
1025    new_plugins(jcr2);
1026
1027    generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1028    generate_plugin_event(jcr1, bEventJobEnd);
1029    generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1030    free_plugins(jcr1);
1031    generate_plugin_event(jcr2, bEventJobEnd);
1032    free_plugins(jcr2);
1033
1034    unload_plugins();
1035
1036    Dmsg0(dbglvl, "bacula: OK ...\n");
1037    close_memory_pool();
1038    sm_dump(false);
1039    return 0;
1040 }
1041
1042 #endif /* TEST_PROGRAM */