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