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