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