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