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