]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/fd_plugins.c
Turn off plugin debug
[bacula/bacula] / bacula / src / filed / fd_plugins.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2010 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, utime_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 static bRC  baculaAddExclude(bpContext *ctx, const char *file);
66 static bool is_plugin_compatible(Plugin *plugin);
67
68 /*
69  * These will be plugged into the global pointer structure for
70  *  the findlib.
71  */
72 static int     my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
73 static int     my_plugin_bclose(BFILE *bfd);
74 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
75 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
76 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
77
78
79 /* Bacula info */
80 static bInfo binfo = {
81    sizeof(bInfo),
82    FD_PLUGIN_INTERFACE_VERSION 
83 };
84
85 /* Bacula entry points */
86 static bFuncs bfuncs = {
87    sizeof(bFuncs),
88    FD_PLUGIN_INTERFACE_VERSION,
89    baculaRegisterEvents,
90    baculaGetValue,
91    baculaSetValue,
92    baculaJobMsg,
93    baculaDebugMsg,
94    baculaMalloc,
95    baculaFree,
96    baculaAddExclude
97 };
98
99 /* 
100  * Bacula private context
101  */
102 struct bacula_ctx {
103    JCR *jcr;                             /* jcr for plugin */
104    bRC  rc;                              /* last return code */
105    bool disabled;                        /* set if plugin disabled */
106    findFILESET *fileset;                 /* pointer to exclude files */
107 };
108
109 static bool is_plugin_disabled(JCR *jcr)
110 {
111    bacula_ctx *b_ctx;
112    if (!jcr->plugin_ctx) {
113       return true;
114    }
115    b_ctx = (bacula_ctx *)jcr->plugin_ctx->bContext;
116    return b_ctx->disabled;
117 }
118
119
120 /**
121  * Create a plugin event 
122  */
123 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)     
124 {
125    bEvent event;
126    Plugin *plugin;
127    int i = 0;
128
129    if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
130       return;                         /* Return if no plugins loaded */
131    }
132
133    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
134    event.eventType = eventType;
135
136    Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
137
138    /* Pass event to every plugin */
139    foreach_alist(plugin, plugin_list) {
140       bRC rc;
141       jcr->plugin_ctx = &plugin_ctx_list[i++];
142       jcr->plugin = plugin;
143       if (is_plugin_disabled(jcr)) {
144          continue;
145       }
146       rc = plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, value);
147       if (rc != bRC_OK) {
148          break;
149       }
150    }
151
152    jcr->plugin = NULL;
153    jcr->plugin_ctx = NULL;
154    return;
155 }
156
157 /**
158  * Check if file was seen for accurate
159  */
160 bool plugin_check_file(JCR *jcr, char *fname)
161 {
162    Plugin *plugin;
163    int rc = bRC_OK;
164    int i = 0;
165
166    if (!plugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
167       return false;                      /* Return if no plugins loaded */
168    }
169
170    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
171
172    Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
173
174    /* Pass event to every plugin */
175    foreach_alist(plugin, plugin_list) {
176       jcr->plugin_ctx = &plugin_ctx_list[i++];
177       jcr->plugin = plugin;
178       if (is_plugin_disabled(jcr)) {
179          continue;
180       }
181       if (plug_func(plugin)->checkFile == NULL) {
182          continue;
183       }
184       rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
185       if (rc == bRC_Seen) {
186          break;
187       }
188    }
189
190    jcr->plugin = NULL;
191    jcr->plugin_ctx = NULL;
192    return rc == bRC_Seen;
193 }
194
195
196 /**  
197  * Sequence of calls for a backup:
198  * 1. plugin_save() here is called with ff_pkt
199  * 2. we find the plugin requested on the command string
200  * 3. we generate a bEventBackupCommand event to the specified plugin
201  *    and pass it the command string.
202  * 4. we make a startPluginBackup call to the plugin, which gives
203  *    us the data we need in save_pkt
204  * 5. we call Bacula's save_file() subroutine to save the specified
205  *    file.  The plugin will be called at pluginIO() to supply the
206  *    file data.
207  *
208  * Sequence of calls for restore:
209  *   See subroutine plugin_name_stream() below.
210  */
211 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
212 {
213    Plugin *plugin;
214    int i = 0;
215    int len;
216    char *p;
217    char *cmd = ff_pkt->top_fname;
218    struct save_pkt sp;
219    bEvent event;
220    POOL_MEM fname(PM_FNAME);
221    POOL_MEM link(PM_FNAME);
222
223    if (!plugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
224       Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not loaded.\n", cmd);
225       return 1;                            /* Return if no plugins loaded */
226    }
227
228    jcr->cmd_plugin = true;
229    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
230    event.eventType = bEventBackupCommand;
231
232    /* Handle plugin command here backup */
233    Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
234    if (!(p = strchr(cmd, ':'))) {
235       Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
236       goto bail_out;
237    }
238    len = p - cmd;
239    if (len <= 0) {
240       goto bail_out;
241    }
242
243    /* Note, we stop the loop on the first plugin that matches the name */
244    foreach_alist(plugin, plugin_list) {
245       Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
246       if (strncmp(plugin->file, cmd, len) != 0) {
247          i++;
248          continue;
249       }
250       /* 
251        * We put the current plugin pointer, and the plugin context
252        *  into the jcr, because during save_file(), the plugin
253        *  will be called many times and these values are needed.
254        */
255       jcr->plugin_ctx = &plugin_ctx_list[i];
256       jcr->plugin = plugin;
257       if (is_plugin_disabled(jcr)) {
258          goto bail_out;
259       }
260
261       Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
262       /* Send the backup command to the right plugin*/
263       if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
264          goto bail_out;
265       }
266       /* Loop getting filenames to backup then saving them */
267       while (!jcr->is_job_canceled()) { 
268          memset(&sp, 0, sizeof(sp));
269          sp.pkt_size = sizeof(sp);
270          sp.pkt_end = sizeof(sp);
271          sp.portable = true;
272          sp.cmd = cmd;
273          Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
274                 &sp);
275          /* Get the file save parameters. I.e. the stat pkt ... */
276          if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
277             goto bail_out;
278          }
279          if (sp.type == 0 || sp.fname == NULL) {
280             Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\" returned bad startBackupFile packet.\n"),
281                cmd);
282             goto bail_out;
283          }
284          jcr->plugin_sp = &sp;
285          ff_pkt = jcr->ff;
286          /*
287           * Copy fname and link because save_file() zaps them.  This 
288           *  avoids zaping the plugin's strings.
289           */
290          pm_strcpy(fname, sp.fname);
291          pm_strcpy(link, sp.link);
292          ff_pkt->fname = fname.c_str();
293          ff_pkt->link = link.c_str();
294          ff_pkt->type = sp.type;
295          ff_pkt->object = sp.object;
296          ff_pkt->object_len = sp.object_len;
297          if (sp.type == FT_RESTORE_FIRST) {
298             ff_pkt->LinkFI = sp.index;     /* restore object index */
299          }
300          memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
301          Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
302          if (sp.object) {
303             Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
304          }   
305          save_file(jcr, ff_pkt, true);
306          bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
307          if (rc == bRC_More || rc == bRC_OK) {
308             accurate_mark_file_as_seen(jcr, fname.c_str());
309          }
310          if (rc == bRC_More) {
311             continue;
312          }
313          goto bail_out;
314       }
315       goto bail_out;
316    }
317    Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
318
319 bail_out:
320    jcr->cmd_plugin = false;
321    jcr->plugin = NULL;
322    jcr->plugin_ctx = NULL;
323    return 1;
324 }
325
326 /**
327  * Send plugin name start/end record to SD
328  */
329 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
330 {
331    int stat;
332    int index = jcr->JobFiles;
333    struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
334
335    if (!sp) {
336       Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
337       return false;
338    }
339    if (jcr->is_job_canceled()) {
340       return false;
341    }
342   
343    if (start) {
344       index++;                  /* JobFiles not incremented yet */
345    }
346    Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
347    /* Send stream header */
348    if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
349      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
350            sd->bstrerror());
351      return false;
352    }
353    Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
354
355    if (start) {
356       /* Send data -- not much */
357       stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
358    } else {
359       /* Send end of data */
360       stat = sd->fsend("0 0");
361    }
362    if (!stat) {
363       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
364             sd->bstrerror());
365          return false;
366    }
367    Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
368    sd->signal(BNET_EOD);            /* indicate end of plugin name data */
369    return true;
370 }
371
372 /**
373  * Plugin name stream found during restore.  The record passed in
374  *  argument name was generated in send_plugin_name() above.
375  *
376  * Returns: true  if start of stream
377  *          false if end of steam
378  */
379 bool plugin_name_stream(JCR *jcr, char *name)    
380 {
381    char *p = name;
382    char *cmd;
383    bool start, portable;
384    Plugin *plugin;
385    int len;
386    int i = 0;
387    bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
388
389    Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
390    skip_nonspaces(&p);             /* skip over jcr->JobFiles */
391    skip_spaces(&p);
392    start = *p == '1';
393    if (start) {
394       /* Start of plugin data */
395       skip_nonspaces(&p);          /* skip start/end flag */
396       skip_spaces(&p);
397       portable = *p == '1';
398       skip_nonspaces(&p);          /* skip portable flag */
399       skip_spaces(&p);
400       cmd = p;
401    } else {
402       /*
403        * End of plugin data, notify plugin, then clear flags   
404        */
405       Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
406       if (jcr->plugin) {
407          plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
408       }
409       jcr->plugin_ctx = NULL;
410       jcr->plugin = NULL;
411       goto bail_out;
412    }
413    if (!plugin_ctx_list) {
414       goto bail_out;
415    }
416       
417    /*
418     * After this point, we are dealing with a restore start
419     */
420
421 // Dmsg1(dbglvl, "plugin restore cmd=%s\n", cmd);
422    if (!(p = strchr(cmd, ':'))) {
423       Jmsg1(jcr, M_ERROR, 0,
424            _("Malformed plugin command. Name not terminated by colon: %s\n"), cmd);
425       goto bail_out;
426    }
427    len = p - cmd;
428    if (len <= 0) {
429       goto bail_out;
430    }
431
432    /*
433     * Search for correct plugin as specified on the command 
434     */
435    foreach_alist(plugin, plugin_list) {
436       bEvent event;
437       Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
438       if (strncmp(plugin->file, cmd, len) != 0) {
439          i++;
440          continue;
441       }
442       jcr->plugin_ctx = &plugin_ctx_list[i];
443       jcr->plugin = plugin;
444       if (is_plugin_disabled(jcr)) {
445          goto bail_out;
446       }
447       Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
448       event.eventType = bEventRestoreCommand;     
449       if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, 
450             &event, cmd) != bRC_OK) {
451          goto bail_out;
452       }
453       /* ***FIXME**** check error code */
454       plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
455       goto bail_out;
456    }
457    Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
458
459 bail_out:
460    return start;
461 }
462
463 /**
464  * Tell the plugin to create the file.  Return values are
465  *   This is called only during Restore
466  *
467  *  CF_ERROR    -- error
468  *  CF_SKIP     -- skip processing this file
469  *  CF_EXTRACT  -- extract the file (i.e.call i/o routines)
470  *  CF_CREATED  -- created, but no content to extract (typically directories)
471  *
472  */
473 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
474 {
475    bpContext *plugin_ctx = jcr->plugin_ctx;
476    Plugin *plugin = jcr->plugin;
477    struct restore_pkt rp;
478    int flags;
479    int rc;
480
481    if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
482       return CF_ERROR;
483    }
484    rp.pkt_size = sizeof(rp);
485    rp.pkt_end = sizeof(rp);
486    rp.stream = attr->stream;
487    rp.data_stream = attr->data_stream;
488    rp.type = attr->type;
489    rp.file_index = attr->file_index;
490    rp.LinkFI = attr->LinkFI;
491    rp.uid = attr->uid;
492    rp.statp = attr->statp;                /* structure assignment */
493    rp.attrEx = attr->attrEx;
494    rp.ofname = attr->ofname;
495    rp.olname = attr->olname;
496    rp.where = jcr->where;
497    rp.RegexWhere = jcr->RegexWhere;
498    rp.replace = jcr->replace;
499    rp.create_status = CF_ERROR;
500    Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n", 
501          rp.stream, rp.type, rp.LinkFI, rp.ofname);
502    if (rp.attrEx) {
503       Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
504    }
505    rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
506    if (rc != bRC_OK) {
507       Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
508             rc, attr->ofname);
509       return CF_ERROR;
510    }
511    if (rp.create_status == CF_ERROR) {
512       Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
513             attr->ofname);
514       return CF_ERROR;
515    }
516    /* Created link or directory? */
517    if (rp.create_status == CF_CREATED) {
518       return rp.create_status;        /* yes, no need to bopen */
519    }
520    if (rp.type == FT_RESTORE_FIRST) {
521       return CF_CREATED;
522    }
523
524    flags =  O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
525    Dmsg0(dbglvl, "call bopen\n");
526    int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
527    Dmsg1(50, "bopen status=%d\n", stat);
528    if (stat < 0) {
529       berrno be;
530       be.set_errno(bfd->berrno);
531       Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
532             attr->ofname, be.bstrerror());
533       Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
534       return CF_ERROR;
535    }
536
537    if (!is_bopen(bfd)) {
538       Dmsg0(000, "===== BFD is not open!!!!\n");
539    }
540    return CF_EXTRACT;
541 }
542
543 /**
544  * Reset the file attributes after all file I/O is done -- this allows
545  *  the previous access time/dates to be set properly, and it also allows
546  *  us to properly set directory permissions.
547  *  Not currently Implemented.
548  */
549 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
550 {
551    Dmsg0(dbglvl, "plugin_set_attributes\n");
552    if (is_bopen(ofd)) {
553       bclose(ofd);
554    }
555    pm_strcpy(attr->ofname, "*none*");
556    return true;
557 }
558
559 /*
560  * Print to file the plugin info.
561  */
562 void dump_fd_plugin(Plugin *plugin, FILE *fp)
563 {
564    if (!plugin) {
565       return ;
566    }
567    pInfo *info = (pInfo *)plugin->pinfo;
568    fprintf(fp, "\tversion=%d\n", info->version);
569    fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
570    fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
571    fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
572    fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
573    fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
574    fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
575 }
576
577 /**
578  * This entry point is called internally by Bacula to ensure
579  *  that the plugin IO calls come into this code.
580  */
581 void load_fd_plugins(const char *plugin_dir)
582 {
583    Plugin *plugin;
584
585    if (!plugin_dir) {
586       Dmsg0(dbglvl, "plugin dir is NULL\n");
587       return;
588    }
589
590    plugin_list = New(alist(10, not_owned_by_alist));
591    if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
592                      is_plugin_compatible)) {
593       /* Either none found, or some error */
594       if (plugin_list->size() == 0) {
595          delete plugin_list;
596          plugin_list = NULL;
597          Dmsg0(dbglvl, "No plugins loaded\n");
598          return;
599       }
600    }
601
602    /* Plug entry points called from findlib */
603    plugin_bopen  = my_plugin_bopen;
604    plugin_bclose = my_plugin_bclose;
605    plugin_bread  = my_plugin_bread;
606    plugin_bwrite = my_plugin_bwrite;
607    plugin_blseek = my_plugin_blseek;
608
609    /* 
610     * Verify that the plugin is acceptable, and print information
611     *  about it.
612     */
613    foreach_alist(plugin, plugin_list) {
614       Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
615       Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
616    }
617
618    dbg_plugin_add_hook(dump_fd_plugin);
619 }
620
621 /**
622  * Check if a plugin is compatible.  Called by the load_plugin function
623  *  to allow us to verify the plugin.
624  */
625 static bool is_plugin_compatible(Plugin *plugin)
626 {
627    pInfo *info = (pInfo *)plugin->pinfo;
628    Dmsg0(50, "is_plugin_compatible called\n");
629    if (debug_level >= 50) {
630       dump_fd_plugin(plugin, stdin);
631    }
632    if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
633       Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
634            plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
635       Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
636            plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
637
638       return false;
639    }
640    if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
641       Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
642            plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
643       Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
644            plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
645       return false;
646    }
647    if (strcmp(info->plugin_license, "Bacula GPLv2") != 0 &&
648        strcmp(info->plugin_license, "GPLv2") != 0) {
649       Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
650            plugin->file, info->plugin_license);
651       Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
652            plugin->file, info->plugin_license);
653       return false;
654    }
655       
656    return true;
657 }
658
659
660 /**
661  * Create a new instance of each plugin for this Job
662  *   Note, plugin_list can exist but jcr->plugin_ctx_list can
663  *   be NULL if no plugins were loaded.
664  */
665 void new_plugins(JCR *jcr)
666 {
667    Plugin *plugin;
668    int i = 0;
669
670    if (!plugin_list) {
671       Dmsg0(dbglvl, "plugin list is NULL\n");
672       return;
673    }
674    if (jcr->is_job_canceled() || jcr->JobId == 0) {
675       return;
676    }
677
678    int num = plugin_list->size();
679
680    if (num == 0) {
681       Dmsg0(dbglvl, "No plugins loaded\n");
682       return;
683    }
684
685    jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
686
687    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
688    Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
689    foreach_alist(plugin, plugin_list) {
690       /* Start a new instance of each plugin */
691       bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
692       memset(b_ctx, 0, sizeof(bacula_ctx));
693       b_ctx->jcr = jcr;
694       plugin_ctx_list[i].bContext = (void *)b_ctx;   /* Bacula private context */
695       plugin_ctx_list[i].pContext = NULL;
696       if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
697          b_ctx->disabled = true;
698       }
699    }
700 }
701
702 /**
703  * Free the plugin instances for this Job
704  */
705 void free_plugins(JCR *jcr)
706 {
707    Plugin *plugin;
708    int i = 0;
709
710    if (!plugin_list || !jcr->plugin_ctx_list) {
711       return;                         /* no plugins, nothing to do */
712    }
713
714    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
715    Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
716    foreach_alist(plugin, plugin_list) {   
717       /* Free the plugin instance */
718       plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
719       free(plugin_ctx_list[i++].bContext);     /* free Bacula private context */
720    }
721    free(plugin_ctx_list);
722    jcr->plugin_ctx_list = NULL;
723 }
724
725 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
726 {
727    JCR *jcr = bfd->jcr;
728    Plugin *plugin = (Plugin *)jcr->plugin;
729    struct io_pkt io;
730
731    Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
732    if (!plugin || !jcr->plugin_ctx) {
733       return 0;
734    }
735    io.pkt_size = sizeof(io);
736    io.pkt_end = sizeof(io);
737    io.func = IO_OPEN;
738    io.count = 0;
739    io.buf = NULL;
740    io.fname = fname;
741    io.flags = flags;
742    io.mode = mode;
743    io.win32 = false;
744    io.lerror = 0;
745    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
746    bfd->berrno = io.io_errno;
747    if (io.win32) {
748       errno = b_errno_win32;
749    } else {
750       errno = io.io_errno;
751       bfd->lerror = io.lerror;
752    }
753    Dmsg1(50, "Return from plugin open status=%d\n", io.status);
754    return io.status;
755 }
756
757 static int my_plugin_bclose(BFILE *bfd)
758 {
759    JCR *jcr = bfd->jcr;
760    Plugin *plugin = (Plugin *)jcr->plugin;
761    struct io_pkt io;
762
763    Dmsg0(dbglvl, "===== plugin_bclose\n");
764    if (!plugin || !jcr->plugin_ctx) {
765       return 0;
766    }
767    io.pkt_size = sizeof(io);
768    io.pkt_end = sizeof(io);
769    io.func = IO_CLOSE;
770    io.count = 0;
771    io.buf = NULL;
772    io.win32 = false;
773    io.lerror = 0;
774    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
775    bfd->berrno = io.io_errno;
776    if (io.win32) {
777       errno = b_errno_win32;
778    } else {
779       errno = io.io_errno;
780       bfd->lerror = io.lerror;
781    }
782    Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
783    return io.status;
784 }
785
786 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
787 {
788    JCR *jcr = bfd->jcr;
789    Plugin *plugin = (Plugin *)jcr->plugin;
790    struct io_pkt io;
791
792    Dmsg0(dbglvl, "plugin_bread\n");
793    if (!plugin || !jcr->plugin_ctx) {
794       return 0;
795    }
796    io.pkt_size = sizeof(io);
797    io.pkt_end = sizeof(io);
798    io.func = IO_READ;
799    io.count = count;
800    io.buf = (char *)buf;
801    io.win32 = false;
802    io.lerror = 0;
803    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
804    bfd->berrno = io.io_errno;
805    if (io.win32) {
806       errno = b_errno_win32;
807    } else {
808       errno = io.io_errno;
809       bfd->lerror = io.lerror;
810    }
811    return (ssize_t)io.status;
812 }
813
814 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
815 {
816    JCR *jcr = bfd->jcr;
817    Plugin *plugin = (Plugin *)jcr->plugin;
818    struct io_pkt io;
819
820    Dmsg0(dbglvl, "plugin_bwrite\n");
821    if (!plugin || !jcr->plugin_ctx) {
822       return 0;
823    }
824    io.pkt_size = sizeof(io);
825    io.pkt_end = sizeof(io);
826    io.func = IO_WRITE;
827    io.count = count;
828    io.buf = (char *)buf;
829    io.win32 = false;
830    io.lerror = 0;
831    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
832    bfd->berrno = io.io_errno;
833    if (io.win32) {
834       errno = b_errno_win32;
835    } else {
836       errno = io.io_errno;
837       bfd->lerror = io.lerror;
838    }
839    return (ssize_t)io.status;
840 }
841
842 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
843 {
844    JCR *jcr = bfd->jcr;
845    Plugin *plugin = (Plugin *)jcr->plugin;
846    struct io_pkt io;
847
848    Dmsg0(dbglvl, "plugin_bseek\n");
849    if (!plugin || !jcr->plugin_ctx) {
850       return 0;
851    }
852    io.pkt_size = sizeof(io);
853    io.pkt_end = sizeof(io);
854    io.func = IO_SEEK;
855    io.offset = offset;
856    io.whence = whence;
857    io.win32 = false;
858    io.lerror = 0;
859    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
860    bfd->berrno = io.io_errno;
861    if (io.win32) {
862       errno = b_errno_win32;
863    } else {
864       errno = io.io_errno;
865       bfd->lerror = io.lerror;
866    }
867    return (boffset_t)io.offset;
868 }
869
870 /* ==============================================================
871  *
872  * Callbacks from the plugin
873  *
874  * ==============================================================
875  */
876 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
877 {
878    JCR *jcr;
879    if (!value || !ctx) {
880       return bRC_Error;
881    }
882 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
883    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
884    if (!jcr) {
885       return bRC_Error;
886    }
887 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
888    switch (var) {
889    case bVarJobId:
890       *((int *)value) = jcr->JobId;
891       Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
892       break;
893    case bVarFDName:
894       *((char **)value) = my_name;
895       Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
896       break;
897    case bVarLevel:
898       *((int *)value) = jcr->getJobLevel();
899       Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
900       break;
901    case bVarType:
902       *((int *)value) = jcr->getJobType();
903       Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
904       break;
905    case bVarClient:
906       *((char **)value) = jcr->client_name;
907       Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
908       break;
909    case bVarJobName:
910       *((char **)value) = jcr->Job;
911       Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
912       break;
913    case bVarJobStatus:
914       *((int *)value) = jcr->JobStatus;
915       Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
916       break;
917    case bVarSinceTime:
918       *((int *)value) = (int)jcr->mtime;
919       Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
920       break;
921    case bVarAccurate:
922       *((int *)value) = (int)jcr->accurate;
923       Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
924       break;
925    case bVarFileSeen:
926       break;                 /* a write only variable, ignore read request */
927    case bVarVssObject:
928 #ifdef HAVE_WIN32
929       if (g_pVSSClient) {
930          *(void **)value = g_pVSSClient->GetVssObject();
931          break;
932        }
933 #endif
934        return bRC_Error;
935    case bVarVssDllHandle:
936 #ifdef HAVE_WIN32
937       if (g_pVSSClient) {
938          *(void **)value = g_pVSSClient->GetVssDllHandle();
939          break;
940        }
941 #endif
942        return bRC_Error;
943    }
944    return bRC_OK;
945 }
946
947 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
948 {
949    JCR *jcr;
950    if (!value || !ctx) {
951       return bRC_Error;
952    }
953 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
954    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
955    if (!jcr) {
956       return bRC_Error;
957    }
958 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
959    switch (var) {
960    case bVarFileSeen:
961       if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
962          return bRC_Error;
963       } 
964       break;
965    default:
966       break;
967    }
968    return bRC_OK;
969 }
970
971 static bRC baculaRegisterEvents(bpContext *ctx, ...)
972 {
973    va_list args;
974    uint32_t event;
975
976    if (!ctx) {
977       return bRC_Error;
978    }
979
980    va_start(args, ctx);
981    while ((event = va_arg(args, uint32_t))) {
982       Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
983    }
984    va_end(args);
985    return bRC_OK;
986 }
987
988 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
989   int type, utime_t mtime, const char *fmt, ...)
990 {
991    va_list arg_ptr;
992    char buf[2000];
993    JCR *jcr;
994
995    if (ctx) {
996       jcr = ((bacula_ctx *)ctx->bContext)->jcr;
997    } else {
998       jcr = NULL;
999    }
1000
1001    va_start(arg_ptr, fmt);
1002    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1003    va_end(arg_ptr);
1004    Jmsg(jcr, type, mtime, "%s", buf);
1005    return bRC_OK;
1006 }
1007
1008 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1009   int level, const char *fmt, ...)
1010 {
1011    va_list arg_ptr;
1012    char buf[2000];
1013
1014    va_start(arg_ptr, fmt);
1015    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1016    va_end(arg_ptr);
1017    d_msg(file, line, level, "%s", buf);
1018    return bRC_OK;
1019 }
1020
1021 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1022               size_t size)
1023 {
1024 #ifdef SMARTALLOC
1025    return sm_malloc(file, line, size);
1026 #else
1027    return malloc(size);
1028 #endif
1029 }
1030
1031 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1032 {
1033 #ifdef SMARTALLOC
1034    sm_free(file, line, mem);
1035 #else
1036    free(mem);
1037 #endif
1038 }
1039
1040 /**
1041  * Let the plugin define files/directories to be excluded
1042  *  from the main backup.
1043  */
1044 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1045 {
1046    JCR *jcr;
1047    bacula_ctx *bctx;
1048    if (!ctx) {
1049       return bRC_Error;
1050    }
1051    if (!file) {
1052       return bRC_Error;
1053    }
1054    bctx = (bacula_ctx *)ctx->bContext;
1055    if (!bctx) {
1056       return bRC_Error;
1057    }
1058    jcr = bctx->jcr;
1059    if (!jcr) {
1060       return bRC_Error;
1061    }
1062    if (!bctx->fileset) {  
1063       bctx->fileset = new_exclude(jcr);
1064    }
1065    add_file_to_fileset(jcr, file, bctx->fileset, true);
1066    return bRC_OK;
1067 }
1068
1069
1070 #ifdef TEST_PROGRAM
1071
1072 int     (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1073 int     (*plugin_bclose)(JCR *jcr) = NULL;
1074 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1075 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1076 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1077
1078 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1079 {
1080    return 0;
1081 }
1082
1083 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1084 {
1085    return true;
1086 }
1087
1088 int main(int argc, char *argv[])
1089 {
1090    char plugin_dir[1000];
1091    JCR mjcr1, mjcr2;
1092    JCR *jcr1 = &mjcr1;
1093    JCR *jcr2 = &mjcr2;
1094
1095    strcpy(my_name, "test-fd");
1096     
1097    getcwd(plugin_dir, sizeof(plugin_dir)-1);
1098    load_fd_plugins(plugin_dir);
1099
1100    jcr1->JobId = 111;
1101    new_plugins(jcr1);
1102
1103    jcr2->JobId = 222;
1104    new_plugins(jcr2);
1105
1106    generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1107    generate_plugin_event(jcr1, bEventJobEnd);
1108    generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1109    free_plugins(jcr1);
1110    generate_plugin_event(jcr2, bEventJobEnd);
1111    free_plugins(jcr2);
1112
1113    unload_plugins();
1114
1115    Dmsg0(dbglvl, "bacula: OK ...\n");
1116    close_memory_pool();
1117    sm_dump(false);
1118    return 0;
1119 }
1120
1121 #endif /* TEST_PROGRAM */