]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/fd_plugins.c
kes Fix plugin_bwrite - plugin-blseek mixup pointed out by James.
[bacula/bacula] / bacula / src / filed / fd_plugins.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2008 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation, which is 
11    listed in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  * Main program to test loading and running Bacula plugins.
30  *   Destined to become Bacula pluginloader, ...
31  *
32  * Kern Sibbald, October 2007
33  */
34 #include "bacula.h"
35 #include "filed.h"
36
37 const int dbglvl = 50;
38 #ifdef HAVE_WIN32
39 const char *plugin_type = "-fd.dll";
40 #else
41 const char *plugin_type = "-fd.so";
42 #endif
43
44 extern int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
45
46 /* Function pointers to be set here */
47 extern DLL_IMP_EXP int     (*plugin_bopen)(BFILE *bfd, const char *fname, int flags, mode_t mode);
48 extern DLL_IMP_EXP int     (*plugin_bclose)(BFILE *bfd);
49 extern DLL_IMP_EXP ssize_t (*plugin_bread)(BFILE *bfd, void *buf, size_t count);
50 extern DLL_IMP_EXP ssize_t (*plugin_bwrite)(BFILE *bfd, void *buf, size_t count);
51 extern DLL_IMP_EXP boffset_t (*plugin_blseek)(BFILE *bfd, boffset_t offset, int whence);
52
53
54 /* Forward referenced functions */
55 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value);
56 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value);
57 static bRC baculaRegisterEvents(bpContext *ctx, ...);
58 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
59   int type, time_t mtime, const char *fmt, ...);
60 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
61   int level, const char *fmt, ...);
62
63 static int     my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
64 static int     my_plugin_bclose(BFILE *bfd);
65 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
66 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
67 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
68
69
70 /* Bacula info */
71 static bInfo binfo = {
72    sizeof(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    mode_t mode;
345
346    if (!set_cmd_plugin(bfd, jcr)) {
347       return CF_ERROR;
348    }
349    rp.pkt_size = sizeof(rp);
350    rp.pkt_end = sizeof(rp);
351    rp.stream = attr->stream;
352    rp.data_stream = attr->data_stream;
353    rp.type = attr->type;
354    rp.file_index = attr->file_index;
355    rp.LinkFI = attr->LinkFI;
356    rp.uid = attr->uid;
357    rp.statp = attr->statp;                /* structure assignment */
358    rp.attrEx = attr->attrEx;
359    rp.ofname = attr->ofname;
360    rp.olname = attr->olname;
361    rp.where = jcr->where;
362    rp.RegexWhere = jcr->RegexWhere;
363    rp.replace = jcr->replace;
364    rp.create_status = CF_ERROR;
365    Dmsg1(dbglvl, "call plugin createFile=%s\n", rp.ofname);
366    if (plug_func(plugin)->createFile(plugin_ctx, &rp) != bRC_OK) {
367       return CF_ERROR;
368    }
369    if (rp.create_status == CF_ERROR || rp.create_status == CF_CREATED) {
370       return rp.create_status;
371    }
372    mode =  O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
373    Dmsg0(dbglvl, "call bopen\n");
374    if ((bopen(bfd, attr->ofname, mode, S_IRUSR | S_IWUSR)) < 0) {
375       berrno be;
376       be.set_errno(bfd->berrno);
377       Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
378             attr->ofname, be.bstrerror());
379       Dmsg2(dbglvl,"Could not create %s: ERR=%s\n", attr->ofname, be.bstrerror());
380       return CF_ERROR;
381    }
382    return CF_EXTRACT;
383 }
384
385 /*
386  * Reset the file attributes after all file I/O is done -- this allows
387  *  the previous access time/dates to be set properly, and it also allows
388  *  us to properly set directory permissions.
389  */
390 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
391 {
392    Dmsg0(dbglvl, "plugin_set_attributes\n");
393    return true;
394 }
395
396 /*
397  * This entry point is called internally by Bacula to ensure
398  *  that the plugin IO calls come into this code.
399  */
400 void load_fd_plugins(const char *plugin_dir)
401 {
402    Plugin *plugin;
403
404    if (!plugin_dir) {
405       Dmsg0(dbglvl, "plugin dir is NULL\n");
406       return;
407    }
408
409    plugin_list = New(alist(10, not_owned_by_alist));
410    if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type)) {
411       /* Either none found, or some error */
412       if (plugin_list->size() == 0) {
413          delete plugin_list;
414          plugin_list = NULL;
415          Dmsg0(dbglvl, "No plugins loaded\n");
416          return;
417       }
418    }
419
420    /* Plug entry points called from findlib */
421    plugin_bopen  = my_plugin_bopen;
422    plugin_bclose = my_plugin_bclose;
423    plugin_bread  = my_plugin_bread;
424    plugin_bwrite = my_plugin_bwrite;
425    plugin_blseek = my_plugin_blseek;
426    foreach_alist(plugin, plugin_list) {
427       Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
428       Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
429
430    }
431 }
432
433 /*
434  * Create a new instance of each plugin for this Job
435  *   Note, plugin_list can exist but jcr->plugin_ctx_list can
436  *   be NULL if no plugins were loaded.
437  */
438 void new_plugins(JCR *jcr)
439 {
440    Plugin *plugin;
441    int i = 0;
442
443    if (!plugin_list) {
444       Dmsg0(dbglvl, "plugin list is NULL\n");
445       return;
446    }
447
448    int num = plugin_list->size();
449
450    if (num == 0) {
451       Dmsg0(dbglvl, "No plugins loaded\n");
452       return;
453    }
454
455    jcr->plugin_ctx_list = (void *)malloc(sizeof(bpContext) * num);
456
457    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
458    Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
459    foreach_alist(plugin, plugin_list) {
460       /* Start a new instance of each plugin */
461       plugin_ctx_list[i].bContext = (void *)jcr;
462       plugin_ctx_list[i].pContext = NULL;
463       plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]);
464    }
465 }
466
467 /*
468  * Free the plugin instances for this Job
469  */
470 void free_plugins(JCR *jcr)
471 {
472    Plugin *plugin;
473    int i = 0;
474
475    if (!plugin_list || !jcr->plugin_ctx_list) {
476       return;                         /* no plugins, nothing to do */
477    }
478
479    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
480    Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
481    foreach_alist(plugin, plugin_list) {
482       /* Free the plugin instance */
483       plug_func(plugin)->freePlugin(&plugin_ctx_list[i++]);
484    }
485    free(plugin_ctx_list);
486    jcr->plugin_ctx_list = NULL;
487 }
488
489 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
490 {
491    JCR *jcr = bfd->jcr;
492    Plugin *plugin = (Plugin *)jcr->plugin;
493    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
494    struct io_pkt io;
495    Dmsg0(dbglvl, "plugin_bopen\n");
496    io.pkt_size = sizeof(io);
497    io.pkt_end = sizeof(io);
498    io.func = IO_OPEN;
499    io.count = 0;
500    io.buf = NULL;
501    io.fname = fname;
502    io.flags = flags;
503    io.mode = mode;
504    io.win32 = false;
505    io.lerror = 0;
506    plug_func(plugin)->pluginIO(plugin_ctx, &io);
507    bfd->berrno = io.io_errno;
508    if (io.win32) {
509       errno = b_errno_win32;
510    } else {
511       errno = io.io_errno;
512       bfd->lerror = io.lerror;
513    }
514    return io.status;
515 }
516
517 static int my_plugin_bclose(BFILE *bfd)
518 {
519    JCR *jcr = bfd->jcr;
520    Plugin *plugin = (Plugin *)jcr->plugin;
521    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
522    struct io_pkt io;
523    Dmsg0(dbglvl, "plugin_bclose\n");
524    io.pkt_size = sizeof(io);
525    io.pkt_end = sizeof(io);
526    io.func = IO_CLOSE;
527    io.count = 0;
528    io.buf = NULL;
529    io.win32 = false;
530    io.lerror = 0;
531    plug_func(plugin)->pluginIO(plugin_ctx, &io);
532    bfd->berrno = io.io_errno;
533    if (io.win32) {
534       errno = b_errno_win32;
535    } else {
536       errno = io.io_errno;
537       bfd->lerror = io.lerror;
538    }
539    Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
540    return io.status;
541 }
542
543 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
544 {
545    JCR *jcr = bfd->jcr;
546    Plugin *plugin = (Plugin *)jcr->plugin;
547    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
548    struct io_pkt io;
549    Dmsg0(dbglvl, "plugin_bread\n");
550    io.pkt_size = sizeof(io);
551    io.pkt_end = sizeof(io);
552    io.func = IO_READ;
553    io.count = count;
554    io.buf = (char *)buf;
555    io.win32 = false;
556    io.lerror = 0;
557    plug_func(plugin)->pluginIO(plugin_ctx, &io);
558    bfd->berrno = io.io_errno;
559    if (io.win32) {
560       errno = b_errno_win32;
561    } else {
562       errno = io.io_errno;
563       bfd->lerror = io.lerror;
564    }
565    return (ssize_t)io.status;
566 }
567
568 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
569 {
570    JCR *jcr = bfd->jcr;
571    Plugin *plugin = (Plugin *)jcr->plugin;
572    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
573    struct io_pkt io;
574    Dmsg0(dbglvl, "plugin_bwrite\n");
575    io.pkt_size = sizeof(io);
576    io.pkt_end = sizeof(io);
577    io.func = IO_WRITE;
578    io.count = count;
579    io.buf = (char *)buf;
580    io.win32 = false;
581    io.lerror = 0;
582    plug_func(plugin)->pluginIO(plugin_ctx, &io);
583    bfd->berrno = io.io_errno;
584    if (io.win32) {
585       errno = b_errno_win32;
586    } else {
587       errno = io.io_errno;
588       bfd->lerror = io.lerror;
589    }
590    return (ssize_t)io.status;
591 }
592
593 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
594 {
595    JCR *jcr = bfd->jcr;
596    Plugin *plugin = (Plugin *)jcr->plugin;
597    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
598    struct io_pkt io;
599    Dmsg0(dbglvl, "plugin_bseek\n");
600    io.pkt_size = sizeof(io);
601    io.pkt_end = sizeof(io);
602    io.func = IO_SEEK;
603    io.offset = offset;
604    io.whence = whence;
605    io.win32 = false;
606    io.lerror = 0;
607    plug_func(plugin)->pluginIO(plugin_ctx, &io);
608    bfd->berrno = io.io_errno;
609    if (io.win32) {
610       errno = b_errno_win32;
611    } else {
612       errno = io.io_errno;
613       bfd->lerror = io.lerror;
614    }
615    return (boffset_t)io.offset;
616 }
617
618 /* ==============================================================
619  *
620  * Callbacks from the plugin
621  *
622  * ==============================================================
623  */
624 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
625 {
626    JCR *jcr = (JCR *)(ctx->bContext);
627 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
628    if (!value) {
629       return bRC_Error;
630    }
631 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
632    switch (var) {
633    case bVarJobId:
634       *((int *)value) = jcr->JobId;
635       Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
636       break;
637    case bVarFDName:
638       *((char **)value) = my_name;
639       Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
640       break;
641    case bVarLevel:
642    case bVarType:
643    case bVarClient:
644    case bVarJobName:
645    case bVarJobStatus:
646    case bVarSinceTime:
647       break;
648    }
649    return bRC_OK;
650 }
651
652 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
653 {
654    Dmsg1(dbglvl, "bacula: baculaSetValue var=%d\n", var);
655    return bRC_OK;
656 }
657
658 static bRC baculaRegisterEvents(bpContext *ctx, ...)
659 {
660    va_list args;
661    uint32_t event;
662
663    va_start(args, ctx);
664    while ((event = va_arg(args, uint32_t))) {
665       Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
666    }
667    va_end(args);
668    return bRC_OK;
669 }
670
671 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
672   int type, time_t mtime, const char *fmt, ...)
673 {
674    va_list arg_ptr;
675    char buf[2000];
676    JCR *jcr = (JCR *)(ctx->bContext);
677
678    va_start(arg_ptr, fmt);
679    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
680    va_end(arg_ptr);
681    Jmsg(jcr, type, mtime, "%s", buf);
682    return bRC_OK;
683 }
684
685 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
686   int level, const char *fmt, ...)
687 {
688    va_list arg_ptr;
689    char buf[2000];
690
691    va_start(arg_ptr, fmt);
692    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
693    va_end(arg_ptr);
694    d_msg(file, line, level, "%s", buf);
695    return bRC_OK;
696 }
697
698 #ifdef TEST_PROGRAM
699
700 int     (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
701 int     (*plugin_bclose)(JCR *jcr) = NULL;
702 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
703 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
704 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
705
706 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
707 {
708    return 0;
709 }
710
711 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
712 {
713    return true;
714 }
715
716 int main(int argc, char *argv[])
717 {
718    char plugin_dir[1000];
719    JCR mjcr1, mjcr2;
720    JCR *jcr1 = &mjcr1;
721    JCR *jcr2 = &mjcr2;
722
723    strcpy(my_name, "test-fd");
724     
725    getcwd(plugin_dir, sizeof(plugin_dir)-1);
726    load_fd_plugins(plugin_dir);
727
728    jcr1->JobId = 111;
729    new_plugins(jcr1);
730
731    jcr2->JobId = 222;
732    new_plugins(jcr2);
733
734    generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
735    generate_plugin_event(jcr1, bEventJobEnd);
736    generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
737    free_plugins(jcr1);
738    generate_plugin_event(jcr2, bEventJobEnd);
739    free_plugins(jcr2);
740
741    unload_plugins();
742
743    Dmsg0(dbglvl, "bacula: OK ...\n");
744    close_memory_pool();
745    sm_dump(false);
746    return 0;
747 }
748
749 #endif /* TEST_PROGRAM */