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 *msg);
57 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
58 int level, const char *msg);
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,
85 * Sequence of calls for a backup:
86 * 1. generate_plugin_event called with bEventPluginCommand
87 * the command string is passed as an argument.
88 * 2. we find the plugin requested on the command string
89 * 3. we generate a bEventPluginCommand event to the specified plugin
90 * 4. we make a startPluginBackup call to the plugin, which gives
91 * us the data we need in save_pkt
92 * 5. we call Bacula's save_file() subroutine to save the specified
93 * file. The plugin will be called at pluginIO() to supply the
96 * Sequence of calls for restore:
97 * See subroutine plugin_name_stream() below.
101 * Create a plugin event
103 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)
110 char *cmd = (char *)value;
118 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
119 event.eventType = eventType;
121 Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
123 case bEventPluginCommand:
124 /* Handle plugin command here backup */
125 Dmsg1(100, "plugin cmd=%s\n", cmd);
126 if (!(p = strchr(cmd, ':'))) {
127 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
135 foreach_alist(plugin, plugin_list) {
136 Dmsg3(100, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
137 if (strncmp(plugin->file, cmd, len) != 0) {
141 while (!job_canceled(jcr)) {
142 Dmsg1(100, "Command plugin = %s\n", cmd);
143 if (plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i], &event, value) != bRC_OK) {
146 memset(&sp, 0, sizeof(sp));
150 Dmsg3(000, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
152 if (plug_func(plugin)->startPluginBackup(&plugin_ctx_list[i], &sp) != bRC_OK) {
155 jcr->plugin_ctx = &plugin_ctx_list[i];
156 jcr->plugin = plugin;
157 jcr->plugin_sp = &sp;
159 ff_pkt->fname = sp.fname;
160 ff_pkt->type = sp.type;
161 memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
162 Dmsg1(000, "Save_file: file=%s\n", ff_pkt->fname);
163 save_file(jcr, ff_pkt, true);
165 /* add call to endPluginBackup() and loop on bRC_MORE */
169 Jmsg1(jcr, M_ERROR, 0, "Command plugin \"%s\" not found.\n", cmd);
173 /* Pass event to every plugin */
174 foreach_alist(plugin, plugin_list) {
176 rc = plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i++], &event, value);
190 * Send plugin name start/end record to SD
192 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
195 struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
197 if (!sd->fsend("%ld %d %d", jcr->JobFiles, STREAM_PLUGIN_NAME, start)) {
198 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
202 stat = sd->fsend("%ld %d %d %s%c", jcr->JobFiles, start, sp->portable, sp->cmd, 0);
204 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
208 sd->signal(BNET_EOD); /* indicate end of plugin name data */
213 * Plugin name stream found during restore. This is the record
214 * that was generated in send_plugin_name() above.
216 void plugin_name_stream(JCR *jcr, char *name)
220 bool start, portable;
225 bpContext *plugin_ctx_list;
227 Dmsg1(000, "plugin stream string=%s\n", name);
228 skip_nonspaces(&p); /* skip over jcr->JobFiles */
231 skip_nonspaces(&p); /* skip start/end flag */
233 portable = *p == '1';
234 skip_nonspaces(&p); /* skip portable flag */
237 event.eventType = start ? bEventRestoreStart : bEventRestoreEnd;
239 /* Check for restore end */
242 * If end of restore, notify plugin, then clear flags
244 plugin = (Plugin *)jcr->plugin;
245 plug_func(plugin)->handlePluginEvent((bpContext *)jcr->plugin_ctx, &event, cmd);
246 jcr->plugin_ctx = NULL;
252 * After this point, we are dealing with a restore start
255 Dmsg1(000, "plugin cmd=%s\n", cmd);
256 if (!(p = strchr(cmd, ':'))) {
257 Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
266 plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
267 foreach_alist(plugin, plugin_list) {
268 Dmsg3(100, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
269 if (strncmp(plugin->file, cmd, len) != 0) {
273 Dmsg1(100, "Command plugin = %s\n", cmd);
274 if (plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i],
275 &event, (void *)name) != bRC_OK) {
278 jcr->plugin_ctx = &plugin_ctx_list[i];
279 jcr->plugin = plugin;
287 void load_fd_plugins(const char *plugin_dir)
293 plugin_list = New(alist(10, not_owned_by_alist));
294 load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type);
295 plugin_bopen = my_plugin_bopen;
296 plugin_bclose = my_plugin_bclose;
297 plugin_bread = my_plugin_bread;
298 plugin_bwrite = my_plugin_bwrite;
299 plugin_blseek = my_plugin_blseek;
304 * Create a new instance of each plugin for this Job
306 void new_plugins(JCR *jcr)
315 int num = plugin_list->size();
321 jcr->plugin_ctx_list = (void *)malloc(sizeof(bpContext) * num);
323 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
324 Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
325 foreach_alist(plugin, plugin_list) {
326 /* Start a new instance of each plugin */
327 plugin_ctx_list[i].bContext = (void *)jcr;
328 plugin_ctx_list[i].pContext = NULL;
329 plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]);
334 * Free the plugin instances for this Job
336 void free_plugins(JCR *jcr)
345 bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
346 Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
347 foreach_alist(plugin, plugin_list) {
348 /* Free the plugin instance */
349 plug_func(plugin)->freePlugin(&plugin_ctx_list[i++]);
351 free(plugin_ctx_list);
352 jcr->plugin_ctx_list = NULL;
355 static int my_plugin_bopen(JCR *jcr, const char *fname, int flags, mode_t mode)
357 Plugin *plugin = (Plugin *)jcr->plugin;
358 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
360 Dmsg0(000, "plugin_bopen\n");
364 plug_func(plugin)->pluginIO(plugin_ctx, &io);
368 static int my_plugin_bclose(JCR *jcr)
370 Plugin *plugin = (Plugin *)jcr->plugin;
371 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
373 Dmsg0(000, "plugin_bclose\n");
377 plug_func(plugin)->pluginIO(plugin_ctx, &io);
381 static ssize_t my_plugin_bread(JCR *jcr, void *buf, size_t count)
383 Plugin *plugin = (Plugin *)jcr->plugin;
384 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
386 Dmsg0(000, "plugin_bread\n");
389 io.buf = (char *)buf;
390 plug_func(plugin)->pluginIO(plugin_ctx, &io);
391 return (ssize_t)io.status;
394 static ssize_t my_plugin_bwrite(JCR *jcr, void *buf, size_t count)
396 Plugin *plugin = (Plugin *)jcr->plugin;
397 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
399 Dmsg0(000, "plugin_bwrite\n");
402 io.buf = (char *)buf;
403 plug_func(plugin)->pluginIO(plugin_ctx, &io);
404 return (ssize_t)io.status;
407 static boffset_t my_plugin_blseek(JCR *jcr, boffset_t offset, int whence)
409 Plugin *plugin = (Plugin *)jcr->plugin;
410 bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
412 Dmsg0(000, "plugin_bseek\n");
416 plug_func(plugin)->pluginIO(plugin_ctx, &io);
417 return (boffset_t)io.offset;
420 /* ==============================================================
422 * Callbacks from the plugin
424 * ==============================================================
426 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
428 JCR *jcr = (JCR *)(ctx->bContext);
429 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
433 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
436 *((int *)value) = jcr->JobId;
437 Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
440 *((char **)value) = my_name;
441 Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
454 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
456 Dmsg1(dbglvl, "bacula: baculaSetValue var=%d\n", var);
460 static bRC baculaRegisterEvents(bpContext *ctx, ...)
466 while ((event = va_arg(args, uint32_t))) {
467 Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
473 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
474 int type, time_t mtime, const char *msg)
476 Dmsg5(dbglvl, "Job message: %s:%d type=%d time=%ld msg=%s\n",
477 file, line, type, mtime, msg);
481 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
482 int level, const char *msg)
484 Dmsg4(dbglvl, "Debug message: %s:%d level=%d msg=%s\n",
485 file, line, level, msg);
491 int (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
492 int (*plugin_bclose)(JCR *jcr) = NULL;
493 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
494 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
495 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
497 int save_file(FF_PKT *ff_pkt, void *vjcr, bool top_level)
502 int main(int argc, char *argv[])
504 char plugin_dir[1000];
509 strcpy(my_name, "test-fd");
511 getcwd(plugin_dir, sizeof(plugin_dir)-1);
512 load_fd_plugins(plugin_dir);
520 generate_plugin_event(jcr1, bEventJobStart);
521 generate_plugin_event(jcr1, bEventJobEnd);
522 generate_plugin_event(jcr2, bEventJobStart);
524 generate_plugin_event(jcr2, bEventJobEnd);
529 Dmsg0(dbglvl, "bacula: OK ...\n");
535 #endif /* TEST_PROGRAM */