]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/fd_plugins.c
Send previous Job name during Incremental/Differential to the FD
[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       Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
1009            plugin->file, info->plugin_license);
1010       Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
1011            plugin->file, info->plugin_license);
1012       return false;
1013    }
1014    if (info->size != sizeof(pInfo)) {
1015       Jmsg(NULL, M_ERROR, 0,
1016            _("Plugin size incorrect. Plugin=%s wanted=%d got=%d\n"),
1017            plugin->file, sizeof(pInfo), info->size);
1018       return false;
1019    }
1020       
1021    return true;
1022 }
1023
1024
1025 /**
1026  * Create a new instance of each plugin for this Job
1027  *   Note, bplugin_list can exist but jcr->plugin_ctx_list can
1028  *   be NULL if no plugins were loaded.
1029  */
1030 void new_plugins(JCR *jcr)
1031 {
1032    Plugin *plugin;
1033    int i = 0;
1034
1035    if (!bplugin_list) {
1036       Dmsg0(dbglvl, "plugin list is NULL\n");
1037       return;
1038    }
1039    if (jcr->is_job_canceled()) {
1040       return;
1041    }
1042
1043    int num = bplugin_list->size();
1044
1045    if (num == 0) {
1046       Dmsg0(dbglvl, "No plugins loaded\n");
1047       return;
1048    }
1049
1050    jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
1051
1052    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1053    Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1054    foreach_alist(plugin, bplugin_list) {
1055       /* Start a new instance of each plugin */
1056       bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
1057       memset(b_ctx, 0, sizeof(bacula_ctx));
1058       b_ctx->jcr = jcr;
1059       plugin_ctx_list[i].bContext = (void *)b_ctx;   /* Bacula private context */
1060       plugin_ctx_list[i].pContext = NULL;
1061       if (plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
1062          b_ctx->disabled = true;
1063       }
1064    }
1065 }
1066
1067 /**
1068  * Free the plugin instances for this Job
1069  */
1070 void free_plugins(JCR *jcr)
1071 {
1072    Plugin *plugin;
1073    int i = 0;
1074
1075    if (!bplugin_list || !jcr->plugin_ctx_list) {
1076       return;                         /* no plugins, nothing to do */
1077    }
1078
1079    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
1080    Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
1081    foreach_alist(plugin, bplugin_list) {   
1082       /* Free the plugin instance */
1083       plug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
1084       free(plugin_ctx_list[i++].bContext);     /* free Bacula private context */
1085    }
1086    free(plugin_ctx_list);
1087    jcr->plugin_ctx_list = NULL;
1088 }
1089
1090 static int my_plugin_bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
1091 {
1092    JCR *jcr = bfd->jcr;
1093    Plugin *plugin = (Plugin *)jcr->plugin;
1094    struct io_pkt io;
1095
1096    Dmsg1(dbglvl, "plugin_bopen flags=%x\n", flags);
1097    if (!plugin || !jcr->plugin_ctx) {
1098       return 0;
1099    }
1100    io.pkt_size = sizeof(io);
1101    io.pkt_end = sizeof(io);
1102    io.func = IO_OPEN;
1103    io.count = 0;
1104    io.buf = NULL;
1105    io.fname = fname;
1106    io.flags = flags;
1107    io.mode = mode;
1108    io.win32 = false;
1109    io.lerror = 0;
1110    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1111    bfd->berrno = io.io_errno;
1112    if (io.win32) {
1113       errno = b_errno_win32;
1114    } else {
1115       errno = io.io_errno;
1116       bfd->lerror = io.lerror;
1117    }
1118    Dmsg1(50, "Return from plugin open status=%d\n", io.status);
1119    return io.status;
1120 }
1121
1122 static int my_plugin_bclose(BFILE *bfd)
1123 {
1124    JCR *jcr = bfd->jcr;
1125    Plugin *plugin = (Plugin *)jcr->plugin;
1126    struct io_pkt io;
1127
1128    Dmsg0(dbglvl, "===== plugin_bclose\n");
1129    if (!plugin || !jcr->plugin_ctx) {
1130       return 0;
1131    }
1132    io.pkt_size = sizeof(io);
1133    io.pkt_end = sizeof(io);
1134    io.func = IO_CLOSE;
1135    io.count = 0;
1136    io.buf = NULL;
1137    io.win32 = false;
1138    io.lerror = 0;
1139    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1140    bfd->berrno = io.io_errno;
1141    if (io.win32) {
1142       errno = b_errno_win32;
1143    } else {
1144       errno = io.io_errno;
1145       bfd->lerror = io.lerror;
1146    }
1147    Dmsg1(dbglvl, "plugin_bclose stat=%d\n", io.status);
1148    return io.status;
1149 }
1150
1151 static ssize_t my_plugin_bread(BFILE *bfd, void *buf, size_t count)
1152 {
1153    JCR *jcr = bfd->jcr;
1154    Plugin *plugin = (Plugin *)jcr->plugin;
1155    struct io_pkt io;
1156
1157    Dmsg0(dbglvl, "plugin_bread\n");
1158    if (!plugin || !jcr->plugin_ctx) {
1159       return 0;
1160    }
1161    io.pkt_size = sizeof(io);
1162    io.pkt_end = sizeof(io);
1163    io.func = IO_READ;
1164    io.count = count;
1165    io.buf = (char *)buf;
1166    io.win32 = false;
1167    io.offset = 0;
1168    io.lerror = 0;
1169    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1170    bfd->offset = io.offset;
1171    bfd->berrno = io.io_errno;
1172    if (io.win32) {
1173       errno = b_errno_win32;
1174    } else {
1175       errno = io.io_errno;
1176       bfd->lerror = io.lerror;
1177    }
1178    return (ssize_t)io.status;
1179 }
1180
1181 static ssize_t my_plugin_bwrite(BFILE *bfd, void *buf, size_t count)
1182 {
1183    JCR *jcr = bfd->jcr;
1184    Plugin *plugin = (Plugin *)jcr->plugin;
1185    struct io_pkt io;
1186
1187    Dmsg0(dbglvl, "plugin_bwrite\n");
1188    if (!plugin || !jcr->plugin_ctx) {
1189       return 0;
1190    }
1191    io.pkt_size = sizeof(io);
1192    io.pkt_end = sizeof(io);
1193    io.func = IO_WRITE;
1194    io.count = count;
1195    io.buf = (char *)buf;
1196    io.win32 = false;
1197    io.lerror = 0;
1198    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1199    bfd->berrno = io.io_errno;
1200    if (io.win32) {
1201       errno = b_errno_win32;
1202    } else {
1203       errno = io.io_errno;
1204       bfd->lerror = io.lerror;
1205    }
1206    return (ssize_t)io.status;
1207 }
1208
1209 static boffset_t my_plugin_blseek(BFILE *bfd, boffset_t offset, int whence)
1210 {
1211    JCR *jcr = bfd->jcr;
1212    Plugin *plugin = (Plugin *)jcr->plugin;
1213    struct io_pkt io;
1214
1215    Dmsg0(dbglvl, "plugin_bseek\n");
1216    if (!plugin || !jcr->plugin_ctx) {
1217       return 0;
1218    }
1219    io.pkt_size = sizeof(io);
1220    io.pkt_end = sizeof(io);
1221    io.func = IO_SEEK;
1222    io.offset = offset;
1223    io.whence = whence;
1224    io.win32 = false;
1225    io.lerror = 0;
1226    plug_func(plugin)->pluginIO(jcr->plugin_ctx, &io);
1227    bfd->berrno = io.io_errno;
1228    if (io.win32) {
1229       errno = b_errno_win32;
1230    } else {
1231       errno = io.io_errno;
1232       bfd->lerror = io.lerror;
1233    }
1234    return (boffset_t)io.offset;
1235 }
1236
1237 /* ==============================================================
1238  *
1239  * Callbacks from the plugin
1240  *
1241  * ==============================================================
1242  */
1243 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
1244 {
1245    JCR *jcr;
1246    if (!value) {
1247       return bRC_Error;
1248    }
1249
1250    switch (var) {               /* General variables, no need of ctx */
1251    case bVarFDName:
1252       *((char **)value) = my_name;
1253       break;
1254    case bVarWorkingDir:
1255       *(void **)value = me->working_directory;
1256       break;
1257    case bVarExePath:
1258       *(char **)value = exepath;
1259       break;
1260    case bVarVersion:
1261       *(char **)value = version;
1262       break;
1263    case bVarDistName:
1264       *(char **)value = dist_name;
1265       break;
1266    case bVarBEEF:
1267       *((int *)value) = beef;
1268       break;
1269    default:
1270       break;
1271    }
1272
1273    if (!ctx) {                  /* Other variables need context */
1274       return bRC_Error;
1275    }
1276
1277    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1278    if (!jcr) {
1279       return bRC_Error;
1280    }
1281
1282    switch (var) {
1283    case bVarJobId:
1284       *((int *)value) = jcr->JobId;
1285       Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
1286       break;
1287    case bVarLevel:
1288       *((int *)value) = jcr->getJobLevel();
1289       Dmsg1(dbglvl, "Bacula: return bVarJobLevel=%d\n", jcr->getJobLevel());
1290       break;
1291    case bVarType:
1292       *((int *)value) = jcr->getJobType();
1293       Dmsg1(dbglvl, "Bacula: return bVarJobType=%d\n", jcr->getJobType());
1294       break;
1295    case bVarClient:
1296       *((char **)value) = jcr->client_name;
1297       Dmsg1(dbglvl, "Bacula: return Client_name=%s\n", jcr->client_name);
1298       break;
1299    case bVarJobName:
1300       *((char **)value) = jcr->Job;
1301       Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
1302       break;
1303    case bVarPrevJobName:
1304       *((char **)value) = jcr->PrevJob;
1305       Dmsg1(dbglvl, "Bacula: return Previous Job name=%s\n", jcr->PrevJob);
1306       break;
1307    case bVarJobStatus:
1308       *((int *)value) = jcr->JobStatus;
1309       Dmsg1(dbglvl, "Bacula: return bVarJobStatus=%d\n", jcr->JobStatus);
1310       break;
1311    case bVarSinceTime:
1312       *((int *)value) = (int)jcr->mtime;
1313       Dmsg1(dbglvl, "Bacula: return since=%d\n", (int)jcr->mtime);
1314       break;
1315    case bVarAccurate:
1316       *((int *)value) = (int)jcr->accurate;
1317       Dmsg1(dbglvl, "Bacula: return accurate=%d\n", (int)jcr->accurate);
1318       break;
1319    case bVarFileSeen:
1320       break;                 /* a write only variable, ignore read request */
1321    case bVarVssObject:
1322 #ifdef HAVE_WIN32
1323       if (g_pVSSClient) {
1324          *(void **)value = g_pVSSClient->GetVssObject();
1325          break;
1326        }
1327 #endif
1328        return bRC_Error;
1329    case bVarVssDllHandle:
1330 #ifdef HAVE_WIN32
1331       if (g_pVSSClient) {
1332          *(void **)value = g_pVSSClient->GetVssDllHandle();
1333          break;
1334        }
1335 #endif
1336        return bRC_Error;
1337    case bVarWhere:
1338       *(char **)value = jcr->where;
1339       break;
1340    case bVarRegexWhere:
1341       *(char **)value = jcr->RegexWhere;
1342       break;
1343
1344    case bVarFDName:             /* get warning with g++ if we missed one */
1345    case bVarWorkingDir:
1346    case bVarExePath:
1347    case bVarVersion:
1348    case bVarDistName:
1349    case bVarBEEF:
1350       break;
1351    }
1352    return bRC_OK;
1353 }
1354
1355 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
1356 {
1357    JCR *jcr;
1358    if (!value || !ctx) {
1359       return bRC_Error;
1360    }
1361 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
1362    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1363    if (!jcr) {
1364       return bRC_Error;
1365    }
1366 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
1367    switch (var) {
1368    case bVarFileSeen:
1369       if (!accurate_mark_file_as_seen(jcr, (char *)value)) {
1370          return bRC_Error;
1371       } 
1372       break;
1373    default:
1374       break;
1375    }
1376    return bRC_OK;
1377 }
1378
1379 static bRC baculaRegisterEvents(bpContext *ctx, ...)
1380 {
1381    va_list args;
1382    uint32_t event;
1383
1384    if (!ctx) {
1385       return bRC_Error;
1386    }
1387
1388    va_start(args, ctx);
1389    while ((event = va_arg(args, uint32_t))) {
1390       Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
1391    }
1392    va_end(args);
1393    return bRC_OK;
1394 }
1395
1396 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
1397   int type, utime_t mtime, const char *fmt, ...)
1398 {
1399    va_list arg_ptr;
1400    char buf[2000];
1401    JCR *jcr;
1402
1403    if (ctx) {
1404       jcr = ((bacula_ctx *)ctx->bContext)->jcr;
1405    } else {
1406       jcr = NULL;
1407    }
1408
1409    va_start(arg_ptr, fmt);
1410    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1411    va_end(arg_ptr);
1412    Jmsg(jcr, type, mtime, "%s", buf);
1413    return bRC_OK;
1414 }
1415
1416 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
1417   int level, const char *fmt, ...)
1418 {
1419    va_list arg_ptr;
1420    char buf[2000];
1421
1422    va_start(arg_ptr, fmt);
1423    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
1424    va_end(arg_ptr);
1425    d_msg(file, line, level, "%s", buf);
1426    return bRC_OK;
1427 }
1428
1429 static void *baculaMalloc(bpContext *ctx, const char *file, int line,
1430               size_t size)
1431 {
1432 #ifdef SMARTALLOC
1433    return sm_malloc(file, line, size);
1434 #else
1435    return malloc(size);
1436 #endif
1437 }
1438
1439 static void baculaFree(bpContext *ctx, const char *file, int line, void *mem)
1440 {
1441 #ifdef SMARTALLOC
1442    sm_free(file, line, mem);
1443 #else
1444    free(mem);
1445 #endif
1446 }
1447
1448 static bool is_ctx_good(bpContext *ctx, JCR *&jcr, bacula_ctx *&bctx)
1449 {
1450    if (!ctx) {
1451       return false;
1452    }
1453    bctx = (bacula_ctx *)ctx->bContext;
1454    if (!bctx) {
1455       return false;
1456    }
1457    jcr = bctx->jcr;
1458    if (!jcr) {
1459       return false;
1460    }
1461    return true;
1462 }
1463
1464 /**
1465  * Let the plugin define files/directories to be excluded
1466  *  from the main backup.
1467  */
1468 static bRC baculaAddExclude(bpContext *ctx, const char *file)
1469 {
1470    JCR *jcr;
1471    bacula_ctx *bctx;
1472    if (!is_ctx_good(ctx, jcr, bctx)) {
1473       return bRC_Error;
1474    }
1475    if (!file) {
1476       return bRC_Error;
1477    }
1478    if (!bctx->exclude) {  
1479       bctx->exclude = new_exclude(jcr);
1480       new_options(jcr, bctx->exclude);
1481    }
1482    set_incexe(jcr, bctx->exclude);
1483    add_file_to_fileset(jcr, file, true);
1484    Dmsg1(100, "Add exclude file=%s\n", file);
1485    return bRC_OK;
1486 }
1487
1488 /**
1489  * Let the plugin define files/directories to be excluded
1490  *  from the main backup.
1491  */
1492 static bRC baculaAddInclude(bpContext *ctx, const char *file)
1493 {
1494    JCR *jcr;
1495    bacula_ctx *bctx;
1496    if (!is_ctx_good(ctx, jcr, bctx)) {
1497       return bRC_Error;
1498    }
1499    if (!file) {
1500       return bRC_Error;
1501    }
1502    /* Not right time to add include */
1503    if (!(jcr->ff && jcr->ff->fileset && jcr->ff->fileset->incexe)) {
1504       return bRC_Error;
1505    }
1506    if (!bctx->include) {
1507       bctx->include = jcr->ff->fileset->incexe;
1508    }
1509    set_incexe(jcr, bctx->include);
1510    add_file_to_fileset(jcr, file, true);
1511    Dmsg1(100, "Add include file=%s\n", file);
1512    return bRC_OK;
1513 }
1514
1515 static bRC baculaAddOptions(bpContext *ctx, const char *opts)
1516 {
1517    JCR *jcr;
1518    bacula_ctx *bctx;
1519    if (!is_ctx_good(ctx, jcr, bctx)) {
1520       return bRC_Error;
1521    }
1522    if (!opts) {
1523       return bRC_Error;
1524    }
1525    add_options_to_fileset(jcr, opts);
1526    Dmsg1(1000, "Add options=%s\n", opts);
1527    return bRC_OK;
1528 }
1529
1530 static bRC baculaAddRegex(bpContext *ctx, const char *item, int type)
1531 {
1532    JCR *jcr;
1533    bacula_ctx *bctx;
1534    if (!is_ctx_good(ctx, jcr, bctx)) {
1535       return bRC_Error;
1536    }
1537    if (!item) {
1538       return bRC_Error;
1539    }
1540    add_regex_to_fileset(jcr, item, type);
1541    Dmsg1(100, "Add regex=%s\n", item);
1542    return bRC_OK;
1543 }
1544
1545 static bRC baculaAddWild(bpContext *ctx, const char *item, int type)
1546 {
1547    JCR *jcr;
1548    bacula_ctx *bctx;
1549    if (!is_ctx_good(ctx, jcr, bctx)) {
1550       return bRC_Error;
1551    }
1552    if (!item) {
1553       return bRC_Error;
1554    }
1555    add_wild_to_fileset(jcr, item, type);
1556    Dmsg1(100, "Add wild=%s\n", item);
1557    return bRC_OK;
1558 }
1559
1560 static bRC baculaNewOptions(bpContext *ctx)
1561 {
1562    JCR *jcr;
1563    bacula_ctx *bctx;
1564    if (!is_ctx_good(ctx, jcr, bctx)) {
1565       return bRC_Error;
1566    }
1567    (void)new_options(jcr, NULL);
1568    return bRC_OK;
1569 }
1570
1571 static bRC baculaNewInclude(bpContext *ctx)
1572 {
1573    JCR *jcr;
1574    bacula_ctx *bctx;
1575    if (!is_ctx_good(ctx, jcr, bctx)) {
1576       return bRC_Error;
1577    }
1578    (void)new_include(jcr);
1579    return bRC_OK;
1580 }
1581
1582 static bRC baculaNewPreInclude(bpContext *ctx)
1583 {
1584    JCR *jcr;
1585    bacula_ctx *bctx;
1586    if (!is_ctx_good(ctx, jcr, bctx)) {
1587       return bRC_Error;
1588    }
1589
1590    bctx->include = new_preinclude(jcr);
1591    new_options(jcr, bctx->include);
1592    set_incexe(jcr, bctx->include);
1593
1594    return bRC_OK;
1595 }
1596
1597 /* 
1598  * Check if a file have to be backuped using Accurate code
1599  */
1600 static bRC baculaCheckChanges(bpContext *ctx, struct save_pkt *sp)
1601 {
1602    JCR *jcr;
1603    bacula_ctx *bctx;
1604    FF_PKT *ff_pkt;
1605    bRC ret = bRC_Error;
1606
1607    if (!is_ctx_good(ctx, jcr, bctx)) {
1608       goto bail_out;
1609    }
1610    if (!sp) {
1611       goto bail_out;
1612    }
1613    
1614    ff_pkt = jcr->ff;
1615    /*
1616     * Copy fname and link because save_file() zaps them.  This 
1617     *  avoids zaping the plugin's strings.
1618     */
1619    ff_pkt->type = sp->type;
1620    if (!sp->fname) {
1621       Jmsg0(jcr, M_FATAL, 0, _("Command plugin: no fname in baculaCheckChanges packet.\n"));
1622       goto bail_out;
1623    }
1624
1625    ff_pkt->fname = sp->fname;
1626    ff_pkt->link = sp->link;
1627    memcpy(&ff_pkt->statp, &sp->statp, sizeof(ff_pkt->statp));
1628
1629    if (check_changes(jcr, ff_pkt))  {
1630       ret = bRC_OK;
1631    } else {
1632       ret = bRC_Seen;
1633    }
1634
1635    /* check_changes() can update delta sequence number, return it to the
1636     * plugin 
1637     */
1638    sp->delta_seq = ff_pkt->delta_seq;
1639
1640 bail_out:
1641    Dmsg1(100, "checkChanges=%i\n", ret);
1642    return ret;
1643 }
1644
1645
1646 #ifdef TEST_PROGRAM
1647
1648 int     (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
1649 int     (*plugin_bclose)(JCR *jcr) = NULL;
1650 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
1651 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
1652 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
1653
1654 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
1655 {
1656    return 0;
1657 }
1658
1659 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
1660 {
1661    return true;
1662 }
1663
1664 int main(int argc, char *argv[])
1665 {
1666    char plugin_dir[1000];
1667    JCR mjcr1, mjcr2;
1668    JCR *jcr1 = &mjcr1;
1669    JCR *jcr2 = &mjcr2;
1670
1671    strcpy(my_name, "test-fd");
1672     
1673    getcwd(plugin_dir, sizeof(plugin_dir)-1);
1674    load_fd_plugins(plugin_dir);
1675
1676    jcr1->JobId = 111;
1677    new_plugins(jcr1);
1678
1679    jcr2->JobId = 222;
1680    new_plugins(jcr2);
1681
1682    generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
1683    generate_plugin_event(jcr1, bEventJobEnd);
1684    generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
1685    free_plugins(jcr1);
1686    generate_plugin_event(jcr2, bEventJobEnd);
1687    free_plugins(jcr2);
1688
1689    unload_plugins();
1690
1691    Dmsg0(dbglvl, "bacula: OK ...\n");
1692    close_memory_pool();
1693    sm_dump(false);     /* unit test */
1694    return 0;
1695 }
1696
1697 #endif /* TEST_PROGRAM */