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