Bacula DIR Plugin API
To write a Bacula plugin, you create a dynamic shared object program (or
dll on Win32) with a particular name and two exported entry points,
place it in the Plugins Directory, which is defined in the
bacula-dir.conf
file in the Director resource, and when the
Director starts, it will load all the plugins that end with -dir.so
(or -dir.dll
on Win32) found in that directory.
Loading Plugins
Once the Director loads the plugins, it asks the dynamic loader for the
two entry points (loadPlugin
and unloadPlugin
) then calls the
loadPlugin
entry point (see below). Bacula passes information to the
plugin through this call and it gets back information that it needs to
use the plugin. Later, Bacula will call particular functions that are
defined by the loadPlugin interface. When Bacula is finished with the
plugin (when Bacula is going to exit), it will call the unloadPlugin
entry point.
The two entry points are:
bRC loadPlugin(bDirInfo *lbinfo, bDirFuncs *lbfuncs, pDirInfo **pinfo, pDirFuncs **pfuncs)
and
bRC unloadPlugin()
both these external entry points to the shared object are defined as C entry points to avoid name mangling complications with C++. However, the shared object can actually be written in any language (preferably C or C++) providing that it follows C language calling conventions.
The definitions for bRC
and the arguments are
src/dird/dir_plugins.h
and so this header file needs to be included
in your plugin. It along with src/lib/plugins.h
define basically the
whole plugin interface. Within this header file, it includes the
following files:
#include <sys/types.h>
#include "config.h"
#include "bc_types.h"
#include "lib/plugins.h"
loadPlugin
As previously mentioned, the loadPlugin
entry point in the plugin is
called immediately after Bacula loads the plugin when the Director
itself is first starting. This entry point is only called once during
the execution of the Director. In calling the plugin, the first two
arguments are information from Bacula that is passed to the plugin, and
the last two arguments are information about the plugin that the plugin
must return to Bacula. The call is:
bRC loadPlugin(bDirInfo *lbinfo, bDirFuncs *lbfuncs, pDirInfo **pinfo, pDirFuncs **pfuncs)
and the arguments are:
- lbinfo
This is information about Bacula in general. Currently, the only value defined in the
bInfo
structure is the version, which is the Bacula plugin interface version, currently defined as 1. The size is set to the byte size of the structure. The exact definition of thebInfo
structure as of this writing is:typedef struct s_dirbaculaInfo { uint32_t size; uint32_t version; } bDirInfo;
- lbfuncs
The
bFuncs
structure defines the callback entry points within Bacula that the plugin can use register events, get Bacula values, set Bacula values, and send messages to the Job output or debug output. The exact definition as of this writing is:typedef struct s_dirbaculaFuncs { uint32_t size; uint32_t version; bRC (*registerBaculaEvents)(bpContext *ctx, ...); bRC (*getBaculaValue)(bpContext *ctx, brDirVariable var, void *value); bRC (*setBaculaValue)(bpContext *ctx, bwDirVariable var, void *value); bRC (*JobMessage)(bpContext *ctx, const char *file, int line, int type, utime_t mtime, const char *fmt, ...); bRC (*DebugMessage)(bpContext *ctx, const char *file, int line, int level, const char *fmt, ...); } bDirFuncs;
We will discuss these entry points and how to use them a bit later when describing the plugin code.
- pInfo
When the
loadPlugin
entry point is called, the plugin must initialize an information structure about the plugin and return a pointer to this structure to Bacula. The exact definition as of this writing is:typedef struct s_dirpluginInfo { uint32_t size; uint32_t version; const char *plugin_magic; const char *plugin_license; const char *plugin_author; const char *plugin_date; const char *plugin_version; const char *plugin_description; } pDirInfo;
Where:
- version
is the current Bacula defined plugin interface version, currently set to 1. If the interface version differs from the current version of Bacula, the plugin will not be run (not yet implemented).
- plugin_magic
is a pointer to the text string
DirPluginData
defined inDIR_PLUGIN_MAGIC
, a sort of sanity check. If this value is not specified, the plugin will not be run (not yet implemented).- plugin_license
is a pointer to a text string that describes the plugin license. Bacula will only accept compatible licenses. The accepted licenses as of this writing are: “Bacula AGPLv3”, “AGPLv3”, “Bacula Systems(R) SA” and defined in
BPLUGIN_LICENSE
compile time definition.- plugin_author
is a pointer to the text name of the author of the plugin. This string can be anything but is generally the author’s name.
- plugin_date
is the pointer to a text string containing the date of the plugin. This string can be anything but is generally some human readable form of the date.
- plugin_version
is a pointer to a text string containing the version of the plugin. The contents are determined by the plugin writer.
- plugin_description
is a pointer to a text string describing what the plugin does. The contents are determined by the plugin writer.
The pInfo structure must be defined in static memory because Bacula does not copy it and may refer to the values at any time while the plugin is loaded. All values must be supplied or the plugin will not run (not yet implemented). All text strings must be either ASCII or UTF-8 strings that are terminated with a zero (
nul
) byte.- pFuncs
When the
loadPlugin
entry point is called, the plugin must initialize an entry point structure about the plugin and return a pointer to this structure to Bacula. This structure contains pointer to each of the entry points that the plugin must (or will) provide for Bacula. When Bacula is actually running the plugin, it will call the defined entry points at particular times. ThepFuncs
structure must be defined in static memory because Bacula does not copy it and may refer to the values at any time while the plugin is loaded.The exact definition as of this writing is:
typedef struct s_dirpluginFuncs { uint32_t size; uint32_t version; bRC (*newPlugin)(bpContext *ctx); bRC (*freePlugin)(bpContext *ctx); bRC (*getPluginValue)(bpContext *ctx, pDirVariable var, void *value); bRC (*setPluginValue)(bpContext *ctx, pDirVariable var, void *value); bRC (*handlePluginEvent)(bpContext *ctx, bDirEvent *event, void *value); bRC (*getPluginAuthenticationData)(bpContext *ctx, const char *param, void **data); bRC (*getPluginAuthorizationData)(bpContext *ctx, const char *param, void **data); } pDirFuncs;
The details of the entry points will be presented in separate sections below. Where:
- size
is the byte size of the structure.
- version
is the plugin interface version currently set to 1 (defined in
DIR_PLUGIN_INTERFACE_VERSION
).
unloadPlugin
As previously mentioned, the unloadPlugin
entry point in the plugin
is called just before a Director finish execution. This entry point is
responsible for releasing any resources allocated by plugin during
loadPlugin
.
Plugin Entry Points
This section will describe each of the entry points (subroutines) within
the plugin that the plugin must provide for Bacula, when they are called
and their arguments. As noted above, pointers to these subroutines are
passed back to Bacula in the pFuncs
structure when Bacula calls the
loadPlugin()
externally defined entry point.
newPlugin
This is the entry point that Bacula will call when a new instance
of
the plugin is created. This typically happens at the beginning of any
Job or Console connection. If 10 Jobs are running simultaneously, there
will be at least 10 instances of the plugin.
The bpContext
structure will be passed to the plugin, and during
this call, if the plugin needs to have its private working storage that
is associated with the particular instance of the plugin, it should
create it from the heap (malloc the memory) and store a pointer to its
private working storage in the pContext variable.
Note: since Bacula is a multi-threaded program, you must not keep
any variable data in your plugin unless it is truly meant to apply
globally to the whole plugin. In addition, you must be aware that except
the first and last call to the plugin (loadPlugin
and
unloadPlugin
) all the other calls will be made by threads that
correspond to a Bacula job. The bpContext
that will be passed for
each thread will remain the same throughout the Job thus you can keep
your private Job specific data in it (bContext
).
typedef struct s_bpContext {
void *pContext; /* Plugin private context */
void *bContext; /* Bacula private context */
} bpContext;
This context pointer will be passed as the first argument to all the entry points that Bacula calls within the plugin. Needless to say, the plugin should not change the bContext variable, which is Bacula’s private context pointer for this instance (Job) of this plugin.
freePlugin
This entry point is called when the this instance of the plugin is no
longer needed (the Job/Console is ending), and the plugin should release
all memory it may have allocated for this particular instance i.e. the
pContext
. This is not the final termination of the plugin signaled
by a call to unloadPlugin
. Any other instances (Job) will continue
to run, and the entry point newPlugin may be called again if other jobs
start.
getPluginValue
Bacula will call this entry point to get a value from the plugin. This entry point is currently not called.
setPluginValue
Bacula will call this entry point to set a value in the plugin. This entry point is currently not called.
handlePluginEvent
This entry point is called when Bacula encounters certain events. This
is, in fact, the main way that most plugins get control when a
Job/Console runs and how they know what is happening in the job. It can
be likened to the RunScript feature that calls external programs and
scripts. When the plugin is called, Bacula passes it the pointer to an
event structure (bDirEvent
), which currently has one item, the
eventType
:
typedef struct s_bDirEvent {
uint32_t eventType;
} bDirEvent;
which defines what event has been triggered, and for each event, Bacula will pass a pointer to a value associated with that event. If no value is associated with a particular event, Bacula will pass a NULL pointer, so the plugin must be careful to always check value pointer prior to dereferencing it.
The current list of events are:
typedef enum {
bDirEventJobStart = 1,
bDirEventJobEnd = 2,
bDirEventJobInit = 3,
bDirEventJobRun = 4,
bDirEventVolumePurged = 5,
bDirEventNewVolume = 6,
bDirEventNeedVolume = 7,
bDirEventVolumeFull = 8,
bDirEventRecyle = 9,
bDirEventGetScratch = 10,
bDirEventAuthenticationQuestion = 1000, // *value is a bDirAuthValue struct allocated by Dir
// to get return value from
bDirEventAuthenticationResponse = 1001, // *value is a char* to user response
bDirEventAuthenticate = 1002, // return bRC_OK when authenticate is successful
} bDirEventsType;
Most of the above are self-explanatory.
- bEventJobStart
is called whenever a Job starts.
- bEventJobEnd
is called whenever a Job ends. No value is passed.
- bDirEventJobInit
is called when … .
- bDirEventJobRun
is called when … .
- bDirEventVolumePurged
is called when … .
- bDirEventAuthenticationQuestion
is called for authentication plugin (see description below - BPAM) whenever Director wants to get the next question in user (bconsole) authentication session. A value is a pointer to
bDirAuthValue
. Plugin should return a singlebDirAuthData
structure (described below) in this value.- bDirEventAuthenticationResponse
is called for authentication plugin to forward user response for the last handled authentication question. A value is a pointer to
bDirAuthValue
. Plugin will get a response invalue->response
(as achar*
, anul
terminated string) andvalue->seqdata
copied from authentication question. Plugin should returnbRC_OK
if Director can proceed to the next step.- bDirEventAuthenticate
is called for authentication plugin whenever Director asks a plugin to final authenticate result where
bRC_OK
when successful andbRC_Error
when error.
During each of the above calls, the plugin receives either no specific value or only one value, which in some cases may not be sufficient. However, knowing the context of the event, the plugin can call back to the Bacula entry points it was passed during the loadPlugin call and get to a number of Bacula variables. (at the current time few Bacula variables are implemented, but it easily extended at a future time and as needs require).
Possible Next Steps
Go to Bacula Pluggable Authentication Modules API Framework BPAM.
Go back to Bacula FD Plugin API.
Go back to Developer Guide.