Sun Java System Directory Server Enterprise Edition 6.0 Developer's Guide

Chapter 8 Performing Internal Operations With Plug-Ins

This chapter explains how to perform search, add, modify, modify DN, and delete operations for which no corresponding client requests exist.

Refer to Chapter 16, Function Reference, Part I for information about the plug-in API functions used in this chapter.

This chapter covers the following topics:

Using Internal Operations

This chapter shows how to use internal search, add, modify, modify RDN, and delete operations.

When to Use Internal Operations

Internal operations are internal in the sense that they are initiated not by external requests from clients, but internally by plug-ins. Use internal operation calls when your plug-in needs Directory Server to perform an operation for which no client request exists.

Issues With Internal Operations

You set up the parameter blocks and handle all memory management directly when developing with internal operations. Debug sessions with optimized binaries such as, the libraries that are delivered with the product, can be tedious. Review the code carefully. If you want to, work with a partner who can point out errors that you miss. Memory management mistakes around internal operation calls lead more quickly to incomprehensible segmentation faults than other calls in the plug-in API.

Furthermore, internal operations result in updates to some internal caches but not others. For example, changes to access control instructions cause updates to the access control cache. Internal operation changes to attributes used in CoS and roles do not cause CoS and roles caches to be updated.

Finding the Internal Operations Example

The plug-in code used in this chapter can be found in install-path/examples/internal.c.

Before Using the Internal Operations Example

Before using the example plug-in internal.c, create a new suffix, dc=example,dc=com, and populate the suffix with example data.

ProcedureTo Set Up an Example Suffix

If you have not done so already, set up a directory instance with a suffix, dc=example,dc=com, containing data loaded from a sample LDIF file, install-path/ds6/ldif/Example.ldif.

  1. Create a new Directory Server instance.

    For example:


    $ dsadm create /local/ds
    Choose the Directory Manager password:
    Confirm the Directory Manager password:
    $ 
  2. Start the new Directory Server instance.

    For example:


    $ dsadm start /local/ds
    Server started: pid=4705
    $ 
  3. Create a suffix called dc=example,dc=com.

    For example, with long lines folded for the printed page:


    $ dsconf create-suffix -h localhost -p 1389 dc=example,dc=com
    Enter "cn=directory manager" password: 
    Certificate "CN=defaultCert, CN=hostname:1636" presented by the
     server is not trusted.
    Type "Y" to accept, "y" to accept just once,
     "n" to refuse, "d" for more details: Y
    $ 
  4. Load the sample LDIF.

    For example, with long lines folded for the printed page:


    $ dsconf import -h localhost -p 1389 \
     /opt/SUNWdsee/ds6/ldif/Example.ldif dc=example,dc=com
    Enter "cn=directory manager" password:  
    New data will override existing data of the suffix
     "dc=example,dc=com".
    Initialization will have to be performed on replicated suffixes. 
    Do you want to continue [y/n] ? y
    
    ## Index buffering enabled with bucket size 16
    ## Beginning import job...
    ## Processing file "/opt/SUNWdsee/ds6/ldif/Example.ldif"
    ## Finished scanning file "/opt/SUNWdsee/ds6/ldif/Example.ldif" (160 entries)
    ## Workers finished; cleaning up...
    ## Workers cleaned up.
    ## Cleaning up producer thread...
    ## Indexing complete.
    ## Starting numsubordinates attribute generation.
     This may take a while, please wait for further activity reports.
    ## Numsubordinates attribute generation complete. Flushing caches...
    ## Closing files...
    ## Import complete. Processed 160 entries in 5 seconds.
     (32.00 entries/sec)
    
    Task completed (slapd exit code: 0).
    $ 
See Also

You can use Directory Service Control Center to perform this task. For more information, see the Directory Service Control Center online help.

Internal Add

For an internal add, you allocate space for a parameter block. You set up the parameter block for the add with slapi_add_entry_internal_set_pb(). Thus, the entry is in the parameter block when you invoke slapi_add_internal_pb(). Then you free the parameter block. The internal add consumes the entry that is passed in to the server through the parameter block.


Example 8–1 Internal Add Operation (internal.c)

#include "slapi-plugin.h"

/* Internal operations require an ID for the plug-in.                   */
static Slapi_ComponentId * plugin_id     = NULL;

int
test_internal()
{
    Slapi_DN       * sdn;              /* DN holder for internal ops    */
    Slapi_Entry    * entry;            /* Entry holder for internal ops */
    Slapi_PBlock   * pb;               /* PBlock for internal ops       */
    char           * str = NULL;       /* String holder for internal ops*/
    int              len;              /* Length of LDIF from entry     */
    int              rc;               /* Return code; 0 means success. */

    /* Check that the example suffix exists.                            */
    sdn = slapi_sdn_new_dn_byval("dc=example,dc=com");
    if (slapi_be_exist(sdn)) {
        slapi_log_info_ex(
            SLAPI_LOG_INFO_AREA_PLUGIN,
            SLAPI_LOG_INFO_LEVEL_DEFAULT,
            SLAPI_LOG_NO_MSGID,
            SLAPI_LOG_NO_CONNID,
            SLAPI_LOG_NO_OPID,
            "test_internal in test-internal plug-in",
            "Suffix (%s) does not exist, exiting.\n",
            slapi_sdn_get_dn(sdn)
        );
        slapi_sdn_free(&sdn);
        return (0);
    }
    slapi_sdn_free(&sdn);

    /*
     * Add an entry for Quentin Cubbins to the example suffix
     * using slapi_add_entry_internal().                                */
    entry = slapi_entry_alloc();
    slapi_entry_set_dn(                /* slapi_entry_set_dn()          */
        entry,                         /* requires a copy of the DN.    */
        slapi_ch_strdup("uid=qcubbins,ou=People,dc=example,dc=com")
    );
                                       /* slapi_entry_add_string()      */
                                       /* does not require a copy.      */
    slapi_entry_add_string(entry, "objectclass", "top");
    slapi_entry_add_string(entry, "objectclass", "person");
    slapi_entry_add_string(entry, "objectclass", "organizationalPerson");
    slapi_entry_add_string(entry, "objectclass", "inetOrgPerson");
    slapi_entry_add_string(entry, "uid", "qcubbins");
    slapi_entry_add_string(entry, "givenName", "Quentin");
    slapi_entry_add_string(entry, "sn", "Cubbins");
    slapi_entry_add_string(entry, "cn", "Quentin Cubbins");
    slapi_entry_add_string(entry, "mail", "qcubbins@example.com");
    slapi_entry_add_string(entry, "userPassword", "qcubbins");
    slapi_entry_add_string(entry, "secretary",
        "uid=bjensen,ou=People,dc=example,dc=com");

    pb = slapi_pblock_new();           /* Make a new PBlock...          */
    rc = slapi_add_entry_internal_set_pb(
        pb,
        entry,
        NULL,                          /* No controls                   */
        plugin_id,
        SLAPI_OP_FLAG_NEVER_CHAIN      /* Never chain this operation.   */
    );
    if (rc != 0) {
        slapi_pblock_destroy(pb);
        return (-1);
    }

    str = slapi_entry2str(entry, &len);/* Entry as LDIF for the log.
                                        * Note you have to capture this
                                        * before the internal add, during
                                        * which the entry is consumed.  */
    rc = slapi_add_internal_pb(pb);    /* Entry consumed here           */
                                       /* ... get status ...            */
    slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
    if (rc != LDAP_SUCCESS) {
        slapi_pblock_destroy(pb);
        return (-1);
    }

    slapi_pblock_destroy(pb);

    slapi_log_info_ex(
        SLAPI_LOG_INFO_AREA_PLUGIN,
        SLAPI_LOG_INFO_LEVEL_DEFAULT,
        SLAPI_LOG_NO_MSGID,
        SLAPI_LOG_NO_CONNID,
        SLAPI_LOG_NO_OPID,
        "test_internal in test-internal plug-in",
        "\nAdded entry:\n%sEntry length: %d\n", str, len
    );

    return (0);
}

Notice that the internal operation requires a plugin_id. As shown, the plug-in ID is a global variable. The plug-in ID is set during plug-in initialization, using this function:

slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &plugin_id);

The parameter block, pb, is passed to the plug-in initialization function. Refer to the internal_init() function in internal.c for a sample implementation.

Internal Modify

For internal modify, you first set up an array of LDAPMod modifications. The array contains information about the attribute types to modify. The modifications also contain the attribute values. Then, as for internal add, you allocate space for a parameter block. You set up the parameter block with slapi_modify_internal_set_pb(). Then you invoke the modify operation with slapi_modify_internal_pb(). Finally, you free the memory used.


Example 8–2 Internal Modify Operation (internal.c)

This example demonstrates internal modification of a user mail address.

#include "slapi-plugin.h"

static Slapi_ComponentId * plugin_id     = NULL;

int
test_internal()
{
    Slapi_Entry    * entry;            /* Entry holder for internal ops */
    Slapi_PBlock   * pb;               /* PBlock for internal ops       */
    LDAPMod          mod_attr;         /* Attribute to modify           */
    LDAPMod        * mods[2];          /* Array of modifications        */
    char           * mail_vals[]  =    /* New mail address              */
                       {"quentin@example.com", NULL};
    int              rc;               /* Return code; 0 means success. */

    /* Modify Quentin's entry after his email address changes from
     * qcubbins@example.com to quentin@example.com.                     */
    mod_attr.mod_type   = "mail";
    mod_attr.mod_op     = LDAP_MOD_REPLACE;
    mod_attr.mod_values = mail_vals;   /* mail: quentin@example.com     */
    mods[0]             = &mod_attr;
    mods[1]             = NULL;

    pb = slapi_pblock_new();           /* Set up a PBlock...            */
    rc = slapi_modify_internal_set_pb(
        pb,
        "uid=qcubbins,ou=people,dc=example,dc=com",
        mods,
        NULL,                          /* No controls                   */
        NULL,                          /* DN rather than unique ID      */
        plugin_id,
        SLAPI_OP_FLAG_NEVER_CHAIN      /* Never chain this operation.   */
    );
    if (rc != 0) {
        slapi_pblock_destroy(pb);
        return (-1);
    }

    rc = slapi_modify_internal_pb(pb); /* Unlike internal add,          */
                                       /* nothing consumed here         */
                                       /* ... get status ...            */
    slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
    if (rc != LDAP_SUCCESS) {
        slapi_pblock_destroy(pb);
        return (-1);
    }

    slapi_pblock_destroy(pb);          /* ... clean up the PBlock.      */

    slapi_log_info_ex(
        SLAPI_LOG_INFO_AREA_PLUGIN,
        SLAPI_LOG_INFO_LEVEL_DEFAULT,
        SLAPI_LOG_NO_MSGID,
        SLAPI_LOG_NO_CONNID,
        SLAPI_LOG_NO_OPID,
        "test_internal in test-internal plug-in",
        "\nModified attribute: %s\nNew value: %s\n",
        mod_attr.mod_type, mail_vals[0]
    );

    return (0);
}

Notice that the data in mod_attr and mail_vals is still available for use after the modification. Unlike internal add, internal modify does not consume the data that you set in the parameter block.

Internal Rename and Move (Modify DN)

This section describes how to develop a plug-in to rename an entry, or to move an entry.

    An internal modify DN operation is performed in these stages:

  1. Allocate space for a parameter block.

  2. Set up the operation by using slapi_rename_internal_set_pb().

  3. Invoke the operation by using slapi_modrdn_internal_pb().

    The first stage of the operation is rename. The second stage of the operation is modrdn.

  4. Free the parameter block.


Example 8–3 Internal Rename or Move Operation (internal.c)

This example demonstrates an internal modify DN operation. Internal modify DN does not consume the data that you set in the parameter block.

#include "slapi-plugin.h"

static Slapi_ComponentId * plugin_id     = NULL;

int
test_internal()
{
    Slapi_PBlock   * pb;               /* PBlock for internal ops       */
    int              rc;               /* Return code; 0 means success. */

    pb = slapi_pblock_new();           /* Set up a PBlock again...      */
    rc = slapi_rename_internal_set_pb(
        pb,
        "uid=qcubbins,ou=people,dc=example,dc=com", /*Specify target entry*/
        "uid=fcubbins",                             /*Specify new RDN     */
        "ou=people,dc=example,dc=com",              /*Specify new superior*/
        /* The new superior is the same as the old superior.            */
        1,                             /* Delete old RDN                */
        NULL,                          /* No controls                   */
        NULL,                          /* DN rather than unique ID      */
        plugin_id,
        SLAPI_OP_FLAG_NEVER_CHAIN      /* Never chain this operation.   */
    );

    if (rc != LDAP_SUCCESS) {
        slapi_pblock_destroy(pb);
        return (-1);
    }

    rc = slapi_modrdn_internal_pb(pb); /* Like internal modify,         */
                                       /* nothing consumed here.        */
                                       /* ... get status ...            */
    slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
    if (rc != LDAP_SUCCESS) {
        slapi_pblock_destroy(pb);
        return (-1);
    }

    slapi_pblock_destroy(pb);          /* ... cleaning up.              */

    slapi_log_info_ex(
        SLAPI_LOG_INFO_AREA_PLUGIN,
        SLAPI_LOG_INFO_LEVEL_DEFAULT,
        SLAPI_LOG_NO_MSGID,
        SLAPI_LOG_NO_CONNID,
        SLAPI_LOG_NO_OPID,
        "test_internal in test-internal plug-in",
        "\nNew entry RDN: %s\n", "uid=fcubbins"
    );

    return (0);
}

Internal Search

For an internal search, use callbacks to retrieve what the server finds. The callbacks allow you to retrieve the info that would be sent back to a client application were the operation initiated by an external request: LDAP result codes, entries found, and referrals.

You set up the callback data that you want to retrieve. You also write the function that is called back by the server.


Example 8–4 Search Callback (internal.c)

This code shows how the example plug-in internal.c uses a callback to retrieve an LDIF representation of the first entry that is found. The entry is found during an internal search with slapi_entry2str() as the callback function.

#include "slapi-plugin.h"

struct cb_data {                       /* Data returned from search     */
    char * e_str;                      /* Entry as LDIF                 */
    int    e_len;                      /* Length of LDIF                */
};

int
test_internal_entry_callback(Slapi_Entry * entry, void * callback_data)
{
    struct cb_data * data;
    int              len;

    /* This callback could do something more interesting with the
     * data such as build an array of entries returned by the search.
     * Here, simply log the result.                                     */
    data        = (struct cb_data *)callback_data;
    data->e_str = slapi_entry2str(entry, &len);
    data->e_len = len;
    slapi_log_info_ex(
        SLAPI_LOG_INFO_AREA_PLUGIN,
        SLAPI_LOG_INFO_LEVEL_DEFAULT,
        SLAPI_LOG_NO_MSGID,
        SLAPI_LOG_NO_CONNID,
        SLAPI_LOG_NO_OPID,
        "test_internal_entry_callback in test-internal plug-in",
        "\nFound entry: %sLength: %d\n", data->e_str, data->e_len
    );
    return (-1);                       /* Stop at the first entry.      */
}                                      /* To continue, return 0.        */

This callback stops the search at the first entry. Your plug-in might have to deal with more than one entry being returned by the search. Therefore, consider how you want to allocate space for your data depending on what your plug-in does.

With the callback data and function implemented, you are ready to process the internal search. First, allocate space for the parameter block and your callback data, and set up the parameter block with slapi_search_internal_pb_set(). Next, invoke the search with slapi_search_internal_pb(), and also set up the callback with slapi_search_internal_callback_pb(). When you are finished, free the space that you have allocated.



Example 8–5 Internal Search Operation (internal.c)

#include "slapi-plugin.h"

static Slapi_ComponentId * plugin_id     = NULL;

int
test_internal()
{
    Slapi_PBlock   * pb;               /* PBlock for internal ops       */
    char           * srch_attrs[] =    /* Attr. to get during search    */
                       {LDAP_ALL_USER_ATTRS, NULL};
    struct cb_data   callback_data;    /* Data returned from search     */
    int              rc;               /* Return code; 0 means success. */

    slapi_log_info_ex(
        SLAPI_LOG_INFO_AREA_PLUGIN,
        SLAPI_LOG_INFO_LEVEL_DEFAULT,
        SLAPI_LOG_NO_MSGID,
        SLAPI_LOG_NO_CONNID,
        SLAPI_LOG_NO_OPID,
        "test_internal in test-internal plug-in",
        "\nSearching with base DN %s, filter %s...\n",
        "dc=example,dc=com", "(uid=fcubbins)"
    );

    pb = slapi_pblock_new();           /* Set up a PBlock...            */
    rc = slapi_search_internal_set_pb(
        pb,
        "dc=example,dc=com",           /* Base DN for search            */
        LDAP_SCOPE_SUBTREE,            /* Scope                         */
        "(uid=fcubbins)",              /* Filter                        */
        srch_attrs,                    /* Set to get all user attrs.    */
        0,                             /* Return attrs. and values      */
        NULL,                          /* No controls                   */
        NULL,                          /* DN rather than unique ID      */
        plugin_id,
        SLAPI_OP_FLAG_NEVER_CHAIN      /* Never chain this operation.   */
    );

    if (rc != LDAP_SUCCESS) {
        slapi_pblock_destroy(pb);
        return (-1);
    }

    /* Internal search puts results into the PBlock, but uses callbacks
     * to get at the data as it is turned up by the search. In this case,
     * what you want to get is the entry found by the search.           */
    rc  = slapi_search_internal_pb(pb);
    rc |= slapi_search_internal_callback_pb(
        pb,
        &callback_data,
        NULL,                          /* No result callback            */
        test_internal_entry_callback,
        NULL                           /* No referral callback          */
    );

                                       /* ... get status ...            */
    slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
    if (rc != LDAP_SUCCESS) {
        slapi_pblock_destroy(pb);
        return -1;
    }
                                       /* Free the search results when
                                        * finished with them.           */
    slapi_free_search_results_internal(pb);
    slapi_pblock_destroy(pb);          /* ... done cleaning up.         */

    return (0);
}

Here, you allocate and free callback_data locally. You can manage memory differently if you pass the data to another plug-in function.


Internal Delete

For internal delete, you allocate space for the parameter block, then set up the parameter block with slapi_delete_internal_set_pb(). You invoke the delete with slapi_delete_internal_pb(). Finally, you free the parameter block.

Internal delete does not consume the data that you set in the parameter block.