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