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 John Walker.
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);
43 /* Function pointers to be set here */
44 extern DLL_IMP_EXP int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode);
45 extern DLL_IMP_EXP int (*plugin_bclose)(JCR *jcr);
46 extern DLL_IMP_EXP ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count);
47 extern DLL_IMP_EXP ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count);
48 extern DLL_IMP_EXP boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence);
51 /* Forward referenced functions */
52 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value);
53 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value);
54 static bRC baculaRegisterEvents(bpContext *ctx, ...);
55 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
56 int type, time_t mtime, const char *fmt, ...);
57 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
58 int level, const char *fmt, ...);
60 static int my_plugin_bopen(JCR *jcr, const char *fname, int flags, mode_t mode);
61 static int my_plugin_bclose(JCR *jcr);
62 static ssize_t my_plugin_bread(JCR *jcr, void *buf, size_t count);
63 static ssize_t my_plugin_bwrite(JCR *jcr, void *buf, size_t count);
64 static boffset_t my_plugin_blseek(JCR *jcr, boffset_t offset, int whence);
68 static bInfo binfo = {
70 PLUGIN_INTERFACE_VERSION
73 /* Bacula entry points */
74 static bFuncs bfuncs = {
76 PLUGIN_INTERFACE_VERSION,
86 * Create a plugin event
88 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
95 return; /* Return if no plugins loaded */
98 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
99 event.eventType = eventType;
101 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
103 /* Pass event to every plugin */
104 foreach_alist(plugin, plugin_list) {
106 rc = plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i++], &event, value);
116 * Sequence of calls for a backup:
117 * 1. plugin_save() here is called with ff_pkt
118 * 2. we find the plugin requested on the command string
119 * 3. we generate a bEventBackupCommand event to the specified plugin
120 * and pass it the command string.
121 * 4. we make a startPluginBackup call to the plugin, which gives
122 * us the data we need in save_pkt
123 * 5. we call Bacula's save_file() subroutine to save the specified
124 * file. The plugin will be called at pluginIO() to supply the
127 * Sequence of calls for restore:
128 * See subroutine plugin_name_stream() below.
130 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
136 char *cmd = ff_pkt->top_fname;
141 return 1; /* Return if no plugins loaded */
144 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
145 event.eventType = bEventBackupCommand;
147 /* Handle plugin command here backup */
148 Dmsg1(100, "plugin cmd=%s\n", cmd);
149 if (!(p = strchr(cmd, ':'))) {
150 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
158 foreach_alist(plugin, plugin_list) {
159 Dmsg3(100, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
160 if (strncmp(plugin->file, cmd, len) != 0) {
164 Dmsg1(100, "Command plugin = %s\n", cmd);
165 /* Send the backup command */
166 if (plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i], &event, cmd) != bRC_OK) {
169 /* Loop getting filenames to backup then saving them */
170 while (!job_canceled(jcr)) {
171 memset(&sp, 0, sizeof(sp));
175 Dmsg3(000, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
177 /* Get the file save parameters */
178 if (plug_func(plugin)->startBackupFile(&plugin_ctx_list[i], &sp) != bRC_OK) {
181 jcr->plugin_ctx = &plugin_ctx_list[i];
182 jcr->plugin = plugin;
183 jcr->plugin_sp = &sp;
185 ff_pkt->fname = sp.fname;
186 ff_pkt->type = sp.type;
187 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
188 Dmsg1(000, "Save_file: file=%s\n", ff_pkt->fname);
189 save_file(jcr, ff_pkt, true);
190 if (plug_func(plugin)->endBackupFile(&plugin_ctx_list[i]) != bRC_More) {
195 Jmsg1(jcr, M_ERROR, 0, "Command plugin \"%s\" not found.\n", cmd);
202 * Send plugin name start/end record to SD
204 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
207 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
209 Dmsg1(000, "send_plugin_name=%s\n", sp->cmd);
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 stat = sd->fsend("%ld 1 %d %s%c", jcr->JobFiles+1, sp->portable, sp->cmd, 0);
220 stat = sd->fsend("%ld 0");
223 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
227 Dmsg1(000, "send: %s\n", sd->msg);
228 sd->signal(BNET_EOD); /* indicate end of plugin name data */
233 * Plugin name stream found during restore. The record passed in
234 * argument name was generated in send_plugin_name() above.
236 void plugin_name_stream(JCR *jcr, char *name)
240 bool start, portable;
244 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
246 Dmsg1(100, "Read plugin stream string=%s\n", name);
247 skip_nonspaces(&p); /* skip over jcr->JobFiles */
250 skip_nonspaces(&p); /* skip start/end flag */
252 portable = *p == '1';
253 skip_nonspaces(&p); /* skip portable flag */
257 /* Check for restore end */
260 * If end of restore, notify plugin, then clear flags
262 plugin = (Plugin *)jcr->plugin;
263 plug_func(plugin)->endRestoreFile(&plugin_ctx_list[i]);
264 jcr->plugin_ctx = NULL;
270 * After this point, we are dealing with a restore start
273 Dmsg1(100, "plugin restore cmd=%s\n", cmd);
274 if (!(p = strchr(cmd, ':'))) {
275 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
284 * Search for correct plugin as specified on the command
286 foreach_alist(plugin, plugin_list) {
288 Dmsg3(100, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
289 if (strncmp(plugin->file, cmd, len) != 0) {
293 Dmsg1(100, "Restore Command plugin = %s\n", cmd);
294 event.eventType = bEventRestoreCommand;
295 if (plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i],
296 &event, cmd) != bRC_OK) {
299 jcr->plugin_ctx = &plugin_ctx_list[i];
300 jcr->plugin = plugin;
308 * Tell the plugin to create the file. Return values are
311 * CF_SKIP -- skip processing this file
312 * CF_EXTRACT -- extract the file (i.e.call i/o routines)
313 * CF_CREATED -- created, but no content to extract (typically directories)
316 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
318 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
319 Plugin *plugin = (Plugin *)jcr->plugin;
320 struct restore_pkt rp;
323 if (!set_cmd_plugin(bfd, jcr)) {
326 rp.stream = attr->stream;
327 rp.data_stream = attr->data_stream;
328 rp.type = attr->type;
329 rp.file_index = attr->file_index;
330 rp.LinkFI = attr->LinkFI;
332 rp.statp = attr->statp; /* structure assignment */
333 rp.attrEx = attr->attrEx;
334 rp.ofname = attr->ofname;
335 rp.olname = attr->olname;
336 if (plug_func(plugin)->createFile(plugin_ctx, &rp) != bRC_OK) {
342 io.mode = 0777 & attr->statp.st_mode;
344 if (plug_func(plugin)->pluginIO(plugin_ctx, &io) != bRC_OK) {
351 * Reset the file attributes after all file I/O is done -- this allows
352 * the previous access time/dates to be set properly, and it also allows
353 * us to properly set directory permissions.
355 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
360 void load_fd_plugins(const char *plugin_dir)
366 plugin_list = New(alist(10, not_owned_by_alist));
367 load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type);
368 plugin_bopen = my_plugin_bopen;
369 plugin_bclose = my_plugin_bclose;
370 plugin_bread = my_plugin_bread;
371 plugin_bwrite = my_plugin_bwrite;
372 plugin_blseek = my_plugin_blseek;
377 * Create a new instance of each plugin for this Job
379 void new_plugins(JCR *jcr)
388 int num = plugin_list->size();
394 jcr->plugin_ctx_list = (void *)malloc(sizeof(bpContext) * num);
396 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
397 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
398 foreach_alist(plugin, plugin_list) {
399 /* Start a new instance of each plugin */
400 plugin_ctx_list[i].bContext = (void *)jcr;
401 plugin_ctx_list[i].pContext = NULL;
402 plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]);
407 * Free the plugin instances for this Job
409 void free_plugins(JCR *jcr)
418 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
419 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
420 foreach_alist(plugin, plugin_list) {
421 /* Free the plugin instance */
422 plug_func(plugin)->freePlugin(&plugin_ctx_list[i++]);
424 free(plugin_ctx_list);
425 jcr->plugin_ctx_list = NULL;
428 static int my_plugin_bopen(JCR *jcr, const char *fname, int flags, mode_t mode)
430 Plugin *plugin = (Plugin *)jcr->plugin;
431 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
433 Dmsg0(000, "plugin_bopen\n");
439 plug_func(plugin)->pluginIO(plugin_ctx, &io);
443 static int my_plugin_bclose(JCR *jcr)
445 Plugin *plugin = (Plugin *)jcr->plugin;
446 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
448 Dmsg0(000, "plugin_bclose\n");
452 plug_func(plugin)->pluginIO(plugin_ctx, &io);
456 static ssize_t my_plugin_bread(JCR *jcr, void *buf, size_t count)
458 Plugin *plugin = (Plugin *)jcr->plugin;
459 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
461 Dmsg0(000, "plugin_bread\n");
464 io.buf = (char *)buf;
465 plug_func(plugin)->pluginIO(plugin_ctx, &io);
466 return (ssize_t)io.status;
469 static ssize_t my_plugin_bwrite(JCR *jcr, void *buf, size_t count)
471 Plugin *plugin = (Plugin *)jcr->plugin;
472 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
474 Dmsg0(000, "plugin_bwrite\n");
477 io.buf = (char *)buf;
478 plug_func(plugin)->pluginIO(plugin_ctx, &io);
479 return (ssize_t)io.status;
482 static boffset_t my_plugin_blseek(JCR *jcr, boffset_t offset, int whence)
484 Plugin *plugin = (Plugin *)jcr->plugin;
485 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
487 Dmsg0(000, "plugin_bseek\n");
491 plug_func(plugin)->pluginIO(plugin_ctx, &io);
492 return (boffset_t)io.offset;
495 /* ==============================================================
497 * Callbacks from the plugin
499 * ==============================================================
501 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
503 JCR *jcr = (JCR *)(ctx->bContext);
504 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
508 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
511 *((int *)value) = jcr->JobId;
512 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
515 *((char **)value) = my_name;
516 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
529 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
531 Dmsg1(dbglvl, "bacula: baculaSetValue var=%d\n", var);
535 static bRC baculaRegisterEvents(bpContext *ctx, ...)
541 while ((event = va_arg(args, uint32_t))) {
542 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
548 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
549 int type, time_t mtime, const char *fmt, ...)
553 JCR *jcr = (JCR *)(ctx->bContext);
555 va_start(arg_ptr, fmt);
556 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
558 Jmsg(jcr, type, mtime, "%s", buf);
562 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
563 int level, const char *fmt, ...)
568 va_start(arg_ptr, fmt);
569 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
571 d_msg(file, line, level, "%s", buf);
577 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
578 int (*plugin_bclose)(JCR *jcr) = NULL;
579 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
580 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
581 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
583 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
588 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
593 int main(int argc, char *argv[])
595 char plugin_dir[1000];
600 strcpy(my_name, "test-fd");
602 getcwd(plugin_dir, sizeof(plugin_dir)-1);
603 load_fd_plugins(plugin_dir);
611 generate_plugin_event(jcr1, bEventJobStart);
612 generate_plugin_event(jcr1, bEventJobEnd);
613 generate_plugin_event(jcr2, bEventJobStart);
615 generate_plugin_event(jcr2, bEventJobEnd);
620 Dmsg0(dbglvl, "bacula: OK ...\n");
626 #endif /* TEST_PROGRAM */