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