Complete Contents
Getting Started
Chapter 1 Understanding Server Plug-Ins
Chapter 2 Writing and Compiling Plug-Ins
Chapter 3 Calling the Front-End API Functions
Chapter 4 Quick Start
Chapter 5 Writing Database Plug-Ins
Chapter 6 Writing Pre/Post-Operation Plug-Ins
Chapter 7 Defining Functions for LDAP Operations
Chapter 8 Defining Functions for Database Operations
Chapter 9 Defining Functions for Authentication
Chapter 10 Writing Entry Store/Fetch Plug-Ins
Chapter 11 Writing Extended Operation Plug-Ins
Chapter 12 Writing Matching Rule Plug-Ins
Chapter 13 Data Type and Structure Reference
Chapter 14 Function Reference
Chapter 15 Parameter Reference
Glossary
Previous Next Contents Bookshelf


Chapter 2 Writing and Compiling Plug-Ins

This chapter explains how to write, compile, and install server plug-ins for the Directory Server.


Writing a Plug-In Function
This section provides general information on writing a server plug-in, including the following:

For additional information on specific plug-in types, see the following sections:

Including the API Header File

Make sure to include the slapi-plugin.h header file, which is located in the <server_root>/plugins/slapd/slapi/include directory on UNIX and the <server_root>\plugins\slapd\slapi\include directory on Windows NT:

#include "slapi-plugin.h"

Make sure that the ldap.h and lber.h header files (which are included by slapi-plugin.h) are in your include path.

Working with Parameter Blocks

The following types of plug-ins pass a parameter block as an argument:

When invoking these types of plug-in functions, the Directory Server passes a single argument of type Slapi_PBlock when calling the plug-in function. Your plug-in function should have the following prototype:

int myfunction( Slapi_PBlock pb );

pb is a parameter block that contains parameters pertaining to the operation or function.

For example, the parameter block for the add operation contains the target DN and the entry to be added, while the parameter block for the bind operation contains the DN of the user, the authentication method, and the user's credentials.

Note the following:

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 (Slapi_PBlock). To access the data in a parameter block, call the slapi_pblock_get() function (see "slapi_pblock_get()" on page  327 for details).

Important. In many cases, 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, normalizes the DN, converts all characters to lowercase, and writes the converted DN to the error log. Note that the actual DN (not a copy of it) is normalized and converted to lowercase.

#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 );

}

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 IDs, see Chapter  15, "Parameter Reference".

Setting Data in the Parameter Block

You can also change the value of a parameter in the parameter block by calling the slapi_pblock_set() function (see "slapi_pblock_set()" on page  329 for details). For example, you call can slapi_pblock_set() to change the value of the SLAPI_PRIVATE parameter, which stores private data for this 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:

#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" );

}

...

}

In this example, the slapi_log_error() function is used to indicate that an error occurred.

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 IDs, see Chapter  15, "Parameter 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, you can call the front-end API functions provided with the Directory Server. For example, you can call some of the front-end API functions to:

For more information on the front-end API, see Chapter  3, "Calling the Front-End API Functions" and Chapter  14, "Function Reference".

Specifying Return Values

If the function is successful, it should return 0. 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 a message describing the problem (see "Logging Messages" on page  50).

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, you should return a non-zero value, log an error message, and send the appropriate LDAP result code back to the client. (For information on which result code to send, see the chapter documenting the type of plug-in that you are writing.)


Writing an Initialization Function
Next, you need to write an initialization function for your server plug-in. Your initialization function should do the following:

  1. Specify the plug-in version.
  2. Specify information about the plug-in.
  3. Register your plug-in functions.
  4. Return a value to the Directory Server.
Your initialization function should have the following prototype:

int my_init_function( Slapi_PBlock pb );

Directory Server passes a single argument of type Slapi_PBlock when calling your initialization function.

If you are writing an initialization function for a plug-in on Windows NT, make sure to export the initialization functions. Use the following prototype:

__declspec( dllexport ) int my_init_function( Slapi_PBlock pb );

and specify the initialization function names in a .def file.

Note. You do not need to export all plug-in functions. Any plug-in functions that you specify in the pblock (see "Registering Your Plug-In Functions" on page  36) do not need to be exported. You only need to export the initialization functions and any functions that are not registered by initialization functions (such as the entry store and entry fetch plug-in functions, which are described in Chapter  10, "Writing Entry Store/Fetch Plug-Ins").

Specifying the Plug-In Version

You need to specify the version of your plug-in so that the Directory Server can determine whether or not it supports the plug-in.

To specify the plug-in version, call the slapi_pblock_set() function, and set the SLAPI_PLUGIN_VERSION parameter to the version number. For example:

slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_02);

Plug-in version 1 (SLAPI_PLUGIN_VERSION_01) is compatible with versions 3.x and 4.x of the Netscape Directory Server. Plug-in version 2 (SLAPI_PLUGIN_VERSION_02) is compatible with version 4.x of the Netscape Directory Server.

Specifying Information about the Plug-In

You can specify information about your plug-in (such as a description of the plug-in) in an Slapi_PluginDesc structure. Call the slapi_pblock_set() function, and set the SLAPI_PLUGIN_DESCRIPTION parameter to this structure. For example:

/* Specify information about the plug-in */

Slapi_PluginDesc mypdesc = { "test-plugin", "Airius.com", "0.5", "sample pre-operation plugin" };

...

/* Set this information in the parameter block */

slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&mypdesc );

In this example, the following information about the server plug-in is specified:

Registering Your Plug-In Functions

The way in which you register your plug-in function depends on the type of the plug-in function.

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 slapd.conf configuration file.)

For other plug-in types (such as pre-operation plug-ins, post-operation plug-ins, and database plug-ins), the Directory Server gets the pointer to your function from a parameter in the parameter block. You register plug-in functions by calling the slapi_pblock_set() function to specify the name of your plug-in function.

These parameters that you can use to specify plug-in functions are listed in "Parameters for Registering Plug-In Functions" on page  354. 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.

Important. 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.

Note that you can register more than one plug-in function in your initialization function. (In other words, you do not need to define an initialization function for each plug-in function. You should, however, define a different initialization function for each type of plug-in.)

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 a pre-operation plug-in function searchdn_preop_search(). After the initialization function is done, the server will call searchdn_preop_search() before each LDAP search operation is executed.

#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 and
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 Server Plug-In
Keep in mind the following tips when compiling your server plug-in:

The following is a sample makefile for Solaris. The example assumes that the source files are located in the <server_root>/plugins/slapd/slapi/examples directory. The server plug-in API header files are located in the relative path ../include.

# 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 $@ $(OBJS)

.c.o:

$(CC) $(CFLAGS) -c $<

clean:

-rm -f $(OBJS) libtest-plugin.so


Configuring the Server
After you compile your server plug-in, you need to configure the Directory Server to load your plug-in. The rest of this section explains the steps you need to take to configure the server.

Editing the slapd.conf File

To configure the server to load your plug-ins, you need to edit the slapd.conf server configuration file. (Before you edit this file, make sure to read the caveats in the section "Important Note on Manually Editing the Configuration File" on page  44.)

Open the slapd.conf file in a text editor, and add plugin directive to specify the plug-ins that you want to load.

Note that the directive should not contain any line breaks.

The argument for this directive are explained below:

For example, the following directive configures the server to load the library mydb.so and call the initialization function my_init_fn() on startup.

Again, note that the directive should not contain any line breaks.

Determining Where to Add the Directives

Each pre-operation, post-operation, and extended operation plug-in is associated with a back-end. Make sure that the plugin directive that registers the plug-in is in the database section for that back-end. (The plugin directive should be after the database directive and before the next database directive, if any.)

Directives registering entry store and entry fetch plug-ins must appear within the database section for the default ldbm database. (The plugin directive should be after the database ldbm directive and before the next database directive, if any.)

Specifying the Order of plugin Directives

Plug-ins of different types can be listed in any order. For example:

plugin database /usr/netscape/suitespot/lib/plugin.so db_init_fn

plugin preoperation /usr/netscape/suitespot/lib/plugin.so pr_init_fn

(In the example above, initialization functions and plug-in functions for both database and preoperation plug-ins have been defined in the same library.)

You can also load different plug-ins of the same type. Plug-ins of the same type are loaded and called in the order in which they are listed in the slapd.conf file. For example:

plugin preoperation /usr/netscape/suitespot/lib/plugin.so pre_fn_1

plugin preoperation /usr/netscape/suitespot/lib/plugin2.so pre_fn_2

In the example above for a given LDAP operation, the pre-operation plug-in function registered by pre_fn_1() are called before the pre-operation plug-in function registered by pre_fn_2().

So if pre_search_fn() is registered by pre_fn_1() and pre_search_fn2() is registered by pre_fn_2(), the Directory Server calls pre_search_fn() before calling pre_search_fn2(). Note that if pre_search_fn() terminates the LDAP operation by returning a non-zero value, the server will not call pre_search_fn2().

Summary of plugin Directives

The following table summarizes the different types of plug-ins that you can specify in the slapd.conf file.

Table 2.1 Directives in slapd.conf for specifying different plug-in types

Directive in slapd.conf
Plug-in Type
Description
plugin database

Database
Called by the server when interacting with the back-end database.
Example of use: You can define functions that allow the server to store and retrieve data from your own database.
plugin entryfetch

Entry fetch
Called by the server after retrieving an entry from the default back-end database. (If you are writing your own database plug-in, you do not need to use this plug-in.)
Example of use: If you encrypt data with an entry store plug-in function before saving the data to the database, you can define an entry fetch function that decrypts data after reading it from the database.
plugin entrystore

Entry store
Called by the server before saving an entry to the default back-end database. (If you are writing your own database plug-in, you do not need to use this plug-in.)
Example of use: You can define an entry store function to encrypt data before saving the data to the database.
plugin extendedop

Extended operation
Called by the server when receiving a request for an extended operation from a client.
plugin matchingrule

Matching rule
Called by the server when receives a search request with an extensible matching search filter from a client.
Also called by the server when indexing attributes for the default back-end database. (If you are using your own database plug-in, you do not need to use this type of plug-in for indexing attributes.)
plugin postoperation

Post-operation/data notification
Called by the server after performing an LDAP operation.
Example of use: You can define a data notification function to send notification to the administrator if certain data has changed.
plugin preoperation

Pre-operation/data validation
Called by the server before performing an LDAP operation.
Example of use: You can define a data validation function to check new entries before they are added to the directory.
plugin syntax

Syntax
Called by the server when getting a list of possible candidates for a search, when determining how to compare values in searches, and when adding or deleting values from certain attribute indexes.
Example of use: You can define a function that specifies how the "equals" comparison works for case-insensitive strings)

Important Note on Manually Editing the Configuration File

In the administration server, you can apply either hand-edited changes to the configuration file or changes made through the server manager interface -- but not both.

For example, suppose you made some changes using the server manager interface and then hand-edited the configuration file. When you click the Apply button at the top of the server manager interface, the administration server will allow you to apply either the changes made through the interface or the hand-edited changes. You cannot apply both sets of changes.

For this reason, before you open the slapd.conf file in a text editor, you should always click the Apply button in the server manager interface and apply any changes made through the interface.

Likewise, after you are done editing the slapd.conf file, you should click the Apply button again to apply the manual changes you have just made.

Passing Extra Arguments to the Plug-Ins

You can specify additional arguments at the end of the plugin directive. For example:

plugin preoperation /usr/lib/myplugin.so my_init_fn arg1 arg2

From the initialization function and the plug-in functions, you can get these arguments by getting the following parameters from the parameter block:

The following example illustrates how you can pass directive arguments to the initialization function and to the plug-in function. In both functions in the example, the extra arguments are written to the error log.

/* Initialization function */

#ifdef _WIN32

__declspec(dllexport)

#endif

int

searchdn_preop_init( Slapi_PBlock *pb )

{

int argc, cnt;

char ***argv;

/* 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 ||

/* Get the number of additional arguments */

slapi_pblock_get( pb, SLAPI_PLUGIN_ARGC, &argc ) != 0 ||

/* Get the array of additional arguments */

slapi_pblock_get( pb, SLAPI_PLUGIN_ARGV, &argv ) != 0 ) {

/* If a problem occurs, log an error message and
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" );

/* Log a note indicating the number of additional arguments found. */

slapi_log_error( SLAPI_LOG_PLUGIN, "searchdn_preop_init",
"Read %d additional arguments from configuration file.\n", argc );

/* Log each additional argument found. */

for ( cnt = 0; cnt < argc; cnt++ ) {

slapi_log_error( SLAPI_LOG_PLUGIN, "searchdn_preop_init",
"-> Argument #%d: %s\n", cnt + 1, argv[cnt] );

}

return( 0 );

}

/* Pre-operation plug-in function; invoked before LDAP searches */

int

searchdn_preop_search( Slapi_PBlock *pb )

{

int argc, cnt;

char *dn;

char ***argv;

/* 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 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 );

/* Get the number of additional arguments and the
array of these arguments and write this to the error log. */

if ( slapi_pblock_get( pb, SLAPI_PLUGIN_ARGC, &argc ) == 0 &&

slapi_pblock_get( pb, SLAPI_PLUGIN_ARGV, &argv ) == 0 ) {

slapi_log_error( SLAPI_LOG_PLUGIN, "searchdn_preop_search",
"Read %d additional parameters from the configuration file.\n",
argc );

for ( cnt = 0; cnt < argc; cnt++ ) {

slapi_log_error( SLAPI_LOG_PLUGIN, "searchdn_preop_search",
"-> Argument #%d: %s\n", cnt + 1, argv[cnt] );

}

}

return( 0 );

}

In the example above, if the plugin directive is:

plugin preoperation /usr/ns/plugin.so searchdn_preop_search foo bar

the following lines are written to the error log on server startup:

[01/Oct/1997:08:41:47 -0700] searchdn_preop_search - Plug-in successfully registered.

[01/Oct/1997:08:41:47 -0700] searchdn_preop_search - Read 2 additional parameters from configuration file.

[01/Oct/1997:08:41:47 -0700] searchdn_preop_search - -> Argument #1: foo

[01/Oct/1997:08:41:47 -0700] searchdn_preop_search - -> Argument #2: bar

The arguments are also written to the error log when the plug-in function is invoked (before each LDAP search operation is executed).

Setting the Log Level of the Server

If your functions call the slapi_log_error() function to write messages to the error log (see "slapi_log_error()" on page  308), you need to make sure that the Directory Server is configured to log messages with the severity level you've specified. (The available severity levels are also documented under "slapi_log_error()" on page  308.)

For example, suppose you call this function in your plug-in:

slapi_log_error( SLAPI_LOG_PLUGIN, "searchdn_preop_init",
"Plug-in successfully registered.\n" );

You need to make sure that the Directory Server is configured to log messages with the severity level SLAPI_LOG_PLUGIN.

To specify the level of severity at which the Directory Server logs messages, do the following:

  1. In the server manager interface, click the Apply button to make sure you've applied any recent manual changes to the slapd.conf file.
  2. It is important that you save any manual edits now. See the section "Important Note on Manually Editing the Configuration File" on page  44 for more details.

  3. If it is not already selected, click the Server Preferences tab.
  4. Click the LDAP link to display the page on LDAP settings.
  5. In the Log Level list box, select the severity levels that you have specified in your slapi_log_error() function calls.
  6. For example, if you use the SLAPI_LOG_PLUGIN severity level, choose Plug- Ins from the list. To select more than one level, press the Ctrl key down and click on the additional levels.

  7. Click OK.
  8. Click the Apply button at the top of the server manager interface.
  9. Make sure that you apply your changes.

 

© Copyright 1998 Netscape Communications Corporation