1 /* conn.c - handles connections to remote targets */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2016-2021 The OpenLDAP Foundation.
6  * Portions Copyright 2016 Symas Corporation.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 
18 /* ACKNOWLEDGEMENTS:
19 + * This work was developed by Symas Corporation
20 + * based on back-meta module for inclusion in OpenLDAP Software.
21 + * This work was sponsored by Ericsson. */
22 
23 #include "portable.h"
24 
25 #include <stdio.h>
26 
27 #include <ac/errno.h>
28 #include <ac/socket.h>
29 #include <ac/string.h>
30 #include "slap.h"
31 #include "../../../libraries/libldap/ldap-int.h"
32 #include "../back-ldap/back-ldap.h"
33 #include "back-asyncmeta.h"
34 
35 /*
36  * asyncmeta_conn_alloc
37  *
38  * Allocates a connection structure, making room for all the referenced targets
39  */
40 static a_metaconn_t *
asyncmeta_conn_alloc(a_metainfo_t * mi)41 asyncmeta_conn_alloc(
42 	a_metainfo_t	*mi)
43 {
44 	a_metaconn_t	*mc;
45 	int		ntargets = mi->mi_ntargets;
46 
47 	assert( ntargets > 0 );
48 
49 	/* malloc all in one */
50 	mc = ( a_metaconn_t * )ch_calloc( 1, sizeof( a_metaconn_t ) + ntargets * sizeof( a_metasingleconn_t ));
51 	if ( mc == NULL ) {
52 		return NULL;
53 	}
54 
55 	mc->mc_info = mi;
56 	ldap_pvt_thread_mutex_init( &mc->mc_om_mutex);
57 	mc->mc_authz_target = META_BOUND_NONE;
58 	mc->mc_conns = (a_metasingleconn_t *)(mc+1);
59 	return mc;
60 }
61 
62 /*
63  * asyncmeta_init_one_conn
64  *
65  * Initializes one connection
66  */
67 int
asyncmeta_init_one_conn(Operation * op,SlapReply * rs,a_metaconn_t * mc,int candidate,int ispriv,ldap_back_send_t sendok,int dolock)68 asyncmeta_init_one_conn(
69 	Operation		*op,
70 	SlapReply		*rs,
71 	a_metaconn_t		*mc,
72 	int			candidate,
73 	int			ispriv,
74 	ldap_back_send_t	sendok,
75 	int			dolock)
76 {
77 	a_metainfo_t		*mi = mc->mc_info;
78 	a_metatarget_t		*mt = mi->mi_targets[ candidate ];
79 	a_metasingleconn_t	*msc = NULL;
80 	int			version;
81 	a_dncookie		dc;
82 	int			isauthz = ( candidate == mc->mc_authz_target );
83 	int			do_return = 0;
84 #ifdef HAVE_TLS
85 	int			is_ldaps = 0;
86 	int			do_start_tls = 0;
87 #endif /* HAVE_TLS */
88 
89 	/* if the server is quarantined, and
90 	 * - the current interval did not expire yet, or
91 	 * - no more retries should occur,
92 	 * don't return the connection */
93 	if ( mt->mt_isquarantined ) {
94 		slap_retry_info_t	*ri = &mt->mt_quarantine;
95 		int			dont_retry = 0;
96 
97 		if ( mt->mt_quarantine.ri_interval ) {
98 			ldap_pvt_thread_mutex_lock( &mt->mt_quarantine_mutex );
99 			dont_retry = ( mt->mt_isquarantined > LDAP_BACK_FQ_NO );
100 			if ( dont_retry ) {
101 				dont_retry = ( ri->ri_num[ ri->ri_idx ] == SLAP_RETRYNUM_TAIL
102 					|| slap_get_time() < ri->ri_last + ri->ri_interval[ ri->ri_idx ] );
103 				if ( !dont_retry ) {
104 					Debug(LDAP_DEBUG_ANY,
105 					      "%s asyncmeta_init_one_conn[%d]: quarantine " "retry block #%d try #%d.\n",
106 					      op->o_log_prefix,
107 					      candidate, ri->ri_idx,
108 					      ri->ri_count );
109 
110 					mt->mt_isquarantined = LDAP_BACK_FQ_RETRYING;
111 				}
112 
113 			}
114 			ldap_pvt_thread_mutex_unlock( &mt->mt_quarantine_mutex );
115 		}
116 
117 		if ( dont_retry ) {
118 			rs->sr_err = LDAP_UNAVAILABLE;
119 			rs->sr_text = "Target is quarantined";
120 			Debug( LDAP_DEBUG_ANY, "%s asyncmeta_init_one_conn: Target is quarantined\n",
121 			       op->o_log_prefix );
122 			if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
123 					send_ldap_result( op, rs );
124 			}
125 			return rs->sr_err;
126 		}
127 	}
128 		msc = &mc->mc_conns[candidate];
129 	/*
130 	 * Already init'ed
131 	 */
132 	if ( LDAP_BACK_CONN_ISBOUND( msc )
133 		|| LDAP_BACK_CONN_ISANON( msc ) )
134 	{
135 		assert( msc->msc_ld != NULL );
136 		rs->sr_err = LDAP_SUCCESS;
137 		do_return = 1;
138 
139 	} else if ( META_BACK_CONN_CREATING( msc )
140 		|| LDAP_BACK_CONN_BINDING( msc ) )
141 	{
142 		rs->sr_err = LDAP_SUCCESS;
143 		do_return = 1;
144 
145 	} else if ( META_BACK_CONN_INITED( msc ) ) {
146 		assert( msc->msc_ld != NULL );
147 		rs->sr_err = LDAP_SUCCESS;
148 		do_return = 1;
149 
150 	} else {
151 		/*
152 		 * creating...
153 		 */
154 		META_BACK_CONN_CREATING_SET( msc );
155 	}
156 
157 	if ( do_return ) {
158 		if ( rs->sr_err != LDAP_SUCCESS
159 			&& op->o_conn
160 			&& ( sendok & LDAP_BACK_SENDERR ) )
161 		{
162 			send_ldap_result( op, rs );
163 		}
164 
165 		return rs->sr_err;
166 	}
167 
168 	assert( msc->msc_ld == NULL );
169 
170 	/*
171 	 * Attempts to initialize the connection to the target ds
172 	 */
173 	ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
174 
175 	rs->sr_err = ldap_initialize( &msc->msc_ld, mt->mt_uri );
176 #ifdef HAVE_TLS
177 	is_ldaps = ldap_is_ldaps_url( mt->mt_uri );
178 #endif /* HAVE_TLS */
179 	ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
180 	if ( rs->sr_err != LDAP_SUCCESS ) {
181 		Debug( LDAP_DEBUG_ANY, "%s asyncmeta_init_one_conn: ldap_initialize failed err=%d\n",
182 		       op->o_log_prefix, rs->sr_err );
183 		goto error_return;
184 	}
185 
186 	ldap_set_option( msc->msc_ld, LDAP_OPT_KEEPCONN, LDAP_OPT_ON);
187 
188 	msc->msc_ldr = ldap_dup(msc->msc_ld);
189 	if (!msc->msc_ldr) {
190 		ldap_ld_free(msc->msc_ld, 0, NULL, NULL);
191 		rs->sr_err = LDAP_NO_MEMORY;
192 		goto error_return;
193 	}
194 
195 	/*
196 	 * Set LDAP version. This will always succeed: If the client
197 	 * bound with a particular version, then so can we.
198 	 */
199 	if ( mt->mt_version != 0 ) {
200 		version = mt->mt_version;
201 
202 	} else if ( op->o_conn->c_protocol != 0 ) {
203 		version = op->o_conn->c_protocol;
204 
205 	} else {
206 		version = LDAP_VERSION3;
207 	}
208 	ldap_set_option( msc->msc_ld, LDAP_OPT_PROTOCOL_VERSION, &version );
209 	ldap_set_urllist_proc( msc->msc_ld, mt->mt_urllist_f, mt->mt_urllist_p );
210 
211 	/* automatically chase referrals ("chase-referrals [{yes|no}]" statement) */
212 	ldap_set_option( msc->msc_ld, LDAP_OPT_REFERRALS,
213 		META_BACK_TGT_CHASE_REFERRALS( mt ) ? LDAP_OPT_ON : LDAP_OPT_OFF );
214 
215 	slap_client_keepalive(msc->msc_ld, &mt->mt_tls.sb_keepalive);
216 
217 	if ( mt->mt_tls.sb_tcp_user_timeout > 0 ) {
218 		ldap_set_option( msc->msc_ld, LDAP_OPT_TCP_USER_TIMEOUT,
219 				 &mt->mt_tls.sb_tcp_user_timeout );
220 	}
221 
222 #ifdef HAVE_TLS
223 	{
224 		slap_bindconf *sb = NULL;
225 
226 		if ( ispriv ) {
227 			sb = &mt->mt_idassert.si_bc;
228 		} else {
229 			sb = &mt->mt_tls;
230 		}
231 
232 		bindconf_tls_set( sb, msc->msc_ld );
233 
234 		if ( !is_ldaps ) {
235 			if ( META_BACK_TGT_USE_TLS( mt )
236 				|| ( op->o_conn->c_is_tls && META_BACK_TGT_PROPAGATE_TLS( mt ) ) )
237 			{
238 				do_start_tls = 1;
239 			}
240 		}
241 	}
242 
243 	/* start TLS ("tls [try-]{start|propagate}" statement) */
244 	if ( do_start_tls ) {
245 #ifdef SLAP_STARTTLS_ASYNCHRONOUS
246 		/*
247 		 * use asynchronous StartTLS; in case, chase referral
248 		 * FIXME: OpenLDAP does not return referral on StartTLS yet
249 		 */
250 		int		msgid;
251 
252 		rs->sr_err = ldap_start_tls( msc->msc_ld, NULL, NULL, &msgid );
253 		if ( rs->sr_err == LDAP_SUCCESS ) {
254 			LDAPMessage	*res = NULL;
255 			int		rc, nretries = mt->mt_nretries;
256 			struct timeval	tv;
257 
258 			LDAP_BACK_TV_SET( &tv );
259 
260 retry:;
261 			rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
262 			switch ( rc ) {
263 			case -1:
264 				rs->sr_err = LDAP_OTHER;
265 				break;
266 
267 			case 0:
268 				if ( nretries != 0 ) {
269 					if ( nretries > 0 ) {
270 						nretries--;
271 					}
272 					LDAP_BACK_TV_SET( &tv );
273 					goto retry;
274 				}
275 				rs->sr_err = LDAP_OTHER;
276 				break;
277 
278 			default:
279 				/* only touch when activity actually took place... */
280 				if ( mi->mi_idle_timeout != 0 ) {
281 					asyncmeta_set_msc_time(msc);
282 				}
283 				break;
284 			}
285 
286 			if ( rc == LDAP_RES_EXTENDED ) {
287 				struct berval	*data = NULL;
288 
289 				/* NOTE: right now, data is unused, so don't get it */
290 				rs->sr_err = ldap_parse_extended_result( msc->msc_ld,
291 					res, NULL, NULL /* &data */ , 0 );
292 				if ( rs->sr_err == LDAP_SUCCESS ) {
293 					int		err;
294 
295 					/* FIXME: matched? referrals? response controls? */
296 					rs->sr_err = ldap_parse_result( msc->msc_ld,
297 						res, &err, NULL, NULL, NULL, NULL, 1 );
298 					res = NULL;
299 
300 					if ( rs->sr_err == LDAP_SUCCESS ) {
301 
302 						rs->sr_err = err;
303 					}
304 					rs->sr_err = slap_map_api2result( rs );
305 
306 					/* FIXME: in case a referral
307 					 * is returned, should we try
308 					 * using it instead of the
309 					 * configured URI? */
310 					if ( rs->sr_err == LDAP_SUCCESS ) {
311 						rs->sr_err = ldap_install_tls( msc->msc_ld );
312 
313 					} else if ( rs->sr_err == LDAP_REFERRAL ) {
314 						/* FIXME: LDAP_OPERATIONS_ERROR? */
315 						rs->sr_err = LDAP_OTHER;
316 						rs->sr_text = "Unwilling to chase referral "
317 							"returned by Start TLS exop";
318 					}
319 
320 					if ( data ) {
321 						ber_bvfree( data );
322 					}
323 				}
324 
325 			} else {
326 				rs->sr_err = LDAP_OTHER;
327 			}
328 
329 			if ( res != NULL ) {
330 				ldap_msgfree( res );
331 			}
332 		}
333 #else /* ! SLAP_STARTTLS_ASYNCHRONOUS */
334 		/*
335 		 * use synchronous StartTLS
336 		 */
337 		rs->sr_err = ldap_start_tls_s( msc->msc_ld, NULL, NULL );
338 #endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */
339 		if (rs->sr_err != LDAP_SUCCESS) {
340 			Debug( LDAP_DEBUG_ANY, "%s asyncmeta_init_one_conn: ldap_start_tls_s failed err=%d\n",
341 			       op->o_log_prefix, rs->sr_err );
342 		}
343 		/* if StartTLS is requested, only attempt it if the URL
344 		 * is not "ldaps://"; this may occur not only in case
345 		 * of misconfiguration, but also when used in the chain
346 		 * overlay, where the "uri" can be parsed out of a referral */
347 		if ( rs->sr_err == LDAP_SERVER_DOWN
348 			|| ( rs->sr_err != LDAP_SUCCESS
349 				&& META_BACK_TGT_TLS_CRITICAL( mt ) ) )
350 		{
351 
352 #ifdef DEBUG_205
353 			Debug( LDAP_DEBUG_ANY,
354 				"### %s asyncmeta_init_one_conn(TLS) "
355 				"ldap_unbind_ext[%d] ld=%p\n",
356 				op->o_log_prefix, candidate,
357 				(void *)msc->msc_ld );
358 #endif /* DEBUG_205 */
359 
360 			/* need to trash a failed Start TLS */
361 			asyncmeta_clear_one_msc( op, mc, candidate, 1, __FUNCTION__ );
362 			goto error_return;
363 		}
364 	}
365 #endif /* HAVE_TLS */
366 	/*
367 	 * Set the network timeout if set
368 	 */
369 	if ( mt->mt_network_timeout != 0 ) {
370 		struct timeval  network_timeout;
371 		network_timeout.tv_sec = 0;
372 		network_timeout.tv_usec = mt->mt_network_timeout*1000;
373 
374 		ldap_set_option( msc->msc_ld, LDAP_OPT_NETWORK_TIMEOUT,
375 				 (void *)&network_timeout );
376 	}
377 
378 	/*
379 	 * If the connection DN is not null, an attempt to rewrite it is made
380 	 */
381 
382 	if ( ispriv ) {
383 		if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) ) {
384 			ber_bvreplace( &msc->msc_bound_ndn, &mt->mt_idassert_authcDN );
385 			if ( !BER_BVISNULL( &mt->mt_idassert_passwd ) ) {
386 				if ( !BER_BVISNULL( &msc->msc_cred ) ) {
387 					memset( msc->msc_cred.bv_val, 0,
388 						msc->msc_cred.bv_len );
389 				}
390 				ber_bvreplace( &msc->msc_cred, &mt->mt_idassert_passwd );
391 			}
392 			LDAP_BACK_CONN_ISIDASSERT_SET( msc );
393 
394 		} else {
395 			ber_bvreplace( &msc->msc_bound_ndn, &slap_empty_bv );
396 		}
397 
398 	} else {
399 		if ( !BER_BVISNULL( &msc->msc_cred ) ) {
400 			memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
401 			ber_memfree_x( msc->msc_cred.bv_val, NULL );
402 			BER_BVZERO( &msc->msc_cred );
403 		}
404 		if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
405 			ber_memfree_x( msc->msc_bound_ndn.bv_val, NULL );
406 			BER_BVZERO( &msc->msc_bound_ndn );
407 		}
408 		if ( !BER_BVISEMPTY( &op->o_ndn )
409 			&& isauthz )
410 		{
411 			dc.op = op;
412 			dc.target = mt;
413 			dc.memctx = NULL;
414 			dc.to_from = MASSAGE_REQ;
415 
416 			/*
417 			 * Rewrite the bind dn if needed
418 			 */
419 			asyncmeta_dn_massage( &dc, &op->o_conn->c_dn, &msc->msc_bound_ndn );
420 
421 			/* copy the DN if needed */
422 			if ( msc->msc_bound_ndn.bv_val == op->o_conn->c_dn.bv_val ) {
423 				ber_dupbv( &msc->msc_bound_ndn, &op->o_conn->c_dn );
424 			}
425 		} else {
426 			ber_dupbv( &msc->msc_bound_ndn, (struct berval *)&slap_empty_bv );
427 		}
428 	}
429 	assert( !BER_BVISNULL( &msc->msc_bound_ndn ) );
430 
431 error_return:;
432 
433 	if (msc != NULL) {
434 		META_BACK_CONN_CREATING_CLEAR( msc );
435 	}
436 	if ( rs->sr_err == LDAP_SUCCESS && msc != NULL) {
437 		META_BACK_CONN_INITED_SET( msc );
438 	}
439 
440 	if ( rs->sr_err != LDAP_SUCCESS ) {
441 		rs->sr_err = slap_map_api2result( rs );
442 		if ( sendok & LDAP_BACK_SENDERR ) {
443 			send_ldap_result( op, rs );
444 		}
445 	}
446 	return rs->sr_err;
447 }
448 
449 
450 static int
asyncmeta_get_candidate(Operation * op,SlapReply * rs,struct berval * ndn)451 asyncmeta_get_candidate(
452 	Operation	*op,
453 	SlapReply	*rs,
454 	struct berval	*ndn )
455 {
456 	a_metainfo_t	*mi = ( a_metainfo_t * )op->o_bd->be_private;
457 	long		candidate;
458 
459 	/*
460 	 * tries to get a unique candidate
461 	 * (takes care of default target)
462 	 */
463 	candidate = asyncmeta_select_unique_candidate( mi, ndn );
464 
465 	/*
466 	 * if any is found, inits the connection
467 	 */
468 	if ( candidate == META_TARGET_NONE ) {
469 		rs->sr_err = LDAP_NO_SUCH_OBJECT;
470 		rs->sr_text = "No suitable candidate target found";
471 
472 	} else {
473 		rs->sr_err = LDAP_SUCCESS;
474 	}
475 
476 	return candidate;
477 }
478 
479 
480 /*
481  * asyncmeta_getconn
482  *
483  * Prepares the connection structure
484  *
485  * RATIONALE:
486  *
487  * - determine what DN is being requested:
488  *
489  *	op	requires candidate	checks
490  *
491  *	add	unique			parent of o_req_ndn
492  *	bind	unique^*[/all]		o_req_ndn [no check]
493  *	compare	unique^+		o_req_ndn
494  *	delete	unique			o_req_ndn
495  *	modify	unique			o_req_ndn
496  *	search	any			o_req_ndn
497  *	modrdn	unique[, unique]	o_req_ndn[, orr_nnewSup]
498  *
499  * - for ops that require the candidate to be unique, in case of multiple
500  *   occurrences an internal search with sizeLimit=1 is performed
501  *   if a unique candidate can actually be determined.  If none is found,
502  *   the operation aborts; if multiple are found, the default target
503  *   is used if defined and candidate; otherwise the operation aborts.
504  *
505  * *^note: actually, the bind operation is handled much like a search;
506  *   i.e. the bind is broadcast to all candidate targets.
507  *
508  * +^note: actually, the compare operation is handled much like a search;
509  *   i.e. the compare is broadcast to all candidate targets, while checking
510  *   that exactly none (noSuchObject) or one (TRUE/FALSE/UNDEFINED) is
511  *   returned.
512  */
513 a_metaconn_t *
asyncmeta_getconn(Operation * op,SlapReply * rs,SlapReply * candidates,int * candidate,ldap_back_send_t sendok,int alloc_new)514 asyncmeta_getconn(
515 	Operation 		*op,
516 	SlapReply		*rs,
517 	SlapReply               *candidates,
518 	int 			*candidate,
519 	ldap_back_send_t	sendok,
520 	int                     alloc_new)
521 {
522 	a_metainfo_t	*mi = ( a_metainfo_t * )op->o_bd->be_private;
523 	a_metaconn_t	*mc = NULL,
524 			mc_curr = {{ 0 }};
525 	int		cached = META_TARGET_NONE,
526 			i = META_TARGET_NONE,
527 			err = LDAP_SUCCESS,
528 			new_conn = 0,
529 			ncandidates = 0;
530 
531 
532 	meta_op_type	op_type = META_OP_REQUIRE_SINGLE;
533 	enum		{
534 		META_DNTYPE_ENTRY,
535 		META_DNTYPE_PARENT,
536 		META_DNTYPE_NEWPARENT
537 	}		dn_type = META_DNTYPE_ENTRY;
538 	struct berval	ndn = op->o_req_ndn,
539 			pndn;
540 
541 	if (alloc_new > 0) {
542 		mc = asyncmeta_conn_alloc(mi);
543 		new_conn = 0;
544 	} else {
545 		mc = asyncmeta_get_next_mc(mi);
546 	}
547 
548 	ldap_pvt_thread_mutex_lock(&mc->mc_om_mutex);
549 	/* Internal searches are privileged and shared. So is root. */
550 	if ( ( !BER_BVISEMPTY( &op->o_ndn ) && META_BACK_PROXYAUTHZ_ALWAYS( mi ) )
551 		|| ( BER_BVISEMPTY( &op->o_ndn ) && META_BACK_PROXYAUTHZ_ANON( mi ) )
552 		|| op->o_do_not_cache || be_isroot( op ) )
553 	{
554 		LDAP_BACK_CONN_ISPRIV_SET( &mc_curr );
555 		LDAP_BACK_PCONN_ROOTDN_SET( &mc_curr, op );
556 
557 	} else if ( BER_BVISEMPTY( &op->o_ndn ) && META_BACK_PROXYAUTHZ_NOANON( mi ) )
558 	{
559 		LDAP_BACK_CONN_ISANON_SET( &mc_curr );
560 		LDAP_BACK_PCONN_ANON_SET( &mc_curr, op );
561 
562 	} else {
563 		/* Explicit binds must not be shared */
564 		if ( !BER_BVISEMPTY( &op->o_ndn )
565 			|| op->o_tag == LDAP_REQ_BIND
566 			|| SLAP_IS_AUTHZ_BACKEND( op ) )
567 		{
568 			//mc_curr.mc_conn = op->o_conn;
569 
570 		} else {
571 			LDAP_BACK_CONN_ISANON_SET( &mc_curr );
572 			LDAP_BACK_PCONN_ANON_SET( &mc_curr, op );
573 		}
574 	}
575 
576 	switch ( op->o_tag ) {
577 	case LDAP_REQ_ADD:
578 		/* if we go to selection, the entry must not exist,
579 		 * and we must be able to resolve the parent */
580 		dn_type = META_DNTYPE_PARENT;
581 		dnParent( &ndn, &pndn );
582 		break;
583 
584 	case LDAP_REQ_MODRDN:
585 		/* if nnewSuperior is not NULL, it must resolve
586 		 * to the same candidate as the req_ndn */
587 		if ( op->orr_nnewSup ) {
588 			dn_type = META_DNTYPE_NEWPARENT;
589 		}
590 		break;
591 
592 	case LDAP_REQ_BIND:
593 		/* if bound as rootdn, the backend must bind to all targets
594 		 * with the administrative identity
595 		 * (unless pseoudoroot-bind-defer is TRUE) */
596 		if ( op->orb_method == LDAP_AUTH_SIMPLE && be_isroot_pw( op ) ) {
597 			op_type = META_OP_REQUIRE_ALL;
598 		}
599 		break;
600 
601 	case LDAP_REQ_COMPARE:
602 	case LDAP_REQ_DELETE:
603 	case LDAP_REQ_MODIFY:
604 		/* just a unique candidate */
605 		break;
606 
607 	case LDAP_REQ_SEARCH:
608 		/* allow multiple candidates for the searchBase */
609 		op_type = META_OP_ALLOW_MULTIPLE;
610 		break;
611 
612 	default:
613 		/* right now, just break (exop?) */
614 		break;
615 	}
616 
617 	/*
618 	 * require all connections ...
619 	 */
620 	if ( op_type == META_OP_REQUIRE_ALL ) {
621 		if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
622 			LDAP_BACK_CONN_ISPRIV_SET( mc );
623 
624 		} else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
625 			LDAP_BACK_CONN_ISANON_SET( mc );
626 		}
627 
628 		for ( i = 0; i < mi->mi_ntargets; i++ ) {
629 			/*
630 			 * The target is activated; if needed, it is
631 			 * also init'd
632 			 */
633 			candidates[ i ].sr_err = asyncmeta_init_one_conn( op,
634 				rs, mc, i, LDAP_BACK_CONN_ISPRIV( &mc_curr ),
635 				LDAP_BACK_DONTSEND, !new_conn );
636 			if ( candidates[ i ].sr_err == LDAP_SUCCESS ) {
637 				if ( new_conn && ( sendok & LDAP_BACK_BINDING ) ) {
638 					LDAP_BACK_CONN_BINDING_SET( &mc->mc_conns[ i ] );
639 				}
640 				META_CANDIDATE_SET( &candidates[ i ] );
641 				ncandidates++;
642 
643 			} else {
644 
645 				/*
646 				 * FIXME: in case one target cannot
647 				 * be init'd, should the other ones
648 				 * be tried?
649 				 */
650 				META_CANDIDATE_RESET( &candidates[ i ] );
651 				err = candidates[ i ].sr_err;
652 				continue;
653 			}
654 		}
655 
656 		if ( ncandidates == 0 ) {
657 			rs->sr_err = LDAP_NO_SUCH_OBJECT;
658 			rs->sr_text = "Unable to select valid candidates";
659 
660 			if ( sendok & LDAP_BACK_SENDERR ) {
661 				if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
662 					rs->sr_matched = mi->mi_suffix.bv_val;
663 				}
664 				send_ldap_result( op, rs );
665 				rs->sr_matched = NULL;
666 			}
667 			ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
668 			if ( alloc_new > 0) {
669 				asyncmeta_back_conn_free( mc );
670 			}
671 			return NULL;
672 		}
673 
674 		goto done;
675 	}
676 
677 	/*
678 	 * looks in cache, if any
679 	 */
680 	if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
681 		cached = i = asyncmeta_dncache_get_target( &mi->mi_cache, &op->o_req_ndn );
682 	}
683 
684 	if ( op_type == META_OP_REQUIRE_SINGLE ) {
685 		int			j;
686 
687 		for ( j = 0; j < mi->mi_ntargets; j++ ) {
688 			META_CANDIDATE_RESET( &candidates[ j ] );
689 		}
690 
691 		/*
692 		 * tries to get a unique candidate
693 		 * (takes care of default target)
694 		 */
695 		if ( i == META_TARGET_NONE ) {
696 			i = asyncmeta_get_candidate( op, rs, &ndn );
697 
698 			if ( rs->sr_err == LDAP_NO_SUCH_OBJECT && dn_type == META_DNTYPE_PARENT ) {
699 				i = asyncmeta_get_candidate( op, rs, &pndn );
700 			}
701 
702 			if ( i < 0 || rs->sr_err != LDAP_SUCCESS ) {
703 				if ( sendok & LDAP_BACK_SENDERR ) {
704 					if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
705 						rs->sr_matched = mi->mi_suffix.bv_val;
706 					}
707 					send_ldap_result( op, rs );
708 					rs->sr_matched = NULL;
709 				}
710 				ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
711 				if ( mc != NULL && alloc_new ) {
712 					asyncmeta_back_conn_free( mc );
713 				}
714 				return NULL;
715 			}
716 		}
717 
718 		if ( dn_type == META_DNTYPE_NEWPARENT && asyncmeta_get_candidate( op, rs, op->orr_nnewSup ) != i )
719 		{
720 			rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
721 			rs->sr_text = "Cross-target rename not supported";
722 			if ( sendok & LDAP_BACK_SENDERR ) {
723 				send_ldap_result( op, rs );
724 			}
725 			ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
726 			if ( mc != NULL && alloc_new > 0 ) {
727 				asyncmeta_back_conn_free( mc );
728 			}
729 			return NULL;
730 		}
731 
732 		Debug( LDAP_DEBUG_TRACE,
733 		       "==>asyncmeta__getconn: got target=%d for ndn=\"%s\" from cache\n",
734 		       i, op->o_req_ndn.bv_val );
735 			if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
736 				LDAP_BACK_CONN_ISPRIV_SET( mc );
737 
738 			} else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
739 				LDAP_BACK_CONN_ISANON_SET( mc );
740 			}
741 
742 		/*
743 		 * Clear all other candidates
744 		 */
745 			( void )asyncmeta_clear_unused_candidates( op, i , mc, candidates);
746 
747 		/*
748 		 * The target is activated; if needed, it is
749 		 * also init'd. In case of error, asyncmeta_init_one_conn
750 		 * sends the appropriate result.
751 		 */
752 		err = asyncmeta_init_one_conn( op, rs, mc, i,
753 			LDAP_BACK_CONN_ISPRIV( &mc_curr ), sendok, !new_conn );
754 		if ( err != LDAP_SUCCESS ) {
755 			/*
756 			 * FIXME: in case one target cannot
757 			 * be init'd, should the other ones
758 			 * be tried?
759 			 */
760 			META_CANDIDATE_RESET( &candidates[ i ] );
761 			ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
762 			if ( mc != NULL && alloc_new > 0 ) {
763 				asyncmeta_back_conn_free( mc );
764 			}
765 			return NULL;
766 		}
767 
768 		candidates[ i ].sr_err = LDAP_SUCCESS;
769 		META_CANDIDATE_SET( &candidates[ i ] );
770 		ncandidates++;
771 
772 		if ( candidate ) {
773 			*candidate = i;
774 		}
775 
776 	/*
777 	 * if no unique candidate ...
778 	 */
779 	} else {
780 	if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
781 		LDAP_BACK_CONN_ISPRIV_SET( mc );
782 
783 	} else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
784 		LDAP_BACK_CONN_ISANON_SET( mc );
785 	}
786 
787 		for ( i = 0; i < mi->mi_ntargets; i++ ) {
788 			a_metatarget_t		*mt = mi->mi_targets[ i ];
789 
790 			META_CANDIDATE_RESET( &candidates[ i ] );
791 
792 			if ( i == cached
793 				|| asyncmeta_is_candidate( mt, &op->o_req_ndn,
794 					op->o_tag == LDAP_REQ_SEARCH ? op->ors_scope : LDAP_SCOPE_SUBTREE ) )
795 			{
796 
797 				/*
798 				 * The target is activated; if needed, it is
799 				 * also init'd
800 				 */
801 				int lerr = asyncmeta_init_one_conn( op, rs, mc, i,
802 					LDAP_BACK_CONN_ISPRIV( &mc_curr ),
803 					LDAP_BACK_DONTSEND, !new_conn );
804 				candidates[ i ].sr_err = lerr;
805 				if ( lerr == LDAP_SUCCESS ) {
806 					META_CANDIDATE_SET( &candidates[ i ] );
807 					ncandidates++;
808 
809 					Debug( LDAP_DEBUG_TRACE, "%s: asyncmeta_getconn[%d]\n",
810 						op->o_log_prefix, i );
811 
812 				} else if ( lerr == LDAP_UNAVAILABLE && !META_BACK_ONERR_STOP( mi ) ) {
813 					META_CANDIDATE_SET( &candidates[ i ] );
814 
815 					Debug( LDAP_DEBUG_TRACE, "%s: asyncmeta_getconn[%d] %s\n",
816 						op->o_log_prefix, i,
817 						mt->mt_isquarantined != LDAP_BACK_FQ_NO ? "quarantined" : "unavailable" );
818 
819 				} else {
820 
821 					/*
822 					 * FIXME: in case one target cannot
823 					 * be init'd, should the other ones
824 					 * be tried?
825 					 */
826 					/* leave the target candidate, but record the error for later use */
827 					err = lerr;
828 
829 					if ( lerr == LDAP_UNAVAILABLE && mt->mt_isquarantined != LDAP_BACK_FQ_NO ) {
830 						Debug( LDAP_DEBUG_TRACE, "%s: asyncmeta_getconn[%d] quarantined err=%d\n",
831 							op->o_log_prefix, i, lerr );
832 
833 					} else {
834 						Debug( LDAP_DEBUG_ANY, "%s: asyncmeta_getconn[%d] failed err=%d\n",
835 							op->o_log_prefix, i, lerr );
836 					}
837 
838 					if ( META_BACK_ONERR_STOP( mi ) ) {
839 						if ( sendok & LDAP_BACK_SENDERR ) {
840 							send_ldap_result( op, rs );
841 						}
842 						ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
843 						if ( alloc_new > 0 ) {
844 							asyncmeta_back_conn_free( mc );
845 
846 						}
847 						return NULL;
848 					}
849 
850 					continue;
851 				}
852 
853 			}
854 		}
855 
856 		if ( ncandidates == 0 ) {
857 			if ( rs->sr_err == LDAP_SUCCESS ) {
858 				rs->sr_err = LDAP_NO_SUCH_OBJECT;
859 				rs->sr_text = "Unable to select valid candidates";
860 			}
861 
862 			if ( sendok & LDAP_BACK_SENDERR ) {
863 				if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
864 					rs->sr_matched = mi->mi_suffix.bv_val;
865 				}
866 				send_ldap_result( op, rs );
867 				rs->sr_matched = NULL;
868 			}
869 			if ( alloc_new > 0 ) {
870 				asyncmeta_back_conn_free( mc );
871 
872 			}
873 			ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
874 			return NULL;
875 		}
876 	}
877 
878 done:;
879 	rs->sr_err = LDAP_SUCCESS;
880 	rs->sr_text = NULL;
881 
882 	if ( new_conn ) {
883 		if ( !LDAP_BACK_PCONN_ISPRIV( mc ) ) {
884 			/*
885 			 * Err could be -1 in case a duplicate metaconn is inserted
886 			 */
887 			switch ( err ) {
888 			case 0:
889 				break;
890 			default:
891 				LDAP_BACK_CONN_CACHED_CLEAR( mc );
892 				if ( LogTest( LDAP_DEBUG_ANY ) ) {
893 					char buf[STRLENOF("4294967295U") + 1] = { 0 };
894 					mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
895 
896 					Debug( LDAP_DEBUG_ANY,
897 						"%s asyncmeta_getconn: candidates=%d conn=%s insert failed\n",
898 						op->o_log_prefix, ncandidates, buf );
899 				}
900 
901 				asyncmeta_back_conn_free( mc );
902 
903 				rs->sr_err = LDAP_OTHER;
904 				rs->sr_text = "Proxy bind collision";
905 				if ( sendok & LDAP_BACK_SENDERR ) {
906 					send_ldap_result( op, rs );
907 				}
908 				return NULL;
909 			}
910 		}
911 
912 		if ( LogTest( LDAP_DEBUG_TRACE ) ) {
913 			char buf[STRLENOF("4294967295U") + 1] = { 0 };
914 			mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
915 
916 			Debug( LDAP_DEBUG_TRACE,
917 				"%s asyncmeta_getconn: candidates=%d conn=%s inserted\n",
918 				op->o_log_prefix, ncandidates, buf );
919 		}
920 
921 	} else {
922 		if ( LogTest( LDAP_DEBUG_TRACE ) ) {
923 			char buf[STRLENOF("4294967295U") + 1] = { 0 };
924 			mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
925 
926 			Debug( LDAP_DEBUG_TRACE,
927 				"%s asyncmeta_getconn: candidates=%d conn=%s fetched\n",
928 				op->o_log_prefix, ncandidates, buf );
929 		}
930 	}
931 	ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
932 	return mc;
933 }
934 
935 void
asyncmeta_quarantine(Operation * op,a_metainfo_t * mi,SlapReply * rs,int candidate)936 asyncmeta_quarantine(
937 	Operation	*op,
938 	a_metainfo_t    *mi,
939 	SlapReply	*rs,
940 	int		candidate )
941 {
942 	a_metatarget_t		*mt = mi->mi_targets[ candidate ];
943 
944 	slap_retry_info_t	*ri = &mt->mt_quarantine;
945 
946 	ldap_pvt_thread_mutex_lock( &mt->mt_quarantine_mutex );
947 
948 	if ( rs->sr_err == LDAP_UNAVAILABLE ) {
949 		time_t	new_last = slap_get_time();
950 
951 		switch ( mt->mt_isquarantined ) {
952 		case LDAP_BACK_FQ_NO:
953 			if ( ri->ri_last == new_last ) {
954 				goto done;
955 			}
956 
957 			Debug( LDAP_DEBUG_ANY,
958 				"%s asyncmeta_quarantine[%d]: enter.\n",
959 				op->o_log_prefix, candidate );
960 
961 			ri->ri_idx = 0;
962 			ri->ri_count = 0;
963 			break;
964 
965 		case LDAP_BACK_FQ_RETRYING:
966 			Debug(LDAP_DEBUG_ANY,
967 			      "%s asyncmeta_quarantine[%d]: block #%d try #%d failed.\n",
968 			      op->o_log_prefix, candidate, ri->ri_idx,
969 			      ri->ri_count );
970 
971 			++ri->ri_count;
972 			if ( ri->ri_num[ ri->ri_idx ] != SLAP_RETRYNUM_FOREVER
973 				&& ri->ri_count == ri->ri_num[ ri->ri_idx ] )
974 			{
975 				ri->ri_count = 0;
976 				++ri->ri_idx;
977 			}
978 			break;
979 
980 		default:
981 			goto done;
982 		}
983 
984 		mt->mt_isquarantined = LDAP_BACK_FQ_YES;
985 		ri->ri_last = new_last;
986 
987 	} else if ( mt->mt_isquarantined == LDAP_BACK_FQ_RETRYING ) {
988 		Debug( LDAP_DEBUG_ANY,
989 			"%s asyncmeta_quarantine[%d]: exit.\n",
990 			op->o_log_prefix, candidate );
991 
992 		if ( mi->mi_quarantine_f ) {
993 			(void)mi->mi_quarantine_f( mi, candidate,
994 				mi->mi_quarantine_p );
995 		}
996 
997 		ri->ri_count = 0;
998 		ri->ri_idx = 0;
999 		mt->mt_isquarantined = LDAP_BACK_FQ_NO;
1000 		mt->mt_timeout_ops = 0;
1001 	}
1002 
1003 done:;
1004 	ldap_pvt_thread_mutex_unlock( &mt->mt_quarantine_mutex );
1005 }
1006 
1007 a_metaconn_t *
asyncmeta_get_next_mc(a_metainfo_t * mi)1008 asyncmeta_get_next_mc( a_metainfo_t *mi )
1009 {
1010 	a_metaconn_t *mc = NULL;
1011 
1012 	ldap_pvt_thread_mutex_lock( &mi->mi_mc_mutex );
1013 	if (mi->mi_next_conn >= mi->mi_num_conns-1) {
1014 		mi->mi_next_conn = 0;
1015 	} else {
1016 		mi->mi_next_conn++;
1017 	}
1018 
1019 	mc = &mi->mi_conns[mi->mi_next_conn];
1020 	ldap_pvt_thread_mutex_unlock( &mi->mi_mc_mutex );
1021 	return mc;
1022 }
1023 
asyncmeta_start_listeners(a_metaconn_t * mc,SlapReply * candidates,bm_context_t * bc)1024 int asyncmeta_start_listeners(a_metaconn_t *mc, SlapReply *candidates, bm_context_t *bc)
1025 {
1026 	int i;
1027 	for (i = 0; i < mc->mc_info->mi_ntargets; i++) {
1028 		asyncmeta_start_one_listener(mc, candidates, bc, i);
1029 	}
1030 	return LDAP_SUCCESS;
1031 }
1032 
asyncmeta_start_one_listener(a_metaconn_t * mc,SlapReply * candidates,bm_context_t * bc,int candidate)1033 int asyncmeta_start_one_listener(a_metaconn_t *mc,
1034 				 SlapReply *candidates,
1035 				 bm_context_t *bc,
1036 				 int candidate)
1037 {
1038 	a_metasingleconn_t *msc;
1039 	ber_socket_t s;
1040 
1041 	msc = &mc->mc_conns[candidate];
1042 	if ( slapd_shutdown || !META_BACK_CONN_INITED( msc ) || msc->msc_ld == NULL
1043 	    || META_BACK_CONN_INVALID(msc) || !META_IS_CANDIDATE( &candidates[ candidate ] )) {
1044 		return LDAP_SUCCESS;
1045 	}
1046 	bc->msgids[candidate] = candidates[candidate].sr_msgid;
1047 	if ( msc->conn == NULL) {
1048 		ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
1049 		if (s < 0) {
1050 			/* Todo a meaningful log pls */
1051 			return LDAP_OTHER;
1052 		}
1053 		msc->conn = connection_client_setup( s, asyncmeta_op_handle_result, mc );
1054 	}
1055 	connection_client_enable( msc->conn );
1056 	return LDAP_SUCCESS;
1057 }
1058 
1059 int
asyncmeta_clear_one_msc(Operation * op,a_metaconn_t * mc,int candidate,int unbind,const char * caller)1060 asyncmeta_clear_one_msc(
1061 	Operation	*op,
1062 	a_metaconn_t	*mc,
1063 	int		candidate,
1064 	int             unbind,
1065 	const char *caller)
1066 {
1067 	a_metasingleconn_t *msc;
1068 	if (mc == NULL) {
1069 		return 0;
1070 	}
1071 	msc = &mc->mc_conns[candidate];
1072 	if ( LogTest( asyncmeta_debug ) ) {
1073 		char	time_buf[ SLAP_TEXT_BUFLEN ];
1074 		asyncmeta_get_timestamp(time_buf);
1075 		Debug( asyncmeta_debug, "[%s] Resetting msc: %p, msc_ld: %p, "
1076 		       "msc_bound_ndn: %s, msc->conn: %p, %s \n",
1077 		       time_buf, msc, msc->msc_ld, msc->msc_bound_ndn.bv_val,
1078 		       msc->conn, caller ? caller : "" );
1079 	}
1080 	msc->msc_mscflags = 0;
1081 	if (msc->conn) {
1082 		connection_client_stop( msc->conn );
1083 		msc->conn = NULL;
1084 	}
1085 
1086 	if ( msc->msc_ld != NULL ) {
1087 
1088 #ifdef DEBUG_205
1089 		Debug( LDAP_DEBUG_ANY, "### %s asyncmeta_clear_one_msc ldap_unbind_ext[%d] ld=%p\n",
1090 		       op ? op->o_log_prefix : "", candidate, (void *)msc->msc_ld );
1091 #endif /* DEBUG_205 */
1092 
1093 		ldap_unbind_ext( msc->msc_ld, NULL, NULL );
1094 		msc->msc_ld = NULL;
1095 		ldap_ld_free( msc->msc_ldr, 0, NULL, NULL );
1096 		msc->msc_ldr = NULL;
1097 	}
1098 
1099 	if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
1100 		ber_memfree_x( msc->msc_bound_ndn.bv_val, NULL );
1101 		BER_BVZERO( &msc->msc_bound_ndn );
1102 	}
1103 
1104 	if ( !BER_BVISNULL( &msc->msc_cred ) ) {
1105 		memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
1106 		ber_memfree_x( msc->msc_cred.bv_val, NULL );
1107 		BER_BVZERO( &msc->msc_cred );
1108 	}
1109 	msc->msc_time = 0;
1110 	msc->msc_binding_time = 0;
1111 	msc->msc_result_time = 0;
1112 	return 0;
1113 }
1114 
asyncmeta_get_timestamp(char * buf)1115 void asyncmeta_get_timestamp(char *buf)
1116 {
1117 	struct timespec tp;
1118 	struct tm *ttm;
1119 	clock_gettime(CLOCK_REALTIME, &tp);
1120 	ttm = gmtime(&tp.tv_sec);
1121 	sprintf(buf, "%d:%d:%d.%ld", ttm->tm_hour, ttm->tm_min, ttm->tm_sec, tp.tv_nsec/1000);
1122 }
1123 
1124 int
asyncmeta_reset_msc(Operation * op,a_metaconn_t * mc,int candidate,int unbind,const char * caller)1125 asyncmeta_reset_msc(
1126 	Operation	*op,
1127 	a_metaconn_t	*mc,
1128 	int		candidate,
1129 	int             unbind,
1130 	const char *caller)
1131 {
1132 	a_metasingleconn_t *msc = &mc->mc_conns[candidate];
1133 	if ( LogTest( asyncmeta_debug ) ) {
1134 		char	time_buf[ SLAP_TEXT_BUFLEN ];
1135 		asyncmeta_get_timestamp(time_buf);
1136 		Debug(asyncmeta_debug, "[%x] Will attempt to reset [%s] msc: %p, "
1137 		      "msc->msc_binding_time: %x, msc->msc_flags:%x %s\n",
1138 		      (unsigned int)slap_get_time(), time_buf, msc,
1139 		      (unsigned int)msc->msc_binding_time, msc->msc_mscflags, caller );
1140 	}
1141 	if (msc->msc_active <= 1 && mc->mc_active < 1) {
1142 		bm_context_t *om;
1143 		asyncmeta_clear_one_msc(NULL, mc, candidate, 0, caller);
1144 		/* set whatever's in the queue to invalid, so the timeout loop cleans it up,
1145 		 * but do not invalidate the current op*/
1146 		LDAP_STAILQ_FOREACH( om, &mc->mc_om_list, bc_next ) {
1147 			if (om->candidates[candidate].sr_msgid >= 0 && (om->op != op)) {
1148 				om->bc_invalid = 1;
1149 			}
1150 		}
1151 		return LDAP_SUCCESS;
1152 	} else {
1153 		META_BACK_CONN_INVALID_SET(msc);
1154 		Debug( asyncmeta_debug, "[%x] Failed to reset msc %p, msc_active=%d, mc_active=%d, %s\n",
1155 		       (unsigned int)slap_get_time(), msc, msc->msc_active, mc->mc_active, caller );
1156 	}
1157 	return LDAP_OTHER;
1158 }
1159 
1160 
asyncmeta_log_msc(a_metasingleconn_t * msc)1161 void asyncmeta_log_msc(a_metasingleconn_t *msc)
1162 {
1163 	ber_socket_t s = 0;
1164 	if (msc->msc_ld) {
1165 		ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
1166 	}
1167 	Debug( asyncmeta_debug, "msc: %p, msc_ld: %p, msc_ld socket: %d, "
1168 	       "msc_bound_ndn: %s, msc->conn: %p\n", msc, msc->msc_ld,
1169 	       (int)s, msc->msc_bound_ndn.bv_val, msc->conn );
1170 }
1171 
asyncmeta_log_conns(a_metainfo_t * mi)1172 void asyncmeta_log_conns(a_metainfo_t *mi)
1173 {
1174 	a_metaconn_t *mc;
1175 	int i, j;
1176 	for (i = 0; i < mi->mi_num_conns; i++) {
1177 		mc = &mi->mi_conns[i];
1178 		Debug(asyncmeta_debug, "mc: %p, mc->pending_ops: %d\n", mc, mc->pending_ops);
1179 		for (j = 0; j < mi->mi_ntargets; j++ ) {
1180 			asyncmeta_log_msc(&mc->mc_conns[j]);
1181 		}
1182 
1183 	}
1184 }
1185 
1186 int
asyncmeta_db_has_pending_ops(a_metainfo_t * mi)1187 asyncmeta_db_has_pending_ops(a_metainfo_t *mi)
1188 {
1189 	int i;
1190 	if (mi->mi_ntargets == 0) {
1191 		return 0;
1192 	}
1193 
1194 	for (i = 0; i < mi->mi_num_conns; i++) {
1195 		if (mi->mi_conns[i].pending_ops > 0) {
1196 			return mi->mi_conns[i].pending_ops;
1197 		}
1198 	}
1199 
1200 	return 0;
1201 }
1202 
1203 
1204 int
asyncmeta_db_has_mscs(a_metainfo_t * mi)1205 asyncmeta_db_has_mscs(a_metainfo_t *mi)
1206 {
1207 	int i, j;
1208 	if (mi->mi_ntargets == 0) {
1209 		return 0;
1210 	}
1211 
1212 	for (i = 0; i < mi->mi_num_conns; i++) {
1213 		for (j = 0; j < mi->mi_ntargets; j++) {
1214 			if (mi->mi_conns[i].mc_conns[j].msc_ld != NULL ||
1215 			    mi->mi_conns[i].mc_conns[j].msc_ldr != NULL ) {
1216 				return 1;
1217 			}
1218 		}
1219 	}
1220 
1221 	return 0;
1222 }
1223