diff --git a/lib/common/ais.c b/lib/common/ais.c index e46999a49d..97d32e2198 100644 --- a/lib/common/ais.c +++ b/lib/common/ais.c @@ -1,657 +1,666 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include "stack.h" #ifdef SUPPORT_COROSYNC # include #endif enum crm_ais_msg_types text2msg_type(const char *text) { int type = crm_msg_none; CRM_CHECK(text != NULL, return type); if(safe_str_eq(text, "ais")) { type = crm_msg_ais; } else if(safe_str_eq(text, "crm_plugin")) { type = crm_msg_ais; } else if(safe_str_eq(text, CRM_SYSTEM_CIB)) { type = crm_msg_cib; } else if(safe_str_eq(text, CRM_SYSTEM_CRMD)) { type = crm_msg_crmd; } else if(safe_str_eq(text, CRM_SYSTEM_DC)) { type = crm_msg_crmd; } else if(safe_str_eq(text, CRM_SYSTEM_TENGINE)) { type = crm_msg_te; } else if(safe_str_eq(text, CRM_SYSTEM_PENGINE)) { type = crm_msg_pe; } else if(safe_str_eq(text, CRM_SYSTEM_LRMD)) { type = crm_msg_lrmd; } else if(safe_str_eq(text, CRM_SYSTEM_STONITHD)) { type = crm_msg_stonithd; } else if(safe_str_eq(text, "stonith-ng")) { type = crm_msg_stonith_ng; } else if(safe_str_eq(text, "attrd")) { type = crm_msg_attrd; } else { /* This will normally be a transient client rather than * a cluster daemon. Set the type to the pid of the client */ int scan_rc = sscanf(text, "%d", &type); if(scan_rc != 1) { /* Ensure its sane */ type = crm_msg_none; } } return type; } char *get_ais_data(const AIS_Message *msg) { int rc = BZ_OK; char *uncompressed = NULL; unsigned int new_size = msg->size + 1; if(msg->is_compressed == FALSE) { crm_debug_2("Returning uncompressed message data"); uncompressed = strdup(msg->data); } else { crm_debug_2("Decompressing message data"); crm_malloc0(uncompressed, new_size); rc = BZ2_bzBuffToBuffDecompress( uncompressed, &new_size, (char*)msg->data, msg->compressed_size, 1, 0); CRM_ASSERT(rc == BZ_OK); CRM_ASSERT(new_size == msg->size); } return uncompressed; } #if SUPPORT_COROSYNC int ais_fd_sync = -1; int ais_fd_async = -1; /* never send messages via this channel */ void *ais_ipc_ctx = NULL; hdb_handle_t ais_ipc_handle = 0; GFDSource *ais_source = NULL; GFDSource *ais_source_sync = NULL; static char *ais_cluster_name = NULL; gboolean get_ais_nodeid(uint32_t *id, char **uname) { struct iovec iov; int retries = 0; int rc = CS_OK; coroipc_response_header_t header; struct crm_ais_nodeid_resp_s answer; header.error = CS_OK; header.id = crm_class_nodeid; header.size = sizeof(coroipc_response_header_t); CRM_CHECK(id != NULL, return FALSE); CRM_CHECK(uname != NULL, return FALSE); iov.iov_base = &header; iov.iov_len = header.size; retry: errno = 0; rc = coroipcc_msg_send_reply_receive( ais_ipc_handle, &iov, 1, &answer, sizeof (answer)); if(rc == CS_OK) { CRM_CHECK(answer.header.size == sizeof (struct crm_ais_nodeid_resp_s), crm_err("Odd message: id=%d, size=%d, error=%d", answer.header.id, answer.header.size, answer.header.error)); CRM_CHECK(answer.header.id == crm_class_nodeid, crm_err("Bad response id: %d", answer.header.id)); } if(rc == CS_ERR_TRY_AGAIN && retries < 20) { retries++; crm_info("Peer overloaded: Re-sending message (Attempt %d of 20)", retries); sleep(retries); /* Proportional back off */ goto retry; } if(rc != CS_OK) { crm_err("Sending nodeid request: FAILED (rc=%d): %s", rc, ais_error2text(rc)); return FALSE; } else if(answer.header.error != CS_OK) { crm_err("Bad response from peer: (rc=%d): %s", rc, ais_error2text(rc)); return FALSE; } crm_info("Server details: id=%u uname=%s cname=%s", answer.id, answer.uname, answer.cname); *id = answer.id; *uname = crm_strdup(answer.uname); ais_cluster_name = crm_strdup(answer.cname); return TRUE; } gboolean crm_get_cluster_name(char **cname) { CRM_CHECK(cname != NULL, return FALSE); if(ais_cluster_name) { *cname = crm_strdup(ais_cluster_name); return TRUE; } return FALSE; } gboolean send_ais_text(int class, const char *data, gboolean local, const char *node, enum crm_ais_msg_types dest) { static int msg_id = 0; static int local_pid = 0; int retries = 0; int rc = CS_OK; int buf_len = sizeof(coroipc_response_header_t); char *buf = NULL; struct iovec iov; coroipc_response_header_t *header; AIS_Message *ais_msg = NULL; enum crm_ais_msg_types sender = text2msg_type(crm_system_name); /* There are only 6 handlers registered to crm_lib_service in plugin.c */ CRM_CHECK(class < 6, crm_err("Invalid message class: %d", class); return FALSE); if(data == NULL) { data = ""; } if(local_pid == 0) { local_pid = getpid(); } if(sender == crm_msg_none) { sender = local_pid; } crm_malloc0(ais_msg, sizeof(AIS_Message)); ais_msg->id = msg_id++; ais_msg->header.id = class; ais_msg->header.error = CS_OK; ais_msg->host.type = dest; ais_msg->host.local = local; if(node) { ais_msg->host.size = strlen(node); memset(ais_msg->host.uname, 0, MAX_NAME); memcpy(ais_msg->host.uname, node, ais_msg->host.size); ais_msg->host.id = 0; } else { ais_msg->host.size = 0; memset(ais_msg->host.uname, 0, MAX_NAME); ais_msg->host.id = 0; } ais_msg->sender.type = sender; ais_msg->sender.pid = local_pid; ais_msg->sender.size = 0; memset(ais_msg->sender.uname, 0, MAX_NAME); ais_msg->sender.id = 0; ais_msg->size = 1 + strlen(data); if(ais_msg->size < CRM_BZ2_THRESHOLD) { failback: crm_realloc(ais_msg, sizeof(AIS_Message) + ais_msg->size); memcpy(ais_msg->data, data, ais_msg->size); } else { char *compressed = NULL; char *uncompressed = crm_strdup(data); unsigned int len = (ais_msg->size * 1.1) + 600; /* recomended size */ crm_debug_5("Compressing message payload"); crm_malloc(compressed, len); rc = BZ2_bzBuffToBuffCompress( compressed, &len, uncompressed, ais_msg->size, CRM_BZ2_BLOCKS, 0, CRM_BZ2_WORK); crm_free(uncompressed); if(rc != BZ_OK) { crm_err("Compression failed: %d", rc); crm_free(compressed); goto failback; } crm_realloc(ais_msg, sizeof(AIS_Message) + len + 1); memcpy(ais_msg->data, compressed, len); ais_msg->data[len] = 0; crm_free(compressed); ais_msg->is_compressed = TRUE; ais_msg->compressed_size = len; crm_debug_2("Compression details: %d -> %d", ais_msg->size, ais_data_len(ais_msg)); } ais_msg->header.size = sizeof(AIS_Message) + ais_data_len(ais_msg); crm_debug_3("Sending%s message %d to %s.%s (data=%d, total=%d)", ais_msg->is_compressed?" compressed":"", ais_msg->id, ais_dest(&(ais_msg->host)), msg_type2text(dest), ais_data_len(ais_msg), ais_msg->header.size); iov.iov_base = ais_msg; iov.iov_len = ais_msg->header.size; retry: errno = 0; crm_realloc(buf, buf_len); rc = coroipcc_msg_send_reply_receive(ais_ipc_handle, &iov, 1, buf, buf_len); header = (coroipc_response_header_t *)buf; if(rc == CS_ERR_TRY_AGAIN && retries < 20) { retries++; crm_info("Peer overloaded: Re-sending message (Attempt %d of 20)", retries); sleep(retries); /* Proportional back off */ goto retry; } else if(rc == CS_OK) { CRM_CHECK_AND_STORE(header->size == sizeof (coroipc_response_header_t), crm_err("Odd message: id=%d, size=%d, class=%d, error=%d", header->id, header->size, class, header->error)); if(buf_len < header->size) { crm_err("Increasing buffer length to %d and retrying", header->size); buf_len = header->size + 1; goto retry; } else if(header->id == crm_class_nodeid && header->size == sizeof (struct crm_ais_nodeid_resp_s)){ struct crm_ais_nodeid_resp_s *answer = (struct crm_ais_nodeid_resp_s *)header; crm_err("Server details: id=%u uname=%s counter=%u", answer->id, answer->uname, answer->counter); } else { CRM_CHECK_AND_STORE(header->id == CRM_MESSAGE_IPC_ACK, crm_err("Bad response id (%d) for request (%d)", header->id, ais_msg->header.id)); CRM_CHECK(header->error == CS_OK, rc = header->error); } } if(rc != CS_OK) { crm_perror(LOG_ERR,"Sending message %d: FAILED (rc=%d): %s", ais_msg->id, rc, ais_error2text(rc)); ais_fd_async = -1; } else { crm_debug_4("Message %d: sent", ais_msg->id); } crm_free(buf); crm_free(ais_msg); return (rc == CS_OK); } gboolean send_ais_message(xmlNode *msg, gboolean local, const char *node, enum crm_ais_msg_types dest) { gboolean rc = TRUE; char *data = NULL; if(ais_fd_async < 0 || ais_source == NULL) { crm_err("Not connected to AIS"); return FALSE; } data = dump_xml_unformatted(msg); rc = send_ais_text(0, data, local, node, dest); crm_free(data); return rc; } void terminate_ais_connection(void) { if(ais_ipc_ctx) { coroipcc_service_disconnect(ais_ipc_handle); } crm_notice("Disconnected from AIS"); /* G_main_del_fd(ais_source); */ /* G_main_del_fd(ais_source_sync); */ } int ais_membership_timer = 0; gboolean ais_membership_force = FALSE; gboolean ais_dispatch(int sender, gpointer user_data) { char *data = NULL; char *buffer = NULL; char *uncompressed = NULL; int rc = CS_OK; xmlNode *xml = NULL; AIS_Message *msg = NULL; gboolean (*dispatch)(AIS_Message*,char*,int) = user_data; rc = coroipcc_dispatch_get (ais_ipc_handle, (void**)&buffer, 0); if (rc == 0) { /* Zero is a legal "no message afterall" value */ goto done; } else if (rc != CS_OK) { crm_perror(LOG_ERR,"Receiving message body failed: (%d) %s", rc, ais_error2text(rc)); goto bail; } msg = (AIS_Message*)buffer; crm_debug_3("Got new%s message (size=%d, %d, %d)", msg->is_compressed?" compressed":"", ais_data_len(msg), msg->size, msg->compressed_size); data = msg->data; if(msg->is_compressed && msg->size > 0) { int rc = BZ_OK; unsigned int new_size = msg->size + 1; if(check_message_sanity(msg, NULL) == FALSE) { goto badmsg; } crm_debug_5("Decompressing message data"); crm_malloc0(uncompressed, new_size); rc = BZ2_bzBuffToBuffDecompress( uncompressed, &new_size, data, msg->compressed_size, 1, 0); if(rc != BZ_OK) { crm_err("Decompression failed: %d", rc); goto badmsg; } CRM_ASSERT(rc == BZ_OK); CRM_ASSERT(new_size == msg->size); data = uncompressed; } else if(check_message_sanity(msg, data) == FALSE) { goto badmsg; } else if(safe_str_eq("identify", data)) { int pid = getpid(); char *pid_s = crm_itoa(pid); send_ais_text(0, pid_s, TRUE, NULL, crm_msg_ais); crm_free(pid_s); goto done; } if(msg->header.id != crm_class_members) { crm_update_peer(msg->sender.id, 0,0,0,0, msg->sender.uname, msg->sender.uname, NULL, NULL); } if(msg->header.id == crm_class_rmpeer) { uint32_t id = crm_int_helper(data, NULL); crm_info("Removing peer %s/%u", data, id); reap_crm_member(id); goto done; } else if(msg->header.id == crm_class_members || msg->header.id == crm_class_quorum) { const char *value = NULL; gboolean quorate = FALSE; xml = string2xml(data); if(xml == NULL) { crm_err("Invalid membership update: %s", data); goto badmsg; } value = crm_element_value(xml, "quorate"); CRM_CHECK(value != NULL, crm_log_xml_err(xml, "No quorum value:"); goto badmsg); if(crm_is_true(value)) { quorate = TRUE; } value = crm_element_value(xml, "id"); CRM_CHECK(value != NULL, crm_log_xml_err(xml, "No membership id"); goto badmsg); crm_peer_seq = crm_int_helper(value, NULL); if(quorate != crm_have_quorum) { crm_notice("Membership %s: quorum %s", value, quorate?"acquired":"lost"); crm_have_quorum = quorate; } else { crm_info("Membership %s: quorum %s", value, quorate?"retained":"still lost"); } xml_child_iter(xml, node, crm_update_ais_node(node, crm_peer_seq)); } if(dispatch != NULL) { dispatch(msg, data, sender); } done: crm_free(uncompressed); free_xml(xml); coroipcc_dispatch_put (ais_ipc_handle); buffer = NULL; crm_free(buffer); return TRUE; badmsg: crm_err("Invalid message (id=%d, dest=%s:%s, from=%s:%s.%d):" " min=%d, total=%d, size=%d, bz2_size=%d", msg->id, ais_dest(&(msg->host)), msg_type2text(msg->host.type), ais_dest(&(msg->sender)), msg_type2text(msg->sender.type), msg->sender.pid, (int)sizeof(AIS_Message), msg->header.size, msg->size, msg->compressed_size); goto done; bail: crm_err("AIS connection failed"); buffer = NULL; return FALSE; } static void ais_destroy(gpointer user_data) { crm_err("AIS connection terminated"); ais_fd_sync = -1; exit(1); } gboolean init_ais_connection( gboolean (*dispatch)(AIS_Message*,char*,int), void (*destroy)(gpointer), char **our_uuid, char **our_uname, int *nodeid) { - int pid = 0; int retries = 0; + while(retries++ < 30) { + int rc = init_ais_connection(dispatch, destroy, our_uuid, our_uname, nodeid); + switch(rc) { + case CS_OK: + return TRUE; + break; + case CS_ERR_TRY_AGAIN: + break; + default: + return FALSE; + } + } + + crm_err("Retry count exceeded: %d", retries); + return FALSE; +} + +gboolean init_ais_connection_once( + gboolean (*dispatch)(AIS_Message*,char*,int), + void (*destroy)(gpointer), char **our_uuid, char **our_uname, int *nodeid) +{ + int pid = 0; int rc = CS_OK; char *pid_s = NULL; struct utsname name; uint32_t local_nodeid = 0; char *local_uname = NULL; - retry: crm_info("Creating connection to our AIS plugin"); rc = coroipcc_service_connect( COROSYNC_SOCKET_NAME, PCMK_SERVICE_ID, AIS_IPC_MESSAGE_SIZE, AIS_IPC_MESSAGE_SIZE, AIS_IPC_MESSAGE_SIZE, &ais_ipc_handle); if(ais_ipc_handle) { coroipcc_fd_get(ais_ipc_handle, &ais_fd_async); } if(ais_fd_async <= 0 && rc == CS_OK) { crm_err("No context created, but connection reported 'ok'"); rc = CS_ERR_LIBRARY; } if (rc != CS_OK) { crm_info("Connection to our AIS plugin (%d) failed: %s (%d)", PCMK_SERVICE_ID, ais_error2text(rc), rc); } - switch(rc) { - case CS_OK: - break; - case CS_ERR_TRY_AGAIN: - if(retries < 30) { - sleep(1); - retries++; - goto retry; - } - crm_err("Retry count exceeded"); - return FALSE; - default: - return FALSE; + if(rc != CS_OK) { + return rc; } if(destroy == NULL) { destroy = ais_destroy; } crm_info("AIS connection established"); pid = getpid(); pid_s = crm_itoa(pid); send_ais_text(0, pid_s, TRUE, NULL, crm_msg_ais); crm_free(pid_s); crm_peer_init(); get_ais_nodeid(&local_nodeid, &local_uname); if(uname(&name) < 0) { crm_perror(LOG_ERR,"uname(2) call failed"); exit(100); } if(safe_str_neq(name.nodename, local_uname)) { crm_crit("Node name mismatch! OpenAIS supplied %s, our lookup returned %s", local_uname, name.nodename); crm_notice("Node name mismatches usually occur when assigned automatically by DHCP servers"); crm_notice("If this node was part of the cluster with a different name," " you will need to remove the old entry with crm_node --remove"); } if(local_nodeid != 0) { /* Ensure the local node always exists */ crm_update_peer(local_nodeid, 0, 0, 0, 0, local_uname, local_uname, NULL, NULL); } if(our_uuid != NULL) { *our_uuid = crm_strdup(local_uname); } if(our_uname != NULL) { *our_uname = local_uname; } else { crm_free(local_uname); } if(nodeid != NULL) { *nodeid = local_nodeid; } if(dispatch) { ais_source = G_main_add_fd( G_PRIORITY_HIGH, ais_fd_async, FALSE, ais_dispatch, dispatch, destroy); } return TRUE; } gboolean check_message_sanity(const AIS_Message *msg, const char *data) { gboolean sane = TRUE; gboolean repaired = FALSE; int dest = msg->host.type; int tmp_size = msg->header.size - sizeof(AIS_Message); if(sane && msg->header.size == 0) { crm_warn("Message with no size"); sane = FALSE; } if(sane && msg->header.error != CS_OK) { crm_warn("Message header contains an error: %d", msg->header.error); sane = FALSE; } if(sane && ais_data_len(msg) != tmp_size) { crm_warn("Message payload size is incorrect: expected %d, got %d", ais_data_len(msg), tmp_size); sane = TRUE; } if(sane && ais_data_len(msg) == 0) { crm_warn("Message with no payload"); sane = FALSE; } if(sane && data && msg->is_compressed == FALSE) { int str_size = strlen(data) + 1; if(ais_data_len(msg) != str_size) { int lpc = 0; crm_warn("Message payload is corrupted: expected %d bytes, got %d", ais_data_len(msg), str_size); sane = FALSE; for(lpc = (str_size - 10); lpc < msg->size; lpc++) { if(lpc < 0) { lpc = 0; } crm_debug("bad_data[%d]: %d / '%c'", lpc, data[lpc], data[lpc]); } } } if(sane == FALSE) { crm_err("Invalid message %d: (dest=%s:%s, from=%s:%s.%d, compressed=%d, size=%d, total=%d)", msg->id, ais_dest(&(msg->host)), msg_type2text(dest), ais_dest(&(msg->sender)), msg_type2text(msg->sender.type), msg->sender.pid, msg->is_compressed, ais_data_len(msg), msg->header.size); } else if(repaired) { crm_err("Repaired message %d: (dest=%s:%s, from=%s:%s.%d, compressed=%d, size=%d, total=%d)", msg->id, ais_dest(&(msg->host)), msg_type2text(dest), ais_dest(&(msg->sender)), msg_type2text(msg->sender.type), msg->sender.pid, msg->is_compressed, ais_data_len(msg), msg->header.size); } else { crm_debug_3("Verfied message %d: (dest=%s:%s, from=%s:%s.%d, compressed=%d, size=%d, total=%d)", msg->id, ais_dest(&(msg->host)), msg_type2text(dest), ais_dest(&(msg->sender)), msg_type2text(msg->sender.type), msg->sender.pid, msg->is_compressed, ais_data_len(msg), msg->header.size); } return sane; } #endif diff --git a/lib/common/stack.h b/lib/common/stack.h index d5294cad8c..f1eef5141c 100644 --- a/lib/common/stack.h +++ b/lib/common/stack.h @@ -1,48 +1,51 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef CRM_STACK__H #define CRM_STACK__H #if SUPPORT_HEARTBEAT extern ll_cluster_t *heartbeat_cluster; extern gboolean send_ha_message(ll_cluster_t *hb_conn, xmlNode *msg, const char *node, gboolean force_ordered); extern gboolean ha_msg_dispatch(ll_cluster_t *cluster_conn, gpointer user_data); extern gboolean register_heartbeat_conn( ll_cluster_t *hb_cluster, char **uuid, char **uname, void (*hb_message)(HA_Message *msg, void* private_data), void (*hb_destroy)(gpointer user_data)); #endif #if SUPPORT_COROSYNC extern gboolean send_ais_message( xmlNode *msg, gboolean local, const char *node, enum crm_ais_msg_types dest); extern void terminate_ais_connection(void); extern gboolean init_ais_connection( gboolean (*dispatch)(AIS_Message*,char*,int), void (*destroy)(gpointer), char **our_uuid, char **our_uname, int *nodeid); +extern gboolean init_ais_connection_once( + gboolean (*dispatch)(AIS_Message*,char*,int), + void (*destroy)(gpointer), char **our_uuid, char **our_uname, int *nodeid); #endif #endif diff --git a/tools/ccm_epoche.c b/tools/ccm_epoche.c index 198e21c916..40927764f9 100644 --- a/tools/ccm_epoche.c +++ b/tools/ccm_epoche.c @@ -1,447 +1,459 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include /* for basename() */ #include #include #include #include int command = 0; int ccm_fd = 0; int try_hb = 1; int try_ais = 1; gboolean do_quiet = FALSE; char *target_uuid = NULL; char *target_uname = NULL; const char *standby_value = NULL; const char *standby_scope = NULL; -void ais_membership_destroy(gpointer user_data); -gboolean ais_membership_dispatch(AIS_Message *wrapper, char *data, int sender); #include <../lib/common/stack.h> +static struct crm_option long_options[] = { + /* Top-level Options */ + {"help", 0, 0, '?', "\tThis text"}, + {"version", 0, 0, '$', "\tVersion information" }, + {"verbose", 0, 0, 'V', "\tIncrease debug output"}, + {"quiet", 0, 0, 'Q', "\tEssential output only"}, + + {"-spacer-", 1, 0, '-', "\nStack:", SUPPORT_HEARTBEAT}, + {"openais", 0, 0, 'A', "\tOnly try connecting to an OpenAIS-based cluster", SUPPORT_HEARTBEAT}, + {"heartbeat", 0, 0, 'H', "Only try connecting to a Heartbeat-based cluster", SUPPORT_HEARTBEAT}, + + {"-spacer-", 1, 0, '-', "\nCommands:"}, + {"epoch", 0, 0, 'e', "\tDisplay the epoch during which this node joined the cluster"}, + {"quorum", 0, 0, 'q', "\tDisplay a 1 if our partition has quorum, 0 if not"}, + {"list", 0, 0, 'l', "(AIS-Only) Display all known members (past and present) of this cluster"}, + {"partition", 0, 0, 'p', "Display the members of this partition"}, + {"cluster-id", 0, 0, 'i', "Display this node's cluster id"}, + {"remove", 1, 0, 'R', "(Advanced, AIS-Only) Remove the (stopped) node with the specified nodeid from the cluster"}, + + {"-spacer-", 1, 0, '-', "\nAdditional Options:"}, + {"force", 0, 0, 'f'}, + + {0, 0, 0, 0} +}; + +int local_id = 0; + + #if SUPPORT_HEARTBEAT # include # include # include # define UUID_LEN 16 oc_ev_t *ccm_token = NULL; void oc_ev_special(const oc_ev_t *, oc_ev_class_t , int ); -void ccm_age_callback( - oc_ed_t event, void *cookie, size_t size, const void *data); -gboolean ccm_age_connect(int *ccm_fd); -static int read_local_hb_uuid(void) +static gboolean read_local_hb_uuid(void) { - int rc = 0; cl_uuid_t uuid; char *buffer = NULL; long start = 0, read_len = 0; FILE *input = fopen(UUID_FILE, "r"); if(input == NULL) { - cl_perror("Could not open UUID file %s\n", UUID_FILE); - return 1; + crm_info("Could not open UUID file %s\n", UUID_FILE); + return FALSE; } /* see how big the file is */ start = ftell(input); fseek(input, 0L, SEEK_END); if(UUID_LEN != ftell(input)) { fprintf(stderr, "%s must contain exactly %d bytes\n", UUID_FILE, UUID_LEN); abort(); } fseek(input, 0L, start); if(start != ftell(input)) { fprintf(stderr, "fseek not behaving: %ld vs. %ld\n", start, ftell(input)); - rc = 2; - goto bail; + exit(2); } buffer = malloc(50); read_len = fread(uuid.uuid, 1, UUID_LEN, input); if(read_len != UUID_LEN) { fprintf(stderr, "Expected and read bytes differ: %d vs. %ld\n", UUID_LEN, read_len); - rc = 3; - goto bail; + exit(3); } else if(buffer != NULL) { cl_uuid_unparse(&uuid, buffer); fprintf(stdout, "%s\n", buffer); - + return TRUE; + } else { fprintf(stderr, "No buffer to unparse\n"); - rc = 4; + exit(4); } - bail: free(buffer); fclose(input); - return rc; + return FALSE; } -#endif - -static struct crm_option long_options[] = { - /* Top-level Options */ - {"help", 0, 0, '?', "\tThis text"}, - {"version", 0, 0, '$', "\tVersion information" }, - {"verbose", 0, 0, 'V', "\tIncrease debug output"}, - {"quiet", 0, 0, 'Q', "\tEssential output only"}, - - {"-spacer-", 1, 0, '-', "\nStack:", SUPPORT_HEARTBEAT}, - {"openais", 0, 0, 'A', "\tOnly try connecting to an OpenAIS-based cluster", SUPPORT_HEARTBEAT}, - {"heartbeat", 0, 0, 'H', "Only try connecting to a Heartbeat-based cluster", SUPPORT_HEARTBEAT}, - - {"-spacer-", 1, 0, '-', "\nCommands:"}, - {"epoch", 0, 0, 'e', "\tDisplay the epoch during which this node joined the cluster"}, - {"quorum", 0, 0, 'q', "\tDisplay a 1 if our partition has quorum, 0 if not"}, - {"list", 0, 0, 'l', "(AIS-Only) Display all known members (past and present) of this cluster"}, - {"partition", 0, 0, 'p', "Display the members of this partition"}, - {"cluster-id", 0, 0, 'i', "Display this node's cluster id"}, - {"remove", 1, 0, 'R', "(Advanced, AIS-Only) Remove the (stopped) node with the specified nodeid from the cluster"}, - {"-spacer-", 1, 0, '-', "\nAdditional Options:"}, - {"force", 0, 0, 'f'}, - - {0, 0, 0, 0} -}; - -int local_id = 0; - -int -main(int argc, char ** argv) +static void +ccm_age_callback(oc_ed_t event, void *cookie, size_t size, const void *data) { - int flag = 0; - int argerr = 0; - gboolean force_flag = FALSE; - gboolean dangerous_cmd = FALSE; - - int option_index = 0; + int lpc; + int node_list_size; + const oc_ev_membership_t *oc = (const oc_ev_membership_t *)data; - crm_peer_init(); - crm_log_init(NULL, LOG_WARNING, FALSE, FALSE, argc, argv, FALSE); - crm_set_options("?V$qepHR:ifl", "command [options]", long_options, - "Tool for displaying low-level node information"); - - while (flag >= 0) { - flag = crm_get_option(argc, argv, &option_index); - switch(flag) { - case -1: - break; - case 'V': - cl_log_enable_stderr(TRUE); - alter_debug(DEBUG_INC); - break; - case '$': - case '?': - crm_help(flag, LSB_EXIT_OK); - break; - case 'Q': - do_quiet = TRUE; - break; - case 'H': - try_ais = 0; - break; - case 'A': - try_hb = 0; - break; - case 'f': - force_flag = TRUE; - break; - case 'R': - dangerous_cmd = TRUE; - command = flag; - target_uname = optarg; - break; - case 'p': - case 'e': - case 'q': - case 'i': - case 'l': - command = flag; - break; - default: - ++argerr; - break; + node_list_size = oc->m_n_member; + if(command == 'q') { + crm_debug("Processing \"%s\" event.", + event==OC_EV_MS_NEW_MEMBERSHIP?"NEW MEMBERSHIP": + event==OC_EV_MS_NOT_PRIMARY?"NOT PRIMARY": + event==OC_EV_MS_PRIMARY_RESTORED?"PRIMARY RESTORED": + event==OC_EV_MS_EVICTED?"EVICTED": + "NO QUORUM MEMBERSHIP"); + if(ccm_have_quorum(event)) { + fprintf(stdout, "1\n"); + } else { + fprintf(stdout, "0\n"); } + + } else if(command == 'e') { + crm_debug("Searching %d members for our birth", oc->m_n_member); } - - if (optind > argc) { - ++argerr; - } - - if (argerr) { - crm_help('?', LSB_EXIT_GENERIC); - } - - if(dangerous_cmd && force_flag == FALSE) { - fprintf(stderr, "The supplied command is considered dangerous." - " To prevent accidental destruction of the cluster," - " the --force flag is required in order to proceed.\n"); - fflush(stderr); - exit(LSB_EXIT_GENERIC); - } - -#if SUPPORT_COROSYNC - if(try_ais && init_ais_connection( - ais_membership_dispatch, ais_membership_destroy, NULL, NULL, &local_id)) { - - GMainLoop* amainloop = NULL; - switch(command) { - case 'R': - send_ais_text(crm_class_rmpeer, target_uname, TRUE, NULL, crm_msg_ais); - return 0; - - case 'e': - /* Age makes no sense (yet) in an AIS cluster */ - fprintf(stdout, "1\n"); - return 0; - - case 'q': - send_ais_text(crm_class_quorum, NULL, TRUE, NULL, crm_msg_ais); - break; - - case 'l': - case 'p': - crm_info("Requesting the list of configured nodes"); - send_ais_text(crm_class_members, __FUNCTION__, TRUE, NULL, crm_msg_ais); - break; - - case 'i': - printf("%d\n", local_id); - return 0; + for(lpc=0; lpcm_array[oc->m_memb_idx+lpc].node_uname); - default: - fprintf(stderr, "Unknown option '%c'\n", command); - crm_help('?', LSB_EXIT_GENERIC); + } else if(command == 'e') { + if(oc_ev_is_my_nodeid(ccm_token, &(oc->m_array[lpc]))){ + crm_debug("MATCH: nodeid=%d, uname=%s, born=%d", + oc->m_array[oc->m_memb_idx+lpc].node_id, + oc->m_array[oc->m_memb_idx+lpc].node_uname, + oc->m_array[oc->m_memb_idx+lpc].node_born_on); + fprintf(stdout, "%d\n", + oc->m_array[oc->m_memb_idx+lpc].node_born_on); + } } - amainloop = g_main_new(FALSE); - g_main_run(amainloop); } -#endif -#if SUPPORT_HEARTBEAT - if(try_hb && command == 'i') { - return read_local_hb_uuid(); - - } else if(try_hb && ccm_age_connect(&ccm_fd)) { - int rc = 0; - fd_set rset; - oc_ev_t *ccm_token = NULL; - while (1) { - - sleep(1); - FD_ZERO(&rset); - FD_SET(ccm_fd, &rset); - errno = 0; - rc = select(ccm_fd + 1, &rset, NULL,NULL,NULL); + oc_ev_callback_done(cookie); - if(rc > 0 && oc_ev_handle_event(ccm_token) != 0) { - crm_err("oc_ev_handle_event failed"); - return 1; - - } else if(rc < 0 && errno != EINTR) { - crm_perror(LOG_ERR, "select failed"); - return 1; - } - } + if(command == 'p') { + fprintf(stdout, "\n"); } -#endif - return(1); + fflush(stdout); + exit(0); } -#if SUPPORT_HEARTBEAT -gboolean +static gboolean ccm_age_connect(int *ccm_fd) { gboolean did_fail = FALSE; int ret = 0; crm_debug("Registering with CCM"); ret = oc_ev_register(&ccm_token); if (ret != 0) { - crm_warn("CCM registration failed"); + crm_info("CCM registration failed: %d", ret); did_fail = TRUE; } if(did_fail == FALSE) { crm_debug("Setting up CCM callbacks"); ret = oc_ev_set_callback(ccm_token, OC_EV_MEMB_CLASS, ccm_age_callback, NULL); if (ret != 0) { - crm_warn("CCM callback not set"); + crm_warn("CCM callback not set: %d", ret); did_fail = TRUE; } } if(did_fail == FALSE) { oc_ev_special(ccm_token, OC_EV_MEMB_CLASS, 0/*don't care*/); crm_debug("Activating CCM token"); ret = oc_ev_activate(ccm_token, ccm_fd); if (ret != 0){ - crm_warn("CCM Activation failed"); + crm_warn("CCM Activation failed: %d", ret); did_fail = TRUE; } } return !did_fail; } - -void -ccm_age_callback(oc_ed_t event, void *cookie, size_t size, const void *data) +static gboolean try_heartbeat(int command) { - int lpc; - int node_list_size; - const oc_ev_membership_t *oc = (const oc_ev_membership_t *)data; - - node_list_size = oc->m_n_member; - if(command == 'q') { - crm_debug("Processing \"%s\" event.", - event==OC_EV_MS_NEW_MEMBERSHIP?"NEW MEMBERSHIP": - event==OC_EV_MS_NOT_PRIMARY?"NOT PRIMARY": - event==OC_EV_MS_PRIMARY_RESTORED?"PRIMARY RESTORED": - event==OC_EV_MS_EVICTED?"EVICTED": - "NO QUORUM MEMBERSHIP"); - if(ccm_have_quorum(event)) { - fprintf(stdout, "1\n"); - } else { - fprintf(stdout, "0\n"); + crm_debug("Attempting to process %c command", command); + + if(command == 'i') { + if(read_local_hb_uuid()) { + exit(0); } + + } else if(ccm_age_connect(&ccm_fd)) { + int rc = 0; + fd_set rset; + while (1) { + + sleep(1); + FD_ZERO(&rset); + FD_SET(ccm_fd, &rset); + + errno = 0; + rc = select(ccm_fd + 1, &rset, NULL,NULL,NULL); + + if(rc > 0 && oc_ev_handle_event(ccm_token) != 0) { + crm_err("oc_ev_handle_event failed"); + return FALSE; - } else if(command == 'e') { - crm_debug("Searching %d members for our birth", oc->m_n_member); - } - for(lpc=0; lpcm_array[oc->m_memb_idx+lpc].node_uname); - - } else if(command == 'e') { - if(oc_ev_is_my_nodeid(ccm_token, &(oc->m_array[lpc]))){ - crm_debug("MATCH: nodeid=%d, uname=%s, born=%d", - oc->m_array[oc->m_memb_idx+lpc].node_id, - oc->m_array[oc->m_memb_idx+lpc].node_uname, - oc->m_array[oc->m_memb_idx+lpc].node_born_on); - fprintf(stdout, "%d\n", - oc->m_array[oc->m_memb_idx+lpc].node_born_on); - } + } else if(rc < 0 && errno != EINTR) { + crm_perror(LOG_ERR, "select failed: %d", rc); + return FALSE; + } } } - - oc_ev_callback_done(cookie); - - if(command == 'p') { - fprintf(stdout, "\n"); - } - fflush(stdout); - exit(0); + return FALSE; } #endif + #if SUPPORT_COROSYNC -void +static void ais_membership_destroy(gpointer user_data) { crm_err("AIS connection terminated"); ais_fd_sync = -1; exit(1); } -#endif static gint member_sort(gconstpointer a, gconstpointer b) { const crm_node_t *node_a = a; const crm_node_t *node_b = b; return strcmp(node_a->uname, node_b->uname); } static void crm_add_member( gpointer key, gpointer value, gpointer user_data) { GList **list = user_data; crm_node_t *node = value; if(node->uname != NULL) { *list = g_list_insert_sorted(*list, node, member_sort); } } -gboolean +static gboolean ais_membership_dispatch(AIS_Message *wrapper, char *data, int sender) { switch(wrapper->header.id) { case crm_class_members: case crm_class_notify: case crm_class_quorum: break; default: return TRUE; break; } if(command == 'q') { if(crm_have_quorum) { fprintf(stdout, "1\n"); } else { fprintf(stdout, "0\n"); } } else if(command == 'l') { GList *nodes = NULL; g_hash_table_foreach(crm_peer_cache, crm_add_member, &nodes); slist_iter(node, crm_node_t, nodes, lpc, fprintf(stdout, "%u %s %s\n", node->id, node->uname, node->state); ); fprintf(stdout, "\n"); } else if(command == 'p') { GList *nodes = NULL; g_hash_table_foreach(crm_peer_cache, crm_add_member, &nodes); slist_iter(node, crm_node_t, nodes, lpc, if(node->uname && crm_is_member_active(node)) { fprintf(stdout, "%s ", node->uname); } ); fprintf(stdout, "\n"); } exit(0); return TRUE; } + +static gboolean try_corosync(int command) +{ + crm_debug("Attempting to process %c command", command); + + if(CS_OK == init_ais_connection_once( + ais_membership_dispatch, ais_membership_destroy, NULL, NULL, &local_id)) { + + GMainLoop* amainloop = NULL; + switch(command) { + case 'R': + send_ais_text(crm_class_rmpeer, target_uname, TRUE, NULL, crm_msg_ais); + return 0; + + case 'e': + /* Age makes no sense (yet) in an AIS cluster */ + fprintf(stdout, "1\n"); + return 0; + + case 'q': + send_ais_text(crm_class_quorum, NULL, TRUE, NULL, crm_msg_ais); + break; + + case 'l': + case 'p': + crm_info("Requesting the list of configured nodes"); + send_ais_text(crm_class_members, __FUNCTION__, TRUE, NULL, crm_msg_ais); + break; + + case 'i': + printf("%d\n", local_id); + return 0; + + default: + fprintf(stderr, "Unknown option '%c'\n", command); + crm_help('?', LSB_EXIT_GENERIC); + } + amainloop = g_main_new(FALSE); + g_main_run(amainloop); + } + return FALSE; +} +#endif + +int +main(int argc, char ** argv) +{ + int flag = 0; + int argerr = 0; + gboolean force_flag = FALSE; + gboolean dangerous_cmd = FALSE; + + int option_index = 0; + + crm_peer_init(); + crm_log_init(NULL, LOG_WARNING, FALSE, FALSE, argc, argv, FALSE); + crm_set_options("?V$qepHR:ifl", "command [options]", long_options, + "Tool for displaying low-level node information"); + + while (flag >= 0) { + flag = crm_get_option(argc, argv, &option_index); + switch(flag) { + case -1: + break; + case 'V': + cl_log_enable_stderr(TRUE); + alter_debug(DEBUG_INC); + break; + case '$': + case '?': + crm_help(flag, LSB_EXIT_OK); + break; + case 'Q': + do_quiet = TRUE; + break; + case 'H': + try_ais = 0; + break; + case 'A': + try_hb = 0; + break; + case 'f': + force_flag = TRUE; + break; + case 'R': + dangerous_cmd = TRUE; + command = flag; + target_uname = optarg; + break; + case 'p': + case 'e': + case 'q': + case 'i': + case 'l': + command = flag; + break; + default: + ++argerr; + break; + } + } + + if (optind > argc) { + ++argerr; + } + + if (argerr) { + crm_help('?', LSB_EXIT_GENERIC); + } + + if(dangerous_cmd && force_flag == FALSE) { + fprintf(stderr, "The supplied command is considered dangerous." + " To prevent accidental destruction of the cluster," + " the --force flag is required in order to proceed.\n"); + fflush(stderr); + exit(LSB_EXIT_GENERIC); + } + +#if SUPPORT_COROSYNC + if(try_ais) { + try_corosync(command); + } +#endif +#if SUPPORT_HEARTBEAT + if(try_hb) { + try_heartbeat(command); + } +#endif + return(1); +}