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 /* Send stream header */
210 if (!sd->fsend("%ld %d 0", jcr->JobFiles+1, STREAM_PLUGIN_NAME)) {
211 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
215 Dmsg1(000, "send: %s\n", sd->msg);
218 /* Send data -- not much */
219 stat = sd->fsend("%ld 1 %d %s%c", jcr->JobFiles+1, sp->portable, sp->cmd, 0);
221 /* Send end of data */
222 stat = sd->fsend("0 0");
225 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
229 Dmsg1(000, "send: %s\n", sd->msg);
230 sd->signal(BNET_EOD); /* indicate end of plugin name data */
235 * Plugin name stream found during restore. The record passed in
236 * argument name was generated in send_plugin_name() above.
238 void plugin_name_stream(JCR *jcr, char *name)
242 bool start, portable;
246 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
248 Dmsg1(100, "Read plugin stream string=%s\n", name);
249 skip_nonspaces(&p); /* skip over jcr->JobFiles */
253 /* Start of plugin data */
254 skip_nonspaces(&p); /* skip start/end flag */
256 portable = *p == '1';
257 skip_nonspaces(&p); /* skip portable flag */
262 * End of plugin data, notify plugin, then clear flags
264 plugin = (Plugin *)jcr->plugin;
265 plug_func(plugin)->endRestoreFile(&plugin_ctx_list[i]);
266 jcr->plugin_ctx = NULL;
272 * After this point, we are dealing with a restore start
275 Dmsg1(100, "plugin restore cmd=%s\n", cmd);
276 if (!(p = strchr(cmd, ':'))) {
277 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
286 * Search for correct plugin as specified on the command
288 foreach_alist(plugin, plugin_list) {
290 Dmsg3(100, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
291 if (strncmp(plugin->file, cmd, len) != 0) {
295 Dmsg1(100, "Restore Command plugin = %s\n", cmd);
296 event.eventType = bEventRestoreCommand;
297 if (plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i],
298 &event, cmd) != bRC_OK) {
301 jcr->plugin_ctx = &plugin_ctx_list[i];
302 jcr->plugin = plugin;
310 * Tell the plugin to create the file. Return values are
313 * CF_SKIP -- skip processing this file
314 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
315 * CF_CREATED -- created, but no content to extract (typically directories)
318 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
320 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
321 Plugin *plugin = (Plugin *)jcr->plugin;
322 struct restore_pkt rp;
325 if (!set_cmd_plugin(bfd, jcr)) {
328 rp.stream = attr->stream;
329 rp.data_stream = attr->data_stream;
330 rp.type = attr->type;
331 rp.file_index = attr->file_index;
332 rp.LinkFI = attr->LinkFI;
334 rp.statp = attr->statp; /* structure assignment */
335 rp.attrEx = attr->attrEx;
336 rp.ofname = attr->ofname;
337 rp.olname = attr->olname;
338 rp.where = jcr->where;
339 rp.RegexWhere = jcr->RegexWhere;
340 rp.replace = jcr->replace;
341 if (plug_func(plugin)->createFile(plugin_ctx, &rp) != bRC_OK) {
347 io.mode = 0777 & attr->statp.st_mode;
349 if (plug_func(plugin)->pluginIO(plugin_ctx, &io) != bRC_OK) {
356 * Reset the file attributes after all file I/O is done -- this allows
357 * the previous access time/dates to be set properly, and it also allows
358 * us to properly set directory permissions.
360 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
366 * This entry point is called internally by Bacula to ensure
367 * that the plugin IO calls come into this code.
369 void load_fd_plugins(const char *plugin_dir)
377 plugin_list = New(alist(10, not_owned_by_alist));
378 load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type);
380 /* Plug entry points called from findlib */
381 plugin_bopen = my_plugin_bopen;
382 plugin_bclose = my_plugin_bclose;
383 plugin_bread = my_plugin_bread;
384 plugin_bwrite = my_plugin_bwrite;
385 plugin_blseek = my_plugin_blseek;
386 foreach_alist(plugin, plugin_list) {
387 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
393 * Create a new instance of each plugin for this Job
395 void new_plugins(JCR *jcr)
404 int num = plugin_list->size();
410 jcr->plugin_ctx_list = (void *)malloc(sizeof(bpContext) * num);
412 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
413 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
414 foreach_alist(plugin, plugin_list) {
415 /* Start a new instance of each plugin */
416 plugin_ctx_list[i].bContext = (void *)jcr;
417 plugin_ctx_list[i].pContext = NULL;
418 plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]);
423 * Free the plugin instances for this Job
425 void free_plugins(JCR *jcr)
434 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
435 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
436 foreach_alist(plugin, plugin_list) {
437 /* Free the plugin instance */
438 plug_func(plugin)->freePlugin(&plugin_ctx_list[i++]);
440 free(plugin_ctx_list);
441 jcr->plugin_ctx_list = NULL;
444 static int my_plugin_bopen(JCR *jcr, const char *fname, int flags, mode_t mode)
446 Plugin *plugin = (Plugin *)jcr->plugin;
447 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
449 Dmsg0(000, "plugin_bopen\n");
455 plug_func(plugin)->pluginIO(plugin_ctx, &io);
459 static int my_plugin_bclose(JCR *jcr)
461 Plugin *plugin = (Plugin *)jcr->plugin;
462 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
464 Dmsg0(000, "plugin_bclose\n");
468 plug_func(plugin)->pluginIO(plugin_ctx, &io);
472 static ssize_t my_plugin_bread(JCR *jcr, void *buf, size_t count)
474 Plugin *plugin = (Plugin *)jcr->plugin;
475 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
477 Dmsg0(000, "plugin_bread\n");
480 io.buf = (char *)buf;
481 plug_func(plugin)->pluginIO(plugin_ctx, &io);
482 return (ssize_t)io.status;
485 static ssize_t my_plugin_bwrite(JCR *jcr, void *buf, size_t count)
487 Plugin *plugin = (Plugin *)jcr->plugin;
488 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
490 Dmsg0(000, "plugin_bwrite\n");
493 io.buf = (char *)buf;
494 plug_func(plugin)->pluginIO(plugin_ctx, &io);
495 return (ssize_t)io.status;
498 static boffset_t my_plugin_blseek(JCR *jcr, boffset_t offset, int whence)
500 Plugin *plugin = (Plugin *)jcr->plugin;
501 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
503 Dmsg0(000, "plugin_bseek\n");
507 plug_func(plugin)->pluginIO(plugin_ctx, &io);
508 return (boffset_t)io.offset;
511 /* ==============================================================
513 * Callbacks from the plugin
515 * ==============================================================
517 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
519 JCR *jcr = (JCR *)(ctx->bContext);
520 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
524 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
527 *((int *)value) = jcr->JobId;
528 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
531 *((char **)value) = my_name;
532 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
545 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
547 Dmsg1(dbglvl, "bacula: baculaSetValue var=%d\n", var);
551 static bRC baculaRegisterEvents(bpContext *ctx, ...)
557 while ((event = va_arg(args, uint32_t))) {
558 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
564 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
565 int type, time_t mtime, const char *fmt, ...)
569 JCR *jcr = (JCR *)(ctx->bContext);
571 va_start(arg_ptr, fmt);
572 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
574 Jmsg(jcr, type, mtime, "%s", buf);
578 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
579 int level, const char *fmt, ...)
584 va_start(arg_ptr, fmt);
585 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
587 d_msg(file, line, level, "%s", buf);
593 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
594 int (*plugin_bclose)(JCR *jcr) = NULL;
595 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
596 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
597 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
599 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
604 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
609 int main(int argc, char *argv[])
611 char plugin_dir[1000];
616 strcpy(my_name, "test-fd");
618 getcwd(plugin_dir, sizeof(plugin_dir)-1);
619 load_fd_plugins(plugin_dir);
627 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
628 generate_plugin_event(jcr1, bEventJobEnd);
629 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
631 generate_plugin_event(jcr2, bEventJobEnd);
636 Dmsg0(dbglvl, "bacula: OK ...\n");
642 #endif /* TEST_PROGRAM */