]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/fd-plugins.c
Plugin updates
[bacula/bacula] / bacula / src / filed / fd-plugins.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2008 Free Software Foundation Europe e.V.
5
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.
12
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.
17
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
21    02110-1301, USA.
22
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.
27 */
28 /*
29  * Main program to test loading and running Bacula plugins.
30  *   Destined to become Bacula pluginloader, ...
31  *
32  * Kern Sibbald, October 2007
33  */
34 #include "bacula.h"
35 #include "filed.h"
36
37 const int dbglvl = 50;
38 const char *plugin_type = "-fd.so";
39
40 extern int save_file(FF_PKT *ff_pkt, void *vjcr, bool top_level);
41
42
43 /* Function pointers to be set here */
44 extern int     (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode);
45 extern int     (*plugin_bclose)(JCR *jcr);
46 extern ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count);
47 extern ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count);
48 extern boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence);
49
50
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);
59
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);
65
66
67 /* Bacula info */
68 static bInfo binfo = {
69    sizeof(bFuncs),
70    PLUGIN_INTERFACE_VERSION 
71 };
72
73 /* Bacula entry points */
74 static bFuncs bfuncs = {
75    sizeof(bFuncs),
76    PLUGIN_INTERFACE_VERSION,
77    baculaRegisterEvents,
78    baculaGetValue,
79    baculaSetValue,
80    baculaJobMsg,
81    baculaDebugMsg
82 };
83
84
85 /*
86  * Create a plugin event 
87  */
88 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)     
89 {
90    bEvent event;
91    Plugin *plugin;
92    int i = 0;
93    int len;
94    char *p;
95    char *cmd = (char *)value;
96    struct save_pkt sp;
97    FF_PKT *ff_pkt;
98
99    if (!plugin_list) {
100       return;
101    }
102
103    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
104    event.eventType = eventType;
105
106    Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
107    if (eventType != bEventPluginCommand) {
108       /* Pass event to every plugin */
109       foreach_alist(plugin, plugin_list) {
110          plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i++], &event, value);
111       }
112       goto bail_out;
113    }
114
115    /* Handle plugin command here (backup/restore of file) */
116    Dmsg1(100, "plugin cmd=%s\n", cmd);
117    if (!(p = strchr(cmd, ':'))) {
118       Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
119       goto bail_out;
120    }
121    len = p - cmd;
122    if (len <= 0) {
123       goto bail_out;
124    }
125
126    foreach_alist(plugin, plugin_list) {
127       Dmsg3(100, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
128       if (strncmp(plugin->file, cmd, len) == 0) {
129          Dmsg1(100, "Command plugin = %s\n", cmd);
130          if (plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i], &event, value) != bRC_OK) {
131             goto bail_out;
132          }
133          memset(&sp, 0, sizeof(sp));
134          sp.type = FT_REG;
135          sp.portable = true;
136          sp.cmd = cmd;
137          Dmsg3(000, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
138                 &sp);
139          if (plug_func(plugin)->startPluginBackup(&plugin_ctx_list[i], &sp) != bRC_OK) {
140             goto bail_out;
141          }
142          jcr->plugin_ctx = &plugin_ctx_list[i];
143          jcr->plugin = plugin;
144          jcr->plugin_sp = &sp;
145          ff_pkt = jcr->ff;
146          ff_pkt->fname = sp.fname;
147          ff_pkt->type = sp.type;
148          memcpy(&ff_pkt->statp, &sp.statp, sizeof(ff_pkt->statp));
149          Dmsg1(000, "Save_file: file=%s\n", ff_pkt->fname);
150          save_file(ff_pkt, (void *)jcr, true);
151          goto bail_out;
152       }
153       i++;
154    }
155    Jmsg1(jcr, M_ERROR, 0, "Command plugin \"%s\" not found.\n", cmd);
156       
157 bail_out:
158    return;
159 }
160
161 /* 
162  * Send plugin name start/end record to SD
163  */
164 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
165 {
166    int stat;
167    struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
168    Plugin *plugin = (Plugin *)jcr->plugin;
169   
170    if (!sd->fsend("%ld %d %d", jcr->JobFiles, STREAM_PLUGIN_NAME, start)) {
171      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
172            sd->bstrerror());
173      return false;
174    }
175    if (start) {
176       stat = sd->fsend("%ld 1 %d %s%c%s%c", jcr->JobFiles, sp->portable, plugin->file, 0,
177                   sp->cmd, 0);
178    } else {
179       stat = sd->fsend("%ld 0 %d %s%c%s%c", jcr->JobFiles, sp->portable, plugin->file, 0,
180                   sp->cmd, 0);
181    }
182    if (!stat) {
183       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
184             sd->bstrerror());
185          return false;
186    }
187    sd->signal(BNET_EOD);            /* indicate end of plugin name data */
188    return true;
189 }
190
191
192 void load_fd_plugins(const char *plugin_dir)
193 {
194    if (!plugin_dir) {
195       return;
196    }
197
198    plugin_list = New(alist(10, not_owned_by_alist));
199    load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type);
200    plugin_bopen  = my_plugin_bopen;
201    plugin_bclose = my_plugin_bclose;
202    plugin_bread  = my_plugin_bread;
203    plugin_bwrite = my_plugin_bwrite;
204    plugin_blseek = my_plugin_blseek;
205
206 }
207
208 /*
209  * Create a new instance of each plugin for this Job
210  */
211 void new_plugins(JCR *jcr)
212 {
213    Plugin *plugin;
214    int i = 0;
215
216    if (!plugin_list) {
217       return;
218    }
219
220    int num = plugin_list->size();
221
222    if (num == 0) {
223       return;
224    }
225
226    jcr->plugin_ctx_list = (void *)malloc(sizeof(bpContext) * num);
227
228    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
229    Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
230    foreach_alist(plugin, plugin_list) {
231       /* Start a new instance of each plugin */
232       plugin_ctx_list[i].bContext = (void *)jcr;
233       plugin_ctx_list[i].pContext = NULL;
234       plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]);
235    }
236 }
237
238 /*
239  * Free the plugin instances for this Job
240  */
241 void free_plugins(JCR *jcr)
242 {
243    Plugin *plugin;
244    int i = 0;
245
246    if (!plugin_list) {
247       return;
248    }
249
250    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
251    Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
252    foreach_alist(plugin, plugin_list) {
253       /* Free the plugin instance */
254       plug_func(plugin)->freePlugin(&plugin_ctx_list[i++]);
255    }
256    free(plugin_ctx_list);
257    jcr->plugin_ctx_list = NULL;
258 }
259
260 static int my_plugin_bopen(JCR *jcr, const char *fname, int flags, mode_t mode)
261 {
262    Plugin *plugin = (Plugin *)jcr->plugin;
263    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
264    struct io_pkt io;
265    Dmsg0(000, "plugin_bopen\n");
266    io.func = IO_OPEN;
267    io.count = 0;
268    io.buf = NULL;
269    plug_func(plugin)->pluginIO(plugin_ctx, &io);
270    return io.status;
271 }
272
273 static int my_plugin_bclose(JCR *jcr)
274 {
275    Plugin *plugin = (Plugin *)jcr->plugin;
276    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
277    struct io_pkt io;
278    Dmsg0(000, "plugin_bclose\n");
279    io.func = IO_CLOSE;
280    io.count = 0;
281    io.buf = NULL;
282    plug_func(plugin)->pluginIO(plugin_ctx, &io);
283    return io.status;
284 }
285
286 static ssize_t my_plugin_bread(JCR *jcr, void *buf, size_t count)
287 {
288    Plugin *plugin = (Plugin *)jcr->plugin;
289    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
290    struct io_pkt io;
291    Dmsg0(000, "plugin_bread\n");
292    io.func = IO_READ;
293    io.count = count;
294    io.buf = (char *)buf;
295    plug_func(plugin)->pluginIO(plugin_ctx, &io);
296    return (ssize_t)io.status;
297 }
298
299 static ssize_t my_plugin_bwrite(JCR *jcr, void *buf, size_t count)
300 {
301    Plugin *plugin = (Plugin *)jcr->plugin;
302    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
303    struct io_pkt io;
304    Dmsg0(000, "plugin_bwrite\n");
305    io.func = IO_WRITE;
306    io.count = count;
307    io.buf = (char *)buf;
308    plug_func(plugin)->pluginIO(plugin_ctx, &io);
309    return (ssize_t)io.status;
310 }
311
312 static boffset_t my_plugin_blseek(JCR *jcr, boffset_t offset, int whence)
313 {
314    Plugin *plugin = (Plugin *)jcr->plugin;
315    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
316    struct io_pkt io;
317    Dmsg0(000, "plugin_bseek\n");
318    io.func = IO_SEEK;
319    io.offset = offset;
320    io.whence = whence;
321    plug_func(plugin)->pluginIO(plugin_ctx, &io);
322    return (boffset_t)io.offset;
323 }
324
325 /* ==============================================================
326  *
327  * Callbacks from the plugin
328  *
329  * ==============================================================
330  */
331 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
332 {
333    JCR *jcr = (JCR *)(ctx->bContext);
334 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
335    if (!value) {
336       return bRC_Error;
337    }
338 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
339    switch (var) {
340    case bVarJobId:
341       *((int *)value) = jcr->JobId;
342       Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
343       break;
344    case bVarFDName:
345       *((char **)value) = my_name;
346       Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
347       break;
348    case bVarLevel:
349    case bVarType:
350    case bVarClient:
351    case bVarJobName:
352    case bVarJobStatus:
353    case bVarSinceTime:
354       break;
355    }
356    return bRC_OK;
357 }
358
359 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
360 {
361    Dmsg1(dbglvl, "bacula: baculaSetValue var=%d\n", var);
362    return bRC_OK;
363 }
364
365 static bRC baculaRegisterEvents(bpContext *ctx, ...)
366 {
367    va_list args;
368    uint32_t event;
369
370    va_start(args, ctx);
371    while ((event = va_arg(args, uint32_t))) {
372       Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
373    }
374    va_end(args);
375    return bRC_OK;
376 }
377
378 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
379   int type, time_t mtime, const char *msg)
380 {
381    Dmsg5(dbglvl, "Job message: %s:%d type=%d time=%ld msg=%s\n",
382       file, line, type, mtime, msg);
383    return bRC_OK;
384 }
385
386 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
387   int level, const char *msg)
388 {
389    Dmsg4(dbglvl, "Debug message: %s:%d level=%d msg=%s\n",
390       file, line, level, msg);
391    return bRC_OK;
392 }
393
394 #ifdef TEST_PROGRAM
395
396 int     (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
397 int     (*plugin_bclose)(JCR *jcr) = NULL;
398 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
399 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
400 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
401
402 int save_file(FF_PKT *ff_pkt, void *vjcr, bool top_level)
403 {
404    return 0;
405 }
406
407 int main(int argc, char *argv[])
408 {
409    char plugin_dir[1000];
410    JCR mjcr1, mjcr2;
411    JCR *jcr1 = &mjcr1;
412    JCR *jcr2 = &mjcr2;
413
414    strcpy(my_name, "test-fd");
415     
416    getcwd(plugin_dir, sizeof(plugin_dir)-1);
417    load_fd_plugins(plugin_dir);
418
419    jcr1->JobId = 111;
420    new_plugins(jcr1);
421
422    jcr2->JobId = 222;
423    new_plugins(jcr2);
424
425    generate_plugin_event(jcr1, bEventJobStart);
426    generate_plugin_event(jcr1, bEventJobEnd);
427    generate_plugin_event(jcr2, bEventJobStart);
428    free_plugins(jcr1);
429    generate_plugin_event(jcr2, bEventJobEnd);
430    free_plugins(jcr2);
431
432    unload_plugins();
433
434    Dmsg0(dbglvl, "bacula: OK ...\n");
435    close_memory_pool();
436    sm_dump(false);
437    return 0;
438 }
439
440 #endif /* TEST_PROGRAM */