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