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