+
+ return rc;
+}
+
+/*
+ * [listener=<listener>] [{read|write}=]<size>
+ */
+
+#ifdef LDAP_TCP_BUFFER
+static BerVarray tcp_buffer;
+int tcp_buffer_num;
+
+#define SLAP_TCP_RMEM (0x1U)
+#define SLAP_TCP_WMEM (0x2U)
+
+static int
+tcp_buffer_parse( struct berval *val, int argc, char **argv,
+ int *size, int *rw, Listener **l )
+{
+ int i, rc = LDAP_SUCCESS;
+ LDAPURLDesc *lud = NULL;
+ char *ptr;
+
+ if ( val != NULL && argv == NULL ) {
+ char *s = val->bv_val;
+
+ argv = ldap_str2charray( s, " \t" );
+ if ( argv == NULL ) {
+ return LDAP_OTHER;
+ }
+ }
+
+ i = 0;
+ if ( strncasecmp( argv[ i ], "listener=", STRLENOF( "listener=" ) )
+ == 0 )
+ {
+ char *url = argv[ i ] + STRLENOF( "listener=" );
+
+ if ( ldap_url_parse( url, &lud ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ *l = config_check_my_url( url, lud );
+ if ( *l == NULL ) {
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ goto done;
+ }
+
+ i++;
+ }
+
+ ptr = argv[ i ];
+ if ( strncasecmp( ptr, "read=", STRLENOF( "read=" ) ) == 0 ) {
+ *rw |= SLAP_TCP_RMEM;
+ ptr += STRLENOF( "read=" );
+
+ } else if ( strncasecmp( ptr, "write=", STRLENOF( "write=" ) ) == 0 ) {
+ *rw |= SLAP_TCP_WMEM;
+ ptr += STRLENOF( "write=" );
+
+ } else {
+ *rw |= ( SLAP_TCP_RMEM | SLAP_TCP_WMEM );
+ }
+
+ /* accept any base */
+ if ( lutil_atoix( size, ptr, 0 ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+done:;
+ if ( val != NULL && argv != NULL ) {
+ ldap_charray_free( argv );
+ }
+
+ if ( lud != NULL ) {
+ ldap_free_urldesc( lud );
+ }
+
+ return rc;
+}
+
+static int
+tcp_buffer_delete_one( struct berval *val )
+{
+ int rc = 0;
+ int size = -1, rw = 0;
+ Listener *l = NULL;
+
+ rc = tcp_buffer_parse( val, 0, NULL, &size, &rw, &l );
+ if ( rc != 0 ) {
+ return rc;
+ }
+
+ if ( l != NULL ) {
+ int i;
+ Listener **ll = slapd_get_listeners();
+
+ for ( i = 0; ll[ i ] != NULL; i++ ) {
+ if ( ll[ i ] == l ) break;
+ }
+
+ if ( ll[ i ] == NULL ) {
+ return LDAP_NO_SUCH_ATTRIBUTE;
+ }
+
+ if ( rw & SLAP_TCP_RMEM ) l->sl_tcp_rmem = -1;
+ if ( rw & SLAP_TCP_WMEM ) l->sl_tcp_wmem = -1;
+
+ for ( i++ ; ll[ i ] != NULL && bvmatch( &l->sl_url, &ll[ i ]->sl_url ); i++ ) {
+ if ( rw & SLAP_TCP_RMEM ) ll[ i ]->sl_tcp_rmem = -1;
+ if ( rw & SLAP_TCP_WMEM ) ll[ i ]->sl_tcp_wmem = -1;
+ }
+
+ } else {
+ /* NOTE: this affects listeners without a specific setting,
+ * does not reset all listeners. If a listener without
+ * specific settings was assigned a buffer because of
+ * a global setting, it will not be reset. In any case,
+ * buffer changes will only take place at restart. */
+ if ( rw & SLAP_TCP_RMEM ) slapd_tcp_rmem = -1;
+ if ( rw & SLAP_TCP_WMEM ) slapd_tcp_wmem = -1;
+ }
+
+ return rc;
+}
+
+static int
+tcp_buffer_delete( BerVarray vals )
+{
+ int i;
+
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
+ tcp_buffer_delete_one( &vals[ i ] );
+ }
+
+ return 0;
+}
+
+static int
+tcp_buffer_unparse( int size, int rw, Listener *l, struct berval *val )
+{
+ char buf[sizeof("2147483648")], *ptr;
+
+ /* unparse for later use */
+ val->bv_len = snprintf( buf, sizeof( buf ), "%d", size );
+ if ( l != NULL ) {
+ val->bv_len += STRLENOF( "listener=" " " ) + l->sl_url.bv_len;
+ }
+
+ if ( rw != ( SLAP_TCP_RMEM | SLAP_TCP_WMEM ) ) {
+ if ( rw & SLAP_TCP_RMEM ) {
+ val->bv_len += STRLENOF( "read=" );
+ } else if ( rw & SLAP_TCP_WMEM ) {
+ val->bv_len += STRLENOF( "write=" );
+ }
+ }
+
+ val->bv_val = SLAP_MALLOC( val->bv_len + 1 );
+
+ ptr = val->bv_val;
+
+ if ( l != NULL ) {
+ ptr = lutil_strcopy( ptr, "listener=" );
+ ptr = lutil_strncopy( ptr, l->sl_url.bv_val, l->sl_url.bv_len );
+ *ptr++ = ' ';
+ }
+
+ if ( rw != ( SLAP_TCP_RMEM | SLAP_TCP_WMEM ) ) {
+ if ( rw & SLAP_TCP_RMEM ) {
+ ptr = lutil_strcopy( ptr, "read=" );
+ } else if ( rw & SLAP_TCP_WMEM ) {
+ ptr = lutil_strcopy( ptr, "write=" );
+ }
+ }
+
+ ptr = lutil_strcopy( ptr, buf );
+ *ptr = '\0';
+
+ assert( val->bv_val + val->bv_len == ptr );
+
+ return LDAP_SUCCESS;
+}
+
+static int
+tcp_buffer_add_one( int argc, char **argv )
+{
+ int rc = 0;
+ int size = -1, rw = 0;
+ Listener *l = NULL;
+
+ struct berval val;
+
+ /* parse */
+ rc = tcp_buffer_parse( NULL, argc, argv, &size, &rw, &l );
+ if ( rc != 0 ) {
+ return rc;
+ }
+
+ /* unparse for later use */
+ rc = tcp_buffer_unparse( size, rw, l, &val );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ /* use parsed values */
+ if ( l != NULL ) {
+ int i;
+ Listener **ll = slapd_get_listeners();
+
+ for ( i = 0; ll[ i ] != NULL; i++ ) {
+ if ( ll[ i ] == l ) break;
+ }
+
+ if ( ll[ i ] == NULL ) {
+ return LDAP_NO_SUCH_ATTRIBUTE;
+ }
+
+ /* buffer only applies to TCP listeners;
+ * we do not do any check here, and delegate them
+ * to setsockopt(2) */
+ if ( rw & SLAP_TCP_RMEM ) l->sl_tcp_rmem = size;
+ if ( rw & SLAP_TCP_WMEM ) l->sl_tcp_wmem = size;
+
+ for ( i++ ; ll[ i ] != NULL && bvmatch( &l->sl_url, &ll[ i ]->sl_url ); i++ ) {
+ if ( rw & SLAP_TCP_RMEM ) ll[ i ]->sl_tcp_rmem = size;
+ if ( rw & SLAP_TCP_WMEM ) ll[ i ]->sl_tcp_wmem = size;
+ }
+
+ } else {
+ /* NOTE: this affects listeners without a specific setting,
+ * does not set all listeners */
+ if ( rw & SLAP_TCP_RMEM ) slapd_tcp_rmem = size;
+ if ( rw & SLAP_TCP_WMEM ) slapd_tcp_wmem = size;
+ }
+
+ tcp_buffer = SLAP_REALLOC( tcp_buffer, sizeof( struct berval ) * ( tcp_buffer_num + 2 ) );
+ /* append */
+ tcp_buffer[ tcp_buffer_num ] = val;
+
+ tcp_buffer_num++;
+ BER_BVZERO( &tcp_buffer[ tcp_buffer_num ] );
+