Plug-in Programmerís Guide
Red Hat Directory Server                                                            

Previous
Contents
Index
Next

Chapter 2

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:

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:

For additional information on writing specific plug-in types, refer to the following chapters:

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:

#include "slapi-plugin.h"

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:

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.

Note

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.

Code Example 2-1 Getting Data from the Parameter Block 
 
#include <slapi-plugin.h>
...
int
searchdn_preop_search( Slapi_PBlock *pb )
{
  char *dn;
  /* 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 );
 
  /* Normalize the DN (the actual DN, not a copy of it)
    and convert it to lowercase */
  slapi_dn_normalize_case( dn );
 
  /* Log the normalized DN */
  slapi_log_error( SLAPI_LOG_PLUGIN, "searchdn_preop_search",
    "Normalized DN: %s\n", dn );
  return( 0 );
}
 

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.

Code Example 2-2 Setting Values in the Parameter Block  
 
#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_PRIVATE, (void *) db ) == -1 )
{
    slapi_log_error( SLAPI_LOG_PLUGIN, "ldif_back_init",
    "Unable to store database information\n" );
  }
...
}
 

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:

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:

  1. Specify the plug-in compatibility version.
  2. Register each of your plug-in functions.
  3. 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);
 

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:

Code Example 2-3 Specifying Plug-in Information  
 
/* Specify information about the plug-in */
Slapi_PluginDesc mypdesc = { "test-plugin", "example.com",
  "0.5", "sample pre-operation plugin" };
...
 
/* Set this information in the parameter block */
slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
  (void *)&mypdesc );
 

This example code specifies the following plug-in information:

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.

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.

Note

If you do not register your plug-in functions, the Directory Server will not call your plug-in functions. Make sure to register your plug-in functions.


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.

Code Example 2-4 An Example Initialization Function  
 
#include "slapi-plugin.h"
...
#ifdef _WIN32
__declspec(dllexport)
#endif
 
searchdn_preop_init( Slapi_PBlock *pb )
{
  /* Specify the version of the plug-in (set this to "01")*/
  if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
        SLAPI_PLUGIN_VERSION_01 ) != 0 ||
  /* Set up the server to call searchdn_preop_search()
    before each LDAP search operation. */
  slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_SEARCH_FN,
    (void *) searchdn_preop_search ) !=0 ) {
  /* If a problem occurs, log an error message, return -1.*/
    slapi_log_error(SLAPI_LOG_PLUGIN,"searchdn_preop_init",
      "Error registering function.\n" );
  return( -1 );
  }
 
  /* If the plug-in was successfully registered, log a
      message and return 0. */
  slapi_log_error( SLAPI_LOG_PLUGIN, "searchdn_preop_init",
    "Plug-in successfully registered.\n" );
  return( 0 );
}
 

Compiling a Directory Server Plug-in

Keep in mind the following tips when compiling your server plug-in:

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.

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.

Code Example 2-5 Example Solaris Makefile  
 
# SOLARIS Makefile for Directory Server plug-in examples
#
CC = cc
LD = ld
INCLUDE_FLAGS = -I../include
CFLAGS = $(INCLUDE_FLAGS) -D_REENTRANT -KPIC
LDFLAGS = -G
OBJS = testsaslbind.o testextendedop.o testpreop.o testpostop.o testentry.o
all: libtest-plugin.so
libtest-plugin.so: $(OBJS)
  $(LD) $(LDFLAGS) -o [email protected] $(OBJS)
.c.o:
  $(CC) $(CFLAGS) -c $<
clean:
  -rm -f $(OBJS) libtest-plugin.so
 




Previous
Contents
Index
Next

© 2001 Sun Microsystems, Inc. Used by permission. © 2005 Red Hat, Inc. All rights reserved.
Read the Full Copyright and Third-Party Acknowledgments.

last updated May 26, 2005