]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/fd_plugins.c
Add more plugin documentation and fix include of config.h for
[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 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.
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 /* 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);
48
49
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, ...);
58
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);
64
65
66 /* Bacula info */
67 static bInfo binfo = {
68    sizeof(bFuncs),
69    FD_PLUGIN_INTERFACE_VERSION 
70 };
71
72 /* Bacula entry points */
73 static bFuncs bfuncs = {
74    sizeof(bFuncs),
75    FD_PLUGIN_INTERFACE_VERSION,
76    baculaRegisterEvents,
77    baculaGetValue,
78    baculaSetValue,
79    baculaJobMsg,
80    baculaDebugMsg
81 };
82
83
84 /*
85  * Create a plugin event 
86  */
87 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value)     
88 {
89    bEvent event;
90    Plugin *plugin;
91    int i = 0;
92
93    if (!plugin_list) {
94       return;                         /* Return if no plugins loaded */
95    }
96
97    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
98    event.eventType = eventType;
99
100    Dmsg2(dbglvl, "plugin_ctx=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
101
102    /* Pass event to every plugin */
103    foreach_alist(plugin, plugin_list) {
104       bRC rc;
105       rc = plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i++], &event, value);
106       if (rc != bRC_OK) {
107          break;
108       }
109    }
110
111    return;
112 }
113
114 /*   
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
124  *    file data.
125  *
126  * Sequence of calls for restore:
127  *   See subroutine plugin_name_stream() below.
128  */
129 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
130 {
131    Plugin *plugin;
132    int i = 0;
133    int len;
134    char *p;
135    char *cmd = ff_pkt->top_fname;
136    struct save_pkt sp;
137    bEvent event;
138
139    if (!plugin_list) {
140       return 1;                            /* Return if no plugins loaded */
141    }
142
143    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
144    event.eventType = bEventBackupCommand;
145
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);
150       goto bail_out;
151    }
152    len = p - cmd;
153    if (len <= 0) {
154       goto bail_out;
155    }
156
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) {
160          i++;
161          continue;
162       }
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) {
166          goto bail_out;
167       }
168       /* Loop getting filenames to backup then saving them */
169       while (!job_canceled(jcr)) { 
170          memset(&sp, 0, sizeof(sp));
171          sp.type = FT_REG;
172          sp.portable = true;
173          sp.cmd = cmd;
174          Dmsg3(000, "startBackup st_size=%p st_blocks=%p sp=%p\n", &sp.statp.st_size, &sp.statp.st_blocks,
175                 &sp);
176          /* Get the file save parameters */
177          if (plug_func(plugin)->startBackupFile(&plugin_ctx_list[i], &sp) != bRC_OK) {
178             goto bail_out;
179          }
180          jcr->plugin_ctx = &plugin_ctx_list[i];
181          jcr->plugin = plugin;
182          jcr->plugin_sp = &sp;
183          ff_pkt = jcr->ff;
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) {
190             goto bail_out;
191          }
192       }
193    }
194    Jmsg1(jcr, M_ERROR, 0, "Command plugin \"%s\" not found.\n", cmd);
195
196 bail_out:
197    return 1;
198 }
199
200 /* 
201  * Send plugin name start/end record to SD
202  */
203 bool send_plugin_name(JCR *jcr, BSOCK *sd, bool start)
204 {
205    int stat;
206    struct save_pkt *sp = (struct save_pkt *)jcr->plugin_sp;
207   
208    Dmsg1(000, "send_plugin_name=%s\n", sp->cmd);
209    /* Send stream header */
210    if (!sd->fsend("%ld %d 0", jcr->JobFiles+1, STREAM_PLUGIN_NAME)) {
211      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
212            sd->bstrerror());
213      return false;
214    }
215    Dmsg1(000, "send: %s\n", sd->msg);
216
217    if (start) {
218       /* Send data -- not much */
219       stat = sd->fsend("%ld 1 %d %s%c", jcr->JobFiles+1, sp->portable, sp->cmd, 0);
220    } else {
221       /* Send end of data */
222       stat = sd->fsend("0 0");
223    }
224    if (!stat) {
225       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
226             sd->bstrerror());
227          return false;
228    }
229    Dmsg1(000, "send: %s\n", sd->msg);
230    sd->signal(BNET_EOD);            /* indicate end of plugin name data */
231    return true;
232 }
233
234 /*
235  * Plugin name stream found during restore.  The record passed in
236  *  argument name was generated in send_plugin_name() above.
237  */
238 void plugin_name_stream(JCR *jcr, char *name)    
239 {
240    char *p = name;
241    char *cmd;
242    bool start, portable;
243    Plugin *plugin;
244    int len;
245    int i = 0;
246    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
247
248    Dmsg1(100, "Read plugin stream string=%s\n", name);
249    skip_nonspaces(&p);             /* skip over jcr->JobFiles */
250    skip_spaces(&p);
251    start = *p == '1';
252    if (start) {
253       /* Start of plugin data */
254       skip_nonspaces(&p);          /* skip start/end flag */
255       skip_spaces(&p);
256       portable = *p == '1';
257       skip_nonspaces(&p);          /* skip portable flag */
258       skip_spaces(&p);
259       cmd = p;
260    } else {
261       /*
262        * End of plugin data, notify plugin, then clear flags   
263        */
264       plugin = (Plugin *)jcr->plugin;
265       plug_func(plugin)->endRestoreFile(&plugin_ctx_list[i]);
266       jcr->plugin_ctx = NULL;
267       jcr->plugin = NULL;
268       goto bail_out;
269    }
270       
271    /*
272     * After this point, we are dealing with a restore start
273     */
274
275    Dmsg1(100, "plugin restore cmd=%s\n", cmd);
276    if (!(p = strchr(cmd, ':'))) {
277       Jmsg1(jcr, M_ERROR, 0, "Malformed plugin command: %s\n", cmd);
278       goto bail_out;
279    }
280    len = p - cmd;
281    if (len <= 0) {
282       goto bail_out;
283    }
284
285    /*
286     * Search for correct plugin as specified on the command 
287     */
288    foreach_alist(plugin, plugin_list) {
289       bEvent event;
290       Dmsg3(100, "plugin=%s cmd=%s len=%d\n", plugin->file, cmd, len);
291       if (strncmp(plugin->file, cmd, len) != 0) {
292          i++;
293          continue;
294       }
295       Dmsg1(100, "Restore Command plugin = %s\n", cmd);
296       event.eventType = bEventRestoreCommand;     
297       if (plug_func(plugin)->handlePluginEvent(&plugin_ctx_list[i], 
298             &event, cmd) != bRC_OK) {
299          goto bail_out;
300       }
301       jcr->plugin_ctx = &plugin_ctx_list[i];
302       jcr->plugin = plugin;
303       goto bail_out;
304    }
305 bail_out:
306    return;
307 }
308
309 /*
310  * Tell the plugin to create the file.  Return values are
311  *
312  *  CF_ERROR    -- error
313  *  CF_SKIP     -- skip processing this file
314  *  CF_EXTRACT  -- extract the file (i.e.call i/o routines)
315  *  CF_CREATED  -- created, but no content to extract (typically directories)
316  *
317  */
318 int plugin_create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace)
319 {
320    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
321    Plugin *plugin = (Plugin *)jcr->plugin;
322    struct restore_pkt rp;
323    struct io_pkt io;
324
325    if (!set_cmd_plugin(bfd, jcr)) {
326       return CF_ERROR;
327    }
328    rp.stream = attr->stream;
329    rp.data_stream = attr->data_stream;
330    rp.type = attr->type;
331    rp.file_index = attr->file_index;
332    rp.LinkFI = attr->LinkFI;
333    rp.uid = attr->uid;
334    rp.statp = attr->statp;                /* structure assignment */
335    rp.attrEx = attr->attrEx;
336    rp.ofname = attr->ofname;
337    rp.olname = attr->olname;
338    rp.where = jcr->where;
339    rp.RegexWhere = jcr->RegexWhere;
340    rp.replace = jcr->replace;
341    if (plug_func(plugin)->createFile(plugin_ctx, &rp) != bRC_OK) {
342       return CF_ERROR;
343    }
344    io.func = IO_OPEN;
345    io.count = 0;
346    io.buf = NULL;
347    io.mode = 0777 & attr->statp.st_mode;
348    io.flags = O_WRONLY;
349    if (plug_func(plugin)->pluginIO(plugin_ctx, &io) != bRC_OK) {
350       return CF_ERROR;
351    }
352    return CF_EXTRACT;
353 }
354
355 /*
356  * Reset the file attributes after all file I/O is done -- this allows
357  *  the previous access time/dates to be set properly, and it also allows
358  *  us to properly set directory permissions.
359  */
360 bool plugin_set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
361 {
362    return true;
363 }
364
365 /*
366  * This entry point is called internally by Bacula to ensure
367  *  that the plugin IO calls come into this code.
368  */
369 void load_fd_plugins(const char *plugin_dir)
370 {
371    Plugin *plugin;
372
373    if (!plugin_dir) {
374       return;
375    }
376
377    plugin_list = New(alist(10, not_owned_by_alist));
378    load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type);
379
380    /* Plug entry points called from findlib */
381    plugin_bopen  = my_plugin_bopen;
382    plugin_bclose = my_plugin_bclose;
383    plugin_bread  = my_plugin_bread;
384    plugin_bwrite = my_plugin_bwrite;
385    plugin_blseek = my_plugin_blseek;
386    foreach_alist(plugin, plugin_list) {
387       Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
388    }
389
390 }
391
392 /*
393  * Create a new instance of each plugin for this Job
394  */
395 void new_plugins(JCR *jcr)
396 {
397    Plugin *plugin;
398    int i = 0;
399
400    if (!plugin_list) {
401       return;
402    }
403
404    int num = plugin_list->size();
405
406    if (num == 0) {
407       return;
408    }
409
410    jcr->plugin_ctx_list = (void *)malloc(sizeof(bpContext) * num);
411
412    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
413    Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
414    foreach_alist(plugin, plugin_list) {
415       /* Start a new instance of each plugin */
416       plugin_ctx_list[i].bContext = (void *)jcr;
417       plugin_ctx_list[i].pContext = NULL;
418       plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]);
419    }
420 }
421
422 /*
423  * Free the plugin instances for this Job
424  */
425 void free_plugins(JCR *jcr)
426 {
427    Plugin *plugin;
428    int i = 0;
429
430    if (!plugin_list) {
431       return;
432    }
433
434    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
435    Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
436    foreach_alist(plugin, plugin_list) {
437       /* Free the plugin instance */
438       plug_func(plugin)->freePlugin(&plugin_ctx_list[i++]);
439    }
440    free(plugin_ctx_list);
441    jcr->plugin_ctx_list = NULL;
442 }
443
444 static int my_plugin_bopen(JCR *jcr, const char *fname, int flags, mode_t mode)
445 {
446    Plugin *plugin = (Plugin *)jcr->plugin;
447    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
448    struct io_pkt io;
449    Dmsg0(000, "plugin_bopen\n");
450    io.func = IO_OPEN;
451    io.count = 0;
452    io.buf = NULL;
453    io.mode = mode;
454    io.flags = flags;
455    plug_func(plugin)->pluginIO(plugin_ctx, &io);
456    return io.status;
457 }
458
459 static int my_plugin_bclose(JCR *jcr)
460 {
461    Plugin *plugin = (Plugin *)jcr->plugin;
462    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
463    struct io_pkt io;
464    Dmsg0(000, "plugin_bclose\n");
465    io.func = IO_CLOSE;
466    io.count = 0;
467    io.buf = NULL;
468    plug_func(plugin)->pluginIO(plugin_ctx, &io);
469    return io.status;
470 }
471
472 static ssize_t my_plugin_bread(JCR *jcr, void *buf, size_t count)
473 {
474    Plugin *plugin = (Plugin *)jcr->plugin;
475    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
476    struct io_pkt io;
477    Dmsg0(000, "plugin_bread\n");
478    io.func = IO_READ;
479    io.count = count;
480    io.buf = (char *)buf;
481    plug_func(plugin)->pluginIO(plugin_ctx, &io);
482    return (ssize_t)io.status;
483 }
484
485 static ssize_t my_plugin_bwrite(JCR *jcr, void *buf, size_t count)
486 {
487    Plugin *plugin = (Plugin *)jcr->plugin;
488    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
489    struct io_pkt io;
490    Dmsg0(000, "plugin_bwrite\n");
491    io.func = IO_WRITE;
492    io.count = count;
493    io.buf = (char *)buf;
494    plug_func(plugin)->pluginIO(plugin_ctx, &io);
495    return (ssize_t)io.status;
496 }
497
498 static boffset_t my_plugin_blseek(JCR *jcr, boffset_t offset, int whence)
499 {
500    Plugin *plugin = (Plugin *)jcr->plugin;
501    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
502    struct io_pkt io;
503    Dmsg0(000, "plugin_bseek\n");
504    io.func = IO_SEEK;
505    io.offset = offset;
506    io.whence = whence;
507    plug_func(plugin)->pluginIO(plugin_ctx, &io);
508    return (boffset_t)io.offset;
509 }
510
511 /* ==============================================================
512  *
513  * Callbacks from the plugin
514  *
515  * ==============================================================
516  */
517 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
518 {
519    JCR *jcr = (JCR *)(ctx->bContext);
520 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
521    if (!value) {
522       return bRC_Error;
523    }
524 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
525    switch (var) {
526    case bVarJobId:
527       *((int *)value) = jcr->JobId;
528       Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
529       break;
530    case bVarFDName:
531       *((char **)value) = my_name;
532       Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
533       break;
534    case bVarLevel:
535    case bVarType:
536    case bVarClient:
537    case bVarJobName:
538    case bVarJobStatus:
539    case bVarSinceTime:
540       break;
541    }
542    return bRC_OK;
543 }
544
545 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
546 {
547    Dmsg1(dbglvl, "bacula: baculaSetValue var=%d\n", var);
548    return bRC_OK;
549 }
550
551 static bRC baculaRegisterEvents(bpContext *ctx, ...)
552 {
553    va_list args;
554    uint32_t event;
555
556    va_start(args, ctx);
557    while ((event = va_arg(args, uint32_t))) {
558       Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
559    }
560    va_end(args);
561    return bRC_OK;
562 }
563
564 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
565   int type, time_t mtime, const char *fmt, ...)
566 {
567    va_list arg_ptr;
568    char buf[2000];
569    JCR *jcr = (JCR *)(ctx->bContext);
570
571    va_start(arg_ptr, fmt);
572    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
573    va_end(arg_ptr);
574    Jmsg(jcr, type, mtime, "%s", buf);
575    return bRC_OK;
576 }
577
578 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
579   int level, const char *fmt, ...)
580 {
581    va_list arg_ptr;
582    char buf[2000];
583
584    va_start(arg_ptr, fmt);
585    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
586    va_end(arg_ptr);
587    d_msg(file, line, level, "%s", buf);
588    return bRC_OK;
589 }
590
591 #ifdef TEST_PROGRAM
592
593 int     (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
594 int     (*plugin_bclose)(JCR *jcr) = NULL;
595 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
596 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
597 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
598
599 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
600 {
601    return 0;
602 }
603
604 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
605 {
606    return true;
607 }
608
609 int main(int argc, char *argv[])
610 {
611    char plugin_dir[1000];
612    JCR mjcr1, mjcr2;
613    JCR *jcr1 = &mjcr1;
614    JCR *jcr2 = &mjcr2;
615
616    strcpy(my_name, "test-fd");
617     
618    getcwd(plugin_dir, sizeof(plugin_dir)-1);
619    load_fd_plugins(plugin_dir);
620
621    jcr1->JobId = 111;
622    new_plugins(jcr1);
623
624    jcr2->JobId = 222;
625    new_plugins(jcr2);
626
627    generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
628    generate_plugin_event(jcr1, bEventJobEnd);
629    generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
630    free_plugins(jcr1);
631    generate_plugin_event(jcr2, bEventJobEnd);
632    free_plugins(jcr2);
633
634    unload_plugins();
635
636    Dmsg0(dbglvl, "bacula: OK ...\n");
637    close_memory_pool();
638    sm_dump(false);
639    return 0;
640 }
641
642 #endif /* TEST_PROGRAM */