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