]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/fd_plugins.c
c0aa2eed0246c11c8fb2396579c2e8219dc2c608
[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 = 150;
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 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
63               size_t size);
64 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem);
65
66 /*
67  * These will be plugged into the global pointer structure for
68  *  the findlib.
69  */
70 static int     my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
71 static int     my_plugin_bclose(BFILE *bfd);
72 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
73 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
74 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
75
76
77 /* Bacula info */
78 static bInfo binfo = {
79    sizeof(bInfo),
80    FD_PLUGIN_INTERFACE_VERSION 
81 };
82
83 /* Bacula entry points */
84 static bFuncs bfuncs = {
85    sizeof(bFuncs),
86    FD_PLUGIN_INTERFACE_VERSION,
87    baculaRegisterEvents,
88    baculaGetValue,
89    baculaSetValue,
90    baculaJobMsg,
91    baculaDebugMsg,
92    baculaMalloc,
93    baculaFree
94 };
95
96 /* 
97  * Bacula private context
98  */
99 struct bacula_ctx {
100    JCR *jcr;                             /* jcr for plugin */
101    bRC  rc;                              /* last return code */
102    bool disabled;                        /* set if plugin disabled */
103 };
104
105 static bool is_plugin_disabled(JCR *jcr)
106 {
107    bacula_ctx *b_ctx;
108    if (!jcr->plugin_ctx) {
109       return true;
110    }
111    b_ctx = (bacula_ctx *)jcr->plugin_ctx->bContext;
112    return b_ctx->disabled;
113 }
114
115
116 /*
117  * Create a plugin event 
118  */
119 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)     
120 {
121    bEvent event;
122    Plugin *plugin;
123    int i = 0;
124
125    if (!plugin_list || !jcr->plugin_ctx_list) {
126       return;                         /* Return if no plugins loaded */
127    }
128
129    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
130    event.eventType = eventType;
131
132    Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
133
134    /* Pass event to every plugin */
135    foreach_alist(plugin, plugin_list) {
136       bRC rc;
137       jcr->plugin_ctx = &plugin_ctx_list[i++];
138       jcr->plugin = plugin;
139       if (is_plugin_disabled(jcr)) {
140          continue;
141       }
142       rc = plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, value);
143       if (rc != bRC_OK) {
144          break;
145       }
146    }
147
148    jcr->plugin = NULL;
149    jcr->plugin_ctx = NULL;
150    return;
151 }
152
153 /*   
154  * Sequence of calls for a backup:
155  * 1. plugin_save() here is called with ff_pkt
156  * 2. we find the plugin requested on the command string
157  * 3. we generate a bEventBackupCommand event to the specified plugin
158  *    and pass it the command string.
159  * 4. we make a startPluginBackup call to the plugin, which gives
160  *    us the data we need in save_pkt
161  * 5. we call Bacula's save_file() subroutine to save the specified
162  *    file.  The plugin will be called at pluginIO() to supply the
163  *    file data.
164  *
165  * Sequence of calls for restore:
166  *   See subroutine plugin_name_stream() below.
167  */
168 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
169 {
170    Plugin *plugin;
171    int i = 0;
172    int len;
173    char *p;
174    char *cmd = ff_pkt->top_fname;
175    struct save_pkt sp;
176    bEvent event;
177
178    if (!plugin_list || !jcr->plugin_ctx_list) {
179       return 1;                            /* Return if no plugins loaded */
180    }
181
182    jcr->cmd_plugin = true;
183    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
184    event.eventType = bEventBackupCommand;
185
186    /* Handle plugin command here backup */
187    Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
188    if (!(p = strchr(cmd, ':'))) {
189       Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
190       goto bail_out;
191    }
192    len = p - cmd;
193    if (len <= 0) {
194       goto bail_out;
195    }
196
197    /* Note, we stop the loop on the first plugin that matches the name */
198    foreach_alist(plugin, plugin_list) {
199       Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
200       if (strncmp(plugin->file, cmd, len) != 0) {
201          i++;
202          continue;
203       }
204       /* 
205        * We put the current plugin pointer, and the plugin context
206        *  into the jcr, because during save_file(), the plugin
207        *  will be called many times and these values are needed.
208        */
209       jcr->plugin_ctx = &plugin_ctx_list[i];
210       jcr->plugin = plugin;
211       if (is_plugin_disabled(jcr)) {
212          goto bail_out;
213       }
214
215       Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
216       /* Send the backup command to the right plugin*/
217       if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
218          goto bail_out;
219       }
220       /* Loop getting filenames to backup then saving them */
221       while (!job_canceled(jcr)) { 
222          memset(&sp, 0, sizeof(sp));
223          sp.pkt_size = sizeof(sp);
224          sp.pkt_end = sizeof(sp);
225          sp.portable = true;
226          sp.cmd = cmd;
227          Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
228                 &sp);
229          /* Get the file save parameters */
230          if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
231             goto bail_out;
232          }
233          if (sp.type == 0 || sp.fname == NULL) {
234             Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\" returned bad startBackupFile packet.\n"),
235                cmd);
236             goto bail_out;
237          }
238          jcr->plugin_sp = &sp;
239          ff_pkt = jcr->ff;
240          ff_pkt->fname = sp.fname;
241          ff_pkt->link = sp.link;
242          ff_pkt->type = sp.type;
243          memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
244          Dmsg1(dbglvl, "Save_file: file=%s\n", ff_pkt->fname);
245          save_file(jcr, ff_pkt, true);
246          bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
247          if (rc == bRC_More) {
248             continue;
249          }
250          goto bail_out;
251       }
252       goto bail_out;
253    }
254    Jmsg1(jcr, M_ERROR, 0, "Command plugin \"%s\" not found.\n", cmd);
255
256 bail_out:
257    jcr->cmd_plugin = false;
258    jcr->plugin = NULL;
259    jcr->plugin_ctx = NULL;
260    return 1;
261 }
262
263 /* 
264  * Send plugin name start/end record to SD
265  */
266 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
267 {
268    int stat;
269    int index = jcr->JobFiles;
270    struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
271
272    if (!sp) {
273       Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
274       return false;
275    }
276   
277    if (start) {
278       index++;                  /* JobFiles not incremented yet */
279    }
280    Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
281    /* Send stream header */
282    if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
283      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
284            sd->bstrerror());
285      return false;
286    }
287    Dmsg1(50, "send: %s\n", sd->msg);
288
289    if (start) {
290       /* Send data -- not much */
291       stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
292    } else {
293       /* Send end of data */
294       stat = sd->fsend("0 0");
295    }
296    if (!stat) {
297       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
298             sd->bstrerror());
299          return false;
300    }
301    Dmsg1(dbglvl, "send: %s\n", sd->msg);
302    sd->signal(BNET_EOD);            /* indicate end of plugin name data */
303    return true;
304 }
305
306 /*
307  * Plugin name stream found during restore.  The record passed in
308  *  argument name was generated in send_plugin_name() above.
309  *
310  * Returns: true  if start of stream
311  *          false if end of steam
312  */
313 bool plugin_name_stream(JCR *jcr, char *name)    
314 {
315    char *p = name;
316    char *cmd;
317    bool start, portable;
318    Plugin *plugin;
319    int len;
320    int i = 0;
321    bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
322
323    Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
324    skip_nonspaces(&p);             /* skip over jcr->JobFiles */
325    skip_spaces(&p);
326    start = *p == '1';
327    if (start) {
328       /* Start of plugin data */
329       skip_nonspaces(&p);          /* skip start/end flag */
330       skip_spaces(&p);
331       portable = *p == '1';
332       skip_nonspaces(&p);          /* skip portable flag */
333       skip_spaces(&p);
334       cmd = p;
335    } else {
336       /*
337        * End of plugin data, notify plugin, then clear flags   
338        */
339       Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
340       if (jcr->plugin) {
341          plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
342       }
343       jcr->plugin_ctx = NULL;
344       jcr->plugin = NULL;
345       goto bail_out;
346    }
347    if (!plugin_ctx_list) {
348       goto bail_out;
349    }
350       
351    /*
352     * After this point, we are dealing with a restore start
353     */
354
355 // Dmsg1(dbglvl, "plugin restore cmd=%s\n", cmd);
356    if (!(p = strchr(cmd, ':'))) {
357       Jmsg1(jcr, M_ERROR, 0,
358            _("Malformed plugin command. Name not terminated by colon: %s\n"), cmd);
359       goto bail_out;
360    }
361    len = p - cmd;
362    if (len <= 0) {
363       goto bail_out;
364    }
365
366    /*
367     * Search for correct plugin as specified on the command 
368     */
369    foreach_alist(plugin, plugin_list) {
370       bEvent event;
371       Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
372       if (strncmp(plugin->file, cmd, len) != 0) {
373          i++;
374          continue;
375       }
376       jcr->plugin_ctx = &plugin_ctx_list[i];
377       jcr->plugin = plugin;
378       if (is_plugin_disabled(jcr)) {
379          goto bail_out;
380       }
381       Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
382       event.eventType = bEventRestoreCommand;     
383       if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, 
384             &event, cmd) != bRC_OK) {
385          goto bail_out;
386       }
387       /* ***FIXME**** check error code */
388       plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
389       goto bail_out;
390    }
391    Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
392
393 bail_out:
394    return start;
395 }
396
397 /*
398  * Tell the plugin to create the file.  Return values are
399  *
400  *  CF_ERROR    -- error
401  *  CF_SKIP     -- skip processing this file
402  *  CF_EXTRACT  -- extract the file (i.e.call i/o routines)
403  *  CF_CREATED  -- created, but no content to extract (typically directories)
404  *
405  */
406 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
407 {
408    bpContext *plugin_ctx = jcr->plugin_ctx;
409    Plugin *plugin = jcr->plugin;
410    struct restore_pkt rp;
411    int flags;
412    int rc;
413
414    if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr)) {
415       return CF_ERROR;
416    }
417    rp.pkt_size = sizeof(rp);
418    rp.pkt_end = sizeof(rp);
419    rp.stream = attr->stream;
420    rp.data_stream = attr->data_stream;
421    rp.type = attr->type;
422    rp.file_index = attr->file_index;
423    rp.LinkFI = attr->LinkFI;
424    rp.uid = attr->uid;
425    rp.statp = attr->statp;                /* structure assignment */
426    rp.attrEx = attr->attrEx;
427    rp.ofname = attr->ofname;
428    rp.olname = attr->olname;
429    rp.where = jcr->where;
430    rp.RegexWhere = jcr->RegexWhere;
431    rp.replace = jcr->replace;
432    rp.create_status = CF_ERROR;
433    Dmsg1(dbglvl, "call plugin createFile=%s\n", rp.ofname);
434    rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
435    if (rc != bRC_OK) {
436       Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
437             rc, attr->ofname);
438       return CF_ERROR;
439    }
440    if (rp.create_status == CF_ERROR) {
441       Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
442             attr->ofname);
443       return CF_ERROR;
444    }
445    /* Created link or directory? */
446    if (rp.create_status == CF_CREATED) {
447       return rp.create_status;        /* yes, no need to bopen */
448    }
449
450    flags =  O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
451    Dmsg0(dbglvl, "call bopen\n");
452    int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
453    Dmsg1(50, "bopen status=%d\n", stat);
454    if (stat < 0) {
455       berrno be;
456       be.set_errno(bfd->berrno);
457       Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
458             attr->ofname, be.bstrerror());
459       Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
460       return CF_ERROR;
461    }
462
463    if (!is_bopen(bfd)) {
464       Dmsg0(000, "===== BFD is not open!!!!\n");
465    }
466    return CF_EXTRACT;
467 }
468
469 /*
470  * Reset the file attributes after all file I/O is done -- this allows
471  *  the previous access time/dates to be set properly, and it also allows
472  *  us to properly set directory permissions.
473  */
474 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
475 {
476    Dmsg0(dbglvl, "plugin_set_attributes\n");
477    if (is_bopen(ofd)) {
478       bclose(ofd);
479    }
480    pm_strcpy(attr->ofname, "*none*");
481    return true;
482 }
483
484 /*
485  * This entry point is called internally by Bacula to ensure
486  *  that the plugin IO calls come into this code.
487  */
488 void load_fd_plugins(const char *plugin_dir)
489 {
490    Plugin *plugin;
491
492    if (!plugin_dir) {
493       Dmsg0(dbglvl, "plugin dir is NULL\n");
494       return;
495    }
496
497    plugin_list = New(alist(10, not_owned_by_alist));
498    if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type)) {
499       /* Either none found, or some error */
500       if (plugin_list->size() == 0) {
501          delete plugin_list;
502          plugin_list = NULL;
503          Dmsg0(dbglvl, "No plugins loaded\n");
504          return;
505       }
506    }
507
508    /* Plug entry points called from findlib */
509    plugin_bopen  = my_plugin_bopen;
510    plugin_bclose = my_plugin_bclose;
511    plugin_bread  = my_plugin_bread;
512    plugin_bwrite = my_plugin_bwrite;
513    plugin_blseek = my_plugin_blseek;
514    foreach_alist(plugin, plugin_list) {
515       Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
516       Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
517
518    }
519 }
520
521 /*
522  * Create a new instance of each plugin for this Job
523  *   Note, plugin_list can exist but jcr->plugin_ctx_list can
524  *   be NULL if no plugins were loaded.
525  */
526 void new_plugins(JCR *jcr)
527 {
528    Plugin *plugin;
529    int i = 0;
530
531    if (!plugin_list) {
532       Dmsg0(dbglvl, "plugin list is NULL\n");
533       return;
534    }
535
536    int num = plugin_list->size();
537
538    if (num == 0) {
539       Dmsg0(dbglvl, "No plugins loaded\n");
540       return;
541    }
542
543    jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
544
545    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
546    Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
547    foreach_alist(plugin, plugin_list) {
548       /* Start a new instance of each plugin */
549       bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
550       memset(b_ctx, 0, sizeof(bacula_ctx));
551       b_ctx->jcr = jcr;
552       plugin_ctx_list[i].bContext = (void *)b_ctx;   /* Bacula private context */
553       plugin_ctx_list[i].pContext = NULL;
554       if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
555          b_ctx->disabled = true;
556       }
557    }
558 }
559
560 /*
561  * Free the plugin instances for this Job
562  */
563 void free_plugins(JCR *jcr)
564 {
565    Plugin *plugin;
566    int i = 0;
567
568    if (!plugin_list || !jcr->plugin_ctx_list) {
569       return;                         /* no plugins, nothing to do */
570    }
571
572    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
573    Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
574    foreach_alist(plugin, plugin_list) {   
575       /* Free the plugin instance */
576       plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
577       free(plugin_ctx_list[i++].bContext);     /* free Bacula private context */
578    }
579    free(plugin_ctx_list);
580    jcr->plugin_ctx_list = NULL;
581 }
582
583 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
584 {
585    JCR *jcr = bfd->jcr;
586    Plugin *plugin = (Plugin *)jcr->plugin;
587    struct io_pkt io;
588
589    Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
590    io.pkt_size = sizeof(io);
591    io.pkt_end = sizeof(io);
592    io.func = IO_OPEN;
593    io.count = 0;
594    io.buf = NULL;
595    io.fname = fname;
596    io.flags = flags;
597    io.mode = mode;
598    io.win32 = false;
599    io.lerror = 0;
600    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
601    bfd->berrno = io.io_errno;
602    if (io.win32) {
603       errno = b_errno_win32;
604    } else {
605       errno = io.io_errno;
606       bfd->lerror = io.lerror;
607    }
608    Dmsg1(50, "Return from plugin open status=%d\n", io.status);
609    return io.status;
610 }
611
612 static int my_plugin_bclose(BFILE *bfd)
613 {
614    JCR *jcr = bfd->jcr;
615    Plugin *plugin = (Plugin *)jcr->plugin;
616    struct io_pkt io;
617    Dmsg0(dbglvl, "===== plugin_bclose\n");
618    io.pkt_size = sizeof(io);
619    io.pkt_end = sizeof(io);
620    io.func = IO_CLOSE;
621    io.count = 0;
622    io.buf = NULL;
623    io.win32 = false;
624    io.lerror = 0;
625    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
626    bfd->berrno = io.io_errno;
627    if (io.win32) {
628       errno = b_errno_win32;
629    } else {
630       errno = io.io_errno;
631       bfd->lerror = io.lerror;
632    }
633    Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
634    return io.status;
635 }
636
637 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
638 {
639    JCR *jcr = bfd->jcr;
640    Plugin *plugin = (Plugin *)jcr->plugin;
641    struct io_pkt io;
642    Dmsg0(dbglvl, "plugin_bread\n");
643    io.pkt_size = sizeof(io);
644    io.pkt_end = sizeof(io);
645    io.func = IO_READ;
646    io.count = count;
647    io.buf = (char *)buf;
648    io.win32 = false;
649    io.lerror = 0;
650    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
651    bfd->berrno = io.io_errno;
652    if (io.win32) {
653       errno = b_errno_win32;
654    } else {
655       errno = io.io_errno;
656       bfd->lerror = io.lerror;
657    }
658    return (ssize_t)io.status;
659 }
660
661 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
662 {
663    JCR *jcr = bfd->jcr;
664    Plugin *plugin = (Plugin *)jcr->plugin;
665    struct io_pkt io;
666    Dmsg0(dbglvl, "plugin_bwrite\n");
667    io.pkt_size = sizeof(io);
668    io.pkt_end = sizeof(io);
669    io.func = IO_WRITE;
670    io.count = count;
671    io.buf = (char *)buf;
672    io.win32 = false;
673    io.lerror = 0;
674    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
675    bfd->berrno = io.io_errno;
676    if (io.win32) {
677       errno = b_errno_win32;
678    } else {
679       errno = io.io_errno;
680       bfd->lerror = io.lerror;
681    }
682    return (ssize_t)io.status;
683 }
684
685 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
686 {
687    JCR *jcr = bfd->jcr;
688    Plugin *plugin = (Plugin *)jcr->plugin;
689    struct io_pkt io;
690    Dmsg0(dbglvl, "plugin_bseek\n");
691    io.pkt_size = sizeof(io);
692    io.pkt_end = sizeof(io);
693    io.func = IO_SEEK;
694    io.offset = offset;
695    io.whence = whence;
696    io.win32 = false;
697    io.lerror = 0;
698    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
699    bfd->berrno = io.io_errno;
700    if (io.win32) {
701       errno = b_errno_win32;
702    } else {
703       errno = io.io_errno;
704       bfd->lerror = io.lerror;
705    }
706    return (boffset_t)io.offset;
707 }
708
709 /* ==============================================================
710  *
711  * Callbacks from the plugin
712  *
713  * ==============================================================
714  */
715 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
716 {
717    JCR *jcr;
718    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
719 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
720    if (!value || !ctx) {
721       return bRC_Error;
722    }
723    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
724    if (!jcr) {
725       return bRC_Error;
726    }
727 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
728    switch (var) {
729    case bVarJobId:
730       *((int *)value) = jcr->JobId;
731       Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
732       break;
733    case bVarFDName:
734       *((char **)value) = my_name;
735       Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
736       break;
737    case bVarLevel:
738    case bVarType:
739    case bVarClient:
740    case bVarJobName:
741    case bVarJobStatus:
742    case bVarSinceTime:
743       break;
744    }
745    return bRC_OK;
746 }
747
748 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
749 {
750    if (!value || !ctx) {
751       return bRC_Error;
752    }
753    Dmsg1(dbglvl, "bacula: baculaSetValue var=%d\n", var);
754    return bRC_OK;
755 }
756
757 static bRC baculaRegisterEvents(bpContext *ctx, ...)
758 {
759    va_list args;
760    uint32_t event;
761
762    if (!ctx) {
763       return bRC_Error;
764    }
765
766    va_start(args, ctx);
767    while ((event = va_arg(args, uint32_t))) {
768       Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
769    }
770    va_end(args);
771    return bRC_OK;
772 }
773
774 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
775   int type, time_t mtime, const char *fmt, ...)
776 {
777    va_list arg_ptr;
778    char buf[2000];
779    JCR *jcr;
780
781    if (ctx) {
782       jcr = ((bacula_ctx *)ctx->bContext)->jcr;
783    } else {
784       jcr = NULL;
785    }
786
787    va_start(arg_ptr, fmt);
788    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
789    va_end(arg_ptr);
790    Jmsg(jcr, type, mtime, "%s", buf);
791    return bRC_OK;
792 }
793
794 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
795   int level, const char *fmt, ...)
796 {
797    va_list arg_ptr;
798    char buf[2000];
799
800    va_start(arg_ptr, fmt);
801    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
802    va_end(arg_ptr);
803    d_msg(file, line, level, "%s", buf);
804    return bRC_OK;
805 }
806
807 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
808               size_t size)
809 {
810 #ifdef SMARTALLOC
811    return sm_malloc(file, line, size);
812 #else
813    return malloc(size);
814 #endif
815 }
816
817 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
818 {
819 #ifdef SMARTALLOC
820    sm_free(file, line, mem);
821 #else
822    free(mem);
823 #endif
824 }
825
826
827
828 #ifdef TEST_PROGRAM
829
830 int     (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
831 int     (*plugin_bclose)(JCR *jcr) = NULL;
832 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
833 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
834 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
835
836 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
837 {
838    return 0;
839 }
840
841 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
842 {
843    return true;
844 }
845
846 int main(int argc, char *argv[])
847 {
848    char plugin_dir[1000];
849    JCR mjcr1, mjcr2;
850    JCR *jcr1 = &mjcr1;
851    JCR *jcr2 = &mjcr2;
852
853    strcpy(my_name, "test-fd");
854     
855    getcwd(plugin_dir, sizeof(plugin_dir)-1);
856    load_fd_plugins(plugin_dir);
857
858    jcr1->JobId = 111;
859    new_plugins(jcr1);
860
861    jcr2->JobId = 222;
862    new_plugins(jcr2);
863
864    generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
865    generate_plugin_event(jcr1, bEventJobEnd);
866    generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
867    free_plugins(jcr1);
868    generate_plugin_event(jcr2, bEventJobEnd);
869    free_plugins(jcr2);
870
871    unload_plugins();
872
873    Dmsg0(dbglvl, "bacula: OK ...\n");
874    close_memory_pool();
875    sm_dump(false);
876    return 0;
877 }
878
879 #endif /* TEST_PROGRAM */