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