1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2007-2012 Free Software Foundation Europe e.V.
5    Copyright (C) 2014-2020 Bareos GmbH & Co. KG
6 
7    This program is Free Software; you can redistribute it and/or
8    modify it under the terms of version three of the GNU Affero General Public
9    License as published by the Free Software Foundation, which is
10    listed in the file LICENSE.
11 
12    This program is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15    Affero General Public License for more details.
16 
17    You should have received a copy of the GNU Affero General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20    02110-1301, USA.
21 */
22 /*
23  * Kern Sibbald, October 2007
24  */
25 /**
26  * @file
27  * A simple pipe plugin for the Bareos File Daemon
28  */
29 #include "include/bareos.h"
30 #include "filed/fd_plugins.h"
31 #include "plugins/include/common.h"
32 
33 namespace filedaemon {
34 
35 static const int debuglevel = 150;
36 
37 #define PLUGIN_LICENSE "Bareos AGPLv3"
38 #define PLUGIN_AUTHOR "Kern Sibbald"
39 #define PLUGIN_DATE "January 2014"
40 #define PLUGIN_VERSION "2"
41 #define PLUGIN_DESCRIPTION "Bareos Pipe File Daemon Plugin"
42 #define PLUGIN_USAGE                                                   \
43   "bpipe:file=<filepath>:reader=<readprogram>:writer=<writeprogram>\n" \
44   " readprogram runs on backup and its stdout is saved\n"              \
45   " writeprogram runs on restore and gets restored data into stdin\n"  \
46   " the data is internally stored as filepath (e.g. mybackup/backup1)"
47 
48 /* Forward referenced functions */
49 static bRC newPlugin(PluginContext* ctx);
50 static bRC freePlugin(PluginContext* ctx);
51 static bRC getPluginValue(PluginContext* ctx, pVariable var, void* value);
52 static bRC setPluginValue(PluginContext* ctx, pVariable var, void* value);
53 static bRC handlePluginEvent(PluginContext* ctx, bEvent* event, void* value);
54 static bRC startBackupFile(PluginContext* ctx, struct save_pkt* sp);
55 static bRC endBackupFile(PluginContext* ctx);
56 static bRC pluginIO(PluginContext* ctx, struct io_pkt* io);
57 static bRC startRestoreFile(PluginContext* ctx, const char* cmd);
58 static bRC endRestoreFile(PluginContext* ctx);
59 static bRC createFile(PluginContext* ctx, struct restore_pkt* rp);
60 static bRC setFileAttributes(PluginContext* ctx, struct restore_pkt* rp);
61 static bRC checkFile(PluginContext* ctx, char* fname);
62 static bRC getAcl(PluginContext* ctx, acl_pkt* ap);
63 static bRC setAcl(PluginContext* ctx, acl_pkt* ap);
64 static bRC getXattr(PluginContext* ctx, xattr_pkt* xp);
65 static bRC setXattr(PluginContext* ctx, xattr_pkt* xp);
66 
67 static char* apply_rp_codes(PluginContext* ctx);
68 static bRC parse_plugin_definition(PluginContext* ctx, void* value);
69 static bRC plugin_has_all_arguments(PluginContext* ctx);
70 
71 /* Pointers to Bareos functions */
72 static CoreFunctions* bareos_core_functions = NULL;
73 static PluginApiDefinition* bareos_plugin_interface_version = NULL;
74 
75 /* Plugin Information block */
76 static PluginInformation pluginInfo
77     = {sizeof(pluginInfo), FD_PLUGIN_INTERFACE_VERSION,
78        FD_PLUGIN_MAGIC,    PLUGIN_LICENSE,
79        PLUGIN_AUTHOR,      PLUGIN_DATE,
80        PLUGIN_VERSION,     PLUGIN_DESCRIPTION,
81        PLUGIN_USAGE};
82 
83 /* Plugin entry points for Bareos */
84 static PluginFunctions pluginFuncs
85     = {sizeof(pluginFuncs), FD_PLUGIN_INTERFACE_VERSION,
86 
87        /* Entry points into plugin */
88        newPlugin,  /* new plugin instance */
89        freePlugin, /* free plugin instance */
90        getPluginValue, setPluginValue, handlePluginEvent, startBackupFile,
91        endBackupFile, startRestoreFile, endRestoreFile, pluginIO, createFile,
92        setFileAttributes, checkFile, getAcl, setAcl, getXattr, setXattr};
93 
94 /**
95  * Plugin private context
96  */
97 struct plugin_ctx {
98   boffset_t offset;
99   Bpipe* pfd;           /* bpipe() descriptor */
100   char* plugin_options; /* Override of plugin options passed in */
101   char* fname;          /* Filename to "backup/restore" */
102   char* reader;         /* Reader program for backup */
103   char* writer;         /* Writer program for backup */
104 
105   char where[512];
106   int replace;
107 };
108 
109 /**
110  * This defines the arguments that the plugin parser understands.
111  */
112 enum plugin_argument_type
113 {
114   argument_none = 0,
115   argument_file,
116   argument_reader,
117   argument_writer
118 };
119 
120 struct plugin_argument {
121   const char* name;
122   enum plugin_argument_type type;
123   int cmp_length;
124 };
125 
126 static plugin_argument plugin_arguments[] = {{"file=", argument_file, 4},
127                                              {"reader=", argument_reader, 6},
128                                              {"writer=", argument_writer, 6},
129                                              {NULL, argument_none, 0}};
130 
131 #ifdef __cplusplus
132 extern "C" {
133 #endif
134 
135 /**
136  * loadPlugin() and unloadPlugin() are entry points that are
137  *  exported, so Bareos can directly call these two entry points
138  *  they are common to all Bareos plugins.
139  */
140 /**
141  * External entry point called by Bareos to "load" the plugin
142  */
loadPlugin(PluginApiDefinition * lbareos_plugin_interface_version,CoreFunctions * lbareos_core_functions,PluginInformation ** plugin_information,PluginFunctions ** plugin_functions)143 bRC loadPlugin(PluginApiDefinition* lbareos_plugin_interface_version,
144                CoreFunctions* lbareos_core_functions,
145                PluginInformation** plugin_information,
146                PluginFunctions** plugin_functions)
147 {
148   bareos_core_functions
149       = lbareos_core_functions; /* set Bareos funct pointers */
150   bareos_plugin_interface_version = lbareos_plugin_interface_version;
151   *plugin_information = &pluginInfo; /* return pointer to our info */
152   *plugin_functions = &pluginFuncs;  /* return pointer to our functions */
153 
154   return bRC_OK;
155 }
156 
157 /**
158  * External entry point to unload the plugin
159  */
unloadPlugin()160 bRC unloadPlugin() { return bRC_OK; }
161 
162 #ifdef __cplusplus
163 }
164 #endif
165 
166 /**
167  * The following entry points are accessed through the function
168  *   pointers we supplied to Bareos. Each plugin type (dir, fd, sd)
169  *   has its own set of entry points that the plugin must define.
170  */
171 /**
172  * Create a new instance of the plugin i.e. allocate our private storage
173  */
newPlugin(PluginContext * ctx)174 static bRC newPlugin(PluginContext* ctx)
175 {
176   struct plugin_ctx* p_ctx
177       = (struct plugin_ctx*)malloc(sizeof(struct plugin_ctx));
178   if (!p_ctx) { return bRC_Error; }
179   memset(p_ctx, 0, sizeof(struct plugin_ctx));
180   ctx->plugin_private_context = (void*)p_ctx; /* set our context pointer */
181 
182   bareos_core_functions->registerBareosEvents(
183       ctx, 6, bEventNewPluginOptions, bEventPluginCommand, bEventJobStart,
184       bEventRestoreCommand, bEventEstimateCommand, bEventBackupCommand);
185 
186   return bRC_OK;
187 }
188 
189 /**
190  * Free a plugin instance, i.e. release our private storage
191  */
freePlugin(PluginContext * ctx)192 static bRC freePlugin(PluginContext* ctx)
193 {
194   struct plugin_ctx* p_ctx = (struct plugin_ctx*)ctx->plugin_private_context;
195 
196   if (!p_ctx) { return bRC_Error; }
197 
198   if (p_ctx->fname) { free(p_ctx->fname); }
199 
200   if (p_ctx->reader) { free(p_ctx->reader); }
201 
202   if (p_ctx->writer) { free(p_ctx->writer); }
203 
204   if (p_ctx->plugin_options) { free(p_ctx->plugin_options); }
205 
206   free(p_ctx); /* free our private context */
207   p_ctx = NULL;
208   return bRC_OK;
209 }
210 
211 /**
212  * Return some plugin value (none defined)
213  */
getPluginValue(PluginContext * ctx,pVariable var,void * value)214 static bRC getPluginValue(PluginContext* ctx, pVariable var, void* value)
215 {
216   return bRC_OK;
217 }
218 
219 /**
220  * Set a plugin value (none defined)
221  */
setPluginValue(PluginContext * ctx,pVariable var,void * value)222 static bRC setPluginValue(PluginContext* ctx, pVariable var, void* value)
223 {
224   return bRC_OK;
225 }
226 
227 /**
228  * Handle an event that was generated in Bareos
229  */
handlePluginEvent(PluginContext * ctx,bEvent * event,void * value)230 static bRC handlePluginEvent(PluginContext* ctx, bEvent* event, void* value)
231 {
232   bRC retval = bRC_OK;
233   struct plugin_ctx* p_ctx = (struct plugin_ctx*)ctx->plugin_private_context;
234 
235   if (!p_ctx) { return bRC_Error; }
236 
237   switch (event->eventType) {
238     case bEventJobStart:
239       Dmsg(ctx, debuglevel, "bpipe-fd: JobStart=%s\n", (char*)value);
240       break;
241     case bEventRestoreCommand:
242       /*
243        * Fall-through wanted
244        */
245     case bEventBackupCommand:
246       /*
247        * Fall-through wanted
248        */
249     case bEventEstimateCommand:
250       /*
251        * Fall-through wanted
252        */
253     case bEventPluginCommand:
254       retval = parse_plugin_definition(ctx, value);
255       break;
256     case bEventNewPluginOptions:
257       /*
258        * Free any previous value.
259        */
260       if (p_ctx->plugin_options) {
261         free(p_ctx->plugin_options);
262         p_ctx->plugin_options = NULL;
263       }
264 
265       retval = parse_plugin_definition(ctx, value);
266 
267       /*
268        * Save that we got a plugin override.
269        */
270       p_ctx->plugin_options = strdup((char*)value);
271       break;
272     default:
273       Jmsg(ctx, M_FATAL, "bpipe-fd: unknown event=%d\n", event->eventType);
274       Dmsg(ctx, debuglevel, "bpipe-fd: unknown event=%d\n", event->eventType);
275       retval = bRC_Error;
276       break;
277   }
278 
279   return retval;
280 }
281 
282 /**
283  * Start the backup of a specific file
284  */
startBackupFile(PluginContext * ctx,struct save_pkt * sp)285 static bRC startBackupFile(PluginContext* ctx, struct save_pkt* sp)
286 {
287   time_t now;
288   struct plugin_ctx* p_ctx;
289 
290   if (plugin_has_all_arguments(ctx) != bRC_OK) { return bRC_Error; }
291 
292   p_ctx = (struct plugin_ctx*)ctx->plugin_private_context;
293   if (!p_ctx) { return bRC_Error; }
294 
295   now = time(NULL);
296   sp->fname = p_ctx->fname;
297   sp->type = FT_REG;
298   sp->statp.st_mode = 0700 | S_IFREG;
299   sp->statp.st_ctime = now;
300   sp->statp.st_mtime = now;
301   sp->statp.st_atime = now;
302   sp->statp.st_size = -1;
303   sp->statp.st_blksize = 4096;
304   sp->statp.st_blocks = 1;
305 
306   return bRC_OK;
307 }
308 
309 /**
310  * Done with backup of this file
311  */
endBackupFile(PluginContext * ctx)312 static bRC endBackupFile(PluginContext* ctx)
313 {
314   /*
315    * We would return bRC_More if we wanted startBackupFile to be called again to
316    * backup another file
317    */
318   return bRC_OK;
319 }
320 
321 /**
322  * Bareos is calling us to do the actual I/O
323  */
pluginIO(PluginContext * ctx,struct io_pkt * io)324 static bRC pluginIO(PluginContext* ctx, struct io_pkt* io)
325 {
326   struct plugin_ctx* p_ctx = (struct plugin_ctx*)ctx->plugin_private_context;
327   if (!p_ctx) { return bRC_Error; }
328 
329   io->status = 0;
330   io->io_errno = 0;
331   switch (io->func) {
332     case IO_OPEN:
333       Dmsg(ctx, debuglevel, "bpipe-fd: IO_OPEN\n");
334       if (io->flags & (O_CREAT | O_WRONLY)) {
335         char* writer_codes = apply_rp_codes(ctx);
336 
337         p_ctx->pfd = OpenBpipe(writer_codes, 0, "w");
338         Dmsg(ctx, debuglevel, "bpipe-fd: IO_OPEN fd=%p writer=%s\n", p_ctx->pfd,
339              writer_codes);
340         if (!p_ctx->pfd) {
341           io->io_errno = errno;
342           Jmsg(ctx, M_FATAL, "bpipe-fd: Open pipe writer=%s failed: ERR=%s\n",
343                writer_codes, strerror(io->io_errno));
344           Dmsg(ctx, debuglevel,
345                "bpipe-fd: Open pipe writer=%s failed: ERR=%s\n", writer_codes,
346                strerror(io->io_errno));
347           if (writer_codes) { free(writer_codes); }
348           return bRC_Error;
349         }
350         if (writer_codes) { free(writer_codes); }
351       } else {
352         p_ctx->pfd = OpenBpipe(p_ctx->reader, 0, "r", false);
353         Dmsg(ctx, debuglevel, "bpipe-fd: IO_OPEN fd=%p reader=%s\n", p_ctx->pfd,
354              p_ctx->reader);
355         if (!p_ctx->pfd) {
356           io->io_errno = errno;
357           Jmsg(ctx, M_FATAL, "bpipe-fd: Open pipe reader=%s failed: ERR=%s\n",
358                p_ctx->reader, strerror(io->io_errno));
359           Dmsg(ctx, debuglevel,
360                "bpipe-fd: Open pipe reader=%s failed: ERR=%s\n", p_ctx->reader,
361                strerror(io->io_errno));
362           return bRC_Error;
363         }
364       }
365       sleep(1); /* let pipe connect */
366       break;
367     case IO_READ:
368       if (!p_ctx->pfd) {
369         Jmsg(ctx, M_FATAL, "bpipe-fd: Logic error: NULL read FD\n");
370         Dmsg(ctx, debuglevel, "bpipe-fd: Logic error: NULL read FD\n");
371         return bRC_Error;
372       }
373       io->status = fread(io->buf, 1, io->count, p_ctx->pfd->rfd);
374       if (io->status == 0 && ferror(p_ctx->pfd->rfd)) {
375         io->io_errno = errno;
376         Jmsg(ctx, M_FATAL, "bpipe-fd: Pipe read error: ERR=%s\n",
377              strerror(io->io_errno));
378         Dmsg(ctx, debuglevel, "bpipe-fd: Pipe read error: ERR=%s\n",
379              strerror(io->io_errno));
380         return bRC_Error;
381       }
382       break;
383     case IO_WRITE:
384       if (!p_ctx->pfd) {
385         Jmsg(ctx, M_FATAL, "bpipe-fd: Logic error: NULL write FD\n");
386         Dmsg(ctx, debuglevel, "bpipe-fd: Logic error: NULL write FD\n");
387         return bRC_Error;
388       }
389       io->status = fwrite(io->buf, 1, io->count, p_ctx->pfd->wfd);
390       if (io->status == 0 && ferror(p_ctx->pfd->wfd)) {
391         io->io_errno = errno;
392         Jmsg(ctx, M_FATAL, "bpipe-fd: Pipe write error: ERR=%s\n",
393              strerror(io->io_errno));
394         Dmsg(ctx, debuglevel, "bpipe-fd: Pipe write error: ERR=%s\n",
395              strerror(io->io_errno));
396         return bRC_Error;
397       }
398       break;
399     case IO_CLOSE:
400       if (!p_ctx->pfd) {
401         Jmsg(ctx, M_FATAL, "bpipe-fd: Logic error: NULL FD on bpipe close\n");
402         Dmsg(ctx, debuglevel,
403              "bpipe-fd: Logic error: NULL FD on bpipe close\n");
404         return bRC_Error;
405       }
406       io->status = CloseBpipe(p_ctx->pfd);
407       if (io->status) {
408         Jmsg(ctx, M_FATAL,
409              "bpipe-fd: Error closing stream for pseudo file %s: %d\n",
410              p_ctx->fname, io->status);
411         Dmsg(ctx, debuglevel,
412              "bpipe-fd: Error closing stream for pseudo file %s: %d\n",
413              p_ctx->fname, io->status);
414       }
415       break;
416     case IO_SEEK:
417       io->offset = p_ctx->offset;
418       break;
419   }
420 
421   return bRC_OK;
422 }
423 
424 /**
425  * Bareos is notifying us that a plugin name string was found, and
426  *   passing us the plugin command, so we can prepare for a restore.
427  */
startRestoreFile(PluginContext * ctx,const char * cmd)428 static bRC startRestoreFile(PluginContext* ctx, const char* cmd)
429 {
430   if (plugin_has_all_arguments(ctx) != bRC_OK) { return bRC_Error; }
431 
432   return bRC_OK;
433 }
434 
435 /**
436  * Bareos is notifying us that the plugin data has terminated, so
437  *  the restore for this particular file is done.
438  */
endRestoreFile(PluginContext * ctx)439 static bRC endRestoreFile(PluginContext* ctx)
440 {
441   struct plugin_ctx* p_ctx = (struct plugin_ctx*)ctx->plugin_private_context;
442   if (!p_ctx) { return bRC_Error; }
443 
444   return bRC_OK;
445 }
446 
447 /**
448  * This is called during restore to create the file (if necessary)
449  * We must return in rp->create_status:
450  *
451  * CF_ERROR    -- error
452  * CF_SKIP     -- skip processing this file
453  * CF_EXTRACT  -- extract the file (i.e.call i/o routines)
454  * CF_CREATED  -- created, but no content to extract (typically directories)
455  */
createFile(PluginContext * ctx,struct restore_pkt * rp)456 static bRC createFile(PluginContext* ctx, struct restore_pkt* rp)
457 {
458   if (strlen(rp->where) > 512) {
459     printf(
460         "bpipe-fd: Restore target dir too long. Restricting to first 512 "
461         "bytes.\n");
462   }
463 
464   bstrncpy(((struct plugin_ctx*)ctx->plugin_private_context)->where, rp->where,
465            513);
466   ((struct plugin_ctx*)ctx->plugin_private_context)->replace = rp->replace;
467 
468   rp->create_status = CF_EXTRACT;
469   return bRC_OK;
470 }
471 
setFileAttributes(PluginContext * ctx,struct restore_pkt * rp)472 static bRC setFileAttributes(PluginContext* ctx, struct restore_pkt* rp)
473 {
474   return bRC_OK;
475 }
476 
477 /**
478  * When using Incremental dump, all previous dumps are necessary
479  */
checkFile(PluginContext * ctx,char * fname)480 static bRC checkFile(PluginContext* ctx, char* fname) { return bRC_OK; }
481 
getAcl(PluginContext * ctx,acl_pkt * ap)482 static bRC getAcl(PluginContext* ctx, acl_pkt* ap) { return bRC_OK; }
483 
setAcl(PluginContext * ctx,acl_pkt * ap)484 static bRC setAcl(PluginContext* ctx, acl_pkt* ap) { return bRC_OK; }
485 
getXattr(PluginContext * ctx,xattr_pkt * xp)486 static bRC getXattr(PluginContext* ctx, xattr_pkt* xp) { return bRC_OK; }
487 
setXattr(PluginContext * ctx,xattr_pkt * xp)488 static bRC setXattr(PluginContext* ctx, xattr_pkt* xp) { return bRC_OK; }
489 
490 /**
491  * Apply codes in writer command:
492  * %w -> "where"
493  * %r -> "replace"
494  *
495  * Replace:
496  * 'always' => 'a', chr(97)
497  * 'ifnewer' => 'w', chr(119)
498  * 'ifolder' => 'o', chr(111)
499  * 'never' => 'n', chr(110)
500  *
501  * This function will allocate the required amount of memory with malloc.
502  * Need to be free()d manually.
503  *
504  * Inspired by edit_job_codes in lib/util.c
505  */
apply_rp_codes(PluginContext * ctx)506 static char* apply_rp_codes(PluginContext* ctx)
507 {
508   char add[10];
509   const char* str;
510   char *p, *q, *omsg, *imsg;
511   int w_count = 0, r_count = 0;
512   struct plugin_ctx* p_ctx = (struct plugin_ctx*)ctx->plugin_private_context;
513 
514   if (!p_ctx) { return NULL; }
515 
516   imsg = p_ctx->writer;
517   if (!imsg) { return NULL; }
518 
519   if ((p = imsg)) {
520     while ((q = strstr(p, "%w"))) {
521       w_count++;
522       p = q + 1;
523     }
524 
525     p = imsg;
526     while ((q = strstr(p, "%r"))) {
527       r_count++;
528       p = q + 1;
529     }
530   }
531 
532   /*
533    * Required mem:
534    * len(imsg)
535    * + number of "where" codes * (len(where)-2)
536    * - number of "replace" codes
537    */
538   omsg = (char*)malloc(strlen(imsg) + (w_count * (strlen(p_ctx->where) - 2))
539                        - r_count + 1);
540   if (!omsg) {
541     Jmsg(ctx, M_FATAL, "bpipe-fd: Out of memory.");
542     return NULL;
543   }
544 
545   *omsg = 0;
546   for (p = imsg; *p; p++) {
547     if (*p == '%') {
548       switch (*++p) {
549         case '%':
550           str = "%";
551           break;
552         case 'w':
553           str = p_ctx->where;
554           break;
555         case 'r':
556           snprintf(add, 2, "%c", p_ctx->replace);
557           str = add;
558           break;
559         default:
560           add[0] = '%';
561           add[1] = *p;
562           add[2] = 0;
563           str = add;
564           break;
565       }
566     } else {
567       add[0] = *p;
568       add[1] = 0;
569       str = add;
570     }
571     strcat(omsg, str);
572   }
573   return omsg;
574 }
575 
576 /**
577  * Strip any backslashes in the string.
578  */
StripBackSlashes(char * value)579 static inline void StripBackSlashes(char* value)
580 {
581   char* bp;
582 
583   bp = value;
584   while (*bp) {
585     switch (*bp) {
586       case '\\':
587         bstrinlinecpy(bp, bp + 1);
588         break;
589       default:
590         break;
591     }
592 
593     bp++;
594   }
595 }
596 
597 /**
598  * Only set destination to value when it has no previous setting.
599  */
SetStringIfNull(char ** destination,char * value)600 static inline void SetStringIfNull(char** destination, char* value)
601 {
602   if (!*destination) {
603     *destination = strdup(value);
604     StripBackSlashes(*destination);
605   }
606 }
607 
608 /**
609  * Always set destination to value and clean any previous one.
610  */
SetString(char ** destination,char * value)611 static inline void SetString(char** destination, char* value)
612 {
613   if (*destination) { free(*destination); }
614 
615   *destination = strdup(value);
616   StripBackSlashes(*destination);
617 }
618 
619 /**
620  * Parse the plugin definition passed in.
621  *
622  * The definition is in this form:
623  *
624  * bpipe:file=<filepath>:read=<readprogram>:write=<writeprogram>
625  */
parse_plugin_definition(PluginContext * ctx,void * value)626 static bRC parse_plugin_definition(PluginContext* ctx, void* value)
627 {
628   int i, cnt;
629   char *plugin_definition, *bp, *argument, *argument_value;
630   plugin_ctx* p_ctx = (plugin_ctx*)ctx->plugin_private_context;
631   bool keep_existing;
632   bool compatible = true;
633 
634   if (!p_ctx || !value) { return bRC_Error; }
635 
636   keep_existing = (p_ctx->plugin_options) ? true : false;
637 
638   /*
639    * Parse the plugin definition.
640    * Make a private copy of the whole string.
641    */
642   plugin_definition = strdup((char*)value);
643 
644   bp = strchr(plugin_definition, ':');
645   if (!bp) {
646     Jmsg(ctx, M_FATAL, "bpipe-fd: Illegal plugin definition %s\n",
647          plugin_definition);
648     Dmsg(ctx, debuglevel, "bpipe-fd: Illegal plugin definition %s\n",
649          plugin_definition);
650     goto bail_out;
651   }
652 
653   /*
654    * Skip the first ':'
655    */
656   bp++;
657 
658   /*
659    * See if we are parsing a new plugin definition e.g. one with keywords.
660    */
661   argument = bp;
662   while (argument) {
663     if (strlen(argument) == 0) { break; }
664 
665     for (i = 0; plugin_arguments[i].name; i++) {
666       if (bstrncasecmp(argument, plugin_arguments[i].name,
667                        strlen(plugin_arguments[i].name))) {
668         compatible = false;
669         break;
670       }
671     }
672 
673     if (!plugin_arguments[i].name && !compatible) {
674       /*
675        * Parsing something fishy ? e.g. partly with known keywords.
676        */
677       Jmsg(ctx, M_FATAL,
678            "bpipe-fd: Found mixing of old and new syntax, please fix your "
679            "plugin definition (%s)\n",
680            plugin_definition);
681       Dmsg(ctx, debuglevel,
682            "bpipe-fd: Found mixing of old and new syntax, please fix your "
683            "plugin definition (%s)\n",
684            plugin_definition);
685       goto bail_out;
686     }
687 
688     argument = strchr(argument, ':');
689     if (argument) { argument++; }
690   }
691 
692   /*
693    * Start processing the definition, if compatible is left set we are
694    * pretending that we are parsing a plugin definition in the old syntax and
695    * hope for the best.
696    */
697   cnt = 1;
698   while (bp) {
699     if (strlen(bp) == 0) { break; }
700 
701     argument = bp;
702     if (compatible) {
703       char** str_destination = NULL;
704 
705       /*
706        * See if there are more arguments and setup for the next run.
707        */
708       do {
709         bp = strchr(bp, ':');
710         if (bp) {
711           if (*(bp - 1) != '\\') {
712             *bp++ = '\0';
713             break;
714           } else {
715             bp++;
716           }
717         }
718       } while (bp);
719 
720       /*
721        * See which field this is in the argument string.
722        */
723       switch (cnt) {
724         case 1:
725           str_destination = &p_ctx->fname;
726           break;
727         case 2:
728           str_destination = &p_ctx->reader;
729           break;
730         case 3:
731           str_destination = &p_ctx->writer;
732           break;
733         default:
734           break;
735       }
736 
737       if (str_destination) {
738         if (keep_existing) {
739           /*
740            * Keep the first value, ignore any next setting.
741            */
742           SetStringIfNull(str_destination, argument);
743         } else {
744           /*
745            * Overwrite any existing value.
746            */
747           SetString(str_destination, argument);
748         }
749       }
750     } else {
751       /*
752        * Each argument is in the form:
753        *    <argument> = <argument_value>
754        *
755        * So we setup the right pointers here, argument to the beginning
756        * of the argument, argument_value to the beginning of the argument_value.
757        */
758       argument_value = strchr(bp, '=');
759       *argument_value++ = '\0';
760 
761       /*
762        * See if there are more arguments and setup for the next run.
763        */
764       bp = argument_value;
765       do {
766         bp = strchr(bp, ':');
767         if (bp) {
768           if (*(bp - 1) != '\\') {
769             *bp++ = '\0';
770             break;
771           } else {
772             bp++;
773           }
774         }
775       } while (bp);
776 
777       for (i = 0; plugin_arguments[i].name; i++) {
778         if (bstrncasecmp(argument, plugin_arguments[i].name,
779                          plugin_arguments[i].cmp_length)) {
780           char** str_destination = NULL;
781 
782           switch (plugin_arguments[i].type) {
783             case argument_file:
784               if (!PathContainsDirectory(argument_value)) {
785                 Jmsg(ctx, M_FATAL,
786                      "bpipe-fd: file argument (%s) must contain a directory "
787                      "structure. Please fix your plugin definition\n",
788                      argument_value);
789                 Dmsg(ctx, debuglevel,
790                      "bpipe-fd: file argument (%s) must contain a directory "
791                      "structure. Please fix your plugin definition\n",
792                      argument_value);
793                 goto bail_out;
794               }
795               str_destination = &p_ctx->fname;
796               break;
797             case argument_reader:
798               str_destination = &p_ctx->reader;
799               break;
800             case argument_writer:
801               str_destination = &p_ctx->writer;
802               break;
803             default:
804               break;
805           }
806 
807           if (str_destination) {
808             if (keep_existing) {
809               /*
810                * Keep the first value, ignore any next setting.
811                */
812               SetStringIfNull(str_destination, argument_value);
813             } else {
814               /*
815                * Overwrite any existing value.
816                */
817               SetString(str_destination, argument_value);
818             }
819           }
820 
821           /*
822            * When we have a match break the loop.
823            */
824           break;
825         }
826       }
827 
828       /*
829        * Got an invalid keyword ?
830        */
831       if (!plugin_arguments[i].name) {
832         Jmsg(ctx, M_FATAL,
833              "bpipe-fd: Illegal argument %s with value %s in plugin "
834              "definition\n",
835              argument, argument_value);
836         Dmsg(ctx, debuglevel,
837              "bpipe-fd: Illegal argument %s with value %s in plugin "
838              "definition\n",
839              argument, argument_value);
840         goto bail_out;
841       }
842     }
843     cnt++;
844   }
845 
846   free(plugin_definition);
847   return bRC_OK;
848 
849 bail_out:
850   free(plugin_definition);
851   return bRC_Error;
852 }
853 
plugin_has_all_arguments(PluginContext * ctx)854 static bRC plugin_has_all_arguments(PluginContext* ctx)
855 {
856   bRC retval = bRC_OK;
857   plugin_ctx* p_ctx = (plugin_ctx*)ctx->plugin_private_context;
858 
859   if (!p_ctx) { retval = bRC_Error; }
860 
861   if (!p_ctx->fname) {
862     Jmsg(ctx, M_FATAL, _("bpipe-fd: Plugin File argument not specified.\n"));
863     Dmsg(ctx, debuglevel, "bpipe-fd: Plugin File argument not specified.\n");
864     retval = bRC_Error;
865   }
866 
867   if (!p_ctx->reader) {
868     Jmsg(ctx, M_FATAL, _("bpipe-fd: Plugin Reader argument not specified.\n"));
869     Dmsg(ctx, debuglevel, "bpipe-fd: Plugin Reader argument not specified.\n");
870     retval = bRC_Error;
871   }
872 
873   if (!p_ctx->writer) {
874     Jmsg(ctx, M_FATAL, _("bpipe-fd: Plugin Writer argument not specified.\n"));
875     Dmsg(ctx, debuglevel, "bpipe-fd: Plugin Writer argument not specified.\n");
876     retval = bRC_Error;
877   }
878 
879   return retval;
880 }
881 } /* namespace filedaemon */
882