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