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