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