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