]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/sd_plugins.c
backport code from master
[bacula/bacula] / bacula / src / stored / sd_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 "stored.h"
36 #include "sd_plugins.h"
37
38 const int dbglvl = 150;
39 const char *plugin_type = "-sd.so";
40
41
42 /* Forward referenced functions */
43 static bRC baculaGetValue(bpContext *ctx, bsdrVariable var, void *value);
44 static bRC baculaSetValue(bpContext *ctx, bsdwVariable 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 char *baculaEditDeviceCodes(DCR *dcr, char *omsg, 
51   const char *imsg, const char *cmd);
52 static bool is_plugin_compatible(Plugin *plugin);
53
54
55 /* Bacula info */
56 static bsdInfo binfo = {
57    sizeof(bsdFuncs),
58    SD_PLUGIN_INTERFACE_VERSION
59 };
60
61 /* Bacula entry points */
62 static bsdFuncs bfuncs = {
63    sizeof(bsdFuncs),
64    SD_PLUGIN_INTERFACE_VERSION,
65    baculaRegisterEvents,
66    baculaGetValue,
67    baculaSetValue,
68    baculaJobMsg,
69    baculaDebugMsg,
70    baculaEditDeviceCodes
71 };
72
73 /* 
74  * Bacula private context
75  */
76 struct bacula_ctx {
77    JCR *jcr;                             /* jcr for plugin */
78    bRC  rc;                              /* last return code */
79    bool disabled;                        /* set if plugin disabled */
80 };
81
82 static bool is_plugin_disabled(bpContext *plugin_ctx)
83 {
84    bacula_ctx *b_ctx;
85    if (!plugin_ctx) {
86       return true;
87    }
88    b_ctx = (bacula_ctx *)plugin_ctx->bContext;
89    return b_ctx->disabled;
90 }
91
92 #ifdef needed
93 static bool is_plugin_disabled(JCR *jcr)
94 {
95    return is_plugin_disabled(jcr->plugin_ctx);
96 }
97 #endif
98
99 /*
100  * Create a plugin event 
101  */
102 int generate_plugin_event(JCR *jcr, bsdEventType eventType, void *value)
103 {
104    bpContext *plugin_ctx;
105    bsdEvent event;
106    Plugin *plugin;
107    int i;
108    bRC rc = bRC_OK;
109
110    if (!bplugin_list) {
111       Dmsg0(dbglvl, "No bplugin_list: generate_plugin_event ignored.\n");
112       return bRC_OK;
113    }
114    if (!jcr) {
115       Dmsg0(dbglvl, "No jcr: generate_plugin_event ignored.\n");
116       return bRC_OK;
117    }
118    if (!jcr->plugin_ctx_list) {
119       Dmsg0(dbglvl, "No plugin_ctx_list: generate_plugin_event ignored.\n");
120       return bRC_OK;                  /* Return if no plugins loaded */
121    }
122    if (jcr->is_job_canceled()) {
123       Dmsg0(dbglvl, "Cancel return from generate_plugin_event\n");
124       return bRC_Cancel;
125    }
126
127    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
128    event.eventType = eventType;
129
130    Dmsg2(dbglvl, "sd-plugin_ctx_list=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
131
132    foreach_alist_index(i, plugin, bplugin_list) {
133       plugin_ctx = &plugin_ctx_list[i];
134       if (is_plugin_disabled(plugin_ctx)) {
135          continue;
136       }
137       rc = sdplug_func(plugin)->handlePluginEvent(plugin_ctx, &event, value);
138       if (rc != bRC_OK) {
139          break;
140       }
141    }
142
143    return rc;
144 }
145
146 /*
147  * Print to file the plugin info.
148  */
149 void dump_sd_plugin(Plugin *plugin, FILE *fp)
150 {
151    if (!plugin) {
152       return ;
153    }
154    psdInfo *info = (psdInfo *) plugin->pinfo;
155    fprintf(fp, "\tversion=%d\n", info->version);
156    fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
157    fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
158    fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
159    fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
160    fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
161    fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
162 }
163
164 /**
165  * This entry point is called internally by Bacula to ensure
166  *  that the plugin IO calls come into this code.
167  */
168 void load_sd_plugins(const char *plugin_dir)
169 {
170    Plugin *plugin;
171    int i;
172
173    Dmsg0(dbglvl, "Load sd plugins\n");
174    if (!plugin_dir) {
175       Dmsg0(dbglvl, "No sd plugin dir!\n");
176       return;
177    }
178    bplugin_list = New(alist(10, not_owned_by_alist));
179    if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type, 
180                 is_plugin_compatible)) {
181       /* Either none found, or some error */
182       if (bplugin_list->size() == 0) {
183          delete bplugin_list;
184          bplugin_list = NULL;
185          Dmsg0(dbglvl, "No plugins loaded\n");
186          return;
187       }
188    }
189    /* 
190     * Verify that the plugin is acceptable, and print information
191     *  about it.
192     */
193    foreach_alist_index(i, plugin, bplugin_list) {
194       Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
195       Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
196    }
197
198    Dmsg1(dbglvl, "num plugins=%d\n", bplugin_list->size());
199    dbg_plugin_add_hook(dump_sd_plugin);
200 }
201
202 /**
203  * Check if a plugin is compatible.  Called by the load_plugin function
204  *  to allow us to verify the plugin.
205  */
206 static bool is_plugin_compatible(Plugin *plugin)
207 {
208    psdInfo *info = (psdInfo *)plugin->pinfo;
209    Dmsg0(50, "is_plugin_compatible called\n");
210    if (debug_level >= 50) {
211       dump_sd_plugin(plugin, stdin);
212    }
213    if (strcmp(info->plugin_magic, SD_PLUGIN_MAGIC) != 0) {
214       Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
215            plugin->file, SD_PLUGIN_MAGIC, info->plugin_magic);
216       Dmsg3(50, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
217            plugin->file, SD_PLUGIN_MAGIC, info->plugin_magic);
218
219       return false;
220    }
221    if (info->version != SD_PLUGIN_INTERFACE_VERSION) {
222       Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
223            plugin->file, SD_PLUGIN_INTERFACE_VERSION, info->version);
224       Dmsg3(50, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
225            plugin->file, SD_PLUGIN_INTERFACE_VERSION, info->version);
226       return false;
227    }
228    if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
229        strcmp(info->plugin_license, "AGPLv3") != 0 &&
230        strcmp(info->plugin_license, "Bacula Systems(R) SA") != 0) {
231       Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
232            plugin->file, info->plugin_license);
233       Dmsg2(50, "Plugin license incompatible. Plugin=%s license=%s\n",
234            plugin->file, info->plugin_license);
235       return false;
236    }
237    if (info->size != sizeof(psdInfo)) {
238       Jmsg(NULL, M_ERROR, 0,
239            _("Plugin size incorrect. Plugin=%s wanted=%d got=%d\n"),
240            plugin->file, sizeof(psdInfo), info->size);
241       return false;
242    }
243       
244    return true;
245 }
246
247
248 /*
249  * Create a new instance of each plugin for this Job
250  */
251 void new_plugins(JCR *jcr)
252 {
253    Plugin *plugin;
254    int i;
255
256    Dmsg0(dbglvl, "=== enter new_plugins ===\n");
257    if (!bplugin_list) {
258       Dmsg0(dbglvl, "No sd plugin list!\n");
259       return;
260    }
261    if (jcr->is_job_canceled()) {
262       return;
263    }
264    /* 
265     * If plugins already loaded, just return
266     */
267    if (jcr->plugin_ctx_list) {
268       return;
269    }
270
271    int num = bplugin_list->size();
272
273    Dmsg1(dbglvl, "sd-plugin-list size=%d\n", num);
274    if (num == 0) {
275       return;
276    }
277
278    jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
279
280    bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
281    Dmsg2(dbglvl, "Instantiate sd-plugin_ctx_list=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
282    foreach_alist_index(i, plugin, bplugin_list) {
283       /* Start a new instance of each plugin */
284       bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
285       memset(b_ctx, 0, sizeof(bacula_ctx));
286       b_ctx->jcr = jcr;
287       plugin_ctx_list[i].bContext = (void *)b_ctx;
288       plugin_ctx_list[i].pContext = NULL;
289       if (sdplug_func(plugin)->newPlugin(&plugin_ctx_list[i]) != bRC_OK) {
290          b_ctx->disabled = true;
291       }
292    }
293 }
294
295 /*
296  * Free the plugin instances for this Job
297  */
298 void free_plugins(JCR *jcr)
299 {
300    Plugin *plugin;
301    int i;
302
303    if (!bplugin_list || !jcr->plugin_ctx_list) {
304       return;
305    }
306
307    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
308    Dmsg2(dbglvl, "Free instance sd-plugin_ctx_list=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
309    foreach_alist_index(i, plugin, bplugin_list) {
310       /* Free the plugin instance */
311       sdplug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
312       free(plugin_ctx_list[i].bContext);     /* free Bacula private context */
313    }
314    free(plugin_ctx_list);
315    jcr->plugin_ctx_list = NULL;
316 }
317
318
319 /* ==============================================================
320  *
321  * Callbacks from the plugin
322  *
323  * ==============================================================
324  */
325 static bRC baculaGetValue(bpContext *ctx, bsdrVariable var, void *value)
326 {
327    JCR *jcr;
328    if (!ctx) {
329       return bRC_Error;
330    }
331    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
332    if (!jcr) {
333       return bRC_Error;
334    }
335    if (!value) {
336       return bRC_Error;
337    }
338    switch (var) {
339    case bsdVarJobId:
340       *((int *)value) = jcr->JobId;
341       Dmsg1(dbglvl, "sd-plugin: return bVarJobId=%d\n", jcr->JobId);
342       break;
343    case bsdVarJobName:
344       *((char **)value) = jcr->Job;
345       Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
346       break;
347    default:
348       break;
349    }
350    return bRC_OK;
351 }
352
353 static bRC baculaSetValue(bpContext *ctx, bsdwVariable var, void *value)
354 {
355    JCR *jcr;   
356    if (!value || !ctx) {
357       return bRC_Error;
358    }
359 // Dmsg1(dbglvl, "bacula: baculaGetValue var=%d\n", var);
360    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
361    if (!jcr) {
362       return bRC_Error;
363    }
364 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr); 
365    /* Nothing implemented yet */
366    Dmsg1(dbglvl, "sd-plugin: baculaSetValue var=%d\n", var);
367    return bRC_OK;
368 }
369
370 static bRC baculaRegisterEvents(bpContext *ctx, ...)
371 {
372    va_list args;
373    uint32_t event;
374
375    va_start(args, ctx);
376    while ((event = va_arg(args, uint32_t))) {
377       Dmsg1(dbglvl, "sd-Plugin wants event=%u\n", event);
378    }
379    va_end(args);
380    return bRC_OK;
381 }
382
383 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
384   int type, utime_t mtime, const char *fmt, ...)
385 {
386    va_list arg_ptr;
387    char buf[2000];
388    JCR *jcr;
389
390    if (ctx) {
391       jcr = ((bacula_ctx *)ctx->bContext)->jcr;
392    } else {
393       jcr = NULL;
394    }
395
396    va_start(arg_ptr, fmt);
397    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
398    va_end(arg_ptr);
399    Jmsg(jcr, type, mtime, "%s", buf);
400    return bRC_OK;
401 }
402
403 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
404   int level, const char *fmt, ...)
405 {
406    va_list arg_ptr;
407    char buf[2000];
408
409    va_start(arg_ptr, fmt);
410    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
411    va_end(arg_ptr);
412    d_msg(file, line, level, "%s", buf);
413    return bRC_OK;
414 }
415
416 static char *baculaEditDeviceCodes(DCR *dcr, char *omsg, 
417   const char *imsg, const char *cmd)
418 {
419    return edit_device_codes(dcr, omsg, imsg, cmd);
420 }
421
422 #ifdef TEST_PROGRAM
423
424 int main(int argc, char *argv[])
425 {
426    char plugin_dir[1000];
427    JCR mjcr1, mjcr2;
428    JCR *jcr1 = &mjcr1;
429    JCR *jcr2 = &mjcr2;
430
431    strcpy(my_name, "test-dir");
432     
433    getcwd(plugin_dir, sizeof(plugin_dir)-1);
434    load_sd_plugins(plugin_dir);
435
436    jcr1->JobId = 111;
437    new_plugins(jcr1);
438
439    jcr2->JobId = 222;
440    new_plugins(jcr2);
441
442    generate_plugin_event(jcr1, bsdEventJobStart, (void *)"Start Job 1");
443    generate_plugin_event(jcr1, bsdEventJobEnd);
444    generate_plugin_event(jcr2, bsdEventJobStart, (void *)"Start Job 1");
445    free_plugins(jcr1);
446    generate_plugin_event(jcr2, bsdEventJobEnd);
447    free_plugins(jcr2);
448
449    unload_plugins();
450
451    Dmsg0(dbglvl, "sd-plugin: OK ...\n");
452    close_memory_pool();
453    sm_dump(false);
454    return 0;
455 }
456
457 #endif /* TEST_PROGRAM */