Home | History | Annotate | Line # | Download | only in autogroup
autogroup.c revision 1.1.1.1
      1 /* autogroup.c - automatic group overlay */
      2 /* $OpenLDAP: pkg/ldap/contrib/slapd-modules/autogroup/autogroup.c,v 1.2.2.1 2008/02/08 23:00:43 quanah Exp $ */
      3 /*
      4  * Copyright 2007 Micha Szulczyski.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted only as authorized by the OpenLDAP
      9  * Public License.
     10  *
     11  * A copy of this license is available in the file LICENSE in the
     12  * top-level directory of the distribution or, alternatively, at
     13  * <http://www.OpenLDAP.org/license.html>.
     14  */
     15 
     16 #include "portable.h"
     17 
     18 #include <stdio.h>
     19 
     20 #include <ac/string.h>
     21 
     22 #include "slap.h"
     23 #include "config.h"
     24 #include "lutil.h"
     25 
     26 /* Filter represents the memberURL of a group. */
     27 typedef struct autogroup_filter_t {
     28 	struct berval			agf_dn;	/* The base DN in memberURL */
     29 	struct berval			agf_ndn;
     30 	struct berval			agf_filterstr;
     31 	Filter				*agf_filter;
     32 	int				agf_scope;
     33 	struct autogroup_filter_t	*agf_next;
     34 } autogroup_filter_t;
     35 
     36 /* Description of group attributes. */
     37 typedef struct autogroup_def_t {
     38 	ObjectClass		*agd_oc;
     39 	AttributeDescription	*agd_member_url_ad;
     40 	AttributeDescription	*agd_member_ad;
     41 	struct autogroup_def_t	*agd_next;
     42 } autogroup_def_t;
     43 
     44 /* Represents the group entry. */
     45 typedef struct autogroup_entry_t {
     46 	BerValue		age_dn;
     47 	BerValue		age_ndn;
     48 	autogroup_filter_t	*age_filter; /* List of filters made from memberURLs */
     49 	autogroup_def_t		*age_def; /* Attribute definition */
     50 	ldap_pvt_thread_mutex_t age_mutex;
     51 	struct autogroup_entry_t	*age_next;
     52 } autogroup_entry_t;
     53 
     54 /* Holds pointers to attribute definitions and groups. */
     55 typedef struct autogroup_info_t {
     56 	autogroup_def_t		*agi_def;	/* Group attributes definitions. */
     57 	autogroup_entry_t	*agi_entry;	/* Group entries.  */
     58 	ldap_pvt_thread_mutex_t agi_mutex;
     59 } autogroup_info_t;
     60 
     61 /* Search callback for adding groups initially. */
     62 typedef struct autogroup_sc_t {
     63 	autogroup_info_t		*ags_info;	/* Group definitions and entries.  */
     64 	autogroup_def_t		*ags_def;	/* Attributes definition of the group being added. */
     65 } autogroup_sc_t;
     66 
     67 /* Used for adding members, found when searching, to a group. */
     68 typedef struct autogroup_ga_t {
     69 	autogroup_entry_t	*agg_group;	/* The group to which the members will be added. */
     70 	Entry			*agg_entry;	/* Used in autogroup_member_search_cb to modify
     71 						this entry with the search results. */
     72 
     73 	Modifications		*agg_mod;	/* Used in autogroup_member_search_modify_cb to hold the
     74 						search results which will be added to the group. */
     75 
     76 	Modifications		*agg_mod_last; /* Used in autogroup_member_search_modify_cb so we don't
     77 						have to search for the last mod added. */
     78 } autogroup_ga_t;
     79 
     80 
     81 /*
     82 **	dn, ndn	- the DN of the member to add
     83 **	age	- the group to which the member DN will be added
     84 */
     85 static int
     86 autogroup_add_member_to_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
     87 {
     88 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
     89 	Modifications	modlist;
     90 	SlapReply	sreply = {REP_RESULT};
     91 	BerValue	vals[ 2 ], nvals[ 2 ];
     92 	slap_callback	cb = { NULL, slap_null_cb, NULL, NULL };
     93 	Operation	o = *op;
     94 
     95 	Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_to_group adding <%s> to <%s>\n",
     96 		dn->bv_val, age->age_dn.bv_val, 0);
     97 
     98 	assert( dn != NULL );
     99 	assert( ndn != NULL );
    100 
    101 	vals[ 0 ] = *dn;
    102 	BER_BVZERO( &vals[ 1 ] );
    103 	nvals[ 0 ] = *ndn;
    104 	BER_BVZERO( &nvals[ 1 ] );
    105 
    106 	modlist.sml_op = LDAP_MOD_ADD;
    107 	modlist.sml_desc = age->age_def->agd_member_ad;
    108 	modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
    109 	modlist.sml_values = vals;
    110 	modlist.sml_nvalues = nvals;
    111 	modlist.sml_numvals = 1;
    112 	modlist.sml_flags = SLAP_MOD_INTERNAL;
    113 	modlist.sml_next = NULL;
    114 
    115 	o.o_tag = LDAP_REQ_MODIFY;
    116 	o.o_callback = &cb;
    117 	o.orm_modlist = &modlist;
    118 	o.o_req_dn = age->age_dn;
    119 	o.o_req_ndn = age->age_ndn;
    120 	o.o_permissive_modify = 1;
    121 	o.o_managedsait = SLAP_CONTROL_CRITICAL;
    122 	o.o_relax = SLAP_CONTROL_CRITICAL;
    123 
    124 	o.o_bd->bd_info = (BackendInfo *)on->on_info;
    125 	(void)op->o_bd->be_modify( &o, &sreply );
    126 	o.o_bd->bd_info = (BackendInfo *)on;
    127 
    128 	return sreply.sr_err;
    129 }
    130 
    131 /*
    132 ** dn,ndn	- the DN to be deleted
    133 ** age		- the group from which the DN will be deleted
    134 ** If we pass a NULL dn and ndn, all members are deleted from the group.
    135 */
    136 static int
    137 autogroup_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
    138 {
    139 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
    140 	Modifications	modlist;
    141 	SlapReply	sreply = {REP_RESULT};
    142 	BerValue	vals[ 2 ], nvals[ 2 ];
    143 	slap_callback	cb = { NULL, slap_null_cb, NULL, NULL };
    144 	Operation	o = *op;
    145 
    146 	if ( dn == NULL || ndn == NULL ) {
    147 		Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing all members from <%s>\n",
    148 			age->age_dn.bv_val, 0 ,0);
    149 
    150 		modlist.sml_values = NULL;
    151 		modlist.sml_nvalues = NULL;
    152 		modlist.sml_numvals = 0;
    153 	} else {
    154 		Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing <%s> from <%s>\n",
    155 			dn->bv_val, age->age_dn.bv_val, 0);
    156 
    157 		vals[ 0 ] = *dn;
    158 		BER_BVZERO( &vals[ 1 ] );
    159 		nvals[ 0 ] = *ndn;
    160 		BER_BVZERO( &nvals[ 1 ] );
    161 
    162 		modlist.sml_values = vals;
    163 		modlist.sml_nvalues = nvals;
    164 		modlist.sml_numvals = 1;
    165 	}
    166 
    167 
    168 	modlist.sml_op = LDAP_MOD_DELETE;
    169 	modlist.sml_desc = age->age_def->agd_member_ad;
    170 	modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
    171 	modlist.sml_flags = SLAP_MOD_INTERNAL;
    172 	modlist.sml_next = NULL;
    173 
    174 	o.o_callback = &cb;
    175 	o.o_tag = LDAP_REQ_MODIFY;
    176 	o.orm_modlist = &modlist;
    177 	o.o_req_dn = age->age_dn;
    178 	o.o_req_ndn = age->age_ndn;
    179 	o.o_relax = SLAP_CONTROL_CRITICAL;
    180 	o.o_managedsait = SLAP_CONTROL_CRITICAL;
    181 	o.o_permissive_modify = 1;
    182 
    183 	o.o_bd->bd_info = (BackendInfo *)on->on_info;
    184 	(void)op->o_bd->be_modify( &o, &sreply );
    185 	o.o_bd->bd_info = (BackendInfo *)on;
    186 
    187 	return sreply.sr_err;
    188 }
    189 
    190 /*
    191 ** Callback used to add entries to a group,
    192 ** which are going to be written in the database
    193 ** (used in bi_op_add)
    194 ** The group is passed in autogroup_ga_t->agg_group
    195 */
    196 static int
    197 autogroup_member_search_cb( Operation *op, SlapReply *rs )
    198 {
    199 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
    200 
    201 	assert( op->o_tag == LDAP_REQ_SEARCH );
    202 
    203 	if ( rs->sr_type == REP_SEARCH ) {
    204 		autogroup_ga_t		*agg = (autogroup_ga_t *)op->o_callback->sc_private;
    205 		autogroup_entry_t	*age = agg->agg_group;
    206 		Modification		mod;
    207 		const char		*text = NULL;
    208 		char			textbuf[1024];
    209 		struct berval		vals[ 2 ], nvals[ 2 ];
    210 
    211 		Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_cb <%s>\n",
    212 			rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
    213 
    214 		vals[ 0 ] = rs->sr_entry->e_name;
    215 		BER_BVZERO( &vals[ 1 ] );
    216 		nvals[ 0 ] = rs->sr_entry->e_nname;
    217 		BER_BVZERO( &nvals[ 1 ] );
    218 
    219 		mod.sm_op = LDAP_MOD_ADD;
    220 		mod.sm_desc = age->age_def->agd_member_ad;
    221 		mod.sm_type = age->age_def->agd_member_ad->ad_cname;
    222 		mod.sm_values = vals;
    223 		mod.sm_nvalues = nvals;
    224 		mod.sm_numvals = 1;
    225 
    226 		modify_add_values( agg->agg_entry, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
    227 	}
    228 
    229 	return 0;
    230 }
    231 
    232 /*
    233 ** Callback used to add entries to a group, which is already in the database.
    234 ** (used in on_response)
    235 ** The group is passed in autogroup_ga_t->agg_group
    236 ** NOTE: Very slow.
    237 */
    238 static int
    239 autogroup_member_search_modify_cb( Operation *op, SlapReply *rs )
    240 {
    241 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
    242 
    243 	assert( op->o_tag == LDAP_REQ_SEARCH );
    244 
    245 	if ( rs->sr_type == REP_SEARCH ) {
    246 		autogroup_ga_t		*agg = (autogroup_ga_t *)op->o_callback->sc_private;
    247 		autogroup_entry_t	*age = agg->agg_group;
    248 		Operation		o = *op;
    249 		Modifications		*modlist;
    250 		SlapReply		sreply = {REP_RESULT};
    251 		const char		*text = NULL;
    252 		char			textbuf[1024];
    253 		struct berval		vals[ 2 ], nvals[ 2 ];
    254 		slap_callback		cb = { NULL, slap_null_cb, NULL, NULL };
    255 
    256 		Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_modify_cb <%s>\n",
    257 			rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
    258 
    259 		vals[ 0 ] = rs->sr_entry->e_name;
    260 		BER_BVZERO( &vals[ 1 ] );
    261 		nvals[ 0 ] = rs->sr_entry->e_nname;
    262 		BER_BVZERO( &nvals[ 1 ] );
    263 
    264 		modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
    265 
    266 		modlist->sml_op = LDAP_MOD_ADD;
    267 		modlist->sml_desc = age->age_def->agd_member_ad;
    268 		modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
    269 
    270 		ber_bvarray_dup_x( &modlist->sml_values, vals, NULL );
    271 		ber_bvarray_dup_x( &modlist->sml_nvalues, nvals, NULL );
    272 		modlist->sml_numvals = 1;
    273 
    274 		modlist->sml_flags = SLAP_MOD_INTERNAL;
    275 		modlist->sml_next = NULL;
    276 
    277 		if ( agg->agg_mod == NULL ) {
    278 			agg->agg_mod = modlist;
    279 			agg->agg_mod_last = modlist;
    280 		} else {
    281 			agg->agg_mod_last->sml_next = modlist;
    282 			agg->agg_mod_last = modlist;
    283 		}
    284 
    285 	}
    286 
    287 	return 0;
    288 }
    289 
    290 
    291 /*
    292 ** Adds all entries matching the passed filter to the specified group.
    293 ** If modify == 1, then we modify the group's entry in the database using be_modify.
    294 ** If modify == 0, then, we must supply a rw entry for the group,
    295 **	because we only modify the entry, without calling be_modify.
    296 ** e	- the group entry, to which the members will be added
    297 ** age	- the group
    298 ** agf	- the filter
    299 */
    300 static int
    301 autogroup_add_members_from_filter( Operation *op, Entry *e, autogroup_entry_t *age, autogroup_filter_t *agf, int modify)
    302 {
    303 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
    304 	Operation		o = *op;
    305 	SlapReply		rs = { REP_SEARCH };
    306 	slap_callback		cb = { 0 };
    307 	slap_callback		null_cb = { NULL, slap_null_cb, NULL, NULL };
    308 	autogroup_ga_t		agg;
    309 
    310 	Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_members_from_filter <%s>\n",
    311 		age->age_dn.bv_val, 0, 0);
    312 
    313 	o.ors_attrsonly = 0;
    314 	o.o_tag = LDAP_REQ_SEARCH;
    315 
    316 	o.o_req_dn = agf->agf_dn;
    317 	o.o_req_ndn = agf->agf_ndn;
    318 
    319 	o.ors_filterstr = agf->agf_filterstr;
    320 	o.ors_filter = agf->agf_filter;
    321 
    322 	o.ors_scope = agf->agf_scope;
    323 	o.ors_deref = LDAP_DEREF_NEVER;
    324 	o.ors_limit = NULL;
    325 	o.ors_tlimit = SLAP_NO_LIMIT;
    326 	o.ors_slimit = SLAP_NO_LIMIT;
    327 	o.ors_attrs =  slap_anlist_no_attrs;
    328 
    329 	agg.agg_group = age;
    330 	agg.agg_mod = NULL;
    331 	agg.agg_mod_last = NULL;
    332 	agg.agg_entry = e;
    333 	cb.sc_private = &agg;
    334 
    335 	if ( modify == 1 ) {
    336 		cb.sc_response = autogroup_member_search_modify_cb;
    337 	} else {
    338 		cb.sc_response = autogroup_member_search_cb;
    339 	}
    340 
    341 	cb.sc_cleanup = NULL;
    342 	cb.sc_next = NULL;
    343 
    344 	o.o_callback = &cb;
    345 
    346 	o.o_bd->bd_info = (BackendInfo *)on->on_info;
    347 	op->o_bd->be_search( &o, &rs );
    348 	o.o_bd->bd_info = (BackendInfo *)on;
    349 
    350 	if ( modify == 1 ) {
    351 		o = *op;
    352 		o.o_callback = &null_cb;
    353 		o.o_tag = LDAP_REQ_MODIFY;
    354 		o.orm_modlist = agg.agg_mod;
    355 		o.o_req_dn = age->age_dn;
    356 		o.o_req_ndn = age->age_ndn;
    357 		o.o_relax = SLAP_CONTROL_CRITICAL;
    358 		o.o_managedsait = SLAP_CONTROL_NONCRITICAL;
    359 		o.o_permissive_modify = 1;
    360 
    361 		o.o_bd->bd_info = (BackendInfo *)on->on_info;
    362 		(void)op->o_bd->be_modify( &o, &rs );
    363 		o.o_bd->bd_info = (BackendInfo *)on;
    364 
    365 		slap_mods_free(agg.agg_mod, 1);
    366 	}
    367 
    368 	return 0;
    369 }
    370 
    371 /*
    372 ** Adds a group to the internal list from the passed entry.
    373 ** scan specifies whether to add all maching members to the group.
    374 ** modify specifies whether to modify the given group entry (when modify == 0),
    375 **	or to modify the group entry in the database (when modify == 1 and e = NULL and ndn != NULL).
    376 ** agi	- pointer to the groups and the attribute definitions
    377 ** agd - the attribute definition of the added group
    378 ** e	- the entry representing the group, can be NULL if the ndn is specified, and modify == 1
    379 ** ndn	- the DN of the group, can be NULL if we give a non-NULL e
    380 */
    381 static int
    382 autogroup_add_group( Operation *op, autogroup_info_t *agi, autogroup_def_t *agd, Entry *e, BerValue *ndn, int scan, int modify)
    383 {
    384 	autogroup_entry_t	**agep = &agi->agi_entry;
    385 	autogroup_filter_t	*agf, *agf_prev = NULL;
    386 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
    387 	LDAPURLDesc		*lud = NULL;
    388 	Attribute		*a;
    389 	BerValue		*bv, dn;
    390 	int			rc = 0, match = 1, null_entry = 0;
    391 
    392 	if ( e == NULL ) {
    393 		if ( overlay_entry_get_ov( op, ndn, NULL, NULL, 0, &e, on ) !=
    394 			LDAP_SUCCESS || e == NULL ) {
    395 			Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot get entry for <%s>\n", ndn->bv_val, 0, 0);
    396 			return 1;
    397 		}
    398 
    399 		null_entry = 1;
    400 	}
    401 
    402 	Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_group <%s>\n",
    403 		e->e_name.bv_val, 0, 0);
    404 
    405 	if ( agi->agi_entry != NULL ) {
    406 		for ( ; *agep ; agep = &(*agep)->age_next ) {
    407 			dnMatch( &match, 0, NULL, NULL, &e->e_nname, &(*agep)->age_ndn );
    408 			if ( match == 0 ) {
    409 				Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group already exists: <%s>\n", e->e_name.bv_val,0,0);
    410 				return 1;
    411 			}
    412 			/* goto last */;
    413 		}
    414 	}
    415 
    416 
    417 	*agep = (autogroup_entry_t *)ch_calloc( 1, sizeof( autogroup_entry_t ) );
    418 	ldap_pvt_thread_mutex_init( &(*agep)->age_mutex );
    419 	(*agep)->age_def = agd;
    420 	(*agep)->age_filter = NULL;
    421 
    422 	ber_dupbv( &(*agep)->age_dn, &e->e_name );
    423 	ber_dupbv( &(*agep)->age_ndn, &e->e_nname );
    424 
    425 	a = attrs_find( e->e_attrs, agd->agd_member_url_ad );
    426 
    427 	if ( null_entry == 1 ) {
    428 		a = attrs_dup( a );
    429 		overlay_entry_release_ov( op, e, 0, on );
    430 	}
    431 
    432 	if( a == NULL ) {
    433 		Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group has no memberURL\n", 0,0,0);
    434 	} else {
    435 		for ( bv = a->a_nvals; !BER_BVISNULL( bv ); bv++ ) {
    436 
    437 			agf = (autogroup_filter_t*)ch_calloc( 1, sizeof( autogroup_filter_t ) );
    438 
    439 			if ( ldap_url_parse( bv->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
    440 				Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot parse url <%s>\n", bv->bv_val,0,0);
    441 				/* FIXME: error? */
    442 				ch_free( agf );
    443 				continue;
    444 			}
    445 
    446 			agf->agf_scope = lud->lud_scope;
    447 
    448 			if ( lud->lud_dn == NULL ) {
    449 				BER_BVSTR( &dn, "" );
    450 			} else {
    451 				ber_str2bv( lud->lud_dn, 0, 0, &dn );
    452 			}
    453 
    454 			rc = dnPrettyNormal( NULL, &dn, &agf->agf_dn, &agf->agf_ndn, NULL );
    455 			if ( rc != LDAP_SUCCESS ) {
    456 				Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot normalize DN <%s>\n", dn.bv_val,0,0);
    457 				/* FIXME: error? */
    458 				goto cleanup;
    459 			}
    460 
    461 			if ( lud->lud_filter != NULL ) {
    462 				ber_str2bv( lud->lud_filter, 0, 1, &agf->agf_filterstr);
    463 				agf->agf_filter = str2filter( lud->lud_filter );
    464 			}
    465 
    466 			agf->agf_next = NULL;
    467 
    468 
    469 			if( (*agep)->age_filter == NULL ) {
    470 				(*agep)->age_filter = agf;
    471 			}
    472 
    473 			if( agf_prev != NULL ) {
    474 				agf_prev->agf_next = agf;
    475 			}
    476 
    477 			agf_prev = agf;
    478 
    479 			if ( scan == 1 ){
    480 				autogroup_add_members_from_filter( op, e, (*agep), agf, modify );
    481 			}
    482 
    483 			Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: added memberURL DN <%s> with filter <%s>\n",
    484 				agf->agf_ndn.bv_val, agf->agf_filterstr.bv_val, 0);
    485 
    486 			ldap_free_urldesc( lud );
    487 
    488 			continue;
    489 
    490 
    491 cleanup:;
    492 
    493 			ldap_free_urldesc( lud );
    494 			ch_free( agf );
    495 		}
    496 	}
    497 
    498 	if ( null_entry == 1 ) {
    499 		attrs_free( a );
    500 	}
    501 	return rc;
    502 }
    503 
    504 /*
    505 ** Used when opening the database to add all existing
    506 ** groups from the database to our internal list.
    507 */
    508 static int
    509 autogroup_group_add_cb( Operation *op, SlapReply *rs )
    510 {
    511 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
    512 
    513 	assert( op->o_tag == LDAP_REQ_SEARCH );
    514 
    515 
    516 	if ( rs->sr_type == REP_SEARCH ) {
    517 		autogroup_sc_t		*ags = (autogroup_sc_t *)op->o_callback->sc_private;
    518 
    519 		Debug(LDAP_DEBUG_TRACE, "==> autogroup_group_add_cb <%s>\n",
    520 			rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
    521 
    522 		autogroup_add_group( op, ags->ags_info, ags->ags_def, rs->sr_entry, NULL, 0, 0);
    523 	}
    524 
    525 	return 0;
    526 }
    527 
    528 
    529 /*
    530 ** When adding a group, we first strip any existing members,
    531 ** and add all which match the filters ourselfs.
    532 */
    533 static int
    534 autogroup_add_entry( Operation *op, SlapReply *rs)
    535 {
    536 		slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
    537 	autogroup_info_t		*agi = (autogroup_info_t *)on->on_bi.bi_private;
    538 	autogroup_def_t		*agd = agi->agi_def;
    539 	autogroup_entry_t	*age = agi->agi_entry;
    540 	autogroup_filter_t	*agf;
    541 	Attribute		*a;
    542 	int			rc = 0;
    543 
    544 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n",
    545 		op->ora_e->e_name.bv_val, 0, 0);
    546 
    547 	ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
    548 
    549 	/* Check if it's a group. */
    550 	for ( ; agd ; agd = agd->agd_next ) {
    551 		if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
    552 			Modification		mod;
    553 			const char		*text = NULL;
    554 			char			textbuf[1024];
    555 
    556 			mod.sm_op = LDAP_MOD_DELETE;
    557 			mod.sm_desc = agd->agd_member_ad;
    558 			mod.sm_type = agd->agd_member_ad->ad_cname;
    559 			mod.sm_values = NULL;
    560 			mod.sm_nvalues = NULL;
    561 
    562 			/* We don't want any member attributes added by the user. */
    563 			modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
    564 
    565 			autogroup_add_group( op, agi, agd, op->ora_e, NULL, 1 , 0);
    566 			ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
    567 			return SLAP_CB_CONTINUE;
    568 		}
    569 	}
    570 
    571 	for ( ; age ; age = age->age_next ) {
    572 		ldap_pvt_thread_mutex_lock( &age->age_mutex );
    573 
    574 		/* Check if any of the filters are the suffix to the entry DN.
    575 		   If yes, we can test that filter against the entry. */
    576 
    577 		for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
    578 			if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
    579 				rc = test_filter( op, op->ora_e, agf->agf_filter );
    580 				if ( rc == LDAP_COMPARE_TRUE ) {
    581 				autogroup_add_member_to_group( op, &op->ora_e->e_name, &op->ora_e->e_nname, age );
    582 					break;
    583 				}
    584 			}
    585 		}
    586 		ldap_pvt_thread_mutex_unlock( &age->age_mutex );
    587 	}
    588 
    589 	ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
    590 
    591 	return SLAP_CB_CONTINUE;
    592 }
    593 
    594 /*
    595 ** agi	- internal group and attribute definitions list
    596 ** e	- the group to remove from the internal list
    597 */
    598 static int
    599 autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
    600 {
    601 	autogroup_entry_t	*age = agi->agi_entry,
    602 				*age_prev = NULL,
    603 				*age_next;
    604 	int			rc = 1;
    605 
    606 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n",
    607 		age->age_dn.bv_val, 0, 0);
    608 
    609 	for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
    610 		age_next = age->age_next;
    611 
    612 		if ( age == e ) {
    613 			autogroup_filter_t	*agf = age->age_filter,
    614 							*agf_next;
    615 
    616 			if ( age_prev != NULL ) {
    617 				age_prev->age_next = age_next;
    618 			} else {
    619 				agi->agi_entry = NULL;
    620 			}
    621 
    622 			ch_free( age->age_dn.bv_val );
    623 			ch_free( age->age_ndn.bv_val );
    624 
    625 			for( agf_next = agf ; agf_next ; agf = agf_next ){
    626 				agf_next = agf->agf_next;
    627 
    628 				filter_free( agf->agf_filter );
    629 				ch_free( agf->agf_filterstr.bv_val );
    630 				ch_free( agf->agf_dn.bv_val );
    631 				ch_free( agf->agf_ndn.bv_val );
    632 			}
    633 
    634 			ldap_pvt_thread_mutex_unlock( &age->age_mutex );
    635 			ldap_pvt_thread_mutex_destroy( &age->age_mutex );
    636 			ch_free( age );
    637 
    638 			rc = 0;
    639 			return rc;
    640 
    641 		}
    642 	}
    643 
    644 	Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val, 0, 0);
    645 
    646 	return rc;
    647 
    648 }
    649 
    650 static int
    651 autogroup_delete_entry( Operation *op, SlapReply *rs)
    652 {
    653 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
    654 	autogroup_info_t		*agi = (autogroup_info_t *)on->on_bi.bi_private;
    655 	autogroup_def_t		*agd = agi->agi_def;
    656 	autogroup_entry_t	*age = agi->agi_entry,
    657 				*age_prev, *age_next;
    658 	autogroup_filter_t	*agf;
    659 	Entry			*e;
    660 	int			matched_group = 0, rc = 0;
    661 
    662 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
    663 
    664 	ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
    665 
    666 	if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
    667 		LDAP_SUCCESS || e == NULL ) {
    668 		Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
    669 		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
    670 		return SLAP_CB_CONTINUE;
    671 	}
    672 
    673 	/* Check if the entry to be deleted is one of our groups. */
    674 	for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
    675 		ldap_pvt_thread_mutex_lock( &age->age_mutex );
    676 		age_next = age->age_next;
    677 
    678 		if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
    679 			int match = 1;
    680 
    681 			matched_group = 1;
    682 
    683 			dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
    684 
    685 			if ( match == 0 ) {
    686 				autogroup_filter_t	*agf = age->age_filter,
    687 							*agf_next;
    688 
    689 				autogroup_delete_group( agi, age );
    690 				break;
    691 			}
    692 		}
    693 
    694 		ldap_pvt_thread_mutex_unlock( &age->age_mutex );
    695 	}
    696 
    697 	if ( matched_group == 1 ) {
    698 		overlay_entry_release_ov( op, e, 0, on );
    699 		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
    700 		return SLAP_CB_CONTINUE;
    701 	}
    702 
    703 	/* Check if the entry matches any of the groups.
    704 	   If yes, we can delete the entry from that group. */
    705 
    706 	for ( age = agi->agi_entry ; age ; age = age->age_next ) {
    707 		ldap_pvt_thread_mutex_lock( &age->age_mutex );
    708 
    709 		for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
    710 			if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
    711 				rc = test_filter( op, e, agf->agf_filter );
    712 				if ( rc == LDAP_COMPARE_TRUE ) {
    713 				autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
    714 					break;
    715 				}
    716 			}
    717 		}
    718 		ldap_pvt_thread_mutex_unlock( &age->age_mutex );
    719 	}
    720 
    721 	overlay_entry_release_ov( op, e, 0, on );
    722 	ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
    723 
    724 	return SLAP_CB_CONTINUE;
    725 }
    726 
    727 static int
    728 autogroup_response( Operation *op, SlapReply *rs )
    729 {
    730 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
    731 	autogroup_info_t		*agi = (autogroup_info_t *)on->on_bi.bi_private;
    732 	autogroup_def_t		*agd = agi->agi_def;
    733 	autogroup_entry_t	*age = agi->agi_entry;
    734 	autogroup_filter_t	*agf;
    735 	BerValue		new_dn, new_ndn, pdn;
    736 	Entry			*e, *group;
    737 	Attribute		*a;
    738 	int			is_olddn, is_newdn, dn_equal;
    739 
    740 	if ( op->o_tag == LDAP_REQ_MODRDN ) {
    741 		if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op )) {
    742 
    743 			Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val, 0, 0);
    744 
    745 			ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
    746 
    747 			if ( op->oq_modrdn.rs_newSup ) {
    748 				pdn = *op->oq_modrdn.rs_newSup;
    749 			} else {
    750 				dnParent( &op->o_req_dn, &pdn );
    751 			}
    752 			build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
    753 
    754 			if ( op->oq_modrdn.rs_nnewSup ) {
    755 				pdn = *op->oq_modrdn.rs_nnewSup;
    756 			} else {
    757 				dnParent( &op->o_req_ndn, &pdn );
    758 			}
    759 			build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
    760 
    761 			Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val, 0, 0);
    762 
    763 			dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
    764 
    765 			if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
    766 				LDAP_SUCCESS || e == NULL ) {
    767 				Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val, 0, 0);
    768 				ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
    769 				return SLAP_CB_CONTINUE;
    770 			}
    771 
    772 			a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
    773 
    774 
    775 			if ( a == NULL ) {
    776 				Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val, 0, 0);
    777 				overlay_entry_release_ov( op, e, 0, on );
    778 				ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
    779 				return SLAP_CB_CONTINUE;
    780 			}
    781 
    782 
    783 			/* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
    784 			for ( ; agd; agd = agd->agd_next ) {
    785 
    786 				if ( value_find_ex( slap_schema.si_ad_objectClass,
    787 						SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
    788 						SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
    789 						a->a_nvals, &agd->agd_oc->soc_cname,
    790 						op->o_tmpmemctx ) == 0 )
    791 				{
    792 					for ( age = agi->agi_entry ; age ; age = age->age_next ) {
    793 						int match = 1;
    794 
    795 						dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
    796 						if ( match == 0 ) {
    797 							Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val, 0, 0);
    798 							ber_dupbv( &age->age_dn, &new_dn );
    799 							ber_dupbv( &age->age_ndn, &new_ndn );
    800 
    801 							op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx  );
    802 							op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
    803 							overlay_entry_release_ov( op, e, 0, on );
    804 							ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
    805 							return SLAP_CB_CONTINUE;
    806 						}
    807 					}
    808 
    809 				}
    810 			}
    811 
    812 			overlay_entry_release_ov( op, e, 0, on );
    813 
    814 			/* For each group:
    815 			   1. check if the orginal entry's DN is in the group.
    816 			   2. chceck if the any of the group filter's base DN is a suffix of the new DN
    817 
    818 			   If 1 and 2 are both false, we do nothing.
    819 			   If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
    820 			   If 1 is false, and 2 is true, we check the entry against the group's filters,
    821 				and add it's DN to the group.
    822 			   If 1 is true, and 2 is false, we delete the entry's DN from the group.
    823 			*/
    824 			for ( age = agi->agi_entry ; age ; age = age->age_next ) {
    825 				is_olddn = 0;
    826 				is_newdn = 0;
    827 
    828 
    829 				ldap_pvt_thread_mutex_lock( &age->age_mutex );
    830 
    831 				if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
    832 					LDAP_SUCCESS || group == NULL ) {
    833 					Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val, 0, 0);
    834 
    835 					op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
    836 					op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
    837 
    838 					ldap_pvt_thread_mutex_unlock( &age->age_mutex );
    839 					ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
    840 					return SLAP_CB_CONTINUE;
    841 				}
    842 
    843 				a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
    844 
    845 				if ( a != NULL ) {
    846 					if ( value_find_ex( age->age_def->agd_member_ad,
    847 							SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
    848 							SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
    849 							a->a_nvals, &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
    850 					{
    851 						is_olddn = 1;
    852 					}
    853 
    854 				}
    855 
    856 				overlay_entry_release_ov( op, group, 0, on );
    857 
    858 				for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
    859 					if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
    860 						is_newdn = 1;
    861 						break;
    862 					}
    863 				}
    864 
    865 
    866 				if ( is_olddn == 1 && is_newdn == 0 ) {
    867 					autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
    868 				} else
    869 				if ( is_olddn == 0 && is_newdn == 1 ) {
    870 					for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
    871 						if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
    872 							autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
    873 							break;
    874 						}
    875 					}
    876 				} else
    877 				if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
    878 					autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
    879 					autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
    880 				}
    881 
    882 				ldap_pvt_thread_mutex_unlock( &age->age_mutex );
    883 			}
    884 
    885 			op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
    886 			op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
    887 
    888 			ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
    889 		}
    890 	}
    891 
    892 	if ( op->o_tag == LDAP_REQ_MODIFY ) {
    893 		if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS  && !get_manageDSAit( op ) ) {
    894 			Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val, 0, 0);
    895 
    896 			ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
    897 
    898 			if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
    899 				LDAP_SUCCESS || e == NULL ) {
    900 				Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
    901 				ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
    902 				return SLAP_CB_CONTINUE;
    903 			}
    904 
    905 			a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
    906 
    907 
    908 			if ( a == NULL ) {
    909 				Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
    910 				overlay_entry_release_ov( op, e, 0, on );
    911 				ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
    912 				return SLAP_CB_CONTINUE;
    913 			}
    914 
    915 
    916 			/* If we modify a group's memberURL, we have to delete all of it's members,
    917 			   and add them anew, because we cannot tell from which memberURL a member was added. */
    918 			for ( ; agd; agd = agd->agd_next ) {
    919 
    920 				if ( value_find_ex( slap_schema.si_ad_objectClass,
    921 						SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
    922 						SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
    923 						a->a_nvals, &agd->agd_oc->soc_cname,
    924 						op->o_tmpmemctx ) == 0 )
    925 				{
    926 					Modifications	*m;
    927 					int		match = 1;
    928 
    929 					m = op->orm_modlist;
    930 
    931 					for ( ; age ; age = age->age_next ) {
    932 						ldap_pvt_thread_mutex_lock( &age->age_mutex );
    933 
    934 						dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
    935 
    936 						if ( match == 0 ) {
    937 							for ( ; m ; m = m->sml_next ) {
    938 								if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
    939 									autogroup_def_t	*group_agd = age->age_def;
    940 									Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n",
    941 										op->o_req_dn.bv_val, 0, 0);
    942 
    943 									overlay_entry_release_ov( op, e, 0, on );
    944 
    945 									autogroup_delete_member_from_group( op, NULL, NULL, age );
    946 									autogroup_delete_group( agi, age );
    947 
    948 									autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
    949 
    950 									ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
    951 									return SLAP_CB_CONTINUE;
    952 								}
    953 							}
    954 
    955 							ldap_pvt_thread_mutex_unlock( &age->age_mutex );
    956 							break;
    957 						}
    958 
    959 						ldap_pvt_thread_mutex_unlock( &age->age_mutex );
    960 					}
    961 
    962 					overlay_entry_release_ov( op, e, 0, on );
    963 					ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
    964 					return SLAP_CB_CONTINUE;
    965 				}
    966 			}
    967 
    968 			overlay_entry_release_ov( op, e, 0, on );
    969 
    970 			/* When modifing any of the attributes of an entry, we must
    971 			   check if the entry is in any of our groups, and if
    972 			   the modified entry maches any of the filters of that group.
    973 
    974 			   If the entry exists in a group, but the modified attributes do
    975 				not match any of the group's filters, we delete the entry from that group.
    976 			   If the entry doesn't exist in a group, but matches a filter,
    977 				we add it to that group.
    978 			*/
    979 			for ( age = agi->agi_entry ; age ; age = age->age_next ) {
    980 				is_olddn = 0;
    981 				is_newdn = 0;
    982 
    983 
    984 				ldap_pvt_thread_mutex_lock( &age->age_mutex );
    985 
    986 				if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
    987 					LDAP_SUCCESS || group == NULL ) {
    988 					Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n",
    989 						age->age_dn.bv_val, 0, 0);
    990 
    991 					ldap_pvt_thread_mutex_unlock( &age->age_mutex );
    992 					ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
    993 					return SLAP_CB_CONTINUE;
    994 				}
    995 
    996 				a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
    997 
    998 				if ( a != NULL ) {
    999 					if ( value_find_ex( age->age_def->agd_member_ad,
   1000 							SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
   1001 							SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
   1002 							a->a_nvals, &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
   1003 					{
   1004 						is_olddn = 1;
   1005 					}
   1006 
   1007 				}
   1008 
   1009 				overlay_entry_release_ov( op, group, 0, on );
   1010 
   1011 				for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
   1012 					if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
   1013 						if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
   1014 							is_newdn = 1;
   1015 							break;
   1016 						}
   1017 					}
   1018 				}
   1019 
   1020 				if ( is_olddn == 1 && is_newdn == 0 ) {
   1021 					autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
   1022 				} else
   1023 				if ( is_olddn == 0 && is_newdn == 1 ) {
   1024 					autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
   1025 				}
   1026 
   1027 				ldap_pvt_thread_mutex_unlock( &age->age_mutex );
   1028 			}
   1029 
   1030 			ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
   1031 		}
   1032 	}
   1033 
   1034 	return SLAP_CB_CONTINUE;
   1035 }
   1036 
   1037 /*
   1038 ** When modifing a group, we must deny any modifications to the member attribute,
   1039 ** because the group would be inconsistent.
   1040 */
   1041 static int
   1042 autogroup_modify_entry( Operation *op, SlapReply *rs)
   1043 {
   1044 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
   1045 	autogroup_info_t		*agi = (autogroup_info_t *)on->on_bi.bi_private;
   1046 	autogroup_def_t		*agd = agi->agi_def;
   1047 	autogroup_entry_t	*age = agi->agi_entry;
   1048 	Entry			*e;
   1049 	Attribute		*a;
   1050 
   1051 	if ( get_manageDSAit( op ) ) {
   1052 		return SLAP_CB_CONTINUE;
   1053 	}
   1054 
   1055 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
   1056 	ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
   1057 
   1058 	if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
   1059 		LDAP_SUCCESS || e == NULL ) {
   1060 		Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
   1061 		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
   1062 		return SLAP_CB_CONTINUE;
   1063 	}
   1064 
   1065 	a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
   1066 
   1067 	if ( a == NULL ) {
   1068 		Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
   1069 		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
   1070 		return SLAP_CB_CONTINUE;
   1071 	}
   1072 
   1073 
   1074 	for ( ; agd; agd = agd->agd_next ) {
   1075 
   1076 		if ( value_find_ex( slap_schema.si_ad_objectClass,
   1077 				SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
   1078 				SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
   1079 				a->a_nvals, &agd->agd_oc->soc_cname,
   1080 				op->o_tmpmemctx ) == 0 )
   1081 		{
   1082 			Modifications	*m;
   1083 			int		match = 1;
   1084 
   1085 			m = op->orm_modlist;
   1086 
   1087 			for ( ; age ; age = age->age_next ) {
   1088 				dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
   1089 
   1090 				if ( match == 0 ) {
   1091 					for ( ; m ; m = m->sml_next ) {
   1092 						if ( m->sml_desc == age->age_def->agd_member_ad ) {
   1093 							overlay_entry_release_ov( op, e, 0, on );
   1094 							ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
   1095 							Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
   1096 							send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
   1097 							return LDAP_CONSTRAINT_VIOLATION;
   1098 						}
   1099 					}
   1100 					break;
   1101 				}
   1102 			}
   1103 
   1104 			overlay_entry_release_ov( op, e, 0, on );
   1105 			ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
   1106 			return SLAP_CB_CONTINUE;
   1107 		}
   1108 	}
   1109 
   1110 	overlay_entry_release_ov( op, e, 0, on );
   1111 	ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
   1112 	return SLAP_CB_CONTINUE;
   1113 }
   1114 
   1115 /*
   1116 ** Builds a filter for searching for the
   1117 ** group entries, according to the objectClass.
   1118 */
   1119 static int
   1120 autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
   1121 {
   1122 	char	*ptr;
   1123 
   1124 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n", 0, 0, 0);
   1125 
   1126 	op->ors_filterstr.bv_len = STRLENOF( "(=)" )
   1127 			+ slap_schema.si_ad_objectClass->ad_cname.bv_len
   1128 			+ agd->agd_oc->soc_cname.bv_len;
   1129 	ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
   1130 	*ptr++ = '(';
   1131 	ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
   1132 	*ptr++ = '=';
   1133 	ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
   1134 	*ptr++ = ')';
   1135 	*ptr = '\0';
   1136 
   1137 	op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
   1138 
   1139 	assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
   1140 
   1141 	return 0;
   1142 }
   1143 
   1144 enum {
   1145 	AG_ATTRSET = 1,
   1146 	AG_LAST
   1147 };
   1148 
   1149 static ConfigDriver	ag_cfgen;
   1150 
   1151 static ConfigTable agcfg[] = {
   1152 	{ "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
   1153 		3, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
   1154 		"( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
   1155 			"DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
   1156 			"EQUALITY caseIgnoreMatch "
   1157 			"SYNTAX OMsDirectoryString "
   1158 			"X-ORDERED 'VALUES' )",
   1159 			NULL, NULL },
   1160 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
   1161 };
   1162 
   1163 static ConfigOCs agocs[] = {
   1164 	{ "( OLcfgCtOc:2.1 "
   1165 		"NAME 'olcAutomaticGroups' "
   1166 		"DESC 'Automatic groups configuration' "
   1167 		"SUP olcOverlayConfig "
   1168 		"MAY olcAGattrSet )",
   1169 		Cft_Overlay, agcfg, NULL, NULL },
   1170 	{ NULL, 0, NULL }
   1171 };
   1172 
   1173 
   1174 static int
   1175 ag_cfgen( ConfigArgs *c )
   1176 {
   1177 	slap_overinst		*on = (slap_overinst *)c->bi;
   1178 	autogroup_info_t		*agi = (autogroup_info_t *)on->on_bi.bi_private;
   1179 	autogroup_def_t		*agd;
   1180 	autogroup_entry_t	*age;
   1181 
   1182 	int rc = 0, i;
   1183 
   1184 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n", 0, 0, 0);
   1185 
   1186 	if( agi == NULL ) {
   1187 		agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
   1188 		ldap_pvt_thread_mutex_init( &agi->agi_mutex );
   1189 		agi->agi_def = NULL;
   1190 		agi->agi_entry = NULL;
   1191 		on->on_bi.bi_private = (void *)agi;
   1192 	}
   1193 
   1194 	agd = agi->agi_def;
   1195 	age = agi->agi_entry;
   1196 
   1197 	if ( c->op == SLAP_CONFIG_EMIT ) {
   1198 
   1199 		ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
   1200 
   1201 		for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
   1202 			struct berval	bv;
   1203 			char		*ptr = c->cr_msg;
   1204 
   1205 			assert(agd->agd_oc != NULL);
   1206 			assert(agd->agd_member_url_ad != NULL);
   1207 			assert(agd->agd_member_ad != NULL);
   1208 
   1209 			ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
   1210 				SLAP_X_ORDERED_FMT "%s %s %s", i,
   1211 				agd->agd_oc->soc_cname.bv_val,
   1212 				agd->agd_member_url_ad->ad_cname.bv_val,
   1213 				agd->agd_member_ad->ad_cname.bv_val );
   1214 
   1215 			bv.bv_val = c->cr_msg;
   1216 			bv.bv_len = ptr - bv.bv_val;
   1217 			value_add_one ( &c->rvalue_vals, &bv );
   1218 
   1219 		}
   1220 		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
   1221 
   1222 		return rc;
   1223 
   1224 	}else if ( c->op == LDAP_MOD_DELETE ) {
   1225 		if ( c->valx < 0) {
   1226 			autogroup_def_t 		*agd_next;
   1227 			autogroup_entry_t	*age_next;
   1228 			autogroup_filter_t	*agf = age->age_filter,
   1229 						*agf_next;
   1230 
   1231 			ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
   1232 
   1233 			for ( agd_next = agd; agd_next; agd = agd_next ) {
   1234 				agd_next = agd->agd_next;
   1235 
   1236 				ch_free( agd );
   1237 			}
   1238 
   1239 			for ( age_next = age ; age_next ; age = age_next ) {
   1240 				age_next = age->age_next;
   1241 
   1242 				ch_free( age->age_dn.bv_val );
   1243 				ch_free( age->age_ndn.bv_val );
   1244 
   1245 				for( agf_next = agf ; agf_next ; agf = agf_next ){
   1246 					agf_next = agf->agf_next;
   1247 
   1248 					filter_free( agf->agf_filter );
   1249 					ch_free( agf->agf_filterstr.bv_val );
   1250 					ch_free( agf->agf_dn.bv_val );
   1251 					ch_free( agf->agf_ndn.bv_val );
   1252 				}
   1253 
   1254 				ldap_pvt_thread_mutex_init( &age->age_mutex );
   1255 				ch_free( age );
   1256 			}
   1257 
   1258 			ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
   1259 
   1260 			ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
   1261 			ch_free( agi );
   1262 			on->on_bi.bi_private = NULL;
   1263 
   1264 		} else {
   1265 			autogroup_def_t		**agdp;
   1266 			autogroup_entry_t	*age_next, *age_prev;
   1267 			autogroup_filter_t	*agf,
   1268 						*agf_next;
   1269 			struct berval		*bv;
   1270 
   1271 			ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
   1272 
   1273 			for ( i = 0, agdp = &agi->agi_def;
   1274 				i < c->valx; i++ )
   1275 			{
   1276 				if ( *agdp == NULL) {
   1277 					return 1;
   1278 				}
   1279 				agdp = &(*agdp)->agd_next;
   1280 			}
   1281 
   1282 			agd = *agdp;
   1283 			*agdp = agd->agd_next;
   1284 
   1285 			for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
   1286 				age_next = age->age_next;
   1287 
   1288 				if( age->age_def == agd ) {
   1289 					agf = age->age_filter;
   1290 
   1291 					ch_free( age->age_dn.bv_val );
   1292 					ch_free( age->age_ndn.bv_val );
   1293 
   1294 					for ( agf_next = agf; agf_next ; agf = agf_next ) {
   1295 						agf_next = agf->agf_next;
   1296 						filter_free( agf->agf_filter );
   1297 						ch_free( agf->agf_filterstr.bv_val );
   1298 						ch_free( agf->agf_dn.bv_val );
   1299 						ch_free( agf->agf_ndn.bv_val );
   1300 					}
   1301 
   1302 					ldap_pvt_thread_mutex_destroy( &age->age_mutex );
   1303 					ch_free( age );
   1304 
   1305 					age = age_prev;
   1306 
   1307 					if( age_prev != NULL ) {
   1308 						age_prev->age_next = age_next;
   1309 					}
   1310 				}
   1311 			}
   1312 
   1313 			ch_free( agd );
   1314 			agd = agi->agi_def;
   1315 			ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
   1316 
   1317 		}
   1318 
   1319 		return rc;
   1320 	}
   1321 
   1322 	switch(c->type){
   1323 	case AG_ATTRSET: {
   1324 		autogroup_def_t		**agdp,
   1325 					*agd_next = NULL;
   1326 		ObjectClass		*oc = NULL;
   1327 		AttributeDescription	*member_url_ad = NULL,
   1328 					*member_ad = NULL;
   1329 		const char		*text;
   1330 
   1331 
   1332 		oc = oc_find( c->argv[ 1 ] );
   1333 		if( oc == NULL ){
   1334 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
   1335 				"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
   1336 				"unable to find ObjectClass \"%s\"",
   1337 				c->argv[ 1 ] );
   1338 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
   1339 				c->log, c->cr_msg, 0 );
   1340 			return 1;
   1341 		}
   1342 
   1343 
   1344 		rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
   1345 		if( rc != LDAP_SUCCESS ) {
   1346 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
   1347 				"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
   1348 				"unable to find AttributeDescription \"%s\"",
   1349 				c->argv[ 2 ] );
   1350 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
   1351 				c->log, c->cr_msg, 0 );
   1352 			return 1;
   1353 		}
   1354 
   1355 		if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
   1356 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
   1357 				"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
   1358 				"AttributeDescription \"%s\" ",
   1359 				"must be of a subtype \"labeledURI\"",
   1360 				c->argv[ 2 ] );
   1361 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
   1362 				c->log, c->cr_msg, 0 );
   1363 			return 1;
   1364 		}
   1365 
   1366 		rc = slap_str2ad( c->argv[3], &member_ad, &text );
   1367 		if( rc != LDAP_SUCCESS ) {
   1368 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
   1369 				"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
   1370 				"unable to find AttributeDescription \"%s\"",
   1371 				c->argv[ 3 ] );
   1372 			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
   1373 				c->log, c->cr_msg, 0 );
   1374 			return 1;
   1375 		}
   1376 
   1377 		ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
   1378 
   1379 		for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
   1380 			/* The same URL attribute / member attribute pair
   1381 			* cannot be repeated */
   1382 
   1383 			if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
   1384 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
   1385 					"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
   1386 					"URL attributeDescription \"%s\" already mapped",
   1387 					member_ad->ad_cname.bv_val );
   1388 				Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
   1389 					c->log, c->cr_msg, 0 );
   1390 /*				return 1; //warning*/
   1391 			}
   1392 		}
   1393 
   1394 		if ( c->valx > 0 ) {
   1395 			int	i;
   1396 
   1397 			for ( i = 0, agdp = &agi->agi_def ;
   1398 				i < c->valx; i++ )
   1399 			{
   1400 				if ( *agdp == NULL ) {
   1401 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
   1402 						"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
   1403 						"invalid index {%d}",
   1404 						c->valx );
   1405 					Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
   1406 						c->log, c->cr_msg, 0 );
   1407 
   1408 					ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
   1409 					return 1;
   1410 				}
   1411 				agdp = &(*agdp)->agd_next;
   1412 			}
   1413 			agd_next = *agdp;
   1414 
   1415 		} else {
   1416 			for ( agdp = &agi->agi_def; *agdp;
   1417 				agdp = &(*agdp)->agd_next )
   1418 				/* goto last */;
   1419 		}
   1420 
   1421 		*agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
   1422 
   1423 		(*agdp)->agd_oc = oc;
   1424 		(*agdp)->agd_member_url_ad = member_url_ad;
   1425 		(*agdp)->agd_member_ad = member_ad;
   1426 		(*agdp)->agd_next = agd_next;
   1427 
   1428 		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
   1429 
   1430 		} break;
   1431 
   1432 	default:
   1433 		rc = 1;
   1434 		break;
   1435 	}
   1436 
   1437 	return rc;
   1438 }
   1439 
   1440 /*
   1441 ** Do a search for all the groups in the
   1442 ** database, and add them to out internal list.
   1443 */
   1444 static int
   1445 autogroup_db_open(
   1446 	BackendDB	*be,
   1447 	ConfigReply	*cr )
   1448 {
   1449 	slap_overinst			*on = (slap_overinst *) be->bd_info,
   1450 				*on_bd;
   1451 	autogroup_info_t		*agi = on->on_bi.bi_private;
   1452 	autogroup_def_t		*agd;
   1453 	autogroup_sc_t		ags;
   1454 	Operation		*op;
   1455 	SlapReply		rs = { REP_RESULT };
   1456 	slap_callback		cb = { 0 };
   1457 
   1458 	void				*thrctx = ldap_pvt_thread_pool_context();
   1459 	Connection			conn = { 0 };
   1460 	OperationBuffer 	opbuf;
   1461 	BerValue		bv;
   1462 	char			*ptr;
   1463 	int			rc = 0;
   1464 
   1465 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
   1466 
   1467 	connection_fake_init( &conn, &opbuf, thrctx );
   1468 	op = &opbuf.ob_op;
   1469 
   1470 	op->ors_attrsonly = 0;
   1471 	op->o_tag = LDAP_REQ_SEARCH;
   1472 	op->o_dn = be->be_rootdn;
   1473 	op->o_ndn = be->be_rootndn;
   1474 
   1475 	op->o_req_dn = be->be_suffix[0];
   1476 	op->o_req_ndn = be->be_nsuffix[0];
   1477 
   1478 	op->ors_scope = LDAP_SCOPE_SUBTREE;
   1479 	op->ors_deref = LDAP_DEREF_NEVER;
   1480 	op->ors_limit = NULL;
   1481 	op->ors_tlimit = SLAP_NO_LIMIT;
   1482 	op->ors_slimit = SLAP_NO_LIMIT;
   1483 	op->ors_attrs =  slap_anlist_no_attrs;
   1484 
   1485 	op->o_bd = select_backend(&op->o_req_ndn, 0);
   1486 
   1487 	ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
   1488 	for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
   1489 
   1490 		autogroup_build_def_filter(agd, op);
   1491 
   1492 
   1493 		ags.ags_info = agi;
   1494 		ags.ags_def = agd;
   1495 		cb.sc_private = &ags;
   1496 		cb.sc_response = autogroup_group_add_cb;
   1497 		cb.sc_cleanup = NULL;
   1498 		cb.sc_next = NULL;
   1499 
   1500 		op->o_callback = &cb;
   1501 
   1502 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
   1503 		op->o_bd->be_search( op, &rs );
   1504 		op->o_bd->bd_info = (BackendInfo *)on;
   1505 
   1506 		filter_free_x( op, op->ors_filter );
   1507 		op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
   1508 	}
   1509 	ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
   1510 
   1511 	return 0;
   1512 }
   1513 
   1514 static int
   1515 autogroup_db_close(
   1516 	BackendDB	*be,
   1517 	ConfigReply	*cr )
   1518 {
   1519 	slap_overinst			*on = (slap_overinst *) be->bd_info;
   1520 
   1521 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n", 0, 0, 0);
   1522 
   1523 	if ( on->on_bi.bi_private ) {
   1524 		autogroup_info_t		*agi = on->on_bi.bi_private;
   1525 		autogroup_entry_t	*age = agi->agi_entry,
   1526 					*age_next;
   1527 		autogroup_filter_t	*agf, *agf_next;
   1528 
   1529 		for ( age_next = age; age_next; age = age_next ) {
   1530 			age_next = age->age_next;
   1531 
   1532 			ch_free( age->age_dn.bv_val );
   1533 			ch_free( age->age_ndn.bv_val );
   1534 
   1535 			agf = age->age_filter;
   1536 
   1537 			for ( agf_next = agf; agf_next; agf = agf_next ) {
   1538 				agf_next = agf->agf_next;
   1539 
   1540 				filter_free( agf->agf_filter );
   1541 				ch_free( agf->agf_filterstr.bv_val );
   1542 				ch_free( agf->agf_dn.bv_val );
   1543 				ch_free( agf->agf_ndn.bv_val );
   1544 				ch_free( agf );
   1545 			}
   1546 
   1547 			ldap_pvt_thread_mutex_destroy( &age->age_mutex );
   1548 			ch_free( age );
   1549 		}
   1550 	}
   1551 
   1552 	return 0;
   1553 }
   1554 
   1555 static int
   1556 autogroup_db_destroy(
   1557 	BackendDB	*be,
   1558 	ConfigReply	*cr )
   1559 {
   1560 	slap_overinst			*on = (slap_overinst *) be->bd_info;
   1561 
   1562 	Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n", 0, 0, 0);
   1563 
   1564 	if ( on->on_bi.bi_private ) {
   1565 		autogroup_info_t		*agi = on->on_bi.bi_private;
   1566 		autogroup_def_t		*agd = agi->agi_def,
   1567 					*agd_next;
   1568 
   1569 		for ( agd_next = agd; agd_next; agd = agd_next ) {
   1570 			agd_next = agd->agd_next;
   1571 
   1572 			ch_free( agd );
   1573 		}
   1574 
   1575 		ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
   1576 		ch_free( agi );
   1577 	}
   1578 
   1579 	return 0;
   1580 }
   1581 
   1582 static slap_overinst	autogroup = { { NULL } };
   1583 
   1584 static
   1585 int
   1586 autogroup_initialize(void)
   1587 {
   1588 	int		rc = 0;
   1589 	autogroup.on_bi.bi_type = "autogroup";
   1590 
   1591 	autogroup.on_bi.bi_db_open = autogroup_db_open;
   1592 	autogroup.on_bi.bi_db_close = autogroup_db_close;
   1593 	autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
   1594 
   1595 	autogroup.on_bi.bi_op_add = autogroup_add_entry;
   1596 	autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
   1597 	autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
   1598 
   1599 	autogroup.on_response = autogroup_response;
   1600 
   1601 	autogroup.on_bi.bi_cf_ocs = agocs;
   1602 
   1603 	rc = config_register_schema( agcfg, agocs );
   1604 	if ( rc ) {
   1605 		return rc;
   1606 	}
   1607 
   1608 	return overlay_register( &autogroup );
   1609 }
   1610 
   1611 int
   1612 init_module( int argc, char *argv[] )
   1613 {
   1614 	return autogroup_initialize();
   1615 }
   1616