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