mirror of
https://git.proxmox.com/git/mirror_corosync
synced 2025-08-13 18:56:25 +00:00
This patch adds key_increment and key_decrement calls to the objdb and
confdb subsystems. This is useful to provide atomic counters (ag handle numbers) for long-running (though not persistent) connections. It's not currently possible via confdb to atomically get a new number from objdb due to the lack of locking. Doing it via increment operations in the IPC thread provides enough atomicity to make it useful. Fabio has already identified a use for these calls. It could also provide some form of basic co-operative locking mechanism for IPC-using processes (not direct objdb calls). git-svn-id: http://svn.fedorahosted.org/svn/corosync/trunk@1662 fd59a12c-fef9-0310-b244-a6a79926bd2f
This commit is contained in:
parent
c60fc43bcb
commit
1d2d771f99
10
Makefile
10
Makefile
@ -98,11 +98,11 @@ help:
|
||||
@echo " doxygen - doxygen html docs"
|
||||
@echo
|
||||
@echo "Options: (* - default)"
|
||||
@echo " OPENCOROSYNC [DEBUG/RELEASE*] - Enable/Disable debug symbols"
|
||||
@echo " DESTDIR [directory] - Install prefix."
|
||||
@echo " O [directory] - Locate all output files in \"dir\"."
|
||||
@echo " BUILD_DYNAMIC [1*/0] - Enable/disable dynamic loading of service handler modules"
|
||||
@echo " OPENCOROSYNC_PROFILE [1/0*] - Enable profiling"
|
||||
@echo " COROSYNC_BUILD [DEBUG/RELEASE*] - Enable/Disable debug symbols"
|
||||
@echo " DESTDIR [directory] - Install prefix."
|
||||
@echo " O [directory] - Locate all output files in \"dir\"."
|
||||
@echo " BUILD_DYNAMIC [1*/0] - Enable/disable dynamic loading of service handler modules"
|
||||
@echo " COROSYNC_PROFILE [1/0*] - Enable profiling"
|
||||
@echo
|
||||
|
||||
|
||||
|
@ -66,7 +66,7 @@ endif
|
||||
LDFLAGS +=
|
||||
endif
|
||||
ifeq (${COROSYNC_BUILD}, DEBUG)
|
||||
CFLAGS += -O0 -g -Wall -DDEBUG
|
||||
CFLAGS += -O0 -g -Wall -DDEBUG --time
|
||||
LDFLAGS += -g
|
||||
ifeq (${COROSYNC_COMPAT}, SOLARIS)
|
||||
CFLAGS += -Werror -DTS_CLASS
|
||||
|
@ -124,6 +124,8 @@ void apidef_init (struct objdb_iface_ver0 *objdb) {
|
||||
apidef_corosync_api_v1.object_track_stop = objdb->object_track_stop;
|
||||
apidef_corosync_api_v1.object_write_config = objdb->object_write_config;
|
||||
apidef_corosync_api_v1.object_reload_config = objdb->object_reload_config;
|
||||
apidef_corosync_api_v1.object_key_increment = objdb->object_key_increment;
|
||||
apidef_corosync_api_v1.object_key_decrement = objdb->object_key_decrement;
|
||||
}
|
||||
|
||||
struct corosync_api_v1 *apidef_get (void)
|
||||
|
88
exec/objdb.c
88
exec/objdb.c
@ -772,6 +772,92 @@ error_exit:
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static int object_key_increment (
|
||||
unsigned int object_handle,
|
||||
void *key_name,
|
||||
int key_len,
|
||||
unsigned int *value)
|
||||
{
|
||||
unsigned int res = 0;
|
||||
struct object_instance *instance;
|
||||
struct object_key *object_key = NULL;
|
||||
struct list_head *list;
|
||||
int found = 0;
|
||||
|
||||
res = hdb_handle_get (&object_instance_database,
|
||||
object_handle, (void *)&instance);
|
||||
if (res != 0) {
|
||||
goto error_exit;
|
||||
}
|
||||
for (list = instance->key_head.next;
|
||||
list != &instance->key_head; list = list->next) {
|
||||
|
||||
object_key = list_entry (list, struct object_key, list);
|
||||
|
||||
if ((object_key->key_len == key_len) &&
|
||||
(memcmp (object_key->key_name, key_name, key_len) == 0)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found && object_key->value_len == sizeof(int)) {
|
||||
(*(int *)object_key->value)++;
|
||||
*value = *(int *)object_key->value;
|
||||
}
|
||||
else {
|
||||
res = -1;
|
||||
}
|
||||
|
||||
hdb_handle_put (&object_instance_database, object_handle);
|
||||
return (res);
|
||||
|
||||
error_exit:
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static int object_key_decrement (
|
||||
unsigned int object_handle,
|
||||
void *key_name,
|
||||
int key_len,
|
||||
unsigned int *value)
|
||||
{
|
||||
unsigned int res = 0;
|
||||
struct object_instance *instance;
|
||||
struct object_key *object_key = NULL;
|
||||
struct list_head *list;
|
||||
int found = 0;
|
||||
|
||||
res = hdb_handle_get (&object_instance_database,
|
||||
object_handle, (void *)&instance);
|
||||
if (res != 0) {
|
||||
goto error_exit;
|
||||
}
|
||||
for (list = instance->key_head.next;
|
||||
list != &instance->key_head; list = list->next) {
|
||||
|
||||
object_key = list_entry (list, struct object_key, list);
|
||||
|
||||
if ((object_key->key_len == key_len) &&
|
||||
(memcmp (object_key->key_name, key_name, key_len) == 0)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found && object_key->value_len == sizeof(int)) {
|
||||
(*(int *)object_key->value)--;
|
||||
*value = *(int *)object_key->value;
|
||||
}
|
||||
else {
|
||||
res = -1;
|
||||
}
|
||||
|
||||
hdb_handle_put (&object_instance_database, object_handle);
|
||||
return (res);
|
||||
|
||||
error_exit:
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static int object_key_delete (
|
||||
unsigned int object_handle,
|
||||
void *key_name,
|
||||
@ -1306,6 +1392,8 @@ struct objdb_iface_ver0 objdb_iface = {
|
||||
.object_dump = object_dump,
|
||||
.object_write_config = object_write_config,
|
||||
.object_reload_config = object_reload_config,
|
||||
.object_key_increment = object_key_increment,
|
||||
.object_key_decrement = object_key_decrement,
|
||||
};
|
||||
|
||||
struct lcr_iface objdb_iface_ver0[1] = {
|
||||
|
@ -223,6 +223,20 @@ confdb_error_t confdb_key_replace (
|
||||
void *new_value,
|
||||
int new_value_len);
|
||||
|
||||
confdb_error_t confdb_key_increment (
|
||||
confdb_handle_t handle,
|
||||
unsigned int parent_object_handle,
|
||||
void *key_name,
|
||||
int key_name_len,
|
||||
unsigned int *value);
|
||||
|
||||
confdb_error_t confdb_key_decrement (
|
||||
confdb_handle_t handle,
|
||||
unsigned int parent_object_handle,
|
||||
void *key_name,
|
||||
int key_name_len,
|
||||
unsigned int *value);
|
||||
|
||||
/*
|
||||
* Object queries
|
||||
* "find" loops through all objects of a given name and is also
|
||||
|
@ -281,6 +281,18 @@ struct corosync_api_v1 {
|
||||
int (*object_reload_config) (int flush,
|
||||
char **error_string);
|
||||
|
||||
int (*object_key_increment) (
|
||||
unsigned int object_handle,
|
||||
void *key_name,
|
||||
int key_len,
|
||||
unsigned int *value);
|
||||
|
||||
int (*object_key_decrement) (
|
||||
unsigned int object_handle,
|
||||
void *key_name,
|
||||
int key_len,
|
||||
unsigned int *value);
|
||||
|
||||
/*
|
||||
* Time and timer APIs
|
||||
*/
|
||||
|
@ -211,6 +211,18 @@ struct objdb_iface_ver0 {
|
||||
int (*object_reload_config) (
|
||||
int flush,
|
||||
char **error_string);
|
||||
|
||||
int (*object_key_increment) (
|
||||
unsigned int object_handle,
|
||||
void *key_name,
|
||||
int key_len,
|
||||
unsigned int *value);
|
||||
|
||||
int (*object_key_decrement) (
|
||||
unsigned int object_handle,
|
||||
void *key_name,
|
||||
int key_len,
|
||||
unsigned int *value);
|
||||
};
|
||||
|
||||
#endif /* OBJDB_H_DEFINED */
|
||||
|
@ -53,7 +53,9 @@ enum req_confdb_types {
|
||||
MESSAGE_REQ_CONFDB_TRACK_STOP = 11,
|
||||
MESSAGE_REQ_CONFDB_WRITE = 12,
|
||||
MESSAGE_REQ_CONFDB_RELOAD = 13,
|
||||
MESSAGE_REQ_CONFDB_OBJECT_FIND_DESTROY = 14
|
||||
MESSAGE_REQ_CONFDB_OBJECT_FIND_DESTROY = 14,
|
||||
MESSAGE_REQ_CONFDB_KEY_INCREMENT = 15,
|
||||
MESSAGE_REQ_CONFDB_KEY_DECREMENT = 16
|
||||
};
|
||||
|
||||
enum res_confdb_types {
|
||||
@ -74,7 +76,9 @@ enum res_confdb_types {
|
||||
MESSAGE_RES_CONFDB_OBJECT_DESTROY_CALLBACK = 14,
|
||||
MESSAGE_RES_CONFDB_WRITE = 15,
|
||||
MESSAGE_RES_CONFDB_RELOAD = 16,
|
||||
MESSAGE_RES_CONFDB_OBJECT_FIND_DESTROY = 17
|
||||
MESSAGE_RES_CONFDB_OBJECT_FIND_DESTROY = 17,
|
||||
MESSAGE_RES_CONFDB_KEY_INCREMENT = 18,
|
||||
MESSAGE_RES_CONFDB_KEY_DECREMENT = 19
|
||||
};
|
||||
|
||||
|
||||
@ -181,6 +185,11 @@ struct res_lib_confdb_key_get {
|
||||
mar_name_t value __attribute__((aligned(8)));
|
||||
};
|
||||
|
||||
struct res_lib_confdb_key_incdec {
|
||||
mar_res_header_t header __attribute__((aligned(8)));
|
||||
mar_uint32_t value __attribute__((aligned(8)));
|
||||
};
|
||||
|
||||
struct res_lib_confdb_write {
|
||||
mar_res_header_t header __attribute__((aligned(8)));
|
||||
mar_name_t error __attribute__((aligned(8)));
|
||||
|
115
lib/confdb.c
115
lib/confdb.c
@ -898,6 +898,121 @@ error_exit:
|
||||
return (error);
|
||||
}
|
||||
|
||||
confdb_error_t confdb_key_increment (
|
||||
confdb_handle_t handle,
|
||||
unsigned int parent_object_handle,
|
||||
void *key_name,
|
||||
int key_name_len,
|
||||
unsigned int *value)
|
||||
{
|
||||
confdb_error_t error;
|
||||
struct confdb_inst *confdb_inst;
|
||||
struct iovec iov[2];
|
||||
struct req_lib_confdb_key_get req_lib_confdb_key_get;
|
||||
struct res_lib_confdb_key_incdec res_lib_confdb_key_incdec;
|
||||
|
||||
error = saHandleInstanceGet (&confdb_handle_t_db, handle, (void *)&confdb_inst);
|
||||
if (error != SA_AIS_OK) {
|
||||
return (error);
|
||||
}
|
||||
|
||||
if (confdb_inst->standalone) {
|
||||
error = SA_AIS_OK;
|
||||
|
||||
if (confdb_sa_key_increment(parent_object_handle,
|
||||
key_name, key_name_len,
|
||||
value))
|
||||
error = SA_AIS_ERR_ACCESS;
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
req_lib_confdb_key_get.header.size = sizeof (struct req_lib_confdb_key_get);
|
||||
req_lib_confdb_key_get.header.id = MESSAGE_REQ_CONFDB_KEY_INCREMENT;
|
||||
req_lib_confdb_key_get.parent_object_handle = parent_object_handle;
|
||||
memcpy(req_lib_confdb_key_get.key_name.value, key_name, key_name_len);
|
||||
req_lib_confdb_key_get.key_name.length = key_name_len;
|
||||
|
||||
iov[0].iov_base = (char *)&req_lib_confdb_key_get;
|
||||
iov[0].iov_len = sizeof (struct req_lib_confdb_key_get);
|
||||
|
||||
pthread_mutex_lock (&confdb_inst->response_mutex);
|
||||
|
||||
error = saSendMsgReceiveReply (confdb_inst->response_fd, iov, 1,
|
||||
&res_lib_confdb_key_incdec, sizeof (struct res_lib_confdb_key_incdec));
|
||||
|
||||
pthread_mutex_unlock (&confdb_inst->response_mutex);
|
||||
if (error != SA_AIS_OK) {
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
error = res_lib_confdb_key_incdec.header.error;
|
||||
if (error == SA_AIS_OK) {
|
||||
*value = res_lib_confdb_key_incdec.value;
|
||||
}
|
||||
|
||||
error_exit:
|
||||
saHandleInstancePut (&confdb_handle_t_db, handle);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
confdb_error_t confdb_key_decrement (
|
||||
confdb_handle_t handle,
|
||||
unsigned int parent_object_handle,
|
||||
void *key_name,
|
||||
int key_name_len,
|
||||
unsigned int *value)
|
||||
{
|
||||
confdb_error_t error;
|
||||
struct confdb_inst *confdb_inst;
|
||||
struct iovec iov[2];
|
||||
struct req_lib_confdb_key_get req_lib_confdb_key_get;
|
||||
struct res_lib_confdb_key_incdec res_lib_confdb_key_incdec;
|
||||
|
||||
error = saHandleInstanceGet (&confdb_handle_t_db, handle, (void *)&confdb_inst);
|
||||
if (error != SA_AIS_OK) {
|
||||
return (error);
|
||||
}
|
||||
|
||||
if (confdb_inst->standalone) {
|
||||
error = SA_AIS_OK;
|
||||
|
||||
if (confdb_sa_key_decrement(parent_object_handle,
|
||||
key_name, key_name_len,
|
||||
value))
|
||||
error = SA_AIS_ERR_ACCESS;
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
req_lib_confdb_key_get.header.size = sizeof (struct req_lib_confdb_key_get);
|
||||
req_lib_confdb_key_get.header.id = MESSAGE_REQ_CONFDB_KEY_DECREMENT;
|
||||
req_lib_confdb_key_get.parent_object_handle = parent_object_handle;
|
||||
memcpy(req_lib_confdb_key_get.key_name.value, key_name, key_name_len);
|
||||
req_lib_confdb_key_get.key_name.length = key_name_len;
|
||||
|
||||
iov[0].iov_base = (char *)&req_lib_confdb_key_get;
|
||||
iov[0].iov_len = sizeof (struct req_lib_confdb_key_get);
|
||||
|
||||
pthread_mutex_lock (&confdb_inst->response_mutex);
|
||||
|
||||
error = saSendMsgReceiveReply (confdb_inst->response_fd, iov, 1,
|
||||
&res_lib_confdb_key_incdec, sizeof (struct res_lib_confdb_key_incdec));
|
||||
|
||||
pthread_mutex_unlock (&confdb_inst->response_mutex);
|
||||
if (error != SA_AIS_OK) {
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
error = res_lib_confdb_key_incdec.header.error;
|
||||
if (error == SA_AIS_OK) {
|
||||
*value = res_lib_confdb_key_incdec.value;
|
||||
}
|
||||
|
||||
error_exit:
|
||||
saHandleInstancePut (&confdb_handle_t_db, handle);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
confdb_error_t confdb_key_replace (
|
||||
confdb_handle_t handle,
|
||||
|
@ -250,6 +250,34 @@ int confdb_sa_key_get (
|
||||
return res;
|
||||
}
|
||||
|
||||
int confdb_sa_key_increment (
|
||||
unsigned int parent_object_handle,
|
||||
void *key_name,
|
||||
int key_name_len,
|
||||
unsigned int *value)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = objdb->object_key_increment(parent_object_handle,
|
||||
key_name, key_name_len,
|
||||
value);
|
||||
return res;
|
||||
}
|
||||
|
||||
int confdb_sa_key_decrement (
|
||||
unsigned int parent_object_handle,
|
||||
void *key_name,
|
||||
int key_name_len,
|
||||
unsigned int *value)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = objdb->object_key_decrement(parent_object_handle,
|
||||
key_name, key_name_len,
|
||||
value);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
int confdb_sa_key_replace (
|
||||
unsigned int parent_object_handle,
|
||||
|
@ -69,6 +69,9 @@ static void message_handler_req_lib_confdb_key_replace (void *conn, void *messag
|
||||
static void message_handler_req_lib_confdb_key_delete (void *conn, void *message);
|
||||
static void message_handler_req_lib_confdb_key_iter (void *conn, void *message);
|
||||
|
||||
static void message_handler_req_lib_confdb_key_increment (void *conn, void *message);
|
||||
static void message_handler_req_lib_confdb_key_decrement (void *conn, void *message);
|
||||
|
||||
static void message_handler_req_lib_confdb_object_iter (void *conn, void *message);
|
||||
static void message_handler_req_lib_confdb_object_find (void *conn, void *message);
|
||||
|
||||
@ -189,6 +192,18 @@ static struct corosync_lib_handler confdb_lib_engine[] =
|
||||
.response_id = MESSAGE_RES_CONFDB_OBJECT_FIND_DESTROY,
|
||||
.flow_control = COROSYNC_LIB_FLOW_CONTROL_NOT_REQUIRED
|
||||
},
|
||||
{ /* 15 */
|
||||
.lib_handler_fn = message_handler_req_lib_confdb_key_increment,
|
||||
.response_size = sizeof (struct res_lib_confdb_key_incdec),
|
||||
.response_id = MESSAGE_RES_CONFDB_KEY_INCREMENT,
|
||||
.flow_control = COROSYNC_LIB_FLOW_CONTROL_NOT_REQUIRED
|
||||
},
|
||||
{ /* 16 */
|
||||
.lib_handler_fn = message_handler_req_lib_confdb_key_decrement,
|
||||
.response_size = sizeof (struct res_lib_confdb_key_incdec),
|
||||
.response_id = MESSAGE_RES_CONFDB_KEY_DECREMENT,
|
||||
.flow_control = COROSYNC_LIB_FLOW_CONTROL_NOT_REQUIRED
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -365,6 +380,46 @@ static void message_handler_req_lib_confdb_key_get (void *conn, void *message)
|
||||
api->ipc_conn_send_response(conn, &res_lib_confdb_key_get, sizeof(res_lib_confdb_key_get));
|
||||
}
|
||||
|
||||
static void message_handler_req_lib_confdb_key_increment (void *conn, void *message)
|
||||
{
|
||||
struct req_lib_confdb_key_get *req_lib_confdb_key_get = (struct req_lib_confdb_key_get *)message;
|
||||
struct res_lib_confdb_key_incdec res_lib_confdb_key_incdec;
|
||||
int value_len;
|
||||
void *value;
|
||||
int ret = SA_AIS_OK;
|
||||
|
||||
if (api->object_key_increment(req_lib_confdb_key_get->parent_object_handle,
|
||||
req_lib_confdb_key_get->key_name.value,
|
||||
req_lib_confdb_key_get->key_name.length,
|
||||
&res_lib_confdb_key_incdec.value))
|
||||
ret = SA_AIS_ERR_ACCESS;
|
||||
|
||||
res_lib_confdb_key_incdec.header.size = sizeof(res_lib_confdb_key_incdec);
|
||||
res_lib_confdb_key_incdec.header.id = MESSAGE_RES_CONFDB_KEY_INCREMENT;
|
||||
res_lib_confdb_key_incdec.header.error = ret;
|
||||
api->ipc_conn_send_response(conn, &res_lib_confdb_key_incdec, sizeof(res_lib_confdb_key_incdec));
|
||||
}
|
||||
|
||||
static void message_handler_req_lib_confdb_key_decrement (void *conn, void *message)
|
||||
{
|
||||
struct req_lib_confdb_key_get *req_lib_confdb_key_get = (struct req_lib_confdb_key_get *)message;
|
||||
struct res_lib_confdb_key_incdec res_lib_confdb_key_incdec;
|
||||
int value_len;
|
||||
void *value;
|
||||
int ret = SA_AIS_OK;
|
||||
|
||||
if (api->object_key_decrement(req_lib_confdb_key_get->parent_object_handle,
|
||||
req_lib_confdb_key_get->key_name.value,
|
||||
req_lib_confdb_key_get->key_name.length,
|
||||
&res_lib_confdb_key_incdec.value))
|
||||
ret = SA_AIS_ERR_ACCESS;
|
||||
|
||||
res_lib_confdb_key_incdec.header.size = sizeof(res_lib_confdb_key_incdec);
|
||||
res_lib_confdb_key_incdec.header.id = MESSAGE_RES_CONFDB_KEY_DECREMENT;
|
||||
res_lib_confdb_key_incdec.header.error = ret;
|
||||
api->ipc_conn_send_response(conn, &res_lib_confdb_key_incdec, sizeof(res_lib_confdb_key_incdec));
|
||||
}
|
||||
|
||||
static void message_handler_req_lib_confdb_key_replace (void *conn, void *message)
|
||||
{
|
||||
struct req_lib_confdb_key_replace *req_lib_confdb_key_replace = (struct req_lib_confdb_key_replace *)message;
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include <corosync/saAis.h>
|
||||
#include <corosync/confdb.h>
|
||||
|
||||
#define INCDEC_VALUE 45
|
||||
|
||||
/* Callbacks are not supported yet */
|
||||
confdb_callbacks_t callbacks = {
|
||||
@ -109,6 +110,7 @@ static void print_config_tree(confdb_handle_t handle, unsigned int parent_object
|
||||
static void do_write_tests(confdb_handle_t handle)
|
||||
{
|
||||
int res;
|
||||
unsigned int incdec_value;
|
||||
unsigned int object_handle;
|
||||
char error_string[1024];
|
||||
|
||||
@ -147,6 +149,33 @@ static void do_write_tests(confdb_handle_t handle)
|
||||
|
||||
/* Print it for verification */
|
||||
print_config_tree(handle, object_handle, 0);
|
||||
|
||||
incdec_value = INCDEC_VALUE;
|
||||
res = confdb_key_create(handle, object_handle, "incdec", strlen("incdec"), &incdec_value, sizeof(incdec_value));
|
||||
if (res != SA_AIS_OK) {
|
||||
printf( "error creating 'testconfdb' key 4: %d\n", res);
|
||||
return;
|
||||
}
|
||||
res = confdb_key_increment(handle, object_handle, "incdec", strlen("incdec"), &incdec_value);
|
||||
if (res != SA_AIS_OK) {
|
||||
printf( "error incrementing 'testconfdb' key 4: %d\n", res);
|
||||
return;
|
||||
}
|
||||
if (incdec_value == INCDEC_VALUE+1)
|
||||
printf("incremented value = %d\n", incdec_value);
|
||||
else
|
||||
printf("ERROR: incremented value = %d (should be %d)\n", incdec_value, INCDEC_VALUE+1);
|
||||
|
||||
res = confdb_key_decrement(handle, object_handle, "incdec", strlen("incdec"), &incdec_value);
|
||||
if (res != SA_AIS_OK) {
|
||||
printf( "error decrementing 'testconfdb' key 4: %d\n", res);
|
||||
return;
|
||||
}
|
||||
if (incdec_value == INCDEC_VALUE)
|
||||
printf("decremented value = %d\n", incdec_value);
|
||||
else
|
||||
printf("ERROR: decremented value = %d (should be %d)\n", incdec_value, INCDEC_VALUE);
|
||||
|
||||
printf("-------------------------\n");
|
||||
|
||||
/* Remove it.
|
||||
|
Loading…
Reference in New Issue
Block a user