syncprov.c 77 KB
Newer Older
Howard Chu's avatar
Howard Chu committed
1
/* $OpenLDAP$ */
Howard Chu's avatar
Howard Chu committed
2
3
4
/* syncprov.c - syncrepl provider */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Kurt Zeilenga's avatar
Kurt Zeilenga committed
5
 * Copyright 2004-2007 The OpenLDAP Foundation.
Howard Chu's avatar
Howard Chu committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
/* ACKNOWLEDGEMENTS:
 * This work was initially developed by Howard Chu for inclusion in
 * OpenLDAP Software.
 */

#include "portable.h"

#ifdef SLAPD_OVER_SYNCPROV

Howard Chu's avatar
Howard Chu committed
25
26
#include <ac/string.h>
#include "lutil.h"
Howard Chu's avatar
Howard Chu committed
27
#include "slap.h"
Howard Chu's avatar
Howard Chu committed
28
#include "config.h"
29
#include "ldap_rq.h"
Howard Chu's avatar
Howard Chu committed
30

31
32
33
34
35
36
37
38
39
40
41
42
43
/* A modify request on a particular entry */
typedef struct modinst {
	struct modinst *mi_next;
	Operation *mi_op;
} modinst;

typedef struct modtarget {
	struct modinst *mt_mods;
	struct modinst *mt_tail;
	Operation *mt_op;
	ldap_pvt_thread_mutex_t mt_mutex;
} modtarget;

Howard Chu's avatar
Howard Chu committed
44
45
46
/* A queued result of a persistent search */
typedef struct syncres {
	struct syncres *s_next;
47
	struct berval s_dn;
Howard Chu's avatar
Howard Chu committed
48
49
	struct berval s_ndn;
	struct berval s_uuid;
50
51
52
	struct berval s_csn;
	char s_mode;
	char s_isreference;
Howard Chu's avatar
Howard Chu committed
53
54
} syncres;

Howard Chu's avatar
Howard Chu committed
55
56
57
58
59
60
/* Record of a persistent search */
typedef struct syncops {
	struct syncops *s_next;
	struct berval	s_base;		/* ndn of search base */
	ID		s_eid;		/* entryID of search base */
	Operation	*s_op;		/* search op */
61
	int		s_rid;
62
	int		s_sid;
Howard Chu's avatar
Howard Chu committed
63
	struct berval s_filterstr;
Howard Chu's avatar
Howard Chu committed
64
	int		s_flags;	/* search status */
Howard Chu's avatar
Howard Chu committed
65
66
67
68
#define	PS_IS_REFRESHING	0x01
#define	PS_IS_DETACHED		0x02
#define	PS_WROTE_BASE		0x04
#define	PS_FIND_BASE		0x08
69
#define	PS_FIX_FILTER		0x10
Howard Chu's avatar
Howard Chu committed
70

Howard Chu's avatar
Howard Chu committed
71
	int		s_inuse;	/* reference count */
Howard Chu's avatar
Howard Chu committed
72
73
	struct syncres *s_res;
	struct syncres *s_restail;
Howard Chu's avatar
Howard Chu committed
74
	struct re_s	*s_qtask;	/* task for playing psearch responses */
75
#define	RUNQ_INTERVAL	36000	/* a long time */
Howard Chu's avatar
Howard Chu committed
76
	ldap_pvt_thread_mutex_t	s_mutex;
Howard Chu's avatar
Howard Chu committed
77
78
} syncops;

79
80
81
82
83
84
/* A received sync control */
typedef struct sync_control {
	struct sync_cookie sr_state;
	int sr_rhint;
} sync_control;

85
86
87
#if 0 /* moved back to slap.h */
#define	o_sync	o_ctrlflag[slap_cids.sc_LDAPsync]
#endif
88
/* o_sync_mode uses data bits of o_sync */
89
#define	o_sync_mode	o_ctrlflag[slap_cids.sc_LDAPsync]
90
91
92
93
94
95

#define SLAP_SYNC_NONE					(LDAP_SYNC_NONE<<SLAP_CONTROL_SHIFT)
#define SLAP_SYNC_REFRESH				(LDAP_SYNC_REFRESH_ONLY<<SLAP_CONTROL_SHIFT)
#define SLAP_SYNC_PERSIST				(LDAP_SYNC_RESERVED<<SLAP_CONTROL_SHIFT)
#define SLAP_SYNC_REFRESH_AND_PERSIST	(LDAP_SYNC_REFRESH_AND_PERSIST<<SLAP_CONTROL_SHIFT)

Howard Chu's avatar
Howard Chu committed
96
97
98
99
100
101
/* Record of which searches matched at premodify step */
typedef struct syncmatches {
	struct syncmatches *sm_next;
	syncops *sm_op;
} syncmatches;

102
103
104
105
106
/* Session log data */
typedef struct slog_entry {
	struct slog_entry *se_next;
	struct berval se_uuid;
	struct berval se_csn;
107
	int	se_sid;
108
109
110
111
112
113
114
115
116
117
118
119
	ber_tag_t	se_tag;
} slog_entry;

typedef struct sessionlog {
	struct berval	sl_mincsn;
	int		sl_num;
	int		sl_size;
	slog_entry *sl_head;
	slog_entry *sl_tail;
	ldap_pvt_thread_mutex_t sl_mutex;
} sessionlog;

Howard Chu's avatar
Cleanup    
Howard Chu committed
120
/* The main state for this overlay */
Howard Chu's avatar
Howard Chu committed
121
122
typedef struct syncprov_info_t {
	syncops		*si_ops;
123
124
125
	BerVarray	si_ctxcsn;	/* ldapsync context */
	int		*si_sids;
	int		si_numcsns;
126
127
128
	int		si_chkops;	/* checkpointing info */
	int		si_chktime;
	int		si_numops;	/* number of ops since last checkpoint */
129
	int		si_nopres;	/* Skip present phase */
130
	int		si_usehint;	/* use reload hint */
131
	time_t	si_chklast;	/* time of last checkpoint */
132
	Avlnode	*si_mods;	/* entries being modified */
133
	sessionlog	*si_logs;
Howard Chu's avatar
Howard Chu committed
134
	ldap_pvt_thread_rdwr_t	si_csn_rwlock;
Howard Chu's avatar
Howard Chu committed
135
	ldap_pvt_thread_mutex_t	si_ops_mutex;
Howard Chu's avatar
Howard Chu committed
136
	ldap_pvt_thread_mutex_t	si_mods_mutex;
Howard Chu's avatar
Howard Chu committed
137
138
139
140
141
} syncprov_info_t;

typedef struct opcookie {
	slap_overinst *son;
	syncmatches *smatches;
142
143
144
145
146
	struct berval sdn;	/* DN of entry, for deletes */
	struct berval sndn;
	struct berval suuid;	/* UUID of entry */
	struct berval sctxcsn;
	int sreference;	/* Is the entry a reference? */
Howard Chu's avatar
Howard Chu committed
147
148
} opcookie;

149
typedef struct fbase_cookie {
150
151
152
153
	struct berval *fdn;	/* DN of a modified entry, for scope testing */
	syncops *fss;	/* persistent search we're testing against */
	int fbase;	/* if TRUE we found the search base and it's still valid */
	int fscope;	/* if TRUE then fdn is within the psearch scope */
154
} fbase_cookie;
Howard Chu's avatar
Howard Chu committed
155

156
static AttributeName csn_anlist[3];
Howard Chu's avatar
Howard Chu committed
157
158
static AttributeName uuid_anlist[2];

Howard Chu's avatar
Cleanup    
Howard Chu committed
159
/* Build a LDAPsync intermediate state control */
160
161
162
163
164
static int
syncprov_state_ctrl(
	Operation	*op,
	SlapReply	*rs,
	Entry		*e,
165
	int		entry_sync_state,
166
	LDAPControl	**ctrls,
167
168
169
	int		num_ctrls,
	int		send_cookie,
	struct berval	*cookie )
170
171
172
173
174
175
176
{
	Attribute* a;
	int ret;

	BerElementBuffer berbuf;
	BerElement *ber = (BerElement *)&berbuf;

177
	struct berval	entryuuid_bv = BER_BVNULL;
178
179
180
181

	ber_init2( ber, 0, LBER_USE_DER );
	ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );

Howard Chu's avatar
Howard Chu committed
182
	ctrls[num_ctrls] = op->o_tmpalloc( sizeof ( LDAPControl ), op->o_tmpmemctx );
183

184
185
186
187
188
	for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
		AttributeDescription *desc = a->a_desc;
		if ( desc == slap_schema.si_ad_entryUUID ) {
			entryuuid_bv = a->a_nvals[0];
			break;
189
		}
190
	}
191

192
193
	/* FIXME: what if entryuuid is NULL or empty ? */

194
195
196
197
198
199
	if ( send_cookie && cookie ) {
		ber_printf( ber, "{eOON}",
			entry_sync_state, &entryuuid_bv, cookie );
	} else {
		ber_printf( ber, "{eON}",
			entry_sync_state, &entryuuid_bv );
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
	}

	ctrls[num_ctrls]->ldctl_oid = LDAP_CONTROL_SYNC_STATE;
	ctrls[num_ctrls]->ldctl_iscritical = (op->o_sync == SLAP_CONTROL_CRITICAL);
	ret = ber_flatten2( ber, &ctrls[num_ctrls]->ldctl_value, 1 );

	ber_free_buf( ber );

	if ( ret < 0 ) {
		Debug( LDAP_DEBUG_TRACE,
			"slap_build_sync_ctrl: ber_flatten2 failed\n",
			0, 0, 0 );
		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
		return ret;
	}

	return LDAP_SUCCESS;
}

Howard Chu's avatar
Cleanup    
Howard Chu committed
219
/* Build a LDAPsync final state control */
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
static int
syncprov_done_ctrl(
	Operation	*op,
	SlapReply	*rs,
	LDAPControl	**ctrls,
	int			num_ctrls,
	int			send_cookie,
	struct berval *cookie,
	int			refreshDeletes )
{
	int ret;
	BerElementBuffer berbuf;
	BerElement *ber = (BerElement *)&berbuf;

	ber_init2( ber, NULL, LBER_USE_DER );
	ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );

Howard Chu's avatar
Howard Chu committed
237
	ctrls[num_ctrls] = op->o_tmpalloc( sizeof ( LDAPControl ), op->o_tmpmemctx );
238
239
240
241
242
243
244
245

	ber_printf( ber, "{" );
	if ( send_cookie && cookie ) {
		ber_printf( ber, "O", cookie );
	}
	if ( refreshDeletes == LDAP_SYNC_REFRESH_DELETES ) {
		ber_printf( ber, "b", refreshDeletes );
	}
246
	ber_printf( ber, "N}" );
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264

	ctrls[num_ctrls]->ldctl_oid = LDAP_CONTROL_SYNC_DONE;
	ctrls[num_ctrls]->ldctl_iscritical = (op->o_sync == SLAP_CONTROL_CRITICAL);
	ret = ber_flatten2( ber, &ctrls[num_ctrls]->ldctl_value, 1 );

	ber_free_buf( ber );

	if ( ret < 0 ) {
		Debug( LDAP_DEBUG_TRACE,
			"syncprov_done_ctrl: ber_flatten2 failed\n",
			0, 0, 0 );
		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
		return ret;
	}

	return LDAP_SUCCESS;
}

Howard Chu's avatar
Howard Chu committed
265
static int
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
syncprov_sendinfo(
	Operation	*op,
	SlapReply	*rs,
	int			type,
	struct berval *cookie,
	int			refreshDone,
	BerVarray	syncUUIDs,
	int			refreshDeletes )
{
	BerElementBuffer berbuf;
	BerElement *ber = (BerElement *)&berbuf;
	struct berval rspdata;

	int ret;

	ber_init2( ber, NULL, LBER_USE_DER );
	ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );

	if ( type ) {
		switch ( type ) {
		case LDAP_TAG_SYNC_NEW_COOKIE:
			ber_printf( ber, "tO", type, cookie );
			break;
		case LDAP_TAG_SYNC_REFRESH_DELETE:
		case LDAP_TAG_SYNC_REFRESH_PRESENT:
			ber_printf( ber, "t{", type );
			if ( cookie ) {
				ber_printf( ber, "O", cookie );
			}
			if ( refreshDone == 0 ) {
				ber_printf( ber, "b", refreshDone );
			}
			ber_printf( ber, "N}" );
			break;
		case LDAP_TAG_SYNC_ID_SET:
			ber_printf( ber, "t{", type );
			if ( cookie ) {
				ber_printf( ber, "O", cookie );
			}
			if ( refreshDeletes == 1 ) {
				ber_printf( ber, "b", refreshDeletes );
			}
			ber_printf( ber, "[W]", syncUUIDs );
			ber_printf( ber, "N}" );
			break;
		default:
			Debug( LDAP_DEBUG_TRACE,
				"syncprov_sendinfo: invalid syncinfo type (%d)\n",
				type, 0, 0 );
			return LDAP_OTHER;
		}
	}

	ret = ber_flatten2( ber, &rspdata, 0 );

	if ( ret < 0 ) {
		Debug( LDAP_DEBUG_TRACE,
			"syncprov_sendinfo: ber_flatten2 failed\n",
			0, 0, 0 );
		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
		return ret;
	}

Howard Chu's avatar
Cleanup    
Howard Chu committed
329
	rs->sr_rspoid = LDAP_SYNC_INFO;
330
331
332
333
334
335
336
	rs->sr_rspdata = &rspdata;
	send_ldap_intermediate( op, rs );
	rs->sr_rspdata = NULL;
	ber_free_buf( ber );

	return LDAP_SUCCESS;
}
337

Howard Chu's avatar
Cleanup    
Howard Chu committed
338
/* Find a modtarget in an AVL tree */
339
340
341
342
343
344
345
346
347
348
349
350
351
static int
sp_avl_cmp( const void *c1, const void *c2 )
{
	const modtarget *m1, *m2;
	int rc;

	m1 = c1; m2 = c2;
	rc = m1->mt_op->o_req_ndn.bv_len - m2->mt_op->o_req_ndn.bv_len;

	if ( rc ) return rc;
	return ber_bvcmp( &m1->mt_op->o_req_ndn, &m2->mt_op->o_req_ndn );
}

352
353
354
355
356
357
358
359
360
/* syncprov_findbase:
 *   finds the true DN of the base of a search (with alias dereferencing) and
 * checks to make sure the base entry doesn't get replaced with a different
 * entry (e.g., swapping trees via ModDN, or retargeting an alias). If a
 * change is detected, any persistent search on this base must be terminated /
 * reloaded.
 *   On the first call, we just save the DN and entryID. On subsequent calls
 * we compare the DN and entryID with the saved values.
 */
Howard Chu's avatar
Howard Chu committed
361
362
363
364
365
366
static int
findbase_cb( Operation *op, SlapReply *rs )
{
	slap_callback *sc = op->o_callback;

	if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) {
367
368
369
370
371
372
		fbase_cookie *fc = sc->sc_private;

		/* If no entryID, we're looking for the first time.
		 * Just store whatever we got.
		 */
		if ( fc->fss->s_eid == NOID ) {
Howard Chu's avatar
Howard Chu committed
373
			fc->fbase = 2;
374
375
			fc->fss->s_eid = rs->sr_entry->e_id;
			ber_dupbv( &fc->fss->s_base, &rs->sr_entry->e_nname );
376

377
		} else if ( rs->sr_entry->e_id == fc->fss->s_eid &&
Howard Chu's avatar
Howard Chu committed
378
			dn_match( &rs->sr_entry->e_nname, &fc->fss->s_base )) {
379

Howard Chu's avatar
Howard Chu committed
380
		/* OK, the DN is the same and the entryID is the same. */
Howard Chu's avatar
Howard Chu committed
381
382
			fc->fbase = 1;
		}
Howard Chu's avatar
Howard Chu committed
383
	}
384
385
386
	if ( rs->sr_err != LDAP_SUCCESS ) {
		Debug( LDAP_DEBUG_ANY, "findbase failed! %d\n", rs->sr_err,0,0 );
	}
Howard Chu's avatar
Howard Chu committed
387
388
389
	return LDAP_SUCCESS;
}

Howard Chu's avatar
Howard Chu committed
390
391
392
static Filter generic_filter = { LDAP_FILTER_PRESENT, { 0 }, NULL };
static struct berval generic_filterstr = BER_BVC("(objectclass=*)");

Howard Chu's avatar
Howard Chu committed
393
static int
394
syncprov_findbase( Operation *op, fbase_cookie *fc )
Howard Chu's avatar
Howard Chu committed
395
{
396
397
	opcookie *opc = op->o_callback->sc_private;
	slap_overinst *on = opc->son;
Howard Chu's avatar
Howard Chu committed
398

Howard Chu's avatar
Howard Chu committed
399
400
401
	/* Use basic parameters from syncrepl search, but use
	 * current op's threadctx / tmpmemctx
	 */
Howard Chu's avatar
Howard Chu committed
402
403
404
405
406
	ldap_pvt_thread_mutex_lock( &fc->fss->s_mutex );
	if ( fc->fss->s_flags & PS_FIND_BASE ) {
		slap_callback cb = {0};
		Operation fop;
		SlapReply frs = { REP_RESULT };
407
		BackendInfo *bi;
Howard Chu's avatar
Howard Chu committed
408
		int rc;
Howard Chu's avatar
Howard Chu committed
409

Howard Chu's avatar
Howard Chu committed
410
411
		fc->fss->s_flags ^= PS_FIND_BASE;
		ldap_pvt_thread_mutex_unlock( &fc->fss->s_mutex );
Howard Chu's avatar
Howard Chu committed
412

Howard Chu's avatar
Howard Chu committed
413
		fop = *fc->fss->s_op;
Howard Chu's avatar
Howard Chu committed
414

Howard Chu's avatar
Howard Chu committed
415
416
417
418
		fop.o_hdr = op->o_hdr;
		fop.o_bd = op->o_bd;
		fop.o_time = op->o_time;
		fop.o_tincr = op->o_tincr;
419
		bi = op->o_bd->bd_info;
Howard Chu's avatar
Howard Chu committed
420

Howard Chu's avatar
Howard Chu committed
421
422
		cb.sc_response = findbase_cb;
		cb.sc_private = fc;
Howard Chu's avatar
Howard Chu committed
423

Howard Chu's avatar
Howard Chu committed
424
425
426
427
428
429
430
431
432
433
		fop.o_sync_mode = 0;	/* turn off sync mode */
		fop.o_managedsait = SLAP_CONTROL_CRITICAL;
		fop.o_callback = &cb;
		fop.o_tag = LDAP_REQ_SEARCH;
		fop.ors_scope = LDAP_SCOPE_BASE;
		fop.ors_limit = NULL;
		fop.ors_slimit = 1;
		fop.ors_tlimit = SLAP_NO_LIMIT;
		fop.ors_attrs = slap_anlist_no_attrs;
		fop.ors_attrsonly = 1;
Howard Chu's avatar
Howard Chu committed
434
435
		fop.ors_filter = &generic_filter;
		fop.ors_filterstr = generic_filterstr;
Howard Chu's avatar
Howard Chu committed
436

437
438
		rc = overlay_op_walk( &fop, &frs, op_search, on->on_info, on );
		op->o_bd->bd_info = bi;
Howard Chu's avatar
Howard Chu committed
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
	} else {
		ldap_pvt_thread_mutex_unlock( &fc->fss->s_mutex );
		fc->fbase = 1;
	}

	/* After the first call, see if the fdn resides in the scope */
	if ( fc->fbase == 1 ) {
		switch ( fc->fss->s_op->ors_scope ) {
		case LDAP_SCOPE_BASE:
			fc->fscope = dn_match( fc->fdn, &fc->fss->s_base );
			break;
		case LDAP_SCOPE_ONELEVEL: {
			struct berval pdn;
			dnParent( fc->fdn, &pdn );
			fc->fscope = dn_match( &pdn, &fc->fss->s_base );
			break; }
		case LDAP_SCOPE_SUBTREE:
			fc->fscope = dnIsSuffix( fc->fdn, &fc->fss->s_base );
			break;
		case LDAP_SCOPE_SUBORDINATE:
			fc->fscope = dnIsSuffix( fc->fdn, &fc->fss->s_base ) &&
				!dn_match( fc->fdn, &fc->fss->s_base );
			break;
		}
	}

	if ( fc->fbase )
		return LDAP_SUCCESS;
Howard Chu's avatar
Howard Chu committed
467
468
469
470
471
472

	/* If entryID has changed, then the base of this search has
	 * changed. Invalidate the psearch.
	 */
	return LDAP_NO_SUCH_OBJECT;
}
Howard Chu's avatar
Howard Chu committed
473

474
/* syncprov_findcsn:
475
 *   This function has three different purposes, but they all use a search
476
 * that filters on entryCSN so they're combined here.
477
478
479
480
481
 * 1: at startup time, after a contextCSN has been read from the database,
 * we search for all entries with CSN >= contextCSN in case the contextCSN
 * was not checkpointed at the previous shutdown.
 *
 * 2: when the current contextCSN is known and we have a sync cookie, we search
Howard Chu's avatar
Howard Chu committed
482
483
 * for one entry with CSN = the cookie CSN. If not found, try <= cookie CSN.
 * If an entry is found, the cookie CSN is valid, otherwise it is stale.
484
 *
485
 * 3: during a refresh phase, we search for all entries with CSN <= the cookie
486
487
488
 * CSN, and generate Present records for them. We always collect this result
 * in SyncID sets, even if there's only one match.
 */
489
490
491
492
493
typedef enum find_csn_t {
	FIND_MAXCSN	= 1,
	FIND_CSN	= 2,
	FIND_PRESENT	= 3
} find_csn_t;
494
495
496
497
498
499
500
501
502

static int
findmax_cb( Operation *op, SlapReply *rs )
{
	if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) {
		struct berval *maxcsn = op->o_callback->sc_private;
		Attribute *a = attr_find( rs->sr_entry->e_attrs,
			slap_schema.si_ad_entryCSN );

503
		if ( a && ber_bvcmp( &a->a_vals[0], maxcsn ) > 0 ) {
504
505
506
507
508
509
			maxcsn->bv_len = a->a_vals[0].bv_len;
			strcpy( maxcsn->bv_val, a->a_vals[0].bv_val );
		}
	}
	return LDAP_SUCCESS;
}
Howard Chu's avatar
Howard Chu committed
510
511
512
513
514
515

static int
findcsn_cb( Operation *op, SlapReply *rs )
{
	slap_callback *sc = op->o_callback;

Howard Chu's avatar
Howard Chu committed
516
517
518
519
520
	/* We just want to know that at least one exists, so it's OK if
	 * we exceed the unchecked limit.
	 */
	if ( rs->sr_err == LDAP_ADMINLIMIT_EXCEEDED ||
		(rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS )) {
521
		sc->sc_private = (void *)1;
Howard Chu's avatar
Howard Chu committed
522
523
524
525
	}
	return LDAP_SUCCESS;
}

526
527
/* Build a list of entryUUIDs for sending in a SyncID set */

528
529
#define UUID_LEN	16

Howard Chu's avatar
Howard Chu committed
530
531
532
typedef struct fpres_cookie {
	int num;
	BerVarray uuids;
533
	char *last;
Howard Chu's avatar
Howard Chu committed
534
535
536
537
538
539
540
} fpres_cookie;

static int
findpres_cb( Operation *op, SlapReply *rs )
{
	slap_callback *sc = op->o_callback;
	fpres_cookie *pc = sc->sc_private;
541
	Attribute *a;
Howard Chu's avatar
Howard Chu committed
542
	int ret = SLAP_CB_CONTINUE;
Howard Chu's avatar
Howard Chu committed
543

544
545
546
	switch ( rs->sr_type ) {
	case REP_SEARCH:
		a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryUUID );
547
548
549
550
		if ( a ) {
			pc->uuids[pc->num].bv_val = pc->last;
			AC_MEMCPY( pc->uuids[pc->num].bv_val, a->a_nvals[0].bv_val,
				pc->uuids[pc->num].bv_len );
Howard Chu's avatar
Howard Chu committed
551
			pc->num++;
552
553
			pc->last = pc->uuids[pc->num].bv_val;
			pc->uuids[pc->num].bv_val = NULL;
Howard Chu's avatar
Howard Chu committed
554
		}
555
		ret = LDAP_SUCCESS;
556
557
558
559
		if ( pc->num != SLAP_SYNCUUID_SET_SIZE )
			break;
		/* FALLTHRU */
	case REP_RESULT:
Howard Chu's avatar
Howard Chu committed
560
561
		ret = rs->sr_err;
		if ( pc->num ) {
562
			ret = syncprov_sendinfo( op, rs, LDAP_TAG_SYNC_ID_SET, NULL,
Howard Chu's avatar
Howard Chu committed
563
				0, pc->uuids, 0 );
564
			pc->uuids[pc->num].bv_val = pc->last;
Howard Chu's avatar
Howard Chu committed
565
			pc->num = 0;
566
			pc->last = pc->uuids[0].bv_val;
Howard Chu's avatar
Howard Chu committed
567
		}
568
569
570
		break;
	default:
		break;
Howard Chu's avatar
Howard Chu committed
571
572
573
574
575
	}
	return ret;
}

static int
576
syncprov_findcsn( Operation *op, find_csn_t mode )
Howard Chu's avatar
Howard Chu committed
577
578
579
580
581
582
583
584
585
{
	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
	syncprov_info_t		*si = on->on_bi.bi_private;

	slap_callback cb = {0};
	Operation fop;
	SlapReply frs = { REP_RESULT };
	char buf[LDAP_LUTIL_CSNSTR_BUFSIZE + STRLENOF("(entryCSN<=)")];
	char cbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
586
	struct berval maxcsn;
587
	Filter cf;
Ralf Haferkamp's avatar
Ralf Haferkamp committed
588
	AttributeAssertion eq = ATTRIBUTEASSERTION_INIT;
Howard Chu's avatar
Howard Chu committed
589
	fpres_cookie pcookie;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
590
	sync_control *srs = NULL;
591
592
	struct slap_limits_set fc_limits;
	int i, rc = LDAP_SUCCESS, findcsn_retry = 1;
593
	int maxid = 0;
594
595
596

	if ( mode != FIND_MAXCSN ) {
		srs = op->o_controls[slap_cids.sc_LDAPsync];
Howard Chu's avatar
Howard Chu committed
597
598
599
	}

	fop = *op;
600
	fop.o_sync_mode &= SLAP_CONTROL_MASK;	/* turn off sync_mode */
601
602
	/* We want pure entries, not referrals */
	fop.o_managedsait = SLAP_CONTROL_CRITICAL;
Howard Chu's avatar
Howard Chu committed
603

604
605
	cf.f_ava = &eq;
	cf.f_av_desc = slap_schema.si_ad_entryCSN;
606
	BER_BVZERO( &cf.f_av_value );
607
608
	cf.f_next = NULL;

609
610
611
612
	fop.o_callback = &cb;
	fop.ors_limit = NULL;
	fop.ors_tlimit = SLAP_NO_LIMIT;
	fop.ors_filter = &cf;
613
	fop.ors_filterstr.bv_val = buf;
614

Howard Chu's avatar
Howard Chu committed
615
again:
616
617
618
	switch( mode ) {
	case FIND_MAXCSN:
		cf.f_choice = LDAP_FILTER_GE;
619
620
621
622
623
624
625
626
		cf.f_av_value = si->si_ctxcsn[0];
		/* If there are multiple CSNs, use the largest */
		for ( i=1; i<si->si_numcsns; i++) {
			if ( ber_bvcmp( &cf.f_av_value, &si->si_ctxcsn[i] ) < 0 ) {
				cf.f_av_value = si->si_ctxcsn[i];
				maxid = i;
			}
		}
627
628
629
630
631
		fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
			"(entryCSN>=%s)", cf.f_av_value.bv_val );
		if ( fop.ors_filterstr.bv_len < 0 || fop.ors_filterstr.bv_len >= sizeof( buf ) ) {
			return LDAP_OTHER;
		}
632
633
634
635
636
		fop.ors_attrsonly = 0;
		fop.ors_attrs = csn_anlist;
		fop.ors_slimit = SLAP_NO_LIMIT;
		cb.sc_private = &maxcsn;
		cb.sc_response = findmax_cb;
637
		strcpy( cbuf, cf.f_av_value.bv_val );
638
		maxcsn.bv_val = cbuf;
639
		maxcsn.bv_len = cf.f_av_value.bv_len;
640
641
		break;
	case FIND_CSN:
642
643
644
645
646
647
648
649
650
651
		if ( BER_BVISEMPTY( &cf.f_av_value )) {
			cf.f_av_value = srs->sr_state.ctxcsn[0];
			/* If there are multiple CSNs, use the smallest */
			for ( i=1; i<srs->sr_state.numcsns; i++ ) {
				if ( ber_bvcmp( &cf.f_av_value, &srs->sr_state.ctxcsn[i] )
					> 0 ) {
					cf.f_av_value = srs->sr_state.ctxcsn[i];
				}
			}
		}
Howard Chu's avatar
Howard Chu committed
652
653
654
		/* Look for exact match the first time */
		if ( findcsn_retry ) {
			cf.f_choice = LDAP_FILTER_EQUALITY;
655
656
			fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
				"(entryCSN=%s)", cf.f_av_value.bv_val );
Howard Chu's avatar
Howard Chu committed
657
658
659
		/* On retry, look for <= */
		} else {
			cf.f_choice = LDAP_FILTER_LE;
660
			fop.ors_limit = &fc_limits;
661
			memset( &fc_limits, 0, sizeof( fc_limits ));
662
			fc_limits.lms_s_unchecked = 1;
663
664
665
666
667
			fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
				"(entryCSN<=%s)", cf.f_av_value.bv_val );
		}
		if ( fop.ors_filterstr.bv_len < 0 || fop.ors_filterstr.bv_len >= sizeof( buf ) ) {
			return LDAP_OTHER;
Howard Chu's avatar
Howard Chu committed
668
		}
669
670
671
672
		fop.ors_attrsonly = 1;
		fop.ors_attrs = slap_anlist_no_attrs;
		fop.ors_slimit = 1;
		cb.sc_private = NULL;
Howard Chu's avatar
Howard Chu committed
673
		cb.sc_response = findcsn_cb;
674
675
		break;
	case FIND_PRESENT:
676
677
		fop.ors_filter = op->ors_filter;
		fop.ors_filterstr = op->ors_filterstr;
Howard Chu's avatar
Howard Chu committed
678
679
680
		fop.ors_attrsonly = 0;
		fop.ors_attrs = uuid_anlist;
		fop.ors_slimit = SLAP_NO_LIMIT;
Howard Chu's avatar
Howard Chu committed
681
		cb.sc_private = &pcookie;
Howard Chu's avatar
Howard Chu committed
682
683
		cb.sc_response = findpres_cb;
		pcookie.num = 0;
684
685
686
687
688
689
690
691
692
693
694
695

		/* preallocate storage for a full set */
		pcookie.uuids = op->o_tmpalloc( (SLAP_SYNCUUID_SET_SIZE+1) *
			sizeof(struct berval) + SLAP_SYNCUUID_SET_SIZE * UUID_LEN,
			op->o_tmpmemctx );
		pcookie.last = (char *)(pcookie.uuids + SLAP_SYNCUUID_SET_SIZE+1);
		pcookie.uuids[0].bv_val = pcookie.last;
		pcookie.uuids[0].bv_len = UUID_LEN;
		for (i=1; i<SLAP_SYNCUUID_SET_SIZE; i++) {
			pcookie.uuids[i].bv_val = pcookie.uuids[i-1].bv_val + UUID_LEN;
			pcookie.uuids[i].bv_len = UUID_LEN;
		}
696
		break;
Howard Chu's avatar
Howard Chu committed
697
698
699
	}

	fop.o_bd->bd_info = on->on_info->oi_orig;
700
	fop.o_bd->be_search( &fop, &frs );
701
	fop.o_bd->bd_info = (BackendInfo *)on;
Howard Chu's avatar
Howard Chu committed
702

703
704
	switch( mode ) {
	case FIND_MAXCSN:
705
706
707
708
		if ( ber_bvcmp( &si->si_ctxcsn[maxid], &maxcsn )) {
			ber_bvreplace( &si->si_ctxcsn[maxid], &maxcsn );
			si->si_numops++;	/* ensure a checkpoint */
		}
709
710
711
		break;
	case FIND_CSN:
		/* If matching CSN was not found, invalidate the context. */
Howard Chu's avatar
Howard Chu committed
712
713
714
715
716
717
718
719
		if ( !cb.sc_private ) {
			/* If we didn't find an exact match, then try for <= */
			if ( findcsn_retry ) {
				findcsn_retry = 0;
				goto again;
			}
			rc = LDAP_NO_SUCH_OBJECT;
		}
720
721
		break;
	case FIND_PRESENT:
722
		op->o_tmpfree( pcookie.uuids, op->o_tmpmemctx );
723
		break;
Howard Chu's avatar
Howard Chu committed
724
725
	}

726
	return rc;
Howard Chu's avatar
Howard Chu committed
727
728
}

729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
static void
syncprov_free_syncop( syncops *so )
{
	syncres *sr, *srnext;
	GroupAssertion *ga, *gnext;

	ldap_pvt_thread_mutex_lock( &so->s_mutex );
	if ( --so->s_inuse > 0 ) {
		ldap_pvt_thread_mutex_unlock( &so->s_mutex );
		return;
	}
	ldap_pvt_thread_mutex_unlock( &so->s_mutex );
	if ( so->s_flags & PS_IS_DETACHED ) {
		filter_free( so->s_op->ors_filter );
		for ( ga = so->s_op->o_groups; ga; ga=gnext ) {
			gnext = ga->ga_next;
			ch_free( ga );
		}
		ch_free( so->s_op );
	}
	ch_free( so->s_base.bv_val );
	for ( sr=so->s_res; sr; sr=srnext ) {
		srnext = sr->s_next;
		ch_free( sr );
	}
	ldap_pvt_thread_mutex_destroy( &so->s_mutex );
	ch_free( so );
}

758
759
/* Send a persistent search response */
static int
760
761
syncprov_sendresp( Operation *op, opcookie *opc, syncops *so,
	Entry **e, int mode )
762
763
764
765
766
{
	slap_overinst *on = opc->son;

	SlapReply rs = { REP_SEARCH };
	LDAPControl *ctrls[2];
767
	struct berval cookie, csns[2];
768
769
770
	Entry e_uuid = {0};
	Attribute a_uuid = {0};

Howard Chu's avatar
Howard Chu committed
771
772
773
	if ( so->s_op->o_abandon )
		return SLAPD_ABANDON;

774
	ctrls[1] = NULL;
775
776
	csns[0] = opc->sctxcsn;
	BER_BVZERO( &csns[1] );
777
	slap_compose_sync_cookie( op, &cookie, csns, so->s_rid, so->s_sid );
778

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
779
780
	Debug( LDAP_DEBUG_SYNC, "syncprov_sendresp: cookie=%s\n", cookie.bv_val, 0, 0 );

781
782
783
	e_uuid.e_attrs = &a_uuid;
	a_uuid.a_desc = slap_schema.si_ad_entryUUID;
	a_uuid.a_nvals = &opc->suuid;
784
	rs.sr_err = syncprov_state_ctrl( op, &rs, &e_uuid,
785
		mode, ctrls, 0, 1, &cookie );
Howard Chu's avatar
Howard Chu committed
786
	op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
787

788
	rs.sr_ctrls = ctrls;
789
	op->o_bd->bd_info = (BackendInfo *)on->on_info;
790
791
	switch( mode ) {
	case LDAP_SYNC_ADD:
792
793
794
		rs.sr_entry = *e;
		if ( rs.sr_entry->e_private )
			rs.sr_flags = REP_ENTRY_MUSTRELEASE;
795
		if ( opc->sreference ) {
796
			rs.sr_ref = get_entry_referrals( op, rs.sr_entry );
797
			rs.sr_err = send_search_reference( op, &rs );
798
			ber_bvarray_free( rs.sr_ref );
799
800
			if ( !rs.sr_entry )
				*e = NULL;
801
802
803
804
			break;
		}
		/* fallthru */
	case LDAP_SYNC_MODIFY:
805
806
807
		rs.sr_entry = *e;
		if ( rs.sr_entry->e_private )
			rs.sr_flags = REP_ENTRY_MUSTRELEASE;
808
		rs.sr_attrs = op->ors_attrs;
809
		rs.sr_err = send_search_entry( op, &rs );
810
811
		if ( !rs.sr_entry )
			*e = NULL;
812
813
814
815
816
817
818
		break;
	case LDAP_SYNC_DELETE:
		e_uuid.e_attrs = NULL;
		e_uuid.e_name = opc->sdn;
		e_uuid.e_nname = opc->sndn;
		rs.sr_entry = &e_uuid;
		if ( opc->sreference ) {
819
			struct berval bv = BER_BVNULL;
820
			rs.sr_ref = &bv;
821
			rs.sr_err = send_search_reference( op, &rs );
Howard Chu's avatar
Howard Chu committed
822
		} else {
823
			rs.sr_err = send_search_entry( op, &rs );
824
825
826
827
828
		}
		break;
	default:
		assert(0);
	}
829
830
831
832
833
	/* In case someone else freed it already? */
	if ( rs.sr_ctrls ) {
		op->o_tmpfree( rs.sr_ctrls[0], op->o_tmpmemctx );
		rs.sr_ctrls = NULL;
	}
834

835
836
837
838
839
840
841
842
843
844
	return rs.sr_err;
}

/* Play back queued responses */
static int
syncprov_qplay( Operation *op, slap_overinst *on, syncops *so )
{
	syncres *sr;
	Entry *e;
	opcookie opc;
845
	int rc = 0;
846
847
848
849

	opc.son = on;

	for (;;) {
850
		ldap_pvt_thread_mutex_lock( &so->s_mutex );
851
852
853
854
855
		sr = so->s_res;
		if ( sr )
			so->s_res = sr->s_next;
		if ( !so->s_res )
			so->s_restail = NULL;
856
		ldap_pvt_thread_mutex_unlock( &so->s_mutex );
857

858
		if ( !sr || so->s_op->o_abandon )
859
860
			break;

861
862
863
864
865
866
867
868
		opc.sdn = sr->s_dn;
		opc.sndn = sr->s_ndn;
		opc.suuid = sr->s_uuid;
		opc.sctxcsn = sr->s_csn;
		opc.sreference = sr->s_isreference;
		e = NULL;

		if ( sr->s_mode != LDAP_SYNC_DELETE ) {
869
			rc = overlay_entry_get_ov( op, &opc.sndn, NULL, NULL, 0, &e, on );
870
			if ( rc ) {
871
				Debug( LDAP_DEBUG_SYNC, "syncprov_qplay: failed to get %s, "
Howard Chu's avatar
Howard Chu committed
872
					"error (%d), ignoring...\n", opc.sndn.bv_val, rc, 0 );
873
				ch_free( sr );
874
				rc = 0;
875
				continue;
876
			}
877
878
		}
		rc = syncprov_sendresp( op, &opc, so, &e, sr->s_mode );
879

880
		if ( e ) {
881
			overlay_entry_release_ov( op, e, 0, on );
882
883
884
		}

		ch_free( sr );
885
886
887

		if ( rc )
			break;
888
	}
889
890
891
892
893
894
895
896
897
898
	return rc;
}

/* runqueue task for playing back queued responses */
static void *
syncprov_qtask( void *ctx, void *arg )
{
	struct re_s *rtask = arg;
	syncops *so = rtask->arg;
	slap_overinst *on = so->s_op->o_private;
899
	OperationBuffer opbuf;
900
901
	Operation *op;
	BackendDB be;
902
	int rc;
903

904
	op = &opbuf.ob_op;
905
	*op = *so->s_op;
906
907
908
	op->o_hdr = &opbuf.ob_hdr;
	op->o_controls = opbuf.ob_controls;
	memset( op->o_controls, 0, sizeof(opbuf.ob_controls) );
909
910
911

	*op->o_hdr = *so->s_op->o_hdr;

912
	op->o_tmpmemctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 1);
913
914
915
916
917
918
919
920
921
922
	op->o_tmpmfuncs = &slap_sl_mfuncs;
	op->o_threadctx = ctx;

	/* syncprov_qplay expects a fake db */
	be = *so->s_op->o_bd;
	be.be_flags |= SLAP_DBFLAG_OVERLAY;
	op->o_bd = &be;
	op->o_private = NULL;
	op->o_callback = NULL;

923
	rc = syncprov_qplay( op, on, so );
924

925
926
927
	/* decrement use count... */
	syncprov_free_syncop( so );

928
929
	/* wait until we get explicitly scheduled again */
	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
930
	ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
931
	if ( rc == 0 ) {
932
		ldap_pvt_runqueue_resched( &slapd_rq, rtask, 1 );
933
934
935
936
	} else {
		/* bail out on any error */
		ldap_pvt_runqueue_remove( &slapd_rq, rtask );
	}
937
938
	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );

939
940
941
942
943
944
945
946
947
948
949
#if 0	/* FIXME: connection_close isn't exported from slapd.
		 * should it be?
		 */
	if ( rc ) {
		ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
		if ( connection_state_closing( op->o_conn )) {
			connection_close( op->o_conn );
		}
		ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
	}
#endif
950
951
952
	return NULL;
}

953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
/* Start the task to play back queued psearch responses */
static void
syncprov_qstart( syncops *so )
{
	int wake=0;
	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
	if ( !so->s_qtask ) {
		so->s_qtask = ldap_pvt_runqueue_insert( &slapd_rq, RUNQ_INTERVAL,
			syncprov_qtask, so, "syncprov_qtask",
			so->s_op->o_conn->c_peer_name.bv_val );
		++so->s_inuse;
		wake = 1;
	} else {
		if (!ldap_pvt_runqueue_isrunning( &slapd_rq, so->s_qtask ) &&
			!so->s_qtask->next_sched.tv_sec ) {
			so->s_qtask->interval.tv_sec = 0;
			ldap_pvt_runqueue_resched( &slapd_rq, so->s_qtask, 0 );
			so->s_qtask->interval.tv_sec = RUNQ_INTERVAL;
			++so->s_inuse;
			wake = 1;
		}
	}
	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
	if ( wake )
		slap_wake_listener();
}

980
981
982
983
984
/* Queue a persistent search response */
static int
syncprov_qresp( opcookie *opc, syncops *so, int mode )
{
	syncres *sr;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
985
	int sid, srsize;
986
987
988

	/* Don't send changes back to their originator */
	sid = slap_parse_csn_sid( &opc->sctxcsn );
989
	if ( sid >= 0 && sid == so->s_sid )
990
		return LDAP_SUCCESS;
991

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
992
993
994
995
996
	srsize = sizeof(syncres) + opc->suuid.bv_len + 1 +
		opc->sdn.bv_len + 1 + opc->sndn.bv_len + 1;
	if ( opc->sctxcsn.bv_len )
		srsize += opc->sctxcsn.bv_len + 1;
	sr = ch_malloc( srsize );
997
998
999
1000
	sr->s_next = NULL;
	sr->s_dn.bv_val = (char *)(sr + 1);
	sr->s_dn.bv_len = opc->sdn.bv_len;
	sr->s_mode = mode;
For faster browsing, not all history is shown. View entire blame