Chapter 2. Writing and Compiling Plug-ins

This chapter provides an introduction on how to write and compile Red Hat Directory Server. Chapter 3, Configuring Plug-ins describes how to load the plug-in into the Directory Server configuration after it has been successfully compiled.
If you have already written a plug-in for the Directory Server, see Section 1.4, “Using Directory Server Plug-in APIs” for information on migrating a custom plug-in to the latest version of Directory Server.

2.1. Writing a Plug-in Function

To write a Directory Server plug-in, the plug-in code must:
  • Include the API header file.
  • Set the function parameters using the parameter block.
  • Call the front-end.
  • Specify the function return value.

Important

Any custom plug-in files must be located in the default plug-ins directory, install_directory/ldapserver/ldap/servers/plugins on Red Hat Enterprise Linux 7. SELinux policies set rules on what directories the Directory Server processes are allowed to access, and any required files (such as plug-ins) must be in the default directories or the server cannot load them.
If the plug-in files are not in the default location, then the SELinux policies must be manually updated to include the alternate location.
For additional information on writing specific plug-in types, see the following sections:

2.1.1. Including the API Header File

The interface to the Directory Server plug-in API is located in the slapi-plugin.h header file. Include this header file in all custom plug-ins:
#include "slapi-plugin.h"
Then provide the path to this file with the compiler command line:
gcc ... -I /usr/lib64/dirsrv/plugins myplugin.c
On many platforms, the header file is provided by the 389-ds-base-devel package. This package installs the file in /usr/include/dirsrv, so the relative path can be used in the include statement — #include .dirsrv/slapi-plugin.h — and there is no need to set the path in the -I since the compiler will find it in the default location. This is recommended.

2.1.2. Passing Data with Parameter Blocks

Plug-in functions often make use of the Slapi_PBlock parameter block (pblock) for passing information to and from the Directory Server. All functions use a pblock.
These functions take a single argument of type Slapi_PBlock* to the Directory Server. This argument contains the parameter values needed to complete the function request. A plug-in function typically has a prototype similar to the following:
int myFunction( Slapi_PBlock *pb [optional_arguments );
In this prototype, pb is the parameter block that contains the parameters pertaining to the operation or function.
For example, the parameter block for an add operation will contain the target DN and the entry to be added; the parameter block for a bind operation will contain the DN of the user, the authentication method, and the user's credentials.

2.1.3. Working with Parameter Blocks

In the functions that you write, you set values in the parameter block that pertain to the operation you are performing. You can also get data from the parameter block that you can use within the plug-in functions. This process is described in Section 2.1.3.1, “Getting Data from the Parameter Block”.
You can also set values in the parameter block during the processing of the plug-in functions. The Directory Server can then use the new data values to complete an operation which it might be processing. This process is described in Section 2.1.3.2, “Setting Data in the Parameter Block”.
Some of the data that you can retrieve from the parameter block includes entries, attributes, search filters, and distinguished names (DNs). After you have retrieved a piece of data, you can manipulate it using the front-end API functions. For example, you can use front-end API functions to verify that an entry complies with the schema or you can split up a search filter into its individual components. This process is described in Section 2.1.4, “Calling Front-end Functions”.

2.1.3.1. Getting Data from the Parameter Block

When the Directory Server calls the plug-in function, it passes any relevant data to the function in a parameter block defined as a data type found in Slapi_PBlock. To access the data in a parameter block, call the slapi_pblock_get() function.

Note

slapi_pblock_get() often returns a pointer to the actual data. When you call custom functions to modify the value retrieved by slapi_pblock_get(), you are modifying the actual data in the parameter block, not a copy of the data.
In Example 2.1, “searchdn_preop_search() Function”, the searchdn_preop_search() function gets the DN of the base DN for the LDAP search operation. It then normalizes the DN, converts all characters to lowercase, and writes the converted DN to the error log. The function makes a copy of the DN so that the actual data in the pblock are not changed.

Note

The data in the pblock can be modified, but this is not common, and be careful doing that.
Since there is a a copy of the data (using slapi_ch_strdup()), free the memory at the end (using slapi_ch_free_string()).

Example 2.1. searchdn_preop_search() Function

#include "slapi-plugin.h"
...
int searchdn_preop_search( Slapi_PBlock *pb )
{
	Slapi_DN *dn;
	char *mydn;
	/* Indicate the point when the plug-in starts executing */
	slapi_log_error( SLAPI_LOG_PLUGIN, "searchdn_preop_search" ,"*** PREOPERATION SEARCH PLUGIN ***\n" );
	
	/* Get the base DN of the search from the parameter block. */
	slapi_pblock_get( pb, SLAPI_SEARCH_TARGET, &dn);

        /* make a copy of the DN */
        mydn = slapi_ch_strdup(dn);
	
	/* Normalize the copy of the DN and convert it to lowercase */
	slapi_dn_normalize_case( mydn );
	
	/* Log the normalized DN */
	slapi_log_error( SLAPI_LOG_PLUGIN, "searchdn_preop_search" ,"Normalized DN: %s\n" , mydn );

        /* more processing . . . */

        /* free the copy */
        slapi_ch_free_string(&mydn);
	
	return( 0 );
}
SLAPI_SEARCH_TARGET identifies the parameter in the parameter block that contains the base DN of the search. The plug-in should arrange to release the resources for any data allocated. In this case, the plug-in should also provide a stop callback which would get the db from the pblock and free the resources associated with it.
For a complete listing of the parameter block IDs, see Part V, “Parameter Block Reference”.

2.1.3.2. Setting Data in the Parameter Block

To modify the value of a parameter in the parameter block, call the slapi_pblock_set() function. For example, you can call slapi_pblock_set() to change the value of the SLAPI_PLUGIN_PRIVATE parameter, which stores private data for the plug-in.
In the following example, the ldif_back_init() function sets the value of the SLAPI_PLUGIN_PRIVATE parameter to the context of the database.
#include "slapi-plugin.h";
...
int
ldif_back_init( Slapi_PBlock *pb )
{
	LDIF *db; /* context of the database */
	...
	/* Allocate space for the database context, which contains information about the 
	database and a pointer to the list of entries. */
	
	if ( slapi_pblock_set( pb, SLAPI_PLUGIN_PRIVATE, (void *) db ) == -1 )
	{
		slapi_log_error( SLAPI_LOG_PLUGIN, "ldif_back_init" ,
		"Unable to store database information\n" );
	}
	...
}
This example uses the slapi_log_error() function to notify the user if an error occurred.
In this code example, SLAPI_PLUGIN_PRIVATE identifies the parameter in the parameter block that contains private data for use in the database functions. For a complete listing of the parameter block IDs, see Part V, “Parameter Block Reference”.

2.1.4. Calling Front-end Functions

The types of data that you can get from a parameter block include entries, attributes, distinguished names, and search filters. If you want to manipulate these data items, you can call the associated front-end API functions provided with the Directory Server. For example, using the front-end API functions, you can:
  • Write messages to the error log.
  • Get the attributes of an entry.
  • Get or set the DN of an entry.
  • Add or delete the values of an attribute.
  • Determine the OID of an attribute.
  • Determine the type of a search filter.
For more information on the front-end API, see Chapter 5, Frontend API Functions and Part IV, “Function Reference”.

2.1.5. Plug-in Return Values

If the plug-in function is successful, it should return 0 to the front-end. If it is not successful, it should return a non-zero value, and you should call the front-end API function slapi_log_error() to log an error message to describe the problem. Refer to Section 5.1, “Logging Messages” for more information.
In some cases, you may need to send an LDAP result back to the client. For example, if you are writing a pre-operation bind function and an error occurs during the processing of the function, the function should return a non-zero value, log an error message, and send the appropriate LDAP result code back to the client. For information on the appropriate result code to return to the client, see the chapter that documents the type of plug-in you are writing.