/* * Copyright 2008 Red Hat, Inc. * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This Program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this Program; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include #include #include #include #ifdef HAVE_DIRSRV_SLAPI_PLUGIN_H #include #include #include #else #include #endif #include #include #ifdef HAVE_TCPD_H #include #endif #include "backend.h" #include "backend.h" #include "back-shr.h" #include "format.h" #include "plugin.h" #include "map.h" #define SCH_CONTAINER_CONFIGURATION_FILTER "(&(objectClass=*)(" SCH_CONTAINER_CONFIGURATION_GROUP_ATTR "=*)(" SCH_CONTAINER_CONFIGURATION_CONTAINER_ATTR "=*)(" SCH_CONTAINER_CONFIGURATION_BASE_ATTR "=*)(" SCH_CONTAINER_CONFIGURATION_FILTER_ATTR "=*)(" SCH_CONTAINER_CONFIGURATION_RDN_ATTR "=*)(" SCH_CONTAINER_CONFIGURATION_ATTR_ATTR "=*))" /* The data we ask the map cache to keep, for us, for each set. */ struct backend_set_data { struct backend_shr_set_data common; /* Schema compatibility-specific data. */ Slapi_DN *container_sdn; char *rdn_format; char **attribute_format; bool_t check_aci; }; struct backend_entry_data { Slapi_Entry *e; }; /* Read the name of the NIS master. A dummy function for the schema * compatibility plugin. */ void backend_free_master_name(struct plugin_state *state, char *master) { } int backend_read_master_name(struct plugin_state *state, char **master) { *master = "localhost"; return -1; } /* Manipulate a backend map configuration. */ static void backend_set_config_free_config_contents(void *data) { struct backend_set_data *set_data = data; if (set_data != NULL) { free(set_data->common.group); free(set_data->common.set); free(set_data->common.bases); format_free_attr_list(set_data->common.ref_attrs); format_free_inref_attrs(set_data->common.inref_attrs); free(set_data->common.entry_filter); slapi_sdn_free(&set_data->container_sdn); free(set_data->rdn_format); backend_shr_free_strlist(set_data->attribute_format); } } void backend_set_config_free_config(struct backend_shr_set_data *data) { backend_set_config_free_config_contents(data->self); free(data); } static struct backend_shr_set_data * backend_copy_set_config(const struct backend_set_data *data) { struct backend_set_data *ret; ret = malloc(sizeof(*ret)); if (ret == NULL) { return NULL; } ret->common.self = ret; ret->common.state = data->common.state; ret->common.group = strdup(data->common.group); ret->common.set = strdup(data->common.set); ret->common.bases = backend_shr_dup_strlist(data->common.bases); ret->common.ref_attrs = data->common.ref_attrs ? format_dup_attr_list(data->common.ref_attrs) : NULL; ret->common.inref_attrs = data->common.inref_attrs ? format_dup_inref_attrs(data->common.inref_attrs) : NULL; ret->common.entry_filter = strdup(data->common.entry_filter); ret->container_sdn = slapi_sdn_dup(data->container_sdn); ret->rdn_format = strdup(data->rdn_format); ret->attribute_format = backend_shr_dup_strlist(data->attribute_format); ret->check_aci = data->check_aci; if ((ret->common.group == NULL) || (ret->common.set == NULL) || (ret->common.bases == NULL) || (ret->common.entry_filter == NULL) || (ret->container_sdn == NULL)) { backend_set_config_free_config(&ret->common); return NULL; } return &ret->common; } /* Given a configuration entry, read the map configuration for the given group * and container name from the entry. */ void backend_set_config_read_config(struct plugin_state *state, Slapi_Entry *e, const char *group, const char *container, bool_t *flag, struct backend_shr_set_data **pret) { char **bases, *entry_filter, **attributes, *rdn_format, *dn; bool_t check_aci; char *actual_attr; const char *cvalue; int i, j, disposition, buffer_flags, count; struct backend_set_data ret; Slapi_ValueSet *values; Slapi_Value *value; /* Read the values from the configuration entry. */ bases = NULL; if (slapi_vattr_values_get(e, SCH_CONTAINER_CONFIGURATION_BASE_ATTR, &values, &disposition, &actual_attr, 0, &buffer_flags) == 0) { count = slapi_valueset_count(values); bases = malloc(sizeof(char *) * (count + 1)); if (bases != NULL) { i = 0; for (j = slapi_valueset_first_value(values, &value); j != -1; j = slapi_valueset_next_value(values, j, &value)) { cvalue = slapi_value_get_string(value); bases[i++] = strdup(cvalue); } bases[i] = NULL; } slapi_vattr_values_free(&values, &actual_attr, buffer_flags); } entry_filter = NULL; if (slapi_vattr_values_get(e, SCH_CONTAINER_CONFIGURATION_FILTER_ATTR, &values, &disposition, &actual_attr, 0, &buffer_flags) == 0) { i = slapi_valueset_first_value(values, &value); if (i != -1) { cvalue = slapi_value_get_string(value); if (strlen(cvalue) > 1) { if ((cvalue[0] != '(') || (cvalue[strlen(cvalue) - 1] != ')')) { entry_filter = malloc(strlen(cvalue) + 3); if (entry_filter != NULL) { sprintf(entry_filter, "(%s)", cvalue); } } } if (entry_filter == NULL) { entry_filter = strdup(cvalue); } } slapi_vattr_values_free(&values, &actual_attr, buffer_flags); } rdn_format = NULL; if (slapi_vattr_values_get(e, SCH_CONTAINER_CONFIGURATION_RDN_ATTR, &values, &disposition, &actual_attr, 0, &buffer_flags) == 0) { i = slapi_valueset_first_value(values, &value); if (i != -1) { cvalue = slapi_value_get_string(value); rdn_format = strdup(cvalue); } slapi_vattr_values_free(&values, &actual_attr, buffer_flags); } check_aci = TRUE; if (slapi_vattr_values_get(e, SCH_CONTAINER_CONFIGURATION_ACCESS_ATTR, &values, &disposition, &actual_attr, 0, &buffer_flags) == 0) { i = slapi_valueset_first_value(values, &value); if (i != -1) { cvalue = slapi_value_get_string(value); /* FIXME: use nl_langinfo(YESEXPR) here? */ if ((strcasecmp(cvalue, "yes") == 0) || (strcasecmp(cvalue, "on") == 0) || (strcasecmp(cvalue, "1") == 0)) { check_aci = TRUE; } else { check_aci = FALSE; } } slapi_vattr_values_free(&values, &actual_attr, buffer_flags); } attributes = NULL; if (slapi_vattr_values_get(e, SCH_CONTAINER_CONFIGURATION_ATTR_ATTR, &values, &disposition, &actual_attr, 0, &buffer_flags) == 0) { count = slapi_valueset_count(values); attributes = malloc(sizeof(char *) * (count + 1)); if (attributes != NULL) { i = 0; for (j = slapi_valueset_first_value(values, &value); j != -1; j = slapi_valueset_next_value(values, j, &value)) { cvalue = slapi_value_get_string(value); attributes[i++] = strdup(cvalue); } attributes[i] = NULL; } slapi_vattr_values_free(&values, &actual_attr, buffer_flags); } /* Populate the returned structure. */ ret.common.state = state; ret.common.group = strdup(group); ret.common.set = strdup(container); ret.common.bases = bases; ret.common.entry_filter = entry_filter; ret.common.ref_attrs = NULL; ret.common.inref_attrs = NULL; dn = slapi_dn_plus_rdn(ret.common.group, ret.common.set); ret.container_sdn = slapi_sdn_new_dn_passin(dn); ret.rdn_format = rdn_format; ret.attribute_format = attributes; ret.check_aci = check_aci; *pret = backend_copy_set_config(&ret); free(ret.common.group); free(ret.common.set); if (ret.common.bases != NULL) { for (i = 0; ret.common.bases[i] != NULL; i++) { free(ret.common.bases[i]); } free(ret.common.bases); } free(ret.common.entry_filter); slapi_sdn_free(&ret.container_sdn); if (ret.attribute_format != NULL) { for (i = 0; ret.attribute_format[i] != NULL; i++) { free(ret.attribute_format[i]); } free(ret.attribute_format); } free(ret.rdn_format); } /* Create and destroy entry-specific data. */ static struct backend_entry_data * backend_entry_make_entry_data(Slapi_Entry *e) { struct backend_entry_data *ret; ret = malloc(sizeof(*ret)); if (ret != NULL) { ret->e = e; } else { slapi_entry_free(e); } return ret; } static void backend_entry_free_entry_data(void *p) { struct backend_entry_data *data; data = p; slapi_entry_free(data->e); free(data); } /* Given a map-entry directory entry, determine a key, a value, and extra data * to be stored in the map cache, and add them to the map cache. */ void backend_set_entry_one(Slapi_Entry *e, struct backend_set_data *data) { char *dn, *rdn, *ndn, *ldif, *plugin_id, *keys[2], **ava, *val; int len, i, j; Slapi_Entry *entry; plugin_id = data->common.state->plugin_desc->spd_id; ndn = slapi_entry_get_ndn(e); /* Generate the RDN for the entry. */ rdn = format_get_data(data->common.state, e, data->common.group, data->common.set, data->rdn_format, NULL, &data->common.ref_attrs, &data->common.inref_attrs); /* Now build the entry itself, and set the DN first. */ entry = slapi_entry_alloc(); dn = slapi_dn_plus_rdn(slapi_sdn_get_ndn(data->container_sdn), rdn); slapi_entry_set_dn(entry, dn); /* NOTE: the entry now owns the dn */ /* Iterate through the set of attributes. */ if (data->attribute_format != NULL) { for (i = 0; data->attribute_format[i] != NULL; i++) { ava = format_get_data_set(data->common.state, e, data->common.group, data->common.set, data->attribute_format[i], NULL, &data->common.ref_attrs, &data->common.inref_attrs); if (ava != NULL) { for (j = 0; ava[j] != NULL; j++) { val = strchr(ava[j], '='); if ((val != NULL) && (val[1] != '\0')) { *val = '\0'; slapi_entry_add_string(entry, ava[j], val + 1); *val = '='; } } format_free_data_set(ava); } } } /* Try to make the entry look "right". */ if (!slapi_entry_rdn_values_present(entry)) { slapi_entry_add_rdn_values(entry); } if (slapi_entry_schema_check(NULL, entry) != 0) { slapi_entry_add_string(entry, "objectClass", "extensibleObject"); } /* Produce some LDIF. */ ldif = slapi_entry2str(entry, &len); /* If we actually generated a useful new entry for this entry, then set * it, otherwise clear it in case there was one set before. */ if ((rdn != NULL) && (ldif != NULL)) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_id, "setting group/container/key/value " "\"%s\"/\"%s\"/\"%s\"(\"%s\")=\"%s\"\n", data->common.group, data->common.set, rdn, ndn, ldif); keys[0] = rdn; keys[1] = NULL; map_data_set_entry(data->common.state, data->common.group, data->common.set, ndn, NULL, keys, len, ldif, backend_entry_make_entry_data(entry), backend_entry_free_entry_data); } else { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_id, "no value for %s, unsetting domain/map/id" "\"%s\"/\"%s\"/(\"%s\")\n", ndn, data->common.group, data->common.set, ndn); map_data_unset_entry_id(data->common.state, data->common.group, data->common.set, ndn); slapi_entry_free(entry); } slapi_ch_free((void **) &ldif); format_free_data(rdn); } /* Process a set configuration directory entry. Pull out the group and * container names which are valid for this configuration and configure such a * container for each in turn. */ int backend_set_config_entry_add_cb(Slapi_Entry *e, void *callback_data) { char **groups, **containers, *actual_attr; const char *cvalue; Slapi_ValueSet *values; Slapi_Value *value; int i, j, ret, disposition, buffer_flags, count; struct plugin_state *state; state = callback_data; groups = NULL; if (slapi_vattr_values_get(e, SCH_CONTAINER_CONFIGURATION_GROUP_ATTR, &values, &disposition, &actual_attr, 0, &buffer_flags) == 0) { count = slapi_valueset_count(values); groups = malloc(sizeof(char *) * (count + 1)); if (groups != NULL) { for (i = slapi_valueset_first_value(values, &value); i != -1; i = slapi_valueset_next_value(values, i, &value)) { cvalue = slapi_value_get_string(value); groups[i] = strdup(cvalue); } groups[count] = NULL; } slapi_vattr_values_free(&values, &actual_attr, buffer_flags); } containers = NULL; if (slapi_vattr_values_get(e, SCH_CONTAINER_CONFIGURATION_CONTAINER_ATTR, &values, &disposition, &actual_attr, 0, &buffer_flags) == 0) { count = slapi_valueset_count(values); containers = malloc(sizeof(char *) * (count + 1)); if (containers != NULL) { for (i = slapi_valueset_first_value(values, &value); i != -1; i = slapi_valueset_next_value(values, i, &value)) { cvalue = slapi_value_get_string(value); containers[i] = strdup(cvalue); } containers[count] = NULL; } slapi_vattr_values_free(&values, &actual_attr, buffer_flags); } for (i = 0; (groups != NULL) && (groups[i] != NULL); i++) { for (j = 0; (containers != NULL) && (containers[j] != NULL); j++) { ret = backend_shr_set_config_entry_add_one(state, e, groups[i], containers[j]); } } if (groups != NULL) { for (i = 0; (groups != NULL) && (groups[i] != NULL); i++) { free(groups[i]); } free(groups); } if (containers != NULL) { for (i = 0; (containers != NULL) && (containers[i] != NULL); i++) { free(containers[i]); } free(containers); } return 0; } /* Process a set configuration directory entry. Pull out the domain and map * names which are specified in the entry and delete each in turn. */ int backend_set_config_entry_delete_cb(Slapi_Entry *e, void *callback_data) { struct plugin_state *state; state = callback_data; return backend_shr_set_config_entry_delete(state, e, SCH_CONTAINER_CONFIGURATION_GROUP_ATTR, SCH_CONTAINER_CONFIGURATION_CONTAINER_ATTR); } /* Functions for passing information about a container's configuration to the * formatting functions. */ struct backend_get_set_config_cb { struct plugin_state *state; char **bases; char *entry_filter; }; void backend_free_set_config(char **bases, char *entry_filter) { int i; if (bases != NULL) { for (i = 0; bases[i] != NULL; i++) { free(bases[i]); } free(bases); } free(entry_filter); } static bool_t backend_get_set_config_entry_cb(Slapi_Entry *e, void *callback_data) { Slapi_ValueSet *values; Slapi_Value *value; struct backend_get_set_config_cb *cbdata; char *actual_attr; const char *cvalue; int disposition, buffer_flags, i, count, len; cbdata = callback_data; slapi_log_error(SLAPI_LOG_PLUGIN, cbdata->state->plugin_desc->spd_id, "reading container configuration from \"%s\"\n", slapi_entry_get_ndn(e)); values = NULL; value = NULL; if (slapi_vattr_values_get(e, SCH_CONTAINER_CONFIGURATION_BASE_ATTR, &values, &disposition, &actual_attr, 0, &buffer_flags) == 0) { count = slapi_valueset_count(values); cbdata->bases = malloc(sizeof(char *) * (count + 1)); if (cbdata->bases != NULL) { for (i = slapi_valueset_first_value(values, &value); i != -1; i = slapi_valueset_next_value(values, i, &value)) { cvalue = slapi_value_get_string(value); cbdata->bases[i] = strdup(cvalue); } cbdata->bases[count] = NULL; } slapi_vattr_values_free(&values, &actual_attr, buffer_flags); } if (slapi_vattr_values_get(e, SCH_CONTAINER_CONFIGURATION_FILTER_ATTR, &values, &disposition, &actual_attr, 0, &buffer_flags) == 0) { if (slapi_valueset_first_value(values, &value) != -1) { cvalue = slapi_value_get_string(value); if (cvalue != NULL) { free(cbdata->entry_filter); cbdata->entry_filter = NULL; if ((cvalue[0] != '(') || (cvalue[strlen(cvalue) - 1] != ')')) { len = strlen(cvalue) + 3; cbdata->entry_filter = malloc(len); if (cbdata->entry_filter != NULL) { sprintf(cbdata->entry_filter, "(%s)", cvalue); } } if (cbdata->entry_filter == NULL) { cbdata->entry_filter = strdup(cvalue); } } } slapi_vattr_values_free(&values, &actual_attr, buffer_flags); } return TRUE; } void backend_get_set_config(struct plugin_state *state, const char *group, const char *container, char ***bases, char **entry_filter) { Slapi_PBlock *pb; char *filter; char *attrs[] = {SCH_CONTAINER_CONFIGURATION_FILTER_ATTR, SCH_CONTAINER_CONFIGURATION_BASE_ATTR, NULL}; struct backend_get_set_config_cb cbdata; /* Build the search filter. */ filter = malloc(strlen("(&(" SCH_CONTAINER_CONFIGURATION_GROUP_ATTR "=)(" SCH_CONTAINER_CONFIGURATION_CONTAINER_ATTR "=)" "()") + strlen(group) + strlen(container) + strlen(SCH_CONTAINER_CONFIGURATION_FILTER) + 1); if (filter == NULL) { slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "out of memory reading configuration for " "\"%s\"/\"%s\"!\n", group, container); return; } sprintf(filter, "(&(" SCH_CONTAINER_CONFIGURATION_GROUP_ATTR "=%s)(" SCH_CONTAINER_CONFIGURATION_CONTAINER_ATTR "=%s)(%s)", group, container, SCH_CONTAINER_CONFIGURATION_FILTER); /* Perform the search. */ pb = slapi_pblock_new(); slapi_search_internal_set_pb(pb, state->plugin_base, LDAP_SCOPE_SUB, filter, attrs, FALSE, NULL, NULL, state->plugin_identity, 0); cbdata.bases = NULL; cbdata.state = state; cbdata.entry_filter = NULL; slapi_search_internal_callback_pb(pb, &cbdata, NULL, backend_get_set_config_entry_cb, NULL); /* Return the results. */ *bases = cbdata.bases; *entry_filter = cbdata.entry_filter; /* Clean up. */ slapi_pblock_destroy(pb); } /* Given an entry, return true if it describes a compatibility container. */ bool_t backend_entry_is_a_set(struct plugin_state *state, Slapi_PBlock *pb, Slapi_Entry *e) { return backend_shr_entry_is_a_set(state, pb, e, SCH_CONTAINER_CONFIGURATION_FILTER); } /* Re-read plugin-wide settings that may have changed. Nothing to do. */ void backend_update_params(struct plugin_state *state) { } /* Intercept a search request, and if it belongs to one of our compatibility * trees, answer from our cache before letting the default database have a * crack at it. */ struct backend_search_cbdata { Slapi_PBlock *pb; struct plugin_state *state; char *target, *strfilter, **attrs; int scope, sizelimit, timelimit, attrsonly; bool_t check_aci; Slapi_DN *target_dn; Slapi_Filter *filter; bool_t answer; int result; const char *matched, *text; int n_entries; }; static bool_t backend_search_set_cb(const char *domain, const char *map, bool_t secure, const char *key, unsigned int key_len, const char *value, unsigned int value_len, const char *id, int key_index, void *backend_data, void *cb_data) { Slapi_DN *sdn; struct backend_search_cbdata *cbdata; struct backend_entry_data *entry_data; int result; cbdata = cb_data; entry_data = backend_data; sdn = slapi_entry_get_sdn(entry_data->e); /* Scope check. */ if (slapi_sdn_scope_test(sdn, cbdata->target_dn, cbdata->scope) == 0) { /* No match. Return. */ return TRUE; } /* Filter check. */ result = slapi_filter_test(cbdata->pb, entry_data->e, cbdata->filter, cbdata->check_aci); if (result == -1) { /* No match. Return. */ return TRUE; } /* Return an LDAP error, if one was generated. */ if (result != 0) { slapi_log_error(SLAPI_LOG_PLUGIN, cbdata->state->plugin_desc->spd_id, "search got error %d checking %s\n", result, slapi_sdn_get_ndn(sdn)); cbdata->answer = TRUE; if (cbdata->result == 0) { cbdata->result = result; } return TRUE; } /* Return the result entry. */ slapi_log_error(SLAPI_LOG_PLUGIN, cbdata->state->plugin_desc->spd_id, "search matched %s\n", slapi_sdn_get_ndn(sdn)); cbdata->answer = TRUE; slapi_send_ldap_search_entry(cbdata->pb, entry_data->e, NULL, cbdata->attrs, cbdata->attrsonly); cbdata->n_entries++; return TRUE; } static bool_t backend_search_set(const char *group, const char *set, bool_t flag, void *backend_data, void *cb_data) { struct backend_search_cbdata *cbdata; struct backend_set_data *set_data; cbdata = cb_data; set_data = backend_data; cbdata->check_aci = set_data->check_aci; /* First, do a scope test on this set. */ switch (cbdata->scope) { case LDAP_SCOPE_BASE: /* If the container is not the parent of the target, and it is * not itself the target, then we're done with this set. */ if ((slapi_sdn_compare(set_data->container_sdn, cbdata->target_dn) != 0) && (slapi_sdn_isparent(set_data->container_sdn, cbdata->target_dn) != 1)) { return TRUE; } break; case LDAP_SCOPE_ONE: /* If it's not a scope=one search of this set or the group to * which the set belongs, then we're done with this set. */ if ((slapi_sdn_compare(set_data->container_sdn, cbdata->target_dn) != 0) && (slapi_sdn_isparent(cbdata->target_dn, set_data->container_sdn) != 1)) { return TRUE; } break; case LDAP_SCOPE_SUB: /* If the target is not a child of this container, it is not * the container itself, and it is not the group, then we're * done with this set. */ if ((slapi_sdn_isparent(set_data->container_sdn, cbdata->target_dn) != 1) && (slapi_sdn_compare(set_data->container_sdn, cbdata->target_dn) != 0) && (slapi_sdn_isparent(cbdata->target_dn, set_data->container_sdn) != 1)) { return TRUE; } break; } map_data_foreach_entry_id(cbdata->state, group, set, NULL, backend_search_set_cb, cbdata); return TRUE; } static int backend_search_cb(Slapi_PBlock *pb) { struct backend_search_cbdata cbdata; cbdata.pb = pb; slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &cbdata.state); slapi_pblock_get(pb, SLAPI_SEARCH_TARGET, &cbdata.target); cbdata.target_dn = slapi_sdn_new_dn_byval(cbdata.target); slapi_pblock_get(pb, SLAPI_SEARCH_SCOPE, &cbdata.scope); slapi_pblock_get(pb, SLAPI_SEARCH_SIZELIMIT, &cbdata.sizelimit); slapi_pblock_get(pb, SLAPI_SEARCH_TIMELIMIT, &cbdata.timelimit); slapi_pblock_get(pb, SLAPI_SEARCH_FILTER, &cbdata.filter); slapi_pblock_get(pb, SLAPI_SEARCH_STRFILTER, &cbdata.strfilter); slapi_pblock_get(pb, SLAPI_SEARCH_ATTRS, &cbdata.attrs); slapi_pblock_get(pb, SLAPI_SEARCH_ATTRSONLY, &cbdata.attrsonly); slapi_log_error(SLAPI_LOG_PLUGIN, cbdata.state->plugin_desc->spd_id, "searching from \"%s\" for \"%s\" with scope %d\n", cbdata.target, cbdata.strfilter, cbdata.scope); cbdata.answer = FALSE; cbdata.result = 0; cbdata.matched = ""; cbdata.text = ""; cbdata.n_entries = 0; map_data_foreach_map(cbdata.state, NULL, backend_search_set, &cbdata); slapi_sdn_free(&cbdata.target_dn); if (cbdata.answer) { slapi_send_ldap_result(cbdata.pb, cbdata.result, (char *) cbdata.matched, (char *) cbdata.text, cbdata.n_entries, NULL); } return cbdata.answer ? -1 : 0; } /* Populate our data. */ void backend_startup(struct plugin_state *state) { backend_shr_startup(state, SCH_CONTAINER_CONFIGURATION_FILTER); } void backend_init_preop(Slapi_PBlock *pb, struct plugin_state *state) { slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "hooking up preoperation callbacks\n"); if (slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_SEARCH_FN, backend_search_cb) != 0) { slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "error hooking up search callback\n"); } } void backend_init_postop(Slapi_PBlock *pb, struct plugin_state *state) { slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "hooking up postoperation callbacks\n"); backend_shr_postop_init(pb, state); }