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