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