]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/back-ldap/config.c
rework quarantine locking and so
[openldap] / servers / slapd / back-ldap / config.c
index 2df70bcfa91a795f4ef15786367754e9fddf8b83..14a44a51ed99627ab7e4d495fe752d3d52d3059a 100644 (file)
@@ -64,6 +64,9 @@ enum {
        LDAP_BACK_CFG_CONN_TTL,
        LDAP_BACK_CFG_NETWORK_TIMEOUT,
        LDAP_BACK_CFG_VERSION,
+       LDAP_BACK_CFG_SINGLECONN,
+       LDAP_BACK_CFG_CANCEL,
+       LDAP_BACK_CFG_QUARANTINE,
        LDAP_BACK_CFG_REWRITE,
 
        LDAP_BACK_CFG_LAST
@@ -250,6 +253,30 @@ static ConfigTable ldapcfg[] = {
                        "SYNTAX OMsInteger "
                        "SINGLE-VALUE )",
                NULL, NULL },
+       { "single-conn", "TRUE/FALSE", 2, 0, 0,
+               ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_SINGLECONN,
+               ldap_back_cf_gen, "( OLcfgDbAt:3.19 "
+                       "NAME 'olcDbSingleConn' "
+                       "DESC 'cache a single connection per identity' "
+                       "SYNTAX OMsBoolean "
+                       "SINGLE-VALUE )",
+               NULL, NULL },
+       { "cancel", "ABANDON|ignore|exop", 2, 0, 0,
+               ARG_MAGIC|LDAP_BACK_CFG_CANCEL,
+               ldap_back_cf_gen, "( OLcfgDbAt:3.20 "
+                       "NAME 'olcDbCancel' "
+                       "DESC 'abandon/ignore/exop operations when appropriate' "
+                       "SYNTAX OMsDirectoryString "
+                       "SINGLE-VALUE )",
+               NULL, NULL },
+       { "quarantine", "retrylist", 2, 0, 0,
+               ARG_MAGIC|LDAP_BACK_CFG_QUARANTINE,
+               ldap_back_cf_gen, "( OLcfgDbAt:3.21 "
+                       "NAME 'olcDbQuarantine' "
+                       "DESC 'Quarantine database if connection fails and retry according to rule' "
+                       "SYNTAX OMsDirectoryString "
+                       "SINGLE-VALUE )",
+               NULL, NULL },
        { "suffixmassage", "[virtual]> <real", 2, 3, 0,
                ARG_STRING|ARG_MAGIC|LDAP_BACK_CFG_REWRITE,
                ldap_back_cf_gen, NULL, NULL, NULL },
@@ -284,6 +311,9 @@ static ConfigOCs ldapocs[] = {
                        "$ olcDbProxyWhoAmI "
                        "$ olcDbTimeout "
                        "$ olcDbIdleTimeout "
+                       "$ olcDbSingleConn "
+                       "$ olcDbCancel "
+                       "$ olcDbQuarantine "
                ") )",
                        Cft_Database, ldapcfg},
        { NULL, 0, NULL }
@@ -307,12 +337,20 @@ static slap_verbmasks tls_mode[] = {
 };
 
 static slap_verbmasks t_f_mode[] = {
-       { BER_BVC( "yes" ),             LDAP_BACK_F_SUPPORT_T_F },
-       { BER_BVC( "discover" ),        LDAP_BACK_F_SUPPORT_T_F_DISCOVER },
+       { BER_BVC( "yes" ),             LDAP_BACK_F_T_F },
+       { BER_BVC( "discover" ),        LDAP_BACK_F_T_F_DISCOVER },
        { BER_BVC( "no" ),              LDAP_BACK_F_NONE },
        { BER_BVNULL,                   0 }
 };
 
+static slap_verbmasks cancel_mode[] = {
+       { BER_BVC( "ignore" ),          LDAP_BACK_F_CANCEL_IGNORE },
+       { BER_BVC( "exop" ),            LDAP_BACK_F_CANCEL_EXOP },
+       { BER_BVC( "exop-discover" ),   LDAP_BACK_F_CANCEL_EXOP_DISCOVER },
+       { BER_BVC( "abandon" ),         LDAP_BACK_F_CANCEL_ABANDON },
+       { BER_BVNULL,                   0 }
+};
+
 static slap_cf_aux_table timeout_table[] = {
        { BER_BVC("add="), 0 * sizeof( time_t ), 'u', 0, NULL },
        { BER_BVC("delete="), 1 * sizeof( time_t ), 'u', 0, NULL },
@@ -321,16 +359,168 @@ static slap_cf_aux_table timeout_table[] = {
        { BER_BVNULL, 0, 0, 0, NULL }
 };
 
+int
+slap_retry_info_parse(
+       char                    *in,
+       slap_retry_info_t       *ri,
+       char                    *buf,
+       ber_len_t               buflen )
+{
+       char                    **retrylist = NULL;
+       int                     rc = 0;
+       int                     i;
+
+       slap_str2clist( &retrylist, in, " ;" );
+       if ( retrylist == NULL ) {
+               return 1;
+       }
+
+       for ( i = 0; retrylist[ i ] != NULL; i++ )
+               /* count */ ;
+
+       ri->ri_interval = ch_calloc( sizeof( time_t ), i + 1 );
+       ri->ri_num = ch_calloc( sizeof( int ), i + 1 );
+
+       for ( i = 0; retrylist[ i ] != NULL; i++ ) {
+               unsigned long   t;
+               char            *sep = strchr( retrylist[ i ], ',' );
+
+               if ( sep == NULL ) {
+                       snprintf( buf, buflen,
+                               "missing comma in retry pattern #%d \"%s\"",
+                               i, retrylist[ i ] );
+                       rc = 1;
+                       goto done;
+               }
+
+               *sep++ = '\0';
+
+               if ( lutil_parse_time( retrylist[ i ], &t ) ) {
+                       snprintf( buf, buflen,
+                               "unable to parse interval #%d \"%s\"",
+                               i, retrylist[ i ] );
+                       rc = 1;
+                       goto done;
+               }
+               ri->ri_interval[ i ] = (time_t)t;
+
+               if ( strcmp( sep, "+" ) == 0 ) {
+                       if ( retrylist[ i + 1 ] != NULL ) {
+                               snprintf( buf, buflen,
+                                       "extra cruft after retry pattern "
+                                       "#%d \"%s,+\" with \"forever\" mark",
+                                       i, retrylist[ i ] );
+                               rc = 1;
+                               goto done;
+                       }
+                       ri->ri_num[ i ] = SLAP_RETRYNUM_FOREVER;
+                       
+               } else if ( lutil_atoi( &ri->ri_num[ i ], sep ) ) {
+                       snprintf( buf, buflen,
+                               "unable to parse retry num #%d \"%s\"",
+                               i, sep );
+                       rc = 1;
+                       goto done;
+               }
+       }
+
+       ri->ri_num[ i ] = SLAP_RETRYNUM_TAIL;
+
+       ri->ri_idx = 0;
+       ri->ri_count = 0;
+       ri->ri_last = (time_t)(-1);
+
+done:;
+       ldap_charray_free( retrylist );
+
+       if ( rc ) {
+               slap_retry_info_destroy( ri );
+       }
+
+       return rc;
+}
+
+int
+slap_retry_info_unparse(
+       slap_retry_info_t       *ri,
+       struct berval           *bvout )
+{
+       int             i;
+       char            buf[ BUFSIZ * 2 ],
+                       *ptr = buf;
+       struct berval   bv = BER_BVNULL;
+
+       assert( ri != NULL );
+       assert( bvout != NULL );
+
+       BER_BVZERO( bvout );
+
+#define WHATSLEFT      ( sizeof( buf ) - ( ptr - buf ) )
+
+       for ( i = 0; ri->ri_num[ i ] != SLAP_RETRYNUM_TAIL; i++ ) {
+               if ( i > 0 ) {
+                       if ( WHATSLEFT <= 1 ) {
+                               return 1;
+                       }
+                       *ptr++ = ';';
+               }
+
+               if ( lutil_unparse_time( ptr, WHATSLEFT, (long)ri->ri_interval[i] ) ) {
+                       return 1;
+               }
+               ptr += strlen( ptr );
+
+               if ( WHATSLEFT <= 1 ) {
+                       return 1;
+               }
+               *ptr++ = ',';
+
+               if ( ri->ri_num[i] == SLAP_RETRYNUM_FOREVER ) {
+                       if ( WHATSLEFT <= 1 ) {
+                               return 1;
+                       }
+                       *ptr++ = '+';
+
+               } else {
+                       ptr += snprintf( ptr, WHATSLEFT, "%d", ri->ri_num[i] );
+                       if ( WHATSLEFT <= 0 ) {
+                               return 1;
+                       }
+               }
+       }
+
+       bv.bv_val = buf;
+       bv.bv_len = ptr - buf;
+
+       ber_dupbv( bvout, &bv );
+
+       return 0;
+}
+
+void
+slap_retry_info_destroy(
+       slap_retry_info_t       *ri )
+{
+       assert( ri != NULL );
+
+       assert( ri->ri_interval != NULL );
+       ch_free( ri->ri_interval );
+       ri->ri_interval = NULL;
+
+       assert( ri->ri_num != NULL );
+       ch_free( ri->ri_num );
+       ri->ri_num = NULL;
+}
+
 static int
 ldap_back_cf_gen( ConfigArgs *c )
 {
        ldapinfo_t      *li = ( ldapinfo_t * )c->be->be_private;
-       int             rc;
+       int             rc = 0;
        int             i;
 
        if ( c->op == SLAP_CONFIG_EMIT ) {
                struct berval   bv = BER_BVNULL;
-               rc = 0;
 
                if ( li == NULL ) {
                        return 1;
@@ -538,7 +728,7 @@ ldap_back_cf_gen( ConfigArgs *c )
                        break;
 
                case LDAP_BACK_CFG_T_F:
-                       enum_to_verb( t_f_mode, (li->li_flags & LDAP_BACK_F_SUPPORT_T_F_MASK), &bv );
+                       enum_to_verb( t_f_mode, (li->li_flags & LDAP_BACK_F_T_F_MASK2), &bv );
                        if ( BER_BVISNULL( &bv ) ) {
                                /* there's something wrong... */
                                assert( 0 );
@@ -629,6 +819,39 @@ ldap_back_cf_gen( ConfigArgs *c )
                        c->value_int = li->li_version;
                        break;
 
+               case LDAP_BACK_CFG_SINGLECONN:
+                       c->value_int = LDAP_BACK_SINGLECONN( li );
+                       break;
+
+               case LDAP_BACK_CFG_CANCEL: {
+                       slap_mask_t     mask = LDAP_BACK_F_CANCEL_MASK2;
+
+                       if ( LDAP_BACK_CANCEL_DISCOVER( li ) ) {
+                               mask &= ~LDAP_BACK_F_CANCEL_EXOP;
+                       }
+                       enum_to_verb( cancel_mode, (li->li_flags & mask), &bv );
+                       if ( BER_BVISNULL( &bv ) ) {
+                               /* there's something wrong... */
+                               assert( 0 );
+                               rc = 1;
+
+                       } else {
+                               value_add_one( &c->rvalue_vals, &bv );
+                       }
+                       } break;
+
+               case LDAP_BACK_CFG_QUARANTINE:
+                       if ( !LDAP_BACK_QUARANTINE( li ) ) {
+                               rc = 1;
+                               break;
+                       }
+
+                       rc = slap_retry_info_unparse( &li->li_quarantine, &bv );
+                       if ( rc == 0 ) {
+                               ber_bvarray_add( &c->rvalue_vals, &bv );
+                       }
+                       break;
+
                default:
                        /* FIXME: we need to handle all... */
                        assert( 0 );
@@ -637,7 +860,6 @@ ldap_back_cf_gen( ConfigArgs *c )
                return rc;
 
        } else if ( c->op == LDAP_MOD_DELETE ) {
-               rc = 0;
                switch( c->type ) {
                case LDAP_BACK_CFG_URI:
                        if ( li->li_uri != NULL ) {
@@ -697,6 +919,7 @@ ldap_back_cf_gen( ConfigArgs *c )
                case LDAP_BACK_CFG_CHASE:
                case LDAP_BACK_CFG_T_F:
                case LDAP_BACK_CFG_WHOAMI:
+               case LDAP_BACK_CFG_CANCEL:
                        rc = 1;
                        break;
 
@@ -722,6 +945,20 @@ ldap_back_cf_gen( ConfigArgs *c )
                        li->li_version = 0;
                        break;
 
+               case LDAP_BACK_CFG_SINGLECONN:
+                       li->li_flags &= ~LDAP_BACK_F_SINGLECONN;
+                       break;
+
+               case LDAP_BACK_CFG_QUARANTINE:
+                       if ( !LDAP_BACK_QUARANTINE( li ) ) {
+                               break;
+                       }
+
+                       slap_retry_info_destroy( &li->li_quarantine );
+                       ldap_pvt_thread_mutex_destroy( &li->li_quarantine_mutex );
+                       li->li_isquarantined = 0;
+                       break;
+
                default:
                        /* FIXME: we need to handle all... */
                        assert( 0 );
@@ -811,7 +1048,7 @@ ldap_back_cf_gen( ConfigArgs *c )
                                                "host and port allowed "
                                                "in \"uri <uri>\" statement "
                                                "for uri #%d of \"%s\"",
-                                               i, c->value_string );
+                                               i, c->argv[ 1 ] );
                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
                        }
                }
@@ -1240,14 +1477,41 @@ done_url:;
                }
                break;
 
-       case LDAP_BACK_CFG_T_F:
+       case LDAP_BACK_CFG_T_F: {
+               slap_mask_t             mask;
+
                i = verb_to_mask( c->argv[1], t_f_mode );
                if ( BER_BVISNULL( &t_f_mode[i].word ) ) {
                        return 1;
                }
-               li->li_flags &= ~LDAP_BACK_F_SUPPORT_T_F_MASK;
-               li->li_flags |= t_f_mode[i].mask;
-               break;
+
+               mask = t_f_mode[i].mask;
+
+               if ( LDAP_BACK_ISOPEN( li )
+                       && mask == LDAP_BACK_F_T_F_DISCOVER
+                       && !LDAP_BACK_T_F( li ) )
+               {
+                       int             rc;
+
+                       if ( li->li_uri == NULL ) {
+                               snprintf( c->msg, sizeof( c->msg ),
+                                       "need URI to discover \"cancel\" support "
+                                       "in \"cancel exop-discover\"" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
+                               return 1;
+                       }
+
+                       rc = slap_discover_feature( li->li_uri, li->li_version,
+                                       slap_schema.si_ad_supportedFeatures->ad_cname.bv_val,
+                                       LDAP_FEATURE_ABSOLUTE_FILTERS );
+                       if ( rc == LDAP_COMPARE_TRUE ) {
+                               mask |= LDAP_BACK_F_T_F;
+                       }
+               }
+
+               li->li_flags &= ~LDAP_BACK_F_T_F_MASK2;
+               li->li_flags |= mask;
+               } break;
 
        case LDAP_BACK_CFG_WHOAMI:
                if ( c->argc == 1 || c->value_int ) {
@@ -1335,6 +1599,71 @@ done_url:;
                li->li_version = c->value_int;
                break;
 
+       case LDAP_BACK_CFG_SINGLECONN:
+               if ( c->value_int ) {
+                       li->li_flags |= LDAP_BACK_F_SINGLECONN;
+
+               } else {
+                       li->li_flags &= ~LDAP_BACK_F_SINGLECONN;
+               }
+               break;
+
+       case LDAP_BACK_CFG_CANCEL: {
+               slap_mask_t             mask;
+
+               i = verb_to_mask( c->argv[1], cancel_mode );
+               if ( BER_BVISNULL( &cancel_mode[i].word ) ) {
+                       return 1;
+               }
+
+               mask = cancel_mode[i].mask;
+
+               if ( LDAP_BACK_ISOPEN( li )
+                       && mask == LDAP_BACK_F_CANCEL_EXOP_DISCOVER
+                       && !LDAP_BACK_CANCEL( li ) )
+               {
+                       int             rc;
+
+                       if ( li->li_uri == NULL ) {
+                               snprintf( c->msg, sizeof( c->msg ),
+                                       "need URI to discover \"cancel\" support "
+                                       "in \"cancel exop-discover\"" );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
+                               return 1;
+                       }
+
+                       rc = slap_discover_feature( li->li_uri, li->li_version,
+                                       slap_schema.si_ad_supportedExtension->ad_cname.bv_val,
+                                       LDAP_EXOP_CANCEL );
+                       if ( rc == LDAP_COMPARE_TRUE ) {
+                               mask |= LDAP_BACK_F_CANCEL_EXOP;
+                       }
+               }
+
+               li->li_flags &= ~LDAP_BACK_F_CANCEL_MASK2;
+               li->li_flags |= mask;
+               } break;
+
+       case LDAP_BACK_CFG_QUARANTINE:
+               if ( LDAP_BACK_QUARANTINE( li ) ) {
+                       snprintf( c->msg, sizeof( c->msg ),
+                               "quarantine already defined" );
+                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
+                       return 1;
+               }
+               rc = slap_retry_info_parse( c->argv[1], &li->li_quarantine,
+                       c->msg, sizeof( c->msg ) );
+               if ( rc ) {
+                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
+
+               } else {
+                       ldap_pvt_thread_mutex_init( &li->li_quarantine_mutex );
+                       /* give it a chance to retry if the pattern gets reset
+                        * via back-config */
+                       li->li_isquarantined = 0;
+               }
+               break;
+
        case LDAP_BACK_CFG_REWRITE:
                snprintf( c->msg, sizeof( c->msg ),
                        "rewrite/remap capabilities have been moved "
@@ -1350,7 +1679,7 @@ done_url:;
                break;
        }
 
-       return 0;
+       return rc;
 }
 
 int