+enum {
+ SP_CHKPT = 1,
+ SP_SESSL,
+ SP_NOPRES,
+ SP_USEHINT
+};
+
+static ConfigDriver sp_cf_gen;
+
+static ConfigTable spcfg[] = {
+ { "syncprov-checkpoint", "ops> <minutes", 3, 3, 0, ARG_MAGIC|SP_CHKPT,
+ sp_cf_gen, "( OLcfgOvAt:1.1 NAME 'olcSpCheckpoint' "
+ "DESC 'ContextCSN checkpoint interval in ops and minutes' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "syncprov-sessionlog", "ops", 2, 2, 0, ARG_INT|ARG_MAGIC|SP_SESSL,
+ sp_cf_gen, "( OLcfgOvAt:1.2 NAME 'olcSpSessionlog' "
+ "DESC 'Session log size in ops' "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "syncprov-nopresent", NULL, 2, 2, 0, ARG_ON_OFF|ARG_MAGIC|SP_NOPRES,
+ sp_cf_gen, "( OLcfgOvAt:1.3 NAME 'olcSpNoPresent' "
+ "DESC 'Omit Present phase processing' "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "syncprov-reloadhint", NULL, 2, 2, 0, ARG_ON_OFF|ARG_MAGIC|SP_USEHINT,
+ sp_cf_gen, "( OLcfgOvAt:1.4 NAME 'olcSpReloadHint' "
+ "DESC 'Observe Reload Hint in Request control' "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs spocs[] = {
+ { "( OLcfgOvOc:1.1 "
+ "NAME 'olcSyncProvConfig' "
+ "DESC 'SyncRepl Provider configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcSpCheckpoint $ olcSpSessionlog $ olcSpNoPresent ) )",
+ Cft_Overlay, spcfg },
+ { NULL, 0, NULL }
+};
+
+static int
+sp_cf_gen(ConfigArgs *c)
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
+ int rc = 0;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ switch ( c->type ) {
+ case SP_CHKPT:
+ if ( si->si_chkops || si->si_chktime ) {
+ struct berval bv;
+ bv.bv_len = sprintf( c->msg, "%d %d",
+ si->si_chkops, si->si_chktime );
+ bv.bv_val = c->msg;
+ value_add_one( &c->rvalue_vals, &bv );
+ } else {
+ rc = 1;
+ }
+ break;
+ case SP_SESSL:
+ if ( si->si_logs ) {
+ c->value_int = si->si_logs->sl_size;
+ } else {
+ rc = 1;
+ }
+ break;
+ case SP_NOPRES:
+ if ( si->si_nopres ) {
+ c->value_int = 1;
+ } else {
+ rc = 1;
+ }
+ break;
+ case SP_USEHINT:
+ if ( si->si_usehint ) {
+ c->value_int = 1;
+ } else {
+ rc = 1;
+ }
+ break;
+ }
+ return rc;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ switch ( c->type ) {
+ case SP_CHKPT:
+ si->si_chkops = 0;
+ si->si_chktime = 0;
+ break;
+ case SP_SESSL:
+ if ( si->si_logs )
+ si->si_logs->sl_size = 0;
+ else
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ break;
+ case SP_NOPRES:
+ if ( si->si_nopres )
+ si->si_nopres = 0;
+ else
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ break;
+ case SP_USEHINT:
+ if ( si->si_usehint )
+ si->si_usehint = 0;
+ else
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ break;
+ }
+ return rc;
+ }
+ switch ( c->type ) {
+ case SP_CHKPT:
+ if ( lutil_atoi( &si->si_chkops, c->argv[1] ) != 0 ) {
+ sprintf( c->msg, "%s unable to parse checkpoint ops # \"%s\"",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s\n", c->log, c->msg, 0 );
+ return ARG_BAD_CONF;
+ }
+ if ( si->si_chkops <= 0 ) {
+ sprintf( c->msg, "%s invalid checkpoint ops # \"%d\"",
+ c->argv[0], si->si_chkops );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s\n", c->log, c->msg, 0 );
+ return ARG_BAD_CONF;
+ }
+ if ( lutil_atoi( &si->si_chktime, c->argv[2] ) != 0 ) {
+ sprintf( c->msg, "%s unable to parse checkpoint time \"%s\"",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s\n", c->log, c->msg, 0 );
+ return ARG_BAD_CONF;
+ }
+ if ( si->si_chktime <= 0 ) {
+ sprintf( c->msg, "%s invalid checkpoint time \"%d\"",
+ c->argv[0], si->si_chkops );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s\n", c->log, c->msg, 0 );
+ return ARG_BAD_CONF;
+ }
+ si->si_chktime *= 60;
+ break;
+ case SP_SESSL: {
+ sessionlog *sl;
+ int size = c->value_int;
+
+ if ( size < 0 ) {
+ sprintf( c->msg, "%s size %d is negative",
+ c->argv[0], size );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s\n", c->log, c->msg, 0 );
+ return ARG_BAD_CONF;
+ }
+ sl = si->si_logs;
+ if ( !sl ) {
+ sl = ch_malloc( sizeof( sessionlog ) + LDAP_LUTIL_CSNSTR_BUFSIZE );
+ sl->sl_mincsn.bv_val = (char *)(sl+1);
+ sl->sl_mincsn.bv_len = 0;
+ sl->sl_num = 0;
+ sl->sl_head = sl->sl_tail = NULL;
+ ldap_pvt_thread_mutex_init( &sl->sl_mutex );
+ si->si_logs = sl;
+ }
+ sl->sl_size = size;
+ }
+ break;
+ case SP_NOPRES:
+ si->si_nopres = c->value_int;
+ break;
+ case SP_USEHINT:
+ si->si_usehint = c->value_int;
+ break;
+ }
+ return rc;
+}
+
+/* ITS#3456 we cannot run this search on the main thread, must use a
+ * child thread in order to insure we have a big enough stack.
+ */
+static void *
+syncprov_db_otask(
+ void *ptr
+)
+{
+ syncprov_findcsn( ptr, FIND_MAXCSN );
+ return NULL;
+}
+
+/* Read any existing contextCSN from the underlying db.
+ * Then search for any entries newer than that. If no value exists,
+ * just generate it. Cache whatever result.
+ */
+static int
+syncprov_db_open(
+ BackendDB *be
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
+
+ Connection conn = { 0 };
+ OperationBuffer opbuf = { 0 };
+ char ctxcsnbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
+ Operation *op = (Operation *) &opbuf;
+ Entry *e;
+ Attribute *a;
+ int rc;
+ void *thrctx = NULL;
+
+ if ( !SLAP_LASTMOD( be )) {
+ Debug( LDAP_DEBUG_ANY,
+ "syncprov_db_open: invalid config, lastmod must be enabled\n", 0, 0, 0 );
+ return -1;
+ }
+
+ if ( slapMode & SLAP_TOOL_MODE ) {
+ return 0;
+ }
+
+ rc = overlay_register_control( be, LDAP_CONTROL_SYNC );
+ if ( rc ) {
+ return rc;
+ }
+
+ thrctx = ldap_pvt_thread_pool_context();
+ connection_fake_init( &conn, op, thrctx );
+ op->o_bd = be;
+ op->o_dn = be->be_rootdn;
+ op->o_ndn = be->be_rootndn;
+
+ ctxcsnbuf[0] = '\0';
+
+ op->o_bd->bd_info = on->on_info->oi_orig;
+ rc = be_entry_get_rw( op, be->be_nsuffix, NULL,
+ slap_schema.si_ad_contextCSN, 0, &e );
+
+ if ( e ) {
+ ldap_pvt_thread_t tid;
+
+ a = attr_find( e->e_attrs, slap_schema.si_ad_contextCSN );
+ if ( a ) {
+ si->si_ctxcsn.bv_len = a->a_nvals[0].bv_len;
+ if ( si->si_ctxcsn.bv_len >= sizeof(si->si_ctxcsnbuf ))
+ si->si_ctxcsn.bv_len = sizeof(si->si_ctxcsnbuf)-1;
+ strncpy( si->si_ctxcsnbuf, a->a_nvals[0].bv_val,
+ si->si_ctxcsn.bv_len );
+ si->si_ctxcsnbuf[si->si_ctxcsn.bv_len] = '\0';
+ strcpy( ctxcsnbuf, si->si_ctxcsnbuf );
+ }
+ be_entry_release_rw( op, e, 0 );
+ if ( !BER_BVISEMPTY( &si->si_ctxcsn ) ) {
+ op->o_bd->bd_info = (BackendInfo *)on;
+ op->o_req_dn = be->be_suffix[0];
+ op->o_req_ndn = be->be_nsuffix[0];
+ op->ors_scope = LDAP_SCOPE_SUBTREE;
+ ldap_pvt_thread_create( &tid, 0, syncprov_db_otask, op );
+ ldap_pvt_thread_join( tid, NULL );
+ }