diff --git a/debian/libcfg7.symbols b/debian/libcfg7.symbols index 27ef42e..30b9cb5 100644 --- a/debian/libcfg7.symbols +++ b/debian/libcfg7.symbols @@ -10,6 +10,7 @@ libcfg.so.7 libcfg7 #MINVER# corosync_cfg_initialize@COROSYNC_CFG_0.82 2.99.5 corosync_cfg_kill_node@Base 2.99.5 corosync_cfg_local_get@Base 2.99.5 + corosync_cfg_node_status_get@COROSYNC_CFG_0.82 3.1.0 corosync_cfg_reload_config@Base 2.99.5 corosync_cfg_reopen_log_files@Base 2.99.5 corosync_cfg_replyto_shutdown@Base 2.99.5 diff --git a/debian/patches/0003-The-ring-id-file-needn-t-be-executable.patch b/debian/patches/0003-The-ring-id-file-needn-t-be-executable.patch new file mode 100644 index 0000000..3856477 --- /dev/null +++ b/debian/patches/0003-The-ring-id-file-needn-t-be-executable.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ferenc=20W=C3=A1gner?= +Date: Sun, 8 Nov 2020 20:49:15 +0100 +Subject: [PATCH 3/6] The ring id file needn't be executable +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +At the same time simplify the overwrite logic and stop clearing the +umask (which is unexpected and quite pointless here, as applications +can't really protect the users from their own pathological settings). + +Signed-off-by: Ferenc Wágner +Reviewed-by: Jan Friesse +Signed-off-by: Thomas Lamprecht +--- + exec/main.c | 10 +++------- + 1 file changed, 3 insertions(+), 7 deletions(-) + +diff --git a/exec/main.c b/exec/main.c +index f1496eb9..1c4c009d 100644 +--- a/exec/main.c ++++ b/exec/main.c +@@ -640,7 +640,7 @@ static void corosync_ring_id_create_or_load ( + + snprintf (filename, sizeof(filename), "%s/ringid_%u", + get_state_dir(), nodeid); +- fd = open (filename, O_RDONLY, 0700); ++ fd = open (filename, O_RDONLY); + /* + * If file can be opened and read, read the ring id + */ +@@ -653,8 +653,7 @@ static void corosync_ring_id_create_or_load ( + */ + if ((fd == -1) || (res != sizeof (uint64_t))) { + memb_ring_id->seq = 0; +- umask(0); +- fd = open (filename, O_CREAT|O_RDWR, 0700); ++ fd = creat (filename, 0600); + if (fd != -1) { + res = write (fd, &memb_ring_id->seq, sizeof (uint64_t)); + close (fd); +@@ -686,10 +685,7 @@ static void corosync_ring_id_store ( + snprintf (filename, sizeof(filename), "%s/ringid_%u", + get_state_dir(), nodeid); + +- fd = open (filename, O_WRONLY, 0700); +- if (fd == -1) { +- fd = open (filename, O_CREAT|O_RDWR, 0700); +- } ++ fd = creat (filename, 0600); + if (fd == -1) { + LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, + "Couldn't store new ring id " CS_PRI_RING_ID_SEQ " to stable storage", diff --git a/debian/patches/0004-totemknet-Check-both-cipher-and-hash-for-crypto.patch b/debian/patches/0004-totemknet-Check-both-cipher-and-hash-for-crypto.patch new file mode 100644 index 0000000..d6259df --- /dev/null +++ b/debian/patches/0004-totemknet-Check-both-cipher-and-hash-for-crypto.patch @@ -0,0 +1,86 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jan Friesse +Date: Tue, 10 Nov 2020 18:10:17 +0100 +Subject: [PATCH 4/6] totemknet: Check both cipher and hash for crypto + +Previously only crypto cipher was used as a way to find out if crypto is +enabled or disabled. + +This usually works ok until cipher is set to none and hash to some other +value (like sha1). Such config is perfectly valid and it was not +supported correctly. + +As a solution, check both cipher and hash. + +Signed-off-by: Jan Friesse +Reviewed-by: Fabio M. Di Nitto +Reviewed-by: Christine Caulfield +Signed-off-by: Thomas Lamprecht +--- + exec/totemknet.c | 18 +++++++++++++----- + 1 file changed, 13 insertions(+), 5 deletions(-) + +diff --git a/exec/totemknet.c b/exec/totemknet.c +index c6a1649d..0834e8e4 100644 +--- a/exec/totemknet.c ++++ b/exec/totemknet.c +@@ -905,6 +905,14 @@ static void totemknet_add_config_notifications(struct totemknet_instance *instan + LEAVE(); + } + ++static int totemknet_is_crypto_enabled(const struct totemknet_instance *instance) ++{ ++ ++ return (!(strcmp(instance->totem_config->crypto_cipher_type, "none") == 0 && ++ strcmp(instance->totem_config->crypto_hash_type, "none") == 0)); ++ ++} ++ + static int totemknet_set_knet_crypto(struct totemknet_instance *instance) + { + struct knet_handle_crypto_cfg crypto_cfg; +@@ -927,7 +935,7 @@ static int totemknet_set_knet_crypto(struct totemknet_instance *instance) + ); + + /* If crypto is being disabled we need to explicitly allow cleartext traffic in knet */ +- if (strcmp(instance->totem_config->crypto_cipher_type, "none") == 0) { ++ if (!totemknet_is_crypto_enabled(instance)) { + res = knet_handle_crypto_rx_clear_traffic(instance->knet_handle, KNET_CRYPTO_RX_ALLOW_CLEAR_TRAFFIC); + if (res) { + knet_log_printf(LOGSYS_LEVEL_ERROR, "knet_handle_crypto_rx_clear_traffic(ALLOW) failed %s", strerror(errno)); +@@ -1108,7 +1116,7 @@ int totemknet_initialize ( + + /* Enable crypto if requested */ + #ifdef HAVE_KNET_CRYPTO_RECONF +- if (strcmp(instance->totem_config->crypto_cipher_type, "none") != 0) { ++ if (totemknet_is_crypto_enabled(instance)) { + res = totemknet_set_knet_crypto(instance); + if (res == 0) { + res = knet_handle_crypto_use_config(instance->knet_handle, totem_config->crypto_index); +@@ -1134,7 +1142,7 @@ int totemknet_initialize ( + } + } + #else +- if (strcmp(instance->totem_config->crypto_cipher_type, "none") != 0) { ++ if (totemknet_is_crypto_enabled(instance)) { + res = totemknet_set_knet_crypto(instance); + if (res) { + knet_log_printf(LOG_DEBUG, "Failed to set up knet crypto"); +@@ -1616,7 +1624,7 @@ int totemknet_crypto_reconfigure_phase ( + switch (phase) { + case CRYPTO_RECONFIG_PHASE_ACTIVATE: + config_to_use = totem_config->crypto_index; +- if (strcmp(instance->totem_config->crypto_cipher_type, "none") == 0) { ++ if (!totemknet_is_crypto_enabled(instance)) { + config_to_use = 0; /* we are clearing it */ + } + +@@ -1647,7 +1655,7 @@ int totemknet_crypto_reconfigure_phase ( + } + + /* If crypto is enabled then disable all cleartext reception */ +- if (strcmp(instance->totem_config->crypto_cipher_type, "none") != 0) { ++ if (totemknet_is_crypto_enabled(instance)) { + res = knet_handle_crypto_rx_clear_traffic(instance->knet_handle, KNET_CRYPTO_RX_DISALLOW_CLEAR_TRAFFIC); + if (res) { + knet_log_printf(LOGSYS_LEVEL_ERROR, "knet_handle_crypto_rx_clear_traffic(DISALLOW) failed %s", strerror(errno)); diff --git a/debian/patches/0005-cfg-New-API-to-get-extended-node-link-infomation.patch b/debian/patches/0005-cfg-New-API-to-get-extended-node-link-infomation.patch new file mode 100644 index 0000000..29dd98d --- /dev/null +++ b/debian/patches/0005-cfg-New-API-to-get-extended-node-link-infomation.patch @@ -0,0 +1,1170 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Christine Caulfield +Date: Thu, 29 Oct 2020 11:07:48 +0000 +Subject: [PATCH 5/6] cfg: New API to get extended node/link infomation + +Current we horribly over-use totempg_ifaces_get() to +retrieve information about knet interfaces. This is an attempt to +improve on that. + +All transports are supported (so not only Knet but also UDP(U)). + +This patch builds best against the "onwire-upgrade" branch of knet +as that's what sparked my interest in getting more information out. + +Signed-off-by: Christine Caulfield +Reviewed-by: Jan Friesse +Signed-off-by: Thomas Lamprecht +--- + configure.ac | 2 + + exec/cfg.c | 66 ++++++++ + exec/totemknet.c | 77 +++++++++ + exec/totemknet.h | 3 + + exec/totemnet.c | 21 +++ + exec/totemnet.h | 5 + + exec/totempg.c | 7 + + exec/totemsrp.c | 21 +++ + exec/totemsrp.h | 3 + + exec/totemudp.c | 29 ++++ + exec/totemudp.h | 3 + + exec/totemudpu.c | 28 ++++ + exec/totemudpu.h | 3 + + include/corosync/cfg.h | 37 +++++ + include/corosync/ipc_cfg.h | 23 ++- + include/corosync/totem/totem.h | 19 +++ + include/corosync/totem/totempg.h | 3 + + lib/cfg.c | 52 +++++- + lib/libcfg.versions | 1 + + lib/libcfg.verso | 2 +- + man/corosync-cfgtool.8 | 34 +++- + tools/corosync-cfgtool.c | 263 ++++++++++++++++--------------- + 22 files changed, 564 insertions(+), 138 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 7705a306..58d46c65 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -194,6 +194,8 @@ AC_CHECK_LIB([knet],[knet_handle_enable_access_lists], + [AC_DEFINE_UNQUOTED([HAVE_KNET_ACCESS_LIST], 1, [have knet access list])]) + AC_CHECK_LIB([knet],[knet_handle_crypto_set_config], + [AC_DEFINE_UNQUOTED([HAVE_KNET_CRYPTO_RECONF], 1, [have knet crypto reconfig support])]) ++AC_CHECK_LIB([knet],[knet_handle_get_onwire_ver], ++ [AC_DEFINE_UNQUOTED([HAVE_KNET_ONWIRE_VER], 1, [have knet onwire versioning])]) + LIBS="$OLDLIBS" + + # Checks for library functions. +diff --git a/exec/cfg.c b/exec/cfg.c +index 75b644ab..c300cc8f 100644 +--- a/exec/cfg.c ++++ b/exec/cfg.c +@@ -65,6 +65,7 @@ + #include + + #include "totemconfig.h" ++#include "totemknet.h" + #include "service.h" + #include "main.h" + +@@ -141,6 +142,10 @@ static void message_handler_req_lib_cfg_ringstatusget ( + void *conn, + const void *msg); + ++static void message_handler_req_lib_cfg_nodestatusget ( ++ void *conn, ++ const void *msg); ++ + static void message_handler_req_lib_cfg_ringreenable ( + void *conn, + const void *msg); +@@ -213,6 +218,10 @@ static struct corosync_lib_handler cfg_lib_engine[] = + { /* 8 */ + .lib_handler_fn = message_handler_req_lib_cfg_reopen_log_files, + .flow_control = CS_LIB_FLOW_CONTROL_NOT_REQUIRED ++ }, ++ { /* 9 */ ++ .lib_handler_fn = message_handler_req_lib_cfg_nodestatusget, ++ .flow_control = CS_LIB_FLOW_CONTROL_NOT_REQUIRED + } + }; + +@@ -957,6 +966,63 @@ send_response: + LEAVE(); + } + ++ ++static void message_handler_req_lib_cfg_nodestatusget ( ++ void *conn, ++ const void *msg) ++{ ++ struct res_lib_cfg_nodestatusget res_lib_cfg_nodestatusget; ++ struct req_lib_cfg_nodestatusget *req_lib_cfg_nodestatusget = (struct req_lib_cfg_nodestatusget *)msg; ++ struct totem_node_status node_status; ++ cs_error_t res = CS_OK; ++ int i; ++ ++ ENTER(); ++ ++ /* Currently only one structure version supported */ ++ if (req_lib_cfg_nodestatusget->version == TOTEM_NODE_STATUS_STRUCTURE_VERSION) ++ { ++ res_lib_cfg_nodestatusget.header.id = MESSAGE_RES_CFG_NODESTATUSGET; ++ res_lib_cfg_nodestatusget.header.size = sizeof (struct res_lib_cfg_nodestatusget); ++ ++ memset(&node_status, 0, sizeof(node_status)); ++ res = totempg_nodestatus_get(req_lib_cfg_nodestatusget->nodeid, ++ &node_status); ++ if (res == 0) { ++ res_lib_cfg_nodestatusget.node_status.nodeid = req_lib_cfg_nodestatusget->nodeid; ++ res_lib_cfg_nodestatusget.node_status.version = node_status.version; ++ res_lib_cfg_nodestatusget.node_status.reachable = node_status.reachable; ++ res_lib_cfg_nodestatusget.node_status.remote = node_status.remote; ++ res_lib_cfg_nodestatusget.node_status.external = node_status.external; ++ res_lib_cfg_nodestatusget.node_status.onwire_min = node_status.onwire_min; ++ res_lib_cfg_nodestatusget.node_status.onwire_max = node_status.onwire_max; ++ res_lib_cfg_nodestatusget.node_status.onwire_ver= node_status.onwire_ver; ++ ++ for (i=0; i < KNET_MAX_LINK; i++) { ++ res_lib_cfg_nodestatusget.node_status.link_status[i].enabled = node_status.link_status[i].enabled; ++ res_lib_cfg_nodestatusget.node_status.link_status[i].connected = node_status.link_status[i].connected; ++ res_lib_cfg_nodestatusget.node_status.link_status[i].dynconnected = node_status.link_status[i].dynconnected; ++ res_lib_cfg_nodestatusget.node_status.link_status[i].mtu = node_status.link_status[i].mtu; ++ memcpy(res_lib_cfg_nodestatusget.node_status.link_status[i].src_ipaddr, ++ node_status.link_status[i].src_ipaddr, CFG_MAX_HOST_LEN); ++ memcpy(res_lib_cfg_nodestatusget.node_status.link_status[i].dst_ipaddr, ++ node_status.link_status[i].dst_ipaddr, CFG_MAX_HOST_LEN); ++ } ++ } ++ } else { ++ res = CS_ERR_NOT_SUPPORTED; ++ } ++ ++ res_lib_cfg_nodestatusget.header.error = res; ++ api->ipc_response_send ( ++ conn, ++ &res_lib_cfg_nodestatusget, ++ sizeof (struct res_lib_cfg_nodestatusget)); ++ ++ LEAVE(); ++} ++ ++ + static void message_handler_req_lib_cfg_ringreenable ( + void *conn, + const void *msg) +diff --git a/exec/totemknet.c b/exec/totemknet.c +index 0834e8e4..772752c5 100644 +--- a/exec/totemknet.c ++++ b/exec/totemknet.c +@@ -488,6 +488,83 @@ static int node_compare(const void *aptr, const void *bptr) + #define OWN_INDEX_NONE -1 + #endif + ++int totemknet_nodestatus_get ( ++ void *knet_context, ++ unsigned int nodeid, ++ struct totem_node_status *node_status) ++{ ++ int i; ++ int res = 0; ++ struct knet_link_status link_status; ++ struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; ++ struct knet_host_status knet_host_status; ++ uint8_t link_list[KNET_MAX_LINK]; ++ size_t num_links; ++ ++ if (!instance->knet_handle) { ++ return CS_ERR_NOT_EXIST; /* Not using knet */ ++ } ++ ++ if (!node_status) { ++ return CS_ERR_INVALID_PARAM; ++ } ++ ++ res = knet_host_get_status(instance->knet_handle, ++ nodeid, ++ &knet_host_status); ++ if (res) { ++ knet_log_printf (LOGSYS_LEVEL_WARNING, "knet_handle_get_host_status(%d) failed: %d", nodeid, res); ++ return (-1); ++ } ++ node_status->nodeid = nodeid; ++ node_status->reachable = knet_host_status.reachable; ++ node_status->remote = knet_host_status.remote; ++ node_status->external = knet_host_status.external; ++ ++#ifdef HAVE_KNET_ONWIRE_VER ++ res = knet_handle_get_onwire_ver(instance->knet_handle, ++ nodeid, ++ &node_status->onwire_min, ++ &node_status->onwire_max, ++ &node_status->onwire_ver); ++ if (res) { ++ knet_log_printf (LOGSYS_LEVEL_WARNING, "knet_handle_get_onwire_ver(%d) failed: %d", nodeid, res); ++ return (-1); ++ } ++#endif ++ /* Get link info */ ++ res = knet_link_get_link_list(instance->knet_handle, ++ nodeid, link_list, &num_links); ++ if (res) { ++ knet_log_printf (LOGSYS_LEVEL_WARNING, "knet_link_get_link_list(%d) failed: %d", nodeid, res); ++ return (-1); ++ } ++ ++ for (i=0; i < num_links; i++) { ++ if (!instance->totem_config->interfaces[link_list[i]].configured) { ++ continue; ++ } ++ res = knet_link_get_status(instance->knet_handle, ++ nodeid, ++ link_list[i], ++ &link_status, ++ sizeof(link_status)); ++ if (res == 0) { ++ node_status->link_status[i].enabled = link_status.enabled; ++ node_status->link_status[i].connected = link_status.connected; ++ node_status->link_status[i].dynconnected = link_status.dynconnected; ++ node_status->link_status[i].mtu = link_status.mtu; ++ memcpy(node_status->link_status[i].src_ipaddr, link_status.src_ipaddr, KNET_MAX_HOST_LEN); ++ memcpy(node_status->link_status[i].dst_ipaddr, link_status.dst_ipaddr, KNET_MAX_HOST_LEN); ++ } else { ++ knet_log_printf (LOGSYS_LEVEL_WARNING, "knet_link_get_link_status(%d, %d) failed: %d", nodeid, link_list[i], res); ++ } ++ } ++ return res; ++} ++ ++ ++ + int totemknet_ifaces_get (void *knet_context, + char ***status, + unsigned int *iface_count) +diff --git a/exec/totemknet.h b/exec/totemknet.h +index 3957b7f2..30068747 100644 +--- a/exec/totemknet.h ++++ b/exec/totemknet.h +@@ -102,6 +102,9 @@ extern int totemknet_finalize (void *knet_context); + + extern void totemknet_net_mtu_adjust (void *knet_context, struct totem_config *totem_config); + ++extern int totemknet_nodestatus_get (void *knet_context, unsigned int nodeid, ++ struct totem_node_status *node_status); ++ + extern int totemknet_ifaces_get (void *net_context, + char ***status, + unsigned int *iface_count); +diff --git a/exec/totemnet.c b/exec/totemnet.c +index ae44dbf8..a4b90a3d 100644 +--- a/exec/totemnet.c ++++ b/exec/totemnet.c +@@ -115,6 +115,11 @@ struct transport { + char ***status, + unsigned int *iface_count); + ++ int (*nodestatus_get) ( ++ void *transport_context, ++ unsigned int nodeid, ++ struct totem_node_status *node_status); ++ + int (*token_target_set) ( + void *transport_context, + unsigned int nodeid); +@@ -179,6 +184,7 @@ struct transport transport_entries[] = { + .finalize = totemudp_finalize, + .net_mtu_adjust = totemudp_net_mtu_adjust, + .ifaces_get = totemudp_ifaces_get, ++ .nodestatus_get = totemudp_nodestatus_get, + .token_target_set = totemudp_token_target_set, + .crypto_set = totemudp_crypto_set, + .recv_mcast_empty = totemudp_recv_mcast_empty, +@@ -203,6 +209,7 @@ struct transport transport_entries[] = { + .finalize = totemudpu_finalize, + .net_mtu_adjust = totemudpu_net_mtu_adjust, + .ifaces_get = totemudpu_ifaces_get, ++ .nodestatus_get = totemudpu_nodestatus_get, + .token_target_set = totemudpu_token_target_set, + .crypto_set = totemudpu_crypto_set, + .recv_mcast_empty = totemudpu_recv_mcast_empty, +@@ -227,6 +234,7 @@ struct transport transport_entries[] = { + .finalize = totemknet_finalize, + .net_mtu_adjust = totemknet_net_mtu_adjust, + .ifaces_get = totemknet_ifaces_get, ++ .nodestatus_get = totemknet_nodestatus_get, + .token_target_set = totemknet_token_target_set, + .crypto_set = totemknet_crypto_set, + .recv_mcast_empty = totemknet_recv_mcast_empty, +@@ -473,6 +481,19 @@ int totemnet_iface_set (void *net_context, + return (res); + } + ++extern int totemnet_nodestatus_get ( ++ void *net_context, ++ unsigned int nodeid, ++ struct totem_node_status *node_status) ++{ ++ struct totemnet_instance *instance = (struct totemnet_instance *)net_context; ++ unsigned int res; ++ ++ res = instance->transport->nodestatus_get (instance->transport_context, nodeid, node_status); ++ ++ return (res); ++} ++ + int totemnet_ifaces_get ( + void *net_context, + char ***status, +diff --git a/exec/totemnet.h b/exec/totemnet.h +index 46c1dd8d..c6a99235 100644 +--- a/exec/totemnet.h ++++ b/exec/totemnet.h +@@ -125,6 +125,11 @@ extern void totemnet_stats_clear (void *net_context); + + extern const char *totemnet_iface_print (void *net_context); + ++extern int totemnet_nodestatus_get ( ++ void *net_context, ++ unsigned int nodeid, ++ struct totem_node_status *node_status); ++ + extern int totemnet_ifaces_get ( + void *net_context, + char ***status, +diff --git a/exec/totempg.c b/exec/totempg.c +index 7b1f755e..a2484323 100644 +--- a/exec/totempg.c ++++ b/exec/totempg.c +@@ -1447,6 +1447,13 @@ int totempg_iface_set ( + return (res); + } + ++int totempg_nodestatus_get (unsigned int nodeid, ++ struct totem_node_status *node_status) ++{ ++ memset(node_status, 0, sizeof(struct totem_node_status)); ++ return totemsrp_nodestatus_get (totemsrp_context, nodeid, node_status); ++} ++ + int totempg_ifaces_get ( + unsigned int nodeid, + unsigned int *interface_id, +diff --git a/exec/totemsrp.c b/exec/totemsrp.c +index 0dadf521..949d367b 100644 +--- a/exec/totemsrp.c ++++ b/exec/totemsrp.c +@@ -1039,6 +1039,27 @@ void totemsrp_finalize ( + free (instance); + } + ++int totemsrp_nodestatus_get ( ++ void *srp_context, ++ unsigned int nodeid, ++ struct totem_node_status *node_status) ++{ ++ struct totemsrp_instance *instance = (struct totemsrp_instance *)srp_context; ++ int i; ++ ++ node_status->version = TOTEM_NODE_STATUS_STRUCTURE_VERSION; ++ ++ /* Fill in 'reachable' here as the lower level UDP[u] layers don't know */ ++ for (i = 0; i < instance->my_proc_list_entries; i++) { ++ if (instance->my_proc_list[i].nodeid == nodeid) { ++ node_status->reachable = 1; ++ } ++ } ++ ++ return totemnet_nodestatus_get(instance->totemnet_context, nodeid, node_status); ++} ++ ++ + /* + * Return configured interfaces. interfaces is array of totem_ip addresses allocated by caller, + * with interaces_size number of items. iface_count is final number of interfaces filled by this +diff --git a/exec/totemsrp.h b/exec/totemsrp.h +index c8c1c45c..49e00955 100644 +--- a/exec/totemsrp.h ++++ b/exec/totemsrp.h +@@ -101,6 +101,9 @@ void totemsrp_event_signal (void *srp_context, enum totem_event_type type, int v + + extern void totemsrp_net_mtu_adjust (struct totem_config *totem_config); + ++extern int totemsrp_nodestatus_get (void *srp_context, unsigned int nodeid, ++ struct totem_node_status *node_status); ++ + extern int totemsrp_ifaces_get ( + void *srp_context, + unsigned int nodeid, +diff --git a/exec/totemudp.c b/exec/totemudp.c +index 749fc7e8..fd3215b5 100644 +--- a/exec/totemudp.c ++++ b/exec/totemudp.c +@@ -1334,6 +1334,35 @@ extern int totemudp_iface_check (void *udp_context) + return (res); + } + ++int totemudp_nodestatus_get (void *udp_context, unsigned int nodeid, ++ struct totem_node_status *node_status) ++{ ++ struct totemudp_instance *instance = (struct totemudp_instance *)udp_context; ++ struct qb_list_head *list; ++ struct totemudp_member *member; ++ ++ qb_list_for_each(list, &(instance->member_list)) { ++ member = qb_list_entry (list, ++ struct totemudp_member, ++ list); ++ ++ if (member->member.nodeid == nodeid) { ++ node_status->nodeid = nodeid; ++ /* reachable is filled in by totemsrp */ ++ node_status->link_status[0].enabled = 1; ++ if (instance->netif_bind_state == BIND_STATE_REGULAR) { ++ node_status->link_status[0].enabled = 1; ++ } else { ++ node_status->link_status[0].enabled = 0; ++ } ++ node_status->link_status[0].connected = node_status->reachable; ++ node_status->link_status[0].mtu = instance->totem_config->net_mtu; ++ strncpy(node_status->link_status[0].src_ipaddr, totemip_print(&member->member), KNET_MAX_HOST_LEN-1); ++ } ++ } ++ return (0); ++} ++ + int totemudp_ifaces_get ( + void *net_context, + char ***status, +diff --git a/exec/totemudp.h b/exec/totemudp.h +index d4a01f64..7d2abcd9 100644 +--- a/exec/totemudp.h ++++ b/exec/totemudp.h +@@ -92,6 +92,9 @@ extern int totemudp_mcast_noflush_send ( + const void *msg, + unsigned int msg_len); + ++extern int totemudp_nodestatus_get (void *net_context, unsigned int nodeid, ++ struct totem_node_status *node_status); ++ + extern int totemudp_ifaces_get (void *net_context, + char ***status, + unsigned int *iface_count); +diff --git a/exec/totemudpu.c b/exec/totemudpu.c +index 914a3285..d095d46d 100644 +--- a/exec/totemudpu.c ++++ b/exec/totemudpu.c +@@ -793,6 +793,34 @@ static int totemudpu_build_sockets_ip ( + return 0; + } + ++int totemudpu_nodestatus_get (void *udpu_context, unsigned int nodeid, ++ struct totem_node_status *node_status) ++{ ++ struct totemudpu_instance *instance = (struct totemudpu_instance *)udpu_context; ++ struct qb_list_head *list; ++ struct totemudpu_member *member; ++ ++ qb_list_for_each(list, &(instance->member_list)) { ++ member = qb_list_entry (list, ++ struct totemudpu_member, ++ list); ++ ++ if (member->member.nodeid == nodeid) { ++ node_status->nodeid = nodeid; ++ /* reachable is filled in by totemsrp */ ++ if (instance->netif_bind_state == BIND_STATE_REGULAR) { ++ node_status->link_status[0].enabled = 1; ++ } else { ++ node_status->link_status[0].enabled = 0; ++ } ++ node_status->link_status[0].connected = node_status->reachable; ++ node_status->link_status[0].mtu = instance->totem_config->net_mtu; ++ strncpy(node_status->link_status[0].src_ipaddr, totemip_print(&member->member), KNET_MAX_HOST_LEN-1); ++ } ++ } ++ return (0); ++} ++ + int totemudpu_ifaces_get ( + void *net_context, + char ***status, +diff --git a/exec/totemudpu.h b/exec/totemudpu.h +index 47ee4772..07e63459 100644 +--- a/exec/totemudpu.h ++++ b/exec/totemudpu.h +@@ -92,6 +92,9 @@ extern int totemudpu_mcast_noflush_send ( + const void *msg, + unsigned int msg_len); + ++extern int totemudpu_nodestatus_get (void *net_context, unsigned int nodeid, ++ struct totem_node_status *node_status); ++ + extern int totemudpu_ifaces_get (void *net_context, + char ***status, + unsigned int *iface_count); +diff --git a/include/corosync/cfg.h b/include/corosync/cfg.h +index fa967b0a..c9cd06d0 100644 +--- a/include/corosync/cfg.h ++++ b/include/corosync/cfg.h +@@ -162,6 +162,43 @@ corosync_cfg_ring_status_get ( + char ***status, + unsigned int *interface_count); + ++#define CFG_NODE_STATUS_STRUCT_VERSION 1 ++#define CFG_MAX_HOST_LEN 256 ++#define CFG_MAX_LINKS 8 ++struct corosync_knet_link_status { ++ uint8_t enabled; /* link is configured and admin enabled for traffic */ ++ uint8_t connected; /* link is connected for data (local view) */ ++ uint8_t dynconnected; /* link has been activated by remote dynip */ ++ unsigned int mtu; /* current detected MTU on this link */ ++ char src_ipaddr[CFG_MAX_HOST_LEN]; ++ char dst_ipaddr[CFG_MAX_HOST_LEN]; ++}; ++ ++struct corosync_knet_node_status { ++ uint32_t version; ++ unsigned int nodeid; ++ uint8_t reachable; ++ uint8_t remote; ++ uint8_t external; ++ uint8_t onwire_min; ++ uint8_t onwire_max; ++ uint8_t onwire_ver; ++ struct corosync_knet_link_status link_status[CFG_MAX_LINKS]; ++}; ++ ++/** ++ * @brief corosync_cfg_node_status_get ++ * @param cfg_handle ++ * @param nodeid ++ * @param node_status ++ * @return ++ */ ++cs_error_t ++corosync_cfg_node_status_get ( ++ corosync_cfg_handle_t cfg_handle, ++ unsigned int nodeid, ++ struct corosync_knet_node_status *node_status); ++ + /** + * @brief corosync_cfg_kill_node + * @param cfg_handle +diff --git a/include/corosync/ipc_cfg.h b/include/corosync/ipc_cfg.h +index 79da02e0..b4ac9fc5 100644 +--- a/include/corosync/ipc_cfg.h ++++ b/include/corosync/ipc_cfg.h +@@ -59,7 +59,8 @@ enum req_lib_cfg_types { + MESSAGE_REQ_CFG_GET_NODE_ADDRS = 5, + MESSAGE_REQ_CFG_LOCAL_GET = 6, + MESSAGE_REQ_CFG_RELOAD_CONFIG = 7, +- MESSAGE_REQ_CFG_REOPEN_LOG_FILES = 8 ++ MESSAGE_REQ_CFG_REOPEN_LOG_FILES = 8, ++ MESSAGE_REQ_CFG_NODESTATUSGET = 9 + }; + + /** +@@ -81,7 +82,8 @@ enum res_lib_cfg_types { + MESSAGE_RES_CFG_LOCAL_GET = 12, + MESSAGE_RES_CFG_REPLYTOSHUTDOWN = 13, + MESSAGE_RES_CFG_RELOAD_CONFIG = 14, +- MESSAGE_RES_CFG_REOPEN_LOG_FILES = 15 ++ MESSAGE_RES_CFG_REOPEN_LOG_FILES = 15, ++ MESSAGE_RES_CFG_NODESTATUSGET = 16 + }; + + /** +@@ -101,6 +103,23 @@ struct res_lib_cfg_ringstatusget { + char interface_status[CFG_MAX_INTERFACES][CFG_INTERFACE_STATUS_MAX_LEN] __attribute__((aligned(8))); + }; + ++/** ++ * @brief The req_lib_cfg_nodestatusget struct ++ */ ++struct req_lib_cfg_nodestatusget { ++ struct qb_ipc_request_header header __attribute__((aligned(8))); ++ unsigned int nodeid __attribute__((aligned(8))); ++ mar_uint32_t version __attribute__((aligned(8))); ++}; ++ ++/** ++ * @brief The res_lib_cfg_nodestatusget struct ++ */ ++struct res_lib_cfg_nodestatusget { ++ struct qb_ipc_response_header header __attribute__((aligned(8))); ++ struct corosync_knet_node_status node_status __attribute__((aligned(8))); ++}; ++ + /** + * @brief The req_lib_cfg_ringreenable struct + */ +diff --git a/include/corosync/totem/totem.h b/include/corosync/totem/totem.h +index 6f43527a..8b166566 100644 +--- a/include/corosync/totem/totem.h ++++ b/include/corosync/totem/totem.h +@@ -253,6 +253,25 @@ struct totem_config { + unsigned int nodeid); + }; + ++/* ++ * Node status returned from the API ++ * Usually the same as the cfg version (except for ++ * link_status) ++ */ ++#define TOTEM_NODE_STATUS_STRUCTURE_VERSION 1 ++struct totem_node_status { ++ uint32_t version; /* Structure version */ ++ unsigned int nodeid; ++ uint8_t reachable; ++ uint8_t remote; ++ uint8_t external; ++ uint8_t onwire_min; ++ uint8_t onwire_max; ++ uint8_t onwire_ver; ++ struct knet_link_status link_status[KNET_MAX_LINK]; ++}; ++ ++ + #define TOTEM_CONFIGURATION_TYPE + enum totem_configuration_type { + TOTEM_CONFIGURATION_REGULAR, +diff --git a/include/corosync/totem/totempg.h b/include/corosync/totem/totempg.h +index af9bf71f..d63540cf 100644 +--- a/include/corosync/totem/totempg.h ++++ b/include/corosync/totem/totempg.h +@@ -146,6 +146,9 @@ extern int totempg_ifaces_get ( + char ***status, + unsigned int *iface_count); + ++extern int totempg_nodestatus_get (unsigned int nodeid, ++ struct totem_node_status *node_status); ++ + extern void* totempg_get_stats (void); + + void totempg_event_signal (enum totem_event_type type, int value); +diff --git a/lib/cfg.c b/lib/cfg.c +index 8a01c589..16ce6be5 100644 +--- a/lib/cfg.c ++++ b/lib/cfg.c +@@ -1,6 +1,6 @@ + /* + * Copyright (c) 2002-2005 MontaVista Software, Inc. +- * Copyright (c) 2006-2018 Red Hat, Inc. ++ * Copyright (c) 2006-2020 Red Hat, Inc. + * + * All rights reserved. + * +@@ -367,6 +367,56 @@ exit_handle_put: + return (error); + } + ++cs_error_t ++corosync_cfg_node_status_get ( ++ corosync_cfg_handle_t cfg_handle, ++ unsigned int nodeid, ++ struct corosync_knet_node_status *node_status) ++{ ++ struct cfg_inst *cfg_inst; ++ struct req_lib_cfg_nodestatusget req_lib_cfg_nodestatusget; ++ struct res_lib_cfg_nodestatusget res_lib_cfg_nodestatusget; ++ cs_error_t error; ++ struct iovec iov; ++ ++ if (!node_status) { ++ return (CS_ERR_INVALID_PARAM); ++ } ++ ++ error = hdb_error_to_cs(hdb_handle_get (&cfg_hdb, cfg_handle, (void *)&cfg_inst)); ++ if (error != CS_OK) { ++ return (error); ++ } ++ ++ req_lib_cfg_nodestatusget.header.size = sizeof (struct req_lib_cfg_nodestatusget); ++ req_lib_cfg_nodestatusget.header.id = MESSAGE_REQ_CFG_NODESTATUSGET; ++ req_lib_cfg_nodestatusget.nodeid = nodeid; ++ req_lib_cfg_nodestatusget.version = CFG_NODE_STATUS_STRUCT_VERSION; ++ ++ iov.iov_base = (void *)&req_lib_cfg_nodestatusget, ++ iov.iov_len = sizeof (struct req_lib_cfg_nodestatusget), ++ ++ error = qb_to_cs_error (qb_ipcc_sendv_recv(cfg_inst->c, ++ &iov, ++ 1, ++ &res_lib_cfg_nodestatusget, ++ sizeof (struct res_lib_cfg_nodestatusget), CS_IPC_TIMEOUT_MS)); ++ ++ if (error == CS_OK) { ++ memcpy(node_status, &res_lib_cfg_nodestatusget.node_status, sizeof(struct corosync_knet_node_status)); ++ } ++ ++ /* corosync sent us something we don't really understand. ++ - we might need to revisit this in the case of future structure versions */ ++ if (res_lib_cfg_nodestatusget.node_status.version != CFG_NODE_STATUS_STRUCT_VERSION) { ++ error = CS_ERR_NOT_SUPPORTED; ++ } ++ ++ (void)hdb_handle_put (&cfg_hdb, cfg_handle); ++ ++ return (error); ++} ++ + cs_error_t + corosync_cfg_kill_node ( + corosync_cfg_handle_t cfg_handle, +diff --git a/lib/libcfg.versions b/lib/libcfg.versions +index a87727c0..8fba9184 100644 +--- a/lib/libcfg.versions ++++ b/lib/libcfg.versions +@@ -11,6 +11,7 @@ COROSYNC_CFG_0.82 { + corosync_cfg_track; + corosync_cfg_track_stop; + corosync_cfg_ring_status_get; ++ corosync_cfg_node_status_get; + corosync_cfg_ring_reenable; + corosync_cfg_service_load; + corosync_cfg_service_unload; +diff --git a/lib/libcfg.verso b/lib/libcfg.verso +index a3fcc712..0ee843cc 100644 +--- a/lib/libcfg.verso ++++ b/lib/libcfg.verso +@@ -1 +1 @@ +-7.1.0 ++7.2.0 +diff --git a/man/corosync-cfgtool.8 b/man/corosync-cfgtool.8 +index 4ec074ad..007cbbe3 100644 +--- a/man/corosync-cfgtool.8 ++++ b/man/corosync-cfgtool.8 +@@ -35,7 +35,7 @@ + .SH "NAME" + corosync-cfgtool \- An administrative tool for corosync. + .SH "SYNOPSIS" +-.B corosync\-cfgtool [[\-i IP_address] [\-b] \-s] [\-R] [\-L] [\-k nodeid] [\-a nodeid] [\-h] [\-H] ++.B corosync\-cfgtool [[\-i IP_address] [\-b] [\-s] [\-n] [\-R] [\-L] [\-k nodeid] [\-a nodeid] [\-h] [\-H] + .SH "DESCRIPTION" + .B corosync\-cfgtool + A tool for displaying and configuring active parameters within corosync. +@@ -48,7 +48,7 @@ Finds only information about the specified interface IP address or link id with + Displays the status of the current links on this node for UDP/UDPU, with extended status + for KNET. + After each link, the nodes on that link are displayed in order with their status, +-for example there are 3 nodes with KNET transportation: ++for example there are 3 nodes with KNET transport: + + LINK ID 0 + addr = 192.168.100.80 +@@ -58,14 +58,14 @@ LINK ID 0 + nodeid 3: connected + .TP + .B -b +-Displays the brief status of the current links on this node (KNET only) when used ++Displays the brief status of the current links on this node when used + with "-s". If any interfaces are faulty, 1 is returned by the binary. If all interfaces + are active 0 is returned to the shell. + After each link, the nodes on that link are displayed in order with their status + encoded into a single digit, or characters 'n', 'd' and '?' with special meaning. + 1=link enabled, 2=link connected, So a 3 in a node position indicates that the + link is both enabled and connected. Status represented by character 'n' is used for +-localhost link. Character '?' means that Crosync was unable to get status of link from knet (log ++localhost link. Character '?' means that Corosync was unable to get status of link from knet (log + should contain more information). Character 'd' shouldn't appear and it means that Corosync + was unable to configure a link and it is result of some error which should have been logged. + +@@ -75,6 +75,32 @@ LINK ID 0 + addr = 192.168.100.80 + status = n33 + .TP ++.B -n ++Displays the status of the current nodes in the system with their link status(es). ++.P ++.nf ++Local node ID 1, transport knet ++nodeid: 2 reachable onwire (min/max/cur): 0, 1, 1 ++ LINK: 0 (192.168.1.101->192.168.1.102) enabled connected mtu: 1397 ++ LINK: 1 (192.168.4.1->192.168.4.2) enabled mtu: 469 ++ LINK: 2 (192.168.9.1->192.168.9.2) enabled mtu: 469 ++.fi ++.P ++Only reachable nodes are displayed so "reachable" should always be there. ++.br ++'onwire' versions are the knet on-wire versions that are supported/in use (where appropriate). ++.br ++IP addresses are the local and remote IP addresses (for UDP[U] only the local IP address is shown) ++.br ++enabled - means the link has been brought up ++.br ++connected - means that the link is connected to the remote node ++.br ++dynconnected - is not currently implemented ++.br ++mtu - shows the size of data packets. Should be the link packet size less a small amount ++for protocol overheads and encryption ++.TP + .B -R + Tell all instances of corosync in this cluster to reload corosync.conf. + +diff --git a/tools/corosync-cfgtool.c b/tools/corosync-cfgtool.c +index d920960b..c4f23f79 100644 +--- a/tools/corosync-cfgtool.c ++++ b/tools/corosync-cfgtool.c +@@ -71,6 +71,7 @@ + enum user_action { + ACTION_NOOP=0, + ACTION_LINKSTATUS_GET, ++ ACTION_NODESTATUS_GET, + ACTION_RELOAD_CONFIG, + ACTION_REOPEN_LOG_FILES, + ACTION_SHUTDOW, +@@ -89,35 +90,35 @@ static int node_compare(const void *aptr, const void *bptr) + } + + static int +-linkstatusget_do (char *interface_name, int brief) ++nodestatusget_do (enum user_action action, int brief) + { + cs_error_t result; + corosync_cfg_handle_t handle; + cmap_handle_t cmap_handle; +- unsigned int interface_count; +- char **interface_names; +- char **interface_status; +- uint32_t nodeid_list[KNET_MAX_HOST]; + char iter_key[CMAP_KEYNAME_MAXLEN]; +- unsigned int i; + cmap_iter_handle_t iter; ++ unsigned int local_nodeid; ++ unsigned int local_nodeid_index=0; ++ unsigned int other_nodeid_index=0; + unsigned int nodeid; + int nodeid_match_guard; + cmap_value_types_t type; + size_t value_len; +- int rc = EXIT_SUCCESS; +- int len, s = 0, t; +- char stat_ch; + char *str; +- totem_transport_t transport_number = TOTEM_TRANSPORT_KNET; +- int no_match = 1; ++ char *transport_str = NULL; ++ uint32_t nodeid_list[KNET_MAX_HOST]; ++ int s = 0; ++ int rc = EXIT_SUCCESS; ++ int transport_number = TOTEM_TRANSPORT_KNET; ++ int i,j; ++ struct corosync_knet_node_status node_status; + +- printf ("Printing link status.\n"); + result = corosync_cfg_initialize (&handle, NULL); + if (result != CS_OK) { + fprintf (stderr, "Could not initialize corosync configuration API error %d\n", result); + exit (EXIT_FAILURE); + } ++ + result = cmap_initialize (&cmap_handle); + if (result != CS_OK) { + fprintf (stderr, "Could not initialize corosync cmap API error %d\n", result); +@@ -132,7 +133,19 @@ linkstatusget_do (char *interface_name, int brief) + if (strcmp (str, "udp") == 0) { + transport_number = TOTEM_TRANSPORT_UDP; + } +- free(str); ++ transport_str = str; ++ } ++ if (!transport_str) { ++ transport_str = strdup("knet"); /* It's the default */ ++ } ++ ++ result = corosync_cfg_local_get(handle, &local_nodeid); ++ if (result != CS_OK) { ++ fprintf (stderr, "Could not get the local node id, the error is: %d\n", result); ++ free(transport_str); ++ cmap_finalize(cmap_handle); ++ corosync_cfg_finalize(handle); ++ return EXIT_FAILURE; + } + + /* Get a list of nodes. We do it this way rather than using votequorum as cfgtool +@@ -141,6 +154,9 @@ linkstatusget_do (char *interface_name, int brief) + result = cmap_iter_init(cmap_handle, "nodelist.node.", &iter); + if (result != CS_OK) { + fprintf (stderr, "Could not get nodelist from cmap. error %d\n", result); ++ free(transport_str); ++ cmap_finalize(cmap_handle); ++ corosync_cfg_finalize(handle); + exit (EXIT_FAILURE); + } + +@@ -154,140 +170,120 @@ linkstatusget_do (char *interface_name, int brief) + continue; + } + if (cmap_get_uint32(cmap_handle, iter_key, &nodeid) == CS_OK) { ++ if (nodeid == local_nodeid) { ++ local_nodeid_index = s; ++ } else { ++ /* Bit of an odd one this. but local node only uses one link (of course, to itself) ++ so if we want to know which links are active across the cluster we need to look ++ at another node (any other) node's link list */ ++ other_nodeid_index = s; ++ } + nodeid_list[s++] = nodeid; + } + } +- +- /* totemknet returns nodes in nodeid order - even though it doesn't tell us +- what the nodeid is. So sort our node list and we can then look up +- knet node pos to get an actual nodeid. +- Yep, I really should have totally rewritten the cfg interface for this. +- */ ++ /* It's nice to have these in nodeid order */ + qsort(nodeid_list, s, sizeof(uint32_t), node_compare); + +- result = corosync_cfg_local_get(handle, &nodeid); +- if (result != CS_OK) { +- fprintf (stderr, "Could not get the local node id, the error is: %d\n", result); ++ cmap_finalize(cmap_handle); ++ ++ printf ("Local node ID " CS_PRI_NODE_ID ", transport %s\n", local_nodeid, transport_str); ++ ++ /* If node status requested then do print node-based info */ ++ if (action == ACTION_NODESTATUS_GET) { ++ for (i=0; i":"", ++ node_status.link_status[j].dst_ipaddr); ++ if (node_status.link_status[j].enabled) { ++ printf(" enabled"); ++ } ++ if (node_status.link_status[j].connected) { ++ printf(" connected"); ++ } ++ if (node_status.link_status[j].dynconnected) { ++ printf(" dynconnected"); ++ } ++ printf(" mtu: %d\n", node_status.link_status[j].mtu); ++ } ++ } ++ printf("\n"); ++ } ++ } ++ } + } ++ /* Print in link order */ + else { +- printf ("Local node ID " CS_PRI_NODE_ID "\n", nodeid); +- } ++ struct corosync_knet_node_status node_info[s]; ++ memset(node_info, 0, sizeof(node_info)); + +- result = corosync_cfg_ring_status_get (handle, +- &interface_names, +- &interface_status, +- &interface_count); +- if (result != CS_OK) { +- fprintf (stderr, "Could not get the link status, the error is: %d\n", result); +- } else { +- for (i = 0; i < interface_count; i++) { +- char *cur_iface_name_space = strchr(interface_names[i], ' '); +- int show_current_iface; +- +- s = 0; +- /* +- * Interface_name is " " +- * separate them out +- */ +- if (!cur_iface_name_space) { +- continue; +- } +- *cur_iface_name_space = '\0'; +- +- show_current_iface = 1; +- if (interface_name != NULL && interface_name[0] != '\0' && +- strcmp(interface_name, interface_names[i]) != 0 && +- strcmp(interface_name, cur_iface_name_space + 1) != 0) { +- show_current_iface = 0; ++ for (i=0; i +Date: Tue, 24 Nov 2020 12:20:25 +0100 +Subject: [PATCH 6/6] cfg: Improve nodestatusget versioning + +Patch tries to make nodestatusget really extendable. Following changes +are implemented: +- corosync_cfg_node_status_version_t is added with (for now) single + value CFG_NODE_STATUS_V1 +- corosync_knet_node_status renamed to corosync_cfg_node_status_v1 (it + isn't really knet because it works as well for udp(u() +- struct res_lib_cfg_nodestatusget_version is added which holds only ipc + result header and version on same position as for + corosync_cfg_node_status_v1 +- corosync_cfg_node_status_get requires version and pointer to one of + corosync_cfg_node_status_v structures +- request is handled in case switches to make adding new version easier + +Also fix following bugs: +- totempg_nodestatus_get error was retyped to cs_error_t without any + meaning. +- header.error was not checked at all in the library + +Signed-off-by: Jan Friesse +Reviewed-by: Christine Caulfield +Signed-off-by: Thomas Lamprecht +--- + exec/cfg.c | 89 ++++++++++++++++++++++++-------------- + include/corosync/cfg.h | 17 +++++--- + include/corosync/ipc_cfg.h | 9 +++- + lib/cfg.c | 50 ++++++++++++++++----- + tools/corosync-cfgtool.c | 8 ++-- + 5 files changed, 118 insertions(+), 55 deletions(-) + +diff --git a/exec/cfg.c b/exec/cfg.c +index c300cc8f..991baf22 100644 +--- a/exec/cfg.c ++++ b/exec/cfg.c +@@ -971,53 +971,76 @@ static void message_handler_req_lib_cfg_nodestatusget ( + void *conn, + const void *msg) + { +- struct res_lib_cfg_nodestatusget res_lib_cfg_nodestatusget; ++ struct res_lib_cfg_nodestatusget_version res_lib_cfg_nodestatusget_version; ++ struct res_lib_cfg_nodestatusget_v1 res_lib_cfg_nodestatusget_v1; ++ void *res_lib_cfg_nodestatusget_ptr = NULL; ++ size_t res_lib_cfg_nodestatusget_size; + struct req_lib_cfg_nodestatusget *req_lib_cfg_nodestatusget = (struct req_lib_cfg_nodestatusget *)msg; + struct totem_node_status node_status; +- cs_error_t res = CS_OK; + int i; + + ENTER(); + ++ memset(&node_status, 0, sizeof(node_status)); ++ if (totempg_nodestatus_get(req_lib_cfg_nodestatusget->nodeid, &node_status) != 0) { ++ res_lib_cfg_nodestatusget_ptr = &res_lib_cfg_nodestatusget_version; ++ res_lib_cfg_nodestatusget_size = sizeof(res_lib_cfg_nodestatusget_version); ++ ++ res_lib_cfg_nodestatusget_version.header.error = CS_ERR_FAILED_OPERATION; ++ res_lib_cfg_nodestatusget_version.header.id = MESSAGE_RES_CFG_NODESTATUSGET; ++ res_lib_cfg_nodestatusget_version.header.size = res_lib_cfg_nodestatusget_size; ++ ++ goto ipc_response_send; ++ } ++ + /* Currently only one structure version supported */ +- if (req_lib_cfg_nodestatusget->version == TOTEM_NODE_STATUS_STRUCTURE_VERSION) +- { +- res_lib_cfg_nodestatusget.header.id = MESSAGE_RES_CFG_NODESTATUSGET; +- res_lib_cfg_nodestatusget.header.size = sizeof (struct res_lib_cfg_nodestatusget); ++ switch (req_lib_cfg_nodestatusget->version) { ++ case CFG_NODE_STATUS_V1: ++ res_lib_cfg_nodestatusget_ptr = &res_lib_cfg_nodestatusget_v1; ++ res_lib_cfg_nodestatusget_size = sizeof(res_lib_cfg_nodestatusget_v1); + +- memset(&node_status, 0, sizeof(node_status)); +- res = totempg_nodestatus_get(req_lib_cfg_nodestatusget->nodeid, +- &node_status); +- if (res == 0) { +- res_lib_cfg_nodestatusget.node_status.nodeid = req_lib_cfg_nodestatusget->nodeid; +- res_lib_cfg_nodestatusget.node_status.version = node_status.version; +- res_lib_cfg_nodestatusget.node_status.reachable = node_status.reachable; +- res_lib_cfg_nodestatusget.node_status.remote = node_status.remote; +- res_lib_cfg_nodestatusget.node_status.external = node_status.external; +- res_lib_cfg_nodestatusget.node_status.onwire_min = node_status.onwire_min; +- res_lib_cfg_nodestatusget.node_status.onwire_max = node_status.onwire_max; +- res_lib_cfg_nodestatusget.node_status.onwire_ver= node_status.onwire_ver; ++ res_lib_cfg_nodestatusget_v1.header.error = CS_OK; ++ res_lib_cfg_nodestatusget_v1.header.id = MESSAGE_RES_CFG_NODESTATUSGET; ++ res_lib_cfg_nodestatusget_v1.header.size = res_lib_cfg_nodestatusget_size; + +- for (i=0; i < KNET_MAX_LINK; i++) { +- res_lib_cfg_nodestatusget.node_status.link_status[i].enabled = node_status.link_status[i].enabled; +- res_lib_cfg_nodestatusget.node_status.link_status[i].connected = node_status.link_status[i].connected; +- res_lib_cfg_nodestatusget.node_status.link_status[i].dynconnected = node_status.link_status[i].dynconnected; +- res_lib_cfg_nodestatusget.node_status.link_status[i].mtu = node_status.link_status[i].mtu; +- memcpy(res_lib_cfg_nodestatusget.node_status.link_status[i].src_ipaddr, +- node_status.link_status[i].src_ipaddr, CFG_MAX_HOST_LEN); +- memcpy(res_lib_cfg_nodestatusget.node_status.link_status[i].dst_ipaddr, +- node_status.link_status[i].dst_ipaddr, CFG_MAX_HOST_LEN); +- } ++ res_lib_cfg_nodestatusget_v1.node_status.version = CFG_NODE_STATUS_V1; ++ res_lib_cfg_nodestatusget_v1.node_status.nodeid = req_lib_cfg_nodestatusget->nodeid; ++ res_lib_cfg_nodestatusget_v1.node_status.reachable = node_status.reachable; ++ res_lib_cfg_nodestatusget_v1.node_status.remote = node_status.remote; ++ res_lib_cfg_nodestatusget_v1.node_status.external = node_status.external; ++ res_lib_cfg_nodestatusget_v1.node_status.onwire_min = node_status.onwire_min; ++ res_lib_cfg_nodestatusget_v1.node_status.onwire_max = node_status.onwire_max; ++ res_lib_cfg_nodestatusget_v1.node_status.onwire_ver = node_status.onwire_ver; ++ ++ for (i=0; i < KNET_MAX_LINK; i++) { ++ res_lib_cfg_nodestatusget_v1.node_status.link_status[i].enabled = node_status.link_status[i].enabled; ++ res_lib_cfg_nodestatusget_v1.node_status.link_status[i].connected = node_status.link_status[i].connected; ++ res_lib_cfg_nodestatusget_v1.node_status.link_status[i].dynconnected = node_status.link_status[i].dynconnected; ++ res_lib_cfg_nodestatusget_v1.node_status.link_status[i].mtu = node_status.link_status[i].mtu; ++ memcpy(res_lib_cfg_nodestatusget_v1.node_status.link_status[i].src_ipaddr, ++ node_status.link_status[i].src_ipaddr, CFG_MAX_HOST_LEN); ++ memcpy(res_lib_cfg_nodestatusget_v1.node_status.link_status[i].dst_ipaddr, ++ node_status.link_status[i].dst_ipaddr, CFG_MAX_HOST_LEN); + } +- } else { +- res = CS_ERR_NOT_SUPPORTED; ++ break; ++ default: ++ /* ++ * Unsupported version requested ++ */ ++ res_lib_cfg_nodestatusget_ptr = &res_lib_cfg_nodestatusget_version; ++ res_lib_cfg_nodestatusget_size = sizeof(res_lib_cfg_nodestatusget_version); ++ ++ res_lib_cfg_nodestatusget_version.header.error = CS_ERR_NOT_SUPPORTED; ++ res_lib_cfg_nodestatusget_version.header.id = MESSAGE_RES_CFG_NODESTATUSGET; ++ res_lib_cfg_nodestatusget_version.header.size = res_lib_cfg_nodestatusget_size; ++ break; + } + +- res_lib_cfg_nodestatusget.header.error = res; ++ipc_response_send: + api->ipc_response_send ( + conn, +- &res_lib_cfg_nodestatusget, +- sizeof (struct res_lib_cfg_nodestatusget)); ++ res_lib_cfg_nodestatusget_ptr, ++ res_lib_cfg_nodestatusget_size); + + LEAVE(); + } +diff --git a/include/corosync/cfg.h b/include/corosync/cfg.h +index c9cd06d0..a7babc28 100644 +--- a/include/corosync/cfg.h ++++ b/include/corosync/cfg.h +@@ -162,10 +162,14 @@ corosync_cfg_ring_status_get ( + char ***status, + unsigned int *interface_count); + +-#define CFG_NODE_STATUS_STRUCT_VERSION 1 ++typedef enum { ++ CFG_NODE_STATUS_V1 = 1, ++} corosync_cfg_node_status_version_t; ++ + #define CFG_MAX_HOST_LEN 256 + #define CFG_MAX_LINKS 8 +-struct corosync_knet_link_status { ++ ++struct corosync_knet_link_status_v1 { + uint8_t enabled; /* link is configured and admin enabled for traffic */ + uint8_t connected; /* link is connected for data (local view) */ + uint8_t dynconnected; /* link has been activated by remote dynip */ +@@ -174,8 +178,8 @@ struct corosync_knet_link_status { + char dst_ipaddr[CFG_MAX_HOST_LEN]; + }; + +-struct corosync_knet_node_status { +- uint32_t version; ++struct corosync_cfg_node_status_v1 { ++ corosync_cfg_node_status_version_t version; + unsigned int nodeid; + uint8_t reachable; + uint8_t remote; +@@ -183,7 +187,7 @@ struct corosync_knet_node_status { + uint8_t onwire_min; + uint8_t onwire_max; + uint8_t onwire_ver; +- struct corosync_knet_link_status link_status[CFG_MAX_LINKS]; ++ struct corosync_knet_link_status_v1 link_status[CFG_MAX_LINKS]; + }; + + /** +@@ -197,7 +201,8 @@ cs_error_t + corosync_cfg_node_status_get ( + corosync_cfg_handle_t cfg_handle, + unsigned int nodeid, +- struct corosync_knet_node_status *node_status); ++ corosync_cfg_node_status_version_t version, ++ void *node_status); + + /** + * @brief corosync_cfg_kill_node +diff --git a/include/corosync/ipc_cfg.h b/include/corosync/ipc_cfg.h +index b4ac9fc5..65285a68 100644 +--- a/include/corosync/ipc_cfg.h ++++ b/include/corosync/ipc_cfg.h +@@ -112,12 +112,17 @@ struct req_lib_cfg_nodestatusget { + mar_uint32_t version __attribute__((aligned(8))); + }; + ++struct res_lib_cfg_nodestatusget_version { ++ struct qb_ipc_response_header header __attribute__((aligned(8))); ++ corosync_cfg_node_status_version_t version __attribute__((aligned(8))); ++}; ++ + /** + * @brief The res_lib_cfg_nodestatusget struct + */ +-struct res_lib_cfg_nodestatusget { ++struct res_lib_cfg_nodestatusget_v1 { + struct qb_ipc_response_header header __attribute__((aligned(8))); +- struct corosync_knet_node_status node_status __attribute__((aligned(8))); ++ struct corosync_cfg_node_status_v1 node_status __attribute__((aligned(8))); + }; + + /** +diff --git a/lib/cfg.c b/lib/cfg.c +index 16ce6be5..4ce3582d 100644 +--- a/lib/cfg.c ++++ b/lib/cfg.c +@@ -371,18 +371,33 @@ cs_error_t + corosync_cfg_node_status_get ( + corosync_cfg_handle_t cfg_handle, + unsigned int nodeid, +- struct corosync_knet_node_status *node_status) ++ corosync_cfg_node_status_version_t version, ++ void *node_status) + { + struct cfg_inst *cfg_inst; + struct req_lib_cfg_nodestatusget req_lib_cfg_nodestatusget; +- struct res_lib_cfg_nodestatusget res_lib_cfg_nodestatusget; + cs_error_t error; + struct iovec iov; ++ size_t cfg_node_status_size; ++ void *res_lib_cfg_nodestatuget_ptr; ++ struct res_lib_cfg_nodestatusget_v1 res_lib_cfg_nodestatusget_v1; ++ struct res_lib_cfg_nodestatusget_version *res_lib_cfg_nodestatusget_version; + + if (!node_status) { + return (CS_ERR_INVALID_PARAM); + } + ++ switch (version) { ++ case CFG_NODE_STATUS_V1: ++ cfg_node_status_size = sizeof(struct res_lib_cfg_nodestatusget_v1); ++ res_lib_cfg_nodestatuget_ptr = &res_lib_cfg_nodestatusget_v1; ++ ++ break; ++ default: ++ return (CS_ERR_INVALID_PARAM); ++ break; ++ } ++ + error = hdb_error_to_cs(hdb_handle_get (&cfg_hdb, cfg_handle, (void *)&cfg_inst)); + if (error != CS_OK) { + return (error); +@@ -391,7 +406,7 @@ corosync_cfg_node_status_get ( + req_lib_cfg_nodestatusget.header.size = sizeof (struct req_lib_cfg_nodestatusget); + req_lib_cfg_nodestatusget.header.id = MESSAGE_REQ_CFG_NODESTATUSGET; + req_lib_cfg_nodestatusget.nodeid = nodeid; +- req_lib_cfg_nodestatusget.version = CFG_NODE_STATUS_STRUCT_VERSION; ++ req_lib_cfg_nodestatusget.version = version; + + iov.iov_base = (void *)&req_lib_cfg_nodestatusget, + iov.iov_len = sizeof (struct req_lib_cfg_nodestatusget), +@@ -399,19 +414,34 @@ corosync_cfg_node_status_get ( + error = qb_to_cs_error (qb_ipcc_sendv_recv(cfg_inst->c, + &iov, + 1, +- &res_lib_cfg_nodestatusget, +- sizeof (struct res_lib_cfg_nodestatusget), CS_IPC_TIMEOUT_MS)); ++ res_lib_cfg_nodestatuget_ptr, ++ cfg_node_status_size, CS_IPC_TIMEOUT_MS)); ++ if (error != CS_OK) { ++ goto error_put; ++ } + +- if (error == CS_OK) { +- memcpy(node_status, &res_lib_cfg_nodestatusget.node_status, sizeof(struct corosync_knet_node_status)); ++ res_lib_cfg_nodestatusget_version = res_lib_cfg_nodestatuget_ptr; ++ error = res_lib_cfg_nodestatusget_version->header.error; ++ if (error != CS_OK) { ++ goto error_put; + } + +- /* corosync sent us something we don't really understand. +- - we might need to revisit this in the case of future structure versions */ +- if (res_lib_cfg_nodestatusget.node_status.version != CFG_NODE_STATUS_STRUCT_VERSION) { ++ if (res_lib_cfg_nodestatusget_version->version != version) { ++ /* ++ * corosync sent us something we don't really understand. ++ */ + error = CS_ERR_NOT_SUPPORTED; ++ goto error_put; + } + ++ switch (version) { ++ case CFG_NODE_STATUS_V1: ++ memcpy(node_status, &res_lib_cfg_nodestatusget_v1.node_status, ++ sizeof(struct corosync_cfg_node_status_v1)); ++ break; ++ } ++ ++error_put: + (void)hdb_handle_put (&cfg_hdb, cfg_handle); + + return (error); +diff --git a/tools/corosync-cfgtool.c b/tools/corosync-cfgtool.c +index c4f23f79..0de50bd7 100644 +--- a/tools/corosync-cfgtool.c ++++ b/tools/corosync-cfgtool.c +@@ -111,7 +111,7 @@ nodestatusget_do (enum user_action action, int brief) + int rc = EXIT_SUCCESS; + int transport_number = TOTEM_TRANSPORT_KNET; + int i,j; +- struct corosync_knet_node_status node_status; ++ struct corosync_cfg_node_status_v1 node_status; + + result = corosync_cfg_initialize (&handle, NULL); + if (result != CS_OK) { +@@ -191,7 +191,7 @@ nodestatusget_do (enum user_action action, int brief) + /* If node status requested then do print node-based info */ + if (action == ACTION_NODESTATUS_GET) { + for (i=0; i