]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/fd_plugins.c
Fix plugin bug with multiple simultaneous jobs
[bacula/bacula] / bacula / src / filed / fd_plugins.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2011 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 extern DLL_IMP_EXP char *exepath;
39 extern DLL_IMP_EXP char *version;
40 extern DLL_IMP_EXP char *dist_name;
41 extern DLL_IMP_EXP int beef;
42
43 const int dbglvl = 150;
44 #ifdef HAVE_WIN32
45 const char *plugin_type = "-fd.dll";
46 #else
47 const char *plugin_type = "-fd.so";
48 #endif
49
50 extern int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
51 extern bool check_changes(JCR *jcr, FF_PKT *ff_pkt);
52
53 /* Function pointers to be set here */
54 extern DLL_IMP_EXP int     (*plugin_bopen)(BFILE *bfd, const char *fname, int flags, mode_t mode);
55 extern DLL_IMP_EXP int     (*plugin_bclose)(BFILE *bfd);
56 extern DLL_IMP_EXP ssize_t (*plugin_bread)(BFILE *bfd, void *buf, size_t count);
57 extern DLL_IMP_EXP ssize_t (*plugin_bwrite)(BFILE *bfd, void *buf, size_t count);
58 extern DLL_IMP_EXP boffset_t (*plugin_blseek)(BFILE *bfd, boffset_t offset, int whence);
59
60
61 /* Forward referenced functions */
62 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value);
63 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value);
64 static bRC baculaRegisterEvents(bpContext *ctx, ...);
65 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
66   int type, utime_t mtime, const char *fmt, ...);
67 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
68   int level, const char *fmt, ...);
69 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
70               size_t size);
71 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem);
72 static bRC  baculaAddExclude(bpContext *ctx, const char *file);
73 static bRC baculaAddInclude(bpContext *ctx, const char *file);
74 static bRC baculaAddOptions(bpContext *ctx, const char *opts);
75 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type);
76 static bRC baculaAddWild(bpContext *ctx, const char *item, int type);
77 static bRC baculaNewOptions(bpContext *ctx);
78 static bRC baculaNewInclude(bpContext *ctx);
79 static bRC baculaNewPreInclude(bpContext *ctx);
80 static bool is_plugin_compatible(Plugin *plugin);
81 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret);
82 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp);
83
84 /*
85  * These will be plugged into the global pointer structure for
86  *  the findlib.
87  */
88 static int     my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode);
89 static int     my_plugin_bclose(BFILE *bfd);
90 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count);
91 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count);
92 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence);
93
94 #define for_this_plug(plugin, str, len) (((len) == (plugin)->file_len) && strncmp((plugin)->file, str, len) == 0)
95
96 /* Bacula info */
97 static bInfo binfo = {
98    sizeof(bInfo),
99    FD_PLUGIN_INTERFACE_VERSION 
100 };
101
102 /* Bacula entry points */
103 static bFuncs bfuncs = {
104    sizeof(bFuncs),
105    FD_PLUGIN_INTERFACE_VERSION,
106    baculaRegisterEvents,
107    baculaGetValue,
108    baculaSetValue,
109    baculaJobMsg,
110    baculaDebugMsg,
111    baculaMalloc,
112    baculaFree,
113    baculaAddExclude,
114    baculaAddInclude,
115    baculaAddOptions,
116    baculaAddRegex,
117    baculaAddWild,
118    baculaNewOptions,
119    baculaNewInclude,
120    baculaNewPreInclude,
121    baculaCheckChanges
122 };
123
124 /* 
125  * Bacula private context
126  */
127 struct bacula_ctx {
128    JCR *jcr;                             /* jcr for plugin */
129    bRC  rc;                              /* last return code */
130    bool disabled;                        /* set if plugin disabled */
131    findINCEXE *exclude;                  /* pointer to exclude files */
132    findINCEXE *include;                  /* pointer to include/exclude files */
133 };
134
135 static bool is_plugin_disabled(bpContext *plugin_ctx)
136 {
137    bacula_ctx *b_ctx;
138    if (!plugin_ctx) {
139       return true;
140    }
141    b_ctx = (bacula_ctx *)plugin_ctx->bContext;
142    if (!b_ctx) {
143       return true;
144    }
145    return b_ctx->disabled;
146 }
147
148 static bool is_plugin_disabled(JCR *jcr)
149 {
150    return is_plugin_disabled(jcr->plugin_ctx);
151 }
152
153 /**
154  * Create a plugin event When receiving bEventCancelCommand, this function is
155  * called by an other thread.
156  */
157 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)     
158 {
159    bpContext *plugin_ctx;
160    bEvent event;
161    Plugin *plugin;
162    int i;
163    char *name = NULL;
164    int len = 0;
165    bool call_if_canceled = false;
166    restore_object_pkt *rop;
167
168    if (!bplugin_list || !jcr || !jcr->plugin_ctx_list) {
169       return;                         /* Return if no plugins loaded */
170    }
171    
172    /*
173     * Some events are sent to only a particular plugin or must be
174     *  called even if the job is canceled
175     */
176    switch(eventType) {
177    case bEventPluginCommand:
178    case bEventOptionPlugin:
179       name = (char *)value;
180       if (!get_plugin_name(jcr, name, &len)) {
181          return;
182       }
183       break;
184    case bEventRestoreObject:
185       /* After all RestoreObject, we have it one more time with value=NULL */
186       if (value) {
187          /* Some RestoreObjects may not have a plugin name */
188          rop = (restore_object_pkt *)value;
189          if (*rop->plugin_name) {
190             name = rop->plugin_name;
191             get_plugin_name(jcr, name, &len);
192          }
193       }
194
195       break;
196    case bEventEndBackupJob:
197    case bEventEndVerifyJob:
198       call_if_canceled = true;
199       break;
200    case bEventStartRestoreJob:
201       foreach_alist_index(i, plugin, bplugin_list) {
202          plugin->restoreFileStarted = false;
203          plugin->createFileCalled = false;
204       }
205       break;
206    case bEventEndRestoreJob:
207       call_if_canceled = true;
208       if (jcr->plugin && jcr->plugin->restoreFileStarted) {
209          plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
210       }
211       if (jcr->plugin) {
212          jcr->plugin->restoreFileStarted = false;
213          jcr->plugin->createFileCalled = false;
214       }
215       break;
216    default:
217       break;
218    }
219
220    if (!call_if_canceled && jcr->is_job_canceled()) {
221       return;
222    }
223
224    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
225    event.eventType = eventType;
226
227    Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
228
229    /*
230     * Pass event to every plugin (except if name is set). If name
231     *   is set, we pass it only to the plugin with that name.
232     */
233    foreach_alist_index(i, plugin, bplugin_list) {
234       if (name && !for_this_plug(plugin, name, len)) {
235          continue;
236       }
237       plugin_ctx = &plugin_ctx_list[i];
238       if (is_plugin_disabled(plugin_ctx)) {
239          continue;
240       }
241       plug_func(plugin)->handlePluginEvent(plugin_ctx, &event, value);
242    }
243    return;
244 }
245
246 /**
247  * Check if file was seen for accurate
248  */
249 bool plugin_check_file(JCR *jcr, char *fname)
250 {
251    Plugin *plugin;
252    int rc = bRC_OK;
253    int i;
254
255    if (!bplugin_list || !jcr || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
256       return false;                      /* Return if no plugins loaded */
257    }
258
259    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
260
261    Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
262
263    /* Pass event to every plugin */
264    foreach_alist_index(i, plugin, bplugin_list) {
265       jcr->plugin_ctx = &plugin_ctx_list[i];
266       jcr->plugin = plugin;
267       if (is_plugin_disabled(jcr)) {
268          continue;
269       }
270       if (plug_func(plugin)->checkFile == NULL) {
271          continue;
272       }
273       rc = plug_func(plugin)->checkFile(jcr->plugin_ctx, fname);
274       if (rc == bRC_Seen) {
275          break;
276       }
277    }
278
279    jcr->plugin = NULL;
280    jcr->plugin_ctx = NULL;
281    return rc == bRC_Seen;
282 }
283
284 /* Get the first part of the the plugin command
285  *  systemstate:/@SYSTEMSTATE/ 
286  * => ret = 11
287  * => can use for_this_plug(plug, cmd, ret);
288  *
289  * The plugin command can contain only the plugin name
290  *  Plugin = alldrives
291  * => ret = 9
292  */
293 static bool get_plugin_name(JCR *jcr, char *cmd, int *ret)
294 {
295    char *p;
296    int len;
297    if (!cmd || (*cmd == '\0')) {
298       return false;
299    }
300    /* Handle plugin command here backup */
301    Dmsg1(dbglvl, "plugin cmd=%s\n", cmd);
302    if ((p = strchr(cmd, ':')) == NULL) {
303       if (strchr(cmd, ' ') == NULL) { /* we have just the plugin name */
304          len = strlen(cmd);
305       } else {
306          Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
307          return false;
308       }
309    } else {                     /* plugin:argument */
310       len = p - cmd;
311       if (len <= 0) {
312          return false;
313       }
314    }
315    *ret = len;
316    return true;
317 }
318
319
320 static void update_ff_pkt(FF_PKT *ff_pkt, struct save_pkt *sp)
321 {
322    ff_pkt->delta_seq = sp->delta_seq;
323    if (sp->flags & FO_DELTA) {
324       ff_pkt->flags |= FO_DELTA;
325       ff_pkt->delta_seq++;          /* make new delta sequence number */
326    } else {
327       ff_pkt->flags &= ~FO_DELTA;   /* clean delta sequence number */
328       ff_pkt->delta_seq = 0;
329    }
330    
331    if (sp->flags & FO_OFFSETS) {
332       ff_pkt->flags |= FO_OFFSETS;
333    }
334    if (sp->flags & FO_PORTABLE_DATA) {
335       ff_pkt->flags |= FO_PORTABLE_DATA;
336    }
337    ff_pkt->flags |= FO_PLUGIN;       /* data from plugin */
338 }
339
340 /* Ask to a Option Plugin what to do with the current file */
341 bRC plugin_option_handle_file(JCR *jcr, FF_PKT *ff_pkt, struct save_pkt *sp)
342 {
343    Plugin *plugin;
344    bRC ret = bRC_Error;
345    char *cmd = ff_pkt->plugin;
346    int len;
347    int i=0;
348    bEvent event;
349    event.eventType = bEventHandleBackupFile;
350
351    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
352    memset(sp, 0, sizeof(struct save_pkt));
353    sp->pkt_size = sp->pkt_end = sizeof(struct save_pkt);
354    sp->portable = true;
355    sp->cmd = cmd;
356    sp->link = ff_pkt->link;
357    sp->cmd = ff_pkt->plugin;
358    sp->statp = ff_pkt->statp;
359    sp->fname = ff_pkt->fname;
360    sp->delta_seq = ff_pkt->delta_seq;
361    sp->accurate_found = ff_pkt->accurate_found;
362
363    if (!bplugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
364       Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
365       goto bail_out;         /* Return if no plugins loaded */
366    }
367
368    if (!get_plugin_name(jcr, cmd, &len)) {
369       goto bail_out;
370    }
371
372    /* Note, we stop the loop on the first plugin that matches the name */
373    foreach_alist_index(i, plugin, bplugin_list) {
374       Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
375       if (!for_this_plug(plugin, cmd, len)) {
376          continue;
377       }
378
379       if (is_plugin_disabled(&plugin_ctx_list[i])) {
380          goto bail_out;
381       }
382       
383       ret = plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i], 
384                                                  &event, sp);
385       
386       /* TODO: would be better to set this in save_file() */
387       if (ret == bRC_OK) {
388          jcr->opt_plugin = true;
389          jcr->plugin = plugin;
390          jcr->plugin_sp = sp;      /* Unset sp in save_file */
391          jcr->plugin_ctx = &plugin_ctx_list[i];
392
393          update_ff_pkt(ff_pkt, sp);
394       }
395
396       goto bail_out;
397    } /* end foreach loop */
398 bail_out:
399    return ret;
400 }
401
402 /**  
403  * Sequence of calls for a backup:
404  * 1. plugin_save() here is called with ff_pkt
405  * 2. we find the plugin requested on the command string
406  * 3. we generate a bEventBackupCommand event to the specified plugin
407  *    and pass it the command string.
408  * 4. we make a startPluginBackup call to the plugin, which gives
409  *    us the data we need in save_pkt
410  * 5. we call Bacula's save_file() subroutine to save the specified
411  *    file.  The plugin will be called at pluginIO() to supply the
412  *    file data.
413  *
414  * Sequence of calls for restore:
415  *   See subroutine plugin_name_stream() below.
416  */
417 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
418 {
419    Plugin *plugin;
420    int i;
421    int len;
422    char *cmd = ff_pkt->top_fname;
423    struct save_pkt sp;
424    bEvent event;
425    POOL_MEM fname(PM_FNAME);
426    POOL_MEM link(PM_FNAME);
427
428    if (!bplugin_list || !jcr->plugin_ctx_list || jcr->is_job_canceled()) {
429       Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
430       return 1;                            /* Return if no plugins loaded */
431    }
432
433    jcr->cmd_plugin = true;
434    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
435    event.eventType = bEventBackupCommand;
436
437    if (!get_plugin_name(jcr, cmd, &len)) {
438       goto bail_out;
439    }
440
441    /* Note, we stop the loop on the first plugin that matches the name */
442    foreach_alist_index(i, plugin, bplugin_list) {
443       Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
444       if (!for_this_plug(plugin, cmd, len)) {
445          continue;
446       }
447       /* 
448        * We put the current plugin pointer, and the plugin context
449        *  into the jcr, because during save_file(), the plugin
450        *  will be called many times and these values are needed.
451        */
452       jcr->plugin_ctx = &plugin_ctx_list[i];
453       jcr->plugin = plugin;
454       if (is_plugin_disabled(jcr)) {
455          goto bail_out;
456       }
457
458       Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
459       /* Send the backup command to the right plugin*/
460       if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
461          goto bail_out;
462       }
463       /* Loop getting filenames to backup then saving them */
464       while (!jcr->is_job_canceled()) { 
465          memset(&sp, 0, sizeof(sp));
466          sp.pkt_size = sizeof(sp);
467          sp.pkt_end = sizeof(sp);
468          sp.portable = true;
469          sp.flags = 0;
470          sp.cmd = cmd;
471          Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
472                 &sp);
473          /* Get the file save parameters. I.e. the stat pkt ... */
474          if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
475             goto bail_out;
476          }
477          if (sp.type == 0) {
478             Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
479                cmd);
480             goto bail_out;
481          }
482          jcr->plugin_sp = &sp;
483          ff_pkt = jcr->ff;
484          /*
485           * Copy fname and link because save_file() zaps them.  This 
486           *  avoids zaping the plugin's strings.
487           */
488          ff_pkt->type = sp.type;
489          if (IS_FT_OBJECT(sp.type)) {
490             if (!sp.object_name) {
491                Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no object_name in startBackupFile packet.\n"),
492                   cmd);
493                goto bail_out;
494             }
495             ff_pkt->fname = cmd;                 /* full plugin string */
496             ff_pkt->object_name = sp.object_name;
497             ff_pkt->object_index = sp.index;     /* restore object index */
498             ff_pkt->object_compression = 0;      /* no compression for now */
499             ff_pkt->object = sp.object;
500             ff_pkt->object_len = sp.object_len;
501          } else {
502             if (!sp.fname) {
503                Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
504                   cmd);
505                goto bail_out;
506             }
507             pm_strcpy(fname, sp.fname);
508             pm_strcpy(link, sp.link);
509
510
511             ff_pkt->fname = fname.c_str();
512             ff_pkt->link = link.c_str();
513             update_ff_pkt(ff_pkt, &sp);
514          }
515
516          memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
517          Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
518          if (sp.object) {
519             Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
520          }   
521          /* Call Bacula core code to backup the plugin's file */
522          save_file(jcr, ff_pkt, true);
523          bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
524          if (rc == bRC_More || rc == bRC_OK) {
525             accurate_mark_file_as_seen(jcr, fname.c_str());
526          }
527          if (rc == bRC_More) {
528             continue;
529          }
530          goto bail_out;
531       } /* end while loop */
532       goto bail_out;
533    } /* end loop over all plugins */
534    Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
535
536 bail_out:
537    jcr->cmd_plugin = false;
538    jcr->plugin = NULL;
539    jcr->plugin_ctx = NULL;
540    return 1;
541 }
542
543
544 /**  
545  * Sequence of calls for a estimate:
546  * 1. plugin_estimate() here is called with ff_pkt
547  * 2. we find the plugin requested on the command string
548  * 3. we generate a bEventEstimateCommand event to the specified plugin
549  *    and pass it the command string.
550  * 4. we make a startPluginBackup call to the plugin, which gives
551  *    us the data we need in save_pkt
552  *
553  */
554 int plugin_estimate(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
555 {
556    Plugin *plugin;
557    int len;
558    int i;
559    char *cmd = ff_pkt->top_fname;
560    struct save_pkt sp;
561    bEvent event;
562    POOL_MEM fname(PM_FNAME);
563    POOL_MEM link(PM_FNAME);
564    ATTR attr;
565
566    if (!bplugin_list || !jcr->plugin_ctx_list) {
567       Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" requested, but is not loaded.\n", cmd);
568       return 1;                            /* Return if no plugins loaded */
569    }
570
571    jcr->cmd_plugin = true;
572    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
573    event.eventType = bEventEstimateCommand;
574
575    if (!get_plugin_name(jcr, cmd, &len)) {
576       goto bail_out;
577    }
578
579    /* Note, we stop the loop on the first plugin that matches the name */
580    foreach_alist_index(i, plugin, bplugin_list) {
581       Dmsg4(dbglvl, "plugin=%s plen=%d cmd=%s len=%d\n", plugin->file, plugin->file_len, cmd, len);
582       if (!for_this_plug(plugin, cmd, len)) {
583          continue;
584       }
585       /* 
586        * We put the current plugin pointer, and the plugin context
587        *  into the jcr, because during save_file(), the plugin
588        *  will be called many times and these values are needed.
589        */
590       jcr->plugin_ctx = &plugin_ctx_list[i];
591       jcr->plugin = plugin;
592       if (is_plugin_disabled(jcr)) {
593          goto bail_out;
594       }
595
596       Dmsg1(dbglvl, "Command plugin = %s\n", cmd);
597       /* Send the backup command to the right plugin*/
598       if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, &event, cmd) != bRC_OK) {
599          goto bail_out;
600       }
601       /* Loop getting filenames to backup then saving them */
602       while (!jcr->is_job_canceled()) { 
603          memset(&sp, 0, sizeof(sp));
604          sp.pkt_size = sizeof(sp);
605          sp.pkt_end = sizeof(sp);
606          sp.portable = true;
607          sp.flags = 0;
608          sp.cmd = cmd;
609          Dmsg3(dbglvl, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
610                 &sp);
611          /* Get the file save parameters. I.e. the stat pkt ... */
612          if (plug_func(plugin)->startBackupFile(jcr->plugin_ctx, &sp) != bRC_OK) {
613             goto bail_out;
614          }
615          if (sp.type == 0) {
616             Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no type in startBackupFile packet.\n"),
617                cmd);
618             goto bail_out;
619          }
620
621          if (!IS_FT_OBJECT(sp.type)) {
622             if (!sp.fname) {
623                Jmsg1(jcr, M_FATAL, 0, _("Command plugin \"%s\": no fname in startBackupFile packet.\n"),
624                   cmd);
625                goto bail_out;
626             }
627
628             jcr->num_files_examined++;
629             jcr->JobFiles++;                  /* increment number of files seen */
630
631             if (sp.type != FT_LNKSAVED && S_ISREG(sp.statp.st_mode)) {
632                if (sp.statp.st_size > 0) {
633                   jcr->JobBytes += sp.statp.st_size;
634                }
635             }
636
637             if (jcr->listing) {
638                memcpy(&attr.statp, &sp.statp, sizeof(struct stat));
639                attr.type = sp.type;
640                attr.ofname = (POOLMEM *)sp.fname;
641                attr.olname = (POOLMEM *)sp.link;
642                print_ls_output(jcr, &attr);
643             }
644          }
645
646          Dmsg2(dbglvl, "startBackup returned type=%d, fname=%s\n", sp.type, sp.fname);
647          if (sp.object) {
648             Dmsg2(dbglvl, "index=%d object=%s\n", sp.index, sp.object);
649          }
650          bRC rc = plug_func(plugin)->endBackupFile(jcr->plugin_ctx);
651          if (rc == bRC_More || rc == bRC_OK) {
652             accurate_mark_file_as_seen(jcr, sp.fname);
653          }
654          if (rc == bRC_More) {
655             continue;
656          }
657          goto bail_out;
658       } /* end while loop */
659       goto bail_out;
660    } /* end loop over all plugins */
661    Jmsg1(jcr, M_FATAL, 0, "Command plugin \"%s\" not found.\n", cmd);
662
663 bail_out:
664    jcr->cmd_plugin = false;
665    jcr->plugin = NULL;
666    jcr->plugin_ctx = NULL;
667    return 1;
668 }
669
670 /**
671  * Send plugin name start/end record to SD
672  */
673 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
674 {
675    int stat;
676    int index = jcr->JobFiles;
677    struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
678
679    if (!sp) {
680       Jmsg0(jcr, M_FATAL, 0, _("Plugin save packet not found.\n"));
681       return false;
682    }
683    if (jcr->is_job_canceled()) {
684       return false;
685    }
686   
687    if (start) {
688       index++;                  /* JobFiles not incremented yet */
689    }
690    Dmsg1(dbglvl, "send_plugin_name=%s\n", sp->cmd);
691    /* Send stream header */
692    if (!sd->fsend("%ld %d 0", index, STREAM_PLUGIN_NAME)) {
693      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
694            sd->bstrerror());
695      return false;
696    }
697    Dmsg1(50, "send plugin name hdr: %s\n", sd->msg);
698
699    if (start) {
700       /* Send data -- not much */
701       stat = sd->fsend("%ld 1 %d %s%c", index, sp->portable, sp->cmd, 0);
702    } else {
703       /* Send end of data */
704       stat = sd->fsend("%ld 0", jcr->JobFiles);
705    }
706    if (!stat) {
707       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
708             sd->bstrerror());
709          return false;
710    }
711    Dmsg1(dbglvl, "send plugin start/end: %s\n", sd->msg);
712    sd->signal(BNET_EOD);            /* indicate end of plugin name data */
713    return true;
714 }
715
716 /**
717  * Plugin name stream found during restore.  The record passed in
718  *  argument name was generated in send_plugin_name() above.
719  *
720  * Returns: true  if start of stream
721  *          false if end of steam
722  */
723 bool plugin_name_stream(JCR *jcr, char *name)    
724 {
725    char *p = name;
726    char *cmd;
727    bool start;
728    Plugin *plugin;
729    int len;
730    int i;
731    bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
732
733    Dmsg1(dbglvl, "Read plugin stream string=%s\n", name);
734    skip_nonspaces(&p);             /* skip over jcr->JobFiles */
735    skip_spaces(&p);
736    start = *p == '1';
737    if (start) {
738       /* Start of plugin data */
739       skip_nonspaces(&p);          /* skip start/end flag */
740       skip_spaces(&p);
741 //    portable = *p == '1';
742       skip_nonspaces(&p);          /* skip portable flag */
743       skip_spaces(&p);
744       cmd = p;
745    } else {
746       /*
747        * End of plugin data, notify plugin, then clear flags   
748        */
749       Dmsg2(dbglvl, "End plugin data plugin=%p ctx=%p\n", jcr->plugin, jcr->plugin_ctx);
750       if (jcr->plugin && jcr->plugin->restoreFileStarted) {
751          plug_func(jcr->plugin)->endRestoreFile(jcr->plugin_ctx);
752       }
753       if (jcr->plugin) {
754          jcr->plugin->restoreFileStarted = false;
755          jcr->plugin->createFileCalled = false;
756       }
757       jcr->plugin_ctx = NULL;
758       jcr->plugin = NULL;
759       goto bail_out;
760    }
761    if (!plugin_ctx_list) {
762       goto bail_out;
763    }
764       
765    /*
766     * After this point, we are dealing with a restore start
767     */
768    if (!get_plugin_name(jcr, cmd, &len)) {
769       goto bail_out;
770    }
771
772    /*
773     * Search for correct plugin as specified on the command 
774     */
775    foreach_alist_index(i, plugin, bplugin_list) {
776       bEvent event;
777       Dmsg3(dbglvl, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
778       if (!for_this_plug(plugin, cmd, len)) {
779          continue;
780       }
781       jcr->plugin_ctx = &plugin_ctx_list[i];
782       jcr->plugin = plugin;
783       if (is_plugin_disabled(jcr)) {
784          goto bail_out;
785       }
786       Dmsg1(000, "Restore Command plugin = %s\n", cmd);
787       event.eventType = bEventRestoreCommand;     
788       if (plug_func(plugin)->handlePluginEvent(jcr->plugin_ctx, 
789             &event, cmd) != bRC_OK) {
790          goto bail_out;
791       }
792       if (plugin->restoreFileStarted) {
793          Jmsg2(jcr, M_FATAL, 0, "Unbalanced call to startRestoreFile. plugin=%s cmd=%s\n", plugin->file, cmd);
794          plugin->restoreFileStarted = false;
795          goto bail_out;
796       }
797       if (plug_func(plugin)->startRestoreFile(jcr->plugin_ctx, cmd) == bRC_OK) {
798          plugin->restoreFileStarted = true;
799       }
800       goto bail_out;
801    }
802    Jmsg1(jcr, M_WARNING, 0, _("Plugin=%s not found.\n"), cmd);
803
804 bail_out:
805    return start;
806 }
807
808 /**
809  * Tell the plugin to create the file.  Return values are
810  *   This is called only during Restore
811  *
812  *  CF_ERROR    -- error
813  *  CF_SKIP     -- skip processing this file
814  *  CF_EXTRACT  -- extract the file (i.e.call i/o routines)
815  *  CF_CREATED  -- created, but no content to extract (typically directories)
816  *
817  */
818 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
819 {
820    bpContext *plugin_ctx = jcr->plugin_ctx;
821    Plugin *plugin = jcr->plugin;
822    struct restore_pkt rp;
823    int flags;
824    int rc;
825
826    if (!plugin || !plugin_ctx || !set_cmd_plugin(bfd, jcr) || jcr->is_job_canceled()) {
827       return CF_ERROR;
828    }
829
830    rp.pkt_size = sizeof(rp);
831    rp.pkt_end = sizeof(rp);
832    rp.delta_seq = attr->delta_seq;
833    rp.stream = attr->stream;
834    rp.data_stream = attr->data_stream;
835    rp.type = attr->type;
836    rp.file_index = attr->file_index;
837    rp.LinkFI = attr->LinkFI;
838    rp.uid = attr->uid;
839    rp.statp = attr->statp;                /* structure assignment */
840    rp.attrEx = attr->attrEx;
841    rp.ofname = attr->ofname;
842    rp.olname = attr->olname;
843    rp.where = jcr->where;
844    rp.RegexWhere = jcr->RegexWhere;
845    rp.replace = jcr->replace;
846    rp.create_status = CF_ERROR;
847    Dmsg4(dbglvl, "call plugin createFile stream=%d type=%d LinkFI=%d File=%s\n", 
848          rp.stream, rp.type, rp.LinkFI, rp.ofname);
849    if (rp.attrEx) {
850       Dmsg1(dbglvl, "attrEx=\"%s\"\n", rp.attrEx);
851    }
852    if (!plugin->restoreFileStarted || plugin->createFileCalled) {
853       Jmsg0(jcr, M_FATAL, 0, "Unbalanced call to createFile\n");
854       plugin->createFileCalled = false;
855       return CF_ERROR;
856    }
857    rc = plug_func(plugin)->createFile(plugin_ctx, &rp);
858    if (rc != bRC_OK) {
859       Qmsg2(jcr, M_ERROR, 0, _("Plugin createFile call failed. Stat=%d file=%s\n"),
860             rc, attr->ofname);
861       return CF_ERROR;
862    }
863    if (rp.create_status == CF_CORE) { 
864       return CF_CORE;           /* Let Bacula core handle the file creation */
865    }
866    if (rp.create_status == CF_SKIP) {
867       return CF_SKIP;
868    }
869    if (rp.create_status == CF_ERROR) {
870       Qmsg1(jcr, M_ERROR, 0, _("Plugin createFile call failed. Returned CF_ERROR file=%s\n"),
871             attr->ofname);
872       return CF_ERROR;
873    }
874    /* Created link or directory? */
875    if (rp.create_status == CF_CREATED) {
876       return rp.create_status;        /* yes, no need to bopen */
877    }
878
879    flags =  O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
880    Dmsg0(dbglvl, "call bopen\n");
881    int stat = bopen(bfd, attr->ofname, flags, S_IRUSR | S_IWUSR);
882    Dmsg1(50, "bopen status=%d\n", stat);
883    if (stat < 0) {
884       berrno be;
885       be.set_errno(bfd->berrno);
886       Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"),
887             attr->ofname, be.bstrerror());
888       Dmsg2(dbglvl,"Could not bopen file %s: ERR=%s\n", attr->ofname, be.bstrerror());
889       return CF_ERROR;
890    }
891
892    if (!is_bopen(bfd)) {
893       Dmsg0(000, "===== BFD is not open!!!!\n");
894    }
895    return CF_EXTRACT;
896 }
897
898 /**
899  * Reset the file attributes after all file I/O is done -- this allows
900  *  the previous access time/dates to be set properly, and it also allows
901  *  us to properly set directory permissions.
902  *  Not currently Implemented.
903  */
904 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
905 {
906    Dmsg0(dbglvl, "plugin_set_attributes\n");
907    if (is_bopen(ofd)) {
908       bclose(ofd);
909    }
910    pm_strcpy(attr->ofname, "*none*");
911    return true;
912 }
913
914 /*
915  * Print to file the plugin info.
916  */
917 void dump_fd_plugin(Plugin *plugin, FILE *fp)
918 {
919    if (!plugin) {
920       return ;
921    }
922    pInfo *info = (pInfo *)plugin->pinfo;
923    fprintf(fp, "\tversion=%d\n", info->version);
924    fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
925    fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
926    fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
927    fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
928    fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
929    fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
930 }
931
932 /**
933  * This entry point is called internally by Bacula to ensure
934  *  that the plugin IO calls come into this code.
935  */
936 void load_fd_plugins(const char *plugin_dir)
937 {
938    Plugin *plugin;
939    int i;
940
941    if (!plugin_dir) {
942       Dmsg0(dbglvl, "plugin dir is NULL\n");
943       return;
944    }
945
946    bplugin_list = New(alist(10, not_owned_by_alist));
947    if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
948                      is_plugin_compatible)) {
949       /* Either none found, or some error */
950       if (bplugin_list->size() == 0) {
951          delete bplugin_list;
952          bplugin_list = NULL;
953          Dmsg0(dbglvl, "No plugins loaded\n");
954          return;
955       }
956    }
957
958    /* Plug entry points called from findlib */
959    plugin_bopen  = my_plugin_bopen;
960    plugin_bclose = my_plugin_bclose;
961    plugin_bread  = my_plugin_bread;
962    plugin_bwrite = my_plugin_bwrite;
963    plugin_blseek = my_plugin_blseek;
964
965    /* 
966     * Verify that the plugin is acceptable, and print information
967     *  about it.
968     */
969    foreach_alist_index(i, plugin, bplugin_list) {
970       Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
971       Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
972    }
973
974    dbg_plugin_add_hook(dump_fd_plugin);
975 }
976
977 /**
978  * Check if a plugin is compatible.  Called by the load_plugin function
979  *  to allow us to verify the plugin.
980  */
981 static bool is_plugin_compatible(Plugin *plugin)
982 {
983    pInfo *info = (pInfo *)plugin->pinfo;
984    Dmsg0(50, "is_plugin_compatible called\n");
985    if (debug_level >= 50) {
986       dump_fd_plugin(plugin, stdin);
987    }
988    if (strcmp(info->plugin_magic, FD_PLUGIN_MAGIC) != 0) {
989       Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
990            plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
991       Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
992            plugin->file, FD_PLUGIN_MAGIC, info->plugin_magic);
993
994       return false;
995    }
996    if (info->version != FD_PLUGIN_INTERFACE_VERSION) {
997       Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
998            plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
999       Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
1000            plugin->file, FD_PLUGIN_INTERFACE_VERSION, info->version);
1001       return false;
1002    }
1003    if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
1004        strcmp(info->plugin_license, "AGPLv3") != 0) {
1005       Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
1006            plugin->file, info->plugin_license);
1007       Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
1008            plugin->file, info->plugin_license);
1009       return false;
1010    }
1011    if (info->size != sizeof(pInfo)) {
1012       Jmsg(NULL, M_ERROR, 0,
1013            _("Plugin size incorrect. Plugin=%s wanted=%d got=%d\n"),
1014            plugin->file, sizeof(pInfo), info->size);
1015       return false;
1016    }
1017       
1018    return true;
1019 }
1020
1021
1022 /**
1023  * Create a new instance of each plugin for this Job
1024  *   Note, bplugin_list can exist but jcr->plugin_ctx_list can
1025  *   be NULL if no plugins were loaded.
1026  */
1027 void new_plugins(JCR *jcr)
1028 {
1029    Plugin *plugin;
1030    int i;
1031
1032    if (!bplugin_list) {
1033       Dmsg0(dbglvl, "plugin list is NULL\n");
1034       return;
1035    }
1036    if (jcr->is_job_canceled()) {
1037       return;
1038    }
1039
1040    int num = bplugin_list->size();
1041
1042    if (num == 0) {
1043       Dmsg0(dbglvl, "No plugins loaded\n");
1044       return;
1045    }
1046
1047    jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
1048
1049    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1050    Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1051    foreach_alist_index(i, plugin, bplugin_list) {
1052       /* Start a new instance of each plugin */
1053       bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
1054       memset(b_ctx, 0, sizeof(bacula_ctx));
1055       b_ctx->jcr = jcr;
1056       plugin_ctx_list[i].bContext = (void *)b_ctx;   /* Bacula private context */
1057       plugin_ctx_list[i].pContext = NULL;
1058       if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i]) != bRC_OK) {
1059          b_ctx->disabled = true;
1060       }
1061    }
1062 }
1063
1064 /**
1065  * Free the plugin instances for this Job
1066  */
1067 void free_plugins(JCR *jcr)
1068 {
1069    Plugin *plugin;
1070    int i;
1071
1072    if (!bplugin_list || !jcr->plugin_ctx_list) {
1073       return;                         /* no plugins, nothing to do */
1074    }
1075
1076    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1077    Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1078    foreach_alist_index(i, plugin, bplugin_list) {   
1079       /* Free the plugin instance */
1080       plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
1081       free(plugin_ctx_list[i].bContext);     /* free Bacula private context */
1082    }
1083    free(plugin_ctx_list);
1084    jcr->plugin_ctx_list = NULL;
1085 }
1086
1087 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
1088 {
1089    JCR *jcr = bfd->jcr;
1090    Plugin *plugin = (Plugin *)jcr->plugin;
1091    struct io_pkt io;
1092
1093    Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
1094    if (!plugin || !jcr->plugin_ctx) {
1095       return 0;
1096    }
1097    io.pkt_size = sizeof(io);
1098    io.pkt_end = sizeof(io);
1099    io.func = IO_OPEN;
1100    io.count = 0;
1101    io.buf = NULL;
1102    io.fname = fname;
1103    io.flags = flags;
1104    io.mode = mode;
1105    io.win32 = false;
1106    io.lerror = 0;
1107    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1108    bfd->berrno = io.io_errno;
1109    if (io.win32) {
1110       errno = b_errno_win32;
1111    } else {
1112       errno = io.io_errno;
1113       bfd->lerror = io.lerror;
1114    }
1115    Dmsg1(50, "Return from plugin open status=%d\n", io.status);
1116    return io.status;
1117 }
1118
1119 static int my_plugin_bclose(BFILE *bfd)
1120 {
1121    JCR *jcr = bfd->jcr;
1122    Plugin *plugin = (Plugin *)jcr->plugin;
1123    struct io_pkt io;
1124
1125    Dmsg0(dbglvl, "===== plugin_bclose\n");
1126    if (!plugin || !jcr->plugin_ctx) {
1127       return 0;
1128    }
1129    io.pkt_size = sizeof(io);
1130    io.pkt_end = sizeof(io);
1131    io.func = IO_CLOSE;
1132    io.count = 0;
1133    io.buf = NULL;
1134    io.win32 = false;
1135    io.lerror = 0;
1136    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1137    bfd->berrno = io.io_errno;
1138    if (io.win32) {
1139       errno = b_errno_win32;
1140    } else {
1141       errno = io.io_errno;
1142       bfd->lerror = io.lerror;
1143    }
1144    Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
1145    return io.status;
1146 }
1147
1148 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
1149 {
1150    JCR *jcr = bfd->jcr;
1151    Plugin *plugin = (Plugin *)jcr->plugin;
1152    struct io_pkt io;
1153
1154    Dmsg0(dbglvl, "plugin_bread\n");
1155    if (!plugin || !jcr->plugin_ctx) {
1156       return 0;
1157    }
1158    io.pkt_size = sizeof(io);
1159    io.pkt_end = sizeof(io);
1160    io.func = IO_READ;
1161    io.count = count;
1162    io.buf = (char *)buf;
1163    io.win32 = false;
1164    io.offset = 0;
1165    io.lerror = 0;
1166    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1167    bfd->offset = io.offset;
1168    bfd->berrno = io.io_errno;
1169    if (io.win32) {
1170       errno = b_errno_win32;
1171    } else {
1172       errno = io.io_errno;
1173       bfd->lerror = io.lerror;
1174    }
1175    return (ssize_t)io.status;
1176 }
1177
1178 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
1179 {
1180    JCR *jcr = bfd->jcr;
1181    Plugin *plugin = (Plugin *)jcr->plugin;
1182    struct io_pkt io;
1183
1184    Dmsg0(dbglvl, "plugin_bwrite\n");
1185    if (!plugin || !jcr->plugin_ctx) {
1186       return 0;
1187    }
1188    io.pkt_size = sizeof(io);
1189    io.pkt_end = sizeof(io);
1190    io.func = IO_WRITE;
1191    io.count = count;
1192    io.buf = (char *)buf;
1193    io.win32 = false;
1194    io.lerror = 0;
1195    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1196    bfd->berrno = io.io_errno;
1197    if (io.win32) {
1198       errno = b_errno_win32;
1199    } else {
1200       errno = io.io_errno;
1201       bfd->lerror = io.lerror;
1202    }
1203    return (ssize_t)io.status;
1204 }
1205
1206 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
1207 {
1208    JCR *jcr = bfd->jcr;
1209    Plugin *plugin = (Plugin *)jcr->plugin;
1210    struct io_pkt io;
1211
1212    Dmsg0(dbglvl, "plugin_bseek\n");
1213    if (!plugin || !jcr->plugin_ctx) {
1214       return 0;
1215    }
1216    io.pkt_size = sizeof(io);
1217    io.pkt_end = sizeof(io);
1218    io.func = IO_SEEK;
1219    io.offset = offset;
1220    io.whence = whence;
1221    io.win32 = false;
1222    io.lerror = 0;
1223    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1224    bfd->berrno = io.io_errno;
1225    if (io.win32) {
1226       errno = b_errno_win32;
1227    } else {
1228       errno = io.io_errno;
1229       bfd->lerror = io.lerror;
1230    }
1231    return (boffset_t)io.offset;
1232 }
1233
1234 /* ==============================================================
1235  *
1236  * Callbacks from the plugin
1237  *
1238  * ==============================================================
1239  */
1240 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
1241 {
1242    JCR *jcr;
1243    if (!value) {
1244       return bRC_Error;
1245    }
1246
1247    switch (var) {               /* General variables, no need of ctx */
1248    case bVarFDName:
1249       *((char **)value) = my_name;
1250       break;
1251    case bVarWorkingDir:
1252       *(void **)value = me->working_directory;
1253       break;
1254    case bVarExePath:
1255       *(char **)value = exepath;
1256       break;
1257    case bVarVersion:
1258       *(char **)value = version;
1259       break;
1260    case bVarDistName:
1261       *(char **)value = dist_name;
1262       break;
1263    case bVarBEEF:
1264       *((int *)value) = beef;
1265       break;
1266    default:
1267       break;
1268    }
1269
1270    if (!ctx) {                  /* Other variables need context */
1271       return bRC_Error;
1272    }
1273
1274    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1275    if (!jcr) {
1276       return bRC_Error;
1277    }
1278
1279    switch (var) {
1280    case bVarJobId:
1281       *((int *)value) = jcr->JobId;
1282       Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
1283       break;
1284    case bVarLevel:
1285       *((int *)value) = jcr->getJobLevel();
1286       Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
1287       break;
1288    case bVarType:
1289       *((int *)value) = jcr->getJobType();
1290       Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
1291       break;
1292    case bVarClient:
1293       *((char **)value) = jcr->client_name;
1294       Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
1295       break;
1296    case bVarJobName:
1297       *((char **)value) = jcr->Job;
1298       Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
1299       break;
1300    case bVarPrevJobName:
1301       *((char **)value) = jcr->PrevJob;
1302       Dmsg1(dbglvl, "Bacula: return Previous Job name=%s\n", jcr->PrevJob);
1303       break;
1304    case bVarJobStatus:
1305       *((int *)value) = jcr->JobStatus;
1306       Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
1307       break;
1308    case bVarSinceTime:
1309       *((int *)value) = (int)jcr->mtime;
1310       Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
1311       break;
1312    case bVarAccurate:
1313       *((int *)value) = (int)jcr->accurate;
1314       Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1315       break;
1316    case bVarFileSeen:
1317       break;                 /* a write only variable, ignore read request */
1318    case bVarVssObject:
1319 #ifdef HAVE_WIN32
1320       if (g_pVSSClient) {
1321          *(void **)value = g_pVSSClient->GetVssObject();
1322          break;
1323        }
1324 #endif
1325        return bRC_Error;
1326    case bVarVssDllHandle:
1327 #ifdef HAVE_WIN32
1328       if (g_pVSSClient) {
1329          *(void **)value = g_pVSSClient->GetVssDllHandle();
1330          break;
1331        }
1332 #endif
1333        return bRC_Error;
1334    case bVarWhere:
1335       *(char **)value = jcr->where;
1336       break;
1337    case bVarRegexWhere:
1338       *(char **)value = jcr->RegexWhere;
1339       break;
1340
1341    case bVarFDName:             /* get warning with g++ if we missed one */
1342    case bVarWorkingDir:
1343    case bVarExePath:
1344    case bVarVersion:
1345    case bVarDistName:
1346    case bVarBEEF:
1347       break;
1348    }
1349    return bRC_OK;
1350 }
1351
1352 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1353 {
1354    JCR *jcr;
1355    if (!value || !ctx) {
1356       return bRC_Error;
1357    }
1358 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1359    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1360    if (!jcr) {
1361       return bRC_Error;
1362    }
1363 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
1364    switch (var) {
1365    case bVarFileSeen:
1366       if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1367          return bRC_Error;
1368       } 
1369       break;
1370    default:
1371       break;
1372    }
1373    return bRC_OK;
1374 }
1375
1376 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1377 {
1378    va_list args;
1379    uint32_t event;
1380
1381    if (!ctx) {
1382       return bRC_Error;
1383    }
1384
1385    va_start(args, ctx);
1386    while ((event = va_arg(args, uint32_t))) {
1387       Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1388    }
1389    va_end(args);
1390    return bRC_OK;
1391 }
1392
1393 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1394   int type, utime_t mtime, const char *fmt, ...)
1395 {
1396    va_list arg_ptr;
1397    char buf[2000];
1398    JCR *jcr;
1399
1400    if (ctx) {
1401       jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1402    } else {
1403       jcr = NULL;
1404    }
1405
1406    va_start(arg_ptr, fmt);
1407    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1408    va_end(arg_ptr);
1409    Jmsg(jcr, type, mtime, "%s", buf);
1410    return bRC_OK;
1411 }
1412
1413 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1414   int level, const char *fmt, ...)
1415 {
1416    va_list arg_ptr;
1417    char buf[2000];
1418
1419    va_start(arg_ptr, fmt);
1420    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1421    va_end(arg_ptr);
1422    d_msg(file, line, level, "%s", buf);
1423    return bRC_OK;
1424 }
1425
1426 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1427               size_t size)
1428 {
1429 #ifdef SMARTALLOC
1430    return sm_malloc(file, line, size);
1431 #else
1432    return malloc(size);
1433 #endif
1434 }
1435
1436 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1437 {
1438 #ifdef SMARTALLOC
1439    sm_free(file, line, mem);
1440 #else
1441    free(mem);
1442 #endif
1443 }
1444
1445 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1446 {
1447    if (!ctx) {
1448       return false;
1449    }
1450    bctx = (bacula_ctx *)ctx->bContext;
1451    if (!bctx) {
1452       return false;
1453    }
1454    jcr = bctx->jcr;
1455    if (!jcr) {
1456       return false;
1457    }
1458    return true;
1459 }
1460
1461 /**
1462  * Let the plugin define files/directories to be excluded
1463  *  from the main backup.
1464  */
1465 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1466 {
1467    JCR *jcr;
1468    bacula_ctx *bctx;
1469    if (!is_ctx_good(ctx, jcr, bctx)) {
1470       return bRC_Error;
1471    }
1472    if (!file) {
1473       return bRC_Error;
1474    }
1475    if (!bctx->exclude) {  
1476       bctx->exclude = new_exclude(jcr);
1477       new_options(jcr, bctx->exclude);
1478    }
1479    set_incexe(jcr, bctx->exclude);
1480    add_file_to_fileset(jcr, file, true);
1481    Dmsg1(100, "Add exclude file=%s\n", file);
1482    return bRC_OK;
1483 }
1484
1485 /**
1486  * Let the plugin define files/directories to be excluded
1487  *  from the main backup.
1488  */
1489 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1490 {
1491    JCR *jcr;
1492    bacula_ctx *bctx;
1493    if (!is_ctx_good(ctx, jcr, bctx)) {
1494       return bRC_Error;
1495    }
1496    if (!file) {
1497       return bRC_Error;
1498    }
1499    /* Not right time to add include */
1500    if (!(jcr->ff && jcr->ff->fileset && jcr->ff->fileset->incexe)) {
1501       return bRC_Error;
1502    }
1503    if (!bctx->include) {
1504       bctx->include = jcr->ff->fileset->incexe;
1505    }
1506    set_incexe(jcr, bctx->include);
1507    add_file_to_fileset(jcr, file, true);
1508    Dmsg1(100, "Add include file=%s\n", file);
1509    return bRC_OK;
1510 }
1511
1512 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1513 {
1514    JCR *jcr;
1515    bacula_ctx *bctx;
1516    if (!is_ctx_good(ctx, jcr, bctx)) {
1517       return bRC_Error;
1518    }
1519    if (!opts) {
1520       return bRC_Error;
1521    }
1522    add_options_to_fileset(jcr, opts);
1523    Dmsg1(1000, "Add options=%s\n", opts);
1524    return bRC_OK;
1525 }
1526
1527 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1528 {
1529    JCR *jcr;
1530    bacula_ctx *bctx;
1531    if (!is_ctx_good(ctx, jcr, bctx)) {
1532       return bRC_Error;
1533    }
1534    if (!item) {
1535       return bRC_Error;
1536    }
1537    add_regex_to_fileset(jcr, item, type);
1538    Dmsg1(100, "Add regex=%s\n", item);
1539    return bRC_OK;
1540 }
1541
1542 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1543 {
1544    JCR *jcr;
1545    bacula_ctx *bctx;
1546    if (!is_ctx_good(ctx, jcr, bctx)) {
1547       return bRC_Error;
1548    }
1549    if (!item) {
1550       return bRC_Error;
1551    }
1552    add_wild_to_fileset(jcr, item, type);
1553    Dmsg1(100, "Add wild=%s\n", item);
1554    return bRC_OK;
1555 }
1556
1557 static bRC baculaNewOptions(bpContext *ctx)
1558 {
1559    JCR *jcr;
1560    bacula_ctx *bctx;
1561    if (!is_ctx_good(ctx, jcr, bctx)) {
1562       return bRC_Error;
1563    }
1564    (void)new_options(jcr, NULL);
1565    return bRC_OK;
1566 }
1567
1568 static bRC baculaNewInclude(bpContext *ctx)
1569 {
1570    JCR *jcr;
1571    bacula_ctx *bctx;
1572    if (!is_ctx_good(ctx, jcr, bctx)) {
1573       return bRC_Error;
1574    }
1575    (void)new_include(jcr);
1576    return bRC_OK;
1577 }
1578
1579 static bRC baculaNewPreInclude(bpContext *ctx)
1580 {
1581    JCR *jcr;
1582    bacula_ctx *bctx;
1583    if (!is_ctx_good(ctx, jcr, bctx)) {
1584       return bRC_Error;
1585    }
1586
1587    bctx->include = new_preinclude(jcr);
1588    new_options(jcr, bctx->include);
1589    set_incexe(jcr, bctx->include);
1590
1591    return bRC_OK;
1592 }
1593
1594 /* 
1595  * Check if a file have to be backuped using Accurate code
1596  */
1597 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
1598 {
1599    JCR *jcr;
1600    bacula_ctx *bctx;
1601    FF_PKT *ff_pkt;
1602    bRC ret = bRC_Error;
1603
1604    if (!is_ctx_good(ctx, jcr, bctx)) {
1605       goto bail_out;
1606    }
1607    if (!sp) {
1608       goto bail_out;
1609    }
1610    
1611    ff_pkt = jcr->ff;
1612    /*
1613     * Copy fname and link because save_file() zaps them.  This 
1614     *  avoids zaping the plugin's strings.
1615     */
1616    ff_pkt->type = sp->type;
1617    if (!sp->fname) {
1618       Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n"));
1619       goto bail_out;
1620    }
1621
1622    ff_pkt->fname = sp->fname;
1623    ff_pkt->link = sp->link;
1624    memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp));
1625
1626    if (check_changes(jcr, ff_pkt))  {
1627       ret = bRC_OK;
1628    } else {
1629       ret = bRC_Seen;
1630    }
1631
1632    /* check_changes() can update delta sequence number, return it to the
1633     * plugin 
1634     */
1635    sp->delta_seq = ff_pkt->delta_seq;
1636    sp->accurate_found = ff_pkt->accurate_found;
1637
1638 bail_out:
1639    Dmsg1(100, "checkChanges=%i\n", ret);
1640    return ret;
1641 }
1642
1643
1644 #ifdef TEST_PROGRAM
1645
1646 int     (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1647 int     (*plugin_bclose)(JCR *jcr) = NULL;
1648 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1649 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1650 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1651
1652 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1653 {
1654    return 0;
1655 }
1656
1657 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1658 {
1659    return true;
1660 }
1661
1662 int main(int argc, char *argv[])
1663 {
1664    char plugin_dir[1000];
1665    JCR mjcr1, mjcr2;
1666    JCR *jcr1 = &mjcr1;
1667    JCR *jcr2 = &mjcr2;
1668
1669    strcpy(my_name, "test-fd");
1670     
1671    getcwd(plugin_dir, sizeof(plugin_dir)-1);
1672    load_fd_plugins(plugin_dir);
1673
1674    jcr1->JobId = 111;
1675    new_plugins(jcr1);
1676
1677    jcr2->JobId = 222;
1678    new_plugins(jcr2);
1679
1680    generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1681    generate_plugin_event(jcr1, bEventJobEnd);
1682    generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1683    free_plugins(jcr1);
1684    generate_plugin_event(jcr2, bEventJobEnd);
1685    free_plugins(jcr2);
1686
1687    unload_plugins();
1688
1689    Dmsg0(dbglvl, "bacula: OK ...\n");
1690    close_memory_pool();
1691    sm_dump(false);     /* unit test */
1692    return 0;
1693 }
1694
1695 #endif /* TEST_PROGRAM */