]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/dir_plugins.c
6eb98f8a7150f16a630bfa83625b89e966dff611
[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 = 0;
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(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
159    Dmsg0(dbglvl, "Load dir plugins\n");
160    if (!plugin_dir) {
161       Dmsg0(dbglvl, "No dir plugin dir!\n");
162       return;
163    }
164    bplugin_list = New(alist(10, not_owned_by_alist));
165    if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type, 
166                 is_plugin_compatible)) {
167       /* Either none found, or some error */
168       if (bplugin_list->size() == 0) {
169          delete bplugin_list;
170          bplugin_list = NULL;
171          Dmsg0(dbglvl, "No plugins loaded\n");
172          return;
173       }
174    }
175    /* 
176     * Verify that the plugin is acceptable, and print information
177     *  about it.
178     */
179    foreach_alist(plugin, bplugin_list) {
180       Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
181       Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
182    }
183
184    Dmsg1(dbglvl, "num plugins=%d\n", bplugin_list->size());
185    dbg_plugin_add_hook(dump_dir_plugin);
186 }
187
188 /**
189  * Check if a plugin is compatible.  Called by the load_plugin function
190  *  to allow us to verify the plugin.
191  */
192 static bool is_plugin_compatible(Plugin *plugin)
193 {
194    pDirInfo *info = (pDirInfo *)plugin->pinfo;
195    Dmsg0(50, "is_plugin_compatible called\n");
196    if (debug_level >= 50) {
197       dump_dir_plugin(plugin, stdin);
198    }
199    if (strcmp(info->plugin_magic, DIR_PLUGIN_MAGIC) != 0) {
200       Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
201            plugin->file, DIR_PLUGIN_MAGIC, info->plugin_magic);
202       Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
203            plugin->file, DIR_PLUGIN_MAGIC, info->plugin_magic);
204
205       return false;
206    }
207    if (info->version != DIR_PLUGIN_INTERFACE_VERSION) {
208       Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
209            plugin->file, DIR_PLUGIN_INTERFACE_VERSION, info->version);
210       Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
211            plugin->file, DIR_PLUGIN_INTERFACE_VERSION, info->version);
212       return false;
213    }
214    if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
215        strcmp(info->plugin_license, "AGPLv3") != 0 &&
216        strcmp(info->plugin_license, "Bacula Systems(R) SA") != 0) {
217       Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
218            plugin->file, info->plugin_license);
219       Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
220            plugin->file, info->plugin_license);
221       return false;
222    }
223    if (info->size != sizeof(pDirInfo)) {
224       Jmsg(NULL, M_ERROR, 0,
225            _("Plugin size incorrect. Plugin=%s wanted=%d got=%d\n"),
226            plugin->file, sizeof(pDirInfo), info->size);
227       return false;
228    }
229       
230    return true;
231 }
232
233
234 /*
235  * Create a new instance of each plugin for this Job
236  */
237 void new_plugins(JCR *jcr)
238 {
239    Plugin *plugin;
240    int i = 0;
241
242    Dmsg0(dbglvl, "=== enter new_plugins ===\n");
243    if (!bplugin_list) {
244       Dmsg0(dbglvl, "No dir plugin list!\n");
245       return;
246    }
247    if (jcr->is_job_canceled()) {
248       return;
249    }
250
251    int num = bplugin_list->size();
252
253    Dmsg1(dbglvl, "dir-plugin-list size=%d\n", num);
254    if (num == 0) {
255       return;
256    }
257
258    jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
259
260    bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
261    Dmsg2(dbglvl, "Instantiate dir-plugin_ctx_list=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
262    foreach_alist(plugin, bplugin_list) {
263       /* Start a new instance of each plugin */
264       bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
265       memset(b_ctx, 0, sizeof(bacula_ctx));
266       b_ctx->jcr = jcr;
267       plugin_ctx_list[i].bContext = (void *)b_ctx;
268       plugin_ctx_list[i].pContext = NULL;
269       if (dirplug_func(plugin)->newPlugin(&plugin_ctx_list[i++]) != bRC_OK) {
270          b_ctx->disabled = true;
271       }
272    }
273 }
274
275 /*
276  * Free the plugin instances for this Job
277  */
278 void free_plugins(JCR *jcr)
279 {
280    Plugin *plugin;
281    int i = 0;
282
283    if (!bplugin_list || !jcr->plugin_ctx_list) {
284       return;
285    }
286
287    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
288    Dmsg2(dbglvl, "Free instance dir-plugin_ctx_list=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
289    foreach_alist(plugin, bplugin_list) {
290       /* Free the plugin instance */
291       dirplug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
292       free(plugin_ctx_list[i++].bContext);     /* free Bacula private context */
293    }
294    free(plugin_ctx_list);
295    jcr->plugin_ctx_list = NULL;
296 }
297
298
299 /* ==============================================================
300  *
301  * Callbacks from the plugin
302  *
303  * ==============================================================
304  */
305 static bRC baculaGetValue(bpContext *ctx, brDirVariable var, void *value)
306 {
307    JCR *jcr;
308    bRC ret = bRC_OK;
309
310    if (!ctx) {
311       return bRC_Error;
312    }
313    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
314    if (!jcr) {
315       return bRC_Error;
316    }
317    if (!value) {
318       return bRC_Error;
319    }
320    switch (var) {
321    case bDirVarJobId:
322       *((int *)value) = jcr->JobId;
323       Dmsg1(dbglvl, "dir-plugin: return bDirVarJobId=%d\n", jcr->JobId);
324       break;
325    case bDirVarJobName:
326       *((char **)value) = jcr->Job;
327       Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
328       break;
329    case bDirVarJob:
330       *((char **)value) = jcr->job->hdr.name;
331       Dmsg1(dbglvl, "Bacula: return bDirVarJob=%s\n", jcr->job->hdr.name);
332       break;
333    case bDirVarLevel:
334       *((int *)value) = jcr->getJobLevel();
335       Dmsg1(dbglvl, "Bacula: return bDirVarLevel=%c\n", jcr->getJobLevel());
336       break;
337    case bDirVarType:
338       *((int *)value) = jcr->getJobType();
339       Dmsg1(dbglvl, "Bacula: return bDirVarType=%c\n", jcr->getJobType());
340       break;
341    case bDirVarClient:
342       *((char **)value) = jcr->client->hdr.name;
343       Dmsg1(dbglvl, "Bacula: return bDirVarClient=%s\n", jcr->client->hdr.name);
344       break;
345    case bDirVarNumVols:
346       POOL_DBR pr;
347       memset(&pr, 0, sizeof(pr));
348       bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
349       if (!db_get_pool_record(jcr, jcr->db, &pr)) {
350          ret=bRC_Error;
351       }
352       *((int *)value) = pr.NumVols;
353       Dmsg1(dbglvl, "Bacula: return bDirVarNumVols=%d\n", pr.NumVols);
354       break;
355    case bDirVarPool:
356       *((char **)value) = jcr->pool->hdr.name;
357       Dmsg1(dbglvl, "Bacula: return bDirVarPool=%s\n", jcr->pool->hdr.name);
358       break;
359    case bDirVarStorage:
360       if (jcr->wstore) {
361          *((char **)value) = jcr->wstore->hdr.name;
362       } else if (jcr->rstore) {
363          *((char **)value) = jcr->rstore->hdr.name;
364       } else {
365          *((char **)value) = NULL;
366          ret=bRC_Error;
367       }
368       Dmsg1(dbglvl, "Bacula: return bDirVarStorage=%s\n", NPRT(*((char **)value)));
369       break;
370    case bDirVarWriteStorage:
371       if (jcr->wstore) {
372          *((char **)value) = jcr->wstore->hdr.name;
373       } else {
374          *((char **)value) = NULL;
375          ret=bRC_Error;
376       }
377       Dmsg1(dbglvl, "Bacula: return bDirVarWriteStorage=%s\n", NPRT(*((char **)value)));
378       break;
379    case bDirVarReadStorage:
380       if (jcr->rstore) {
381          *((char **)value) = jcr->rstore->hdr.name;
382       } else {
383          *((char **)value) = NULL;
384          ret=bRC_Error;
385       }
386       Dmsg1(dbglvl, "Bacula: return bDirVarReadStorage=%s\n", NPRT(*((char **)value)));
387       break;
388    case bDirVarCatalog:
389       *((char **)value) = jcr->catalog->hdr.name;
390       Dmsg1(dbglvl, "Bacula: return bDirVarCatalog=%s\n", jcr->catalog->hdr.name);
391       break;
392    case bDirVarMediaType:
393       if (jcr->wstore) {
394          *((char **)value) = jcr->wstore->media_type;
395       } else if (jcr->rstore) {
396          *((char **)value) = jcr->rstore->media_type;
397       } else {
398          *((char **)value) = NULL;
399          ret=bRC_Error;
400       }
401       Dmsg1(dbglvl, "Bacula: return bDirVarMediaType=%s\n", NPRT(*((char **)value)));
402       break;
403    case bDirVarJobStatus:
404       *((int *)value) = jcr->JobStatus;
405       Dmsg1(dbglvl, "Bacula: return bDirVarJobStatus=%c\n", jcr->JobStatus);
406       break;
407    case bDirVarPriority:
408       *((int *)value) = jcr->JobPriority;
409       Dmsg1(dbglvl, "Bacula: return bDirVarPriority=%d\n", jcr->JobPriority);
410       break;
411    case bDirVarVolumeName:
412       *((char **)value) = jcr->VolumeName;
413       Dmsg1(dbglvl, "Bacula: return bDirVarVolumeName=%s\n", jcr->VolumeName);
414       break;
415    case bDirVarCatalogRes:
416       ret = bRC_Error;
417       break;
418    case bDirVarJobErrors:
419       *((int *)value) = jcr->JobErrors;
420       Dmsg1(dbglvl, "Bacula: return bDirVarErrors=%d\n", jcr->JobErrors);
421       break;
422    case bDirVarJobFiles:
423       *((int *)value) = jcr->JobFiles;
424       Dmsg1(dbglvl, "Bacula: return bDirVarFiles=%d\n", jcr->JobFiles);
425       break;
426    case bDirVarSDJobFiles:
427       *((int *)value) = jcr->SDJobFiles;
428       Dmsg1(dbglvl, "Bacula: return bDirVarSDFiles=%d\n", jcr->SDJobFiles);
429       break;
430    case bDirVarSDErrors:
431       *((int *)value) = jcr->SDErrors;
432       Dmsg1(dbglvl, "Bacula: return bDirVarSDErrors=%d\n", jcr->SDErrors);
433       break;
434    case bDirVarFDJobStatus:
435       *((int *)value) = jcr->FDJobStatus;
436       Dmsg1(dbglvl, "Bacula: return bDirVarFDJobStatus=%c\n", jcr->FDJobStatus);
437       break;      
438    case bDirVarSDJobStatus:
439       *((int *)value) = jcr->SDJobStatus;
440       Dmsg1(dbglvl, "Bacula: return bDirVarSDJobStatus=%c\n", jcr->SDJobStatus);
441       break;      
442    default:
443       break;
444    }
445    return ret;
446 }
447
448 static bRC baculaSetValue(bpContext *ctx, bwDirVariable var, void *value)
449 {
450    JCR *jcr;   
451    if (!value || !ctx) {
452       return bRC_Error;
453    }
454 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
455    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
456    if (!jcr) {
457       return bRC_Error;
458    }
459 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
460    /* Nothing implemented yet */
461    Dmsg1(dbglvl, "dir-plugin: baculaSetValue var=%d\n", var);
462    return bRC_OK;
463 }
464
465 static bRC baculaRegisterEvents(bpContext *ctx, ...)
466 {
467    va_list args;
468    uint32_t event;
469
470    va_start(args, ctx);
471    while ((event = va_arg(args, uint32_t))) {
472       Dmsg1(dbglvl, "dir-Plugin wants event=%u\n", event);
473    }
474    va_end(args);
475    return bRC_OK;
476 }
477
478 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
479   int type, utime_t mtime, const char *fmt, ...)
480 {
481    va_list arg_ptr;
482    char buf[2000];
483    JCR *jcr;
484
485    if (ctx) {
486       jcr = ((bacula_ctx *)ctx->bContext)->jcr;
487    } else {
488       jcr = NULL;
489    }
490
491    va_start(arg_ptr, fmt);
492    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
493    va_end(arg_ptr);
494    Jmsg(jcr, type, mtime, "%s", buf);
495    return bRC_OK;
496 }
497
498 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
499   int level, const char *fmt, ...)
500 {
501    va_list arg_ptr;
502    char buf[2000];
503
504    va_start(arg_ptr, fmt);
505    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
506    va_end(arg_ptr);
507    d_msg(file, line, level, "%s", buf);
508    return bRC_OK;
509 }
510
511 #ifdef TEST_PROGRAM
512
513 int main(int argc, char *argv[])
514 {
515    char plugin_dir[1000];
516    JCR mjcr1, mjcr2;
517    JCR *jcr1 = &mjcr1;
518    JCR *jcr2 = &mjcr2;
519
520    strcpy(my_name, "test-dir");
521     
522    getcwd(plugin_dir, sizeof(plugin_dir)-1);
523    load_dir_plugins(plugin_dir);
524
525    jcr1->JobId = 111;
526    new_plugins(jcr1);
527
528    jcr2->JobId = 222;
529    new_plugins(jcr2);
530
531    generate_plugin_event(jcr1, bDirEventJobStart, (void *)"Start Job 1");
532    generate_plugin_event(jcr1, bDirEventJobEnd);
533    generate_plugin_event(jcr2, bDirEventJobStart, (void *)"Start Job 1");
534    free_plugins(jcr1);
535    generate_plugin_event(jcr2, bDirEventJobEnd);
536    free_plugins(jcr2);
537
538    unload_plugins();
539
540    Dmsg0(dbglvl, "dir-plugin: OK ...\n");
541    close_memory_pool();
542    sm_dump(false);
543    return 0;
544 }
545
546 #endif /* TEST_PROGRAM */