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