Home | History | Annotate | Line # | Download | only in back-asyncmeta
modify.c revision 1.2
      1 /*	$NetBSD: modify.c,v 1.2 2021/08/14 16:14:59 christos Exp $	*/
      2 
      3 /* modify.c - modify request handler for back-asyncmeta */
      4 /* $OpenLDAP$ */
      5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      6  *
      7  * Copyright 2016-2021 The OpenLDAP Foundation.
      8  * Portions Copyright 2016 Symas Corporation.
      9  * All rights reserved.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted only as authorized by the OpenLDAP
     13  * Public License.
     14  *
     15  * A copy of this license is available in the file LICENSE in the
     16  * top-level directory of the distribution or, alternatively, at
     17  * <http://www.OpenLDAP.org/license.html>.
     18  */
     19 
     20 /* ACKNOWLEDGEMENTS:
     21  * This work was developed by Symas Corporation
     22  * based on back-meta module for inclusion in OpenLDAP Software.
     23  * This work was sponsored by Ericsson. */
     24 
     25 #include <sys/cdefs.h>
     26 __RCSID("$NetBSD: modify.c,v 1.2 2021/08/14 16:14:59 christos Exp $");
     27 
     28 #include "portable.h"
     29 
     30 #include <stdio.h>
     31 
     32 #include <ac/string.h>
     33 #include <ac/socket.h>
     34 #include "slap.h"
     35 #include "../../../libraries/liblber/lber-int.h"
     36 #include "../../../libraries/libldap/ldap-int.h"
     37 #include "../back-ldap/back-ldap.h"
     38 #include "back-asyncmeta.h"
     39 
     40 meta_search_candidate_t
     41 asyncmeta_back_modify_start(Operation *op,
     42 			    SlapReply *rs,
     43 			    a_metaconn_t *mc,
     44 			    bm_context_t *bc,
     45 			    int candidate,
     46 			    int do_lock)
     47 {
     48 	int		i, isupdate, rc = 0;
     49 	a_dncookie	dc;
     50 	a_metainfo_t	*mi = mc->mc_info;
     51 	a_metatarget_t	*mt = mi->mi_targets[ candidate ];
     52 	LDAPMod		**modv = NULL;
     53 	LDAPMod		*mods = NULL;
     54 	struct berval mdn;
     55 	Modifications	*ml;
     56 	meta_search_candidate_t retcode = META_SEARCH_CANDIDATE;
     57 		BerElement *ber = NULL;
     58 	a_metasingleconn_t	*msc = &mc->mc_conns[ candidate ];
     59 	SlapReply		*candidates = bc->candidates;
     60 	ber_int_t	msgid;
     61 	LDAPControl		**ctrls = NULL;
     62 
     63 	/*
     64 	 * Rewrite the modify dn, if needed
     65 	 */
     66 	dc.op = op;
     67 	dc.target = mt;
     68 	dc.memctx = op->o_tmpmemctx;
     69 	dc.to_from = MASSAGE_REQ;
     70 
     71 	asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn );
     72 
     73 	for ( i = 0, ml = op->orm_modlist; ml; i++ ,ml = ml->sml_next )
     74 		;
     75 	if (i > 0) {
     76 		mods = op->o_tmpalloc( sizeof( LDAPMod )*i, op->o_tmpmemctx );
     77 	}
     78 
     79 	if ( mods == NULL ) {
     80 		rs->sr_err = LDAP_OTHER;
     81 		retcode = META_SEARCH_ERR;
     82 		goto doreturn;
     83 	}
     84 	modv = ( LDAPMod ** )op->o_tmpalloc( ( i + 1 )*sizeof( LDAPMod * ), op->o_tmpmemctx );
     85 	if ( modv == NULL ) {
     86 		rs->sr_err = LDAP_OTHER;
     87 		retcode = META_SEARCH_ERR;
     88 		goto doreturn;
     89 	}
     90 
     91 	isupdate = be_shadow_update( op );
     92 	for ( i = 0, ml = op->orm_modlist; ml; ml = ml->sml_next ) {
     93 		int	j;
     94 
     95 		if ( !isupdate && !get_relax( op ) && ml->sml_desc->ad_type->sat_no_user_mod  )
     96 		{
     97 			continue;
     98 		}
     99 
    100 		modv[ i ] = &mods[ i ];
    101 		mods[ i ].mod_op = ml->sml_op | LDAP_MOD_BVALUES;
    102 		mods[ i ].mod_type = ml->sml_desc->ad_cname.bv_val;
    103 
    104 		if ( ml->sml_values != NULL ) {
    105 			j = ml->sml_numvals;
    106 			mods[ i ].mod_bvalues =(struct berval **)op->o_tmpalloc( ( j + 1 ) *sizeof( struct berval * ), op->o_tmpmemctx );
    107 			for ( j = 0; !BER_BVISNULL( &ml->sml_values[ j ] ); j++ ) {
    108 				mods[ i ].mod_bvalues[ j ] = op->o_tmpalloc(sizeof( struct berval ), op->o_tmpmemctx );
    109 				if ( ml->sml_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName )
    110 					asyncmeta_dn_massage( &dc, &ml->sml_values[ j ], mods[ i ].mod_bvalues[ j ] );
    111 				else
    112 					*mods[ i ].mod_bvalues[ j ] = ml->sml_values[ j ];
    113 			}
    114 			mods[ i ].mod_bvalues[ j ] = NULL;
    115 
    116 		} else {
    117 			mods[ i ].mod_bvalues = NULL;
    118 		}
    119 
    120 		i++;
    121 	}
    122 	modv[ i ] = 0;
    123 
    124 	asyncmeta_set_msc_time(msc);
    125 	ctrls = op->o_ctrls;
    126 	if ( asyncmeta_controls_add( op, rs, mc, candidate, bc->is_root, &ctrls) != LDAP_SUCCESS )
    127 	{
    128 		candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
    129 		retcode = META_SEARCH_ERR;
    130 		goto done;
    131 	}
    132 
    133 	/* someone reset the connection */
    134 	if (!( LDAP_BACK_CONN_ISBOUND( msc )
    135 	       || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
    136 		Debug( asyncmeta_debug , "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
    137 		goto error_unavailable;
    138 	}
    139 
    140 	ber = ldap_build_modify_req( msc->msc_ld, mdn.bv_val, modv, ctrls, NULL, &msgid);
    141 
    142 	if (!ber) {
    143 		Debug( asyncmeta_debug, "%s asyncmeta_back_modify_start: Operation encoding failed with errno %d\n",
    144 		       op->o_log_prefix, msc->msc_ld->ld_errno );
    145 		rs->sr_err = LDAP_OPERATIONS_ERROR;
    146 		rs->sr_text = "Failed to encode proxied request";
    147 		retcode = META_SEARCH_ERR;
    148 		goto done;
    149 	}
    150 
    151 	if (ber) {
    152 		struct timeval tv = {0, mt->mt_network_timeout*1000};
    153 		ber_socket_t s;
    154 		if (!( LDAP_BACK_CONN_ISBOUND( msc )
    155 		       || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
    156 			Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
    157 			goto error_unavailable;
    158 		}
    159 
    160 		ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
    161 		if (s < 0) {
    162 			Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
    163 			goto error_unavailable;
    164 		}
    165 
    166 		rc = ldap_int_poll( msc->msc_ld, s, &tv, 1);
    167 		if (rc < 0) {
    168 			Debug( asyncmeta_debug, "msc %p not writable within network timeout %s:%d\n", msc, __FILE__, __LINE__ );
    169 			if ((msc->msc_result_time + META_BACK_RESULT_INTERVAL) < slap_get_time()) {
    170 				rc = LDAP_SERVER_DOWN;
    171 			} else {
    172 				goto error_unavailable;
    173 			}
    174 		} else {
    175 			candidates[ candidate ].sr_msgid = msgid;
    176 			rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_MODIFY,
    177 							mdn.bv_val, ber, msgid );
    178 			if (rc == msgid)
    179 				rc = LDAP_SUCCESS;
    180 			else
    181 				rc = LDAP_SERVER_DOWN;
    182 			ber = NULL;
    183 		}
    184 
    185 		switch ( rc ) {
    186 		case LDAP_SUCCESS:
    187 			retcode = META_SEARCH_CANDIDATE;
    188 			asyncmeta_set_msc_time(msc);
    189 			goto done;
    190 
    191 		case LDAP_SERVER_DOWN:
    192 			/* do not lock if called from asyncmeta_handle_bind_result. Also do not reset the connection */
    193 			if (do_lock > 0) {
    194 				ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
    195 				asyncmeta_reset_msc(NULL, mc, candidate, 0, __FUNCTION__);
    196 				ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
    197 			}
    198 			/* fall though*/
    199 		default:
    200 			Debug( asyncmeta_debug, "msc %p ldap_send_initial_request failed. %s:%d\n", msc, __FILE__, __LINE__ );
    201 			goto error_unavailable;
    202 		}
    203 	}
    204 
    205 error_unavailable:
    206 	if (ber)
    207 		ber_free(ber, 1);
    208 	switch (bc->nretries[candidate]) {
    209 	case -1: /* nretries = forever */
    210 		ldap_pvt_thread_yield();
    211 		retcode = META_SEARCH_NEED_BIND;
    212 		break;
    213 	case 0: /* no retries left */
    214 		candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
    215 		rs->sr_err = LDAP_UNAVAILABLE;
    216 		rs->sr_text = "Unable to send modify request to target";
    217 		retcode = META_SEARCH_ERR;
    218 		break;
    219 	default: /* more retries left - try to rebind and go again */
    220 		retcode = META_SEARCH_NEED_BIND;
    221 		bc->nretries[candidate]--;
    222 		ldap_pvt_thread_yield();
    223 		break;
    224 	}
    225 done:
    226 	(void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
    227 
    228 	if ( mdn.bv_val != op->o_req_dn.bv_val ) {
    229 		op->o_tmpfree( mdn.bv_val, op->o_tmpmemctx );
    230 	}
    231 
    232 doreturn:;
    233 	Debug( LDAP_DEBUG_TRACE, "%s <<< asyncmeta_back_modify_start[%p]=%d\n", op->o_log_prefix, msc, candidates[candidate].sr_msgid );
    234 	return retcode;
    235 }
    236 
    237 int
    238 asyncmeta_back_modify( Operation *op, SlapReply *rs )
    239 {
    240 	a_metainfo_t	*mi = ( a_metainfo_t * )op->o_bd->be_private;
    241 	a_metatarget_t	*mt;
    242 	a_metaconn_t	*mc;
    243 	int		rc, candidate = -1;
    244 	void *thrctx = op->o_threadctx;
    245 	bm_context_t *bc;
    246 	SlapReply *candidates;
    247 	time_t current_time = slap_get_time();
    248 	int max_pending_ops = (mi->mi_max_pending_ops == 0) ? META_BACK_CFG_MAX_PENDING_OPS : mi->mi_max_pending_ops;
    249 
    250 	Debug(LDAP_DEBUG_ARGS, "==> asyncmeta_back_modify: %s\n",
    251 	      op->o_req_dn.bv_val );
    252 
    253 	if (current_time > op->o_time) {
    254 		Debug(asyncmeta_debug, "==> asyncmeta_back_modify[%s]: o_time:[%ld], current time: [%ld]\n",
    255 		      op->o_log_prefix, op->o_time, current_time );
    256 	}
    257 
    258 	asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets, mi );
    259 	if (bc == NULL) {
    260 		rs->sr_err = LDAP_OTHER;
    261 		send_ldap_result(op, rs);
    262 		return rs->sr_err;
    263 	}
    264 
    265 	candidates = bc->candidates;
    266 	mc = asyncmeta_getconn( op, rs, candidates, &candidate, LDAP_BACK_DONTSEND, 0);
    267 	if ( !mc || rs->sr_err != LDAP_SUCCESS) {
    268 		send_ldap_result(op, rs);
    269 		return rs->sr_err;
    270 	}
    271 
    272 	mt = mi->mi_targets[ candidate ];
    273 	bc->timeout = mt->mt_timeout[ SLAP_OP_MODIFY ];
    274 	bc->retrying = LDAP_BACK_RETRYING;
    275 	bc->sendok = ( LDAP_BACK_SENDRESULT | bc->retrying );
    276 	bc->stoptime = op->o_time + bc->timeout;
    277 	bc->bc_active = 1;
    278 
    279 	if (mc->pending_ops >= max_pending_ops) {
    280 		rs->sr_err = LDAP_BUSY;
    281 		rs->sr_text = "Maximum pending ops limit exceeded";
    282 		send_ldap_result(op, rs);
    283 		return rs->sr_err;
    284 	}
    285 
    286 	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
    287 	rc = asyncmeta_add_message_queue(mc, bc);
    288 	mc->mc_conns[candidate].msc_active++;
    289 	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
    290 
    291 	if (rc != LDAP_SUCCESS) {
    292 		rs->sr_err = LDAP_BUSY;
    293 		rs->sr_text = "Maximum pending ops limit exceeded";
    294 		send_ldap_result(op, rs);
    295 		ldap_pvt_thread_mutex_lock(&mc->mc_om_mutex);
    296 		mc->mc_conns[candidate].msc_active--;
    297 		ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
    298 		goto finish;
    299 	}
    300 
    301 retry:
    302 	if (bc->timeout && bc->stoptime < slap_get_time()) {
    303 		int		timeout_err;
    304 		timeout_err = op->o_protocol >= LDAP_VERSION3 ?
    305 			LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
    306 		rs->sr_err = timeout_err;
    307 		rs->sr_text = "Operation timed out before it was sent to target";
    308 		asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
    309 		goto finish;
    310 	}
    311 
    312 	rc = asyncmeta_dobind_init_with_retry(op, rs, bc, mc, candidate);
    313 	switch (rc)
    314 	{
    315 	case META_SEARCH_CANDIDATE:
    316 		/* target is already bound, just send the request */
    317 		Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modify:  "
    318 		       "cnd=\"%d\"\n", op->o_log_prefix, candidate );
    319 
    320 		rc = asyncmeta_back_modify_start( op, rs, mc, bc, candidate, 1);
    321 		if (rc == META_SEARCH_ERR) {
    322 			asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
    323 			goto finish;
    324 
    325 		} else if (rc == META_SEARCH_NEED_BIND) {
    326 			goto retry;
    327 		}
    328 		break;
    329 	case META_SEARCH_NOT_CANDIDATE:
    330 		Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modify: NOT_CANDIDATE "
    331 		       "cnd=\"%d\"\n", op->o_log_prefix, candidate );
    332 		asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
    333 		goto finish;
    334 
    335 	case META_SEARCH_NEED_BIND:
    336 	case META_SEARCH_BINDING:
    337 			Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modify: BINDING "
    338 			       "cnd=\"%d\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
    339 			/* Todo add the context to the message queue but do not send the request
    340 			   the receiver must send this when we are done binding */
    341 			break;
    342 
    343 	case META_SEARCH_ERR:
    344 			Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modify: ERR "
    345 			       "cnd=\"%d\"\n", op->o_log_prefix, candidate );
    346 			asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
    347 			goto finish;
    348 		default:
    349 			assert( 0 );
    350 			break;
    351 		}
    352 
    353 	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
    354 	mc->mc_conns[candidate].msc_active--;
    355 	asyncmeta_start_one_listener(mc, candidates, bc, candidate);
    356 	bc->bc_active--;
    357 	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
    358 	rs->sr_err = SLAPD_ASYNCOP;
    359 
    360 finish:
    361 	return rs->sr_err;
    362 }
    363