]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/fd-plugins.c
Plugin update
[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(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
41
42
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);
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  * 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
94  *    file data.
95  *
96  * Sequence of calls for restore:
97  *   See subroutine plugin_name_stream() below.
98  */
99
100 /*
101  * Create a plugin event 
102  */
103 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)     
104 {
105    bEvent event;
106    Plugin *plugin;
107    int i = 0;
108    int len;
109    char *p;
110    char *cmd = (char *)value;
111    struct save_pkt sp;
112    FF_PKT *ff_pkt;
113
114    if (!plugin_list) {
115       return;
116    }
117
118    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
119    event.eventType = eventType;
120
121    Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
122    switch (eventType) {
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);
128          goto bail_out;
129       }
130       len = p - cmd;
131       if (len <= 0) {
132          goto bail_out;
133       }
134
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) {
138             i++;
139             continue;
140          }
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) {
144                goto bail_out;
145             }
146             memset(&sp, 0, sizeof(sp));
147             sp.type = FT_REG;
148             sp.portable = true;
149             sp.cmd = cmd;
150             Dmsg3(000, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
151                    &sp);
152             if (plug_func(plugin)->startPluginBackup(&plugin_ctx_list[i], &sp) != bRC_OK) {
153                goto bail_out;
154             }
155             jcr->plugin_ctx = &plugin_ctx_list[i];
156             jcr->plugin = plugin;
157             jcr->plugin_sp = &sp;
158             ff_pkt = jcr->ff;
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);
164             /* ***FIXME***/
165             /* add call to endPluginBackup() and loop on bRC_MORE */
166             goto bail_out;
167          }
168       }
169       Jmsg1(jcr, M_ERROR, 0, "Command plugin \"%s\" not found.\n", cmd);
170       break;
171
172    default:
173       /* Pass event to every plugin */
174       foreach_alist(plugin, plugin_list) {
175          bRC rc;
176          rc = plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i++], &event, value);
177          if (rc != bRC_OK) {
178             break;
179          }
180       }
181       break;
182    }
183
184       
185 bail_out:
186    return;
187 }
188
189 /* 
190  * Send plugin name start/end record to SD
191  */
192 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
193 {
194    int stat;
195    struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
196   
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"),
199            sd->bstrerror());
200      return false;
201    }
202    stat = sd->fsend("%ld %d %d %s%c", jcr->JobFiles, start, sp->portable, sp->cmd, 0);
203    if (!stat) {
204       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
205             sd->bstrerror());
206          return false;
207    }
208    sd->signal(BNET_EOD);            /* indicate end of plugin name data */
209    return true;
210 }
211
212 /*
213  * Plugin name stream found during restore.  This is the record
214  *  that was generated in send_plugin_name() above.
215  */
216 void plugin_name_stream(JCR *jcr, char *name)    
217 {
218    char *p = name;
219    char *cmd;
220    bool start, portable;
221    bEvent event;
222    Plugin *plugin;
223    int len;
224    int i = 0;
225    bpContext *plugin_ctx_list;
226
227    Dmsg1(000, "plugin stream string=%s\n", name);
228    skip_nonspaces(&p);             /* skip over jcr->JobFiles */
229    skip_spaces(&p);
230    start = *p == '1';
231    skip_nonspaces(&p);             /* skip start/end flag */
232    skip_spaces(&p);
233    portable = *p == '1';
234    skip_nonspaces(&p);             /* skip portable flag */
235    skip_spaces(&p);
236    cmd = p;
237    event.eventType = start ? bEventRestoreStart : bEventRestoreEnd;
238     
239    /* Check for restore end */
240    if (!start) {
241       /*
242        * If end of restore, notify plugin, then clear flags   
243        */
244       plugin = (Plugin *)jcr->plugin;
245       plug_func(plugin)->handlePluginEvent((bpContext *)jcr->plugin_ctx, &event, cmd);
246       jcr->plugin_ctx = NULL;
247       jcr->plugin = NULL;
248       goto bail_out;
249    }
250       
251    /*
252     * After this point, we are dealing with a restore start
253     */
254
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);
258       goto bail_out;
259    }
260    len = p - cmd;
261    if (len <= 0) {
262       goto bail_out;
263    }
264
265    
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) {
270          i++;
271          continue;
272       }
273       Dmsg1(100, "Command plugin = %s\n", cmd);
274       if (plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i], 
275             &event, (void *)name) != bRC_OK) {
276          goto bail_out;
277       }
278       jcr->plugin_ctx = &plugin_ctx_list[i];
279       jcr->plugin = plugin;
280       goto bail_out;
281    }
282 bail_out:
283    return;
284 }
285
286
287 void load_fd_plugins(const char *plugin_dir)
288 {
289    if (!plugin_dir) {
290       return;
291    }
292
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;
300
301 }
302
303 /*
304  * Create a new instance of each plugin for this Job
305  */
306 void new_plugins(JCR *jcr)
307 {
308    Plugin *plugin;
309    int i = 0;
310
311    if (!plugin_list) {
312       return;
313    }
314
315    int num = plugin_list->size();
316
317    if (num == 0) {
318       return;
319    }
320
321    jcr->plugin_ctx_list = (void *)malloc(sizeof(bpContext) * num);
322
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++]);
330    }
331 }
332
333 /*
334  * Free the plugin instances for this Job
335  */
336 void free_plugins(JCR *jcr)
337 {
338    Plugin *plugin;
339    int i = 0;
340
341    if (!plugin_list) {
342       return;
343    }
344
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++]);
350    }
351    free(plugin_ctx_list);
352    jcr->plugin_ctx_list = NULL;
353 }
354
355 static int my_plugin_bopen(JCR *jcr, const char *fname, int flags, mode_t mode)
356 {
357    Plugin *plugin = (Plugin *)jcr->plugin;
358    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
359    struct io_pkt io;
360    Dmsg0(000, "plugin_bopen\n");
361    io.func = IO_OPEN;
362    io.count = 0;
363    io.buf = NULL;
364    plug_func(plugin)->pluginIO(plugin_ctx, &io);
365    return io.status;
366 }
367
368 static int my_plugin_bclose(JCR *jcr)
369 {
370    Plugin *plugin = (Plugin *)jcr->plugin;
371    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
372    struct io_pkt io;
373    Dmsg0(000, "plugin_bclose\n");
374    io.func = IO_CLOSE;
375    io.count = 0;
376    io.buf = NULL;
377    plug_func(plugin)->pluginIO(plugin_ctx, &io);
378    return io.status;
379 }
380
381 static ssize_t my_plugin_bread(JCR *jcr, void *buf, size_t count)
382 {
383    Plugin *plugin = (Plugin *)jcr->plugin;
384    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
385    struct io_pkt io;
386    Dmsg0(000, "plugin_bread\n");
387    io.func = IO_READ;
388    io.count = count;
389    io.buf = (char *)buf;
390    plug_func(plugin)->pluginIO(plugin_ctx, &io);
391    return (ssize_t)io.status;
392 }
393
394 static ssize_t my_plugin_bwrite(JCR *jcr, void *buf, size_t count)
395 {
396    Plugin *plugin = (Plugin *)jcr->plugin;
397    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
398    struct io_pkt io;
399    Dmsg0(000, "plugin_bwrite\n");
400    io.func = IO_WRITE;
401    io.count = count;
402    io.buf = (char *)buf;
403    plug_func(plugin)->pluginIO(plugin_ctx, &io);
404    return (ssize_t)io.status;
405 }
406
407 static boffset_t my_plugin_blseek(JCR *jcr, boffset_t offset, int whence)
408 {
409    Plugin *plugin = (Plugin *)jcr->plugin;
410    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
411    struct io_pkt io;
412    Dmsg0(000, "plugin_bseek\n");
413    io.func = IO_SEEK;
414    io.offset = offset;
415    io.whence = whence;
416    plug_func(plugin)->pluginIO(plugin_ctx, &io);
417    return (boffset_t)io.offset;
418 }
419
420 /* ==============================================================
421  *
422  * Callbacks from the plugin
423  *
424  * ==============================================================
425  */
426 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
427 {
428    JCR *jcr = (JCR *)(ctx->bContext);
429 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
430    if (!value) {
431       return bRC_Error;
432    }
433 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
434    switch (var) {
435    case bVarJobId:
436       *((int *)value) = jcr->JobId;
437       Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
438       break;
439    case bVarFDName:
440       *((char **)value) = my_name;
441       Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
442       break;
443    case bVarLevel:
444    case bVarType:
445    case bVarClient:
446    case bVarJobName:
447    case bVarJobStatus:
448    case bVarSinceTime:
449       break;
450    }
451    return bRC_OK;
452 }
453
454 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
455 {
456    Dmsg1(dbglvl, "bacula: baculaSetValue var=%d\n", var);
457    return bRC_OK;
458 }
459
460 static bRC baculaRegisterEvents(bpContext *ctx, ...)
461 {
462    va_list args;
463    uint32_t event;
464
465    va_start(args, ctx);
466    while ((event = va_arg(args, uint32_t))) {
467       Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
468    }
469    va_end(args);
470    return bRC_OK;
471 }
472
473 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
474   int type, time_t mtime, const char *msg)
475 {
476    Dmsg5(dbglvl, "Job message: %s:%d type=%d time=%ld msg=%s\n",
477       file, line, type, mtime, msg);
478    return bRC_OK;
479 }
480
481 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
482   int level, const char *msg)
483 {
484    Dmsg4(dbglvl, "Debug message: %s:%d level=%d msg=%s\n",
485       file, line, level, msg);
486    return bRC_OK;
487 }
488
489 #ifdef TEST_PROGRAM
490
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;
496
497 int save_file(FF_PKT *ff_pkt, void *vjcr, bool top_level)
498 {
499    return 0;
500 }
501
502 int main(int argc, char *argv[])
503 {
504    char plugin_dir[1000];
505    JCR mjcr1, mjcr2;
506    JCR *jcr1 = &mjcr1;
507    JCR *jcr2 = &mjcr2;
508
509    strcpy(my_name, "test-fd");
510     
511    getcwd(plugin_dir, sizeof(plugin_dir)-1);
512    load_fd_plugins(plugin_dir);
513
514    jcr1->JobId = 111;
515    new_plugins(jcr1);
516
517    jcr2->JobId = 222;
518    new_plugins(jcr2);
519
520    generate_plugin_event(jcr1, bEventJobStart);
521    generate_plugin_event(jcr1, bEventJobEnd);
522    generate_plugin_event(jcr2, bEventJobStart);
523    free_plugins(jcr1);
524    generate_plugin_event(jcr2, bEventJobEnd);
525    free_plugins(jcr2);
526
527    unload_plugins();
528
529    Dmsg0(dbglvl, "bacula: OK ...\n");
530    close_memory_pool();
531    sm_dump(false);
532    return 0;
533 }
534
535 #endif /* TEST_PROGRAM */