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