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)
370 plugin_list = New(alist(10, not_owned_by_alist));
371 load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type);
373 /* Plug entry points called from findlib */
374 plugin_bopen = my_plugin_bopen;
375 plugin_bclose = my_plugin_bclose;
376 plugin_bread = my_plugin_bread;
377 plugin_bwrite = my_plugin_bwrite;
378 plugin_blseek = my_plugin_blseek;
379 foreach_alist(plugin, plugin_list) {
380 Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
386 * Create a new instance of each plugin for this Job
388 void new_plugins(JCR *jcr)
397 int num = plugin_list->size();
403 jcr->plugin_ctx_list = (void *)malloc(sizeof(bpContext) * num);
405 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
406 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
407 foreach_alist(plugin, plugin_list) {
408 /* Start a new instance of each plugin */
409 plugin_ctx_list[i].bContext = (void *)jcr;
410 plugin_ctx_list[i].pContext = NULL;
411 plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]);
416 * Free the plugin instances for this Job
418 void free_plugins(JCR *jcr)
427 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
428 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
429 foreach_alist(plugin, plugin_list) {
430 /* Free the plugin instance */
431 plug_func(plugin)->freePlugin(&plugin_ctx_list[i++]);
433 free(plugin_ctx_list);
434 jcr->plugin_ctx_list = NULL;
437 static int my_plugin_bopen(JCR *jcr, const char *fname, int flags, mode_t mode)
439 Plugin *plugin = (Plugin *)jcr->plugin;
440 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
442 Dmsg0(000, "plugin_bopen\n");
448 plug_func(plugin)->pluginIO(plugin_ctx, &io);
452 static int my_plugin_bclose(JCR *jcr)
454 Plugin *plugin = (Plugin *)jcr->plugin;
455 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
457 Dmsg0(000, "plugin_bclose\n");
461 plug_func(plugin)->pluginIO(plugin_ctx, &io);
465 static ssize_t my_plugin_bread(JCR *jcr, void *buf, size_t count)
467 Plugin *plugin = (Plugin *)jcr->plugin;
468 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
470 Dmsg0(000, "plugin_bread\n");
473 io.buf = (char *)buf;
474 plug_func(plugin)->pluginIO(plugin_ctx, &io);
475 return (ssize_t)io.status;
478 static ssize_t my_plugin_bwrite(JCR *jcr, void *buf, size_t count)
480 Plugin *plugin = (Plugin *)jcr->plugin;
481 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
483 Dmsg0(000, "plugin_bwrite\n");
486 io.buf = (char *)buf;
487 plug_func(plugin)->pluginIO(plugin_ctx, &io);
488 return (ssize_t)io.status;
491 static boffset_t my_plugin_blseek(JCR *jcr, boffset_t offset, int whence)
493 Plugin *plugin = (Plugin *)jcr->plugin;
494 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
496 Dmsg0(000, "plugin_bseek\n");
500 plug_func(plugin)->pluginIO(plugin_ctx, &io);
501 return (boffset_t)io.offset;
504 /* ==============================================================
506 * Callbacks from the plugin
508 * ==============================================================
510 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
512 JCR *jcr = (JCR *)(ctx->bContext);
513 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
517 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
520 *((int *)value) = jcr->JobId;
521 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
524 *((char **)value) = my_name;
525 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
538 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
540 Dmsg1(dbglvl, "bacula: baculaSetValue var=%d\n", var);
544 static bRC baculaRegisterEvents(bpContext *ctx, ...)
550 while ((event = va_arg(args, uint32_t))) {
551 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
557 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
558 int type, time_t mtime, const char *fmt, ...)
562 JCR *jcr = (JCR *)(ctx->bContext);
564 va_start(arg_ptr, fmt);
565 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
567 Jmsg(jcr, type, mtime, "%s", buf);
571 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
572 int level, const char *fmt, ...)
577 va_start(arg_ptr, fmt);
578 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
580 d_msg(file, line, level, "%s", buf);
586 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
587 int (*plugin_bclose)(JCR *jcr) = NULL;
588 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
589 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
590 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
592 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
597 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
602 int main(int argc, char *argv[])
604 char plugin_dir[1000];
609 strcpy(my_name, "test-fd");
611 getcwd(plugin_dir, sizeof(plugin_dir)-1);
612 load_fd_plugins(plugin_dir);
620 generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
621 generate_plugin_event(jcr1, bEventJobEnd);
622 generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
624 generate_plugin_event(jcr2, bEventJobEnd);
629 Dmsg0(dbglvl, "bacula: OK ...\n");
635 #endif /* TEST_PROGRAM */