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