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