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