]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/fd_plugins.c
System State plugin first cut
[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 || job_canceled(jcr)) {
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 || job_canceled(jcr)) {
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 || job_canceled(jcr)) {
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 (!job_canceled(jcr)) { 
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 */
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          memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
296          Dmsg1(dbglvl, "Save_file: file=%s\n", fname.c_str());
297          save_file(jcr, ff_pkt, true);
298          bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
299          if (rc == bRC_More || rc == bRC_OK) {
300             accurate_mark_file_as_seen(jcr, fname.c_str());
301          }
302          if (rc == bRC_More) {
303             continue;
304          }
305          goto bail_out;
306       }
307       goto bail_out;
308    }
309    Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
310
311 bail_out:
312    jcr->cmd_plugin = false;
313    jcr->plugin = NULL;
314    jcr->plugin_ctx = NULL;
315    return 1;
316 }
317
318 /**
319  * Send plugin name start/end record to SD
320  */
321 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
322 {
323    int stat;
324    int index = jcr->JobFiles;
325    struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
326
327    if (!sp) {
328       Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
329       return false;
330    }
331    if (job_canceled(jcr)) {
332       return false;
333    }
334   
335    if (start) {
336       index++;                  /* JobFiles not incremented yet */
337    }
338    Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
339    /* Send stream header */
340    if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
341      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
342            sd->bstrerror());
343      return false;
344    }
345    Dmsg1(50, "send: %s\n", sd->msg);
346
347    if (start) {
348       /* Send data -- not much */
349       stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
350    } else {
351       /* Send end of data */
352       stat = sd->fsend("0 0");
353    }
354    if (!stat) {
355       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
356             sd->bstrerror());
357          return false;
358    }
359    Dmsg1(dbglvl, "send: %s\n", sd->msg);
360    sd->signal(BNET_EOD);            /* indicate end of plugin name data */
361    return true;
362 }
363
364 /**
365  * Plugin name stream found during restore.  The record passed in
366  *  argument name was generated in send_plugin_name() above.
367  *
368  * Returns: true  if start of stream
369  *          false if end of steam
370  */
371 bool plugin_name_stream(JCR *jcr, char *name)    
372 {
373    char *p = name;
374    char *cmd;
375    bool start, portable;
376    Plugin *plugin;
377    int len;
378    int i = 0;
379    bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
380
381    Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
382    skip_nonspaces(&p);             /* skip over jcr->JobFiles */
383    skip_spaces(&p);
384    start = *p == '1';
385    if (start) {
386       /* Start of plugin data */
387       skip_nonspaces(&p);          /* skip start/end flag */
388       skip_spaces(&p);
389       portable = *p == '1';
390       skip_nonspaces(&p);          /* skip portable flag */
391       skip_spaces(&p);
392       cmd = p;
393    } else {
394       /*
395        * End of plugin data, notify plugin, then clear flags   
396        */
397       Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
398       if (jcr->plugin) {
399          plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
400       }
401       jcr->plugin_ctx = NULL;
402       jcr->plugin = NULL;
403       goto bail_out;
404    }
405    if (!plugin_ctx_list) {
406       goto bail_out;
407    }
408       
409    /*
410     * After this point, we are dealing with a restore start
411     */
412
413 // Dmsg1(dbglvl, "plugin restore cmd=%s\n", cmd);
414    if (!(p = strchr(cmd, ':'))) {
415       Jmsg1(jcr, M_ERROR, 0,
416            _("Malformed plugin command. Name not terminated by colon: %s\n"), cmd);
417       goto bail_out;
418    }
419    len = p - cmd;
420    if (len <= 0) {
421       goto bail_out;
422    }
423
424    /*
425     * Search for correct plugin as specified on the command 
426     */
427    foreach_alist(plugin, plugin_list) {
428       bEvent event;
429       Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
430       if (strncmp(plugin->file, cmd, len) != 0) {
431          i++;
432          continue;
433       }
434       jcr->plugin_ctx = &plugin_ctx_list[i];
435       jcr->plugin = plugin;
436       if (is_plugin_disabled(jcr)) {
437          goto bail_out;
438       }
439       Dmsg1(dbglvl, "Restore Command plugin = %s\n", cmd);
440       event.eventType = bEventRestoreCommand;     
441       if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, 
442             &event, cmd) != bRC_OK) {
443          goto bail_out;
444       }
445       /* ***FIXME**** check error code */
446       plug_func(plugin)->startRestoreFile((bpContext *)jcr->plugin_ctx, cmd);
447       goto bail_out;
448    }
449    Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
450
451 bail_out:
452    return start;
453 }
454
455 /**
456  * Tell the plugin to create the file.  Return values are
457  *   This is called only during Restore
458  *
459  *  CF_ERROR    -- error
460  *  CF_SKIP     -- skip processing this file
461  *  CF_EXTRACT  -- extract the file (i.e.call i/o routines)
462  *  CF_CREATED  -- created, but no content to extract (typically directories)
463  *
464  */
465 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
466 {
467    bpContext *plugin_ctx = jcr->plugin_ctx;
468    Plugin *plugin = jcr->plugin;
469    struct restore_pkt rp;
470    int flags;
471    int rc;
472
473    if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || job_canceled(jcr)) {
474       return CF_ERROR;
475    }
476    rp.pkt_size = sizeof(rp);
477    rp.pkt_end = sizeof(rp);
478    rp.stream = attr->stream;
479    rp.data_stream = attr->data_stream;
480    rp.type = attr->type;
481    rp.file_index = attr->file_index;
482    rp.LinkFI = attr->LinkFI;
483    rp.uid = attr->uid;
484    rp.statp = attr->statp;                /* structure assignment */
485    rp.attrEx = attr->attrEx;
486    rp.ofname = attr->ofname;
487    rp.olname = attr->olname;
488    rp.where = jcr->where;
489    rp.RegexWhere = jcr->RegexWhere;
490    rp.replace = jcr->replace;
491    rp.create_status = CF_ERROR;
492    Dmsg1(dbglvl, "call plugin createFile=%s\n", rp.ofname);
493    rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
494    if (rc != bRC_OK) {
495       Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
496             rc, attr->ofname);
497       return CF_ERROR;
498    }
499    if (rp.create_status == CF_ERROR) {
500       Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
501             attr->ofname);
502       return CF_ERROR;
503    }
504    /* Created link or directory? */
505    if (rp.create_status == CF_CREATED) {
506       return rp.create_status;        /* yes, no need to bopen */
507    }
508
509    flags =  O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
510    Dmsg0(dbglvl, "call bopen\n");
511    int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
512    Dmsg1(50, "bopen status=%d\n", stat);
513    if (stat < 0) {
514       berrno be;
515       be.set_errno(bfd->berrno);
516       Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
517             attr->ofname, be.bstrerror());
518       Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
519       return CF_ERROR;
520    }
521
522    if (!is_bopen(bfd)) {
523       Dmsg0(000, "===== BFD is not open!!!!\n");
524    }
525    return CF_EXTRACT;
526 }
527
528 /**
529  * Reset the file attributes after all file I/O is done -- this allows
530  *  the previous access time/dates to be set properly, and it also allows
531  *  us to properly set directory permissions.
532  *  Not currently Implemented.
533  */
534 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
535 {
536    Dmsg0(dbglvl, "plugin_set_attributes\n");
537    if (is_bopen(ofd)) {
538       bclose(ofd);
539    }
540    pm_strcpy(attr->ofname, "*none*");
541    return true;
542 }
543
544 /*
545  * Print to file the plugin info.
546  */
547 void dump_fd_plugin(Plugin *plugin, FILE *fp)
548 {
549    if (!plugin) {
550       return ;
551    }
552    pInfo *info = (pInfo *)plugin->pinfo;
553    fprintf(fp, "\tversion=%d\n", info->version);
554    fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
555    fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
556    fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
557    fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
558    fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
559    fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
560 }
561
562 /**
563  * This entry point is called internally by Bacula to ensure
564  *  that the plugin IO calls come into this code.
565  */
566 void load_fd_plugins(const char *plugin_dir)
567 {
568    Plugin *plugin;
569
570    if (!plugin_dir) {
571       Dmsg0(dbglvl, "plugin dir is NULL\n");
572       return;
573    }
574
575    plugin_list = New(alist(10, not_owned_by_alist));
576    if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
577                      is_plugin_compatible)) {
578       /* Either none found, or some error */
579       if (plugin_list->size() == 0) {
580          delete plugin_list;
581          plugin_list = NULL;
582          Dmsg0(dbglvl, "No plugins loaded\n");
583          return;
584       }
585    }
586
587    /* Plug entry points called from findlib */
588    plugin_bopen  = my_plugin_bopen;
589    plugin_bclose = my_plugin_bclose;
590    plugin_bread  = my_plugin_bread;
591    plugin_bwrite = my_plugin_bwrite;
592    plugin_blseek = my_plugin_blseek;
593
594    /* 
595     * Verify that the plugin is acceptable, and print information
596     *  about it.
597     */
598    foreach_alist(plugin, plugin_list) {
599       Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
600       Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
601    }
602
603    dbg_plugin_add_hook(dump_fd_plugin);
604 }
605
606 /**
607  * Check if a plugin is compatible.  Called by the load_plugin function
608  *  to allow us to verify the plugin.
609  */
610 static bool is_plugin_compatible(Plugin *plugin)
611 {
612    pInfo *info = (pInfo *)plugin->pinfo;
613    Dmsg0(50, "is_plugin_compatible called\n");
614    if (debug_level >= 50) {
615       dump_fd_plugin(plugin, stdin);
616    }
617    if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
618       Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
619            plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
620       Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
621            plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
622
623       return false;
624    }
625    if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
626       Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
627            plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
628       Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
629            plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
630       return false;
631    }
632    if (strcmp(info->plugin_license, "Bacula GPLv2") != 0 &&
633        strcmp(info->plugin_license, "GPLv2") != 0) {
634       Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
635            plugin->file, info->plugin_license);
636       Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
637            plugin->file, info->plugin_license);
638       return false;
639    }
640       
641    return true;
642 }
643
644
645 /**
646  * Create a new instance of each plugin for this Job
647  *   Note, plugin_list can exist but jcr->plugin_ctx_list can
648  *   be NULL if no plugins were loaded.
649  */
650 void new_plugins(JCR *jcr)
651 {
652    Plugin *plugin;
653    int i = 0;
654
655    if (!plugin_list) {
656       Dmsg0(dbglvl, "plugin list is NULL\n");
657       return;
658    }
659    if (job_canceled(jcr)) {
660       return;
661    }
662
663    int num = plugin_list->size();
664
665    if (num == 0) {
666       Dmsg0(dbglvl, "No plugins loaded\n");
667       return;
668    }
669
670    jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
671
672    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
673    Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
674    foreach_alist(plugin, plugin_list) {
675       /* Start a new instance of each plugin */
676       bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
677       memset(b_ctx, 0, sizeof(bacula_ctx));
678       b_ctx->jcr = jcr;
679       plugin_ctx_list[i].bContext = (void *)b_ctx;   /* Bacula private context */
680       plugin_ctx_list[i].pContext = NULL;
681       if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
682          b_ctx->disabled = true;
683       }
684    }
685 }
686
687 /**
688  * Free the plugin instances for this Job
689  */
690 void free_plugins(JCR *jcr)
691 {
692    Plugin *plugin;
693    int i = 0;
694
695    if (!plugin_list || !jcr->plugin_ctx_list) {
696       return;                         /* no plugins, nothing to do */
697    }
698
699    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
700    Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
701    foreach_alist(plugin, plugin_list) {   
702       /* Free the plugin instance */
703       plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
704       free(plugin_ctx_list[i++].bContext);     /* free Bacula private context */
705    }
706    free(plugin_ctx_list);
707    jcr->plugin_ctx_list = NULL;
708 }
709
710 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
711 {
712    JCR *jcr = bfd->jcr;
713    Plugin *plugin = (Plugin *)jcr->plugin;
714    struct io_pkt io;
715
716    Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
717    if (!plugin || !jcr->plugin_ctx) {
718       return 0;
719    }
720    io.pkt_size = sizeof(io);
721    io.pkt_end = sizeof(io);
722    io.func = IO_OPEN;
723    io.count = 0;
724    io.buf = NULL;
725    io.fname = fname;
726    io.flags = flags;
727    io.mode = mode;
728    io.win32 = false;
729    io.lerror = 0;
730    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
731    bfd->berrno = io.io_errno;
732    if (io.win32) {
733       errno = b_errno_win32;
734    } else {
735       errno = io.io_errno;
736       bfd->lerror = io.lerror;
737    }
738    Dmsg1(50, "Return from plugin open status=%d\n", io.status);
739    return io.status;
740 }
741
742 static int my_plugin_bclose(BFILE *bfd)
743 {
744    JCR *jcr = bfd->jcr;
745    Plugin *plugin = (Plugin *)jcr->plugin;
746    struct io_pkt io;
747
748    Dmsg0(dbglvl, "===== plugin_bclose\n");
749    if (!plugin || !jcr->plugin_ctx) {
750       return 0;
751    }
752    io.pkt_size = sizeof(io);
753    io.pkt_end = sizeof(io);
754    io.func = IO_CLOSE;
755    io.count = 0;
756    io.buf = NULL;
757    io.win32 = false;
758    io.lerror = 0;
759    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
760    bfd->berrno = io.io_errno;
761    if (io.win32) {
762       errno = b_errno_win32;
763    } else {
764       errno = io.io_errno;
765       bfd->lerror = io.lerror;
766    }
767    Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
768    return io.status;
769 }
770
771 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
772 {
773    JCR *jcr = bfd->jcr;
774    Plugin *plugin = (Plugin *)jcr->plugin;
775    struct io_pkt io;
776
777    Dmsg0(dbglvl, "plugin_bread\n");
778    if (!plugin || !jcr->plugin_ctx) {
779       return 0;
780    }
781    io.pkt_size = sizeof(io);
782    io.pkt_end = sizeof(io);
783    io.func = IO_READ;
784    io.count = count;
785    io.buf = (char *)buf;
786    io.win32 = false;
787    io.lerror = 0;
788    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
789    bfd->berrno = io.io_errno;
790    if (io.win32) {
791       errno = b_errno_win32;
792    } else {
793       errno = io.io_errno;
794       bfd->lerror = io.lerror;
795    }
796    return (ssize_t)io.status;
797 }
798
799 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
800 {
801    JCR *jcr = bfd->jcr;
802    Plugin *plugin = (Plugin *)jcr->plugin;
803    struct io_pkt io;
804
805    Dmsg0(dbglvl, "plugin_bwrite\n");
806    if (!plugin || !jcr->plugin_ctx) {
807       return 0;
808    }
809    io.pkt_size = sizeof(io);
810    io.pkt_end = sizeof(io);
811    io.func = IO_WRITE;
812    io.count = count;
813    io.buf = (char *)buf;
814    io.win32 = false;
815    io.lerror = 0;
816    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
817    bfd->berrno = io.io_errno;
818    if (io.win32) {
819       errno = b_errno_win32;
820    } else {
821       errno = io.io_errno;
822       bfd->lerror = io.lerror;
823    }
824    return (ssize_t)io.status;
825 }
826
827 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
828 {
829    JCR *jcr = bfd->jcr;
830    Plugin *plugin = (Plugin *)jcr->plugin;
831    struct io_pkt io;
832
833    Dmsg0(dbglvl, "plugin_bseek\n");
834    if (!plugin || !jcr->plugin_ctx) {
835       return 0;
836    }
837    io.pkt_size = sizeof(io);
838    io.pkt_end = sizeof(io);
839    io.func = IO_SEEK;
840    io.offset = offset;
841    io.whence = whence;
842    io.win32 = false;
843    io.lerror = 0;
844    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
845    bfd->berrno = io.io_errno;
846    if (io.win32) {
847       errno = b_errno_win32;
848    } else {
849       errno = io.io_errno;
850       bfd->lerror = io.lerror;
851    }
852    return (boffset_t)io.offset;
853 }
854
855 /* ==============================================================
856  *
857  * Callbacks from the plugin
858  *
859  * ==============================================================
860  */
861 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
862 {
863    JCR *jcr;
864    if (!value || !ctx) {
865       return bRC_Error;
866    }
867 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
868    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
869    if (!jcr) {
870       return bRC_Error;
871    }
872 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
873    switch (var) {
874    case bVarJobId:
875       *((int *)value) = jcr->JobId;
876       Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
877       break;
878    case bVarFDName:
879       *((char **)value) = my_name;
880       Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
881       break;
882    case bVarLevel:
883       *((int *)value) = jcr->getJobLevel();
884       Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
885       break;
886    case bVarType:
887       *((int *)value) = jcr->getJobType();
888       Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
889       break;
890    case bVarClient:
891       *((char **)value) = jcr->client_name;
892       Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
893       break;
894    case bVarJobName:
895       *((char **)value) = jcr->Job;
896       Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
897       break;
898    case bVarJobStatus:
899       *((int *)value) = jcr->JobStatus;
900       Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
901       break;
902    case bVarSinceTime:
903       *((int *)value) = (int)jcr->mtime;
904       Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
905       break;
906    case bVarAccurate:
907       *((int *)value) = (int)jcr->accurate;
908       Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
909       break;
910    case bVarFileSeen:
911       break;                 /* a write only variable, ignore read request */
912    case bVarVssObject:
913 #ifdef HAVE_WIN32
914       if (g_pVSSClient) {
915          *(void **)value = g_pVSSClient->GetVssObject();
916          break;
917        }
918 #endif
919        return bRC_Error;
920    }
921    return bRC_OK;
922 }
923
924 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
925 {
926    JCR *jcr;
927    if (!value || !ctx) {
928       return bRC_Error;
929    }
930 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
931    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
932    if (!jcr) {
933       return bRC_Error;
934    }
935 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
936    switch (var) {
937    case bVarFileSeen:
938       if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
939          return bRC_Error;
940       } 
941       break;
942    default:
943       break;
944    }
945    return bRC_OK;
946 }
947
948 static bRC baculaRegisterEvents(bpContext *ctx, ...)
949 {
950    va_list args;
951    uint32_t event;
952
953    if (!ctx) {
954       return bRC_Error;
955    }
956
957    va_start(args, ctx);
958    while ((event = va_arg(args, uint32_t))) {
959       Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
960    }
961    va_end(args);
962    return bRC_OK;
963 }
964
965 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
966   int type, utime_t mtime, const char *fmt, ...)
967 {
968    va_list arg_ptr;
969    char buf[2000];
970    JCR *jcr;
971
972    if (ctx) {
973       jcr = ((bacula_ctx *)ctx->bContext)->jcr;
974    } else {
975       jcr = NULL;
976    }
977
978    va_start(arg_ptr, fmt);
979    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
980    va_end(arg_ptr);
981    Jmsg(jcr, type, mtime, "%s", buf);
982    return bRC_OK;
983 }
984
985 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
986   int level, const char *fmt, ...)
987 {
988    va_list arg_ptr;
989    char buf[2000];
990
991    va_start(arg_ptr, fmt);
992    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
993    va_end(arg_ptr);
994    d_msg(file, line, level, "%s", buf);
995    return bRC_OK;
996 }
997
998 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
999               size_t size)
1000 {
1001 #ifdef SMARTALLOC
1002    return sm_malloc(file, line, size);
1003 #else
1004    return malloc(size);
1005 #endif
1006 }
1007
1008 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1009 {
1010 #ifdef SMARTALLOC
1011    sm_free(file, line, mem);
1012 #else
1013    free(mem);
1014 #endif
1015 }
1016
1017 /**
1018  * Let the plugin define files/directories to be excluded
1019  *  from the main backup.
1020  */
1021 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1022 {
1023    JCR *jcr;
1024    bacula_ctx *bctx;
1025    if (ctx) {
1026       return bRC_Error;
1027    }
1028    if (!file) {
1029       return bRC_Error;
1030    }
1031    bctx = (bacula_ctx *)ctx->bContext;
1032    if (!bctx) {
1033       return bRC_Error;
1034    }
1035    jcr = bctx->jcr;
1036    if (!jcr) {
1037       return bRC_Error;
1038    }
1039    if (!bctx->fileset) {  
1040       bctx->fileset = new_exclude(jcr);
1041    }
1042    add_file_to_fileset(jcr, file, bctx->fileset, true);
1043    return bRC_OK;
1044 }
1045
1046
1047 #ifdef TEST_PROGRAM
1048
1049 int     (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1050 int     (*plugin_bclose)(JCR *jcr) = NULL;
1051 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1052 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1053 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1054
1055 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1056 {
1057    return 0;
1058 }
1059
1060 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1061 {
1062    return true;
1063 }
1064
1065 int main(int argc, char *argv[])
1066 {
1067    char plugin_dir[1000];
1068    JCR mjcr1, mjcr2;
1069    JCR *jcr1 = &mjcr1;
1070    JCR *jcr2 = &mjcr2;
1071
1072    strcpy(my_name, "test-fd");
1073     
1074    getcwd(plugin_dir, sizeof(plugin_dir)-1);
1075    load_fd_plugins(plugin_dir);
1076
1077    jcr1->JobId = 111;
1078    new_plugins(jcr1);
1079
1080    jcr2->JobId = 222;
1081    new_plugins(jcr2);
1082
1083    generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1084    generate_plugin_event(jcr1, bEventJobEnd);
1085    generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1086    free_plugins(jcr1);
1087    generate_plugin_event(jcr2, bEventJobEnd);
1088    free_plugins(jcr2);
1089
1090    unload_plugins();
1091
1092    Dmsg0(dbglvl, "bacula: OK ...\n");
1093    close_memory_pool();
1094    sm_dump(false);
1095    return 0;
1096 }
1097
1098 #endif /* TEST_PROGRAM */