]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/fd_plugins.c
Fix FD crash when plugin running and cancel given
[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    if (!plugin || !jcr->plugin_ctx) {
715       return 0;
716    }
717    io.pkt_size = sizeof(io);
718    io.pkt_end = sizeof(io);
719    io.func = IO_OPEN;
720    io.count = 0;
721    io.buf = NULL;
722    io.fname = fname;
723    io.flags = flags;
724    io.mode = mode;
725    io.win32 = false;
726    io.lerror = 0;
727    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
728    bfd->berrno = io.io_errno;
729    if (io.win32) {
730       errno = b_errno_win32;
731    } else {
732       errno = io.io_errno;
733       bfd->lerror = io.lerror;
734    }
735    Dmsg1(50, "Return from plugin open status=%d\n", io.status);
736    return io.status;
737 }
738
739 static int my_plugin_bclose(BFILE *bfd)
740 {
741    JCR *jcr = bfd->jcr;
742    Plugin *plugin = (Plugin *)jcr->plugin;
743    struct io_pkt io;
744
745    Dmsg0(dbglvl, "===== plugin_bclose\n");
746    if (!plugin || !jcr->plugin_ctx) {
747       return 0;
748    }
749    io.pkt_size = sizeof(io);
750    io.pkt_end = sizeof(io);
751    io.func = IO_CLOSE;
752    io.count = 0;
753    io.buf = NULL;
754    io.win32 = false;
755    io.lerror = 0;
756    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
757    bfd->berrno = io.io_errno;
758    if (io.win32) {
759       errno = b_errno_win32;
760    } else {
761       errno = io.io_errno;
762       bfd->lerror = io.lerror;
763    }
764    Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
765    return io.status;
766 }
767
768 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
769 {
770    JCR *jcr = bfd->jcr;
771    Plugin *plugin = (Plugin *)jcr->plugin;
772    struct io_pkt io;
773
774    Dmsg0(dbglvl, "plugin_bread\n");
775    if (!plugin || !jcr->plugin_ctx) {
776       return 0;
777    }
778    io.pkt_size = sizeof(io);
779    io.pkt_end = sizeof(io);
780    io.func = IO_READ;
781    io.count = count;
782    io.buf = (char *)buf;
783    io.win32 = false;
784    io.lerror = 0;
785    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
786    bfd->berrno = io.io_errno;
787    if (io.win32) {
788       errno = b_errno_win32;
789    } else {
790       errno = io.io_errno;
791       bfd->lerror = io.lerror;
792    }
793    return (ssize_t)io.status;
794 }
795
796 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
797 {
798    JCR *jcr = bfd->jcr;
799    Plugin *plugin = (Plugin *)jcr->plugin;
800    struct io_pkt io;
801
802    Dmsg0(dbglvl, "plugin_bwrite\n");
803    if (!plugin || !jcr->plugin_ctx) {
804       return 0;
805    }
806    io.pkt_size = sizeof(io);
807    io.pkt_end = sizeof(io);
808    io.func = IO_WRITE;
809    io.count = count;
810    io.buf = (char *)buf;
811    io.win32 = false;
812    io.lerror = 0;
813    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
814    bfd->berrno = io.io_errno;
815    if (io.win32) {
816       errno = b_errno_win32;
817    } else {
818       errno = io.io_errno;
819       bfd->lerror = io.lerror;
820    }
821    return (ssize_t)io.status;
822 }
823
824 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
825 {
826    JCR *jcr = bfd->jcr;
827    Plugin *plugin = (Plugin *)jcr->plugin;
828    struct io_pkt io;
829
830    Dmsg0(dbglvl, "plugin_bseek\n");
831    if (!plugin || !jcr->plugin_ctx) {
832       return 0;
833    }
834    io.pkt_size = sizeof(io);
835    io.pkt_end = sizeof(io);
836    io.func = IO_SEEK;
837    io.offset = offset;
838    io.whence = whence;
839    io.win32 = false;
840    io.lerror = 0;
841    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
842    bfd->berrno = io.io_errno;
843    if (io.win32) {
844       errno = b_errno_win32;
845    } else {
846       errno = io.io_errno;
847       bfd->lerror = io.lerror;
848    }
849    return (boffset_t)io.offset;
850 }
851
852 /* ==============================================================
853  *
854  * Callbacks from the plugin
855  *
856  * ==============================================================
857  */
858 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
859 {
860    JCR *jcr;
861    if (!value || !ctx) {
862       return bRC_Error;
863    }
864    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
865 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
866    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
867    if (!jcr) {
868       return bRC_Error;
869    }
870 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
871    switch (var) {
872    case bVarJobId:
873       *((int *)value) = jcr->JobId;
874       Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
875       break;
876    case bVarFDName:
877       *((char **)value) = my_name;
878       Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
879       break;
880    case bVarLevel:
881       *((int *)value) = jcr->getJobLevel();
882       Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
883       break;
884    case bVarType:
885       *((int *)value) = jcr->getJobType();
886       Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
887       break;
888    case bVarClient:
889       *((char **)value) = jcr->client_name;
890       Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
891       break;
892    case bVarJobName:
893       *((char **)value) = jcr->Job;
894       Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
895       break;
896    case bVarJobStatus:
897       *((int *)value) = jcr->JobStatus;
898       Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
899       break;
900    case bVarSinceTime:
901       *((int *)value) = (int)jcr->mtime;
902       Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
903       break;
904    case bVarAccurate:
905       *((int *)value) = (int)jcr->accurate;
906       Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
907       break;
908    case bVarFileSeen:
909       break;                 /* a write only variable, ignore read request */
910    }
911    return bRC_OK;
912 }
913
914 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
915 {
916    JCR *jcr;
917    if (!value || !ctx) {
918       return bRC_Error;
919    }
920    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
921 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
922    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
923    if (!jcr) {
924       return bRC_Error;
925    }
926 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
927    switch (var) {
928    case bVarFileSeen:
929       if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
930          return bRC_Error;
931       } 
932       break;
933    default:
934       break;
935    }
936    return bRC_OK;
937 }
938
939 static bRC baculaRegisterEvents(bpContext *ctx, ...)
940 {
941    va_list args;
942    uint32_t event;
943
944    if (!ctx) {
945       return bRC_Error;
946    }
947
948    va_start(args, ctx);
949    while ((event = va_arg(args, uint32_t))) {
950       Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
951    }
952    va_end(args);
953    return bRC_OK;
954 }
955
956 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
957   int type, utime_t mtime, const char *fmt, ...)
958 {
959    va_list arg_ptr;
960    char buf[2000];
961    JCR *jcr;
962
963    if (ctx) {
964       jcr = ((bacula_ctx *)ctx->bContext)->jcr;
965    } else {
966       jcr = NULL;
967    }
968
969    va_start(arg_ptr, fmt);
970    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
971    va_end(arg_ptr);
972    Jmsg(jcr, type, mtime, "%s", buf);
973    return bRC_OK;
974 }
975
976 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
977   int level, const char *fmt, ...)
978 {
979    va_list arg_ptr;
980    char buf[2000];
981
982    va_start(arg_ptr, fmt);
983    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
984    va_end(arg_ptr);
985    d_msg(file, line, level, "%s", buf);
986    return bRC_OK;
987 }
988
989 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
990               size_t size)
991 {
992 #ifdef SMARTALLOC
993    return sm_malloc(file, line, size);
994 #else
995    return malloc(size);
996 #endif
997 }
998
999 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1000 {
1001 #ifdef SMARTALLOC
1002    sm_free(file, line, mem);
1003 #else
1004    free(mem);
1005 #endif
1006 }
1007
1008
1009
1010 #ifdef TEST_PROGRAM
1011
1012 int     (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1013 int     (*plugin_bclose)(JCR *jcr) = NULL;
1014 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1015 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1016 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1017
1018 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1019 {
1020    return 0;
1021 }
1022
1023 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1024 {
1025    return true;
1026 }
1027
1028 int main(int argc, char *argv[])
1029 {
1030    char plugin_dir[1000];
1031    JCR mjcr1, mjcr2;
1032    JCR *jcr1 = &mjcr1;
1033    JCR *jcr2 = &mjcr2;
1034
1035    strcpy(my_name, "test-fd");
1036     
1037    getcwd(plugin_dir, sizeof(plugin_dir)-1);
1038    load_fd_plugins(plugin_dir);
1039
1040    jcr1->JobId = 111;
1041    new_plugins(jcr1);
1042
1043    jcr2->JobId = 222;
1044    new_plugins(jcr2);
1045
1046    generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1047    generate_plugin_event(jcr1, bEventJobEnd);
1048    generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1049    free_plugins(jcr1);
1050    generate_plugin_event(jcr2, bEventJobEnd);
1051    free_plugins(jcr2);
1052
1053    unload_plugins();
1054
1055    Dmsg0(dbglvl, "bacula: OK ...\n");
1056    close_memory_pool();
1057    sm_dump(false);
1058    return 0;
1059 }
1060
1061 #endif /* TEST_PROGRAM */