]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/sd_plugins.c
Remove old Bacula Systems notices
[bacula/bacula] / bacula / src / stored / sd_plugins.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2017 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, "No jcr: 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") != 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     */
294    if (jcr->plugin_ctx_list) {
295       return;
296    }
297
298    int num = b_plugin_list->size();
299
300    Dmsg1(dbglvl, "sd-plugin-list size=%d\n", num);
301    if (num == 0) {
302       return;
303    }
304
305    jcr->plugin_ctx_list = (bpContext *)malloc(sizeof(bpContext) * num);
306
307    bpContext *plugin_ctx_list = jcr->plugin_ctx_list;
308    Dmsg2(dbglvl, "Instantiate sd-plugin_ctx_list=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
309    foreach_alist_index(i, plugin, b_plugin_list) {
310       /* Start a new instance of each plugin */
311       bacula_ctx *b_ctx = (bacula_ctx *)malloc(sizeof(bacula_ctx));
312       memset(b_ctx, 0, sizeof(bacula_ctx));
313       b_ctx->jcr = jcr;
314       plugin_ctx_list[i].bContext = (void *)b_ctx;
315       plugin_ctx_list[i].pContext = NULL;
316       if (sdplug_func(plugin)->newPlugin(&plugin_ctx_list[i]) != bRC_OK) {
317          b_ctx->disabled = true;
318       }
319    }
320 }
321
322 /*
323  * Free the plugin instances for this Job
324  */
325 void free_plugins(JCR *jcr)
326 {
327    Plugin *plugin;
328    int i = 0;
329
330    if (!b_plugin_list || !jcr->plugin_ctx_list) {
331       return;
332    }
333
334    bpContext *plugin_ctx_list = (bpContext *)jcr->plugin_ctx_list;
335    Dmsg2(dbglvl, "Free instance sd-plugin_ctx_list=%p JobId=%d\n", jcr->plugin_ctx_list, jcr->JobId);
336    foreach_alist_index(i, plugin, b_plugin_list) {
337       /* Free the plugin instance */
338       sdplug_func(plugin)->freePlugin(&plugin_ctx_list[i]);
339       free(plugin_ctx_list[i].bContext);     /* free Bacula private context */
340    }
341    free(plugin_ctx_list);
342    jcr->plugin_ctx_list = NULL;
343 }
344
345
346 /* ==============================================================
347  *
348  * Callbacks from the plugin
349  *
350  * ==============================================================
351  */
352
353 static bRC baculaGetValue(bpContext *ctx, bsdrVariable var, void *value)
354 {
355    JCR *jcr;
356    if (!ctx) {
357       return bRC_Error;
358    }
359    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
360    if (!jcr) {
361       return bRC_Error;
362    }
363    if (!value) {
364       return bRC_Error;
365    }
366    switch (var) {
367    case bsdVarJobId:
368       *((int *)value) = jcr->JobId;
369       Dmsg1(dbglvl, "sd-plugin: return bVarJobId=%d\n", jcr->JobId);
370       break;
371    case bsdVarJobName:
372       *((char **)value) = jcr->Job;
373       Dmsg1(dbglvl, "Bacula: return Job name=%s\n", jcr->Job);
374       break;
375 #if 0
376    case bsdVarDevTypes:
377       *((s_kw **)value) = dev_types;
378 #endif
379    default:
380       break;
381    }
382    return bRC_OK;
383 }
384
385 static bRC baculaSetValue(bpContext *ctx, bsdwVariable var, void *value)
386 {
387    JCR *jcr;
388    if (!value || !ctx) {
389       return bRC_Error;
390    }
391 // Dmsg1(dbglvl, "bacula: baculaSetValue var=%d\n", var);
392    jcr = ((bacula_ctx *)ctx->bContext)->jcr;
393    if (!jcr) {
394       return bRC_Error;
395    }
396 // Dmsg1(dbglvl, "Bacula: jcr=%p\n", jcr);
397    /* Nothing implemented yet */
398    Dmsg1(dbglvl, "sd-plugin: baculaSetValue var=%d\n", var);
399    return bRC_OK;
400 }
401
402 static bRC baculaRegisterEvents(bpContext *ctx, ...)
403 {
404    va_list args;
405    uint32_t event;
406
407    va_start(args, ctx);
408    while ((event = va_arg(args, uint32_t))) {
409       Dmsg1(dbglvl, "sd-Plugin wants event=%u\n", event);
410    }
411    va_end(args);
412    return bRC_OK;
413 }
414
415 static bRC baculaJobMsg(bpContext *ctx, const char *file, int line,
416   int type, utime_t mtime, const char *fmt, ...)
417 {
418    va_list arg_ptr;
419    char buf[2000];
420    JCR *jcr;
421
422    if (ctx) {
423       jcr = ((bacula_ctx *)ctx->bContext)->jcr;
424    } else {
425       jcr = NULL;
426    }
427
428    va_start(arg_ptr, fmt);
429    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
430    va_end(arg_ptr);
431    Jmsg(jcr, type, mtime, "%s", buf);
432    return bRC_OK;
433 }
434
435 static bRC baculaDebugMsg(bpContext *ctx, const char *file, int line,
436   int level, const char *fmt, ...)
437 {
438    va_list arg_ptr;
439    char buf[2000];
440
441    va_start(arg_ptr, fmt);
442    bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
443    va_end(arg_ptr);
444    d_msg(file, line, level, "%s", buf);
445    return bRC_OK;
446 }
447
448 static char *baculaEditDeviceCodes(DCR *dcr, char *omsg,
449   const char *imsg, const char *cmd)
450 {
451    return edit_device_codes(dcr, omsg, imsg, cmd);
452 }
453
454 #ifdef TEST_PROGRAM
455
456 int main(int argc, char *argv[])
457 {
458    char plugin_dir[1000];
459    JCR mjcr1, mjcr2;
460    JCR *jcr1 = &mjcr1;
461    JCR *jcr2 = &mjcr2;
462
463    strcpy(my_name, "test-dir");
464
465    getcwd(plugin_dir, sizeof(plugin_dir)-1);
466    load_sd_plugins(plugin_dir);
467
468    jcr1->JobId = 111;
469    new_plugins(jcr1);
470
471    jcr2->JobId = 222;
472    new_plugins(jcr2);
473
474    generate_plugin_event(jcr1, bsdEventJobStart, (void *)"Start Job 1");
475    generate_plugin_event(jcr1, bsdEventJobEnd);
476    generate_plugin_event(jcr2, bsdEventJobStart, (void *)"Start Job 1");
477    free_plugins(jcr1);
478    generate_plugin_event(jcr2, bsdEventJobEnd);
479    free_plugins(jcr2);
480
481    unload_plugins();
482
483    Dmsg0(dbglvl, "sd-plugin: Test OK ...\n");
484    close_memory_pool();
485    sm_dump(false);
486    return 0;
487 }
488
489 #endif /* TEST_PROGRAM */