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