]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/fd_plugins.c
Restore object now sent to FD
[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
485    rp.pkt_size = sizeof(rp);
486    rp.pkt_end = sizeof(rp);
487    rp.stream = attr->stream;
488    rp.data_stream = attr->data_stream;
489    rp.type = attr->type;
490    rp.file_index = attr->file_index;
491    rp.LinkFI = attr->LinkFI;
492    rp.uid = attr->uid;
493    rp.statp = attr->statp;                /* structure assignment */
494    rp.attrEx = attr->attrEx;
495    rp.ofname = attr->ofname;
496    rp.olname = attr->olname;
497    rp.where = jcr->where;
498    rp.RegexWhere = jcr->RegexWhere;
499    rp.replace = jcr->replace;
500    rp.create_status = CF_ERROR;
501    Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n", 
502          rp.stream, rp.type, rp.LinkFI, rp.ofname);
503    if (rp.attrEx) {
504       Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
505    }
506    rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
507    if (rc != bRC_OK) {
508       Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
509             rc, attr->ofname);
510       return CF_ERROR;
511    }
512    if (rp.create_status == CF_ERROR) {
513       Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
514             attr->ofname);
515       return CF_ERROR;
516    }
517    /* Created link or directory? */
518    if (rp.create_status == CF_CREATED) {
519       return rp.create_status;        /* yes, no need to bopen */
520    }
521
522    flags =  O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
523    Dmsg0(dbglvl, "call bopen\n");
524    int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
525    Dmsg1(50, "bopen status=%d\n", stat);
526    if (stat < 0) {
527       berrno be;
528       be.set_errno(bfd->berrno);
529       Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
530             attr->ofname, be.bstrerror());
531       Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
532       return CF_ERROR;
533    }
534
535    if (!is_bopen(bfd)) {
536       Dmsg0(000, "===== BFD is not open!!!!\n");
537    }
538    return CF_EXTRACT;
539 }
540
541 /**
542  * Reset the file attributes after all file I/O is done -- this allows
543  *  the previous access time/dates to be set properly, and it also allows
544  *  us to properly set directory permissions.
545  *  Not currently Implemented.
546  */
547 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
548 {
549    Dmsg0(dbglvl, "plugin_set_attributes\n");
550    if (is_bopen(ofd)) {
551       bclose(ofd);
552    }
553    pm_strcpy(attr->ofname, "*none*");
554    return true;
555 }
556
557 /*
558  * Print to file the plugin info.
559  */
560 void dump_fd_plugin(Plugin *plugin, FILE *fp)
561 {
562    if (!plugin) {
563       return ;
564    }
565    pInfo *info = (pInfo *)plugin->pinfo;
566    fprintf(fp, "\tversion=%d\n", info->version);
567    fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
568    fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
569    fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
570    fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
571    fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
572    fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
573 }
574
575 /**
576  * This entry point is called internally by Bacula to ensure
577  *  that the plugin IO calls come into this code.
578  */
579 void load_fd_plugins(const char *plugin_dir)
580 {
581    Plugin *plugin;
582
583    if (!plugin_dir) {
584       Dmsg0(dbglvl, "plugin dir is NULL\n");
585       return;
586    }
587
588    plugin_list = New(alist(10, not_owned_by_alist));
589    if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
590                      is_plugin_compatible)) {
591       /* Either none found, or some error */
592       if (plugin_list->size() == 0) {
593          delete plugin_list;
594          plugin_list = NULL;
595          Dmsg0(dbglvl, "No plugins loaded\n");
596          return;
597       }
598    }
599
600    /* Plug entry points called from findlib */
601    plugin_bopen  = my_plugin_bopen;
602    plugin_bclose = my_plugin_bclose;
603    plugin_bread  = my_plugin_bread;
604    plugin_bwrite = my_plugin_bwrite;
605    plugin_blseek = my_plugin_blseek;
606
607    /* 
608     * Verify that the plugin is acceptable, and print information
609     *  about it.
610     */
611    foreach_alist(plugin, plugin_list) {
612       Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
613       Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
614    }
615
616    dbg_plugin_add_hook(dump_fd_plugin);
617 }
618
619 /**
620  * Check if a plugin is compatible.  Called by the load_plugin function
621  *  to allow us to verify the plugin.
622  */
623 static bool is_plugin_compatible(Plugin *plugin)
624 {
625    pInfo *info = (pInfo *)plugin->pinfo;
626    Dmsg0(50, "is_plugin_compatible called\n");
627    if (debug_level >= 50) {
628       dump_fd_plugin(plugin, stdin);
629    }
630    if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
631       Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
632            plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
633       Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
634            plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
635
636       return false;
637    }
638    if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
639       Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
640            plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
641       Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
642            plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
643       return false;
644    }
645    if (strcmp(info->plugin_license, "Bacula GPLv2") != 0 &&
646        strcmp(info->plugin_license, "GPLv2") != 0) {
647       Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
648            plugin->file, info->plugin_license);
649       Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
650            plugin->file, info->plugin_license);
651       return false;
652    }
653       
654    return true;
655 }
656
657
658 /**
659  * Create a new instance of each plugin for this Job
660  *   Note, plugin_list can exist but jcr->plugin_ctx_list can
661  *   be NULL if no plugins were loaded.
662  */
663 void new_plugins(JCR *jcr)
664 {
665    Plugin *plugin;
666    int i = 0;
667
668    if (!plugin_list) {
669       Dmsg0(dbglvl, "plugin list is NULL\n");
670       return;
671    }
672    if (jcr->is_job_canceled() || jcr->JobId == 0) {
673       return;
674    }
675
676    int num = plugin_list->size();
677
678    if (num == 0) {
679       Dmsg0(dbglvl, "No plugins loaded\n");
680       return;
681    }
682
683    jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
684
685    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
686    Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
687    foreach_alist(plugin, plugin_list) {
688       /* Start a new instance of each plugin */
689       bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
690       memset(b_ctx, 0, sizeof(bacula_ctx));
691       b_ctx->jcr = jcr;
692       plugin_ctx_list[i].bContext = (void *)b_ctx;   /* Bacula private context */
693       plugin_ctx_list[i].pContext = NULL;
694       if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
695          b_ctx->disabled = true;
696       }
697    }
698 }
699
700 /**
701  * Free the plugin instances for this Job
702  */
703 void free_plugins(JCR *jcr)
704 {
705    Plugin *plugin;
706    int i = 0;
707
708    if (!plugin_list || !jcr->plugin_ctx_list) {
709       return;                         /* no plugins, nothing to do */
710    }
711
712    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
713    Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
714    foreach_alist(plugin, plugin_list) {   
715       /* Free the plugin instance */
716       plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
717       free(plugin_ctx_list[i++].bContext);     /* free Bacula private context */
718    }
719    free(plugin_ctx_list);
720    jcr->plugin_ctx_list = NULL;
721 }
722
723 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
724 {
725    JCR *jcr = bfd->jcr;
726    Plugin *plugin = (Plugin *)jcr->plugin;
727    struct io_pkt io;
728
729    Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
730    if (!plugin || !jcr->plugin_ctx) {
731       return 0;
732    }
733    io.pkt_size = sizeof(io);
734    io.pkt_end = sizeof(io);
735    io.func = IO_OPEN;
736    io.count = 0;
737    io.buf = NULL;
738    io.fname = fname;
739    io.flags = flags;
740    io.mode = mode;
741    io.win32 = false;
742    io.lerror = 0;
743    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
744    bfd->berrno = io.io_errno;
745    if (io.win32) {
746       errno = b_errno_win32;
747    } else {
748       errno = io.io_errno;
749       bfd->lerror = io.lerror;
750    }
751    Dmsg1(50, "Return from plugin open status=%d\n", io.status);
752    return io.status;
753 }
754
755 static int my_plugin_bclose(BFILE *bfd)
756 {
757    JCR *jcr = bfd->jcr;
758    Plugin *plugin = (Plugin *)jcr->plugin;
759    struct io_pkt io;
760
761    Dmsg0(dbglvl, "===== plugin_bclose\n");
762    if (!plugin || !jcr->plugin_ctx) {
763       return 0;
764    }
765    io.pkt_size = sizeof(io);
766    io.pkt_end = sizeof(io);
767    io.func = IO_CLOSE;
768    io.count = 0;
769    io.buf = NULL;
770    io.win32 = false;
771    io.lerror = 0;
772    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
773    bfd->berrno = io.io_errno;
774    if (io.win32) {
775       errno = b_errno_win32;
776    } else {
777       errno = io.io_errno;
778       bfd->lerror = io.lerror;
779    }
780    Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
781    return io.status;
782 }
783
784 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
785 {
786    JCR *jcr = bfd->jcr;
787    Plugin *plugin = (Plugin *)jcr->plugin;
788    struct io_pkt io;
789
790    Dmsg0(dbglvl, "plugin_bread\n");
791    if (!plugin || !jcr->plugin_ctx) {
792       return 0;
793    }
794    io.pkt_size = sizeof(io);
795    io.pkt_end = sizeof(io);
796    io.func = IO_READ;
797    io.count = count;
798    io.buf = (char *)buf;
799    io.win32 = false;
800    io.lerror = 0;
801    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
802    bfd->berrno = io.io_errno;
803    if (io.win32) {
804       errno = b_errno_win32;
805    } else {
806       errno = io.io_errno;
807       bfd->lerror = io.lerror;
808    }
809    return (ssize_t)io.status;
810 }
811
812 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
813 {
814    JCR *jcr = bfd->jcr;
815    Plugin *plugin = (Plugin *)jcr->plugin;
816    struct io_pkt io;
817
818    Dmsg0(dbglvl, "plugin_bwrite\n");
819    if (!plugin || !jcr->plugin_ctx) {
820       return 0;
821    }
822    io.pkt_size = sizeof(io);
823    io.pkt_end = sizeof(io);
824    io.func = IO_WRITE;
825    io.count = count;
826    io.buf = (char *)buf;
827    io.win32 = false;
828    io.lerror = 0;
829    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
830    bfd->berrno = io.io_errno;
831    if (io.win32) {
832       errno = b_errno_win32;
833    } else {
834       errno = io.io_errno;
835       bfd->lerror = io.lerror;
836    }
837    return (ssize_t)io.status;
838 }
839
840 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
841 {
842    JCR *jcr = bfd->jcr;
843    Plugin *plugin = (Plugin *)jcr->plugin;
844    struct io_pkt io;
845
846    Dmsg0(dbglvl, "plugin_bseek\n");
847    if (!plugin || !jcr->plugin_ctx) {
848       return 0;
849    }
850    io.pkt_size = sizeof(io);
851    io.pkt_end = sizeof(io);
852    io.func = IO_SEEK;
853    io.offset = offset;
854    io.whence = whence;
855    io.win32 = false;
856    io.lerror = 0;
857    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
858    bfd->berrno = io.io_errno;
859    if (io.win32) {
860       errno = b_errno_win32;
861    } else {
862       errno = io.io_errno;
863       bfd->lerror = io.lerror;
864    }
865    return (boffset_t)io.offset;
866 }
867
868 /* ==============================================================
869  *
870  * Callbacks from the plugin
871  *
872  * ==============================================================
873  */
874 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
875 {
876    JCR *jcr;
877    if (!value || !ctx) {
878       return bRC_Error;
879    }
880 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
881    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
882    if (!jcr) {
883       return bRC_Error;
884    }
885 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
886    switch (var) {
887    case bVarJobId:
888       *((int *)value) = jcr->JobId;
889       Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
890       break;
891    case bVarFDName:
892       *((char **)value) = my_name;
893       Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
894       break;
895    case bVarLevel:
896       *((int *)value) = jcr->getJobLevel();
897       Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
898       break;
899    case bVarType:
900       *((int *)value) = jcr->getJobType();
901       Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
902       break;
903    case bVarClient:
904       *((char **)value) = jcr->client_name;
905       Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
906       break;
907    case bVarJobName:
908       *((char **)value) = jcr->Job;
909       Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
910       break;
911    case bVarJobStatus:
912       *((int *)value) = jcr->JobStatus;
913       Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
914       break;
915    case bVarSinceTime:
916       *((int *)value) = (int)jcr->mtime;
917       Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
918       break;
919    case bVarAccurate:
920       *((int *)value) = (int)jcr->accurate;
921       Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
922       break;
923    case bVarFileSeen:
924       break;                 /* a write only variable, ignore read request */
925    case bVarVssObject:
926 #ifdef HAVE_WIN32
927       if (g_pVSSClient) {
928          *(void **)value = g_pVSSClient->GetVssObject();
929          break;
930        }
931 #endif
932        return bRC_Error;
933    case bVarVssDllHandle:
934 #ifdef HAVE_WIN32
935       if (g_pVSSClient) {
936          *(void **)value = g_pVSSClient->GetVssDllHandle();
937          break;
938        }
939 #endif
940        return bRC_Error;
941    }
942    return bRC_OK;
943 }
944
945 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
946 {
947    JCR *jcr;
948    if (!value || !ctx) {
949       return bRC_Error;
950    }
951 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
952    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
953    if (!jcr) {
954       return bRC_Error;
955    }
956 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
957    switch (var) {
958    case bVarFileSeen:
959       if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
960          return bRC_Error;
961       } 
962       break;
963    default:
964       break;
965    }
966    return bRC_OK;
967 }
968
969 static bRC baculaRegisterEvents(bpContext *ctx, ...)
970 {
971    va_list args;
972    uint32_t event;
973
974    if (!ctx) {
975       return bRC_Error;
976    }
977
978    va_start(args, ctx);
979    while ((event = va_arg(args, uint32_t))) {
980       Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
981    }
982    va_end(args);
983    return bRC_OK;
984 }
985
986 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
987   int type, utime_t mtime, const char *fmt, ...)
988 {
989    va_list arg_ptr;
990    char buf[2000];
991    JCR *jcr;
992
993    if (ctx) {
994       jcr = ((bacula_ctx *)ctx->bContext)->jcr;
995    } else {
996       jcr = NULL;
997    }
998
999    va_start(arg_ptr, fmt);
1000    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1001    va_end(arg_ptr);
1002    Jmsg(jcr, type, mtime, "%s", buf);
1003    return bRC_OK;
1004 }
1005
1006 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1007   int level, const char *fmt, ...)
1008 {
1009    va_list arg_ptr;
1010    char buf[2000];
1011
1012    va_start(arg_ptr, fmt);
1013    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1014    va_end(arg_ptr);
1015    d_msg(file, line, level, "%s", buf);
1016    return bRC_OK;
1017 }
1018
1019 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1020               size_t size)
1021 {
1022 #ifdef SMARTALLOC
1023    return sm_malloc(file, line, size);
1024 #else
1025    return malloc(size);
1026 #endif
1027 }
1028
1029 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1030 {
1031 #ifdef SMARTALLOC
1032    sm_free(file, line, mem);
1033 #else
1034    free(mem);
1035 #endif
1036 }
1037
1038 /**
1039  * Let the plugin define files/directories to be excluded
1040  *  from the main backup.
1041  */
1042 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1043 {
1044    JCR *jcr;
1045    bacula_ctx *bctx;
1046    if (!ctx) {
1047       return bRC_Error;
1048    }
1049    if (!file) {
1050       return bRC_Error;
1051    }
1052    bctx = (bacula_ctx *)ctx->bContext;
1053    if (!bctx) {
1054       return bRC_Error;
1055    }
1056    jcr = bctx->jcr;
1057    if (!jcr) {
1058       return bRC_Error;
1059    }
1060    if (!bctx->fileset) {  
1061       bctx->fileset = new_exclude(jcr);
1062    }
1063    add_file_to_fileset(jcr, file, bctx->fileset, true);
1064    return bRC_OK;
1065 }
1066
1067
1068 #ifdef TEST_PROGRAM
1069
1070 int     (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1071 int     (*plugin_bclose)(JCR *jcr) = NULL;
1072 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1073 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1074 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1075
1076 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1077 {
1078    return 0;
1079 }
1080
1081 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1082 {
1083    return true;
1084 }
1085
1086 int main(int argc, char *argv[])
1087 {
1088    char plugin_dir[1000];
1089    JCR mjcr1, mjcr2;
1090    JCR *jcr1 = &mjcr1;
1091    JCR *jcr2 = &mjcr2;
1092
1093    strcpy(my_name, "test-fd");
1094     
1095    getcwd(plugin_dir, sizeof(plugin_dir)-1);
1096    load_fd_plugins(plugin_dir);
1097
1098    jcr1->JobId = 111;
1099    new_plugins(jcr1);
1100
1101    jcr2->JobId = 222;
1102    new_plugins(jcr2);
1103
1104    generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1105    generate_plugin_event(jcr1, bEventJobEnd);
1106    generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1107    free_plugins(jcr1);
1108    generate_plugin_event(jcr2, bEventJobEnd);
1109    free_plugins(jcr2);
1110
1111    unload_plugins();
1112
1113    Dmsg0(dbglvl, "bacula: OK ...\n");
1114    close_memory_pool();
1115    sm_dump(false);
1116    return 0;
1117 }
1118
1119 #endif /* TEST_PROGRAM */