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