2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2008 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation, which is
11 listed in the file LICENSE.
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * Main program to test loading and running Bacula plugins.
30 * Destined to become Bacula pluginloader, ...
32 * Kern Sibbald, October 2007
37 const int dbglvl = 50;
38 const char *plugin_type = "-fd.so";
40 extern int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
42 /* Function pointers to be set here */
43 extern DLL_IMP_EXP int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode);
44 extern DLL_IMP_EXP int (*plugin_bclose)(JCR *jcr);
45 extern DLL_IMP_EXP ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count);
46 extern DLL_IMP_EXP ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count);
47 extern DLL_IMP_EXP boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence);
50 /* Forward referenced functions */
51 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value);
52 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value);
53 static bRC baculaRegisterEvents(bpContext *ctx, ...);
54 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
55 int type, time_t mtime, const char *fmt, ...);
56 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
57 int level, const char *fmt, ...);
59 static int my_plugin_bopen(JCR *jcr, const char *fname, int flags, mode_t mode);
60 static int my_plugin_bclose(JCR *jcr);
61 static ssize_t my_plugin_bread(JCR *jcr, void *buf, size_t count);
62 static ssize_t my_plugin_bwrite(JCR *jcr, void *buf, size_t count);
63 static boffset_t my_plugin_blseek(JCR *jcr, boffset_t offset, int whence);
67 static bInfo binfo = {
69 FD_PLUGIN_INTERFACE_VERSION
72 /* Bacula entry points */
73 static bFuncs bfuncs = {
75 FD_PLUGIN_INTERFACE_VERSION,
85 * Create a plugin event
87 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
94 return; /* Return if no plugins loaded */
97 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
98 event.eventType = eventType;
100 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
102 /* Pass event to every plugin */
103 foreach_alist(plugin, plugin_list) {
105 rc = plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i++], &event, value);
115 * Sequence of calls for a backup:
116 * 1. plugin_save() here is called with ff_pkt
117 * 2. we find the plugin requested on the command string
118 * 3. we generate a bEventBackupCommand event to the specified plugin
119 * and pass it the command string.
120 * 4. we make a startPluginBackup call to the plugin, which gives
121 * us the data we need in save_pkt
122 * 5. we call Bacula's save_file() subroutine to save the specified
123 * file. The plugin will be called at pluginIO() to supply the
126 * Sequence of calls for restore:
127 * See subroutine plugin_name_stream() below.
129 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
135 char *cmd = ff_pkt->top_fname;
140 return 1; /* Return if no plugins loaded */
143 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
144 event.eventType = bEventBackupCommand;
146 /* Handle plugin command here backup */
147 Dmsg1(100, "plugin cmd=%s\n", cmd);
148 if (!(p = strchr(cmd, ':'))) {
149 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
157 foreach_alist(plugin, plugin_list) {
158 Dmsg3(100, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
159 if (strncmp(plugin->file, cmd, len) != 0) {
163 Dmsg1(100, "Command plugin = %s\n", cmd);
164 /* Send the backup command */
165 if (plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i], &event, cmd) != bRC_OK) {
168 /* Loop getting filenames to backup then saving them */
169 while (!job_canceled(jcr)) {
170 memset(&sp, 0, sizeof(sp));
174 Dmsg3(000, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
176 /* Get the file save parameters */
177 if (plug_func(plugin)->startBackupFile(&plugin_ctx_list[i], &sp) != bRC_OK) {
180 jcr->plugin_ctx = &plugin_ctx_list[i];
181 jcr->plugin = plugin;
182 jcr->plugin_sp = &sp;
184 ff_pkt->fname = sp.fname;
185 ff_pkt->type = sp.type;
186 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
187 Dmsg1(000, "Save_file: file=%s\n", ff_pkt->fname);
188 save_file(jcr, ff_pkt, true);
189 if (plug_func(plugin)->endBackupFile(&plugin_ctx_list[i]) != bRC_More) {
194 Jmsg1(jcr, M_ERROR, 0, "Command plugin \"%s\" not found.\n", cmd);
201 * Send plugin name start/end record to SD
203 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
206 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
208 Dmsg1(000, "send_plugin_name=%s\n", sp->cmd);
209 if (!sd->fsend("%ld %d 0", jcr->JobFiles+1, STREAM_PLUGIN_NAME)) {
210 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
214 Dmsg1(000, "send: %s\n", sd->msg);
217 stat = sd->fsend("%ld 1 %d %s%c", jcr->JobFiles+1, sp->portable, sp->cmd, 0);
219 stat = sd->fsend("%ld 0");
222 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
226 Dmsg1(000, "send: %s\n", sd->msg);
227 sd->signal(BNET_EOD); /* indicate end of plugin name data */
232 * Plugin name stream found during restore. The record passed in
233 * argument name was generated in send_plugin_name() above.
235 void plugin_name_stream(JCR *jcr, char *name)
239 bool start, portable;
243 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
245 Dmsg1(100, "Read plugin stream string=%s\n", name);
246 skip_nonspaces(&p); /* skip over jcr->JobFiles */
249 skip_nonspaces(&p); /* skip start/end flag */
251 portable = *p == '1';
252 skip_nonspaces(&p); /* skip portable flag */
256 /* Check for restore end */
259 * If end of restore, notify plugin, then clear flags
261 plugin = (Plugin *)jcr->plugin;
262 plug_func(plugin)->endRestoreFile(&plugin_ctx_list[i]);
263 jcr->plugin_ctx = NULL;
269 * After this point, we are dealing with a restore start
272 Dmsg1(100, "plugin restore cmd=%s\n", cmd);
273 if (!(p = strchr(cmd, ':'))) {
274 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
283 * Search for correct plugin as specified on the command
285 foreach_alist(plugin, plugin_list) {
287 Dmsg3(100, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
288 if (strncmp(plugin->file, cmd, len) != 0) {
292 Dmsg1(100, "Restore Command plugin = %s\n", cmd);
293 event.eventType = bEventRestoreCommand;
294 if (plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i],
295 &event, cmd) != bRC_OK) {
298 jcr->plugin_ctx = &plugin_ctx_list[i];
299 jcr->plugin = plugin;
307 * Tell the plugin to create the file. Return values are
310 * CF_SKIP -- skip processing this file
311 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
312 * CF_CREATED -- created, but no content to extract (typically directories)
315 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
317 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
318 Plugin *plugin = (Plugin *)jcr->plugin;
319 struct restore_pkt rp;
322 if (!set_cmd_plugin(bfd, jcr)) {
325 rp.stream = attr->stream;
326 rp.data_stream = attr->data_stream;
327 rp.type = attr->type;
328 rp.file_index = attr->file_index;
329 rp.LinkFI = attr->LinkFI;
331 rp.statp = attr->statp; /* structure assignment */
332 rp.attrEx = attr->attrEx;
333 rp.ofname = attr->ofname;
334 rp.olname = attr->olname;
335 rp.where = jcr->where;
336 rp.RegexWhere = jcr->RegexWhere;
337 rp.replace = jcr->replace;
338 if (plug_func(plugin)->createFile(plugin_ctx, &rp) != bRC_OK) {
344 io.mode = 0777 & attr->statp.st_mode;
346 if (plug_func(plugin)->pluginIO(plugin_ctx, &io) != bRC_OK) {
353 * Reset the file attributes after all file I/O is done -- this allows
354 * the previous access time/dates to be set properly, and it also allows
355 * us to properly set directory permissions.
357 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
362 void load_fd_plugins(const char *plugin_dir)
368 plugin_list = New(alist(10, not_owned_by_alist));
369 load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type);
371 /* Plug entry points called from findlib */
372 plugin_bopen = my_plugin_bopen;
373 plugin_bclose = my_plugin_bclose;
374 plugin_bread = my_plugin_bread;
375 plugin_bwrite = my_plugin_bwrite;
376 plugin_blseek = my_plugin_blseek;
381 * Create a new instance of each plugin for this Job
383 void new_plugins(JCR *jcr)
392 int num = plugin_list->size();
398 jcr->plugin_ctx_list = (void *)malloc(sizeof(bpContext) * num);
400 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
401 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
402 foreach_alist(plugin, plugin_list) {
403 /* Start a new instance of each plugin */
404 plugin_ctx_list[i].bContext = (void *)jcr;
405 plugin_ctx_list[i].pContext = NULL;
406 plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]);
411 * Free the plugin instances for this Job
413 void free_plugins(JCR *jcr)
422 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
423 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
424 foreach_alist(plugin, plugin_list) {
425 /* Free the plugin instance */
426 plug_func(plugin)->freePlugin(&plugin_ctx_list[i++]);
428 free(plugin_ctx_list);
429 jcr->plugin_ctx_list = NULL;
432 static int my_plugin_bopen(JCR *jcr, const char *fname, int flags, mode_t mode)
434 Plugin *plugin = (Plugin *)jcr->plugin;
435 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
437 Dmsg0(000, "plugin_bopen\n");
443 plug_func(plugin)->pluginIO(plugin_ctx, &io);
447 static int my_plugin_bclose(JCR *jcr)
449 Plugin *plugin = (Plugin *)jcr->plugin;
450 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
452 Dmsg0(000, "plugin_bclose\n");
456 plug_func(plugin)->pluginIO(plugin_ctx, &io);
460 static ssize_t my_plugin_bread(JCR *jcr, void *buf, size_t count)
462 Plugin *plugin = (Plugin *)jcr->plugin;
463 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
465 Dmsg0(000, "plugin_bread\n");
468 io.buf = (char *)buf;
469 plug_func(plugin)->pluginIO(plugin_ctx, &io);
470 return (ssize_t)io.status;
473 static ssize_t my_plugin_bwrite(JCR *jcr, void *buf, size_t count)
475 Plugin *plugin = (Plugin *)jcr->plugin;
476 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
478 Dmsg0(000, "plugin_bwrite\n");
481 io.buf = (char *)buf;
482 plug_func(plugin)->pluginIO(plugin_ctx, &io);
483 return (ssize_t)io.status;
486 static boffset_t my_plugin_blseek(JCR *jcr, boffset_t offset, int whence)
488 Plugin *plugin = (Plugin *)jcr->plugin;
489 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
491 Dmsg0(000, "plugin_bseek\n");
495 plug_func(plugin)->pluginIO(plugin_ctx, &io);
496 return (boffset_t)io.offset;
499 /* ==============================================================
501 * Callbacks from the plugin
503 * ==============================================================
505 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
507 JCR *jcr = (JCR *)(ctx->bContext);
508 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
512 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
515 *((int *)value) = jcr->JobId;
516 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
519 *((char **)value) = my_name;
520 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
533 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
535 Dmsg1(dbglvl, "bacula: baculaSetValue var=%d\n", var);
539 static bRC baculaRegisterEvents(bpContext *ctx, ...)
545 while ((event = va_arg(args, uint32_t))) {
546 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
552 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
553 int type, time_t mtime, const char *fmt, ...)
557 JCR *jcr = (JCR *)(ctx->bContext);
559 va_start(arg_ptr, fmt);
560 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
562 Jmsg(jcr, type, mtime, "%s", buf);
566 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
567 int level, const char *fmt, ...)
572 va_start(arg_ptr, fmt);
573 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
575 d_msg(file, line, level, "%s", buf);
581 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
582 int (*plugin_bclose)(JCR *jcr) = NULL;
583 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
584 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
585 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
587 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
592 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
597 int main(int argc, char *argv[])
599 char plugin_dir[1000];
604 strcpy(my_name, "test-fd");
606 getcwd(plugin_dir, sizeof(plugin_dir)-1);
607 load_fd_plugins(plugin_dir);
615 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
616 generate_plugin_event(jcr1, bEventJobEnd);
617 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
619 generate_plugin_event(jcr2, bEventJobEnd);
624 Dmsg0(dbglvl, "bacula: OK ...\n");
630 #endif /* TEST_PROGRAM */