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