mirror of
https://git.openldap.org/openldap/openldap.git
synced 2025-01-06 10:46:21 +08:00
fix retry; add per-target configurable number of retries; addresses ITS#3672, ITS#3676 & ITS#3680
This commit is contained in:
parent
0ec5e2df77
commit
fa49a73fc4
@ -93,6 +93,14 @@ This caches the target that holds a given DN to speed up target
|
|||||||
selection in case multiple targets would result from an uncached
|
selection in case multiple targets would result from an uncached
|
||||||
search; forever means cache never expires; disabled means no DN
|
search; forever means cache never expires; disabled means no DN
|
||||||
caching; otherwise a valid ( > 0 ) ttl in seconds is required.
|
caching; otherwise a valid ( > 0 ) ttl in seconds is required.
|
||||||
|
.TP
|
||||||
|
.B nretries {forever|never|<nretries>}
|
||||||
|
This directive defines how many times a bind should be retried
|
||||||
|
in case of temporary failure in contacting a target. If defined
|
||||||
|
before any target specification, it applies to all targets (by default,
|
||||||
|
.BR never );
|
||||||
|
the global value can be overridden by redefinitions inside each target
|
||||||
|
specification.
|
||||||
.SH TARGET SPECIFICATION
|
.SH TARGET SPECIFICATION
|
||||||
Target specification starts with a "uri" directive:
|
Target specification starts with a "uri" directive:
|
||||||
.TP
|
.TP
|
||||||
|
@ -82,7 +82,7 @@ typedef struct dncookie {
|
|||||||
#endif
|
#endif
|
||||||
} dncookie;
|
} dncookie;
|
||||||
|
|
||||||
#define META_BIND_NRETRIES 3
|
/* TODO: allow to define it on a per-target basis */
|
||||||
#define META_BIND_TIMEOUT 10000
|
#define META_BIND_TIMEOUT 10000
|
||||||
|
|
||||||
int ldap_back_dn_massage(dncookie *dc, struct berval *dn,
|
int ldap_back_dn_massage(dncookie *dc, struct berval *dn,
|
||||||
@ -198,6 +198,11 @@ typedef struct metatarget_t {
|
|||||||
struct berval mt_pseudorootdn;
|
struct berval mt_pseudorootdn;
|
||||||
struct berval mt_pseudorootpw;
|
struct berval mt_pseudorootpw;
|
||||||
|
|
||||||
|
int mt_nretries;
|
||||||
|
#define META_RETRY_UNDEFINED (-2)
|
||||||
|
#define META_RETRY_FOREVER (-1)
|
||||||
|
#define META_RETRY_NEVER (0)
|
||||||
|
|
||||||
struct ldaprwmap mt_rwmap;
|
struct ldaprwmap mt_rwmap;
|
||||||
} metatarget_t;
|
} metatarget_t;
|
||||||
|
|
||||||
@ -215,6 +220,8 @@ typedef struct metainfo_t {
|
|||||||
int mi_defaulttarget;
|
int mi_defaulttarget;
|
||||||
int mi_network_timeout;
|
int mi_network_timeout;
|
||||||
#define META_DEFAULT_TARGET_NONE (-1)
|
#define META_DEFAULT_TARGET_NONE (-1)
|
||||||
|
int mi_nretries;
|
||||||
|
|
||||||
metatarget_t **mi_targets;
|
metatarget_t **mi_targets;
|
||||||
SlapReply *mi_candidates;
|
SlapReply *mi_candidates;
|
||||||
|
|
||||||
|
@ -207,21 +207,26 @@ rebind:;
|
|||||||
op->o_ctrls, NULL, &msgid );
|
op->o_ctrls, NULL, &msgid );
|
||||||
if ( rs->sr_err == LDAP_SUCCESS ) {
|
if ( rs->sr_err == LDAP_SUCCESS ) {
|
||||||
LDAPMessage *res;
|
LDAPMessage *res;
|
||||||
struct timeval tv = { 0, 0 };
|
struct timeval tv;
|
||||||
int rc;
|
int rc;
|
||||||
int nretries = META_BIND_NRETRIES;
|
int nretries = mt->mt_nretries;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* handle response!!!
|
* handle response!!!
|
||||||
*/
|
*/
|
||||||
retry:;
|
retry:;
|
||||||
|
tv.tv_sec = 0;
|
||||||
|
tv.tv_usec = META_BIND_TIMEOUT;
|
||||||
switch ( ldap_result( msc->msc_ld, msgid, 0, &tv, &res ) ) {
|
switch ( ldap_result( msc->msc_ld, msgid, 0, &tv, &res ) ) {
|
||||||
case 0:
|
case 0:
|
||||||
if ( nretries > 0 ) {
|
Debug( LDAP_DEBUG_ANY, "%s meta_back_single_bind: ldap_result=%d nretries=%d\n",
|
||||||
|
op->o_log_prefix, 0, nretries );
|
||||||
|
|
||||||
|
if ( nretries != META_RETRY_NEVER ) {
|
||||||
ldap_pvt_thread_yield();
|
ldap_pvt_thread_yield();
|
||||||
tv.tv_sec = 0;
|
if ( nretries > 0 ) {
|
||||||
tv.tv_usec = META_BIND_TIMEOUT;
|
nretries--;
|
||||||
nretries--;
|
}
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
rs->sr_err = LDAP_BUSY;
|
rs->sr_err = LDAP_BUSY;
|
||||||
@ -231,10 +236,11 @@ retry:;
|
|||||||
ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER,
|
ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER,
|
||||||
&rs->sr_err );
|
&rs->sr_err );
|
||||||
|
|
||||||
Debug( LDAP_DEBUG_ANY, "### %s meta_back_single_bind(%s) err=%d\n",
|
Debug( LDAP_DEBUG_ANY, "### %s meta_back_single_bind: err=%d nretries=%d\n",
|
||||||
op->o_log_prefix, mdn.bv_val, rs->sr_err );
|
op->o_log_prefix, rs->sr_err, nretries );
|
||||||
|
|
||||||
if ( rs->sr_err == LDAP_UNAVAILABLE && nretries > 0 ) {
|
rc = slap_map_api2result( rs );
|
||||||
|
if ( rs->sr_err == LDAP_UNAVAILABLE && nretries != META_RETRY_NEVER ) {
|
||||||
ldap_unbind_ext_s( msc->msc_ld, NULL, NULL );
|
ldap_unbind_ext_s( msc->msc_ld, NULL, NULL );
|
||||||
msc->msc_ld = NULL;
|
msc->msc_ld = NULL;
|
||||||
msc->msc_bound = 0;
|
msc->msc_bound = 0;
|
||||||
@ -243,7 +249,9 @@ retry:;
|
|||||||
rc = meta_back_init_one_conn( op, rs, mt, msc,
|
rc = meta_back_init_one_conn( op, rs, mt, msc,
|
||||||
LDAP_BACK_DONTSEND );
|
LDAP_BACK_DONTSEND );
|
||||||
if ( rc ) {
|
if ( rc ) {
|
||||||
nretries--;
|
if ( nretries > 0 ) {
|
||||||
|
nretries--;
|
||||||
|
}
|
||||||
ldap_pvt_thread_yield();
|
ldap_pvt_thread_yield();
|
||||||
goto rebind;
|
goto rebind;
|
||||||
}
|
}
|
||||||
@ -299,7 +307,7 @@ meta_back_single_dobind(
|
|||||||
metaconn_t *mc,
|
metaconn_t *mc,
|
||||||
int candidate,
|
int candidate,
|
||||||
ldap_back_send_t sendok,
|
ldap_back_send_t sendok,
|
||||||
int retries )
|
int nretries )
|
||||||
{
|
{
|
||||||
metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
|
metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
|
||||||
metatarget_t *mt = mi->mi_targets[ candidate ];
|
metatarget_t *mt = mi->mi_targets[ candidate ];
|
||||||
@ -332,7 +340,7 @@ rebind:;
|
|||||||
NULL, NULL, &msgid );
|
NULL, NULL, &msgid );
|
||||||
if ( rc == LDAP_SUCCESS ) {
|
if ( rc == LDAP_SUCCESS ) {
|
||||||
LDAPMessage *res;
|
LDAPMessage *res;
|
||||||
struct timeval tv = { 0, 0 };
|
struct timeval tv;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* handle response!!!
|
* handle response!!!
|
||||||
@ -342,9 +350,14 @@ retry:;
|
|||||||
tv.tv_usec = META_BIND_TIMEOUT;
|
tv.tv_usec = META_BIND_TIMEOUT;
|
||||||
switch ( ldap_result( msc->msc_ld, msgid, 0, &tv, &res ) ) {
|
switch ( ldap_result( msc->msc_ld, msgid, 0, &tv, &res ) ) {
|
||||||
case 0:
|
case 0:
|
||||||
if ( retries > 0 ) {
|
Debug( LDAP_DEBUG_ANY, "%s meta_back_single_dobind: ldap_result=%d nretries=%d\n",
|
||||||
|
op->o_log_prefix, 0, nretries );
|
||||||
|
|
||||||
|
if ( nretries != META_RETRY_NEVER ) {
|
||||||
ldap_pvt_thread_yield();
|
ldap_pvt_thread_yield();
|
||||||
retries--;
|
if ( nretries > 0 ) {
|
||||||
|
nretries--;
|
||||||
|
}
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,11 +368,11 @@ retry:;
|
|||||||
ldap_get_option( msc->msc_ld,
|
ldap_get_option( msc->msc_ld,
|
||||||
LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
|
LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
|
||||||
|
|
||||||
Debug( LDAP_DEBUG_ANY, "### %s meta_back_single_dobind(\"\") err=%d\n",
|
Debug( LDAP_DEBUG_ANY, "### %s meta_back_single_dobind: err=%d nretries=%d\n",
|
||||||
op->o_log_prefix, rs->sr_err, 0 );
|
op->o_log_prefix, rs->sr_err, nretries );
|
||||||
|
|
||||||
rc = slap_map_api2result( rs );
|
rc = slap_map_api2result( rs );
|
||||||
if ( rc == LDAP_UNAVAILABLE && retries > 0 ) {
|
if ( rc == LDAP_UNAVAILABLE && nretries != META_RETRY_NEVER ) {
|
||||||
ldap_unbind_ext_s( msc->msc_ld, NULL, NULL );
|
ldap_unbind_ext_s( msc->msc_ld, NULL, NULL );
|
||||||
msc->msc_ld = NULL;
|
msc->msc_ld = NULL;
|
||||||
msc->msc_bound = 0;
|
msc->msc_bound = 0;
|
||||||
@ -367,9 +380,11 @@ retry:;
|
|||||||
/* mc here must be the regular mc, reset and ready for init */
|
/* mc here must be the regular mc, reset and ready for init */
|
||||||
rc = meta_back_init_one_conn( op, rs, mt, msc, LDAP_BACK_DONTSEND );
|
rc = meta_back_init_one_conn( op, rs, mt, msc, LDAP_BACK_DONTSEND );
|
||||||
|
|
||||||
if ( rc ) {
|
if ( rc == LDAP_SUCCESS ) {
|
||||||
ldap_pvt_thread_yield();
|
ldap_pvt_thread_yield();
|
||||||
retries--;
|
if ( nretries > 0 ) {
|
||||||
|
nretries--;
|
||||||
|
}
|
||||||
goto rebind;
|
goto rebind;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -385,8 +400,8 @@ retry:;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rs->sr_err = rc;
|
||||||
if ( rc != LDAP_SUCCESS && ( sendok & LDAP_BACK_SENDERR ) ) {
|
if ( rc != LDAP_SUCCESS && ( sendok & LDAP_BACK_SENDERR ) ) {
|
||||||
rs->sr_err = rc;
|
|
||||||
send_ldap_result( op, rs );
|
send_ldap_result( op, rs );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -403,6 +418,8 @@ meta_back_dobind(
|
|||||||
metaconn_t *mc,
|
metaconn_t *mc,
|
||||||
ldap_back_send_t sendok )
|
ldap_back_send_t sendok )
|
||||||
{
|
{
|
||||||
|
metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
|
||||||
|
|
||||||
metasingleconn_t *msc;
|
metasingleconn_t *msc;
|
||||||
int bound = 0, i;
|
int bound = 0, i;
|
||||||
|
|
||||||
@ -419,6 +436,7 @@ meta_back_dobind(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for ( i = 0, msc = &mc->mc_conns[ 0 ]; !META_LAST( msc ); ++i, ++msc ) {
|
for ( i = 0, msc = &mc->mc_conns[ 0 ]; !META_LAST( msc ); ++i, ++msc ) {
|
||||||
|
metatarget_t *mt = mi->mi_targets[ i ];
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -437,7 +455,7 @@ meta_back_dobind(
|
|||||||
}
|
}
|
||||||
|
|
||||||
rc = meta_back_single_dobind( op, rs, mc, i,
|
rc = meta_back_single_dobind( op, rs, mc, i,
|
||||||
LDAP_BACK_DONTSEND, META_BIND_NRETRIES );
|
LDAP_BACK_DONTSEND, mt->mt_nretries );
|
||||||
if ( rc != LDAP_SUCCESS ) {
|
if ( rc != LDAP_SUCCESS ) {
|
||||||
Debug( LDAP_DEBUG_ANY, "%s meta_back_dobind[%d]: "
|
Debug( LDAP_DEBUG_ANY, "%s meta_back_dobind[%d]: "
|
||||||
"(anonymous) err=%d\n",
|
"(anonymous) err=%d\n",
|
||||||
|
@ -75,6 +75,8 @@ new_target( void )
|
|||||||
|
|
||||||
ldap_back_map_init( &mt->mt_rwmap.rwm_at, &mapping );
|
ldap_back_map_init( &mt->mt_rwmap.rwm_at, &mapping );
|
||||||
|
|
||||||
|
mt->mt_nretries = META_RETRY_UNDEFINED;
|
||||||
|
|
||||||
return mt;
|
return mt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,6 +136,8 @@ meta_back_db_config(
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mi->mi_targets[ i ]->mt_nretries = mi->mi_nretries;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* uri MUST be legal!
|
* uri MUST be legal!
|
||||||
*/
|
*/
|
||||||
@ -618,6 +622,43 @@ meta_back_db_config(
|
|||||||
return ldap_back_map_config( &mi->mi_targets[ i ]->mt_rwmap.rwm_oc,
|
return ldap_back_map_config( &mi->mi_targets[ i ]->mt_rwmap.rwm_oc,
|
||||||
&mi->mi_targets[ i ]->mt_rwmap.rwm_at,
|
&mi->mi_targets[ i ]->mt_rwmap.rwm_at,
|
||||||
fname, lineno, argc, argv );
|
fname, lineno, argc, argv );
|
||||||
|
|
||||||
|
} else if ( strcasecmp( argv[ 0 ], "nretries" ) == 0 ) {
|
||||||
|
int i = mi->mi_ntargets - 1;
|
||||||
|
int nretries = META_RETRY_UNDEFINED;
|
||||||
|
|
||||||
|
if ( argc != 2 ) {
|
||||||
|
fprintf( stderr,
|
||||||
|
"%s: line %d: need value in \"nretries <value>\"\n",
|
||||||
|
fname, lineno );
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( strcasecmp( argv[ 1 ], "forever" ) == 0 ) {
|
||||||
|
nretries = META_RETRY_FOREVER;
|
||||||
|
|
||||||
|
} else if ( strcasecmp( argv[ 1 ], "never" ) == 0 ) {
|
||||||
|
nretries = META_RETRY_NEVER;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
char *next;
|
||||||
|
|
||||||
|
nretries = strtol( argv[ 1 ], &next, 10 );
|
||||||
|
if ( next == argv[ 1 ] || next[ 0 ] != '\0' ) {
|
||||||
|
fprintf( stderr,
|
||||||
|
"%s: line %d: unable to parse value \"%s\" in \"nretries <value>\"\n",
|
||||||
|
fname, lineno, argv[ 1 ] );
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( i < 0 ) {
|
||||||
|
mi->mi_nretries = nretries;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
mi->mi_targets[ i ]->mt_nretries = nretries;
|
||||||
|
}
|
||||||
|
|
||||||
/* anything else */
|
/* anything else */
|
||||||
} else {
|
} else {
|
||||||
return SLAP_CONF_UNKNOWN;
|
return SLAP_CONF_UNKNOWN;
|
||||||
|
@ -230,7 +230,7 @@ meta_back_init_one_conn(
|
|||||||
rs->sr_err = ldap_start_tls( msc->msc_ld, NULL, NULL, &msgid );
|
rs->sr_err = ldap_start_tls( msc->msc_ld, NULL, NULL, &msgid );
|
||||||
if ( rs->sr_err == LDAP_SUCCESS ) {
|
if ( rs->sr_err == LDAP_SUCCESS ) {
|
||||||
LDAPMessage *res = NULL;
|
LDAPMessage *res = NULL;
|
||||||
int rc, retries = 1;
|
int rc, nretries = mt->mt_nretries;
|
||||||
struct timeval tv = { 0, 0 };
|
struct timeval tv = { 0, 0 };
|
||||||
|
|
||||||
retry:;
|
retry:;
|
||||||
@ -239,8 +239,10 @@ retry:;
|
|||||||
rs->sr_err = LDAP_OTHER;
|
rs->sr_err = LDAP_OTHER;
|
||||||
|
|
||||||
} else if ( rc == 0 ) {
|
} else if ( rc == 0 ) {
|
||||||
if ( retries ) {
|
if ( nretries != 0 ) {
|
||||||
retries--;
|
if ( nretries > 0 ) {
|
||||||
|
nretries--;
|
||||||
|
}
|
||||||
tv.tv_sec = 0;
|
tv.tv_sec = 0;
|
||||||
tv.tv_usec = 100000;
|
tv.tv_usec = 100000;
|
||||||
goto retry;
|
goto retry;
|
||||||
@ -393,8 +395,8 @@ meta_back_retry(
|
|||||||
rc = meta_back_init_one_conn( op, rs, mt, msc, sendok );
|
rc = meta_back_init_one_conn( op, rs, mt, msc, sendok );
|
||||||
|
|
||||||
if ( rc == LDAP_SUCCESS ) {
|
if ( rc == LDAP_SUCCESS ) {
|
||||||
rc = meta_back_single_dobind( op, rs, mc, candidate, sendok,
|
rc = meta_back_single_dobind( op, rs, mc, candidate,
|
||||||
META_BIND_NRETRIES );
|
sendok, mt->mt_nretries );
|
||||||
}
|
}
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_unlock( &mc->mc_mutex );
|
ldap_pvt_thread_mutex_unlock( &mc->mc_mutex );
|
||||||
@ -787,6 +789,8 @@ meta_back_getconn(
|
|||||||
*/
|
*/
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
int ncandidates = 0;
|
||||||
|
|
||||||
/* Looks like we didn't get a bind. Open a new session... */
|
/* Looks like we didn't get a bind. Open a new session... */
|
||||||
if ( !mc ) {
|
if ( !mc ) {
|
||||||
mc = metaconn_alloc( mi->mi_ntargets );
|
mc = metaconn_alloc( mi->mi_ntargets );
|
||||||
@ -809,6 +813,7 @@ meta_back_getconn(
|
|||||||
&mc->mc_conns[ i ], sendok );
|
&mc->mc_conns[ i ], sendok );
|
||||||
if ( lerr == LDAP_SUCCESS ) {
|
if ( lerr == LDAP_SUCCESS ) {
|
||||||
candidates[ i ].sr_tag = META_CANDIDATE;
|
candidates[ i ].sr_tag = META_CANDIDATE;
|
||||||
|
ncandidates++;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@ -817,6 +822,9 @@ meta_back_getconn(
|
|||||||
* be init'd, should the other ones
|
* be init'd, should the other ones
|
||||||
* be tried?
|
* be tried?
|
||||||
*/
|
*/
|
||||||
|
if ( new_conn ) {
|
||||||
|
( void )meta_clear_one_candidate( &mc->mc_conns[ i ] );
|
||||||
|
}
|
||||||
candidates[ i ].sr_tag = META_NOT_CANDIDATE;
|
candidates[ i ].sr_tag = META_NOT_CANDIDATE;
|
||||||
err = lerr;
|
err = lerr;
|
||||||
|
|
||||||
@ -827,9 +835,28 @@ meta_back_getconn(
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
if ( new_conn ) {
|
||||||
|
( void )meta_clear_one_candidate( &mc->mc_conns[ i ] );
|
||||||
|
}
|
||||||
candidates[ i ].sr_tag = META_NOT_CANDIDATE;
|
candidates[ i ].sr_tag = META_NOT_CANDIDATE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( ncandidates == 0 ) {
|
||||||
|
if ( new_conn ) {
|
||||||
|
meta_back_conn_free( mc );
|
||||||
|
}
|
||||||
|
|
||||||
|
rs->sr_err = LDAP_NO_SUCH_OBJECT;
|
||||||
|
rs->sr_text = "Unable to select valid candidates";
|
||||||
|
|
||||||
|
if ( sendok & LDAP_BACK_SENDERR ) {
|
||||||
|
send_ldap_result( op, rs );
|
||||||
|
rs->sr_text = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
done:;
|
done:;
|
||||||
|
@ -43,6 +43,7 @@ database meta
|
|||||||
suffix "o=Example,c=US"
|
suffix "o=Example,c=US"
|
||||||
rootdn "cn=Manager,o=Example,c=US"
|
rootdn "cn=Manager,o=Example,c=US"
|
||||||
rootpw secret
|
rootpw secret
|
||||||
|
nretries forever
|
||||||
|
|
||||||
# local
|
# local
|
||||||
uri "@URI2@ou=Meta,o=Example,c=US"
|
uri "@URI2@ou=Meta,o=Example,c=US"
|
||||||
|
@ -198,7 +198,7 @@ echo "Filtering original ldif used to create database..."
|
|||||||
. $LDIFFILTER < $METACONCURRENCYOUT > $LDIFFLT
|
. $LDIFFILTER < $METACONCURRENCYOUT > $LDIFFLT
|
||||||
echo "Comparing filter output..."
|
echo "Comparing filter output..."
|
||||||
$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
|
$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
|
||||||
|
|
||||||
if test $? != 0 ; then
|
if test $? != 0 ; then
|
||||||
echo "comparison failed - meta search/modification didn't succeed"
|
echo "comparison failed - meta search/modification didn't succeed"
|
||||||
exit 1
|
exit 1
|
||||||
|
Loading…
Reference in New Issue
Block a user