mirror of
https://git.proxmox.com/git/mirror_corosync
synced 2025-07-06 08:03:44 +00:00
commit for redundant ring take 4 was only done from exec directory missing all
of the commits for the rest of the directories. This commit will now allow the tree to compile. git-svn-id: http://svn.fedorahosted.org/svn/corosync/trunk@1035 fd59a12c-fef9-0310-b244-a6a79926bd2f
This commit is contained in:
parent
afe1867a80
commit
953a21eab8
@ -91,14 +91,14 @@ struct evs_group {
|
||||
};
|
||||
|
||||
typedef void (*evs_deliver_fn_t) (
|
||||
struct evs_address *source_addr,
|
||||
unsigned int nodeid,
|
||||
void *msg,
|
||||
int msg_len);
|
||||
|
||||
typedef void (*evs_confchg_fn_t) (
|
||||
struct evs_address *member_list, int member_list_entries,
|
||||
struct evs_address *left_list, int left_list_entries,
|
||||
struct evs_address *joined_list, int joined_list_entries);
|
||||
unsigned int *member_list, int member_list_entries,
|
||||
unsigned int *left_list, int left_list_entries,
|
||||
unsigned int *joined_list, int joined_list_entries);
|
||||
|
||||
typedef struct {
|
||||
evs_deliver_fn_t evs_deliver_fn;
|
||||
@ -183,8 +183,8 @@ evs_error_t evs_mcast_groups (
|
||||
*/
|
||||
evs_error_t evs_membership_get (
|
||||
evs_handle_t handle,
|
||||
struct evs_address *local_addr,
|
||||
struct evs_address *member_list,
|
||||
int *member_list_entries);
|
||||
unsigned int *local_nodeid,
|
||||
unsigned int *member_list,
|
||||
unsigned int *member_list_entries);
|
||||
|
||||
#endif /* OPENAIS_EVS_H_DEFINED */
|
||||
|
@ -76,7 +76,7 @@ struct cpg_name {
|
||||
|
||||
struct req_lib_cpg_join {
|
||||
struct req_header header;
|
||||
struct cpg_name groupName;
|
||||
struct cpg_name group_name;
|
||||
pid_t pid;
|
||||
};
|
||||
|
||||
@ -86,7 +86,7 @@ struct res_lib_cpg_join {
|
||||
|
||||
struct req_lib_cpg_trackstart {
|
||||
struct req_header header;
|
||||
struct cpg_name groupName;
|
||||
struct cpg_name group_name;
|
||||
pid_t pid;
|
||||
};
|
||||
|
||||
@ -96,7 +96,7 @@ struct res_lib_cpg_trackstart {
|
||||
|
||||
struct req_lib_cpg_trackstop {
|
||||
struct req_header header;
|
||||
struct cpg_name groupName;
|
||||
struct cpg_name group_name;
|
||||
pid_t pid;
|
||||
};
|
||||
|
||||
@ -114,7 +114,7 @@ struct req_lib_cpg_mcast {
|
||||
/* Message from another node */
|
||||
struct res_lib_cpg_deliver_callback {
|
||||
struct res_header header;
|
||||
struct cpg_name groupName;
|
||||
struct cpg_name group_name;
|
||||
uint32_t msglen;
|
||||
uint32_t nodeid;
|
||||
uint32_t pid;
|
||||
@ -123,7 +123,7 @@ struct res_lib_cpg_deliver_callback {
|
||||
|
||||
/* Notifications & join return a list of these */
|
||||
struct cpg_groupinfo {
|
||||
uint32_t nodeId;
|
||||
uint32_t nodeid;
|
||||
uint32_t pid;
|
||||
uint32_t reason; /* How joined or left */
|
||||
};
|
||||
@ -131,12 +131,12 @@ struct cpg_groupinfo {
|
||||
|
||||
struct req_lib_cpg_membership {
|
||||
struct req_header header;
|
||||
struct cpg_name groupName;
|
||||
struct cpg_name group_name;
|
||||
};
|
||||
|
||||
struct res_lib_cpg_confchg_callback {
|
||||
struct res_header header;
|
||||
struct cpg_name groupName;
|
||||
struct cpg_name group_name;
|
||||
uint32_t member_list_entries;
|
||||
uint32_t joined_list_entries;
|
||||
uint32_t left_list_entries;
|
||||
@ -147,7 +147,7 @@ struct res_lib_cpg_confchg_callback {
|
||||
|
||||
struct req_lib_cpg_leave {
|
||||
struct req_header header;
|
||||
struct cpg_name groupName;
|
||||
struct cpg_name group_name;
|
||||
pid_t pid;
|
||||
};
|
||||
|
||||
|
@ -59,7 +59,7 @@ enum res_lib_evs_types {
|
||||
|
||||
struct res_evs_deliver_callback {
|
||||
struct res_header header;
|
||||
struct evs_address evs_address;
|
||||
unsigned int local_nodeid;
|
||||
int msglen;
|
||||
char msg[0];
|
||||
};
|
||||
@ -69,9 +69,9 @@ struct res_evs_confchg_callback {
|
||||
int member_list_entries;
|
||||
int left_list_entries;
|
||||
int joined_list_entries;
|
||||
struct evs_address member_list[16];
|
||||
struct evs_address left_list[16];
|
||||
struct evs_address joined_list[16];
|
||||
unsigned int member_list[PROCESSOR_COUNT_MAX];
|
||||
unsigned int left_list[PROCESSOR_COUNT_MAX];
|
||||
unsigned int joined_list[PROCESSOR_COUNT_MAX];
|
||||
};
|
||||
|
||||
struct req_lib_evs_join {
|
||||
@ -132,8 +132,8 @@ struct req_lib_evs_membership_get {
|
||||
|
||||
struct res_lib_evs_membership_get {
|
||||
struct res_header header;
|
||||
struct in_addr local_addr;
|
||||
struct in_addr member_list[16];
|
||||
unsigned int local_nodeid;
|
||||
unsigned int member_list[PROCESSOR_COUNT_MAX];
|
||||
int member_list_entries;
|
||||
};
|
||||
#endif /* IPC_EVS_H_DEFINED */
|
||||
|
@ -273,7 +273,7 @@ struct res_evt_event_data {
|
||||
*/
|
||||
struct lib_event_data {
|
||||
struct res_header led_head;
|
||||
struct totem_ip_address led_addr;
|
||||
unsigned int led_nodeid;
|
||||
SaTimeT led_receive_time;
|
||||
uint32_t led_svr_channel_handle;
|
||||
SaEvtChannelHandleT led_lib_channel_handle;
|
||||
|
@ -114,7 +114,7 @@ struct res_lib_dispatch_init {
|
||||
struct res_header header;
|
||||
};
|
||||
struct message_source {
|
||||
struct totem_ip_address addr;
|
||||
unsigned int nodeid;
|
||||
void *conn;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
@ -368,7 +368,6 @@ int lcr_ifact_reference (
|
||||
*/
|
||||
return (-1);
|
||||
found:
|
||||
printf ("iface number %d\n", iface_number);
|
||||
*iface = instance->ifaces[iface_number].interfaces;
|
||||
if (instance->ifaces[iface_number].constructor) {
|
||||
instance->ifaces[iface_number].constructor (context);
|
||||
|
14
lib/cpg.c
14
lib/cpg.c
@ -298,7 +298,7 @@ cpg_error_t cpg_dispatch (
|
||||
case MESSAGE_RES_CPG_DELIVER_CALLBACK:
|
||||
res_cpg_deliver_callback = (struct res_lib_cpg_deliver_callback *)&dispatch_data;
|
||||
callbacks.cpg_deliver_fn (handle,
|
||||
&res_cpg_deliver_callback->groupName,
|
||||
&res_cpg_deliver_callback->group_name,
|
||||
res_cpg_deliver_callback->nodeid,
|
||||
res_cpg_deliver_callback->pid,
|
||||
&res_cpg_deliver_callback->message,
|
||||
@ -308,7 +308,7 @@ cpg_error_t cpg_dispatch (
|
||||
case MESSAGE_RES_CPG_CONFCHG_CALLBACK:
|
||||
res_cpg_confchg_callback = (struct res_lib_cpg_confchg_callback *)&dispatch_data;
|
||||
callbacks.cpg_confchg_fn (handle,
|
||||
&res_cpg_confchg_callback->groupName,
|
||||
&res_cpg_confchg_callback->group_name,
|
||||
(struct cpg_address *)res_cpg_confchg_callback->member_list,
|
||||
res_cpg_confchg_callback->member_list_entries,
|
||||
(struct cpg_address *)res_cpg_confchg_callback->member_list + res_cpg_confchg_callback->member_list_entries,
|
||||
@ -373,7 +373,7 @@ cpg_error_t cpg_join (
|
||||
/* Automatically add a tracker */
|
||||
req_lib_cpg_trackstart.header.size = sizeof (struct req_lib_cpg_trackstart);
|
||||
req_lib_cpg_trackstart.header.id = MESSAGE_REQ_CPG_TRACKSTART;
|
||||
memcpy(&req_lib_cpg_trackstart.groupName, group, sizeof(struct cpg_name));
|
||||
memcpy(&req_lib_cpg_trackstart.group_name, group, sizeof(struct cpg_name));
|
||||
|
||||
iov[0].iov_base = &req_lib_cpg_trackstart;
|
||||
iov[0].iov_len = sizeof (struct req_lib_cpg_trackstart);
|
||||
@ -390,7 +390,7 @@ cpg_error_t cpg_join (
|
||||
req_lib_cpg_join.header.size = sizeof (struct req_lib_cpg_join);
|
||||
req_lib_cpg_join.header.id = MESSAGE_REQ_CPG_JOIN;
|
||||
req_lib_cpg_join.pid = getpid();
|
||||
memcpy(&req_lib_cpg_join.groupName, group, sizeof(struct cpg_name));
|
||||
memcpy(&req_lib_cpg_join.group_name, group, sizeof(struct cpg_name));
|
||||
|
||||
iov[0].iov_base = &req_lib_cpg_join;
|
||||
iov[0].iov_len = sizeof (struct req_lib_cpg_join);
|
||||
@ -430,7 +430,7 @@ cpg_error_t cpg_leave (
|
||||
req_lib_cpg_leave.header.size = sizeof (struct req_lib_cpg_leave);
|
||||
req_lib_cpg_leave.header.id = MESSAGE_REQ_CPG_LEAVE;
|
||||
req_lib_cpg_leave.pid = getpid();
|
||||
memcpy(&req_lib_cpg_leave.groupName, group, sizeof(struct cpg_name));
|
||||
memcpy(&req_lib_cpg_leave.group_name, group, sizeof(struct cpg_name));
|
||||
|
||||
iov[0].iov_base = &req_lib_cpg_leave;
|
||||
iov[0].iov_len = sizeof (struct req_lib_cpg_leave);
|
||||
@ -508,7 +508,7 @@ error_exit:
|
||||
|
||||
cpg_error_t cpg_membership_get (
|
||||
cpg_handle_t handle,
|
||||
struct cpg_name *groupName,
|
||||
struct cpg_name *group_name,
|
||||
struct cpg_address *member_list,
|
||||
int *member_list_entries)
|
||||
{
|
||||
@ -525,7 +525,7 @@ cpg_error_t cpg_membership_get (
|
||||
|
||||
req_lib_cpg_membership_get.header.size = sizeof (struct req_header);
|
||||
req_lib_cpg_membership_get.header.id = MESSAGE_REQ_CPG_MEMBERSHIP;
|
||||
memcpy(&req_lib_cpg_membership_get.groupName, groupName, sizeof(struct cpg_name));
|
||||
memcpy(&req_lib_cpg_membership_get.group_name, group_name, sizeof(struct cpg_name));
|
||||
|
||||
iov.iov_base = &req_lib_cpg_membership_get;
|
||||
iov.iov_len = sizeof (struct req_header);
|
||||
|
13
lib/evs.c
13
lib/evs.c
@ -1,3 +1,4 @@
|
||||
#define PROCESSOR_COUNT_MAX 32
|
||||
/*
|
||||
* vi: set autoindent tabstop=4 shiftwidth=4 :
|
||||
|
||||
@ -303,7 +304,7 @@ evs_error_t evs_dispatch (
|
||||
case MESSAGE_RES_EVS_DELIVER_CALLBACK:
|
||||
res_evs_deliver_callback = (struct res_evs_deliver_callback *)&dispatch_data;
|
||||
callbacks.evs_deliver_fn (
|
||||
&res_evs_deliver_callback->evs_address,
|
||||
res_evs_deliver_callback->local_nodeid,
|
||||
&res_evs_deliver_callback->msg,
|
||||
res_evs_deliver_callback->msglen);
|
||||
break;
|
||||
@ -551,9 +552,9 @@ error_exit:
|
||||
|
||||
evs_error_t evs_membership_get (
|
||||
evs_handle_t handle,
|
||||
struct evs_address *local_addr,
|
||||
struct evs_address *member_list,
|
||||
int *member_list_entries)
|
||||
unsigned int *local_nodeid,
|
||||
unsigned int *member_list,
|
||||
unsigned int *member_list_entries)
|
||||
{
|
||||
evs_error_t error;
|
||||
struct evs_inst *evs_inst;
|
||||
@ -588,8 +589,8 @@ evs_error_t evs_membership_get (
|
||||
/*
|
||||
* Copy results to caller
|
||||
*/
|
||||
if (local_addr) {
|
||||
memcpy (local_addr, &res_lib_evs_membership_get.local_addr, sizeof (struct in_addr));
|
||||
if (local_nodeid) {
|
||||
*local_nodeid = res_lib_evs_membership_get.local_nodeid;
|
||||
}
|
||||
*member_list_entries = *member_list_entries < res_lib_evs_membership_get.member_list_entries ?
|
||||
*member_list_entries : res_lib_evs_membership_get.member_list_entries;
|
||||
|
@ -159,7 +159,7 @@ A throughput of 60mb/sec is possible when this option is disabled on
|
||||
The default is on.
|
||||
|
||||
.TP
|
||||
redundantring
|
||||
rrp_mode
|
||||
This specifies the mode of redundant ring, which may be none, active, or
|
||||
passive. Active replication offers slightly lower latency from transmit
|
||||
to delivery in faulty network environments but with less performance.
|
||||
@ -240,6 +240,8 @@ receiving a token. This is the time spent detecting a failure of a processor
|
||||
in the current configuration. Reforming a new configuration takes about 50
|
||||
milliseconds in addition to this timeout.
|
||||
|
||||
The default is 1000 milliseconds.
|
||||
|
||||
.TP
|
||||
token_retransmit
|
||||
This timeout specifies in milliseconds after how long before receiving a token
|
||||
@ -247,28 +249,38 @@ the token is retransmitted. This will be automatically calculated if token
|
||||
is modified. It is not recommended to alter this value without guidance from
|
||||
the openais community.
|
||||
|
||||
The default is 238 milliseconds.
|
||||
|
||||
.TP
|
||||
hold
|
||||
This timeout specifies in milliseconds how long the token should be held by
|
||||
the representative when the protocol is under low utilization. It is not
|
||||
recommended to alter this value without guidance from the openais community.
|
||||
|
||||
The default is 180 milliseconds.
|
||||
|
||||
.TP
|
||||
retransmits_before_loss
|
||||
This value identifies how many token retransmits should be attempted before
|
||||
forming a new configuration. If this value is set, retransmit and hold will
|
||||
be automatically calculated from retransmits_before_loss and token.
|
||||
|
||||
The default is 4 retransmissions.
|
||||
|
||||
.TP
|
||||
join
|
||||
This timeout specifies in milliseconds how long to wait for join messages in
|
||||
the membership protocol.
|
||||
|
||||
The default is 100 milliseconds.
|
||||
|
||||
.TP
|
||||
consensus
|
||||
This timeout specifies in milliseconds how long to wait for consensus to be
|
||||
achieved before starting a new round of membership configuration.
|
||||
|
||||
The default is 200 milliseconds.
|
||||
|
||||
.TP
|
||||
merge
|
||||
This timeout specifies in milliseconds how long to wait before checking for
|
||||
@ -276,22 +288,30 @@ a partition when no multicast traffic is being sent. If multicast traffic
|
||||
is being sent, the merge detection happens automatically as a function of
|
||||
the protocol.
|
||||
|
||||
The default is 200 milliseconds.
|
||||
|
||||
.TP
|
||||
downcheck
|
||||
This timeout specifies in milliseconds how long to wait before checking
|
||||
that a network interface is back up after it has been downed.
|
||||
|
||||
The default is 1000 millseconds.
|
||||
|
||||
.TP
|
||||
fail_to_recv_const
|
||||
This constant specifies how many rotations of the token without receiving any
|
||||
of the messages when messages should be received may occur before a new
|
||||
configuration is formed.
|
||||
|
||||
The default is 50 failures to receive a message.
|
||||
|
||||
.TP
|
||||
seqno_unchanged_const
|
||||
This constant specifies how many rotations of the token without any multicast
|
||||
traffic should occur before the merge detection timeout is started.
|
||||
|
||||
The default is 30 rotations.
|
||||
|
||||
.TP
|
||||
heartbeat_failures_allowed
|
||||
[HeartBeating mechanism]
|
||||
@ -341,6 +361,41 @@ processor on receipt of the token. The max_messages parameter is limited to
|
||||
|
||||
The default is 17 messages.
|
||||
|
||||
.TP
|
||||
rrp_problem_count_timeout
|
||||
This specifies the time in milliseconds to wait before decrementing the
|
||||
problem count by 1 for a particular ring to ensure a link is not marked
|
||||
faulty for transient network failures.
|
||||
|
||||
The default is 1000 milliseconds.
|
||||
|
||||
.TP
|
||||
rrp_problem_count_threshold
|
||||
This specifies the number of times a problem is detected with a link before
|
||||
setting the link faulty. Once a link is set faulty, no more data is
|
||||
transmitted upon it. Also, the problem counter is no longer decremented when
|
||||
the problem count timeout expires.
|
||||
|
||||
A problem is detected whenever all tokens from the proceeding processor have
|
||||
not been received within the rrp_token_expired_timeout. The
|
||||
rrp_problem_count_threshold * rrp_token_expired_timeout should be atleast 50
|
||||
milliseconds less then the token timeout, or a complete reconfiguration
|
||||
may occur.
|
||||
|
||||
The default is 20 problem counts.
|
||||
|
||||
.TP
|
||||
rrp_token_expired_timeout
|
||||
This specifies the time in milliseconds to increment the problem counter for
|
||||
the redundant ring protocol after not having received a token from all rings
|
||||
for a particular processor.
|
||||
|
||||
This value will automatically be calculated from the token timeout and
|
||||
problem_count_threshold but may be overridden. It is not recommended to
|
||||
override this value without guidance from the openais community.
|
||||
|
||||
The default is 47 milliseconds.
|
||||
|
||||
.PP
|
||||
Within the
|
||||
.B logging
|
||||
|
@ -58,39 +58,35 @@ int outstanding = 0;
|
||||
|
||||
|
||||
|
||||
void evs_deliver_fn (struct evs_address *source_addr, void *msg, int msg_len)
|
||||
void evs_deliver_fn (
|
||||
unsigned int nodeid,
|
||||
void *msg,
|
||||
int msg_len)
|
||||
{
|
||||
outstanding--;
|
||||
// printf ("Delivering message %s\n", msg);
|
||||
}
|
||||
|
||||
void evs_confchg_fn (
|
||||
struct evs_address *member_list, int member_list_entries,
|
||||
struct evs_address *left_list, int left_list_entries,
|
||||
struct evs_address *joined_list, int joined_list_entries)
|
||||
unsigned int *member_list, int member_list_entries,
|
||||
unsigned int *left_list, int left_list_entries,
|
||||
unsigned int *joined_list, int joined_list_entries)
|
||||
{
|
||||
int i;
|
||||
char buf[1024];
|
||||
|
||||
printf ("CONFIGURATION CHANGE\n");
|
||||
printf ("--------------------\n");
|
||||
printf ("New configuration\n");
|
||||
for (i = 0; i < member_list_entries; i++) {
|
||||
inet_ntop (member_list[i].family, member_list[i].addr,
|
||||
buf, sizeof (buf));
|
||||
printf ("%s\n", buf);
|
||||
printf ("%x\n", member_list[i]);
|
||||
}
|
||||
printf ("Members Left:\n");
|
||||
for (i = 0; i < left_list_entries; i++) {
|
||||
inet_ntop (left_list[i].family, left_list[i].addr,
|
||||
buf, sizeof (buf));
|
||||
printf ("%s\n", buf);
|
||||
printf ("%x\n", left_list[i]);
|
||||
}
|
||||
printf ("Members Joined:\n");
|
||||
for (i = 0; i < joined_list_entries; i++) {
|
||||
inet_ntop (joined_list[i].family, joined_list[i].addr,
|
||||
buf, sizeof (buf));
|
||||
printf ("%s\n", buf);
|
||||
printf ("%x\n", joined_list[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,10 @@
|
||||
char *delivery_string;
|
||||
|
||||
int deliveries = 0;
|
||||
void evs_deliver_fn (struct evs_address *source_addr, void *msg, int msg_len)
|
||||
void evs_deliver_fn (
|
||||
unsigned int nodeid,
|
||||
void *msg,
|
||||
int msg_len)
|
||||
{
|
||||
char *buf = msg;
|
||||
|
||||
@ -53,32 +56,25 @@ void evs_deliver_fn (struct evs_address *source_addr, void *msg, int msg_len)
|
||||
}
|
||||
|
||||
void evs_confchg_fn (
|
||||
struct evs_address *member_list, int member_list_entries,
|
||||
struct evs_address *left_list, int left_list_entries,
|
||||
struct evs_address *joined_list, int joined_list_entries)
|
||||
unsigned int *member_list, int member_list_entries,
|
||||
unsigned int *left_list, int left_list_entries,
|
||||
unsigned int *joined_list, int joined_list_entries)
|
||||
{
|
||||
int i;
|
||||
char buf[256];
|
||||
|
||||
printf ("CONFIGURATION CHANGE\n");
|
||||
printf ("--------------------\n");
|
||||
printf ("New configuration\n");
|
||||
for (i = 0; i < member_list_entries; i++) {
|
||||
inet_ntop (member_list[i].family, member_list[i].addr,
|
||||
buf, sizeof (buf));
|
||||
printf ("%s\n", buf);
|
||||
printf ("%x\n", member_list[i]);
|
||||
}
|
||||
printf ("Members Left:\n");
|
||||
for (i = 0; i < left_list_entries; i++) {
|
||||
inet_ntop (left_list[i].family, left_list[i].addr,
|
||||
buf, sizeof (buf));
|
||||
printf ("%s\n", buf);
|
||||
printf ("%x\n", left_list[i]);
|
||||
}
|
||||
printf ("Members Joined:\n");
|
||||
for (i = 0; i < joined_list_entries; i++) {
|
||||
inet_ntop (joined_list[i].family, joined_list[i].addr,
|
||||
buf, sizeof (buf));
|
||||
printf ("%s\n", buf);
|
||||
printf ("%x\n", joined_list[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,10 +101,9 @@ int main (void)
|
||||
evs_error_t result;
|
||||
int i = 0;
|
||||
int fd;
|
||||
struct evs_address member_list[16];
|
||||
struct evs_address local_addr;
|
||||
int member_list_entries = sizeof (member_list) / sizeof (struct in_addr);
|
||||
char buf[1024];
|
||||
unsigned int member_list[32];
|
||||
unsigned int local_nodeid;
|
||||
int member_list_entries = 32;
|
||||
|
||||
result = evs_initialize (&handle, &callbacks);
|
||||
if (result != EVS_OK) {
|
||||
@ -116,19 +111,16 @@ int main (void)
|
||||
exit (0);
|
||||
}
|
||||
|
||||
result = evs_membership_get (handle, &local_addr,
|
||||
result = evs_membership_get (handle, &local_nodeid,
|
||||
member_list, &member_list_entries);
|
||||
printf ("Current membership from evs_membership_get entries %d\n",
|
||||
member_list_entries);
|
||||
for (i = 0; i < member_list_entries; i++) {
|
||||
inet_ntop (member_list[i].family, member_list[i].addr,
|
||||
buf, sizeof (buf));
|
||||
printf ("member [%d] is %s\n", i, buf);
|
||||
printf ("member [%d] is %x\n", i, member_list[i]);
|
||||
}
|
||||
inet_ntop (local_addr.family, local_addr.addr,
|
||||
buf, sizeof (buf));
|
||||
printf ("local processor from evs_membership_get %s\n", buf);
|
||||
printf ("local processor from evs_membership_get %x\n", local_nodeid);
|
||||
|
||||
exit (1);
|
||||
printf ("Init result %d\n", result);
|
||||
result = evs_join (handle, groups, 3);
|
||||
printf ("Join result %d\n", result);
|
||||
|
Loading…
Reference in New Issue
Block a user