]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/sd_plugins.c
Fix truncate race bug #1382
[bacula/bacula] / bacula / src / stored / sd_plugins.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 Kern Sibbald
5    Copyright (C) 2007-2014 Free Software Foundation Europe e.V.
6
7    The original author of Bacula is Kern Sibbald, with contributions
8    from many others, a complete list can be found in the file AUTHORS.
9
10    You may use this file and others of this release according to the
11    license defined in the LICENSE file, which includes the Affero General
12    Public License, v3.0 ("AGPLv3") and some additional permissions and
13    terms pursuant to its AGPLv3 Section 7.
14
15    This notice must be preserved when any source code is 
16    conveyed and/or propagated.
17
18    Bacula(R) is a registered trademark of Kern Sibbald.
19 */
20 /*
21  * Main program to test loading and running Bacula plugins.
22  *   Destined to become Bacula pluginloader, ...
23  *
24  * Kern Sibbald, October 2007
25  */
26 #include "bacula.h"
27 #include "stored.h"
28 #include "sd_plugins.h"
29
30 const int dbglvl = 250;
31 const char *plugin_type = "-sd.so";
32
33
34 /* Forward referenced functions */
35 static bRC baculaGetValue(bpContext *ctx, bsdrVariable var, void *value);
36 static bRC baculaSetValue(bpContext *ctx, bsdwVariable var, void *value);
37 static bRC baculaRegisterEvents(bpContext *ctx, ...);
38 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
39   int type, utime_t mtime, const char *fmt, ...);
40 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
41   int level, const char *fmt, ...);
42 static char *baculaEditDeviceCodes(DCR *dcr, char *omsg,
43   const char *imsg, const char *cmd);
44 static bool is_plugin_compatible(Plugin *plugin);
45
46
47 /* Bacula info */
48 static bsdInfo binfo = {
49    sizeof(bsdFuncs),
50    SD_PLUGIN_INTERFACE_VERSION
51 };
52
53 /* Bacula entry points */
54 static bsdFuncs bfuncs = {
55    sizeof(bsdFuncs),
56    SD_PLUGIN_INTERFACE_VERSION,
57    baculaRegisterEvents,
58    baculaGetValue,
59    baculaSetValue,
60    baculaJobMsg,
61    baculaDebugMsg,
62    baculaEditDeviceCodes,
63 };
64
65 /*
66  * Bacula private context
67  */
68 struct bacula_ctx {
69    JCR *jcr;                             /* jcr for plugin */
70    bRC  rc;                              /* last return code */
71    bool disabled;                        /* set if plugin disabled */
72 };
73
74 static bool is_plugin_disabled(bpContext *plugin_ctx)
75 {
76    bacula_ctx *b_ctx;
77    if (!plugin_ctx) {
78       return true;
79    }
80    b_ctx = (bacula_ctx *)plugin_ctx->bContext;
81    return b_ctx->disabled;
82 }
83
84 #ifdef needed
85 static bool is_plugin_disabled(JCR *jcr)
86 {
87    return is_plugin_disabled(jcr->plugin_ctx);
88 }
89 #endif
90
91 /*
92  * Create a plugin event
93  */
94 int generate_plugin_event(JCR *jcr, bsdEventType eventType, void *value)
95 {
96    bpContext *plugin_ctx;
97    bsdEvent event;
98    Plugin *plugin;
99    int i = 0;
100    bRC rc = bRC_OK;
101
102    if (!b_plugin_list) {
103       Dmsg0(dbglvl, "No b_plugin_list: generate_plugin_event ignored.\n");
104       return bRC_OK;
105    }
106    if (!jcr) {
107       Dmsg0(dbglvl, "No jcr: generate_plugin_event ignored.\n");
108       return bRC_OK;
109    }
110    if (!jcr->plugin_ctx_list) {
111        Dmsg0(dbglvl, "No plugin_ctx_list: generate_plugin_event ignored.\n");
112        return bRC_OK;                  /* Return if no plugins loaded */
113    }
114
115    /* Always handle JobEnd and DeviceClose requests */
116    switch (eventType) {
117    case bsdEventJobEnd:
118    case bsdEventDeviceClose:
119       break;               /* pass these through even if canceled */
120    default:
121       if (jcr->is_job_canceled()) {
122          Dmsg0(dbglvl, "Cancel return from generate_plugin_event\n");
123          return bRC_Cancel;
124       }
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, b_plugin_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    return rc;
143 }
144
145
146 /*
147  * Create a global plugin event -- i.e. no context
148  */
149 int generate_global_plugin_event(bsdGlobalEventType eventType, void *value)
150 {
151    bsdEvent event;
152    Plugin *plugin;
153    int i;
154    bRC rc = bRC_OK;
155
156    if (!b_plugin_list) {
157       Dmsg0(dbglvl, "No b_plugin_list: generate_global_plugin_event ignored.\n");
158       return bRC_OK;
159    }
160    event.eventType = eventType;
161
162    foreach_alist_index(i, plugin, b_plugin_list) {
163       if (sdplug_func(plugin)->handleGlobalPluginEvent != NULL) {
164          rc = sdplug_func(plugin)->handleGlobalPluginEvent(&event, value);
165          if (rc != bRC_OK) {
166             break;
167          }
168       }
169    }
170    return rc;
171 }
172
173
174 /*
175  * Print to file the plugin info.
176  */
177 void dump_sd_plugin(Plugin *plugin, FILE *fp)
178 {
179    if (!plugin) {
180       return ;
181    }
182    psdInfo *info = (psdInfo *) plugin->pinfo;
183    fprintf(fp, "\tversion=%d\n", info->version);
184    fprintf(fp, "\tdate=%s\n", NPRTB(info->plugin_date));
185    fprintf(fp, "\tmagic=%s\n", NPRTB(info->plugin_magic));
186    fprintf(fp, "\tauthor=%s\n", NPRTB(info->plugin_author));
187    fprintf(fp, "\tlicence=%s\n", NPRTB(info->plugin_license));
188    fprintf(fp, "\tversion=%s\n", NPRTB(info->plugin_version));
189    fprintf(fp, "\tdescription=%s\n", NPRTB(info->plugin_description));
190 }
191
192 /**
193  * This entry point is called internally by Bacula to ensure
194  *  that the plugin IO calls come into this code.
195  */
196 void load_sd_plugins(const char *plugin_dir)
197 {
198    Plugin *plugin;
199    int i;
200
201    Dmsg0(dbglvl, "Load sd plugins\n");
202    if (!plugin_dir) {
203       Dmsg0(dbglvl, "No sd plugin dir!\n");
204       return;
205    }
206    b_plugin_list = New(alist(10, not_owned_by_alist));
207    if (!load_plugins((void *)&binfo, (void *)&bfuncs, plugin_dir, plugin_type,
208                 is_plugin_compatible)) {
209       /* Either none found, or some error */
210       if (b_plugin_list->size() == 0) {
211          delete b_plugin_list;
212          b_plugin_list = NULL;
213          Dmsg0(dbglvl, "No plugins loaded\n");
214          return;
215       }
216    }
217    /*
218     * Verify that the plugin is acceptable, and print information
219     *  about it.
220     */
221    foreach_alist_index(i, plugin, b_plugin_list) {
222       Jmsg(NULL, M_INFO, 0, _("Loaded plugin: %s\n"), plugin->file);
223       Dmsg1(dbglvl, "Loaded plugin: %s\n", plugin->file);
224    }
225
226    Dmsg1(dbglvl, "num plugins=%d\n", b_plugin_list->size());
227    dbg_plugin_add_hook(dump_sd_plugin);
228 }
229
230 /**
231  * Check if a plugin is compatible.  Called by the load_plugin function
232  *  to allow us to verify the plugin.
233  */
234 static bool is_plugin_compatible(Plugin *plugin)
235 {
236    psdInfo *info = (psdInfo *)plugin->pinfo;
237    Dmsg0(50, "is_plugin_compatible called\n");
238    if (chk_dbglvl(50)) {
239       dump_sd_plugin(plugin, stdin);
240    }
241    if (strcmp(info->plugin_magic, SD_PLUGIN_MAGIC) != 0) {
242       Jmsg(NULL, M_ERROR, 0, _("Plugin magic wrong. Plugin=%s wanted=%s got=%s\n"),
243            plugin->file, SD_PLUGIN_MAGIC, info->plugin_magic);
244       Dmsg3(000, "Plugin magic wrong. Plugin=%s wanted=%s got=%s\n",
245            plugin->file, SD_PLUGIN_MAGIC, info->plugin_magic);
246
247       return false;
248    }
249    if (info->version != SD_PLUGIN_INTERFACE_VERSION) {
250       Jmsg(NULL, M_ERROR, 0, _("Plugin version incorrect. Plugin=%s wanted=%d got=%d\n"),
251            plugin->file, SD_PLUGIN_INTERFACE_VERSION, info->version);
252       Dmsg3(000, "Plugin version incorrect. Plugin=%s wanted=%d got=%d\n",
253            plugin->file, SD_PLUGIN_INTERFACE_VERSION, info->version);
254       return false;
255    }
256    if (strcmp(info->plugin_license, "Bacula AGPLv3") != 0 &&
257        strcmp(info->plugin_license, "AGPLv3") != 0 &&
258        strcmp(info->plugin_license, "Bacula Systems(R) SA") != 0) {
259       Jmsg(NULL, M_ERROR, 0, _("Plugin license incompatible. Plugin=%s license=%s\n"),
260            plugin->file, info->plugin_license);
261       Dmsg2(000, "Plugin license incompatible. Plugin=%s license=%s\n",
262            plugin->file, info->plugin_license);
263       return false;
264    }
265    if (info->size != sizeof(psdInfo)) {
266       Jmsg(NULL, M_ERROR, 0,
267            _("Plugin size incorrect. Plugin=%s wanted=%d got=%d\n"),
268            plugin->file, sizeof(psdInfo), info->size);
269       return false;
270    }
271
272    return true;
273 }
274
275
276 /*
277  * Create a new instance of each plugin for this Job
278  */
279 void new_plugins(JCR *jcr)
280 {
281    Plugin *plugin;
282    int i = 0;;
283
284    Dmsg0(dbglvl, "=== enter new_plugins ===\n");
285    if (!b_plugin_list) {
286       Dmsg0(dbglvl, "No sd plugin list!\n");
287       return;
288    }
289    if (jcr->is_job_canceled()) {
290       return;
291    }
292    /*
293     * If plugins already loaded, just return
294     */
295    if (jcr->plugin_ctx_list) {
296       return;
297    }
298
299    int num = b_plugin_list->size();
300
301    Dmsg1(dbglvl, "sd-plugin-list size=%d\n", num);
302    if (num == 0) {
303       return;
304    }
305
306    jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
307
308    bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
309    Dmsg2(dbglvl, "Instantiate sd-plugin_ctx_list=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
310    foreach_alist_index(i, plugin, b_plugin_list) {
311       /* Start a new instance of each plugin */
312       bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
313       memset(b_ctx, 0, sizeof(bacula_ctx));
314       b_ctx->jcr = jcr;
315       plugin_ctx_list[i].bContext = (void *)b_ctx;
316       plugin_ctx_list[i].pContext = NULL;
317       if (sdplug_func(plugin)->newPlugin(&plugin_ctx_list[i]) != bRC_OK) {
318          b_ctx->disabled = true;
319       }
320    }
321 }
322
323 /*
324  * Free the plugin instances for this Job
325  */
326 void free_plugins(JCR *jcr)
327 {
328    Plugin *plugin;
329    int i = 0;
330
331    if (!b_plugin_list || !jcr->plugin_ctx_list) {
332       return;
333    }
334
335    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
336    Dmsg2(dbglvl, "Free instance sd-plugin_ctx_list=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
337    foreach_alist_index(i, plugin, b_plugin_list) {
338       /* Free the plugin instance */
339       sdplug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
340       free(plugin_ctx_list[i].bContext);     /* free Bacula private context */
341    }
342    free(plugin_ctx_list);
343    jcr->plugin_ctx_list = NULL;
344 }
345
346
347 /* ==============================================================
348  *
349  * Callbacks from the plugin
350  *
351  * ==============================================================
352  */
353
354
355 static bRC baculaGetValue(bpContext *ctx, bsdrVariable var, void *value)
356 {
357    JCR *jcr;
358    if (!ctx) {
359       return bRC_Error;
360    }
361    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
362    if (!jcr) {
363       return bRC_Error;
364    }
365    if (!value) {
366       return bRC_Error;
367    }
368    switch (var) {
369    case bsdVarJobId:
370       *((int *)value) = jcr->JobId;
371       Dmsg1(dbglvl, "sd-plugin: return bVarJobId=%d\n", jcr->JobId);
372       break;
373    case bsdVarJobName:
374       *((char **)value) = jcr->Job;
375       Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
376       break;
377    default:
378       break;
379    }
380    return bRC_OK;
381 }
382
383 static bRC baculaSetValue(bpContext *ctx, bsdwVariable var, void *value)
384 {
385    JCR *jcr;
386    if (!value || !ctx) {
387       return bRC_Error;
388    }
389 // Dmsg1(dbglvl, "bacula: baculaSetValue var=%d\n", var);
390    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
391    if (!jcr) {
392       return bRC_Error;
393    }
394 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
395    /* Nothing implemented yet */
396    Dmsg1(dbglvl, "sd-plugin: baculaSetValue var=%d\n", var);
397    return bRC_OK;
398 }
399
400 static bRC baculaRegisterEvents(bpContext *ctx, ...)
401 {
402    va_list args;
403    uint32_t event;
404
405    va_start(args, ctx);
406    while ((event = va_arg(args, uint32_t))) {
407       Dmsg1(dbglvl, "sd-Plugin wants event=%u\n", event);
408    }
409    va_end(args);
410    return bRC_OK;
411 }
412
413 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
414   int type, utime_t mtime, const char *fmt, ...)
415 {
416    va_list arg_ptr;
417    char buf[2000];
418    JCR *jcr;
419
420    if (ctx) {
421       jcr = ((bacula_ctx *)ctx->bContext)->jcr;
422    } else {
423       jcr = NULL;
424    }
425
426    va_start(arg_ptr, fmt);
427    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
428    va_end(arg_ptr);
429    Jmsg(jcr, type, mtime, "%s", buf);
430    return bRC_OK;
431 }
432
433 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
434   int level, const char *fmt, ...)
435 {
436    va_list arg_ptr;
437    char buf[2000];
438
439    va_start(arg_ptr, fmt);
440    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
441    va_end(arg_ptr);
442    d_msg(file, line, level, "%s", buf);
443    return bRC_OK;
444 }
445
446 static char *baculaEditDeviceCodes(DCR *dcr, char *omsg,
447   const char *imsg, const char *cmd)
448 {
449    return edit_device_codes(dcr, omsg, imsg, cmd);
450 }
451
452 #ifdef TEST_PROGRAM
453
454 int main(int argc, char *argv[])
455 {
456    char plugin_dir[1000];
457    JCR mjcr1, mjcr2;
458    JCR *jcr1 = &mjcr1;
459    JCR *jcr2 = &mjcr2;
460
461    strcpy(my_name, "test-dir");
462
463    getcwd(plugin_dir, sizeof(plugin_dir)-1);
464    load_sd_plugins(plugin_dir);
465
466    jcr1->JobId = 111;
467    new_plugins(jcr1);
468
469    jcr2->JobId = 222;
470    new_plugins(jcr2);
471
472    generate_plugin_event(jcr1, bsdEventJobStart, (void *)"Start Job 1");
473    generate_plugin_event(jcr1, bsdEventJobEnd);
474    generate_plugin_event(jcr2, bsdEventJobStart, (void *)"Start Job 1");
475    free_plugins(jcr1);
476    generate_plugin_event(jcr2, bsdEventJobEnd);
477    free_plugins(jcr2);
478
479    unload_plugins();
480
481    Dmsg0(dbglvl, "sd-plugin: Test OK ...\n");
482    close_memory_pool();
483    sm_dump(false);
484    return 0;
485 }
486
487 #endif /* TEST_PROGRAM */