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