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;
39 const char *plugin_type = "-fd.dll";
41 const char *plugin_type = "-fd.so";
44 extern int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
46 /* Function pointers to be set here */
47 extern DLL_IMP_EXP int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode);
48 extern DLL_IMP_EXP int (*plugin_bclose)(JCR *jcr);
49 extern DLL_IMP_EXP ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count);
50 extern DLL_IMP_EXP ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count);
51 extern DLL_IMP_EXP boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence);
54 /* Forward referenced functions */
55 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value);
56 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value);
57 static bRC baculaRegisterEvents(bpContext *ctx, ...);
58 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
59 int type, time_t mtime, const char *fmt, ...);
60 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
61 int level, const char *fmt, ...);
63 static int my_plugin_bopen(JCR *jcr, const char *fname, int flags, mode_t mode);
64 static int my_plugin_bclose(JCR *jcr);
65 static ssize_t my_plugin_bread(JCR *jcr, void *buf, size_t count);
66 static ssize_t my_plugin_bwrite(JCR *jcr, void *buf, size_t count);
67 static boffset_t my_plugin_blseek(JCR *jcr, boffset_t offset, int whence);
71 static bInfo binfo = {
73 FD_PLUGIN_INTERFACE_VERSION
76 /* Bacula entry points */
77 static bFuncs bfuncs = {
79 FD_PLUGIN_INTERFACE_VERSION,
89 * Create a plugin event
91 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
97 if (!plugin_list || !jcr->plugin_ctx_list) {
98 return; /* Return if no plugins loaded */
101 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
102 event.eventType = eventType;
104 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
106 /* Pass event to every plugin */
107 foreach_alist(plugin, plugin_list) {
109 rc = plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i++], &event, value);
119 * Sequence of calls for a backup:
120 * 1. plugin_save() here is called with ff_pkt
121 * 2. we find the plugin requested on the command string
122 * 3. we generate a bEventBackupCommand event to the specified plugin
123 * and pass it the command string.
124 * 4. we make a startPluginBackup call to the plugin, which gives
125 * us the data we need in save_pkt
126 * 5. we call Bacula's save_file() subroutine to save the specified
127 * file. The plugin will be called at pluginIO() to supply the
130 * Sequence of calls for restore:
131 * See subroutine plugin_name_stream() below.
133 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
139 char *cmd = ff_pkt->top_fname;
143 if (!plugin_list || !jcr->plugin_ctx_list) {
144 return 1; /* Return if no plugins loaded */
147 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
148 event.eventType = bEventBackupCommand;
150 /* Handle plugin command here backup */
151 Dmsg1(100, "plugin cmd=%s\n", cmd);
152 if (!(p = strchr(cmd, ':'))) {
153 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
161 foreach_alist(plugin, plugin_list) {
162 Dmsg3(100, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
163 if (strncmp(plugin->file, cmd, len) != 0) {
167 Dmsg1(100, "Command plugin = %s\n", cmd);
168 /* Send the backup command */
169 if (plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i], &event, cmd) != bRC_OK) {
172 /* Loop getting filenames to backup then saving them */
173 while (!job_canceled(jcr)) {
174 memset(&sp, 0, sizeof(sp));
178 Dmsg3(000, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
180 /* Get the file save parameters */
181 if (plug_func(plugin)->startBackupFile(&plugin_ctx_list[i], &sp) != bRC_OK) {
184 jcr->plugin_ctx = &plugin_ctx_list[i];
185 jcr->plugin = plugin;
186 jcr->plugin_sp = &sp;
188 ff_pkt->fname = sp.fname;
189 ff_pkt->type = sp.type;
190 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
191 Dmsg1(000, "Save_file: file=%s\n", ff_pkt->fname);
192 save_file(jcr, ff_pkt, true);
193 if (plug_func(plugin)->endBackupFile(&plugin_ctx_list[i]) != bRC_More) {
198 Jmsg1(jcr, M_ERROR, 0, "Command plugin \"%s\" not found.\n", cmd);
205 * Send plugin name start/end record to SD
207 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
210 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
212 Dmsg1(000, "send_plugin_name=%s\n", sp->cmd);
213 /* Send stream header */
214 if (!sd->fsend("%ld %d 0", jcr->JobFiles+1, STREAM_PLUGIN_NAME)) {
215 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
219 Dmsg1(000, "send: %s\n", sd->msg);
222 /* Send data -- not much */
223 stat = sd->fsend("%ld 1 %d %s%c", jcr->JobFiles+1, sp->portable, sp->cmd, 0);
225 /* Send end of data */
226 stat = sd->fsend("0 0");
229 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
233 Dmsg1(000, "send: %s\n", sd->msg);
234 sd->signal(BNET_EOD); /* indicate end of plugin name data */
239 * Plugin name stream found during restore. The record passed in
240 * argument name was generated in send_plugin_name() above.
242 void plugin_name_stream(JCR *jcr, char *name)
246 bool start, portable;
250 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
251 if (!plugin_ctx_list) {
255 Dmsg1(100, "Read plugin stream string=%s\n", name);
256 skip_nonspaces(&p); /* skip over jcr->JobFiles */
260 /* Start of plugin data */
261 skip_nonspaces(&p); /* skip start/end flag */
263 portable = *p == '1';
264 skip_nonspaces(&p); /* skip portable flag */
269 * End of plugin data, notify plugin, then clear flags
271 plugin = (Plugin *)jcr->plugin;
272 plug_func(plugin)->endRestoreFile(&plugin_ctx_list[i]);
273 jcr->plugin_ctx = NULL;
279 * After this point, we are dealing with a restore start
282 Dmsg1(100, "plugin restore cmd=%s\n", cmd);
283 if (!(p = strchr(cmd, ':'))) {
284 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
293 * Search for correct plugin as specified on the command
295 foreach_alist(plugin, plugin_list) {
297 Dmsg3(100, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
298 if (strncmp(plugin->file, cmd, len) != 0) {
302 Dmsg1(100, "Restore Command plugin = %s\n", cmd);
303 event.eventType = bEventRestoreCommand;
304 if (plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i],
305 &event, cmd) != bRC_OK) {
308 jcr->plugin_ctx = &plugin_ctx_list[i];
309 jcr->plugin = plugin;
317 * Tell the plugin to create the file. Return values are
320 * CF_SKIP -- skip processing this file
321 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
322 * CF_CREATED -- created, but no content to extract (typically directories)
325 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
327 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
328 Plugin *plugin = (Plugin *)jcr->plugin;
329 struct restore_pkt rp;
332 if (!set_cmd_plugin(bfd, jcr)) {
335 rp.stream = attr->stream;
336 rp.data_stream = attr->data_stream;
337 rp.type = attr->type;
338 rp.file_index = attr->file_index;
339 rp.LinkFI = attr->LinkFI;
341 rp.statp = attr->statp; /* structure assignment */
342 rp.attrEx = attr->attrEx;
343 rp.ofname = attr->ofname;
344 rp.olname = attr->olname;
345 rp.where = jcr->where;
346 rp.RegexWhere = jcr->RegexWhere;
347 rp.replace = jcr->replace;
348 if (plug_func(plugin)->createFile(plugin_ctx, &rp) != bRC_OK) {
354 io.mode = 0777 & attr->statp.st_mode;
356 if (plug_func(plugin)->pluginIO(plugin_ctx, &io) != bRC_OK) {
363 * Reset the file attributes after all file I/O is done -- this allows
364 * the previous access time/dates to be set properly, and it also allows
365 * us to properly set directory permissions.
367 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
373 * This entry point is called internally by Bacula to ensure
374 * that the plugin IO calls come into this code.
376 void load_fd_plugins(const char *plugin_dir)
384 plugin_list = New(alist(10, not_owned_by_alist));
385 if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type)) {
386 /* Either none found, or some error */
387 if (plugin_list->size() == 0) {
394 /* Plug entry points called from findlib */
395 plugin_bopen = my_plugin_bopen;
396 plugin_bclose = my_plugin_bclose;
397 plugin_bread = my_plugin_bread;
398 plugin_bwrite = my_plugin_bwrite;
399 plugin_blseek = my_plugin_blseek;
400 foreach_alist(plugin, plugin_list) {
401 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
407 * Create a new instance of each plugin for this Job
408 * Note, plugin_list can exist but jcr->plugin_ctx_list can
409 * be NULL if no plugins were loaded.
411 void new_plugins(JCR *jcr)
420 int num = plugin_list->size();
426 jcr->plugin_ctx_list = (void *)malloc(sizeof(bpContext) * num);
428 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
429 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
430 foreach_alist(plugin, plugin_list) {
431 /* Start a new instance of each plugin */
432 plugin_ctx_list[i].bContext = (void *)jcr;
433 plugin_ctx_list[i].pContext = NULL;
434 plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]);
439 * Free the plugin instances for this Job
441 void free_plugins(JCR *jcr)
446 if (!plugin_list || !jcr->plugin_ctx_list) {
447 return; /* no plugins, nothing to do */
450 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
451 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
452 foreach_alist(plugin, plugin_list) {
453 /* Free the plugin instance */
454 plug_func(plugin)->freePlugin(&plugin_ctx_list[i++]);
456 free(plugin_ctx_list);
457 jcr->plugin_ctx_list = NULL;
460 static int my_plugin_bopen(JCR *jcr, const char *fname, int flags, mode_t mode)
462 Plugin *plugin = (Plugin *)jcr->plugin;
463 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
465 Dmsg0(000, "plugin_bopen\n");
471 plug_func(plugin)->pluginIO(plugin_ctx, &io);
475 static int my_plugin_bclose(JCR *jcr)
477 Plugin *plugin = (Plugin *)jcr->plugin;
478 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
480 Dmsg0(000, "plugin_bclose\n");
484 plug_func(plugin)->pluginIO(plugin_ctx, &io);
488 static ssize_t my_plugin_bread(JCR *jcr, void *buf, size_t count)
490 Plugin *plugin = (Plugin *)jcr->plugin;
491 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
493 Dmsg0(000, "plugin_bread\n");
496 io.buf = (char *)buf;
497 plug_func(plugin)->pluginIO(plugin_ctx, &io);
498 return (ssize_t)io.status;
501 static ssize_t my_plugin_bwrite(JCR *jcr, void *buf, size_t count)
503 Plugin *plugin = (Plugin *)jcr->plugin;
504 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
506 Dmsg0(000, "plugin_bwrite\n");
509 io.buf = (char *)buf;
510 plug_func(plugin)->pluginIO(plugin_ctx, &io);
511 return (ssize_t)io.status;
514 static boffset_t my_plugin_blseek(JCR *jcr, boffset_t offset, int whence)
516 Plugin *plugin = (Plugin *)jcr->plugin;
517 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
519 Dmsg0(000, "plugin_bseek\n");
523 plug_func(plugin)->pluginIO(plugin_ctx, &io);
524 return (boffset_t)io.offset;
527 /* ==============================================================
529 * Callbacks from the plugin
531 * ==============================================================
533 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
535 JCR *jcr = (JCR *)(ctx->bContext);
536 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
540 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
543 *((int *)value) = jcr->JobId;
544 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
547 *((char **)value) = my_name;
548 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
561 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
563 Dmsg1(dbglvl, "bacula: baculaSetValue var=%d\n", var);
567 static bRC baculaRegisterEvents(bpContext *ctx, ...)
573 while ((event = va_arg(args, uint32_t))) {
574 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
580 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
581 int type, time_t mtime, const char *fmt, ...)
585 JCR *jcr = (JCR *)(ctx->bContext);
587 va_start(arg_ptr, fmt);
588 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
590 Jmsg(jcr, type, mtime, "%s", buf);
594 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
595 int level, const char *fmt, ...)
600 va_start(arg_ptr, fmt);
601 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
603 d_msg(file, line, level, "%s", buf);
609 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
610 int (*plugin_bclose)(JCR *jcr) = NULL;
611 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
612 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
613 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
615 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
620 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
625 int main(int argc, char *argv[])
627 char plugin_dir[1000];
632 strcpy(my_name, "test-fd");
634 getcwd(plugin_dir, sizeof(plugin_dir)-1);
635 load_fd_plugins(plugin_dir);
643 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
644 generate_plugin_event(jcr1, bEventJobEnd);
645 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
647 generate_plugin_event(jcr2, bEventJobEnd);
652 Dmsg0(dbglvl, "bacula: OK ...\n");
658 #endif /* TEST_PROGRAM */