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