1 /* $OpenLDAP: pkg/ldap/servers/slapd/overlays/syncprov.c,v 1.147.2.34 2008/07/10 00:13:08 quanah Exp $ */
2 /* syncprov.c - syncrepl provider */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2004-2008 The OpenLDAP Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
12 * A copy of this license is available in the file LICENSE in the
13 * top-level directory of the distribution or, alternatively, at
14 * <http://www.OpenLDAP.org/license.html>.
17 * This work was initially developed by Howard Chu for inclusion in
23 #ifdef SLAPD_OVER_SYNCPROV
25 #include <ac/string.h>
31 /* A modify request on a particular entry */
32 typedef struct modinst
{
33 struct modinst
*mi_next
;
37 typedef struct modtarget
{
38 struct modinst
*mt_mods
;
39 struct modinst
*mt_tail
;
41 ldap_pvt_thread_mutex_t mt_mutex
;
44 /* A queued result of a persistent search */
45 typedef struct syncres
{
46 struct syncres
*s_next
;
55 /* Record of a persistent search */
56 typedef struct syncops
{
57 struct syncops
*s_next
;
58 struct berval s_base
; /* ndn of search base */
59 ID s_eid
; /* entryID of search base */
60 Operation
*s_op
; /* search op */
63 struct berval s_filterstr
;
64 int s_flags
; /* search status */
65 #define PS_IS_REFRESHING 0x01
66 #define PS_IS_DETACHED 0x02
67 #define PS_WROTE_BASE 0x04
68 #define PS_FIND_BASE 0x08
69 #define PS_FIX_FILTER 0x10
71 int s_inuse
; /* reference count */
72 struct syncres
*s_res
;
73 struct syncres
*s_restail
;
74 struct re_s
*s_qtask
; /* task for playing psearch responses */
75 #define RUNQ_INTERVAL 36000 /* a long time */
76 ldap_pvt_thread_mutex_t s_mutex
;
79 /* A received sync control */
80 typedef struct sync_control
{
81 struct sync_cookie sr_state
;
85 #if 0 /* moved back to slap.h */
86 #define o_sync o_ctrlflag[slap_cids.sc_LDAPsync]
88 /* o_sync_mode uses data bits of o_sync */
89 #define o_sync_mode o_ctrlflag[slap_cids.sc_LDAPsync]
91 #define SLAP_SYNC_NONE (LDAP_SYNC_NONE<<SLAP_CONTROL_SHIFT)
92 #define SLAP_SYNC_REFRESH (LDAP_SYNC_REFRESH_ONLY<<SLAP_CONTROL_SHIFT)
93 #define SLAP_SYNC_PERSIST (LDAP_SYNC_RESERVED<<SLAP_CONTROL_SHIFT)
94 #define SLAP_SYNC_REFRESH_AND_PERSIST (LDAP_SYNC_REFRESH_AND_PERSIST<<SLAP_CONTROL_SHIFT)
96 /* Record of which searches matched at premodify step */
97 typedef struct syncmatches
{
98 struct syncmatches
*sm_next
;
102 /* Session log data */
103 typedef struct slog_entry
{
104 struct slog_entry
*se_next
;
105 struct berval se_uuid
;
106 struct berval se_csn
;
111 typedef struct sessionlog
{
112 struct berval sl_mincsn
;
117 ldap_pvt_thread_mutex_t sl_mutex
;
120 /* The main state for this overlay */
121 typedef struct syncprov_info_t
{
123 BerVarray si_ctxcsn
; /* ldapsync context */
126 int si_chkops
; /* checkpointing info */
128 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 */
134 ldap_pvt_thread_rdwr_t si_csn_rwlock
;
135 ldap_pvt_thread_mutex_t si_ops_mutex
;
136 ldap_pvt_thread_mutex_t si_mods_mutex
;
139 typedef struct opcookie
{
141 syncmatches
*smatches
;
142 struct berval sdn
; /* DN of entry, for deletes */
144 struct berval suuid
; /* UUID of entry */
145 struct berval sctxcsn
;
146 int sreference
; /* Is the entry a reference? */
149 typedef struct fbase_cookie
{
150 struct berval
*fdn
; /* DN of a modified entry, for scope testing */
151 syncops
*fss
; /* persistent search we're testing against */
152 int fbase
; /* if TRUE we found the search base and it's still valid */
153 int fscope
; /* if TRUE then fdn is within the psearch scope */
156 static AttributeName csn_anlist
[3];
157 static AttributeName uuid_anlist
[2];
159 /* Build a LDAPsync intermediate state control */
165 int entry_sync_state
,
169 struct berval
*cookie
)
174 BerElementBuffer berbuf
;
175 BerElement
*ber
= (BerElement
*)&berbuf
;
177 struct berval entryuuid_bv
= BER_BVNULL
;
179 ber_init2( ber
, 0, LBER_USE_DER
);
180 ber_set_option( ber
, LBER_OPT_BER_MEMCTX
, &op
->o_tmpmemctx
);
182 ctrls
[num_ctrls
] = op
->o_tmpalloc( sizeof ( LDAPControl
), op
->o_tmpmemctx
);
184 for ( a
= e
->e_attrs
; a
!= NULL
; a
= a
->a_next
) {
185 AttributeDescription
*desc
= a
->a_desc
;
186 if ( desc
== slap_schema
.si_ad_entryUUID
) {
187 entryuuid_bv
= a
->a_nvals
[0];
192 /* FIXME: what if entryuuid is NULL or empty ? */
194 if ( send_cookie
&& cookie
) {
195 ber_printf( ber
, "{eOON}",
196 entry_sync_state
, &entryuuid_bv
, cookie
);
198 ber_printf( ber
, "{eON}",
199 entry_sync_state
, &entryuuid_bv
);
202 ctrls
[num_ctrls
]->ldctl_oid
= LDAP_CONTROL_SYNC_STATE
;
203 ctrls
[num_ctrls
]->ldctl_iscritical
= (op
->o_sync
== SLAP_CONTROL_CRITICAL
);
204 ret
= ber_flatten2( ber
, &ctrls
[num_ctrls
]->ldctl_value
, 1 );
209 Debug( LDAP_DEBUG_TRACE
,
210 "slap_build_sync_ctrl: ber_flatten2 failed\n",
212 send_ldap_error( op
, rs
, LDAP_OTHER
, "internal error" );
219 /* Build a LDAPsync final state control */
227 struct berval
*cookie
,
231 BerElementBuffer berbuf
;
232 BerElement
*ber
= (BerElement
*)&berbuf
;
234 ber_init2( ber
, NULL
, LBER_USE_DER
);
235 ber_set_option( ber
, LBER_OPT_BER_MEMCTX
, &op
->o_tmpmemctx
);
237 ctrls
[num_ctrls
] = op
->o_tmpalloc( sizeof ( LDAPControl
), op
->o_tmpmemctx
);
239 ber_printf( ber
, "{" );
240 if ( send_cookie
&& cookie
) {
241 ber_printf( ber
, "O", cookie
);
243 if ( refreshDeletes
== LDAP_SYNC_REFRESH_DELETES
) {
244 ber_printf( ber
, "b", refreshDeletes
);
246 ber_printf( ber
, "N}" );
248 ctrls
[num_ctrls
]->ldctl_oid
= LDAP_CONTROL_SYNC_DONE
;
249 ctrls
[num_ctrls
]->ldctl_iscritical
= (op
->o_sync
== SLAP_CONTROL_CRITICAL
);
250 ret
= ber_flatten2( ber
, &ctrls
[num_ctrls
]->ldctl_value
, 1 );
255 Debug( LDAP_DEBUG_TRACE
,
256 "syncprov_done_ctrl: ber_flatten2 failed\n",
258 send_ldap_error( op
, rs
, LDAP_OTHER
, "internal error" );
270 struct berval
*cookie
,
275 BerElementBuffer berbuf
;
276 BerElement
*ber
= (BerElement
*)&berbuf
;
277 struct berval rspdata
;
281 ber_init2( ber
, NULL
, LBER_USE_DER
);
282 ber_set_option( ber
, LBER_OPT_BER_MEMCTX
, &op
->o_tmpmemctx
);
286 case LDAP_TAG_SYNC_NEW_COOKIE
:
287 ber_printf( ber
, "tO", type
, cookie
);
289 case LDAP_TAG_SYNC_REFRESH_DELETE
:
290 case LDAP_TAG_SYNC_REFRESH_PRESENT
:
291 ber_printf( ber
, "t{", type
);
293 ber_printf( ber
, "O", cookie
);
295 if ( refreshDone
== 0 ) {
296 ber_printf( ber
, "b", refreshDone
);
298 ber_printf( ber
, "N}" );
300 case LDAP_TAG_SYNC_ID_SET
:
301 ber_printf( ber
, "t{", type
);
303 ber_printf( ber
, "O", cookie
);
305 if ( refreshDeletes
== 1 ) {
306 ber_printf( ber
, "b", refreshDeletes
);
308 ber_printf( ber
, "[W]", syncUUIDs
);
309 ber_printf( ber
, "N}" );
312 Debug( LDAP_DEBUG_TRACE
,
313 "syncprov_sendinfo: invalid syncinfo type (%d)\n",
319 ret
= ber_flatten2( ber
, &rspdata
, 0 );
322 Debug( LDAP_DEBUG_TRACE
,
323 "syncprov_sendinfo: ber_flatten2 failed\n",
325 send_ldap_error( op
, rs
, LDAP_OTHER
, "internal error" );
329 rs
->sr_rspoid
= LDAP_SYNC_INFO
;
330 rs
->sr_rspdata
= &rspdata
;
331 send_ldap_intermediate( op
, rs
);
332 rs
->sr_rspdata
= NULL
;
338 /* Find a modtarget in an AVL tree */
340 sp_avl_cmp( const void *c1
, const void *c2
)
342 const modtarget
*m1
, *m2
;
346 rc
= m1
->mt_op
->o_req_ndn
.bv_len
- m2
->mt_op
->o_req_ndn
.bv_len
;
349 return ber_bvcmp( &m1
->mt_op
->o_req_ndn
, &m2
->mt_op
->o_req_ndn
);
352 /* syncprov_findbase:
353 * finds the true DN of the base of a search (with alias dereferencing) and
354 * checks to make sure the base entry doesn't get replaced with a different
355 * entry (e.g., swapping trees via ModDN, or retargeting an alias). If a
356 * change is detected, any persistent search on this base must be terminated /
358 * On the first call, we just save the DN and entryID. On subsequent calls
359 * we compare the DN and entryID with the saved values.
362 findbase_cb( Operation
*op
, SlapReply
*rs
)
364 slap_callback
*sc
= op
->o_callback
;
366 if ( rs
->sr_type
== REP_SEARCH
&& rs
->sr_err
== LDAP_SUCCESS
) {
367 fbase_cookie
*fc
= sc
->sc_private
;
369 /* If no entryID, we're looking for the first time.
370 * Just store whatever we got.
372 if ( fc
->fss
->s_eid
== NOID
) {
374 fc
->fss
->s_eid
= rs
->sr_entry
->e_id
;
375 ber_dupbv( &fc
->fss
->s_base
, &rs
->sr_entry
->e_nname
);
377 } else if ( rs
->sr_entry
->e_id
== fc
->fss
->s_eid
&&
378 dn_match( &rs
->sr_entry
->e_nname
, &fc
->fss
->s_base
)) {
380 /* OK, the DN is the same and the entryID is the same. */
384 if ( rs
->sr_err
!= LDAP_SUCCESS
) {
385 Debug( LDAP_DEBUG_ANY
, "findbase failed! %d\n", rs
->sr_err
,0,0 );
390 static Filter generic_filter
= { LDAP_FILTER_PRESENT
, { 0 }, NULL
};
391 static struct berval generic_filterstr
= BER_BVC("(objectclass=*)");
394 syncprov_findbase( Operation
*op
, fbase_cookie
*fc
)
396 opcookie
*opc
= op
->o_callback
->sc_private
;
397 slap_overinst
*on
= opc
->son
;
399 /* Use basic parameters from syncrepl search, but use
400 * current op's threadctx / tmpmemctx
402 ldap_pvt_thread_mutex_lock( &fc
->fss
->s_mutex
);
403 if ( fc
->fss
->s_flags
& PS_FIND_BASE
) {
404 slap_callback cb
= {0};
406 SlapReply frs
= { REP_RESULT
};
409 fc
->fss
->s_flags
^= PS_FIND_BASE
;
410 ldap_pvt_thread_mutex_unlock( &fc
->fss
->s_mutex
);
412 fop
= *fc
->fss
->s_op
;
414 fop
.o_bd
= fop
.o_bd
->bd_self
;
415 fop
.o_hdr
= op
->o_hdr
;
416 fop
.o_time
= op
->o_time
;
417 fop
.o_tincr
= op
->o_tincr
;
419 cb
.sc_response
= findbase_cb
;
422 fop
.o_sync_mode
= 0; /* turn off sync mode */
423 fop
.o_managedsait
= SLAP_CONTROL_CRITICAL
;
424 fop
.o_callback
= &cb
;
425 fop
.o_tag
= LDAP_REQ_SEARCH
;
426 fop
.ors_scope
= LDAP_SCOPE_BASE
;
427 fop
.ors_limit
= NULL
;
429 fop
.ors_tlimit
= SLAP_NO_LIMIT
;
430 fop
.ors_attrs
= slap_anlist_no_attrs
;
431 fop
.ors_attrsonly
= 1;
432 fop
.ors_filter
= &generic_filter
;
433 fop
.ors_filterstr
= generic_filterstr
;
435 rc
= fop
.o_bd
->be_search( &fop
, &frs
);
437 ldap_pvt_thread_mutex_unlock( &fc
->fss
->s_mutex
);
441 /* After the first call, see if the fdn resides in the scope */
442 if ( fc
->fbase
== 1 ) {
443 switch ( fc
->fss
->s_op
->ors_scope
) {
444 case LDAP_SCOPE_BASE
:
445 fc
->fscope
= dn_match( fc
->fdn
, &fc
->fss
->s_base
);
447 case LDAP_SCOPE_ONELEVEL
: {
449 dnParent( fc
->fdn
, &pdn
);
450 fc
->fscope
= dn_match( &pdn
, &fc
->fss
->s_base
);
452 case LDAP_SCOPE_SUBTREE
:
453 fc
->fscope
= dnIsSuffix( fc
->fdn
, &fc
->fss
->s_base
);
455 case LDAP_SCOPE_SUBORDINATE
:
456 fc
->fscope
= dnIsSuffix( fc
->fdn
, &fc
->fss
->s_base
) &&
457 !dn_match( fc
->fdn
, &fc
->fss
->s_base
);
465 /* If entryID has changed, then the base of this search has
466 * changed. Invalidate the psearch.
468 return LDAP_NO_SUCH_OBJECT
;
472 * This function has three different purposes, but they all use a search
473 * that filters on entryCSN so they're combined here.
474 * 1: at startup time, after a contextCSN has been read from the database,
475 * we search for all entries with CSN >= contextCSN in case the contextCSN
476 * was not checkpointed at the previous shutdown.
478 * 2: when the current contextCSN is known and we have a sync cookie, we search
479 * for one entry with CSN = the cookie CSN. If not found, try <= cookie CSN.
480 * If an entry is found, the cookie CSN is valid, otherwise it is stale.
482 * 3: during a refresh phase, we search for all entries with CSN <= the cookie
483 * CSN, and generate Present records for them. We always collect this result
484 * in SyncID sets, even if there's only one match.
486 typedef enum find_csn_t
{
493 findmax_cb( Operation
*op
, SlapReply
*rs
)
495 if ( rs
->sr_type
== REP_SEARCH
&& rs
->sr_err
== LDAP_SUCCESS
) {
496 struct berval
*maxcsn
= op
->o_callback
->sc_private
;
497 Attribute
*a
= attr_find( rs
->sr_entry
->e_attrs
,
498 slap_schema
.si_ad_entryCSN
);
500 if ( a
&& ber_bvcmp( &a
->a_vals
[0], maxcsn
) > 0 &&
501 slap_parse_csn_sid( &a
->a_vals
[0] ) == slap_serverID
) {
502 maxcsn
->bv_len
= a
->a_vals
[0].bv_len
;
503 strcpy( maxcsn
->bv_val
, a
->a_vals
[0].bv_val
);
510 findcsn_cb( Operation
*op
, SlapReply
*rs
)
512 slap_callback
*sc
= op
->o_callback
;
514 /* We just want to know that at least one exists, so it's OK if
515 * we exceed the unchecked limit.
517 if ( rs
->sr_err
== LDAP_ADMINLIMIT_EXCEEDED
||
518 (rs
->sr_type
== REP_SEARCH
&& rs
->sr_err
== LDAP_SUCCESS
)) {
519 sc
->sc_private
= (void *)1;
524 /* Build a list of entryUUIDs for sending in a SyncID set */
528 typedef struct fpres_cookie
{
535 findpres_cb( Operation
*op
, SlapReply
*rs
)
537 slap_callback
*sc
= op
->o_callback
;
538 fpres_cookie
*pc
= sc
->sc_private
;
540 int ret
= SLAP_CB_CONTINUE
;
542 switch ( rs
->sr_type
) {
544 a
= attr_find( rs
->sr_entry
->e_attrs
, slap_schema
.si_ad_entryUUID
);
546 pc
->uuids
[pc
->num
].bv_val
= pc
->last
;
547 AC_MEMCPY( pc
->uuids
[pc
->num
].bv_val
, a
->a_nvals
[0].bv_val
,
548 pc
->uuids
[pc
->num
].bv_len
);
550 pc
->last
= pc
->uuids
[pc
->num
].bv_val
;
551 pc
->uuids
[pc
->num
].bv_val
= NULL
;
554 if ( pc
->num
!= SLAP_SYNCUUID_SET_SIZE
)
560 ret
= syncprov_sendinfo( op
, rs
, LDAP_TAG_SYNC_ID_SET
, NULL
,
562 pc
->uuids
[pc
->num
].bv_val
= pc
->last
;
564 pc
->last
= pc
->uuids
[0].bv_val
;
574 syncprov_findcsn( Operation
*op
, find_csn_t mode
)
576 slap_overinst
*on
= (slap_overinst
*)op
->o_bd
->bd_info
;
577 syncprov_info_t
*si
= on
->on_bi
.bi_private
;
579 slap_callback cb
= {0};
581 SlapReply frs
= { REP_RESULT
};
582 char buf
[LDAP_LUTIL_CSNSTR_BUFSIZE
+ STRLENOF("(entryCSN<=)")];
583 char cbuf
[LDAP_LUTIL_CSNSTR_BUFSIZE
];
584 struct berval maxcsn
;
586 AttributeAssertion eq
= ATTRIBUTEASSERTION_INIT
;
587 fpres_cookie pcookie
;
588 sync_control
*srs
= NULL
;
589 struct slap_limits_set fc_limits
;
590 int i
, rc
= LDAP_SUCCESS
, findcsn_retry
= 1;
593 if ( mode
!= FIND_MAXCSN
) {
594 srs
= op
->o_controls
[slap_cids
.sc_LDAPsync
];
598 fop
.o_sync_mode
&= SLAP_CONTROL_MASK
; /* turn off sync_mode */
599 /* We want pure entries, not referrals */
600 fop
.o_managedsait
= SLAP_CONTROL_CRITICAL
;
603 cf
.f_av_desc
= slap_schema
.si_ad_entryCSN
;
604 BER_BVZERO( &cf
.f_av_value
);
607 fop
.o_callback
= &cb
;
608 fop
.ors_limit
= NULL
;
609 fop
.ors_tlimit
= SLAP_NO_LIMIT
;
610 fop
.ors_filter
= &cf
;
611 fop
.ors_filterstr
.bv_val
= buf
;
616 cf
.f_choice
= LDAP_FILTER_GE
;
617 /* If there are multiple CSNs, use the one with our serverID */
618 for ( i
=0; i
<si
->si_numcsns
; i
++) {
619 if ( slap_serverID
== si
->si_sids
[i
] ) {
624 if ( i
== si
->si_numcsns
) {
625 /* No match: this is multimaster, and none of the content in the DB
626 * originated locally. Treat like no CSN.
628 return LDAP_NO_SUCH_OBJECT
;
630 cf
.f_av_value
= si
->si_ctxcsn
[maxid
];
631 fop
.ors_filterstr
.bv_len
= snprintf( buf
, sizeof( buf
),
632 "(entryCSN>=%s)", cf
.f_av_value
.bv_val
);
633 if ( fop
.ors_filterstr
.bv_len
< 0 || fop
.ors_filterstr
.bv_len
>= sizeof( buf
) ) {
636 fop
.ors_attrsonly
= 0;
637 fop
.ors_attrs
= csn_anlist
;
638 fop
.ors_slimit
= SLAP_NO_LIMIT
;
639 cb
.sc_private
= &maxcsn
;
640 cb
.sc_response
= findmax_cb
;
641 strcpy( cbuf
, cf
.f_av_value
.bv_val
);
642 maxcsn
.bv_val
= cbuf
;
643 maxcsn
.bv_len
= cf
.f_av_value
.bv_len
;
646 if ( BER_BVISEMPTY( &cf
.f_av_value
)) {
647 cf
.f_av_value
= srs
->sr_state
.ctxcsn
[0];
648 /* If there are multiple CSNs, use the smallest */
649 for ( i
=1; i
<srs
->sr_state
.numcsns
; i
++ ) {
650 if ( ber_bvcmp( &cf
.f_av_value
, &srs
->sr_state
.ctxcsn
[i
] )
652 cf
.f_av_value
= srs
->sr_state
.ctxcsn
[i
];
656 /* Look for exact match the first time */
657 if ( findcsn_retry
) {
658 cf
.f_choice
= LDAP_FILTER_EQUALITY
;
659 fop
.ors_filterstr
.bv_len
= snprintf( buf
, sizeof( buf
),
660 "(entryCSN=%s)", cf
.f_av_value
.bv_val
);
661 /* On retry, look for <= */
663 cf
.f_choice
= LDAP_FILTER_LE
;
664 fop
.ors_limit
= &fc_limits
;
665 memset( &fc_limits
, 0, sizeof( fc_limits
));
666 fc_limits
.lms_s_unchecked
= 1;
667 fop
.ors_filterstr
.bv_len
= snprintf( buf
, sizeof( buf
),
668 "(entryCSN<=%s)", cf
.f_av_value
.bv_val
);
670 if ( fop
.ors_filterstr
.bv_len
< 0 || fop
.ors_filterstr
.bv_len
>= sizeof( buf
) ) {
673 fop
.ors_attrsonly
= 1;
674 fop
.ors_attrs
= slap_anlist_no_attrs
;
676 cb
.sc_private
= NULL
;
677 cb
.sc_response
= findcsn_cb
;
680 fop
.ors_filter
= op
->ors_filter
;
681 fop
.ors_filterstr
= op
->ors_filterstr
;
682 fop
.ors_attrsonly
= 0;
683 fop
.ors_attrs
= uuid_anlist
;
684 fop
.ors_slimit
= SLAP_NO_LIMIT
;
685 cb
.sc_private
= &pcookie
;
686 cb
.sc_response
= findpres_cb
;
689 /* preallocate storage for a full set */
690 pcookie
.uuids
= op
->o_tmpalloc( (SLAP_SYNCUUID_SET_SIZE
+1) *
691 sizeof(struct berval
) + SLAP_SYNCUUID_SET_SIZE
* UUID_LEN
,
693 pcookie
.last
= (char *)(pcookie
.uuids
+ SLAP_SYNCUUID_SET_SIZE
+1);
694 pcookie
.uuids
[0].bv_val
= pcookie
.last
;
695 pcookie
.uuids
[0].bv_len
= UUID_LEN
;
696 for (i
=1; i
<SLAP_SYNCUUID_SET_SIZE
; i
++) {
697 pcookie
.uuids
[i
].bv_val
= pcookie
.uuids
[i
-1].bv_val
+ UUID_LEN
;
698 pcookie
.uuids
[i
].bv_len
= UUID_LEN
;
703 fop
.o_bd
->bd_info
= (BackendInfo
*)on
->on_info
;
704 fop
.o_bd
->be_search( &fop
, &frs
);
705 fop
.o_bd
->bd_info
= (BackendInfo
*)on
;
709 if ( ber_bvcmp( &si
->si_ctxcsn
[maxid
], &maxcsn
)) {
710 ber_bvreplace( &si
->si_ctxcsn
[maxid
], &maxcsn
);
711 si
->si_numops
++; /* ensure a checkpoint */
715 /* If matching CSN was not found, invalidate the context. */
716 if ( !cb
.sc_private
) {
717 /* If we didn't find an exact match, then try for <= */
718 if ( findcsn_retry
) {
722 rc
= LDAP_NO_SUCH_OBJECT
;
726 op
->o_tmpfree( pcookie
.uuids
, op
->o_tmpmemctx
);
734 syncprov_free_syncop( syncops
*so
)
736 syncres
*sr
, *srnext
;
737 GroupAssertion
*ga
, *gnext
;
739 ldap_pvt_thread_mutex_lock( &so
->s_mutex
);
740 if ( --so
->s_inuse
> 0 ) {
741 ldap_pvt_thread_mutex_unlock( &so
->s_mutex
);
745 ldap_pvt_thread_mutex_lock( &slapd_rq
.rq_mutex
);
746 if ( ldap_pvt_runqueue_isrunning( &slapd_rq
, so
->s_qtask
) )
747 ldap_pvt_runqueue_stoptask( &slapd_rq
, so
->s_qtask
);
748 ldap_pvt_runqueue_remove( &slapd_rq
, so
->s_qtask
);
749 ldap_pvt_thread_mutex_unlock( &slapd_rq
.rq_mutex
);
751 ldap_pvt_thread_mutex_unlock( &so
->s_mutex
);
752 if ( so
->s_flags
& PS_IS_DETACHED
) {
753 filter_free( so
->s_op
->ors_filter
);
754 for ( ga
= so
->s_op
->o_groups
; ga
; ga
=gnext
) {
760 ch_free( so
->s_base
.bv_val
);
761 for ( sr
=so
->s_res
; sr
; sr
=srnext
) {
765 ldap_pvt_thread_mutex_destroy( &so
->s_mutex
);
769 /* Send a persistent search response */
771 syncprov_sendresp( Operation
*op
, opcookie
*opc
, syncops
*so
,
772 Entry
**e
, int mode
)
774 slap_overinst
*on
= opc
->son
;
776 SlapReply rs
= { REP_SEARCH
};
777 LDAPControl
*ctrls
[2];
778 struct berval cookie
, csns
[2];
780 Attribute a_uuid
= {0};
782 if ( so
->s_op
->o_abandon
)
783 return SLAPD_ABANDON
;
786 csns
[0] = opc
->sctxcsn
;
787 BER_BVZERO( &csns
[1] );
788 slap_compose_sync_cookie( op
, &cookie
, csns
, so
->s_rid
, so
->s_sid
);
790 Debug( LDAP_DEBUG_SYNC
, "syncprov_sendresp: cookie=%s\n", cookie
.bv_val
, 0, 0 );
792 e_uuid
.e_attrs
= &a_uuid
;
793 a_uuid
.a_desc
= slap_schema
.si_ad_entryUUID
;
794 a_uuid
.a_nvals
= &opc
->suuid
;
795 rs
.sr_err
= syncprov_state_ctrl( op
, &rs
, &e_uuid
,
796 mode
, ctrls
, 0, 1, &cookie
);
797 op
->o_tmpfree( cookie
.bv_val
, op
->o_tmpmemctx
);
800 op
->o_bd
->bd_info
= (BackendInfo
*)on
->on_info
;
804 if ( rs
.sr_entry
->e_private
)
805 rs
.sr_flags
= REP_ENTRY_MUSTRELEASE
;
806 if ( opc
->sreference
) {
807 rs
.sr_ref
= get_entry_referrals( op
, rs
.sr_entry
);
808 rs
.sr_err
= send_search_reference( op
, &rs
);
809 ber_bvarray_free( rs
.sr_ref
);
815 case LDAP_SYNC_MODIFY
:
817 if ( rs
.sr_entry
->e_private
)
818 rs
.sr_flags
= REP_ENTRY_MUSTRELEASE
;
819 rs
.sr_attrs
= op
->ors_attrs
;
820 rs
.sr_err
= send_search_entry( op
, &rs
);
824 case LDAP_SYNC_DELETE
:
825 e_uuid
.e_attrs
= NULL
;
826 e_uuid
.e_name
= opc
->sdn
;
827 e_uuid
.e_nname
= opc
->sndn
;
828 rs
.sr_entry
= &e_uuid
;
829 if ( opc
->sreference
) {
830 struct berval bv
= BER_BVNULL
;
832 rs
.sr_err
= send_search_reference( op
, &rs
);
834 rs
.sr_err
= send_search_entry( op
, &rs
);
840 /* In case someone else freed it already? */
842 op
->o_tmpfree( rs
.sr_ctrls
[0], op
->o_tmpmemctx
);
849 /* Play back queued responses */
851 syncprov_qplay( Operation
*op
, struct re_s
*rtask
)
853 syncops
*so
= rtask
->arg
;
854 slap_overinst
*on
= LDAP_SLIST_FIRST(&so
->s_op
->o_extra
)->oe_key
;
863 ldap_pvt_thread_mutex_lock( &so
->s_mutex
);
866 so
->s_res
= sr
->s_next
;
868 so
->s_restail
= NULL
;
869 /* Exit loop with mutex held */
870 if ( !sr
|| so
->s_op
->o_abandon
)
872 ldap_pvt_thread_mutex_unlock( &so
->s_mutex
);
875 opc
.sndn
= sr
->s_ndn
;
876 opc
.suuid
= sr
->s_uuid
;
877 opc
.sctxcsn
= sr
->s_csn
;
878 opc
.sreference
= sr
->s_isreference
;
881 if ( sr
->s_mode
!= LDAP_SYNC_DELETE
) {
882 rc
= overlay_entry_get_ov( op
, &opc
.sndn
, NULL
, NULL
, 0, &e
, on
);
884 Debug( LDAP_DEBUG_SYNC
, "syncprov_qplay: failed to get %s, "
885 "error (%d), ignoring...\n", opc
.sndn
.bv_val
, rc
, 0 );
891 rc
= syncprov_sendresp( op
, &opc
, so
, &e
, sr
->s_mode
);
894 overlay_entry_release_ov( op
, e
, 0, on
);
900 /* Exit loop with mutex held */
901 ldap_pvt_thread_mutex_lock( &so
->s_mutex
);
906 /* wait until we get explicitly scheduled again */
907 ldap_pvt_thread_mutex_lock( &slapd_rq
.rq_mutex
);
908 ldap_pvt_runqueue_stoptask( &slapd_rq
, rtask
);
910 ldap_pvt_runqueue_resched( &slapd_rq
, rtask
, 1 );
912 /* bail out on any error */
913 ldap_pvt_runqueue_remove( &slapd_rq
, rtask
);
915 ldap_pvt_thread_mutex_unlock( &slapd_rq
.rq_mutex
);
916 ldap_pvt_thread_mutex_unlock( &so
->s_mutex
);
920 /* runqueue task for playing back queued responses */
922 syncprov_qtask( void *ctx
, void *arg
)
924 struct re_s
*rtask
= arg
;
925 syncops
*so
= rtask
->arg
;
926 OperationBuffer opbuf
;
933 op
->o_hdr
= &opbuf
.ob_hdr
;
934 op
->o_controls
= opbuf
.ob_controls
;
935 memset( op
->o_controls
, 0, sizeof(opbuf
.ob_controls
) );
937 *op
->o_hdr
= *so
->s_op
->o_hdr
;
939 op
->o_tmpmemctx
= slap_sl_mem_create(SLAP_SLAB_SIZE
, SLAP_SLAB_STACK
, ctx
, 1);
940 op
->o_tmpmfuncs
= &slap_sl_mfuncs
;
941 op
->o_threadctx
= ctx
;
943 /* syncprov_qplay expects a fake db */
944 be
= *so
->s_op
->o_bd
;
945 be
.be_flags
|= SLAP_DBFLAG_OVERLAY
;
947 LDAP_SLIST_FIRST(&op
->o_extra
) = NULL
;
948 op
->o_callback
= NULL
;
950 rc
= syncprov_qplay( op
, rtask
);
952 /* decrement use count... */
953 syncprov_free_syncop( so
);
955 #if 0 /* FIXME: connection_close isn't exported from slapd.
959 ldap_pvt_thread_mutex_lock( &op
->o_conn
->c_mutex
);
960 if ( connection_state_closing( op
->o_conn
)) {
961 connection_close( op
->o_conn
);
963 ldap_pvt_thread_mutex_unlock( &op
->o_conn
->c_mutex
);
969 /* Start the task to play back queued psearch responses */
971 syncprov_qstart( syncops
*so
)
974 ldap_pvt_thread_mutex_lock( &slapd_rq
.rq_mutex
);
975 if ( !so
->s_qtask
) {
976 so
->s_qtask
= ldap_pvt_runqueue_insert( &slapd_rq
, RUNQ_INTERVAL
,
977 syncprov_qtask
, so
, "syncprov_qtask",
978 so
->s_op
->o_conn
->c_peer_name
.bv_val
);
982 if (!ldap_pvt_runqueue_isrunning( &slapd_rq
, so
->s_qtask
) &&
983 !so
->s_qtask
->next_sched
.tv_sec
) {
984 so
->s_qtask
->interval
.tv_sec
= 0;
985 ldap_pvt_runqueue_resched( &slapd_rq
, so
->s_qtask
, 0 );
986 so
->s_qtask
->interval
.tv_sec
= RUNQ_INTERVAL
;
991 ldap_pvt_thread_mutex_unlock( &slapd_rq
.rq_mutex
);
993 slap_wake_listener();
996 /* Queue a persistent search response */
998 syncprov_qresp( opcookie
*opc
, syncops
*so
, int mode
)
1003 /* Don't send changes back to their originator */
1004 sid
= slap_parse_csn_sid( &opc
->sctxcsn
);
1005 if ( sid
>= 0 && sid
== so
->s_sid
)
1006 return LDAP_SUCCESS
;
1008 srsize
= sizeof(syncres
) + opc
->suuid
.bv_len
+ 1 +
1009 opc
->sdn
.bv_len
+ 1 + opc
->sndn
.bv_len
+ 1;
1010 if ( opc
->sctxcsn
.bv_len
)
1011 srsize
+= opc
->sctxcsn
.bv_len
+ 1;
1012 sr
= ch_malloc( srsize
);
1014 sr
->s_dn
.bv_val
= (char *)(sr
+ 1);
1015 sr
->s_dn
.bv_len
= opc
->sdn
.bv_len
;
1017 sr
->s_isreference
= opc
->sreference
;
1018 sr
->s_ndn
.bv_val
= lutil_strcopy( sr
->s_dn
.bv_val
,
1019 opc
->sdn
.bv_val
) + 1;
1020 sr
->s_ndn
.bv_len
= opc
->sndn
.bv_len
;
1021 sr
->s_uuid
.bv_val
= lutil_strcopy( sr
->s_ndn
.bv_val
,
1022 opc
->sndn
.bv_val
) + 1;
1023 sr
->s_uuid
.bv_len
= opc
->suuid
.bv_len
;
1024 AC_MEMCPY( sr
->s_uuid
.bv_val
, opc
->suuid
.bv_val
, opc
->suuid
.bv_len
);
1025 if ( opc
->sctxcsn
.bv_len
) {
1026 sr
->s_csn
.bv_val
= sr
->s_uuid
.bv_val
+ sr
->s_uuid
.bv_len
+ 1;
1027 strcpy( sr
->s_csn
.bv_val
, opc
->sctxcsn
.bv_val
);
1029 sr
->s_csn
.bv_val
= NULL
;
1031 sr
->s_csn
.bv_len
= opc
->sctxcsn
.bv_len
;
1033 ldap_pvt_thread_mutex_lock( &so
->s_mutex
);
1037 so
->s_restail
->s_next
= sr
;
1041 /* If the base of the psearch was modified, check it next time round */
1042 if ( so
->s_flags
& PS_WROTE_BASE
) {
1043 so
->s_flags
^= PS_WROTE_BASE
;
1044 so
->s_flags
|= PS_FIND_BASE
;
1046 if ( so
->s_flags
& PS_IS_DETACHED
) {
1047 syncprov_qstart( so
);
1049 ldap_pvt_thread_mutex_unlock( &so
->s_mutex
);
1050 return LDAP_SUCCESS
;
1054 syncprov_drop_psearch( syncops
*so
, int lock
)
1056 if ( so
->s_flags
& PS_IS_DETACHED
) {
1058 ldap_pvt_thread_mutex_lock( &so
->s_op
->o_conn
->c_mutex
);
1059 so
->s_op
->o_conn
->c_n_ops_executing
--;
1060 so
->s_op
->o_conn
->c_n_ops_completed
++;
1061 LDAP_STAILQ_REMOVE( &so
->s_op
->o_conn
->c_ops
, so
->s_op
, Operation
,
1064 ldap_pvt_thread_mutex_unlock( &so
->s_op
->o_conn
->c_mutex
);
1066 syncprov_free_syncop( so
);
1072 syncprov_ab_cleanup( Operation
*op
, SlapReply
*rs
)
1074 slap_callback
*sc
= op
->o_callback
;
1075 op
->o_callback
= sc
->sc_next
;
1076 syncprov_drop_psearch( op
->o_callback
->sc_private
, 0 );
1077 op
->o_tmpfree( sc
, op
->o_tmpmemctx
);
1082 syncprov_op_abandon( Operation
*op
, SlapReply
*rs
)
1084 slap_overinst
*on
= (slap_overinst
*)op
->o_bd
->bd_info
;
1085 syncprov_info_t
*si
= on
->on_bi
.bi_private
;
1086 syncops
*so
, *soprev
;
1088 ldap_pvt_thread_mutex_lock( &si
->si_ops_mutex
);
1089 for ( so
=si
->si_ops
, soprev
= (syncops
*)&si
->si_ops
; so
;
1090 soprev
=so
, so
=so
->s_next
) {
1091 if ( so
->s_op
->o_connid
== op
->o_connid
&&
1092 so
->s_op
->o_msgid
== op
->orn_msgid
) {
1093 so
->s_op
->o_abandon
= 1;
1094 soprev
->s_next
= so
->s_next
;
1098 ldap_pvt_thread_mutex_unlock( &si
->si_ops_mutex
);
1100 /* Is this really a Cancel exop? */
1101 if ( op
->o_tag
!= LDAP_REQ_ABANDON
) {
1102 so
->s_op
->o_cancel
= SLAP_CANCEL_ACK
;
1103 rs
->sr_err
= LDAP_CANCELLED
;
1104 send_ldap_result( so
->s_op
, rs
);
1105 if ( so
->s_flags
& PS_IS_DETACHED
) {
1107 cb
= op
->o_tmpcalloc( 1, sizeof(slap_callback
), op
->o_tmpmemctx
);
1108 cb
->sc_cleanup
= syncprov_ab_cleanup
;
1109 cb
->sc_next
= op
->o_callback
;
1110 cb
->sc_private
= so
;
1111 return SLAP_CB_CONTINUE
;
1114 syncprov_drop_psearch( so
, 0 );
1116 return SLAP_CB_CONTINUE
;
1119 /* Find which persistent searches are affected by this operation */
1121 syncprov_matchops( Operation
*op
, opcookie
*opc
, int saveit
)
1123 slap_overinst
*on
= opc
->son
;
1124 syncprov_info_t
*si
= on
->on_bi
.bi_private
;
1127 syncops
*ss
, *sprev
, *snext
;
1131 struct berval newdn
;
1133 BackendDB
*b0
= op
->o_bd
, db
;
1135 fc
.fdn
= &op
->o_req_ndn
;
1136 /* compute new DN */
1137 if ( op
->o_tag
== LDAP_REQ_MODRDN
&& !saveit
) {
1139 if ( op
->orr_nnewSup
) pdn
= *op
->orr_nnewSup
;
1140 else dnParent( fc
.fdn
, &pdn
);
1141 build_new_dn( &newdn
, &pdn
, &op
->orr_nnewrdn
, op
->o_tmpmemctx
);
1145 if ( op
->o_tag
!= LDAP_REQ_ADD
) {
1146 if ( !SLAP_ISOVERLAY( op
->o_bd
)) {
1150 rc
= overlay_entry_get_ov( op
, fc
.fdn
, NULL
, NULL
, 0, &e
, on
);
1151 /* If we're sending responses now, make a copy and unlock the DB */
1152 if ( e
&& !saveit
) {
1153 Entry
*e2
= entry_dup( e
);
1154 overlay_entry_release_ov( op
, e
, 0, on
);
1165 if ( saveit
|| op
->o_tag
== LDAP_REQ_ADD
) {
1166 ber_dupbv_x( &opc
->sdn
, &e
->e_name
, op
->o_tmpmemctx
);
1167 ber_dupbv_x( &opc
->sndn
, &e
->e_nname
, op
->o_tmpmemctx
);
1168 opc
->sreference
= is_entry_referral( e
);
1169 a
= attr_find( e
->e_attrs
, slap_schema
.si_ad_entryUUID
);
1171 ber_dupbv_x( &opc
->suuid
, &a
->a_nvals
[0], op
->o_tmpmemctx
);
1172 } else if ( op
->o_tag
== LDAP_REQ_MODRDN
&& !saveit
) {
1173 op
->o_tmpfree( opc
->sndn
.bv_val
, op
->o_tmpmemctx
);
1174 op
->o_tmpfree( opc
->sdn
.bv_val
, op
->o_tmpmemctx
);
1175 ber_dupbv_x( &opc
->sdn
, &e
->e_name
, op
->o_tmpmemctx
);
1176 ber_dupbv_x( &opc
->sndn
, &e
->e_nname
, op
->o_tmpmemctx
);
1179 ldap_pvt_thread_mutex_lock( &si
->si_ops_mutex
);
1180 for (ss
= si
->si_ops
, sprev
= (syncops
*)&si
->si_ops
; ss
;
1181 sprev
= ss
, ss
=snext
)
1189 if ( ss
->s_op
->o_abandon
)
1197 /* If the base of the search is missing, signal a refresh */
1198 rc
= syncprov_findbase( op
, &fc
);
1199 if ( rc
!= LDAP_SUCCESS
) {
1200 SlapReply rs
= {REP_RESULT
};
1201 send_ldap_error( ss
->s_op
, &rs
, LDAP_SYNC_REFRESH_REQUIRED
,
1202 "search base has changed" );
1203 sprev
->s_next
= snext
;
1204 syncprov_drop_psearch( ss
, 1 );
1210 /* If we're sending results now, look for this op in old matches */
1214 /* Did we modify the search base? */
1215 if ( dn_match( &op
->o_req_ndn
, &ss
->s_base
)) {
1216 ldap_pvt_thread_mutex_lock( &ss
->s_mutex
);
1217 ss
->s_flags
|= PS_WROTE_BASE
;
1218 ldap_pvt_thread_mutex_unlock( &ss
->s_mutex
);
1221 for ( sm
=opc
->smatches
, old
=(syncmatches
*)&opc
->smatches
; sm
;
1222 old
=sm
, sm
=sm
->sm_next
) {
1223 if ( sm
->sm_op
== ss
) {
1225 old
->sm_next
= sm
->sm_next
;
1226 op
->o_tmpfree( sm
, op
->o_tmpmemctx
);
1235 oh
.oh_conn
= ss
->s_op
->o_conn
;
1236 oh
.oh_connid
= ss
->s_op
->o_connid
;
1238 op2
.o_extra
= op
->o_extra
;
1241 /* check if current o_req_dn is in scope and matches filter */
1242 if ( fc
.fscope
&& test_filter( &op2
, e
, ss
->s_op
->ors_filter
) ==
1243 LDAP_COMPARE_TRUE
) {
1245 sm
= op
->o_tmpalloc( sizeof(syncmatches
), op
->o_tmpmemctx
);
1246 sm
->sm_next
= opc
->smatches
;
1248 ldap_pvt_thread_mutex_lock( &ss
->s_mutex
);
1250 ldap_pvt_thread_mutex_unlock( &ss
->s_mutex
);
1253 /* if found send UPDATE else send ADD */
1254 syncprov_qresp( opc
, ss
,
1255 found
? LDAP_SYNC_MODIFY
: LDAP_SYNC_ADD
);
1257 } else if ( !saveit
&& found
) {
1259 syncprov_qresp( opc
, ss
, LDAP_SYNC_DELETE
);
1262 ldap_pvt_thread_mutex_unlock( &si
->si_ops_mutex
);
1264 if ( op
->o_tag
!= LDAP_REQ_ADD
&& e
) {
1265 if ( !SLAP_ISOVERLAY( op
->o_bd
)) {
1268 overlay_entry_release_ov( op
, e
, 0, on
);
1272 op
->o_tmpfree( fc
.fdn
->bv_val
, op
->o_tmpmemctx
);
1278 syncprov_op_cleanup( Operation
*op
, SlapReply
*rs
)
1280 slap_callback
*cb
= op
->o_callback
;
1281 opcookie
*opc
= cb
->sc_private
;
1282 slap_overinst
*on
= opc
->son
;
1283 syncprov_info_t
*si
= on
->on_bi
.bi_private
;
1284 syncmatches
*sm
, *snext
;
1285 modtarget
*mt
, mtdummy
;
1287 for (sm
= opc
->smatches
; sm
; sm
=snext
) {
1288 snext
= sm
->sm_next
;
1289 syncprov_free_syncop( sm
->sm_op
);
1290 op
->o_tmpfree( sm
, op
->o_tmpmemctx
);
1293 /* Remove op from lock table */
1295 ldap_pvt_thread_mutex_lock( &si
->si_mods_mutex
);
1296 mt
= avl_find( si
->si_mods
, &mtdummy
, sp_avl_cmp
);
1298 modinst
*mi
= mt
->mt_mods
;
1300 /* If there are more, promote the next one */
1301 ldap_pvt_thread_mutex_lock( &mt
->mt_mutex
);
1302 if ( mi
->mi_next
) {
1303 mt
->mt_mods
= mi
->mi_next
;
1304 mt
->mt_op
= mt
->mt_mods
->mi_op
;
1305 ldap_pvt_thread_mutex_unlock( &mt
->mt_mutex
);
1307 avl_delete( &si
->si_mods
, mt
, sp_avl_cmp
);
1308 ldap_pvt_thread_mutex_unlock( &mt
->mt_mutex
);
1309 ldap_pvt_thread_mutex_destroy( &mt
->mt_mutex
);
1313 ldap_pvt_thread_mutex_unlock( &si
->si_mods_mutex
);
1314 if ( !BER_BVISNULL( &opc
->suuid
))
1315 op
->o_tmpfree( opc
->suuid
.bv_val
, op
->o_tmpmemctx
);
1316 if ( !BER_BVISNULL( &opc
->sndn
))
1317 op
->o_tmpfree( opc
->sndn
.bv_val
, op
->o_tmpmemctx
);
1318 if ( !BER_BVISNULL( &opc
->sdn
))
1319 op
->o_tmpfree( opc
->sdn
.bv_val
, op
->o_tmpmemctx
);
1320 op
->o_callback
= cb
->sc_next
;
1321 op
->o_tmpfree(cb
, op
->o_tmpmemctx
);
1327 syncprov_checkpoint( Operation
*op
, SlapReply
*rs
, slap_overinst
*on
)
1329 syncprov_info_t
*si
= (syncprov_info_t
*)on
->on_bi
.bi_private
;
1332 SlapReply rsm
= { 0 };
1333 slap_callback cb
= {0};
1336 mod
.sml_numvals
= si
->si_numcsns
;
1337 mod
.sml_values
= si
->si_ctxcsn
;
1338 mod
.sml_nvalues
= NULL
;
1339 mod
.sml_desc
= slap_schema
.si_ad_contextCSN
;
1340 mod
.sml_op
= LDAP_MOD_REPLACE
;
1341 mod
.sml_flags
= SLAP_MOD_INTERNAL
;
1342 mod
.sml_next
= NULL
;
1344 cb
.sc_response
= slap_null_cb
;
1346 opm
.o_tag
= LDAP_REQ_MODIFY
;
1347 opm
.o_callback
= &cb
;
1348 opm
.orm_modlist
= &mod
;
1349 opm
.orm_no_opattrs
= 1;
1350 if ( SLAP_GLUE_SUBORDINATE( op
->o_bd
)) {
1351 be
= *on
->on_info
->oi_origdb
;
1354 opm
.o_req_dn
= opm
.o_bd
->be_suffix
[0];
1355 opm
.o_req_ndn
= opm
.o_bd
->be_nsuffix
[0];
1356 opm
.o_bd
->bd_info
= on
->on_info
->oi_orig
;
1357 opm
.o_managedsait
= SLAP_CONTROL_NONCRITICAL
;
1358 opm
.o_no_schema_check
= 1;
1359 opm
.o_bd
->be_modify( &opm
, &rsm
);
1360 if ( mod
.sml_next
!= NULL
) {
1361 slap_mods_free( mod
.sml_next
, 1 );
1366 syncprov_add_slog( Operation
*op
)
1368 opcookie
*opc
= op
->o_callback
->sc_private
;
1369 slap_overinst
*on
= opc
->son
;
1370 syncprov_info_t
*si
= on
->on_bi
.bi_private
;
1376 /* Allocate a record. UUIDs are not NUL-terminated. */
1377 se
= ch_malloc( sizeof( slog_entry
) + opc
->suuid
.bv_len
+
1378 op
->o_csn
.bv_len
+ 1 );
1380 se
->se_tag
= op
->o_tag
;
1382 se
->se_uuid
.bv_val
= (char *)(&se
[1]);
1383 AC_MEMCPY( se
->se_uuid
.bv_val
, opc
->suuid
.bv_val
, opc
->suuid
.bv_len
);
1384 se
->se_uuid
.bv_len
= opc
->suuid
.bv_len
;
1386 se
->se_csn
.bv_val
= se
->se_uuid
.bv_val
+ opc
->suuid
.bv_len
;
1387 AC_MEMCPY( se
->se_csn
.bv_val
, op
->o_csn
.bv_val
, op
->o_csn
.bv_len
);
1388 se
->se_csn
.bv_val
[op
->o_csn
.bv_len
] = '\0';
1389 se
->se_csn
.bv_len
= op
->o_csn
.bv_len
;
1390 se
->se_sid
= slap_parse_csn_sid( &se
->se_csn
);
1392 ldap_pvt_thread_mutex_lock( &sl
->sl_mutex
);
1393 if ( sl
->sl_head
) {
1394 sl
->sl_tail
->se_next
= se
;
1400 while ( sl
->sl_num
> sl
->sl_size
) {
1402 sl
->sl_head
= se
->se_next
;
1403 strcpy( sl
->sl_mincsn
.bv_val
, se
->se_csn
.bv_val
);
1404 sl
->sl_mincsn
.bv_len
= se
->se_csn
.bv_len
;
1408 ldap_pvt_thread_mutex_unlock( &sl
->sl_mutex
);
1412 /* Just set a flag if we found the matching entry */
1414 playlog_cb( Operation
*op
, SlapReply
*rs
)
1416 if ( rs
->sr_type
== REP_SEARCH
) {
1417 op
->o_callback
->sc_private
= (void *)1;
1422 /* enter with sl->sl_mutex locked, release before returning */
1424 syncprov_playlog( Operation
*op
, SlapReply
*rs
, sessionlog
*sl
,
1425 sync_control
*srs
, BerVarray ctxcsn
, int numcsns
, int *sids
)
1427 slap_overinst
*on
= (slap_overinst
*)op
->o_bd
->bd_info
;
1429 int i
, j
, ndel
, num
, nmods
, mmods
;
1430 char cbuf
[LDAP_LUTIL_CSNSTR_BUFSIZE
];
1432 struct berval delcsn
[2];
1434 if ( !sl
->sl_num
) {
1435 ldap_pvt_thread_mutex_unlock( &sl
->sl_mutex
);
1443 uuids
= op
->o_tmpalloc( (num
+1) * sizeof( struct berval
) +
1444 num
* UUID_LEN
, op
->o_tmpmemctx
);
1445 uuids
[0].bv_val
= (char *)(uuids
+ num
+ 1);
1447 delcsn
[0].bv_len
= 0;
1448 delcsn
[0].bv_val
= cbuf
;
1449 BER_BVZERO(&delcsn
[1]);
1451 /* Make a copy of the relevant UUIDs. Put the Deletes up front
1452 * and everything else at the end. Do this first so we can
1453 * unlock the list mutex.
1455 Debug( LDAP_DEBUG_SYNC
, "srs csn %s\n",
1456 srs
->sr_state
.ctxcsn
[0].bv_val
, 0, 0 );
1457 for ( se
=sl
->sl_head
; se
; se
=se
->se_next
) {
1459 Debug( LDAP_DEBUG_SYNC
, "log csn %s\n", se
->se_csn
.bv_val
, 0, 0 );
1461 for ( k
=0; k
<srs
->sr_state
.numcsns
; k
++ ) {
1462 if ( se
->se_sid
== srs
->sr_state
.sids
[k
] ) {
1463 ndel
= ber_bvcmp( &se
->se_csn
, &srs
->sr_state
.ctxcsn
[k
] );
1468 Debug( LDAP_DEBUG_SYNC
, "cmp %d, too old\n", ndel
, 0, 0 );
1472 for ( k
=0; k
<numcsns
; k
++ ) {
1473 if ( se
->se_sid
== sids
[k
] ) {
1474 ndel
= ber_bvcmp( &se
->se_csn
, &ctxcsn
[k
] );
1479 Debug( LDAP_DEBUG_SYNC
, "cmp %d, too new\n", ndel
, 0, 0 );
1482 if ( se
->se_tag
== LDAP_REQ_DELETE
) {
1485 AC_MEMCPY( cbuf
, se
->se_csn
.bv_val
, se
->se_csn
.bv_len
);
1486 delcsn
[0].bv_len
= se
->se_csn
.bv_len
;
1487 delcsn
[0].bv_val
[delcsn
[0].bv_len
] = '\0';
1492 uuids
[j
].bv_val
= uuids
[0].bv_val
+ (j
* UUID_LEN
);
1493 AC_MEMCPY(uuids
[j
].bv_val
, se
->se_uuid
.bv_val
, UUID_LEN
);
1494 uuids
[j
].bv_len
= UUID_LEN
;
1496 ldap_pvt_thread_mutex_unlock( &sl
->sl_mutex
);
1500 /* Zero out unused slots */
1501 for ( i
=ndel
; i
< num
- nmods
; i
++ )
1502 uuids
[i
].bv_len
= 0;
1504 /* Mods must be validated to see if they belong in this delete set.
1508 /* Strip any duplicates */
1509 for ( i
=0; i
<nmods
; i
++ ) {
1510 for ( j
=0; j
<ndel
; j
++ ) {
1511 if ( bvmatch( &uuids
[j
], &uuids
[num
- 1 - i
] )) {
1512 uuids
[num
- 1 - i
].bv_len
= 0;
1517 if ( uuids
[num
- 1 - i
].bv_len
== 0 ) continue;
1518 for ( j
=0; j
<i
; j
++ ) {
1519 if ( bvmatch( &uuids
[num
- 1 - j
], &uuids
[num
- 1 - i
] )) {
1520 uuids
[num
- 1 - i
].bv_len
= 0;
1529 SlapReply frs
= { REP_RESULT
};
1532 AttributeAssertion eq
= ATTRIBUTEASSERTION_INIT
;
1533 slap_callback cb
= {0};
1537 fop
.o_sync_mode
= 0;
1538 fop
.o_callback
= &cb
;
1539 fop
.ors_limit
= NULL
;
1540 fop
.ors_tlimit
= SLAP_NO_LIMIT
;
1541 fop
.ors_attrs
= slap_anlist_all_attributes
;
1542 fop
.ors_attrsonly
= 0;
1543 fop
.o_managedsait
= SLAP_CONTROL_CRITICAL
;
1545 af
.f_choice
= LDAP_FILTER_AND
;
1548 mf
.f_choice
= LDAP_FILTER_EQUALITY
;
1550 mf
.f_av_desc
= slap_schema
.si_ad_entryUUID
;
1551 mf
.f_next
= fop
.ors_filter
;
1553 fop
.ors_filter
= &af
;
1555 cb
.sc_response
= playlog_cb
;
1556 fop
.o_bd
->bd_info
= (BackendInfo
*)on
->on_info
;
1558 for ( i
=ndel
; i
<num
; i
++ ) {
1559 if ( uuids
[i
].bv_len
== 0 ) continue;
1561 mf
.f_av_value
= uuids
[i
];
1562 cb
.sc_private
= NULL
;
1564 frs
.sr_nentries
= 0;
1565 rc
= fop
.o_bd
->be_search( &fop
, &frs
);
1567 /* If entry was not found, add to delete list */
1568 if ( !cb
.sc_private
) {
1569 uuids
[ndel
++] = uuids
[i
];
1572 fop
.o_bd
->bd_info
= (BackendInfo
*)on
;
1575 struct berval cookie
;
1577 if ( delcsn
[0].bv_len
) {
1578 slap_compose_sync_cookie( op
, &cookie
, delcsn
, srs
->sr_state
.rid
,
1579 srs
->sr_state
.sid
);
1581 Debug( LDAP_DEBUG_SYNC
, "syncprov_playlog: cookie=%s\n", cookie
.bv_val
, 0, 0 );
1584 uuids
[ndel
].bv_val
= NULL
;
1585 syncprov_sendinfo( op
, rs
, LDAP_TAG_SYNC_ID_SET
,
1586 delcsn
[0].bv_len
? &cookie
: NULL
, 0, uuids
, 1 );
1587 if ( delcsn
[0].bv_len
) {
1588 op
->o_tmpfree( cookie
.bv_val
, op
->o_tmpmemctx
);
1591 op
->o_tmpfree( uuids
, op
->o_tmpmemctx
);
1595 syncprov_op_response( Operation
*op
, SlapReply
*rs
)
1597 opcookie
*opc
= op
->o_callback
->sc_private
;
1598 slap_overinst
*on
= opc
->son
;
1599 syncprov_info_t
*si
= on
->on_bi
.bi_private
;
1602 if ( rs
->sr_err
== LDAP_SUCCESS
)
1604 struct berval maxcsn
= BER_BVNULL
;
1605 char cbuf
[LDAP_LUTIL_CSNSTR_BUFSIZE
];
1606 int do_check
= 0, have_psearches
;
1608 /* Update our context CSN */
1610 ldap_pvt_thread_rdwr_wlock( &si
->si_csn_rwlock
);
1611 slap_get_commit_csn( op
, &maxcsn
);
1612 if ( BER_BVISNULL( &maxcsn
) && SLAP_GLUE_SUBORDINATE( op
->o_bd
)) {
1613 /* syncrepl queues the CSN values in the db where
1614 * it is configured , not where the changes are made.
1615 * So look for a value in the glue db if we didn't
1616 * find any in this db.
1618 BackendDB
*be
= op
->o_bd
;
1619 op
->o_bd
= select_backend( &be
->be_nsuffix
[0], 1);
1620 slap_get_commit_csn( op
, &maxcsn
);
1623 if ( !BER_BVISNULL( &maxcsn
) ) {
1625 strcpy( cbuf
, maxcsn
.bv_val
);
1626 sid
= slap_parse_csn_sid( &maxcsn
);
1627 for ( i
=0; i
<si
->si_numcsns
; i
++ ) {
1628 if ( sid
== si
->si_sids
[i
] ) {
1629 if ( ber_bvcmp( &maxcsn
, &si
->si_ctxcsn
[i
] ) > 0 ) {
1630 ber_bvreplace( &si
->si_ctxcsn
[i
], &maxcsn
);
1635 /* It's a new SID for us */
1636 if ( i
== si
->si_numcsns
) {
1637 value_add_one( &si
->si_ctxcsn
, &maxcsn
);
1639 si
->si_sids
= ch_realloc( si
->si_sids
, si
->si_numcsns
*
1641 si
->si_sids
[i
] = sid
;
1644 /* internal ops that aren't meant to be replicated */
1645 ldap_pvt_thread_rdwr_wunlock( &si
->si_csn_rwlock
);
1646 return SLAP_CB_CONTINUE
;
1649 /* Don't do any processing for consumer contextCSN updates */
1650 if ( SLAP_SYNC_SHADOW( op
->o_bd
) &&
1651 op
->o_msgid
== SLAP_SYNC_UPDATE_MSGID
) {
1652 ldap_pvt_thread_rdwr_wunlock( &si
->si_csn_rwlock
);
1653 return SLAP_CB_CONTINUE
;
1657 if ( si
->si_chkops
|| si
->si_chktime
) {
1658 if ( si
->si_chkops
&& si
->si_numops
>= si
->si_chkops
) {
1662 if ( si
->si_chktime
&&
1663 (op
->o_time
- si
->si_chklast
>= si
->si_chktime
)) {
1664 if ( si
->si_chklast
) {
1666 si
->si_chklast
= op
->o_time
;
1672 ldap_pvt_thread_rdwr_wunlock( &si
->si_csn_rwlock
);
1675 ldap_pvt_thread_rdwr_rlock( &si
->si_csn_rwlock
);
1676 syncprov_checkpoint( op
, rs
, on
);
1677 ldap_pvt_thread_rdwr_runlock( &si
->si_csn_rwlock
);
1680 opc
->sctxcsn
.bv_len
= maxcsn
.bv_len
;
1681 opc
->sctxcsn
.bv_val
= cbuf
;
1683 /* Handle any persistent searches */
1684 ldap_pvt_thread_mutex_lock( &si
->si_ops_mutex
);
1685 have_psearches
= ( si
->si_ops
!= NULL
);
1686 ldap_pvt_thread_mutex_unlock( &si
->si_ops_mutex
);
1687 if ( have_psearches
) {
1690 case LDAP_REQ_MODIFY
:
1691 case LDAP_REQ_MODRDN
:
1692 case LDAP_REQ_EXTENDED
:
1693 syncprov_matchops( op
, opc
, 0 );
1695 case LDAP_REQ_DELETE
:
1696 /* for each match in opc->smatches:
1699 for ( sm
= opc
->smatches
; sm
; sm
=sm
->sm_next
) {
1700 if ( sm
->sm_op
->s_op
->o_abandon
)
1702 syncprov_qresp( opc
, sm
->sm_op
, LDAP_SYNC_DELETE
);
1708 /* Add any log records */
1709 if ( si
->si_logs
&& op
->o_tag
!= LDAP_REQ_ADD
) {
1710 syncprov_add_slog( op
);
1714 return SLAP_CB_CONTINUE
;
1717 /* We don't use a subentry to store the context CSN any more.
1718 * We expose the current context CSN as an operational attribute
1719 * of the suffix entry.
1722 syncprov_op_compare( Operation
*op
, SlapReply
*rs
)
1724 slap_overinst
*on
= (slap_overinst
*)op
->o_bd
->bd_info
;
1725 syncprov_info_t
*si
= on
->on_bi
.bi_private
;
1726 int rc
= SLAP_CB_CONTINUE
;
1728 if ( dn_match( &op
->o_req_ndn
, op
->o_bd
->be_nsuffix
) &&
1729 op
->oq_compare
.rs_ava
->aa_desc
== slap_schema
.si_ad_contextCSN
)
1734 e
.e_name
= op
->o_bd
->be_suffix
[0];
1735 e
.e_nname
= op
->o_bd
->be_nsuffix
[0];
1738 a
.a_desc
= slap_schema
.si_ad_contextCSN
;
1740 ldap_pvt_thread_rdwr_rlock( &si
->si_csn_rwlock
);
1742 a
.a_vals
= si
->si_ctxcsn
;
1743 a
.a_nvals
= a
.a_vals
;
1745 rs
->sr_err
= access_allowed( op
, &e
, op
->oq_compare
.rs_ava
->aa_desc
,
1746 &op
->oq_compare
.rs_ava
->aa_value
, ACL_COMPARE
, NULL
);
1747 if ( ! rs
->sr_err
) {
1748 rs
->sr_err
= LDAP_INSUFFICIENT_ACCESS
;
1749 goto return_results
;
1752 if ( get_assert( op
) &&
1753 ( test_filter( op
, &e
, get_assertion( op
) ) != LDAP_COMPARE_TRUE
) )
1755 rs
->sr_err
= LDAP_ASSERTION_FAILED
;
1756 goto return_results
;
1760 rs
->sr_err
= LDAP_COMPARE_FALSE
;
1762 if ( attr_valfind( &a
,
1763 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH
|
1764 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
,
1765 &op
->oq_compare
.rs_ava
->aa_value
, NULL
, op
->o_tmpmemctx
) == 0 )
1767 rs
->sr_err
= LDAP_COMPARE_TRUE
;
1772 ldap_pvt_thread_rdwr_runlock( &si
->si_csn_rwlock
);
1774 send_ldap_result( op
, rs
);
1776 if( rs
->sr_err
== LDAP_COMPARE_FALSE
|| rs
->sr_err
== LDAP_COMPARE_TRUE
) {
1777 rs
->sr_err
= LDAP_SUCCESS
;
1786 syncprov_op_mod( Operation
*op
, SlapReply
*rs
)
1788 slap_overinst
*on
= (slap_overinst
*)op
->o_bd
->bd_info
;
1789 syncprov_info_t
*si
= on
->on_bi
.bi_private
;
1792 int have_psearches
, cbsize
;
1794 ldap_pvt_thread_mutex_lock( &si
->si_ops_mutex
);
1795 have_psearches
= ( si
->si_ops
!= NULL
);
1796 ldap_pvt_thread_mutex_unlock( &si
->si_ops_mutex
);
1798 cbsize
= sizeof(slap_callback
) + sizeof(opcookie
) +
1799 (have_psearches
? sizeof(modinst
) : 0 );
1801 cb
= op
->o_tmpcalloc(1, cbsize
, op
->o_tmpmemctx
);
1802 opc
= (opcookie
*)(cb
+1);
1804 cb
->sc_response
= syncprov_op_response
;
1805 cb
->sc_cleanup
= syncprov_op_cleanup
;
1806 cb
->sc_private
= opc
;
1807 cb
->sc_next
= op
->o_callback
;
1808 op
->o_callback
= cb
;
1810 /* If there are active persistent searches, lock this operation.
1811 * See seqmod.c for the locking logic on its own.
1813 if ( have_psearches
) {
1814 modtarget
*mt
, mtdummy
;
1817 mi
= (modinst
*)(opc
+1);
1820 /* See if we're already modifying this entry... */
1822 ldap_pvt_thread_mutex_lock( &si
->si_mods_mutex
);
1823 mt
= avl_find( si
->si_mods
, &mtdummy
, sp_avl_cmp
);
1825 ldap_pvt_thread_mutex_lock( &mt
->mt_mutex
);
1826 ldap_pvt_thread_mutex_unlock( &si
->si_mods_mutex
);
1827 mt
->mt_tail
->mi_next
= mi
;
1829 /* wait for this op to get to head of list */
1830 while ( mt
->mt_mods
!= mi
) {
1831 ldap_pvt_thread_mutex_unlock( &mt
->mt_mutex
);
1832 /* FIXME: if dynamic config can delete overlays or
1833 * databases we'll have to check for cleanup here.
1834 * Currently it's not an issue because there are
1835 * no dynamic config deletes...
1837 if ( !ldap_pvt_thread_pool_pausecheck( &connection_pool
))
1838 ldap_pvt_thread_yield();
1839 ldap_pvt_thread_mutex_lock( &mt
->mt_mutex
);
1841 /* clean up if the caller is giving up */
1842 if ( op
->o_abandon
) {
1844 for ( m2
= mt
->mt_mods
; m2
->mi_next
!= mi
;
1846 m2
->mi_next
= mi
->mi_next
;
1847 if ( mt
->mt_tail
== mi
) mt
->mt_tail
= m2
;
1848 op
->o_tmpfree( cb
, op
->o_tmpmemctx
);
1849 ldap_pvt_thread_mutex_unlock( &mt
->mt_mutex
);
1850 return SLAPD_ABANDON
;
1853 ldap_pvt_thread_mutex_unlock( &mt
->mt_mutex
);
1855 /* Record that we're modifying this entry now */
1856 mt
= ch_malloc( sizeof(modtarget
) );
1859 mt
->mt_op
= mi
->mi_op
;
1860 ldap_pvt_thread_mutex_init( &mt
->mt_mutex
);
1861 avl_insert( &si
->si_mods
, mt
, sp_avl_cmp
, avl_dup_error
);
1862 ldap_pvt_thread_mutex_unlock( &si
->si_mods_mutex
);
1866 if (( have_psearches
|| si
->si_logs
) && op
->o_tag
!= LDAP_REQ_ADD
)
1867 syncprov_matchops( op
, opc
, 1 );
1869 return SLAP_CB_CONTINUE
;
1873 syncprov_op_extended( Operation
*op
, SlapReply
*rs
)
1875 if ( exop_is_write( op
))
1876 return syncprov_op_mod( op
, rs
);
1878 return SLAP_CB_CONTINUE
;
1881 typedef struct searchstate
{
1882 slap_overinst
*ss_on
;
1884 BerVarray ss_ctxcsn
;
1887 #define SS_PRESENT 0x01
1888 #define SS_CHANGED 0x02
1893 syncprov_search_cleanup( Operation
*op
, SlapReply
*rs
)
1895 if ( rs
->sr_ctrls
) {
1896 op
->o_tmpfree( rs
->sr_ctrls
[0], op
->o_tmpmemctx
);
1897 op
->o_tmpfree( rs
->sr_ctrls
, op
->o_tmpmemctx
);
1898 rs
->sr_ctrls
= NULL
;
1903 typedef struct SyncOperationBuffer
{
1907 AttributeName sob_extra
; /* not always present */
1908 /* Further data allocated here */
1909 } SyncOperationBuffer
;
1912 syncprov_detach_op( Operation
*op
, syncops
*so
, slap_overinst
*on
)
1914 SyncOperationBuffer
*sopbuf2
;
1919 GroupAssertion
*g1
, *g2
;
1921 /* count the search attrs */
1922 for (i
=0; op
->ors_attrs
&& !BER_BVISNULL( &op
->ors_attrs
[i
].an_name
); i
++) {
1923 alen
+= op
->ors_attrs
[i
].an_name
.bv_len
+ 1;
1925 /* Make a new copy of the operation */
1926 size
= offsetof( SyncOperationBuffer
, sob_extra
) +
1927 (i
? ( (i
+1) * sizeof(AttributeName
) + alen
) : 0) +
1928 op
->o_req_dn
.bv_len
+ 1 +
1929 op
->o_req_ndn
.bv_len
+ 1 +
1930 op
->o_ndn
.bv_len
+ 1 +
1931 so
->s_filterstr
.bv_len
+ 1;
1932 sopbuf2
= ch_calloc( 1, size
);
1933 op2
= &sopbuf2
->sob_op
;
1934 op2
->o_hdr
= &sopbuf2
->sob_hdr
;
1935 LDAP_SLIST_FIRST(&op2
->o_extra
) = &sopbuf2
->sob_oe
;
1937 /* Copy the fields we care about explicitly, leave the rest alone */
1938 *op2
->o_hdr
= *op
->o_hdr
;
1939 op2
->o_tag
= op
->o_tag
;
1940 op2
->o_time
= op
->o_time
;
1941 op2
->o_bd
= on
->on_info
->oi_origdb
;
1942 op2
->o_request
= op
->o_request
;
1943 LDAP_SLIST_FIRST(&op2
->o_extra
)->oe_key
= on
;
1944 LDAP_SLIST_NEXT(LDAP_SLIST_FIRST(&op2
->o_extra
), oe_next
) = NULL
;
1946 ptr
= (char *) sopbuf2
+ offsetof( SyncOperationBuffer
, sob_extra
);
1948 op2
->ors_attrs
= (AttributeName
*) ptr
;
1949 ptr
= (char *) &op2
->ors_attrs
[i
+1];
1950 for (i
=0; !BER_BVISNULL( &op
->ors_attrs
[i
].an_name
); i
++) {
1951 op2
->ors_attrs
[i
] = op
->ors_attrs
[i
];
1952 op2
->ors_attrs
[i
].an_name
.bv_val
= ptr
;
1953 ptr
= lutil_strcopy( ptr
, op
->ors_attrs
[i
].an_name
.bv_val
) + 1;
1955 BER_BVZERO( &op2
->ors_attrs
[i
].an_name
);
1958 op2
->o_authz
= op
->o_authz
;
1959 op2
->o_ndn
.bv_val
= ptr
;
1960 ptr
= lutil_strcopy(ptr
, op
->o_ndn
.bv_val
) + 1;
1961 op2
->o_dn
= op2
->o_ndn
;
1962 op2
->o_req_dn
.bv_len
= op
->o_req_dn
.bv_len
;
1963 op2
->o_req_dn
.bv_val
= ptr
;
1964 ptr
= lutil_strcopy(ptr
, op
->o_req_dn
.bv_val
) + 1;
1965 op2
->o_req_ndn
.bv_len
= op
->o_req_ndn
.bv_len
;
1966 op2
->o_req_ndn
.bv_val
= ptr
;
1967 ptr
= lutil_strcopy(ptr
, op
->o_req_ndn
.bv_val
) + 1;
1968 op2
->ors_filterstr
.bv_val
= ptr
;
1969 strcpy( ptr
, so
->s_filterstr
.bv_val
);
1970 op2
->ors_filterstr
.bv_len
= so
->s_filterstr
.bv_len
;
1972 /* Skip the AND/GE clause that we stuck on in front */
1973 if ( so
->s_flags
& PS_FIX_FILTER
) {
1974 op2
->ors_filter
= op
->ors_filter
->f_and
->f_next
;
1975 so
->s_flags
^= PS_FIX_FILTER
;
1977 op2
->ors_filter
= op
->ors_filter
;
1979 op2
->ors_filter
= filter_dup( op2
->ors_filter
, NULL
);
1982 /* Copy any cached group ACLs individually */
1983 op2
->o_groups
= NULL
;
1984 for ( g1
=op
->o_groups
; g1
; g1
=g1
->ga_next
) {
1985 g2
= ch_malloc( sizeof(GroupAssertion
) + g1
->ga_len
);
1987 strcpy( g2
->ga_ndn
, g1
->ga_ndn
);
1988 g2
->ga_next
= op2
->o_groups
;
1991 /* Don't allow any further group caching */
1992 op2
->o_do_not_cache
= 1;
1994 /* Add op2 to conn so abandon will find us */
1995 op
->o_conn
->c_n_ops_executing
++;
1996 op
->o_conn
->c_n_ops_completed
--;
1997 LDAP_STAILQ_INSERT_TAIL( &op
->o_conn
->c_ops
, op2
, o_next
);
1998 so
->s_flags
|= PS_IS_DETACHED
;
2000 /* Prevent anyone else from trying to send a result for this op */
2005 syncprov_search_response( Operation
*op
, SlapReply
*rs
)
2007 searchstate
*ss
= op
->o_callback
->sc_private
;
2008 slap_overinst
*on
= ss
->ss_on
;
2009 syncprov_info_t
*si
= (syncprov_info_t
*)on
->on_bi
.bi_private
;
2010 sync_control
*srs
= op
->o_controls
[slap_cids
.sc_LDAPsync
];
2012 if ( rs
->sr_type
== REP_SEARCH
|| rs
->sr_type
== REP_SEARCHREF
) {
2014 /* If we got a referral without a referral object, there's
2015 * something missing that we cannot replicate. Just ignore it.
2016 * The consumer will abort because we didn't send the expected
2019 if ( !rs
->sr_entry
) {
2020 assert( rs
->sr_entry
!= NULL
);
2021 Debug( LDAP_DEBUG_ANY
, "bogus referral in context\n",0,0,0 );
2022 return SLAP_CB_CONTINUE
;
2024 a
= attr_find( rs
->sr_entry
->e_attrs
, slap_schema
.si_ad_entryCSN
);
2025 if ( a
== NULL
&& rs
->sr_operational_attrs
!= NULL
) {
2026 a
= attr_find( rs
->sr_operational_attrs
, slap_schema
.si_ad_entryCSN
);
2030 sid
= slap_parse_csn_sid( &a
->a_nvals
[0] );
2032 /* Don't send changed entries back to the originator */
2033 if ( sid
== srs
->sr_state
.sid
&& srs
->sr_state
.numcsns
) {
2034 Debug( LDAP_DEBUG_SYNC
,
2035 "Entry %s changed by peer, ignored\n",
2036 rs
->sr_entry
->e_name
.bv_val
, 0, 0 );
2037 return LDAP_SUCCESS
;
2040 /* If not a persistent search */
2042 /* Make sure entry is less than the snapshot'd contextCSN */
2043 for ( i
=0; i
<ss
->ss_numcsns
; i
++ ) {
2044 if ( sid
== ss
->ss_sids
[i
] && ber_bvcmp( &a
->a_nvals
[0],
2045 &ss
->ss_ctxcsn
[i
] ) > 0 ) {
2046 Debug( LDAP_DEBUG_SYNC
,
2047 "Entry %s CSN %s greater than snapshot %s\n",
2048 rs
->sr_entry
->e_name
.bv_val
,
2049 a
->a_nvals
[0].bv_val
,
2050 ss
->ss_ctxcsn
[i
].bv_val
);
2051 return LDAP_SUCCESS
;
2056 /* Don't send old entries twice */
2057 if ( srs
->sr_state
.ctxcsn
) {
2058 for ( i
=0; i
<srs
->sr_state
.numcsns
; i
++ ) {
2059 if ( sid
== srs
->sr_state
.sids
[i
] &&
2060 ber_bvcmp( &a
->a_nvals
[0],
2061 &srs
->sr_state
.ctxcsn
[i
] )<= 0 ) {
2062 Debug( LDAP_DEBUG_SYNC
,
2063 "Entry %s CSN %s older or equal to ctx %s\n",
2064 rs
->sr_entry
->e_name
.bv_val
,
2065 a
->a_nvals
[0].bv_val
,
2066 srs
->sr_state
.ctxcsn
[i
].bv_val
);
2067 return LDAP_SUCCESS
;
2072 rs
->sr_ctrls
= op
->o_tmpalloc( sizeof(LDAPControl
*)*2,
2074 rs
->sr_ctrls
[1] = NULL
;
2075 /* If we're in delta-sync mode, always send a cookie */
2076 if ( si
->si_nopres
&& si
->si_usehint
&& a
) {
2077 struct berval cookie
;
2078 slap_compose_sync_cookie( op
, &cookie
, a
->a_nvals
, srs
->sr_state
.rid
, srs
->sr_state
.sid
);
2079 rs
->sr_err
= syncprov_state_ctrl( op
, rs
, rs
->sr_entry
,
2080 LDAP_SYNC_ADD
, rs
->sr_ctrls
, 0, 1, &cookie
);
2082 rs
->sr_err
= syncprov_state_ctrl( op
, rs
, rs
->sr_entry
,
2083 LDAP_SYNC_ADD
, rs
->sr_ctrls
, 0, 0, NULL
);
2085 } else if ( rs
->sr_type
== REP_RESULT
&& rs
->sr_err
== LDAP_SUCCESS
) {
2086 struct berval cookie
;
2088 if ( ss
->ss_flags
& SS_CHANGED
) {
2089 slap_compose_sync_cookie( op
, &cookie
, ss
->ss_ctxcsn
,
2090 srs
->sr_state
.rid
, srs
->sr_state
.sid
);
2092 Debug( LDAP_DEBUG_SYNC
, "syncprov_search_response: cookie=%s\n", cookie
.bv_val
, 0, 0 );
2095 /* Is this a regular refresh?
2096 * Note: refresh never gets here if there were no changes
2099 rs
->sr_ctrls
= op
->o_tmpalloc( sizeof(LDAPControl
*)*2,
2101 rs
->sr_ctrls
[1] = NULL
;
2102 rs
->sr_err
= syncprov_done_ctrl( op
, rs
, rs
->sr_ctrls
,
2103 0, 1, &cookie
, ( ss
->ss_flags
& SS_PRESENT
) ? LDAP_SYNC_REFRESH_PRESENTS
:
2104 LDAP_SYNC_REFRESH_DELETES
);
2105 op
->o_tmpfree( cookie
.bv_val
, op
->o_tmpmemctx
);
2107 /* It's RefreshAndPersist, transition to Persist phase */
2108 syncprov_sendinfo( op
, rs
, ( ss
->ss_flags
& SS_PRESENT
) ?
2109 LDAP_TAG_SYNC_REFRESH_PRESENT
: LDAP_TAG_SYNC_REFRESH_DELETE
,
2110 ( ss
->ss_flags
& SS_CHANGED
) ? &cookie
: NULL
,
2112 if ( ss
->ss_flags
& SS_CHANGED
)
2113 op
->o_tmpfree( cookie
.bv_val
, op
->o_tmpmemctx
);
2115 /* Detach this Op from frontend control */
2116 ldap_pvt_thread_mutex_lock( &ss
->ss_so
->s_mutex
);
2117 ldap_pvt_thread_mutex_lock( &op
->o_conn
->c_mutex
);
2119 /* But not if this connection was closed along the way */
2120 if ( op
->o_abandon
) {
2121 ldap_pvt_thread_mutex_unlock( &op
->o_conn
->c_mutex
);
2122 ldap_pvt_thread_mutex_unlock( &ss
->ss_so
->s_mutex
);
2123 /* syncprov_ab_cleanup will free this syncop */
2124 return SLAPD_ABANDON
;
2127 /* Turn off the refreshing flag */
2128 ss
->ss_so
->s_flags
^= PS_IS_REFRESHING
;
2130 syncprov_detach_op( op
, ss
->ss_so
, on
);
2132 ldap_pvt_thread_mutex_unlock( &op
->o_conn
->c_mutex
);
2134 /* If there are queued responses, fire them off */
2135 if ( ss
->ss_so
->s_res
)
2136 syncprov_qstart( ss
->ss_so
);
2138 ldap_pvt_thread_mutex_unlock( &ss
->ss_so
->s_mutex
);
2140 return LDAP_SUCCESS
;
2144 return SLAP_CB_CONTINUE
;
2148 syncprov_op_search( Operation
*op
, SlapReply
*rs
)
2150 slap_overinst
*on
= (slap_overinst
*)op
->o_bd
->bd_info
;
2151 syncprov_info_t
*si
= (syncprov_info_t
*)on
->on_bi
.bi_private
;
2153 int gotstate
= 0, changed
= 0, do_present
= 0;
2154 syncops
*sop
= NULL
;
2158 int i
, *sids
, numcsns
;
2159 struct berval mincsn
;
2161 if ( !(op
->o_sync_mode
& SLAP_SYNC_REFRESH
) ) return SLAP_CB_CONTINUE
;
2163 if ( op
->ors_deref
& LDAP_DEREF_SEARCHING
) {
2164 send_ldap_error( op
, rs
, LDAP_PROTOCOL_ERROR
, "illegal value for derefAliases" );
2168 srs
= op
->o_controls
[slap_cids
.sc_LDAPsync
];
2169 op
->o_managedsait
= SLAP_CONTROL_NONCRITICAL
;
2171 /* If this is a persistent search, set it up right away */
2172 if ( op
->o_sync_mode
& SLAP_SYNC_PERSIST
) {
2182 so
.s_flags
= PS_IS_REFRESHING
| PS_FIND_BASE
;
2183 /* syncprov_findbase expects to be called as a callback... */
2184 sc
.sc_private
= &opc
;
2186 ldap_pvt_thread_mutex_init( &so
.s_mutex
);
2187 cb
= op
->o_callback
;
2188 op
->o_callback
= &sc
;
2189 rs
->sr_err
= syncprov_findbase( op
, &fc
);
2190 op
->o_callback
= cb
;
2191 ldap_pvt_thread_mutex_destroy( &so
.s_mutex
);
2193 if ( rs
->sr_err
!= LDAP_SUCCESS
) {
2194 send_ldap_result( op
, rs
);
2197 sop
= ch_malloc( sizeof( syncops
));
2199 ldap_pvt_thread_mutex_init( &sop
->s_mutex
);
2200 sop
->s_rid
= srs
->sr_state
.rid
;
2201 sop
->s_sid
= srs
->sr_state
.sid
;
2204 ldap_pvt_thread_mutex_lock( &si
->si_ops_mutex
);
2205 sop
->s_next
= si
->si_ops
;
2207 ldap_pvt_thread_mutex_unlock( &si
->si_ops_mutex
);
2210 /* snapshot the ctxcsn */
2211 ldap_pvt_thread_rdwr_rlock( &si
->si_csn_rwlock
);
2212 numcsns
= si
->si_numcsns
;
2214 ber_bvarray_dup_x( &ctxcsn
, si
->si_ctxcsn
, op
->o_tmpmemctx
);
2215 sids
= op
->o_tmpalloc( numcsns
* sizeof(int), op
->o_tmpmemctx
);
2216 for ( i
=0; i
<numcsns
; i
++ )
2217 sids
[i
] = si
->si_sids
[i
];
2222 ldap_pvt_thread_rdwr_runlock( &si
->si_csn_rwlock
);
2224 /* If we have a cookie, handle the PRESENT lookups */
2225 if ( srs
->sr_state
.ctxcsn
) {
2229 /* If we don't have any CSN of our own yet, pretend nothing
2235 if ( !si
->si_nopres
)
2236 do_present
= SS_PRESENT
;
2238 /* If there are SIDs we don't recognize in the cookie, drop them */
2239 for (i
=0; i
<srs
->sr_state
.numcsns
; ) {
2240 for (j
=0; j
<numcsns
; j
++) {
2241 if ( srs
->sr_state
.sids
[i
] == sids
[j
] ) {
2246 if ( j
== numcsns
) {
2247 struct berval tmp
= srs
->sr_state
.ctxcsn
[i
];
2248 j
= srs
->sr_state
.numcsns
- 1;
2249 srs
->sr_state
.ctxcsn
[i
] = srs
->sr_state
.ctxcsn
[j
];
2251 srs
->sr_state
.ctxcsn
[j
] = tmp
;
2252 srs
->sr_state
.numcsns
= j
;
2253 srs
->sr_state
.sids
[i
] = srs
->sr_state
.sids
[j
];
2259 /* Find the smallest CSN */
2260 mincsn
= srs
->sr_state
.ctxcsn
[0];
2261 for ( i
=1; i
<srs
->sr_state
.numcsns
; i
++ ) {
2262 if ( ber_bvcmp( &mincsn
, &srs
->sr_state
.ctxcsn
[i
] ) > 0 )
2263 mincsn
= srs
->sr_state
.ctxcsn
[i
];
2266 /* If nothing has changed, shortcut it */
2267 if ( srs
->sr_state
.numcsns
== numcsns
) {
2269 for ( i
=0; i
<srs
->sr_state
.numcsns
; i
++ ) {
2270 for ( j
=0; j
<numcsns
; j
++ ) {
2271 if ( srs
->sr_state
.sids
[i
] != sids
[j
] )
2273 if ( !bvmatch( &srs
->sr_state
.ctxcsn
[i
], &ctxcsn
[j
] ))
2274 changed
= SS_CHANGED
;
2282 no_change
: if ( !(op
->o_sync_mode
& SLAP_SYNC_PERSIST
) ) {
2283 LDAPControl
*ctrls
[2];
2287 syncprov_done_ctrl( op
, rs
, ctrls
, 0, 0,
2288 NULL
, LDAP_SYNC_REFRESH_DELETES
);
2289 rs
->sr_ctrls
= ctrls
;
2290 rs
->sr_err
= LDAP_SUCCESS
;
2291 send_ldap_result( op
, rs
);
2292 rs
->sr_ctrls
= NULL
;
2298 /* consumer doesn't have the right number of CSNs */
2299 changed
= SS_CHANGED
;
2301 /* Do we have a sessionlog for this search? */
2304 ldap_pvt_thread_mutex_lock( &sl
->sl_mutex
);
2305 /* Are there any log entries, and is the consumer state
2306 * present in the session log?
2308 if ( sl
->sl_num
> 0 && ber_bvcmp( &mincsn
, &sl
->sl_mincsn
) >= 0 ) {
2310 /* mutex is unlocked in playlog */
2311 syncprov_playlog( op
, rs
, sl
, srs
, ctxcsn
, numcsns
, sids
);
2313 ldap_pvt_thread_mutex_unlock( &sl
->sl_mutex
);
2316 /* Is the CSN still present in the database? */
2317 if ( syncprov_findcsn( op
, FIND_CSN
) != LDAP_SUCCESS
) {
2318 /* No, so a reload is required */
2319 /* the 2.2 consumer doesn't send this hint */
2320 if ( si
->si_usehint
&& srs
->sr_rhint
== 0 ) {
2322 ber_bvarray_free_x( ctxcsn
, op
->o_tmpmemctx
);
2324 op
->o_tmpfree( sids
, op
->o_tmpmemctx
);
2325 send_ldap_error( op
, rs
, LDAP_SYNC_REFRESH_REQUIRED
, "sync cookie is stale" );
2328 if ( srs
->sr_state
.ctxcsn
) {
2329 ber_bvarray_free_x( srs
->sr_state
.ctxcsn
, op
->o_tmpmemctx
);
2330 srs
->sr_state
.ctxcsn
= NULL
;
2332 if ( srs
->sr_state
.sids
) {
2333 slap_sl_free( srs
->sr_state
.sids
, op
->o_tmpmemctx
);
2334 srs
->sr_state
.sids
= NULL
;
2336 srs
->sr_state
.numcsns
= 0;
2339 /* If changed and doing Present lookup, send Present UUIDs */
2340 if ( do_present
&& syncprov_findcsn( op
, FIND_PRESENT
) !=
2343 ber_bvarray_free_x( ctxcsn
, op
->o_tmpmemctx
);
2345 op
->o_tmpfree( sids
, op
->o_tmpmemctx
);
2346 send_ldap_result( op
, rs
);
2351 /* No consumer state, assume something has changed */
2352 changed
= SS_CHANGED
;
2356 /* Append CSN range to search filter, save original filter
2357 * for persistent search evaluation
2360 sop
->s_filterstr
= op
->ors_filterstr
;
2363 /* If something changed, find the changes */
2364 if ( gotstate
&& changed
) {
2365 Filter
*fand
, *fava
;
2367 fand
= op
->o_tmpalloc( sizeof(Filter
), op
->o_tmpmemctx
);
2368 fand
->f_choice
= LDAP_FILTER_AND
;
2369 fand
->f_next
= NULL
;
2370 fava
= op
->o_tmpalloc( sizeof(Filter
), op
->o_tmpmemctx
);
2372 fava
->f_choice
= LDAP_FILTER_GE
;
2373 fava
->f_ava
= op
->o_tmpalloc( sizeof(AttributeAssertion
), op
->o_tmpmemctx
);
2374 fava
->f_ava
->aa_desc
= slap_schema
.si_ad_entryCSN
;
2375 #ifdef LDAP_COMP_MATCH
2376 fava
->f_ava
->aa_cf
= NULL
;
2378 ber_dupbv_x( &fava
->f_ava
->aa_value
, &mincsn
, op
->o_tmpmemctx
);
2379 fava
->f_next
= op
->ors_filter
;
2380 op
->ors_filter
= fand
;
2381 filter2bv_x( op
, op
->ors_filter
, &op
->ors_filterstr
);
2383 sop
->s_flags
|= PS_FIX_FILTER
;
2386 /* Let our callback add needed info to returned entries */
2387 cb
= op
->o_tmpcalloc(1, sizeof(slap_callback
)+sizeof(searchstate
), op
->o_tmpmemctx
);
2388 ss
= (searchstate
*)(cb
+1);
2391 ss
->ss_flags
= do_present
| changed
;
2392 ss
->ss_ctxcsn
= ctxcsn
;
2393 ss
->ss_numcsns
= numcsns
;
2395 cb
->sc_response
= syncprov_search_response
;
2396 cb
->sc_cleanup
= syncprov_search_cleanup
;
2397 cb
->sc_private
= ss
;
2398 cb
->sc_next
= op
->o_callback
;
2399 op
->o_callback
= cb
;
2401 /* If this is a persistent search and no changes were reported during
2402 * the refresh phase, just invoke the response callback to transition
2403 * us into persist phase
2406 rs
->sr_err
= LDAP_SUCCESS
;
2407 rs
->sr_nentries
= 0;
2408 send_ldap_result( op
, rs
);
2411 return SLAP_CB_CONTINUE
;
2415 syncprov_operational(
2419 slap_overinst
*on
= (slap_overinst
*)op
->o_bd
->bd_info
;
2420 syncprov_info_t
*si
= (syncprov_info_t
*)on
->on_bi
.bi_private
;
2422 /* This prevents generating unnecessarily; frontend will strip
2423 * any statically stored copy.
2425 if ( op
->o_sync
!= SLAP_CONTROL_NONE
)
2426 return SLAP_CB_CONTINUE
;
2428 if ( rs
->sr_entry
&&
2429 dn_match( &rs
->sr_entry
->e_nname
, op
->o_bd
->be_nsuffix
)) {
2431 if ( SLAP_OPATTRS( rs
->sr_attr_flags
) ||
2432 ad_inlist( slap_schema
.si_ad_contextCSN
, rs
->sr_attrs
)) {
2433 Attribute
*a
, **ap
= NULL
;
2435 for ( a
=rs
->sr_entry
->e_attrs
; a
; a
=a
->a_next
) {
2436 if ( a
->a_desc
== slap_schema
.si_ad_contextCSN
)
2440 ldap_pvt_thread_rdwr_rlock( &si
->si_csn_rwlock
);
2441 if ( si
->si_ctxcsn
) {
2443 for ( ap
= &rs
->sr_operational_attrs
; *ap
;
2444 ap
=&(*ap
)->a_next
);
2446 a
= attr_alloc( slap_schema
.si_ad_contextCSN
);
2451 if ( !(rs
->sr_flags
& REP_ENTRY_MODIFIABLE
) ) {
2452 Entry
*e
= entry_dup( rs
->sr_entry
);
2453 if ( rs
->sr_flags
& REP_ENTRY_MUSTRELEASE
) {
2454 overlay_entry_release_ov( op
, rs
->sr_entry
, 0, on
);
2455 rs
->sr_flags
^= REP_ENTRY_MUSTRELEASE
;
2456 } else if ( rs
->sr_flags
& REP_ENTRY_MUSTBEFREED
) {
2457 entry_free( rs
->sr_entry
);
2461 REP_ENTRY_MODIFIABLE
|REP_ENTRY_MUSTBEFREED
;
2462 a
= attr_find( rs
->sr_entry
->e_attrs
,
2463 slap_schema
.si_ad_contextCSN
);
2465 if ( a
->a_nvals
!= a
->a_vals
) {
2466 ber_bvarray_free( a
->a_nvals
);
2469 ber_bvarray_free( a
->a_vals
);
2473 attr_valadd( a
, si
->si_ctxcsn
, si
->si_ctxcsn
, si
->si_numcsns
);
2475 ldap_pvt_thread_rdwr_runlock( &si
->si_csn_rwlock
);
2478 return SLAP_CB_CONTINUE
;
2488 static ConfigDriver sp_cf_gen
;
2490 static ConfigTable spcfg
[] = {
2491 { "syncprov-checkpoint", "ops> <minutes", 3, 3, 0, ARG_MAGIC
|SP_CHKPT
,
2492 sp_cf_gen
, "( OLcfgOvAt:1.1 NAME 'olcSpCheckpoint' "
2493 "DESC 'ContextCSN checkpoint interval in ops and minutes' "
2494 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL
, NULL
},
2495 { "syncprov-sessionlog", "ops", 2, 2, 0, ARG_INT
|ARG_MAGIC
|SP_SESSL
,
2496 sp_cf_gen
, "( OLcfgOvAt:1.2 NAME 'olcSpSessionlog' "
2497 "DESC 'Session log size in ops' "
2498 "SYNTAX OMsInteger SINGLE-VALUE )", NULL
, NULL
},
2499 { "syncprov-nopresent", NULL
, 2, 2, 0, ARG_ON_OFF
|ARG_MAGIC
|SP_NOPRES
,
2500 sp_cf_gen
, "( OLcfgOvAt:1.3 NAME 'olcSpNoPresent' "
2501 "DESC 'Omit Present phase processing' "
2502 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL
, NULL
},
2503 { "syncprov-reloadhint", NULL
, 2, 2, 0, ARG_ON_OFF
|ARG_MAGIC
|SP_USEHINT
,
2504 sp_cf_gen
, "( OLcfgOvAt:1.4 NAME 'olcSpReloadHint' "
2505 "DESC 'Observe Reload Hint in Request control' "
2506 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL
, NULL
},
2507 { NULL
, NULL
, 0, 0, 0, ARG_IGNORED
}
2510 static ConfigOCs spocs
[] = {
2511 { "( OLcfgOvOc:1.1 "
2512 "NAME 'olcSyncProvConfig' "
2513 "DESC 'SyncRepl Provider configuration' "
2514 "SUP olcOverlayConfig "
2515 "MAY ( olcSpCheckpoint "
2516 "$ olcSpSessionlog "
2518 "$ olcSpReloadHint "
2520 Cft_Overlay
, spcfg
},
2525 sp_cf_gen(ConfigArgs
*c
)
2527 slap_overinst
*on
= (slap_overinst
*)c
->bi
;
2528 syncprov_info_t
*si
= (syncprov_info_t
*)on
->on_bi
.bi_private
;
2531 if ( c
->op
== SLAP_CONFIG_EMIT
) {
2532 switch ( c
->type
) {
2534 if ( si
->si_chkops
|| si
->si_chktime
) {
2536 bv
.bv_len
= snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
2537 "%d %d", si
->si_chkops
, si
->si_chktime
);
2538 if ( bv
.bv_len
< 0 || bv
.bv_len
>= sizeof( c
->cr_msg
) ) {
2541 bv
.bv_val
= c
->cr_msg
;
2542 value_add_one( &c
->rvalue_vals
, &bv
);
2549 if ( si
->si_logs
) {
2550 c
->value_int
= si
->si_logs
->sl_size
;
2556 if ( si
->si_nopres
) {
2563 if ( si
->si_usehint
) {
2571 } else if ( c
->op
== LDAP_MOD_DELETE
) {
2572 switch ( c
->type
) {
2579 si
->si_logs
->sl_size
= 0;
2581 rc
= LDAP_NO_SUCH_ATTRIBUTE
;
2584 if ( si
->si_nopres
)
2587 rc
= LDAP_NO_SUCH_ATTRIBUTE
;
2590 if ( si
->si_usehint
)
2593 rc
= LDAP_NO_SUCH_ATTRIBUTE
;
2598 switch ( c
->type
) {
2600 if ( lutil_atoi( &si
->si_chkops
, c
->argv
[1] ) != 0 ) {
2601 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "%s unable to parse checkpoint ops # \"%s\"",
2602 c
->argv
[0], c
->argv
[1] );
2603 Debug( LDAP_DEBUG_CONFIG
|LDAP_DEBUG_NONE
,
2604 "%s: %s\n", c
->log
, c
->cr_msg
, 0 );
2605 return ARG_BAD_CONF
;
2607 if ( si
->si_chkops
<= 0 ) {
2608 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "%s invalid checkpoint ops # \"%d\"",
2609 c
->argv
[0], si
->si_chkops
);
2610 Debug( LDAP_DEBUG_CONFIG
|LDAP_DEBUG_NONE
,
2611 "%s: %s\n", c
->log
, c
->cr_msg
, 0 );
2612 return ARG_BAD_CONF
;
2614 if ( lutil_atoi( &si
->si_chktime
, c
->argv
[2] ) != 0 ) {
2615 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "%s unable to parse checkpoint time \"%s\"",
2616 c
->argv
[0], c
->argv
[1] );
2617 Debug( LDAP_DEBUG_CONFIG
|LDAP_DEBUG_NONE
,
2618 "%s: %s\n", c
->log
, c
->cr_msg
, 0 );
2619 return ARG_BAD_CONF
;
2621 if ( si
->si_chktime
<= 0 ) {
2622 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "%s invalid checkpoint time \"%d\"",
2623 c
->argv
[0], si
->si_chkops
);
2624 Debug( LDAP_DEBUG_CONFIG
|LDAP_DEBUG_NONE
,
2625 "%s: %s\n", c
->log
, c
->cr_msg
, 0 );
2626 return ARG_BAD_CONF
;
2628 si
->si_chktime
*= 60;
2632 int size
= c
->value_int
;
2635 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "%s size %d is negative",
2637 Debug( LDAP_DEBUG_CONFIG
|LDAP_DEBUG_NONE
,
2638 "%s: %s\n", c
->log
, c
->cr_msg
, 0 );
2639 return ARG_BAD_CONF
;
2643 sl
= ch_malloc( sizeof( sessionlog
) + LDAP_LUTIL_CSNSTR_BUFSIZE
);
2644 sl
->sl_mincsn
.bv_val
= (char *)(sl
+1);
2645 sl
->sl_mincsn
.bv_len
= 0;
2647 sl
->sl_head
= sl
->sl_tail
= NULL
;
2648 ldap_pvt_thread_mutex_init( &sl
->sl_mutex
);
2655 si
->si_nopres
= c
->value_int
;
2658 si
->si_usehint
= c
->value_int
;
2664 /* ITS#3456 we cannot run this search on the main thread, must use a
2665 * child thread in order to insure we have a big enough stack.
2672 syncprov_findcsn( ptr
, FIND_MAXCSN
);
2676 /* Read any existing contextCSN from the underlying db.
2677 * Then search for any entries newer than that. If no value exists,
2678 * just generate it. Cache whatever result.
2686 slap_overinst
*on
= (slap_overinst
*) be
->bd_info
;
2687 syncprov_info_t
*si
= (syncprov_info_t
*)on
->on_bi
.bi_private
;
2689 Connection conn
= { 0 };
2690 OperationBuffer opbuf
;
2695 void *thrctx
= NULL
;
2697 if ( !SLAP_LASTMOD( be
)) {
2698 Debug( LDAP_DEBUG_ANY
,
2699 "syncprov_db_open: invalid config, lastmod must be enabled\n", 0, 0, 0 );
2703 if ( slapMode
& SLAP_TOOL_MODE
) {
2707 rc
= overlay_register_control( be
, LDAP_CONTROL_SYNC
);
2712 thrctx
= ldap_pvt_thread_pool_context();
2713 connection_fake_init( &conn
, &opbuf
, thrctx
);
2716 op
->o_dn
= be
->be_rootdn
;
2717 op
->o_ndn
= be
->be_rootndn
;
2719 rc
= overlay_entry_get_ov( op
, be
->be_nsuffix
, NULL
,
2720 slap_schema
.si_ad_contextCSN
, 0, &e
, on
);
2723 ldap_pvt_thread_t tid
;
2725 a
= attr_find( e
->e_attrs
, slap_schema
.si_ad_contextCSN
);
2727 ber_bvarray_dup_x( &si
->si_ctxcsn
, a
->a_vals
, NULL
);
2728 si
->si_numcsns
= a
->a_numvals
;
2729 si
->si_sids
= slap_parse_csn_sids( si
->si_ctxcsn
, a
->a_numvals
, NULL
);
2731 overlay_entry_release_ov( op
, e
, 0, on
);
2732 if ( si
->si_ctxcsn
) {
2733 op
->o_req_dn
= be
->be_suffix
[0];
2734 op
->o_req_ndn
= be
->be_nsuffix
[0];
2735 op
->ors_scope
= LDAP_SCOPE_SUBTREE
;
2736 ldap_pvt_thread_create( &tid
, 0, syncprov_db_otask
, op
);
2737 ldap_pvt_thread_join( tid
, NULL
);
2741 /* Didn't find a contextCSN, should we generate one? */
2742 if ( !si
->si_ctxcsn
) {
2743 char csnbuf
[ LDAP_LUTIL_CSNSTR_BUFSIZE
];
2746 if ( SLAP_SYNC_SHADOW( op
->o_bd
)) {
2747 /* If we're also a consumer, then don't generate anything.
2748 * Wait for our provider to send it to us, or for a local
2749 * modify if we have multimaster.
2753 csn
.bv_val
= csnbuf
;
2754 csn
.bv_len
= sizeof( csnbuf
);
2755 slap_get_csn( op
, &csn
, 0 );
2756 value_add_one( &si
->si_ctxcsn
, &csn
);
2758 si
->si_sids
= ch_malloc( sizeof(int) );
2759 si
->si_sids
[0] = slap_serverID
;
2761 /* make sure we do a checkpoint on close */
2766 op
->o_bd
->bd_info
= (BackendInfo
*)on
;
2770 /* Write the current contextCSN into the underlying db.
2778 slap_overinst
*on
= (slap_overinst
*) be
->bd_info
;
2779 syncprov_info_t
*si
= (syncprov_info_t
*)on
->on_bi
.bi_private
;
2781 if ( slapMode
& SLAP_TOOL_MODE
) {
2784 if ( si
->si_numops
) {
2785 Connection conn
= {0};
2786 OperationBuffer opbuf
;
2788 SlapReply rs
= {REP_RESULT
};
2791 thrctx
= ldap_pvt_thread_pool_context();
2792 connection_fake_init( &conn
, &opbuf
, thrctx
);
2795 op
->o_dn
= be
->be_rootdn
;
2796 op
->o_ndn
= be
->be_rootndn
;
2797 syncprov_checkpoint( op
, &rs
, on
);
2809 slap_overinst
*on
= (slap_overinst
*)be
->bd_info
;
2810 syncprov_info_t
*si
;
2812 if ( SLAP_ISGLOBALOVERLAY( be
) ) {
2813 Debug( LDAP_DEBUG_ANY
,
2814 "syncprov must be instantiated within a database.\n",
2819 si
= ch_calloc(1, sizeof(syncprov_info_t
));
2820 on
->on_bi
.bi_private
= si
;
2821 ldap_pvt_thread_rdwr_init( &si
->si_csn_rwlock
);
2822 ldap_pvt_thread_mutex_init( &si
->si_ops_mutex
);
2823 ldap_pvt_thread_mutex_init( &si
->si_mods_mutex
);
2825 csn_anlist
[0].an_desc
= slap_schema
.si_ad_entryCSN
;
2826 csn_anlist
[0].an_name
= slap_schema
.si_ad_entryCSN
->ad_cname
;
2827 csn_anlist
[1].an_desc
= slap_schema
.si_ad_entryUUID
;
2828 csn_anlist
[1].an_name
= slap_schema
.si_ad_entryUUID
->ad_cname
;
2830 uuid_anlist
[0].an_desc
= slap_schema
.si_ad_entryUUID
;
2831 uuid_anlist
[0].an_name
= slap_schema
.si_ad_entryUUID
->ad_cname
;
2837 syncprov_db_destroy(
2842 slap_overinst
*on
= (slap_overinst
*)be
->bd_info
;
2843 syncprov_info_t
*si
= (syncprov_info_t
*)on
->on_bi
.bi_private
;
2846 if ( si
->si_logs
) {
2847 slog_entry
*se
= si
->si_logs
->sl_head
;
2850 slog_entry
*se_next
= se
->se_next
;
2855 ch_free( si
->si_logs
);
2857 if ( si
->si_ctxcsn
)
2858 ber_bvarray_free( si
->si_ctxcsn
);
2860 ch_free( si
->si_sids
);
2861 ldap_pvt_thread_mutex_destroy( &si
->si_mods_mutex
);
2862 ldap_pvt_thread_mutex_destroy( &si
->si_ops_mutex
);
2863 ldap_pvt_thread_rdwr_destroy( &si
->si_csn_rwlock
);
2870 static int syncprov_parseCtrl (
2876 BerElementBuffer berbuf
;
2877 BerElement
*ber
= (BerElement
*)&berbuf
;
2880 struct berval cookie
= BER_BVNULL
;
2884 if ( op
->o_sync
!= SLAP_CONTROL_NONE
) {
2885 rs
->sr_text
= "Sync control specified multiple times";
2886 return LDAP_PROTOCOL_ERROR
;
2889 if ( op
->o_pagedresults
!= SLAP_CONTROL_NONE
) {
2890 rs
->sr_text
= "Sync control specified with pagedResults control";
2891 return LDAP_PROTOCOL_ERROR
;
2894 if ( BER_BVISNULL( &ctrl
->ldctl_value
) ) {
2895 rs
->sr_text
= "Sync control value is absent";
2896 return LDAP_PROTOCOL_ERROR
;
2899 if ( BER_BVISEMPTY( &ctrl
->ldctl_value
) ) {
2900 rs
->sr_text
= "Sync control value is empty";
2901 return LDAP_PROTOCOL_ERROR
;
2904 /* Parse the control value
2905 * syncRequestValue ::= SEQUENCE {
2910 * refreshAndPersist (3)
2912 * cookie syncCookie OPTIONAL
2916 ber_init2( ber
, &ctrl
->ldctl_value
, 0 );
2918 if ( (tag
= ber_scanf( ber
, "{i" /*}*/, &mode
)) == LBER_ERROR
) {
2919 rs
->sr_text
= "Sync control : mode decoding error";
2920 return LDAP_PROTOCOL_ERROR
;
2924 case LDAP_SYNC_REFRESH_ONLY
:
2925 mode
= SLAP_SYNC_REFRESH
;
2927 case LDAP_SYNC_REFRESH_AND_PERSIST
:
2928 mode
= SLAP_SYNC_REFRESH_AND_PERSIST
;
2931 rs
->sr_text
= "Sync control : unknown update mode";
2932 return LDAP_PROTOCOL_ERROR
;
2935 tag
= ber_peek_tag( ber
, &len
);
2937 if ( tag
== LDAP_TAG_SYNC_COOKIE
) {
2938 if (( ber_scanf( ber
, /*{*/ "m", &cookie
)) == LBER_ERROR
) {
2939 rs
->sr_text
= "Sync control : cookie decoding error";
2940 return LDAP_PROTOCOL_ERROR
;
2942 tag
= ber_peek_tag( ber
, &len
);
2944 if ( tag
== LDAP_TAG_RELOAD_HINT
) {
2945 if (( ber_scanf( ber
, /*{*/ "b", &rhint
)) == LBER_ERROR
) {
2946 rs
->sr_text
= "Sync control : rhint decoding error";
2947 return LDAP_PROTOCOL_ERROR
;
2950 if (( ber_scanf( ber
, /*{*/ "}")) == LBER_ERROR
) {
2951 rs
->sr_text
= "Sync control : decoding error";
2952 return LDAP_PROTOCOL_ERROR
;
2954 sr
= op
->o_tmpcalloc( 1, sizeof(struct sync_control
), op
->o_tmpmemctx
);
2955 sr
->sr_rhint
= rhint
;
2956 if (!BER_BVISNULL(&cookie
)) {
2957 ber_dupbv_x( &sr
->sr_state
.octet_str
, &cookie
, op
->o_tmpmemctx
);
2958 /* If parse fails, pretend no cookie was sent */
2959 if ( slap_parse_sync_cookie( &sr
->sr_state
, op
->o_tmpmemctx
) ||
2960 sr
->sr_state
.rid
== -1 ) {
2961 if ( sr
->sr_state
.ctxcsn
) {
2962 ber_bvarray_free_x( sr
->sr_state
.ctxcsn
, op
->o_tmpmemctx
);
2963 sr
->sr_state
.ctxcsn
= NULL
;
2965 sr
->sr_state
.numcsns
= 0;
2969 op
->o_controls
[slap_cids
.sc_LDAPsync
] = sr
;
2971 op
->o_sync
= ctrl
->ldctl_iscritical
2972 ? SLAP_CONTROL_CRITICAL
2973 : SLAP_CONTROL_NONCRITICAL
;
2975 op
->o_sync_mode
|= mode
; /* o_sync_mode shares o_sync */
2977 return LDAP_SUCCESS
;
2980 /* This overlay is set up for dynamic loading via moduleload. For static
2981 * configuration, you'll need to arrange for the slap_overinst to be
2982 * initialized and registered by some other function inside slapd.
2985 static slap_overinst syncprov
;
2988 syncprov_initialize()
2992 rc
= register_supported_control( LDAP_CONTROL_SYNC
,
2993 SLAP_CTRL_SEARCH
, NULL
,
2994 syncprov_parseCtrl
, &slap_cids
.sc_LDAPsync
);
2995 if ( rc
!= LDAP_SUCCESS
) {
2996 Debug( LDAP_DEBUG_ANY
,
2997 "syncprov_init: Failed to register control %d\n", rc
, 0, 0 );
3001 syncprov
.on_bi
.bi_type
= "syncprov";
3002 syncprov
.on_bi
.bi_db_init
= syncprov_db_init
;
3003 syncprov
.on_bi
.bi_db_destroy
= syncprov_db_destroy
;
3004 syncprov
.on_bi
.bi_db_open
= syncprov_db_open
;
3005 syncprov
.on_bi
.bi_db_close
= syncprov_db_close
;
3007 syncprov
.on_bi
.bi_op_abandon
= syncprov_op_abandon
;
3008 syncprov
.on_bi
.bi_op_cancel
= syncprov_op_abandon
;
3010 syncprov
.on_bi
.bi_op_add
= syncprov_op_mod
;
3011 syncprov
.on_bi
.bi_op_compare
= syncprov_op_compare
;
3012 syncprov
.on_bi
.bi_op_delete
= syncprov_op_mod
;
3013 syncprov
.on_bi
.bi_op_modify
= syncprov_op_mod
;
3014 syncprov
.on_bi
.bi_op_modrdn
= syncprov_op_mod
;
3015 syncprov
.on_bi
.bi_op_search
= syncprov_op_search
;
3016 syncprov
.on_bi
.bi_extended
= syncprov_op_extended
;
3017 syncprov
.on_bi
.bi_operational
= syncprov_operational
;
3019 syncprov
.on_bi
.bi_cf_ocs
= spocs
;
3021 generic_filter
.f_desc
= slap_schema
.si_ad_objectClass
;
3023 rc
= config_register_schema( spcfg
, spocs
);
3024 if ( rc
) return rc
;
3026 return overlay_register( &syncprov
);
3029 #if SLAPD_OVER_SYNCPROV == SLAPD_MOD_DYNAMIC
3031 init_module( int argc
, char *argv
[] )
3033 return syncprov_initialize();
3035 #endif /* SLAPD_OVER_SYNCPROV == SLAPD_MOD_DYNAMIC */
3037 #endif /* defined(SLAPD_OVER_SYNCPROV) */