]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/fd_plugins.c
Tweak add sm_dump comments in debug code
[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(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\" 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_ERROR) {
574       Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
575             attr->ofname);
576       return CF_ERROR;
577    }
578    /* Created link or directory? */
579    if (rp.create_status == CF_CREATED) {
580       return rp.create_status;        /* yes, no need to bopen */
581    }
582
583    flags =  O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
584    Dmsg0(dbglvl, "call bopen\n");
585    int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
586    Dmsg1(50, "bopen status=%d\n", stat);
587    if (stat < 0) {
588       berrno be;
589       be.set_errno(bfd->berrno);
590       Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
591             attr->ofname, be.bstrerror());
592       Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
593       return CF_ERROR;
594    }
595
596    if (!is_bopen(bfd)) {
597       Dmsg0(000, "===== BFD is not open!!!!\n");
598    }
599    return CF_EXTRACT;
600 }
601
602 /**
603  * Reset the file attributes after all file I/O is done -- this allows
604  *  the previous access time/dates to be set properly, and it also allows
605  *  us to properly set directory permissions.
606  *  Not currently Implemented.
607  */
608 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
609 {
610    Dmsg0(dbglvl, "plugin_set_attributes\n");
611    if (is_bopen(ofd)) {
612       bclose(ofd);
613    }
614    pm_strcpy(attr->ofname, "*none*");
615    return true;
616 }
617
618 /*
619  * Print to file the plugin info.
620  */
621 void dump_fd_plugin(Plugin *plugin, FILE *fp)
622 {
623    if (!plugin) {
624       return ;
625    }
626    pInfo *info = (pInfo *)plugin->pinfo;
627    fprintf(fp, "\tversion=%d\n", info->version);
628    fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
629    fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
630    fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
631    fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
632    fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
633    fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
634 }
635
636 /**
637  * This entry point is called internally by Bacula to ensure
638  *  that the plugin IO calls come into this code.
639  */
640 void load_fd_plugins(const char *plugin_dir)
641 {
642    Plugin *plugin;
643
644    if (!plugin_dir) {
645       Dmsg0(dbglvl, "plugin dir is NULL\n");
646       return;
647    }
648
649    plugin_list = New(alist(10, not_owned_by_alist));
650    if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
651                      is_plugin_compatible)) {
652       /* Either none found, or some error */
653       if (plugin_list->size() == 0) {
654          delete plugin_list;
655          plugin_list = NULL;
656          Dmsg0(dbglvl, "No plugins loaded\n");
657          return;
658       }
659    }
660
661    /* Plug entry points called from findlib */
662    plugin_bopen  = my_plugin_bopen;
663    plugin_bclose = my_plugin_bclose;
664    plugin_bread  = my_plugin_bread;
665    plugin_bwrite = my_plugin_bwrite;
666    plugin_blseek = my_plugin_blseek;
667
668    /* 
669     * Verify that the plugin is acceptable, and print information
670     *  about it.
671     */
672    foreach_alist(plugin, plugin_list) {
673       Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
674       Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
675    }
676
677    dbg_plugin_add_hook(dump_fd_plugin);
678 }
679
680 /**
681  * Check if a plugin is compatible.  Called by the load_plugin function
682  *  to allow us to verify the plugin.
683  */
684 static bool is_plugin_compatible(Plugin *plugin)
685 {
686    pInfo *info = (pInfo *)plugin->pinfo;
687    Dmsg0(50, "is_plugin_compatible called\n");
688    if (debug_level >= 50) {
689       dump_fd_plugin(plugin, stdin);
690    }
691    if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
692       Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
693            plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
694       Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
695            plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
696
697       return false;
698    }
699    if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
700       Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
701            plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
702       Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
703            plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
704       return false;
705    }
706    if (strcmp(info->plugin_license, "Bacula GPLv2") != 0 &&
707        strcmp(info->plugin_license, "GPLv2") != 0) {
708       Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
709            plugin->file, info->plugin_license);
710       Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
711            plugin->file, info->plugin_license);
712       return false;
713    }
714       
715    return true;
716 }
717
718
719 /**
720  * Create a new instance of each plugin for this Job
721  *   Note, plugin_list can exist but jcr->plugin_ctx_list can
722  *   be NULL if no plugins were loaded.
723  */
724 void new_plugins(JCR *jcr)
725 {
726    Plugin *plugin;
727    int i = 0;
728
729    if (!plugin_list) {
730       Dmsg0(dbglvl, "plugin list is NULL\n");
731       return;
732    }
733    if (jcr->is_job_canceled() || jcr->JobId == 0) {
734       return;
735    }
736
737    int num = plugin_list->size();
738
739    if (num == 0) {
740       Dmsg0(dbglvl, "No plugins loaded\n");
741       return;
742    }
743
744    jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
745
746    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
747    Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
748    foreach_alist(plugin, plugin_list) {
749       /* Start a new instance of each plugin */
750       bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
751       memset(b_ctx, 0, sizeof(bacula_ctx));
752       b_ctx->jcr = jcr;
753       plugin_ctx_list[i].bContext = (void *)b_ctx;   /* Bacula private context */
754       plugin_ctx_list[i].pContext = NULL;
755       if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
756          b_ctx->disabled = true;
757       }
758    }
759 }
760
761 /**
762  * Free the plugin instances for this Job
763  */
764 void free_plugins(JCR *jcr)
765 {
766    Plugin *plugin;
767    int i = 0;
768
769    if (!plugin_list || !jcr->plugin_ctx_list) {
770       return;                         /* no plugins, nothing to do */
771    }
772
773    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
774    Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
775    foreach_alist(plugin, plugin_list) {   
776       /* Free the plugin instance */
777       plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
778       free(plugin_ctx_list[i++].bContext);     /* free Bacula private context */
779    }
780    free(plugin_ctx_list);
781    jcr->plugin_ctx_list = NULL;
782 }
783
784 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
785 {
786    JCR *jcr = bfd->jcr;
787    Plugin *plugin = (Plugin *)jcr->plugin;
788    struct io_pkt io;
789
790    Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
791    if (!plugin || !jcr->plugin_ctx) {
792       return 0;
793    }
794    io.pkt_size = sizeof(io);
795    io.pkt_end = sizeof(io);
796    io.func = IO_OPEN;
797    io.count = 0;
798    io.buf = NULL;
799    io.fname = fname;
800    io.flags = flags;
801    io.mode = mode;
802    io.win32 = false;
803    io.lerror = 0;
804    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
805    bfd->berrno = io.io_errno;
806    if (io.win32) {
807       errno = b_errno_win32;
808    } else {
809       errno = io.io_errno;
810       bfd->lerror = io.lerror;
811    }
812    Dmsg1(50, "Return from plugin open status=%d\n", io.status);
813    return io.status;
814 }
815
816 static int my_plugin_bclose(BFILE *bfd)
817 {
818    JCR *jcr = bfd->jcr;
819    Plugin *plugin = (Plugin *)jcr->plugin;
820    struct io_pkt io;
821
822    Dmsg0(dbglvl, "===== plugin_bclose\n");
823    if (!plugin || !jcr->plugin_ctx) {
824       return 0;
825    }
826    io.pkt_size = sizeof(io);
827    io.pkt_end = sizeof(io);
828    io.func = IO_CLOSE;
829    io.count = 0;
830    io.buf = NULL;
831    io.win32 = false;
832    io.lerror = 0;
833    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
834    bfd->berrno = io.io_errno;
835    if (io.win32) {
836       errno = b_errno_win32;
837    } else {
838       errno = io.io_errno;
839       bfd->lerror = io.lerror;
840    }
841    Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
842    return io.status;
843 }
844
845 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
846 {
847    JCR *jcr = bfd->jcr;
848    Plugin *plugin = (Plugin *)jcr->plugin;
849    struct io_pkt io;
850
851    Dmsg0(dbglvl, "plugin_bread\n");
852    if (!plugin || !jcr->plugin_ctx) {
853       return 0;
854    }
855    io.pkt_size = sizeof(io);
856    io.pkt_end = sizeof(io);
857    io.func = IO_READ;
858    io.count = count;
859    io.buf = (char *)buf;
860    io.win32 = false;
861    io.lerror = 0;
862    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
863    bfd->berrno = io.io_errno;
864    if (io.win32) {
865       errno = b_errno_win32;
866    } else {
867       errno = io.io_errno;
868       bfd->lerror = io.lerror;
869    }
870    return (ssize_t)io.status;
871 }
872
873 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
874 {
875    JCR *jcr = bfd->jcr;
876    Plugin *plugin = (Plugin *)jcr->plugin;
877    struct io_pkt io;
878
879    Dmsg0(dbglvl, "plugin_bwrite\n");
880    if (!plugin || !jcr->plugin_ctx) {
881       return 0;
882    }
883    io.pkt_size = sizeof(io);
884    io.pkt_end = sizeof(io);
885    io.func = IO_WRITE;
886    io.count = count;
887    io.buf = (char *)buf;
888    io.win32 = false;
889    io.lerror = 0;
890    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
891    bfd->berrno = io.io_errno;
892    if (io.win32) {
893       errno = b_errno_win32;
894    } else {
895       errno = io.io_errno;
896       bfd->lerror = io.lerror;
897    }
898    return (ssize_t)io.status;
899 }
900
901 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
902 {
903    JCR *jcr = bfd->jcr;
904    Plugin *plugin = (Plugin *)jcr->plugin;
905    struct io_pkt io;
906
907    Dmsg0(dbglvl, "plugin_bseek\n");
908    if (!plugin || !jcr->plugin_ctx) {
909       return 0;
910    }
911    io.pkt_size = sizeof(io);
912    io.pkt_end = sizeof(io);
913    io.func = IO_SEEK;
914    io.offset = offset;
915    io.whence = whence;
916    io.win32 = false;
917    io.lerror = 0;
918    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
919    bfd->berrno = io.io_errno;
920    if (io.win32) {
921       errno = b_errno_win32;
922    } else {
923       errno = io.io_errno;
924       bfd->lerror = io.lerror;
925    }
926    return (boffset_t)io.offset;
927 }
928
929 /* ==============================================================
930  *
931  * Callbacks from the plugin
932  *
933  * ==============================================================
934  */
935 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
936 {
937    JCR *jcr;
938    if (!value || !ctx) {
939       return bRC_Error;
940    }
941 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
942    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
943    if (!jcr) {
944       return bRC_Error;
945    }
946 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
947    switch (var) {
948    case bVarJobId:
949       *((int *)value) = jcr->JobId;
950       Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
951       break;
952    case bVarFDName:
953       *((char **)value) = my_name;
954       Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
955       break;
956    case bVarLevel:
957       *((int *)value) = jcr->getJobLevel();
958       Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
959       break;
960    case bVarType:
961       *((int *)value) = jcr->getJobType();
962       Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
963       break;
964    case bVarClient:
965       *((char **)value) = jcr->client_name;
966       Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
967       break;
968    case bVarJobName:
969       *((char **)value) = jcr->Job;
970       Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
971       break;
972    case bVarJobStatus:
973       *((int *)value) = jcr->JobStatus;
974       Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
975       break;
976    case bVarSinceTime:
977       *((int *)value) = (int)jcr->mtime;
978       Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
979       break;
980    case bVarAccurate:
981       *((int *)value) = (int)jcr->accurate;
982       Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
983       break;
984    case bVarFileSeen:
985       break;                 /* a write only variable, ignore read request */
986    case bVarVssObject:
987 #ifdef HAVE_WIN32
988       if (g_pVSSClient) {
989          *(void **)value = g_pVSSClient->GetVssObject();
990          break;
991        }
992 #endif
993        return bRC_Error;
994    case bVarVssDllHandle:
995 #ifdef HAVE_WIN32
996       if (g_pVSSClient) {
997          *(void **)value = g_pVSSClient->GetVssDllHandle();
998          break;
999        }
1000 #endif
1001        return bRC_Error;
1002    case bVarWorkingDir:
1003       *(void **)value = me->working_directory;
1004       break;
1005    }
1006    return bRC_OK;
1007 }
1008
1009 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1010 {
1011    JCR *jcr;
1012    if (!value || !ctx) {
1013       return bRC_Error;
1014    }
1015 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1016    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1017    if (!jcr) {
1018       return bRC_Error;
1019    }
1020 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
1021    switch (var) {
1022    case bVarFileSeen:
1023       if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1024          return bRC_Error;
1025       } 
1026       break;
1027    default:
1028       break;
1029    }
1030    return bRC_OK;
1031 }
1032
1033 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1034 {
1035    va_list args;
1036    uint32_t event;
1037
1038    if (!ctx) {
1039       return bRC_Error;
1040    }
1041
1042    va_start(args, ctx);
1043    while ((event = va_arg(args, uint32_t))) {
1044       Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1045    }
1046    va_end(args);
1047    return bRC_OK;
1048 }
1049
1050 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1051   int type, utime_t mtime, const char *fmt, ...)
1052 {
1053    va_list arg_ptr;
1054    char buf[2000];
1055    JCR *jcr;
1056
1057    if (ctx) {
1058       jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1059    } else {
1060       jcr = NULL;
1061    }
1062
1063    va_start(arg_ptr, fmt);
1064    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1065    va_end(arg_ptr);
1066    Jmsg(jcr, type, mtime, "%s", buf);
1067    return bRC_OK;
1068 }
1069
1070 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1071   int level, const char *fmt, ...)
1072 {
1073    va_list arg_ptr;
1074    char buf[2000];
1075
1076    va_start(arg_ptr, fmt);
1077    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1078    va_end(arg_ptr);
1079    d_msg(file, line, level, "%s", buf);
1080    return bRC_OK;
1081 }
1082
1083 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1084               size_t size)
1085 {
1086 #ifdef SMARTALLOC
1087    return sm_malloc(file, line, size);
1088 #else
1089    return malloc(size);
1090 #endif
1091 }
1092
1093 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1094 {
1095 #ifdef SMARTALLOC
1096    sm_free(file, line, mem);
1097 #else
1098    free(mem);
1099 #endif
1100 }
1101
1102 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1103 {
1104    if (!ctx) {
1105       return false;
1106    }
1107    bctx = (bacula_ctx *)ctx->bContext;
1108    if (!bctx) {
1109       return false;
1110    }
1111    jcr = bctx->jcr;
1112    if (!jcr) {
1113       return false;
1114    }
1115    return true;
1116 }
1117
1118 /**
1119  * Let the plugin define files/directories to be excluded
1120  *  from the main backup.
1121  */
1122 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1123 {
1124    JCR *jcr;
1125    bacula_ctx *bctx;
1126    if (!is_ctx_good(ctx, jcr, bctx)) {
1127       return bRC_Error;
1128    }
1129    if (!file) {
1130       return bRC_Error;
1131    }
1132    if (!bctx->exclude) {  
1133       bctx->exclude = new_exclude(jcr);
1134       new_options(jcr, bctx->exclude);
1135    }
1136    set_incexe(jcr, bctx->exclude);
1137    add_file_to_fileset(jcr, file, true);
1138    Dmsg1(100, "Add exclude file=%s\n", file);
1139    return bRC_OK;
1140 }
1141
1142 /**
1143  * Let the plugin define files/directories to be excluded
1144  *  from the main backup.
1145  */
1146 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1147 {
1148    JCR *jcr;
1149    bacula_ctx *bctx;
1150    if (!is_ctx_good(ctx, jcr, bctx)) {
1151       return bRC_Error;
1152    }
1153    if (!file) {
1154       return bRC_Error;
1155    }
1156    if (!bctx->include) {  
1157       bctx->include = new_preinclude(jcr);
1158       new_options(jcr, bctx->include);
1159    }
1160    set_incexe(jcr, bctx->include);
1161    add_file_to_fileset(jcr, file, true);
1162    Dmsg1(100, "Add include file=%s\n", file);
1163    return bRC_OK;
1164 }
1165
1166 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1167 {
1168    JCR *jcr;
1169    bacula_ctx *bctx;
1170    if (!is_ctx_good(ctx, jcr, bctx)) {
1171       return bRC_Error;
1172    }
1173    if (!opts) {
1174       return bRC_Error;
1175    }
1176    add_options_to_fileset(jcr, opts);
1177    Dmsg1(1000, "Add options=%s\n", opts);
1178    return bRC_OK;
1179 }
1180
1181 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1182 {
1183    JCR *jcr;
1184    bacula_ctx *bctx;
1185    if (!is_ctx_good(ctx, jcr, bctx)) {
1186       return bRC_Error;
1187    }
1188    if (!item) {
1189       return bRC_Error;
1190    }
1191    add_regex_to_fileset(jcr, item, type);
1192    Dmsg1(100, "Add regex=%s\n", item);
1193    return bRC_OK;
1194 }
1195
1196 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1197 {
1198    JCR *jcr;
1199    bacula_ctx *bctx;
1200    if (!is_ctx_good(ctx, jcr, bctx)) {
1201       return bRC_Error;
1202    }
1203    if (!item) {
1204       return bRC_Error;
1205    }
1206    add_wild_to_fileset(jcr, item, type);
1207    Dmsg1(100, "Add wild=%s\n", item);
1208    return bRC_OK;
1209 }
1210
1211 static bRC baculaNewOptions(bpContext *ctx)
1212 {
1213    JCR *jcr;
1214    bacula_ctx *bctx;
1215    if (!is_ctx_good(ctx, jcr, bctx)) {
1216       return bRC_Error;
1217    }
1218    (void)new_options(jcr, NULL);
1219    return bRC_OK;
1220 }
1221
1222 static bRC baculaNewInclude(bpContext *ctx)
1223 {
1224    JCR *jcr;
1225    bacula_ctx *bctx;
1226    if (!is_ctx_good(ctx, jcr, bctx)) {
1227       return bRC_Error;
1228    }
1229    (void)new_include(jcr);
1230    return bRC_OK;
1231 }
1232
1233
1234 #ifdef TEST_PROGRAM
1235
1236 int     (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1237 int     (*plugin_bclose)(JCR *jcr) = NULL;
1238 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1239 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1240 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1241
1242 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1243 {
1244    return 0;
1245 }
1246
1247 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1248 {
1249    return true;
1250 }
1251
1252 int main(int argc, char *argv[])
1253 {
1254    char plugin_dir[1000];
1255    JCR mjcr1, mjcr2;
1256    JCR *jcr1 = &mjcr1;
1257    JCR *jcr2 = &mjcr2;
1258
1259    strcpy(my_name, "test-fd");
1260     
1261    getcwd(plugin_dir, sizeof(plugin_dir)-1);
1262    load_fd_plugins(plugin_dir);
1263
1264    jcr1->JobId = 111;
1265    new_plugins(jcr1);
1266
1267    jcr2->JobId = 222;
1268    new_plugins(jcr2);
1269
1270    generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1271    generate_plugin_event(jcr1, bEventJobEnd);
1272    generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1273    free_plugins(jcr1);
1274    generate_plugin_event(jcr2, bEventJobEnd);
1275    free_plugins(jcr2);
1276
1277    unload_plugins();
1278
1279    Dmsg0(dbglvl, "bacula: OK ...\n");
1280    close_memory_pool();
1281    sm_dump(false);     /* unit test */
1282    return 0;
1283 }
1284
1285 #endif /* TEST_PROGRAM */