|Plug-in Programmerís Guide
Red Hat Directory Server
Writing and Compiling Plug-ins
This chapter provides an introduction on how to write and compile Red Hat Directory Server (Directory Server) plug-ins. Chapter 3, "Configuring Plug-ins," describes how to load your plug-in into the Directory Server configuration once it is successfully compiled. This chapter covers the following topics:
- Writing a Plug-in Function (page 37)
- Writing Plug-in Initialization Functions (page 42)
- Compiling a Directory Server Plug-in (page 45)
If you have already written a plug-in for the Directory Server, refer to the section "Using Directory Server Plug-in APIs," on page 21, for information on migrating your plug-in to the latest version of the Directory Server.
Writing a Plug-in Function
To write a Directory Server plug-in, you must do the following in your plug-in code:
- Include the API header file.
- Set the function parameters using the parameter block.
- Call the front-end.
- Specify the function return value.
For additional information on writing specific plug-in types, refer to the following chapters:
- Chapter 6, "Writing Pre/Post-Operation Plug-ins"
- Chapter 9, "Writing Entry Store/Fetch Plug-ins"
- Chapter 10, "Writing Extended Operation Plug-ins"
Including the API Header File
The interface to the Directory Server plug-in API is located in the slapi-plugin.h header file. You must include this header file in the plug-ins you write. The following line of code shows an example of including this header file:
When you install the Directory Server, slapi-plugin.h gets installed into the following directory:server_root /plugins/slapd/slapi/include
Passing Data with Parameter Blocks
Often, plug-in functions make use of a parameter block, Slapi_PBlock, for passing information to and from the Directory Server. The following plug-in function types pass a parameter block as a function argument:
- Pre-operation plug-in functions.
- Post-operation plug-in functions.
- Matching rule functions for indexing.
- Factory functions for matching rule index functions.
- Factory functions for matching rule filter functions.
When invoking these types of plug-in functions, you pass to the Directory Server a single argument of type Slapi_PBlock. This argument contains the parameter values needed to complete the function request. Your plug-in function should have a prototype similar to the following:int myFunction( Slapi_PBlock pb );
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.
Working with Parameter Blocks
In the functions 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 which you can use within your functions. This process is described in the next section, "Getting Data from the Parameter Block," on page 39.
You can also set values in the parameter block during the processing of your plug-in functions. The Directory Server can then use the new data values to complete an operation which it might be processing. For details on this, see "Setting Data in the Parameter Block," on page 40.
Some of the data that you can retrieve from the parameter block include entries, attributes, search filters, and distinguished names (DNs). Once you retrieve 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. For details, see "Calling Front-End Functions," on page 41.
Getting Data from the Parameter Block
When the Directory Server calls your plug-in function, it passes any relevant data to your function in a parameter block. The parameter block is defined as a Slapi_PBlock data type. To access the data in a parameter block, call the slapi_pblock_get() function.
Often, slapi_pblock_get() returns a pointer to the actual data. When you call your own 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 the following example, 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 actual DN (not a copy of it) is normalized and converted to lowercase.
In this code example, SLAPI_SEARCH_TARGET identifies the parameter in the parameter block that contains the base DN of the search. For a complete listing of the parameter block IDs, see Chapter 16, "Parameter Block Reference."
Setting Data in the Parameter Block
To modify the value of a parameter in the parameter block, call the function slapi_pblock_set(). For example, you call can slapi_pblock_set() to change the value of the SLAPI_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_PRIVATE parameter to the context of the database.
This example uses the function slapi_new_condvar() to notify the user if an error occurred.
In this code example, SLAPI_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 Chapter 16, "Parameter Block Reference."
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 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, "Front-End API Functions," and Chapter 15, "Function Reference."
Plug-in Return Values
If your 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_new_condvar() to log an error message to describe the problem ("Logging Messages," on page 65, details this process).
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, refer to the chapter that documents the type of plug-in you are writing.
Writing Plug-in Initialization Functions
Before the Directory Server can call your plug-in function, the function must be properly initialized. To do this, you must write an initialization function for your server plug-in. The initialization function should do the following:
- Specify the plug-in compatibility version.
- Register each of your plug-in functions.
- Return a value to the Directory Server.
Your initialization function should have a prototype similar to the following:int my_init_function( Slapi_PBlock pb );
In the initialization function, the Directory Server passes a single argument of type Slapi_PBlock.
Specifying Directory Server Compatibility
You need to specify the compatibility version of your plug-in so that the Directory Server can determine whether it supports the plug-in.
To specify the plug-in compatibility version, call the slapi_pblock_set() function, and set the SLAPI_PLUGIN_VERSION parameter to the version number associated with the plug-in. For example:slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, \ SLAPI_PLUGIN_VERSION_03);
- Plug-in version 1 (SLAPI_PLUGIN_VERSION_01) is compatible with versions 3.x and 4.x of the Directory Server.
- Plug-in version 2 (SLAPI_PLUGIN_VERSION_02) is compatible with version 4.x of the Directory Server.
- Plug-in version 3 (SLAPI_PLUGIN_VERSION_03) is compatible with versions 6.x and later of the Directory Server.
Specifying Information about the Plug-in
You specify information about your plug-in, such as a description of the plug-in, with the Slapi_PluginDesc structure. For details on this data structure, see "Slapi_PluginDesc," on page 215.
It is advised that you include a plug-in description since the Red Hat Console displays this information as part of the server configuration information.
To specify plug-in information, call the slapi_pblock_set() function, and set the SLAPI_PLUGIN_DESCRIPTION parameter to this structure. For example:
This example code specifies the following plug-in information:
- The unique identifier for the server plug-in is test-plugin.
- The vendor of the server plug-in is example.com.
- The version of the server plug-in is 0.5.
- This version is intended to be used for your tracking purposes only; it is not the same as the server compatibility version number specified by the SLAPI_PLUGIN_VERSION parameter (see "Specifying Directory Server Compatibility," on page 42, for details on this parameter). As you make changes to your plug-in code, you can track the version distributed using the number contained in the Slapi_PluginDesc structure.
- The description of the server plug-in is contained in the data structure value sample pre-operation plug-in.
Registering Your Plug-in Functions
Whether you register your plug-in through the initialization function depends upon the type of function you are registering.
For some plug-in types, you do not need to register the plug-in function from within the initialization function. For example, you register entry store and entry fetch plug-ins by specifying the function names in the plugin directive in the dse.ldif file.
For other plug-in types, such as pre-operation plug-ins and post-operation plug-ins, the Directory Server gets the pointer to your function from a parameter in the initialization function parameter block. In these cases, you use the slapi_pblock_set() function to specify the name of your plug-in function.
The full list parameters that you can use to register your plug-in functions are listed in "Parameters for Registering Plug-in Functions," on page 566.
For example, if you want to register searchdn_preop_search() as a pre-operation search function, include the following code in your initialization function:slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_SEARCH_FN, \ (void *) searchdn_preop_search )
SLAPI_PLUGIN_PRE_SEARCH_FN is the parameter that specifies the pre-operation plug-in function for the LDAP search operation.
You can register more than one plug-in function in your initialization function; you do not need to write an initialization function for each plug-in function that you need to register. You should, however, define a different initialization function for each type of plug-in that you are registering.
Returning a Value to the Directory Server
If the initialization function is successful, it should return 0. If an error occurs, it should return -1. If the initialization function returns -1, the Directory Server will exit.
Example of an Initialization Function
The following is an example of an initialization function that registers the pre-operation plug-in function searchdn_preop_search(). After the initialization function returns, the server will call searchdn_preop_search() before each LDAP search operation is executed.
Compiling a Directory Server Plug-in
Keep in mind the following tips when compiling your server plug-in:
- You need to compile and link your code as a shared object or library. For example, on Solaris, specify ld -G objects -o shared_object as your link command.
- Some compilers require that you provide special flags when compiling code to be used in a shared object or DLL. For example, on Solaris, you must specify the -Kpic or -KPIC flag, which specifies that you want to generate position-independent code. Consult the documentation for your platform compiler and linker for details.
- Make sure that the server_root /plugins/slapd/slapi/include directory is in your include path.
- If you want, you can compile all plug-in functions in a single library. Although you can include different types of plug-in functions in the same library, you need to write separate initialization functions for each type of plug-in function. Refer to Chapter 3, "Configuring Plug-ins," for details on how to specify each initialization function in a directive for a specific type of plug-in.
The following code shows a sample Makefile for Solaris. The example assumes that the source files are located in the following directory:server_root /plugins/slapd/slapi/examples
The server plug-in API header files are located in the relative path ../include.
$(LD) $(LDFLAGS) -o [email protected] $(OBJS)