]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/dir_plugins.c
Merge branch 'Branch-5.2' of bsweb:bacula into Branch-5.2
[bacula/bacula] / bacula / src / dird / dir_plugins.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2011 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 three of the GNU Affero 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 Affero 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 "dird.h"
36 #include "dir_plugins.h"
37
38 const int dbglvl = 50;
39 const char *plugin_type = "-dir.so";
40
41
42 /* Forward referenced functions */
43 static bRC baculaGetValue(bpContext *ctx, brDirVariable var, void *value);
44 static bRC baculaSetValue(bpContext *ctx, bwDirVariable var, void *value);
45 static bRC baculaRegisterEvents(bpContext *ctx, ...);
46 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
47   int type, utime_t mtime, const char *fmt, ...);
48 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
49   int level, const char *fmt, ...);
50 static bool is_plugin_compatible(Plugin *plugin);
51
52
53 /* Bacula info */
54 static bDirInfo binfo = {
55    sizeof(bDirFuncs),
56    DIR_PLUGIN_INTERFACE_VERSION
57 };
58
59 /* Bacula entry points */
60 static bDirFuncs bfuncs = {
61    sizeof(bDirFuncs),
62    DIR_PLUGIN_INTERFACE_VERSION,
63    baculaRegisterEvents,
64    baculaGetValue,
65    baculaSetValue,
66    baculaJobMsg,
67    baculaDebugMsg
68 };
69
70 /* 
71  * Bacula private context
72  */
73 struct bacula_ctx {
74    JCR *jcr;                             /* jcr for plugin */
75    bRC  rc;                              /* last return code */
76    bool disabled;                        /* set if plugin disabled */
77 };
78
79 static bool is_plugin_disabled(bpContext *plugin_ctx)
80 {
81    bacula_ctx *b_ctx;
82    if (!plugin_ctx) {
83       return true;
84    }
85    b_ctx = (bacula_ctx *)plugin_ctx->bContext;
86    return b_ctx->disabled;
87 }
88
89 #ifdef needed
90 static bool is_plugin_disabled(JCR *jcr)
91 {
92    return is_plugin_disabled(jcr->plugin_ctx);
93 }
94 #endif
95
96 /*
97  * Create a plugin event 
98  */
99 int generate_plugin_event(JCR *jcr, bDirEventType eventType, void *value)
100 {
101    bpContext *plugin_ctx;
102    bDirEvent event;
103    Plugin *plugin;
104    int i;
105    bRC rc = bRC_OK;
106
107    if (!bplugin_list || !jcr || !jcr->plugin_ctx_list) {
108       return bRC_OK;                  /* Return if no plugins loaded */
109    }
110    if (jcr->is_job_canceled()) {
111       return bRC_Cancel;
112    }
113
114    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
115    event.eventType = eventType;
116
117    Dmsg2(dbglvl, "dir-plugin_ctx_list=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
118
119    foreach_alist_index(i, plugin, bplugin_list) {
120       plugin_ctx = &plugin_ctx_list[i];
121       if (is_plugin_disabled(plugin_ctx)) {
122          continue;
123       }
124       rc = dirplug_func(plugin)->handlePluginEvent(plugin_ctx, &event, value);
125       if (rc != bRC_OK) {
126          break;
127       }
128    }
129
130    return rc;
131 }
132
133 /*
134  * Print to file the plugin info.
135  */
136 void dump_dir_plugin(Plugin *plugin, FILE *fp)
137 {
138    if (!plugin) {
139       return ;
140    }
141    pDirInfo *info = (pDirInfo *) plugin->pinfo;
142    fprintf(fp, "\tversion=%d\n", info->version);
143    fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
144    fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
145    fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
146    fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
147    fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
148    fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
149 }
150
151 /**
152  * This entry point is called internally by Bacula to ensure
153  *  that the plugin IO calls come into this code.
154  */
155 void load_dir_plugins(const char *plugin_dir)
156 {
157    Plugin *plugin;
158    int i;
159
160    Dmsg0(dbglvl, "Load dir plugins\n");
161    if (!plugin_dir) {
162       Dmsg0(dbglvl, "No dir plugin dir!\n");
163       return;
164    }
165    bplugin_list = New(alist(10, not_owned_by_alist));
166    if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type, 
167                 is_plugin_compatible)) {
168       /* Either none found, or some error */
169       if (bplugin_list->size() == 0) {
170          delete bplugin_list;
171          bplugin_list = NULL;
172          Dmsg0(dbglvl, "No plugins loaded\n");
173          return;
174       }
175    }
176    /* 
177     * Verify that the plugin is acceptable, and print information
178     *  about it.
179     */
180    foreach_alist_index(i, plugin, bplugin_list) {
181       Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
182       Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
183    }
184
185    Dmsg1(dbglvl, "num plugins=%d\n", bplugin_list->size());
186    dbg_plugin_add_hook(dump_dir_plugin);
187 }
188
189 /**
190  * Check if a plugin is compatible.  Called by the load_plugin function
191  *  to allow us to verify the plugin.
192  */
193 static bool is_plugin_compatible(Plugin *plugin)
194 {
195    pDirInfo *info = (pDirInfo *)plugin->pinfo;
196    Dmsg0(50, "is_plugin_compatible called\n");
197    if (debug_level >= 50) {
198       dump_dir_plugin(plugin, stdin);
199    }
200    if (strcmp(info->plugin_magic, DIR_PLUGIN_MAGIC) != 0) {
201       Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
202            plugin->file, DIR_PLUGIN_MAGIC, info->plugin_magic);
203       Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
204            plugin->file, DIR_PLUGIN_MAGIC, info->plugin_magic);
205
206       return false;
207    }
208    if (info->version != DIR_PLUGIN_INTERFACE_VERSION) {
209       Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
210            plugin->file, DIR_PLUGIN_INTERFACE_VERSION, info->version);
211       Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
212            plugin->file, DIR_PLUGIN_INTERFACE_VERSION, info->version);
213       return false;
214    }
215    if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
216        strcmp(info->plugin_license, "AGPLv3") != 0 &&
217        strcmp(info->plugin_license, "Bacula Systems(R) SA") != 0) {
218       Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
219            plugin->file, info->plugin_license);
220       Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
221            plugin->file, info->plugin_license);
222       return false;
223    }
224    if (info->size != sizeof(pDirInfo)) {
225       Jmsg(NULL, M_ERROR, 0,
226            _("Plugin size incorrect. Plugin=%s wanted=%d got=%d\n"),
227            plugin->file, sizeof(pDirInfo), info->size);
228       return false;
229    }
230       
231    return true;
232 }
233
234
235 /*
236  * Create a new instance of each plugin for this Job
237  */
238 void new_plugins(JCR *jcr)
239 {
240    Plugin *plugin;
241    int i;
242
243    Dmsg0(dbglvl, "=== enter new_plugins ===\n");
244    if (!bplugin_list) {
245       Dmsg0(dbglvl, "No dir plugin list!\n");
246       return;
247    }
248    if (jcr->is_job_canceled()) {
249       return;
250    }
251
252    int num = bplugin_list->size();
253
254    Dmsg1(dbglvl, "dir-plugin-list size=%d\n", num);
255    if (num == 0) {
256       return;
257    }
258
259    jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
260
261    bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
262    Dmsg2(dbglvl, "Instantiate dir-plugin_ctx_list=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
263    foreach_alist_index(i, plugin, bplugin_list) {
264       /* Start a new instance of each plugin */
265       bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
266       memset(b_ctx, 0, sizeof(bacula_ctx));
267       b_ctx->jcr = jcr;
268       plugin_ctx_list[i].bContext = (void *)b_ctx;
269       plugin_ctx_list[i].pContext = NULL;
270       if (dirplug_func(plugin)->newPlugin(&plugin_ctx_list[i]) != bRC_OK) {
271          b_ctx->disabled = true;
272       }
273    }
274 }
275
276 /*
277  * Free the plugin instances for this Job
278  */
279 void free_plugins(JCR *jcr)
280 {
281    Plugin *plugin;
282    int i;
283
284    if (!bplugin_list || !jcr->plugin_ctx_list) {
285       return;
286    }
287
288    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
289    Dmsg2(dbglvl, "Free instance dir-plugin_ctx_list=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
290    foreach_alist_index(i, plugin, bplugin_list) {
291       /* Free the plugin instance */
292       dirplug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
293       free(plugin_ctx_list[i].bContext);     /* free Bacula private context */
294    }
295    free(plugin_ctx_list);
296    jcr->plugin_ctx_list = NULL;
297 }
298
299
300 /* ==============================================================
301  *
302  * Callbacks from the plugin
303  *
304  * ==============================================================
305  */
306 static bRC baculaGetValue(bpContext *ctx, brDirVariable var, void *value)
307 {
308    JCR *jcr;
309    bRC ret = bRC_OK;
310
311    if (!ctx) {
312       return bRC_Error;
313    }
314    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
315    if (!jcr) {
316       return bRC_Error;
317    }
318    if (!value) {
319       return bRC_Error;
320    }
321    switch (var) {
322    case bDirVarJobId:
323       *((int *)value) = jcr->JobId;
324       Dmsg1(dbglvl, "dir-plugin: return bDirVarJobId=%d\n", jcr->JobId);
325       break;
326    case bDirVarJobName:
327       *((char **)value) = jcr->Job;
328       Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
329       break;
330    case bDirVarJob:
331       *((char **)value) = jcr->job->hdr.name;
332       Dmsg1(dbglvl, "Bacula: return bDirVarJob=%s\n", jcr->job->hdr.name);
333       break;
334    case bDirVarLevel:
335       *((int *)value) = jcr->getJobLevel();
336       Dmsg1(dbglvl, "Bacula: return bDirVarLevel=%c\n", jcr->getJobLevel());
337       break;
338    case bDirVarType:
339       *((int *)value) = jcr->getJobType();
340       Dmsg1(dbglvl, "Bacula: return bDirVarType=%c\n", jcr->getJobType());
341       break;
342    case bDirVarClient:
343       *((char **)value) = jcr->client->hdr.name;
344       Dmsg1(dbglvl, "Bacula: return bDirVarClient=%s\n", jcr->client->hdr.name);
345       break;
346    case bDirVarNumVols:
347       POOL_DBR pr;
348       memset(&pr, 0, sizeof(pr));
349       bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
350       if (!db_get_pool_record(jcr, jcr->db, &pr)) {
351          ret=bRC_Error;
352       }
353       *((int *)value) = pr.NumVols;
354       Dmsg1(dbglvl, "Bacula: return bDirVarNumVols=%d\n", pr.NumVols);
355       break;
356    case bDirVarPool:
357       *((char **)value) = jcr->pool->hdr.name;
358       Dmsg1(dbglvl, "Bacula: return bDirVarPool=%s\n", jcr->pool->hdr.name);
359       break;
360    case bDirVarStorage:
361       if (jcr->wstore) {
362          *((char **)value) = jcr->wstore->hdr.name;
363       } else if (jcr->rstore) {
364          *((char **)value) = jcr->rstore->hdr.name;
365       } else {
366          *((char **)value) = NULL;
367          ret=bRC_Error;
368       }
369       Dmsg1(dbglvl, "Bacula: return bDirVarStorage=%s\n", NPRT(*((char **)value)));
370       break;
371    case bDirVarWriteStorage:
372       if (jcr->wstore) {
373          *((char **)value) = jcr->wstore->hdr.name;
374       } else {
375          *((char **)value) = NULL;
376          ret=bRC_Error;
377       }
378       Dmsg1(dbglvl, "Bacula: return bDirVarWriteStorage=%s\n", NPRT(*((char **)value)));
379       break;
380    case bDirVarReadStorage:
381       if (jcr->rstore) {
382          *((char **)value) = jcr->rstore->hdr.name;
383       } else {
384          *((char **)value) = NULL;
385          ret=bRC_Error;
386       }
387       Dmsg1(dbglvl, "Bacula: return bDirVarReadStorage=%s\n", NPRT(*((char **)value)));
388       break;
389    case bDirVarCatalog:
390       *((char **)value) = jcr->catalog->hdr.name;
391       Dmsg1(dbglvl, "Bacula: return bDirVarCatalog=%s\n", jcr->catalog->hdr.name);
392       break;
393    case bDirVarMediaType:
394       if (jcr->wstore) {
395          *((char **)value) = jcr->wstore->media_type;
396       } else if (jcr->rstore) {
397          *((char **)value) = jcr->rstore->media_type;
398       } else {
399          *((char **)value) = NULL;
400          ret=bRC_Error;
401       }
402       Dmsg1(dbglvl, "Bacula: return bDirVarMediaType=%s\n", NPRT(*((char **)value)));
403       break;
404    case bDirVarJobStatus:
405       *((int *)value) = jcr->JobStatus;
406       Dmsg1(dbglvl, "Bacula: return bDirVarJobStatus=%c\n", jcr->JobStatus);
407       break;
408    case bDirVarPriority:
409       *((int *)value) = jcr->JobPriority;
410       Dmsg1(dbglvl, "Bacula: return bDirVarPriority=%d\n", jcr->JobPriority);
411       break;
412    case bDirVarVolumeName:
413       *((char **)value) = jcr->VolumeName;
414       Dmsg1(dbglvl, "Bacula: return bDirVarVolumeName=%s\n", jcr->VolumeName);
415       break;
416    case bDirVarCatalogRes:
417       ret = bRC_Error;
418       break;
419    case bDirVarJobErrors:
420       *((int *)value) = jcr->JobErrors;
421       Dmsg1(dbglvl, "Bacula: return bDirVarErrors=%d\n", jcr->JobErrors);
422       break;
423    case bDirVarJobFiles:
424       *((int *)value) = jcr->JobFiles;
425       Dmsg1(dbglvl, "Bacula: return bDirVarFiles=%d\n", jcr->JobFiles);
426       break;
427    case bDirVarSDJobFiles:
428       *((int *)value) = jcr->SDJobFiles;
429       Dmsg1(dbglvl, "Bacula: return bDirVarSDFiles=%d\n", jcr->SDJobFiles);
430       break;
431    case bDirVarSDErrors:
432       *((int *)value) = jcr->SDErrors;
433       Dmsg1(dbglvl, "Bacula: return bDirVarSDErrors=%d\n", jcr->SDErrors);
434       break;
435    case bDirVarFDJobStatus:
436       *((int *)value) = jcr->FDJobStatus;
437       Dmsg1(dbglvl, "Bacula: return bDirVarFDJobStatus=%c\n", jcr->FDJobStatus);
438       break;      
439    case bDirVarSDJobStatus:
440       *((int *)value) = jcr->SDJobStatus;
441       Dmsg1(dbglvl, "Bacula: return bDirVarSDJobStatus=%c\n", jcr->SDJobStatus);
442       break;      
443    default:
444       break;
445    }
446    return ret;
447 }
448
449 static bRC baculaSetValue(bpContext *ctx, bwDirVariable var, void *value)
450 {
451    JCR *jcr;   
452    if (!value || !ctx) {
453       return bRC_Error;
454    }
455 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
456    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
457    if (!jcr) {
458       return bRC_Error;
459    }
460 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
461    /* Nothing implemented yet */
462    Dmsg1(dbglvl, "dir-plugin: baculaSetValue var=%d\n", var);
463    return bRC_OK;
464 }
465
466 static bRC baculaRegisterEvents(bpContext *ctx, ...)
467 {
468    va_list args;
469    uint32_t event;
470
471    va_start(args, ctx);
472    while ((event = va_arg(args, uint32_t))) {
473       Dmsg1(dbglvl, "dir-Plugin wants event=%u\n", event);
474    }
475    va_end(args);
476    return bRC_OK;
477 }
478
479 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
480   int type, utime_t mtime, const char *fmt, ...)
481 {
482    va_list arg_ptr;
483    char buf[2000];
484    JCR *jcr;
485
486    if (ctx) {
487       jcr = ((bacula_ctx *)ctx->bContext)->jcr;
488    } else {
489       jcr = NULL;
490    }
491
492    va_start(arg_ptr, fmt);
493    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
494    va_end(arg_ptr);
495    Jmsg(jcr, type, mtime, "%s", buf);
496    return bRC_OK;
497 }
498
499 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
500   int level, const char *fmt, ...)
501 {
502    va_list arg_ptr;
503    char buf[2000];
504
505    va_start(arg_ptr, fmt);
506    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
507    va_end(arg_ptr);
508    d_msg(file, line, level, "%s", buf);
509    return bRC_OK;
510 }
511
512 #ifdef TEST_PROGRAM
513
514 int main(int argc, char *argv[])
515 {
516    char plugin_dir[1000];
517    JCR mjcr1, mjcr2;
518    JCR *jcr1 = &mjcr1;
519    JCR *jcr2 = &mjcr2;
520
521    strcpy(my_name, "test-dir");
522     
523    getcwd(plugin_dir, sizeof(plugin_dir)-1);
524    load_dir_plugins(plugin_dir);
525
526    jcr1->JobId = 111;
527    new_plugins(jcr1);
528
529    jcr2->JobId = 222;
530    new_plugins(jcr2);
531
532    generate_plugin_event(jcr1, bDirEventJobStart, (void *)"Start Job 1");
533    generate_plugin_event(jcr1, bDirEventJobEnd);
534    generate_plugin_event(jcr2, bDirEventJobStart, (void *)"Start Job 1");
535    free_plugins(jcr1);
536    generate_plugin_event(jcr2, bDirEventJobEnd);
537    free_plugins(jcr2);
538
539    unload_plugins();
540
541    Dmsg0(dbglvl, "dir-plugin: OK ...\n");
542    close_memory_pool();
543    sm_dump(false);
544    return 0;
545 }
546
547 #endif /* TEST_PROGRAM */