version 1.19, 2006/04/04 20:16:24
|
version 1.19.2.2, 2006/08/17 23:50:32
|
Line 1
|
Line 1
|
/* refint.c - referential integrity module */ |
/* refint.c - referential integrity module */ |
/* $OpenLDAP: pkg/ldap/servers/slapd/overlays/refint.c,v 1.18 2006/01/03 22:12:25 kurt Exp $ */ |
/* $OpenLDAP: pkg/ldap/servers/slapd/overlays/refint.c,v 1.19.2.1 2006/05/15 17:04:44 kurt Exp $ */ |
/* This work is part of OpenLDAP Software <http://www.openldap.org/>. |
/* This work is part of OpenLDAP Software <http://www.openldap.org/>. |
* |
* |
* Copyright 2004-2006 The OpenLDAP Foundation. |
* Copyright 2004-2006 The OpenLDAP Foundation. |
Line 26
|
Line 26
|
* DN whenever the DN is changed or its entry is deleted, and making |
* DN whenever the DN is changed or its entry is deleted, and making |
* the appropriate update. |
* the appropriate update. |
* |
* |
* Updates are performed using the database rootdn, but the ModifiersName |
* Updates are performed using the database rootdn in a separate task |
* is always set to refint_dn. |
* to allow the original operation to complete immediately. |
*/ |
*/ |
|
|
#ifdef SLAPD_OVER_REFINT |
#ifdef SLAPD_OVER_REFINT |
Line 38
|
Line 38
|
#include <ac/socket.h> |
#include <ac/socket.h> |
|
|
#include "slap.h" |
#include "slap.h" |
|
#include "config.h" |
|
#include "ldap_rq.h" |
|
|
static slap_overinst refint; |
static slap_overinst refint; |
|
|
/* The DN to use in the ModifiersName for all refint updates */ |
/* The DN to use in the ModifiersName for all refint updates */ |
static BerValue refint_dn = BER_BVC("cn=Referential Integrity Overlay"); |
static BerValue refint_dn = BER_BVC("cn=Referential Integrity Overlay"); |
|
static BerValue refint_ndn = BER_BVC("cn=referential integrity overlay"); |
|
|
typedef struct refint_attrs_s { |
typedef struct refint_attrs_s { |
struct refint_attrs_s *next; |
struct refint_attrs_s *next; |
Line 52 typedef struct refint_attrs_s {
|
Line 55 typedef struct refint_attrs_s {
|
typedef struct dependents_s { |
typedef struct dependents_s { |
struct dependents_s *next; |
struct dependents_s *next; |
BerValue dn; /* target dn */ |
BerValue dn; /* target dn */ |
Modifications *mm; |
BerValue ndn; |
|
refint_attrs *attrs; |
} dependent_data; |
} dependent_data; |
|
|
|
typedef struct refint_q { |
|
struct refint_q *next; |
|
struct refint_data_s *rdata; |
|
dependent_data *attrs; /* entries and attrs returned from callback */ |
|
BackendDB *db; |
|
BerValue olddn; |
|
BerValue oldndn; |
|
BerValue newdn; |
|
BerValue newndn; |
|
} refint_q; |
|
|
typedef struct refint_data_s { |
typedef struct refint_data_s { |
const char *message; /* breadcrumbs */ |
const char *message; /* breadcrumbs */ |
struct refint_attrs_s *attrs; /* list of known attrs */ |
struct refint_attrs_s *attrs; /* list of known attrs */ |
struct dependents_s *mods; /* modifications returned from callback */ |
BerValue dn; /* basedn in parent, */ |
BerValue dn; /* basedn in parent, searchdn in call */ |
|
BerValue newdn; /* replacement value for modrdn callback */ |
|
BerValue nnewdn; /* normalized replacement value */ |
|
BerValue nothing; /* the nothing value, if needed */ |
BerValue nothing; /* the nothing value, if needed */ |
BerValue nnothing; /* normalized nothingness */ |
BerValue nnothing; /* normalized nothingness */ |
|
struct re_s *qtask; |
|
refint_q *qhead; |
|
refint_q *qtail; |
|
ldap_pvt_thread_mutex_t qmutex; |
} refint_data; |
} refint_data; |
|
|
|
#define RUNQ_INTERVAL 36000 /* a long time */ |
|
|
|
enum { |
|
REFINT_ATTRS = 1, |
|
REFINT_NOTHING |
|
}; |
|
|
|
static ConfigDriver refint_cf_gen; |
|
|
|
static ConfigTable refintcfg[] = { |
|
{ "refint_attributes", "attribute...", 2, 0, 0, |
|
ARG_MAGIC|REFINT_ATTRS, refint_cf_gen, |
|
"( OLcfgOvAt:11.1 NAME 'olcRefintAttribute' " |
|
"DESC 'Attributes for referential integrity' " |
|
"SYNTAX OMsDirectoryString )", NULL, NULL }, |
|
{ "refint_nothing", "string", 2, 2, 0, |
|
ARG_DN|ARG_MAGIC|REFINT_NOTHING, refint_cf_gen, |
|
"( OLcfgOvAt:11.2 NAME 'olcRefintNothing' " |
|
"DESC 'Replacement DN to supply when needed' " |
|
"SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL }, |
|
{ NULL, NULL, 0, 0, 0, ARG_IGNORED } |
|
}; |
|
|
|
static ConfigOCs refintocs[] = { |
|
{ "( OLcfgOvOc:11.1 " |
|
"NAME 'olcRefintConfig' " |
|
"DESC 'Referential integrity configuration' " |
|
"SUP olcOverlayConfig " |
|
"MAY ( olcRefintAttribute $ olcRefintNothing ) )", |
|
Cft_Overlay, refintcfg }, |
|
{ NULL, 0, NULL } |
|
}; |
|
|
|
static int |
|
refint_cf_gen(ConfigArgs *c) |
|
{ |
|
slap_overinst *on = (slap_overinst *)c->bi; |
|
refint_data *dd = (refint_data *)on->on_bi.bi_private; |
|
refint_attrs *ip, *pip, **pipp = NULL; |
|
AttributeDescription *ad; |
|
const char *text; |
|
int rc = ARG_BAD_CONF; |
|
int i; |
|
|
|
switch ( c->op ) { |
|
case SLAP_CONFIG_EMIT: |
|
switch ( c->type ) { |
|
case REFINT_ATTRS: |
|
ip = dd->attrs; |
|
while ( ip ) { |
|
value_add_one( &c->rvalue_vals, |
|
&ip->attr->ad_cname ); |
|
ip = ip->next; |
|
} |
|
rc = 0; |
|
break; |
|
case REFINT_NOTHING: |
|
if ( !BER_BVISEMPTY( &dd->nothing )) { |
|
rc = value_add_one( &c->rvalue_vals, |
|
&dd->nothing ); |
|
if ( rc ) return rc; |
|
rc = value_add_one( &c->rvalue_nvals, |
|
&dd->nnothing ); |
|
return rc; |
|
} |
|
rc = 0; |
|
break; |
|
default: |
|
abort (); |
|
} |
|
break; |
|
case LDAP_MOD_DELETE: |
|
switch ( c->type ) { |
|
case REFINT_ATTRS: |
|
pipp = &dd->attrs; |
|
if ( c->valx < 0 ) { |
|
ip = *pipp; |
|
*pipp = NULL; |
|
while ( ip ) { |
|
pip = ip; |
|
ip = ip->next; |
|
ch_free ( pip ); |
|
} |
|
} else { |
|
/* delete from linked list */ |
|
for ( i=0; i < c->valx; ++i ) { |
|
pipp = &(*pipp)->next; |
|
} |
|
ip = *pipp; |
|
*pipp = (*pipp)->next; |
|
|
|
/* AttributeDescriptions are global so |
|
* shouldn't be freed here... */ |
|
ch_free ( ip ); |
|
} |
|
rc = 0; |
|
break; |
|
case REFINT_NOTHING: |
|
if ( dd->nothing.bv_val ) |
|
ber_memfree ( dd->nothing.bv_val ); |
|
if ( dd->nnothing.bv_val ) |
|
ber_memfree ( dd->nnothing.bv_val ); |
|
dd->nothing.bv_len = 0; |
|
dd->nnothing.bv_len = 0; |
|
rc = 0; |
|
break; |
|
default: |
|
abort (); |
|
} |
|
break; |
|
case SLAP_CONFIG_ADD: |
|
/* fallthrough to LDAP_MOD_ADD */ |
|
case LDAP_MOD_ADD: |
|
switch ( c->type ) { |
|
case REFINT_ATTRS: |
|
rc = 0; |
|
for ( i=1; i < c->argc; ++i ) { |
|
ad = NULL; |
|
if ( slap_str2ad ( c->argv[i], &ad, &text ) |
|
== LDAP_SUCCESS) { |
|
ip = ch_malloc ( |
|
sizeof ( refint_attrs ) ); |
|
ip->attr = ad; |
|
ip->next = dd->attrs; |
|
dd->attrs = ip; |
|
} else { |
|
Debug ( LDAP_DEBUG_CONFIG, |
|
"refint add: <%s>: %s\n", |
|
c->argv[i], text, NULL ); |
|
strncpy ( c->msg, |
|
text, |
|
SLAP_TEXT_BUFLEN-1 ); |
|
c->msg[SLAP_TEXT_BUFLEN-1] = '\0'; |
|
rc = ARG_BAD_CONF; |
|
} |
|
} |
|
break; |
|
case REFINT_NOTHING: |
|
if ( dd->nothing.bv_val ) |
|
ber_memfree ( dd->nothing.bv_val ); |
|
if ( dd->nnothing.bv_val ) |
|
ber_memfree ( dd->nnothing.bv_val ); |
|
dd->nothing = c->value_dn; |
|
dd->nnothing = c->value_ndn; |
|
rc = 0; |
|
break; |
|
default: |
|
abort (); |
|
} |
|
break; |
|
default: |
|
abort (); |
|
} |
|
|
|
return rc; |
|
} |
|
|
/* |
/* |
** allocate new refint_data; |
** allocate new refint_data; |
** initialize, copy basedn; |
|
** store in on_bi.bi_private; |
** store in on_bi.bi_private; |
** |
** |
*/ |
*/ |
Line 79 refint_db_init(
|
Line 251 refint_db_init(
|
) |
) |
{ |
{ |
slap_overinst *on = (slap_overinst *)be->bd_info; |
slap_overinst *on = (slap_overinst *)be->bd_info; |
refint_data *id = ch_malloc(sizeof(refint_data)); |
refint_data *id = ch_calloc(1,sizeof(refint_data)); |
|
|
id->message = "_init"; |
id->message = "_init"; |
id->attrs = NULL; |
|
id->newdn.bv_val = NULL; |
|
id->nothing.bv_val = NULL; |
|
id->nnothing.bv_val = NULL; |
|
ber_dupbv( &id->dn, &be->be_nsuffix[0] ); |
|
on->on_bi.bi_private = id; |
on->on_bi.bi_private = id; |
|
ldap_pvt_thread_mutex_init( &id->qmutex ); |
return(0); |
return(0); |
} |
} |
|
|
|
|
/* |
|
** if command = attributes: |
|
** foreach argument: |
|
** convert to attribute; |
|
** add to configured attribute list; |
|
** elseif command = basedn: |
|
** set our basedn to argument; |
|
** |
|
*/ |
|
|
|
static int |
static int |
refint_config( |
refint_db_destroy( |
BackendDB *be, |
BackendDB *be |
const char *fname, |
|
int lineno, |
|
int argc, |
|
char **argv |
|
) |
) |
{ |
{ |
slap_overinst *on = (slap_overinst *) be->bd_info; |
slap_overinst *on = (slap_overinst *)be->bd_info; |
refint_data *id = on->on_bi.bi_private; |
|
refint_attrs *ip; |
|
const char *text; |
|
AttributeDescription *ad; |
|
BerValue dn; |
|
int i; |
|
|
|
if(!strcasecmp(*argv, "refint_attributes")) { |
if ( on->on_bi.bi_private ) { |
for(i = 1; i < argc; i++) { |
refint_data *id = on->on_bi.bi_private; |
for(ip = id->attrs; ip; ip = ip->next) |
on->on_bi.bi_private = NULL; |
if(!strcmp(argv[i], ip->attr->ad_cname.bv_val)) { |
ldap_pvt_thread_mutex_destroy( &id->qmutex ); |
Debug(LDAP_DEBUG_ANY, |
ch_free( id ); |
"%s: line %d: duplicate attribute <s>, ignored\n", |
|
fname, lineno, argv[i]); |
|
continue; |
|
} |
|
ad = NULL; |
|
if(slap_str2ad(argv[i], &ad, &text) != LDAP_SUCCESS) { |
|
Debug(LDAP_DEBUG_ANY, |
|
"%s: line %d: bad attribute <%s>, ignored\n", |
|
fname, lineno, text); |
|
continue; /* XXX */ |
|
} else if(ad->ad_next) { |
|
Debug(LDAP_DEBUG_ANY, |
|
"%s: line %d: multiple attributes match <%s>, ignored\n", |
|
fname, lineno, argv[i]); |
|
continue; |
|
} |
|
ip = ch_malloc(sizeof(refint_attrs)); |
|
ip->attr = ad; |
|
ip->next = id->attrs; |
|
id->attrs = ip; |
|
Debug(LDAP_DEBUG_ANY, "%s: line %d: new attribute <%s>\n", |
|
fname, lineno, argv[i]); |
|
} |
|
} else if(!strcasecmp(*argv, "refint_base")) { |
|
/* XXX only one basedn (yet) - need validate argument! */ |
|
if(id->dn.bv_val) ch_free(id->dn.bv_val); |
|
ber_str2bv( argv[1], 0, 0, &dn ); |
|
Debug(LDAP_DEBUG_ANY, "%s: line %d: new baseDN <%s>\n", |
|
fname, lineno, argv[1]); |
|
if(dnNormalize(0, NULL, NULL, &dn, &id->dn, NULL)) { |
|
Debug(LDAP_DEBUG_ANY, "%s: line %d: bad baseDN!\n", fname, lineno, 0); |
|
return(1); |
|
} |
|
} else if(!strcasecmp(*argv, "refint_nothing")) { |
|
if(id->nothing.bv_val) ch_free(id->nothing.bv_val); |
|
if(id->nnothing.bv_val) ch_free(id->nnothing.bv_val); |
|
ber_str2bv( argv[1], 0, 1, &id->nothing ); |
|
if(dnNormalize(0, NULL, NULL, &id->nothing, &id->nnothing, NULL)) { |
|
Debug(LDAP_DEBUG_ANY, "%s: line %d: bad nothingDN!\n", fname, lineno, 0); |
|
return(1); |
|
} |
|
Debug(LDAP_DEBUG_ANY, "%s: line %d: new nothingDN<%s>\n", |
|
fname, lineno, argv[1]); |
|
} else { |
|
return(SLAP_CONF_UNKNOWN); |
|
} |
} |
|
|
id->message = "_config"; |
|
return(0); |
return(0); |
} |
} |
|
|
|
|
/* |
/* |
** nothing really happens here; |
** initialize, copy basedn if not already set |
** |
** |
*/ |
*/ |
|
|
Line 189 refint_open(
|
Line 288 refint_open(
|
slap_overinst *on = (slap_overinst *)be->bd_info; |
slap_overinst *on = (slap_overinst *)be->bd_info; |
refint_data *id = on->on_bi.bi_private; |
refint_data *id = on->on_bi.bi_private; |
id->message = "_open"; |
id->message = "_open"; |
|
|
|
if ( BER_BVISNULL( &id->dn )) { |
|
if ( BER_BVISNULL( &be->be_nsuffix[0] )) |
|
return -1; |
|
ber_dupbv( &id->dn, &be->be_nsuffix[0] ); |
|
} |
return(0); |
return(0); |
} |
} |
|
|
Line 222 refint_close(
|
Line 327 refint_close(
|
ch_free(id->nothing.bv_val); |
ch_free(id->nothing.bv_val); |
ch_free(id->nnothing.bv_val); |
ch_free(id->nnothing.bv_val); |
|
|
on->on_bi.bi_private = NULL; /* XXX */ |
memset( id, 0, sizeof(*id)); |
|
|
ch_free(id); |
|
|
|
return(0); |
return(0); |
} |
} |
|
|
/* |
/* |
** delete callback |
** search callback |
** generates a list of Modification* from search results |
** generates a list of Attributes from search results |
*/ |
*/ |
|
|
static int |
static int |
refint_delete_cb( |
refint_search_cb( |
Operation *op, |
Operation *op, |
SlapReply *rs |
SlapReply *rs |
) |
) |
{ |
{ |
Attribute *a; |
Attribute *a; |
BerVarray b = NULL; |
BerVarray b = NULL; |
refint_data *dd = op->o_callback->sc_private; |
refint_q *rq = op->o_callback->sc_private; |
refint_attrs *ia, *da = dd->attrs; |
refint_data *dd = rq->rdata; |
|
refint_attrs *ia, *da = dd->attrs, *na; |
dependent_data *ip; |
dependent_data *ip; |
Modifications *mp, *ma; |
|
int i; |
int i; |
|
|
Debug(LDAP_DEBUG_TRACE, "refint_delete_cb <%s>\n", |
Debug(LDAP_DEBUG_TRACE, "refint_search_cb <%s>\n", |
rs->sr_entry ? rs->sr_entry->e_name.bv_val : "NOTHING", 0, 0); |
rs->sr_entry ? rs->sr_entry->e_name.bv_val : "NOTHING", 0, 0); |
|
|
if (rs->sr_type != REP_SEARCH || !rs->sr_entry) return(0); |
if (rs->sr_type != REP_SEARCH || !rs->sr_entry) return(0); |
dd->message = "_delete_cb"; |
|
|
|
/* |
/* |
** foreach configured attribute type: |
** foreach configured attribute type: |
** if this attr exists in the search result, |
** if this attr exists in the search result, |
** and it has a value matching the target: |
** and it has a value matching the target: |
** allocate a Modification; |
** allocate an attr; |
** allocate its array of 2 BerValues; |
** if this is a delete and there's only one value: |
** if only one value, and we have a configured Nothing: |
** allocate the same attr again; |
** allocate additional Modification |
|
** type = MOD_ADD |
|
** BerValues[] = { Nothing, NULL }; |
|
** add to list |
|
** type = MOD_DELETE |
|
** BerValues[] = { our target dn, NULL }; |
|
** add this mod to the list of mods; |
|
** |
** |
*/ |
*/ |
|
|
ip = ch_malloc(sizeof(dependent_data)); |
ip = op->o_tmpalloc(sizeof(dependent_data), op->o_tmpmemctx ); |
ip->dn.bv_val = NULL; |
ber_dupbv_x( &ip->dn, &rs->sr_entry->e_name, op->o_tmpmemctx ); |
ip->next = NULL; |
ber_dupbv_x( &ip->ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx ); |
ip->mm = NULL; |
ip->next = rq->attrs; |
ma = NULL; |
rq->attrs = ip; |
|
ip->attrs = NULL; |
for(ia = da; ia; ia = ia->next) { |
for(ia = da; ia; ia = ia->next) { |
if ( (a = attr_find(rs->sr_entry->e_attrs, ia->attr) ) ) |
if ( (a = attr_find(rs->sr_entry->e_attrs, ia->attr) ) ) |
for(i = 0, b = a->a_nvals; b[i].bv_val; i++) |
for(i = 0, b = a->a_nvals; b[i].bv_val; i++) |
if(bvmatch(&dd->dn, &b[i])) { |
if(bvmatch(&rq->oldndn, &b[i])) { |
if(!ip->dn.bv_val) ber_dupbv(&ip->dn, &rs->sr_entry->e_nname); |
na = op->o_tmpalloc(sizeof( refint_attrs ), op->o_tmpmemctx ); |
if(!b[1].bv_val && dd->nothing.bv_val) { |
na->next = ip->attrs; |
mp = ch_malloc(sizeof(Modifications)); |
ip->attrs = na; |
mp->sml_desc = ia->attr; /* XXX */ |
na->attr = ia->attr; |
mp->sml_type = a->a_desc->ad_cname; |
/* If this is a delete and there's only one value, and |
mp->sml_values = ch_malloc(2 * sizeof(BerValue)); |
* we have a nothing DN configured, allocate the attr again. |
mp->sml_nvalues = ch_malloc(2 * sizeof(BerValue)); |
*/ |
mp->sml_values[1].bv_len = mp->sml_nvalues[1].bv_len = 0; |
if(!b[1].bv_val && BER_BVISEMPTY( &rq->newdn ) && |
mp->sml_values[1].bv_val = mp->sml_nvalues[1].bv_val = NULL; |
dd->nothing.bv_val) { |
|
na = op->o_tmpalloc(sizeof( refint_attrs ), op->o_tmpmemctx ); |
mp->sml_op = LDAP_MOD_ADD; |
na->next = ip->attrs; |
mp->sml_flags = 0; |
ip->attrs = na; |
ber_dupbv(&mp->sml_values[0], &dd->nothing); |
na->attr = ia->attr; |
ber_dupbv(&mp->sml_nvalues[0], &dd->nnothing); |
|
mp->sml_next = ma; |
|
ma = mp; |
|
} |
} |
/* this might violate the object class */ |
Debug(LDAP_DEBUG_TRACE, "refint_search_cb: %s: %s\n", |
mp = ch_malloc(sizeof(Modifications)); |
a->a_desc->ad_cname.bv_val, rq->olddn.bv_val, 0); |
mp->sml_desc = ia->attr; /* XXX */ |
|
mp->sml_type = a->a_desc->ad_cname; |
|
mp->sml_values = ch_malloc(2 * sizeof(BerValue)); |
|
mp->sml_nvalues = ch_malloc(2 * sizeof(BerValue)); |
|
mp->sml_values[1].bv_len = mp->sml_nvalues[1].bv_len = 0; |
|
mp->sml_values[1].bv_val = mp->sml_nvalues[1].bv_val = NULL; |
|
mp->sml_op = LDAP_MOD_DELETE; |
|
mp->sml_flags = 0; |
|
ber_dupbv(&mp->sml_values[0], &dd->dn); |
|
ber_dupbv(&mp->sml_nvalues[0], &mp->sml_values[0]); |
|
mp->sml_next = ma; |
|
ma = mp; |
|
Debug(LDAP_DEBUG_TRACE, "refint_delete_cb: %s: %s\n", |
|
a->a_desc->ad_cname.bv_val, dd->dn.bv_val, 0); |
|
break; |
break; |
} |
} |
} |
} |
ip->mm = ma; |
|
ip->next = dd->mods; |
|
dd->mods = ip; |
|
|
|
return(0); |
return(0); |
} |
} |
|
|
/* |
static void * |
** null callback |
refint_qtask( void *ctx, void *arg ) |
** does nothing |
|
*/ |
|
|
|
static int |
|
refint_null_cb( |
|
Operation *op, |
|
SlapReply *rs |
|
) |
|
{ |
{ |
((refint_data *)op->o_callback->sc_private)->message = "_null_cb"; |
struct re_s *rtask = arg; |
return(LDAP_SUCCESS); |
refint_data *id = rtask->arg; |
} |
Connection conn = {0}; |
|
OperationBuffer opbuf; |
/* |
Operation *op; |
** modrdn callback |
SlapReply rs = {REP_RESULT}; |
** generates a list of Modification* from search results |
slap_callback cb = { NULL, NULL, NULL, NULL }; |
*/ |
Filter ftop, *fptr; |
|
refint_q *rq; |
static int |
dependent_data *dp; |
refint_modrdn_cb( |
refint_attrs *ra, *ip; |
Operation *op, |
int rc; |
SlapReply *rs |
|
) |
|
{ |
|
Attribute *a; |
|
BerVarray b = NULL; |
|
refint_data *dd = op->o_callback->sc_private; |
|
refint_attrs *ia, *da = dd->attrs; |
|
dependent_data *ip = NULL; |
|
Modifications *mp; |
|
int i, fix; |
|
|
|
Debug(LDAP_DEBUG_TRACE, "refint_modrdn_cb <%s>\n", |
|
rs->sr_entry ? rs->sr_entry->e_name.bv_val : "NOTHING", 0, 0); |
|
|
|
if (rs->sr_type != REP_SEARCH || !rs->sr_entry) return(0); |
op = (Operation *) &opbuf; |
dd->message = "_modrdn_cb"; |
connection_fake_init( &conn, op, ctx ); |
|
|
/* |
/* |
** foreach configured attribute type: |
** build a search filter for all configured attributes; |
** if this attr exists in the search result, |
** populate our Operation; |
** and it has a value matching the target: |
** pass our data (attr list, dn) to backend via sc_private; |
** allocate a pair of Modifications; |
** call the backend search function; |
** make it MOD_ADD the new value and MOD_DELETE the old; |
** nb: (|(one=thing)) is valid, but do smart formatting anyway; |
** allocate its array of BerValues; |
** nb: 16 is arbitrarily a dozen or so extra bytes; |
** foreach value in the search result: |
|
** if it matches our target value, replace it; |
|
** otherwise, copy from the search result; |
|
** terminate the array of BerValues; |
|
** add these mods to the list of mods; |
|
** |
** |
*/ |
*/ |
|
|
for(ia = da; ia; ia = ia->next) { |
ftop.f_choice = LDAP_FILTER_OR; |
if((a = attr_find(rs->sr_entry->e_attrs, ia->attr))) { |
ftop.f_next = NULL; |
for(fix = 0, i = 0, b = a->a_nvals; b[i].bv_val; i++) |
ftop.f_or = NULL; |
if(bvmatch(&dd->dn, &b[i])) { fix++; break; } |
op->ors_filter = &ftop; |
if(fix) { |
for(ip = id->attrs; ip; ip = ip->next) { |
if (!ip) { |
fptr = op->o_tmpalloc( sizeof(Filter) + sizeof(AttributeAssertion), |
ip = ch_malloc(sizeof(dependent_data)); |
op->o_tmpmemctx ); |
ip->next = NULL; |
fptr->f_choice = LDAP_FILTER_EQUALITY; |
ip->mm = NULL; |
fptr->f_ava = (AttributeAssertion *)(fptr+1); |
ber_dupbv(&ip->dn, &rs->sr_entry->e_nname); |
fptr->f_ava->aa_desc = ip->attr; |
|
fptr->f_next = ftop.f_or; |
|
ftop.f_or = fptr; |
|
} |
|
|
|
for (;;) { |
|
/* Dequeue an op */ |
|
ldap_pvt_thread_mutex_lock( &id->qmutex ); |
|
rq = id->qhead; |
|
if ( rq ) { |
|
id->qhead = rq->next; |
|
if ( !id->qhead ) |
|
id->qtail = NULL; |
|
} |
|
ldap_pvt_thread_mutex_unlock( &id->qmutex ); |
|
if ( !rq ) |
|
break; |
|
|
|
for (fptr = ftop.f_or; fptr; fptr=fptr->f_next ) |
|
fptr->f_av_value = rq->oldndn; |
|
|
|
filter2bv_x( op, op->ors_filter, &op->ors_filterstr ); |
|
|
|
/* callback gets the searched dn instead */ |
|
cb.sc_private = rq; |
|
cb.sc_response = refint_search_cb; |
|
op->o_callback = &cb; |
|
op->o_tag = LDAP_REQ_SEARCH; |
|
op->ors_scope = LDAP_SCOPE_SUBTREE; |
|
op->ors_deref = LDAP_DEREF_NEVER; |
|
op->ors_limit = NULL; |
|
op->ors_slimit = SLAP_NO_LIMIT; |
|
op->ors_tlimit = SLAP_NO_LIMIT; |
|
|
|
/* no attrs! */ |
|
op->ors_attrs = slap_anlist_no_attrs; |
|
|
|
op->o_req_ndn = id->dn; |
|
op->o_req_dn = id->dn; |
|
op->o_bd = rq->db; |
|
op->o_dn = op->o_bd->be_rootdn; |
|
op->o_ndn = op->o_bd->be_rootndn; |
|
slap_op_time( &op->o_time, &op->o_tincr ); |
|
|
|
/* search */ |
|
rc = op->o_bd->be_search(op, &rs); |
|
|
|
op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx ); |
|
|
|
if(rc != LDAP_SUCCESS) { |
|
Debug( LDAP_DEBUG_TRACE, |
|
"refint_response: search failed: %d\n", |
|
rc, 0, 0 ); |
|
continue; |
|
} |
|
|
|
/* safety? paranoid just in case */ |
|
if(!cb.sc_private) { |
|
Debug( LDAP_DEBUG_TRACE, |
|
"refint_response: callback wiped out sc_private?!\n", |
|
0, 0, 0 ); |
|
continue; |
|
} |
|
|
|
/* Set up the Modify requests */ |
|
cb.sc_response = &slap_null_cb; |
|
op->o_tag = LDAP_REQ_MODIFY; |
|
|
|
/* |
|
** [our search callback builds a list of attrs] |
|
** foreach attr: |
|
** make sure its dn has a backend; |
|
** build Modification* chain; |
|
** call the backend modify function; |
|
** |
|
*/ |
|
|
|
for(dp = rq->attrs; dp; dp = dp->next) { |
|
Modifications *m, *first = NULL; |
|
|
|
op->orm_modlist = NULL; |
|
|
|
op->o_req_dn = dp->dn; |
|
op->o_req_ndn = dp->ndn; |
|
op->o_bd = select_backend(&dp->ndn, 0, 1); |
|
if(!op->o_bd) { |
|
Debug( LDAP_DEBUG_TRACE, |
|
"refint_response: no backend for DN %s!\n", |
|
dp->dn.bv_val, 0, 0 ); |
|
goto done; |
} |
} |
mp = ch_malloc(sizeof(Modifications)); |
rs.sr_type = REP_RESULT; |
mp->sml_op = LDAP_MOD_ADD; |
for (ra = dp->attrs; ra; ra = dp->attrs) { |
mp->sml_flags = 0; |
dp->attrs = ra->next; |
mp->sml_desc = ia->attr; /* XXX */ |
/* Set our ModifiersName */ |
mp->sml_type = ia->attr->ad_cname; |
if ( SLAP_LASTMOD( op->o_bd )) { |
mp->sml_values = ch_malloc(2 * sizeof(BerValue)); |
m = op->o_tmpalloc( sizeof(Modifications) + |
mp->sml_nvalues = ch_malloc(2 * sizeof(BerValue)); |
4*sizeof(BerValue), op->o_tmpmemctx ); |
ber_dupbv(&mp->sml_values[0], &dd->newdn); |
m->sml_next = op->orm_modlist; |
ber_dupbv(&mp->sml_nvalues[0], &dd->nnewdn); |
if ( !first ) |
mp->sml_values[1].bv_len = mp->sml_nvalues[1].bv_len = 0; |
first = m; |
mp->sml_values[1].bv_val = mp->sml_nvalues[1].bv_val = NULL; |
op->orm_modlist = m; |
mp->sml_next = ip->mm; |
m->sml_op = LDAP_MOD_REPLACE; |
ip->mm = mp; |
m->sml_flags = SLAP_MOD_INTERNAL; |
mp = ch_malloc(sizeof(Modifications)); |
m->sml_desc = slap_schema.si_ad_modifiersName; |
mp->sml_op = LDAP_MOD_DELETE; |
m->sml_type = m->sml_desc->ad_cname; |
mp->sml_flags = 0; |
m->sml_values = (BerVarray)(m+1); |
mp->sml_desc = ia->attr; /* XXX */ |
m->sml_nvalues = m->sml_values+2; |
mp->sml_type = ia->attr->ad_cname; |
BER_BVZERO( &m->sml_values[1] ); |
mp->sml_values = ch_malloc(2 * sizeof(BerValue)); |
BER_BVZERO( &m->sml_nvalues[1] ); |
mp->sml_nvalues = ch_malloc(2 * sizeof(BerValue)); |
m->sml_values[0] = refint_dn; |
ber_dupbv(&mp->sml_values[0], &dd->dn); |
m->sml_nvalues[0] = refint_ndn; |
ber_dupbv(&mp->sml_nvalues[0], &dd->dn); |
} |
mp->sml_values[1].bv_len = mp->sml_nvalues[1].bv_len = 0; |
if ( !BER_BVISEMPTY( &rq->newdn ) || ( ra->next && |
mp->sml_values[1].bv_val = mp->sml_nvalues[1].bv_val = NULL; |
ra->attr == ra->next->attr )) { |
mp->sml_next = ip->mm; |
m = op->o_tmpalloc( sizeof(Modifications) + |
ip->mm = mp; |
4*sizeof(BerValue), op->o_tmpmemctx ); |
Debug(LDAP_DEBUG_TRACE, "refint_modrdn_cb: %s: %s\n", |
m->sml_next = op->orm_modlist; |
a->a_desc->ad_cname.bv_val, dd->dn.bv_val, 0); |
if ( !first ) |
|
first = m; |
|
op->orm_modlist = m; |
|
m->sml_op = LDAP_MOD_ADD; |
|
m->sml_flags = 0; |
|
m->sml_desc = ra->attr; |
|
m->sml_type = ra->attr->ad_cname; |
|
m->sml_values = (BerVarray)(m+1); |
|
m->sml_nvalues = m->sml_values+2; |
|
BER_BVZERO( &m->sml_values[1] ); |
|
BER_BVZERO( &m->sml_nvalues[1] ); |
|
if ( BER_BVISEMPTY( &rq->newdn )) { |
|
op->o_tmpfree( ra, op->o_tmpmemctx ); |
|
ra = dp->attrs; |
|
dp->attrs = ra->next; |
|
m->sml_values[0] = id->nothing; |
|
m->sml_nvalues[0] = id->nnothing; |
|
} else { |
|
m->sml_values[0] = rq->newdn; |
|
m->sml_nvalues[0] = rq->newndn; |
|
} |
|
} |
|
m = op->o_tmpalloc( sizeof(Modifications) + 4*sizeof(BerValue), |
|
op->o_tmpmemctx ); |
|
m->sml_next = op->orm_modlist; |
|
op->orm_modlist = m; |
|
if ( !first ) |
|
first = m; |
|
m->sml_op = LDAP_MOD_DELETE; |
|
m->sml_flags = 0; |
|
m->sml_desc = ra->attr; |
|
m->sml_type = ra->attr->ad_cname; |
|
m->sml_values = (BerVarray)(m+1); |
|
m->sml_nvalues = m->sml_values+2; |
|
m->sml_values[0] = rq->olddn; |
|
m->sml_nvalues[0] = rq->oldndn; |
|
BER_BVZERO( &m->sml_values[1] ); |
|
BER_BVZERO( &m->sml_nvalues[1] ); |
|
op->o_tmpfree( ra, op->o_tmpmemctx ); |
|
} |
|
|
|
op->o_dn = op->o_bd->be_rootdn; |
|
op->o_ndn = op->o_bd->be_rootndn; |
|
slap_op_time( &op->o_time, &op->o_tincr ); |
|
if((rc = op->o_bd->be_modify(op, &rs)) != LDAP_SUCCESS) { |
|
Debug( LDAP_DEBUG_TRACE, |
|
"refint_response: dependent modify failed: %d\n", |
|
rs.sr_err, 0, 0 ); |
|
} |
|
|
|
while (( m = op->orm_modlist )) { |
|
op->orm_modlist = m->sml_next; |
|
op->o_tmpfree( m, op->o_tmpmemctx ); |
|
if ( m == first ) break; |
|
} |
|
slap_mods_free( op->orm_modlist, 1 ); |
|
op->o_tmpfree( dp->ndn.bv_val, op->o_tmpmemctx ); |
|
op->o_tmpfree( dp->dn.bv_val, op->o_tmpmemctx ); |
|
op->o_tmpfree( dp, op->o_tmpmemctx ); |
} |
} |
} |
done: |
} |
if ( !BER_BVISNULL( &rq->newndn )) { |
if (ip) { |
ch_free( rq->newndn.bv_val ); |
ip->next = dd->mods; |
ch_free( rq->newdn.bv_val ); |
dd->mods = ip; |
} |
|
ch_free( rq->oldndn.bv_val ); |
|
ch_free( rq->olddn.bv_val ); |
|
ch_free( rq ); |
} |
} |
|
|
return(0); |
/* wait until we get explicitly scheduled again */ |
} |
ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex ); |
|
ldap_pvt_runqueue_stoptask( &slapd_rq, id->qtask ); |
|
ldap_pvt_runqueue_resched( &slapd_rq,id->qtask, 1 ); |
|
ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex ); |
|
|
|
return NULL; |
|
} |
|
|
/* |
/* |
** refint_response |
** refint_response |
Line 440 refint_response(
|
Line 644 refint_response(
|
SlapReply *rs |
SlapReply *rs |
) |
) |
{ |
{ |
Operation nop = *op; |
|
SlapReply nrs = { REP_RESULT }; |
|
slap_callback cb = { NULL, NULL, NULL, NULL }; |
|
slap_callback cb2 = { NULL, slap_replog_cb, NULL, NULL }; |
|
slap_callback *cbo, *cbp; |
|
slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; |
slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; |
refint_data *id = on->on_bi.bi_private; |
refint_data *id = on->on_bi.bi_private; |
refint_data dd = *id; |
|
refint_attrs *ip; |
|
dependent_data *dp; |
|
BerValue pdn; |
BerValue pdn; |
int rc, ac; |
int rc, ac; |
Filter ftop, *fptr; |
refint_q *rq; |
|
BackendDB *db; |
|
refint_attrs *ip; |
|
|
id->message = "_refint_response"; |
id->message = "_refint_response"; |
|
|
Line 479 refint_response(
|
Line 677 refint_response(
|
** |
** |
*/ |
*/ |
|
|
nop.o_bd = select_backend(&id->dn, 0, 1); |
db = select_backend(&id->dn, 0, 1); |
|
|
if(nop.o_bd) { |
if(db) { |
if (!nop.o_bd->be_search || !nop.o_bd->be_modify) { |
if (!db->be_search || !db->be_modify) { |
Debug( LDAP_DEBUG_TRACE, |
Debug( LDAP_DEBUG_TRACE, |
"refint_response: backend missing search and/or modify\n", |
"refint_response: backend missing search and/or modify\n", |
0, 0, 0 ); |
0, 0, 0 ); |
Line 495 refint_response(
|
Line 693 refint_response(
|
return SLAP_CB_CONTINUE; |
return SLAP_CB_CONTINUE; |
} |
} |
|
|
cb2.sc_next = &cb; |
rq = ch_calloc( 1, sizeof( refint_q )); |
|
ber_dupbv( &rq->olddn, &op->o_req_dn ); |
|
ber_dupbv( &rq->oldndn, &op->o_req_ndn ); |
|
rq->db = db; |
|
rq->rdata = id; |
|
|
/* |
if(op->o_tag == LDAP_REQ_MODRDN) { |
** if delete: set delete callback; |
|
** else modrdn: create a newdn, set modify callback; |
|
** |
|
*/ |
|
|
|
if(op->o_tag == LDAP_REQ_DELETE) { |
|
cb.sc_response = &refint_delete_cb; |
|
dd.newdn.bv_val = NULL; |
|
dd.nnewdn.bv_val = NULL; |
|
} else { |
|
cb.sc_response = &refint_modrdn_cb; |
|
if ( op->oq_modrdn.rs_newSup ) { |
if ( op->oq_modrdn.rs_newSup ) { |
pdn = *op->oq_modrdn.rs_newSup; |
pdn = *op->oq_modrdn.rs_newSup; |
} else { |
} else { |
dnParent( &op->o_req_dn, &pdn ); |
dnParent( &op->o_req_dn, &pdn ); |
} |
} |
build_new_dn( &dd.newdn, &pdn, &op->orr_newrdn, NULL ); |
build_new_dn( &rq->newdn, &pdn, &op->orr_newrdn, NULL ); |
if ( op->oq_modrdn.rs_nnewSup ) { |
if ( op->oq_modrdn.rs_nnewSup ) { |
pdn = *op->oq_modrdn.rs_nnewSup; |
pdn = *op->oq_modrdn.rs_nnewSup; |
} else { |
} else { |
dnParent( &op->o_req_ndn, &pdn ); |
dnParent( &op->o_req_ndn, &pdn ); |
} |
} |
build_new_dn( &dd.nnewdn, &pdn, &op->orr_nnewrdn, NULL ); |
build_new_dn( &rq->newndn, &pdn, &op->orr_nnewrdn, NULL ); |
} |
} |
|
|
/* |
ldap_pvt_thread_mutex_lock( &id->qmutex ); |
** build a search filter for all configured attributes; |
if ( id->qtail ) { |
** populate our Operation; |
id->qtail->next = rq; |
** pass our data (attr list, dn) to backend via sc_private; |
} else { |
** call the backend search function; |
id->qhead = rq; |
** nb: (|(one=thing)) is valid, but do smart formatting anyway; |
|
** nb: 16 is arbitrarily a dozen or so extra bytes; |
|
** |
|
*/ |
|
|
|
ftop.f_choice = LDAP_FILTER_OR; |
|
ftop.f_next = NULL; |
|
ftop.f_or = NULL; |
|
nop.ors_filter = &ftop; |
|
for(ip = id->attrs; ip; ip = ip->next) { |
|
fptr = ch_malloc( sizeof(Filter) + sizeof(AttributeAssertion) ); |
|
fptr->f_choice = LDAP_FILTER_EQUALITY; |
|
fptr->f_ava = (AttributeAssertion *)(fptr+1); |
|
fptr->f_ava->aa_desc = ip->attr; |
|
fptr->f_ava->aa_value = op->o_req_ndn; |
|
fptr->f_next = ftop.f_or; |
|
ftop.f_or = fptr; |
|
} |
|
filter2bv( nop.ors_filter, &nop.ors_filterstr ); |
|
|
|
/* callback gets the searched dn instead */ |
|
dd.dn = op->o_req_ndn; |
|
dd.message = "_dependent_search"; |
|
dd.mods = NULL; |
|
cb.sc_private = ⅆ |
|
nop.o_callback = &cb; |
|
nop.o_tag = LDAP_REQ_SEARCH; |
|
nop.ors_scope = LDAP_SCOPE_SUBTREE; |
|
nop.ors_deref = LDAP_DEREF_NEVER; |
|
nop.ors_limit = NULL; |
|
nop.ors_slimit = SLAP_NO_LIMIT; |
|
nop.ors_tlimit = SLAP_NO_LIMIT; |
|
|
|
/* no attrs! */ |
|
nop.ors_attrs = slap_anlist_no_attrs; |
|
nop.ors_attrsonly = 1; |
|
|
|
nop.o_req_ndn = id->dn; |
|
nop.o_req_dn = id->dn; |
|
|
|
/* search */ |
|
rc = nop.o_bd->be_search(&nop, &nrs); |
|
|
|
ch_free( nop.ors_filterstr.bv_val ); |
|
while ( (fptr = ftop.f_or) != NULL ) { |
|
ftop.f_or = fptr->f_next; |
|
ch_free( fptr ); |
|
} |
|
ch_free(dd.nnewdn.bv_val); |
|
ch_free(dd.newdn.bv_val); |
|
dd.newdn.bv_val = NULL; |
|
dd.nnewdn.bv_val = NULL; |
|
|
|
if(rc != LDAP_SUCCESS) { |
|
Debug( LDAP_DEBUG_TRACE, |
|
"refint_response: search failed: %d\n", |
|
rc, 0, 0 ); |
|
goto done; |
|
} |
|
|
|
/* safety? paranoid just in case */ |
|
if(!cb.sc_private) { |
|
Debug( LDAP_DEBUG_TRACE, |
|
"refint_response: callback wiped out sc_private?!\n", |
|
0, 0, 0 ); |
|
goto done; |
|
} |
|
|
|
/* presto! now it's a modify request with null callback */ |
|
cb.sc_response = &refint_null_cb; |
|
nop.o_tag = LDAP_REQ_MODIFY; |
|
dd.message = "_dependent_modify"; |
|
|
|
/* See if the parent operation is going into the replog */ |
|
for (cbo=op->o_callback, cbp = cbo->sc_next; cbp; cbo=cbp,cbp=cbp->sc_next) { |
|
if (cbp->sc_response == slap_replog_cb) { |
|
/* Invoke replog now, arrange for our |
|
* dependent mods to also be logged |
|
*/ |
|
cbo->sc_next = cbp->sc_next; |
|
replog( op ); |
|
nop.o_callback = &cb2; |
|
break; |
|
} |
|
} |
} |
|
id->qtail = rq; |
|
ldap_pvt_thread_mutex_unlock( &id->qmutex ); |
|
|
/* |
ac = 0; |
** [our search callback builds a list of mods] |
ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex ); |
** foreach mod: |
if ( !id->qtask ) { |
** make sure its dn has a backend; |
id->qtask = ldap_pvt_runqueue_insert( &slapd_rq, RUNQ_INTERVAL, |
** connect Modification* chain to our op; |
refint_qtask, id, "refint_qtask", |
** call the backend modify function; |
op->o_bd->be_suffix[0].bv_val ); |
** pass any errors upstream; |
ac = 1; |
** |
} else { |
*/ |
if ( !ldap_pvt_runqueue_isrunning( &slapd_rq, id->qtask ) && |
|
!id->qtask->next_sched.tv_sec ) { |
for(dp = dd.mods; dp; dp = dp->next) { |
id->qtask->interval.tv_sec = 0; |
nop.o_req_dn = dp->dn; |
ldap_pvt_runqueue_resched( &slapd_rq, id->qtask, 0 ); |
nop.o_req_ndn = dp->dn; |
id->qtask->interval.tv_sec = RUNQ_INTERVAL; |
nop.o_bd = select_backend(&dp->dn, 0, 1); |
ac = 1; |
if(!nop.o_bd) { |
|
Debug( LDAP_DEBUG_TRACE, |
|
"refint_response: no backend for DN %s!\n", |
|
dp->dn.bv_val, 0, 0 ); |
|
goto done; |
|
} |
|
nrs.sr_type = REP_RESULT; |
|
nop.orm_modlist = dp->mm; /* callback did all the work */ |
|
nop.o_dn = refint_dn; |
|
nop.o_ndn = refint_dn; |
|
nop.o_dn = nop.o_bd->be_rootdn; |
|
nop.o_ndn = nop.o_bd->be_rootndn; |
|
if(rs->sr_err != LDAP_SUCCESS) goto done; |
|
if((rc = nop.o_bd->be_modify(&nop, &nrs)) != LDAP_SUCCESS) { |
|
Debug( LDAP_DEBUG_TRACE, |
|
"refint_response: dependent modify failed: %d\n", |
|
nrs.sr_err, 0, 0 ); |
|
goto done; |
|
} |
} |
} |
} |
|
ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex ); |
|
if ( ac ) |
|
slap_wake_listener(); |
|
|
done: |
return SLAP_CB_CONTINUE; |
for(dp = dd.mods; dp; dp = dd.mods) { |
|
dd.mods = dp->next; |
|
ch_free(dp->dn.bv_val); |
|
slap_mods_free(dp->mm, 1); |
|
} |
|
dd.mods = NULL; |
|
|
|
return(SLAP_CB_CONTINUE); |
|
} |
} |
|
|
/* |
/* |
Line 666 done:
|
Line 752 done:
|
*/ |
*/ |
|
|
int refint_initialize() { |
int refint_initialize() { |
|
int rc; |
|
|
/* statically declared just after the #includes at top */ |
/* statically declared just after the #includes at top */ |
refint.on_bi.bi_type = "refint"; |
refint.on_bi.bi_type = "refint"; |
refint.on_bi.bi_db_init = refint_db_init; |
refint.on_bi.bi_db_init = refint_db_init; |
refint.on_bi.bi_db_config = refint_config; |
refint.on_bi.bi_db_destroy = refint_db_destroy; |
refint.on_bi.bi_db_open = refint_open; |
refint.on_bi.bi_db_open = refint_open; |
refint.on_bi.bi_db_close = refint_close; |
refint.on_bi.bi_db_close = refint_close; |
refint.on_response = refint_response; |
refint.on_response = refint_response; |
|
|
|
refint.on_bi.bi_cf_ocs = refintocs; |
|
rc = config_register_schema ( refintcfg, refintocs ); |
|
if ( rc ) return rc; |
|
|
return(overlay_register(&refint)); |
return(overlay_register(&refint)); |
} |
} |
|
|