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