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:
This chapter shows how to use internal search, add, modify, modify RDN, and delete 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.
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.
The plug-in code used in this chapter can be found in install-path/examples/internal.c.
Before using the example plug-in internal.c, create a new suffix, dc=example,dc=com, and populate the suffix with example data.
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.
Create a new Directory Server instance.
For example:
$ dsadm create /local/ds Choose the Directory Manager password: Confirm the Directory Manager password: $ |
Start the new Directory Server instance.
For example:
$ dsadm start /local/ds Server started: pid=4705 $ |
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 $ |
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). $ |
You can use Directory Service Control Center to perform this task. For more information, see the Directory Service Control Center online help.
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.
#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.
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.
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.
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:
Allocate space for a parameter block.
Set up the operation by using slapi_rename_internal_set_pb().
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.
Free the parameter block.
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); }
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.
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.
#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.
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.