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