]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/fd_plugins.c
Rename fd/dir plugin to use Bacula standard underscore.
[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 /* 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    if (!plugin_dir) {
365       return;
366    }
367
368    plugin_list = New(alist(10, not_owned_by_alist));
369    load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type);
370
371    /* Plug entry points called from findlib */
372    plugin_bopen  = my_plugin_bopen;
373    plugin_bclose = my_plugin_bclose;
374    plugin_bread  = my_plugin_bread;
375    plugin_bwrite = my_plugin_bwrite;
376    plugin_blseek = my_plugin_blseek;
377
378 }
379
380 /*
381  * Create a new instance of each plugin for this Job
382  */
383 void new_plugins(JCR *jcr)
384 {
385    Plugin *plugin;
386    int i = 0;
387
388    if (!plugin_list) {
389       return;
390    }
391
392    int num = plugin_list->size();
393
394    if (num == 0) {
395       return;
396    }
397
398    jcr->plugin_ctx_list = (void *)malloc(sizeof(bpContext) * num);
399
400    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
401    Dmsg2(dbglvl, "Instantiate plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
402    foreach_alist(plugin, plugin_list) {
403       /* Start a new instance of each plugin */
404       plugin_ctx_list[i].bContext = (void *)jcr;
405       plugin_ctx_list[i].pContext = NULL;
406       plug_func(plugin)->newPlugin(&plugin_ctx_list[i++]);
407    }
408 }
409
410 /*
411  * Free the plugin instances for this Job
412  */
413 void free_plugins(JCR *jcr)
414 {
415    Plugin *plugin;
416    int i = 0;
417
418    if (!plugin_list) {
419       return;
420    }
421
422    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
423    Dmsg2(dbglvl, "Free instance plugin_ctx=%p JobId=%d\n", plugin_ctx_list, jcr->JobId);
424    foreach_alist(plugin, plugin_list) {
425       /* Free the plugin instance */
426       plug_func(plugin)->freePlugin(&plugin_ctx_list[i++]);
427    }
428    free(plugin_ctx_list);
429    jcr->plugin_ctx_list = NULL;
430 }
431
432 static int my_plugin_bopen(JCR *jcr, const char *fname, int flags, mode_t mode)
433 {
434    Plugin *plugin = (Plugin *)jcr->plugin;
435    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
436    struct io_pkt io;
437    Dmsg0(000, "plugin_bopen\n");
438    io.func = IO_OPEN;
439    io.count = 0;
440    io.buf = NULL;
441    io.mode = mode;
442    io.flags = flags;
443    plug_func(plugin)->pluginIO(plugin_ctx, &io);
444    return io.status;
445 }
446
447 static int my_plugin_bclose(JCR *jcr)
448 {
449    Plugin *plugin = (Plugin *)jcr->plugin;
450    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
451    struct io_pkt io;
452    Dmsg0(000, "plugin_bclose\n");
453    io.func = IO_CLOSE;
454    io.count = 0;
455    io.buf = NULL;
456    plug_func(plugin)->pluginIO(plugin_ctx, &io);
457    return io.status;
458 }
459
460 static ssize_t my_plugin_bread(JCR *jcr, void *buf, size_t count)
461 {
462    Plugin *plugin = (Plugin *)jcr->plugin;
463    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
464    struct io_pkt io;
465    Dmsg0(000, "plugin_bread\n");
466    io.func = IO_READ;
467    io.count = count;
468    io.buf = (char *)buf;
469    plug_func(plugin)->pluginIO(plugin_ctx, &io);
470    return (ssize_t)io.status;
471 }
472
473 static ssize_t my_plugin_bwrite(JCR *jcr, void *buf, size_t count)
474 {
475    Plugin *plugin = (Plugin *)jcr->plugin;
476    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
477    struct io_pkt io;
478    Dmsg0(000, "plugin_bwrite\n");
479    io.func = IO_WRITE;
480    io.count = count;
481    io.buf = (char *)buf;
482    plug_func(plugin)->pluginIO(plugin_ctx, &io);
483    return (ssize_t)io.status;
484 }
485
486 static boffset_t my_plugin_blseek(JCR *jcr, boffset_t offset, int whence)
487 {
488    Plugin *plugin = (Plugin *)jcr->plugin;
489    bpContext *plugin_ctx = (bpContext *)jcr->plugin_ctx;
490    struct io_pkt io;
491    Dmsg0(000, "plugin_bseek\n");
492    io.func = IO_SEEK;
493    io.offset = offset;
494    io.whence = whence;
495    plug_func(plugin)->pluginIO(plugin_ctx, &io);
496    return (boffset_t)io.offset;
497 }
498
499 /* ==============================================================
500  *
501  * Callbacks from the plugin
502  *
503  * ==============================================================
504  */
505 static bRC baculaGetValue(bpContext *ctx, bVariable var, void *value)
506 {
507    JCR *jcr = (JCR *)(ctx->bContext);
508 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
509    if (!value) {
510       return bRC_Error;
511    }
512 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
513    switch (var) {
514    case bVarJobId:
515       *((int *)value) = jcr->JobId;
516       Dmsg1(dbglvl, "Bacula: return bVarJobId=%d\n", jcr->JobId);
517       break;
518    case bVarFDName:
519       *((char **)value) = my_name;
520       Dmsg1(dbglvl, "Bacula: return my_name=%s\n", my_name);
521       break;
522    case bVarLevel:
523    case bVarType:
524    case bVarClient:
525    case bVarJobName:
526    case bVarJobStatus:
527    case bVarSinceTime:
528       break;
529    }
530    return bRC_OK;
531 }
532
533 static bRC baculaSetValue(bpContext *ctx, bVariable var, void *value)
534 {
535    Dmsg1(dbglvl, "bacula: baculaSetValue var=%d\n", var);
536    return bRC_OK;
537 }
538
539 static bRC baculaRegisterEvents(bpContext *ctx, ...)
540 {
541    va_list args;
542    uint32_t event;
543
544    va_start(args, ctx);
545    while ((event = va_arg(args, uint32_t))) {
546       Dmsg1(dbglvl, "Plugin wants event=%u\n", event);
547    }
548    va_end(args);
549    return bRC_OK;
550 }
551
552 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
553   int type, time_t mtime, const char *fmt, ...)
554 {
555    va_list arg_ptr;
556    char buf[2000];
557    JCR *jcr = (JCR *)(ctx->bContext);
558
559    va_start(arg_ptr, fmt);
560    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
561    va_end(arg_ptr);
562    Jmsg(jcr, type, mtime, "%s", buf);
563    return bRC_OK;
564 }
565
566 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
567   int level, const char *fmt, ...)
568 {
569    va_list arg_ptr;
570    char buf[2000];
571
572    va_start(arg_ptr, fmt);
573    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
574    va_end(arg_ptr);
575    d_msg(file, line, level, "%s", buf);
576    return bRC_OK;
577 }
578
579 #ifdef TEST_PROGRAM
580
581 int     (*plugin_bopen)(JCR *jcr, const char *fname, int flags, mode_t mode) = NULL;
582 int     (*plugin_bclose)(JCR *jcr) = NULL;
583 ssize_t (*plugin_bread)(JCR *jcr, void *buf, size_t count) = NULL;
584 ssize_t (*plugin_bwrite)(JCR *jcr, void *buf, size_t count) = NULL;
585 boffset_t (*plugin_blseek)(JCR *jcr, boffset_t offset, int whence) = NULL;
586
587 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
588 {
589    return 0;
590 }
591
592 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
593 {
594    return true;
595 }
596
597 int main(int argc, char *argv[])
598 {
599    char plugin_dir[1000];
600    JCR mjcr1, mjcr2;
601    JCR *jcr1 = &mjcr1;
602    JCR *jcr2 = &mjcr2;
603
604    strcpy(my_name, "test-fd");
605     
606    getcwd(plugin_dir, sizeof(plugin_dir)-1);
607    load_fd_plugins(plugin_dir);
608
609    jcr1->JobId = 111;
610    new_plugins(jcr1);
611
612    jcr2->JobId = 222;
613    new_plugins(jcr2);
614
615    generate_plugin_event(jcr1, bEventJobStart, (void *)"Start Job 1");
616    generate_plugin_event(jcr1, bEventJobEnd);
617    generate_plugin_event(jcr2, bEventJobStart, (void *)"Start Job 2");
618    free_plugins(jcr1);
619    generate_plugin_event(jcr2, bEventJobEnd);
620    free_plugins(jcr2);
621
622    unload_plugins();
623
624    Dmsg0(dbglvl, "bacula: OK ...\n");
625    close_memory_pool();
626    sm_dump(false);
627    return 0;
628 }
629
630 #endif /* TEST_PROGRAM */