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