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