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 the bInfo 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 in DIR_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. The pFuncs 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 single bDirAuthData 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 in value->response (as a char*, a nul terminated string) and value->seqdata copied from authentication question. Plugin should return bRC_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 and bRC_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.