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