diff --git a/crmd/lrm_state.c b/crmd/lrm_state.c index 7b4379bea5..9c226838f0 100644 --- a/crmd/lrm_state.c +++ b/crmd/lrm_state.c @@ -1,854 +1,705 @@ /* * Copyright (C) 2012 David Vossel * * 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 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include GHashTable *lrm_state_table = NULL; extern GHashTable *proxy_table; int lrmd_internal_proxy_send(lrmd_t * lrmd, xmlNode *msg); void lrmd_internal_set_proxy_callback(lrmd_t * lrmd, void *userdata, void (*callback)(lrmd_t *lrmd, void *userdata, xmlNode *msg)); static void free_rsc_info(gpointer value) { lrmd_rsc_info_t *rsc_info = value; lrmd_free_rsc_info(rsc_info); } static void free_deletion_op(gpointer value) { struct pending_deletion_op_s *op = value; free(op->rsc); delete_ha_msg_input(op->input); free(op); } static void free_recurring_op(gpointer value) { struct recurring_op_s *op = (struct recurring_op_s *)value; free(op->user_data); free(op->rsc_id); free(op->op_type); free(op->op_key); if (op->params) { g_hash_table_destroy(op->params); } free(op); } static gboolean fail_pending_op(gpointer key, gpointer value, gpointer user_data) { lrmd_event_data_t event = { 0, }; lrm_state_t *lrm_state = user_data; struct recurring_op_s *op = (struct recurring_op_s *)value; crm_trace("Pre-emptively failing %s_%s_%d on %s (call=%s, %s)", op->rsc_id, op->op_type, op->interval, lrm_state->node_name, (char*)key, op->user_data); event.type = lrmd_event_exec_complete; event.rsc_id = op->rsc_id; event.op_type = op->op_type; event.user_data = op->user_data; event.timeout = 0; event.interval = op->interval; event.rc = PCMK_OCF_CONNECTION_DIED; event.op_status = PCMK_LRM_OP_ERROR; event.t_run = op->start_time; event.t_rcchange = op->start_time; event.call_id = op->call_id; event.remote_nodename = lrm_state->node_name; event.params = op->params; process_lrm_event(lrm_state, &event, op); return TRUE; } gboolean lrm_state_is_local(lrm_state_t *lrm_state) { if (lrm_state == NULL || fsa_our_uname == NULL) { return FALSE; } if (strcmp(lrm_state->node_name, fsa_our_uname) != 0) { return FALSE; } return TRUE; } lrm_state_t * lrm_state_create(const char *node_name) { lrm_state_t *state = NULL; if (!node_name) { crm_err("No node name given for lrm state object"); return NULL; } state = calloc(1, sizeof(lrm_state_t)); if (!state) { return NULL; } state->node_name = strdup(node_name); state->rsc_info_cache = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, free_rsc_info); state->deletion_ops = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, free_deletion_op); state->pending_ops = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, free_recurring_op); state->resource_history = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, history_free); g_hash_table_insert(lrm_state_table, (char *)state->node_name, state); return state; } void lrm_state_destroy(const char *node_name) { g_hash_table_remove(lrm_state_table, node_name); } static gboolean remote_proxy_remove_by_node(gpointer key, gpointer value, gpointer user_data) { remote_proxy_t *proxy = value; const char *node_name = user_data; if (safe_str_eq(node_name, proxy->node_name)) { return TRUE; } return FALSE; } static void internal_lrm_state_destroy(gpointer data) { lrm_state_t *lrm_state = data; if (!lrm_state) { return; } crm_trace("Destroying proxy table %s with %d members", lrm_state->node_name, g_hash_table_size(proxy_table)); g_hash_table_foreach_remove(proxy_table, remote_proxy_remove_by_node, (char *) lrm_state->node_name); remote_ra_cleanup(lrm_state); lrmd_api_delete(lrm_state->conn); if (lrm_state->rsc_info_cache) { crm_trace("Destroying rsc info cache with %d members", g_hash_table_size(lrm_state->rsc_info_cache)); g_hash_table_destroy(lrm_state->rsc_info_cache); } if (lrm_state->resource_history) { crm_trace("Destroying history op cache with %d members", g_hash_table_size(lrm_state->resource_history)); g_hash_table_destroy(lrm_state->resource_history); } if (lrm_state->deletion_ops) { crm_trace("Destroying deletion op cache with %d members", g_hash_table_size(lrm_state->deletion_ops)); g_hash_table_destroy(lrm_state->deletion_ops); } if (lrm_state->pending_ops) { crm_trace("Destroying pending op cache with %d members", g_hash_table_size(lrm_state->pending_ops)); g_hash_table_destroy(lrm_state->pending_ops); } free((char *)lrm_state->node_name); free(lrm_state); } void lrm_state_reset_tables(lrm_state_t * lrm_state) { if (lrm_state->resource_history) { crm_trace("Re-setting history op cache with %d members", g_hash_table_size(lrm_state->resource_history)); g_hash_table_remove_all(lrm_state->resource_history); } if (lrm_state->deletion_ops) { crm_trace("Re-setting deletion op cache with %d members", g_hash_table_size(lrm_state->deletion_ops)); g_hash_table_remove_all(lrm_state->deletion_ops); } if (lrm_state->pending_ops) { crm_trace("Re-setting pending op cache with %d members", g_hash_table_size(lrm_state->pending_ops)); g_hash_table_remove_all(lrm_state->pending_ops); } if (lrm_state->rsc_info_cache) { crm_trace("Re-setting rsc info cache with %d members", g_hash_table_size(lrm_state->rsc_info_cache)); g_hash_table_remove_all(lrm_state->rsc_info_cache); } } gboolean lrm_state_init_local(void) { if (lrm_state_table) { return TRUE; } lrm_state_table = g_hash_table_new_full(crm_strcase_hash, crm_strcase_equal, NULL, internal_lrm_state_destroy); if (!lrm_state_table) { return FALSE; } proxy_table = g_hash_table_new_full(crm_strcase_hash, crm_strcase_equal, NULL, remote_proxy_free); if (!proxy_table) { g_hash_table_destroy(lrm_state_table); lrm_state_table = NULL; return FALSE; } return TRUE; } void lrm_state_destroy_all(void) { if (lrm_state_table) { crm_trace("Destroying state table with %d members", g_hash_table_size(lrm_state_table)); g_hash_table_destroy(lrm_state_table); lrm_state_table = NULL; } if(proxy_table) { crm_trace("Destroying proxy table with %d members", g_hash_table_size(proxy_table)); g_hash_table_destroy(proxy_table); proxy_table = NULL; } } lrm_state_t * lrm_state_find(const char *node_name) { if (!node_name) { return NULL; } return g_hash_table_lookup(lrm_state_table, node_name); } lrm_state_t * lrm_state_find_or_create(const char *node_name) { lrm_state_t *lrm_state; lrm_state = g_hash_table_lookup(lrm_state_table, node_name); if (!lrm_state) { lrm_state = lrm_state_create(node_name); } return lrm_state; } GList * lrm_state_get_list(void) { return g_hash_table_get_values(lrm_state_table); } static remote_proxy_t * find_connected_proxy_by_node(const char * node_name) { GHashTableIter gIter; remote_proxy_t *proxy = NULL; CRM_CHECK(proxy_table != NULL, return NULL); g_hash_table_iter_init(&gIter, proxy_table); while (g_hash_table_iter_next(&gIter, NULL, (gpointer *) &proxy)) { if (proxy->source && safe_str_eq(node_name, proxy->node_name)) { return proxy; } } return NULL; } static void remote_proxy_disconnect_by_node(const char * node_name) { remote_proxy_t *proxy = NULL; CRM_CHECK(proxy_table != NULL, return); while ((proxy = find_connected_proxy_by_node(node_name)) != NULL) { /* mainloop_del_ipc_client() eventually calls remote_proxy_disconnected() * , which removes the entry from proxy_table. * Do not do this in a g_hash_table_iter_next() loop. */ if (proxy->source) { mainloop_del_ipc_client(proxy->source); } } return; } void lrm_state_disconnect_only(lrm_state_t * lrm_state) { int removed = 0; if (!lrm_state->conn) { return; } crm_trace("Disconnecting %s", lrm_state->node_name); remote_proxy_disconnect_by_node(lrm_state->node_name); ((lrmd_t *) lrm_state->conn)->cmds->disconnect(lrm_state->conn); if (is_not_set(fsa_input_register, R_SHUTDOWN)) { removed = g_hash_table_foreach_remove(lrm_state->pending_ops, fail_pending_op, lrm_state); crm_trace("Synthesized %d operation failures for %s", removed, lrm_state->node_name); } } void lrm_state_disconnect(lrm_state_t * lrm_state) { if (!lrm_state->conn) { return; } lrm_state_disconnect_only(lrm_state); lrmd_api_delete(lrm_state->conn); lrm_state->conn = NULL; } int lrm_state_is_connected(lrm_state_t * lrm_state) { if (!lrm_state->conn) { return FALSE; } return ((lrmd_t *) lrm_state->conn)->cmds->is_connected(lrm_state->conn); } int lrm_state_poke_connection(lrm_state_t * lrm_state) { if (!lrm_state->conn) { return -1; } return ((lrmd_t *) lrm_state->conn)->cmds->poke_connection(lrm_state->conn); } int lrm_state_ipc_connect(lrm_state_t * lrm_state) { int ret; if (!lrm_state->conn) { lrm_state->conn = lrmd_api_new(); ((lrmd_t *) lrm_state->conn)->cmds->set_callback(lrm_state->conn, lrm_op_callback); } ret = ((lrmd_t *) lrm_state->conn)->cmds->connect(lrm_state->conn, CRM_SYSTEM_CRMD, NULL); if (ret != pcmk_ok) { lrm_state->num_lrm_register_fails++; } else { lrm_state->num_lrm_register_fails = 0; } return ret; } -static int -remote_proxy_dispatch_internal(const char *buffer, ssize_t length, gpointer userdata) -{ - /* Async responses from cib and friends back to clients via pacemaker_remoted */ - xmlNode *xml = NULL; - remote_proxy_t *proxy = userdata; - lrm_state_t *lrm_state = lrm_state_find(proxy->node_name); - uint32_t flags; - - if (lrm_state == NULL) { - return 0; - } - - xml = string2xml(buffer); - if (xml == NULL) { - crm_warn("Received a NULL msg from IPC service."); - return 1; - } - - flags = crm_ipc_buffer_flags(proxy->ipc); - if (flags & crm_ipc_proxied_relay_response) { - crm_trace("Passing response back to %.8s on %s: %.200s - request id: %d", proxy->session_id, proxy->node_name, buffer, proxy->last_request_id); - remote_proxy_relay_response(lrm_state->conn, proxy->session_id, xml, proxy->last_request_id); - proxy->last_request_id = 0; - - } else { - crm_trace("Passing event back to %.8s on %s: %.200s", proxy->session_id, proxy->node_name, buffer); - remote_proxy_relay_event(lrm_state->conn, proxy->session_id, xml); - } - free_xml(xml); - return 1; -} - -static void -remote_proxy_disconnected(void *userdata) -{ - remote_proxy_t *proxy = userdata; - lrm_state_t *lrm_state = lrm_state_find(proxy->node_name); - - crm_trace("Destroying %s (%p)", lrm_state->node_name, userdata); - - proxy->source = NULL; - proxy->ipc = NULL; - - if (lrm_state && lrm_state->conn) { - remote_proxy_notify_destroy(lrm_state->conn, proxy->session_id); - } - g_hash_table_remove(proxy_table, proxy->session_id); -} - static remote_proxy_t * -remote_proxy_new(const char *node_name, const char *session_id, const char *channel) +crmd_remote_proxy_new(lrmd_t *lrmd, const char *node_name, const char *session_id, const char *channel) { static struct ipc_client_callbacks proxy_callbacks = { - .dispatch = remote_proxy_dispatch_internal, + .dispatch = remote_proxy_dispatch, .destroy = remote_proxy_disconnected }; - remote_proxy_t *proxy = calloc(1, sizeof(remote_proxy_t)); - - proxy->node_name = strdup(node_name); - proxy->session_id = strdup(session_id); + remote_proxy_t *proxy = remote_proxy_new(lrmd, proxy_callbacks, node_name, session_id, channel); if (safe_str_eq(channel, CRM_SYSTEM_CRMD)) { proxy->is_local = TRUE; - } else { - proxy->source = mainloop_add_ipc_client(channel, G_PRIORITY_LOW, 0, proxy, &proxy_callbacks); - proxy->ipc = mainloop_get_ipc_client(proxy->source); - - if (proxy->source == NULL) { - remote_proxy_free(proxy); - return NULL; - } } - - crm_trace("created proxy session ID %s", proxy->session_id); - g_hash_table_insert(proxy_table, proxy->session_id, proxy); - return proxy; } gboolean crmd_is_proxy_session(const char *session) { return g_hash_table_lookup(proxy_table, session) ? TRUE : FALSE; } void crmd_proxy_send(const char *session, xmlNode *msg) { remote_proxy_t *proxy = g_hash_table_lookup(proxy_table, session); lrm_state_t *lrm_state = NULL; if (!proxy) { return; } crm_log_xml_trace(msg, "to-proxy"); lrm_state = lrm_state_find(proxy->node_name); if (lrm_state) { crm_trace("Sending event to %.8s on %s", proxy->session_id, proxy->node_name); - remote_proxy_relay_event(lrm_state->conn, session, msg); + remote_proxy_relay_event(proxy, msg); } } static void crmd_proxy_dispatch(const char *session, xmlNode *msg) { crm_log_xml_trace(msg, "CRMd-PROXY[inbound]"); crm_xml_add(msg, F_CRM_SYS_FROM, session); if (crmd_authorize_message(msg, NULL, session)) { route_message(C_IPC_MESSAGE, msg); } trigger_fsa(fsa_source); } static void remote_config_check(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data) { if (rc != pcmk_ok) { crm_err("Query resulted in an error: %s", pcmk_strerror(rc)); if (rc == -EACCES || rc == -pcmk_err_schema_validation) { crm_err("The cluster is mis-configured - shutting down and staying down"); } } else { lrmd_t * lrmd = (lrmd_t *)user_data; crm_time_t *now = crm_time_new(NULL); GHashTable *config_hash = g_hash_table_new_full( crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); crm_debug("Call %d : Parsing CIB options", call_id); - + unpack_instance_attributes( output, output, XML_CIB_TAG_PROPSET, NULL, config_hash, CIB_OPTIONS_FIRST, FALSE, now); /* Now send it to the remote peer */ remote_proxy_check(lrmd, config_hash); g_hash_table_destroy(config_hash); crm_time_free(now); } } static void -remote_proxy_cb(lrmd_t *lrmd, void *userdata, xmlNode *msg) +crmd_remote_proxy_cb(lrmd_t *lrmd, void *userdata, xmlNode *msg) { lrm_state_t *lrm_state = userdata; - const char *op = crm_element_value(msg, F_LRMD_IPC_OP); const char *session = crm_element_value(msg, F_LRMD_IPC_SESSION); - int msg_id = 0; - - /* sessions are raw ipc connections to IPC, - * all we do is proxy requests/responses exactly - * like they are given to us at the ipc level. */ + remote_proxy_t *proxy = g_hash_table_lookup(proxy_table, session); - CRM_CHECK(op != NULL, return); - CRM_CHECK(session != NULL, return); + const char *op = crm_element_value(msg, F_LRMD_IPC_OP); + if (safe_str_eq(op, LRMD_IPC_OP_NEW)) { + const char *channel = crm_element_value(msg, F_LRMD_IPC_IPC_SERVER); - crm_element_value_int(msg, F_LRMD_IPC_MSG_ID, &msg_id); - /* This is msg from remote ipc client going to real ipc server */ + proxy = crmd_remote_proxy_new(lrmd, lrm_state->node_name, session, channel); + if (proxy != NULL) { + /* Look up stonith-watchdog-timeout and send to the remote peer for validation */ + int rc = fsa_cib_conn->cmds->query(fsa_cib_conn, XML_CIB_TAG_CRMCONFIG, NULL, cib_scope_local); + fsa_cib_conn->cmds->register_callback_full(fsa_cib_conn, rc, 10, FALSE, lrmd, + "remote_config_check", remote_config_check, NULL); + } - if (safe_str_eq(op, LRMD_IPC_OP_SHUTDOWN_REQ)) { + } else if (safe_str_eq(op, LRMD_IPC_OP_SHUTDOWN_REQ)) { char *now_s = NULL; time_t now = time(NULL); crm_notice("%s requested shutdown of its remote connection", lrm_state->node_name); now_s = crm_itoa(now); update_attrd(lrm_state->node_name, XML_CIB_ATTR_SHUTDOWN, now_s, NULL, TRUE); free(now_s); remote_proxy_ack_shutdown(lrmd); crm_warn("Reconnection attempts to %s may result in failures that must be cleared", lrm_state->node_name); - return; - } else if (safe_str_eq(op, LRMD_IPC_OP_NEW)) { - int rc; - const char *channel = crm_element_value(msg, F_LRMD_IPC_IPC_SERVER); + } else if (safe_str_eq(op, LRMD_IPC_OP_REQUEST) && proxy->is_local) { + /* this is for the crmd, which we are, so don't try + * and connect/send to ourselves over ipc. instead + * do it directly. + */ + int flags = 0; + xmlNode *request = get_message_xml(msg, F_LRMD_IPC_MSG); - CRM_CHECK(channel != NULL, return); + crmd_proxy_dispatch(session, request); + crm_element_value_int(msg, F_LRMD_IPC_MSG_FLAGS, &flags); - if (remote_proxy_new(lrm_state->node_name, session, channel) == NULL) { - remote_proxy_notify_destroy(lrmd, session); - } - crm_trace("new remote proxy client established to %s, session id %s", channel, session); + if (flags & crm_ipc_client_response) { + int msg_id = 0; + xmlNode *op_reply = create_xml_node(NULL, "ack"); - /* Look up stonith-watchdog-timeout and send to the remote peer for validation */ - rc = fsa_cib_conn->cmds->query(fsa_cib_conn, XML_CIB_TAG_CRMCONFIG, NULL, cib_scope_local); - fsa_cib_conn->cmds->register_callback_full(fsa_cib_conn, rc, 10, FALSE, lrmd, "remote_config_check", remote_config_check, NULL); - - } else if (safe_str_eq(op, LRMD_IPC_OP_DESTROY)) { - remote_proxy_end_session(session); + crm_xml_add(op_reply, "function", __FUNCTION__); + crm_xml_add_int(op_reply, "line", __LINE__); - } else if (safe_str_eq(op, LRMD_IPC_OP_REQUEST)) { - int flags = 0; - xmlNode *request = get_message_xml(msg, F_LRMD_IPC_MSG); - const char *name = crm_element_value(msg, F_LRMD_IPC_CLIENT); - remote_proxy_t *proxy = g_hash_table_lookup(proxy_table, session); - - CRM_CHECK(request != NULL, return); - - if (proxy == NULL) { - /* proxy connection no longer exists */ - remote_proxy_notify_destroy(lrmd, session); - return; - } else if ((proxy->is_local == FALSE) && (crm_ipc_connected(proxy->ipc) == FALSE)) { - remote_proxy_end_session(session); - return; - } - proxy->last_request_id = 0; - crm_element_value_int(msg, F_LRMD_IPC_MSG_FLAGS, &flags); - crm_xml_add(request, XML_ACL_TAG_ROLE, "pacemaker-remote"); - -#if ENABLE_ACL - CRM_ASSERT(lrm_state->node_name); - crm_acl_get_set_user(request, F_LRMD_IPC_USER, lrm_state->node_name); -#endif - - if (proxy->is_local) { - /* this is for the crmd, which we are, so don't try - * and connect/send to ourselves over ipc. instead - * do it directly. */ - crmd_proxy_dispatch(session, request); - if (flags & crm_ipc_client_response) { - xmlNode *op_reply = create_xml_node(NULL, "ack"); - - crm_xml_add(op_reply, "function", __FUNCTION__); - crm_xml_add_int(op_reply, "line", __LINE__); - remote_proxy_relay_response(lrmd, session, op_reply, msg_id); - free_xml(op_reply); - } - - } else if(is_set(flags, crm_ipc_proxied)) { - const char *type = crm_element_value(request, F_TYPE); - int rc = 0; - - if (safe_str_eq(type, T_ATTRD) - && crm_element_value(request, F_ATTRD_HOST) == NULL) { - crm_xml_add(request, F_ATTRD_HOST, proxy->node_name); - } - - rc = crm_ipc_send(proxy->ipc, request, flags, 5000, NULL); - - if(rc < 0) { - xmlNode *op_reply = create_xml_node(NULL, "nack"); - - crm_err("Could not relay %s request %d from %s to %s for %s: %s (%d)", - op, msg_id, proxy->node_name, crm_ipc_name(proxy->ipc), name, pcmk_strerror(rc), rc); - - /* Send a n'ack so the caller doesn't block */ - crm_xml_add(op_reply, "function", __FUNCTION__); - crm_xml_add_int(op_reply, "line", __LINE__); - crm_xml_add_int(op_reply, "rc", rc); - remote_proxy_relay_response(lrmd, session, op_reply, msg_id); - free_xml(op_reply); - - } else { - crm_trace("Relayed %s request %d from %s to %s for %s", - op, msg_id, proxy->node_name, crm_ipc_name(proxy->ipc), name); - proxy->last_request_id = msg_id; - } - - } else { - int rc = pcmk_ok; - xmlNode *op_reply = NULL; - /* For backwards compatibility with pacemaker_remoted <= 1.1.10 */ - - crm_trace("Relaying %s request %d from %s to %s for %s", - op, msg_id, proxy->node_name, crm_ipc_name(proxy->ipc), name); - - rc = crm_ipc_send(proxy->ipc, request, flags, 10000, &op_reply); - if(rc < 0) { - crm_err("Could not relay %s request %d from %s to %s for %s: %s (%d)", - op, msg_id, proxy->node_name, crm_ipc_name(proxy->ipc), name, pcmk_strerror(rc), rc); - } else { - crm_trace("Relayed %s request %d from %s to %s for %s", - op, msg_id, proxy->node_name, crm_ipc_name(proxy->ipc), name); - } - - if(op_reply) { - remote_proxy_relay_response(lrmd, session, op_reply, msg_id); - free_xml(op_reply); - } + crm_element_value_int(msg, F_LRMD_IPC_MSG_ID, &msg_id); + remote_proxy_relay_response(proxy, op_reply, msg_id); + + free_xml(op_reply); } + } else { - crm_err("Unknown proxy operation: %s", op); + remote_proxy_cb(lrmd, lrm_state->node_name, msg); } } + int lrm_state_remote_connect_async(lrm_state_t * lrm_state, const char *server, int port, int timeout_ms) { int ret; if (!lrm_state->conn) { lrm_state->conn = lrmd_remote_api_new(lrm_state->node_name, server, port); if (!lrm_state->conn) { return -1; } ((lrmd_t *) lrm_state->conn)->cmds->set_callback(lrm_state->conn, remote_lrm_op_callback); - lrmd_internal_set_proxy_callback(lrm_state->conn, lrm_state, remote_proxy_cb); + lrmd_internal_set_proxy_callback(lrm_state->conn, lrm_state, crmd_remote_proxy_cb); } crm_trace("initiating remote connection to %s at %d with timeout %d", server, port, timeout_ms); ret = ((lrmd_t *) lrm_state->conn)->cmds->connect_async(lrm_state->conn, lrm_state->node_name, timeout_ms); if (ret != pcmk_ok) { lrm_state->num_lrm_register_fails++; } else { lrm_state->num_lrm_register_fails = 0; } return ret; } int lrm_state_get_metadata(lrm_state_t * lrm_state, const char *class, const char *provider, const char *agent, char **output, enum lrmd_call_options options) { if (!lrm_state->conn) { return -ENOTCONN; } /* Optimize this... only retrieve metadata from local lrmd connection. Perhaps consider * caching result. */ return ((lrmd_t *) lrm_state->conn)->cmds->get_metadata(lrm_state->conn, class, provider, agent, output, options); } int lrm_state_cancel(lrm_state_t * lrm_state, const char *rsc_id, const char *action, int interval) { if (!lrm_state->conn) { return -ENOTCONN; } /* Figure out a way to make this async? * NOTICE: Currently it's synced and directly acknowledged in do_lrm_invoke(). */ if (is_remote_lrmd_ra(NULL, NULL, rsc_id)) { return remote_ra_cancel(lrm_state, rsc_id, action, interval); } return ((lrmd_t *) lrm_state->conn)->cmds->cancel(lrm_state->conn, rsc_id, action, interval); } lrmd_rsc_info_t * lrm_state_get_rsc_info(lrm_state_t * lrm_state, const char *rsc_id, enum lrmd_call_options options) { lrmd_rsc_info_t *rsc = NULL; if (!lrm_state->conn) { return NULL; } if (is_remote_lrmd_ra(NULL, NULL, rsc_id)) { return remote_ra_get_rsc_info(lrm_state, rsc_id); } rsc = g_hash_table_lookup(lrm_state->rsc_info_cache, rsc_id); if (rsc == NULL) { /* only contact the lrmd if we don't already have a cached rsc info */ rsc = ((lrmd_t *) lrm_state->conn)->cmds->get_rsc_info(lrm_state->conn, rsc_id, options); if (rsc == NULL) { return NULL; } /* cache the result */ g_hash_table_insert(lrm_state->rsc_info_cache, rsc->id, rsc); } return lrmd_copy_rsc_info(rsc); } int lrm_state_exec(lrm_state_t * lrm_state, const char *rsc_id, const char *action, const char *userdata, int interval, /* ms */ int timeout, /* ms */ int start_delay, /* ms */ lrmd_key_value_t * params) { if (!lrm_state->conn) { lrmd_key_value_freeall(params); return -ENOTCONN; } if (is_remote_lrmd_ra(NULL, NULL, rsc_id)) { return remote_ra_exec(lrm_state, rsc_id, action, userdata, interval, timeout, start_delay, params); } return ((lrmd_t *) lrm_state->conn)->cmds->exec(lrm_state->conn, rsc_id, action, userdata, interval, timeout, start_delay, lrmd_opt_notify_changes_only, params); } int lrm_state_register_rsc(lrm_state_t * lrm_state, const char *rsc_id, const char *class, const char *provider, const char *agent, enum lrmd_call_options options) { if (!lrm_state->conn) { return -ENOTCONN; } /* optimize this... this function is a synced round trip from client to daemon. * The crmd/lrm.c code path should be re-factored to allow the register of resources * to be performed async. The lrmd client api needs to make an async version * of register available. */ if (is_remote_lrmd_ra(agent, provider, NULL)) { return lrm_state_find_or_create(rsc_id) ? pcmk_ok : -1; } return ((lrmd_t *) lrm_state->conn)->cmds->register_rsc(lrm_state->conn, rsc_id, class, provider, agent, options); } int lrm_state_unregister_rsc(lrm_state_t * lrm_state, const char *rsc_id, enum lrmd_call_options options) { if (!lrm_state->conn) { return -ENOTCONN; } /* optimize this... this function is a synced round trip from client to daemon. * The crmd/lrm.c code path that uses this function should always treat it as an * async operation. The lrmd client api needs to make an async version unreg available. */ if (is_remote_lrmd_ra(NULL, NULL, rsc_id)) { lrm_state_destroy(rsc_id); return pcmk_ok; } g_hash_table_remove(lrm_state->rsc_info_cache, rsc_id); return ((lrmd_t *) lrm_state->conn)->cmds->unregister_rsc(lrm_state->conn, rsc_id, options); } diff --git a/include/crm_internal.h b/include/crm_internal.h index a8fee470eb..0f8219f4cc 100644 --- a/include/crm_internal.h +++ b/include/crm_internal.h @@ -1,376 +1,386 @@ /* crm_internal.h */ /* * Copyright (C) 2006 - 2008 * Andrew Beekhof * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CRM_INTERNAL__H # define CRM_INTERNAL__H # include # include # include # include # include # include # include # include # include /* Dynamic loading of libraries */ void *find_library_function(void **handle, const char *lib, const char *fn, int fatal); void *convert_const_pointer(const void *ptr); /* For ACLs */ char *uid2username(uid_t uid); void determine_request_user(const char *user, xmlNode * request, const char *field); const char *crm_acl_get_set_user(xmlNode * request, const char *field, const char *peer_user); # if ENABLE_ACL # include static inline gboolean is_privileged(const char *user) { if (user == NULL) { return FALSE; } else if (strcmp(user, CRM_DAEMON_USER) == 0) { return TRUE; } else if (strcmp(user, "root") == 0) { return TRUE; } return FALSE; } # endif /* CLI option processing*/ # ifdef HAVE_GETOPT_H # include # else # define no_argument 0 # define required_argument 1 # endif # define pcmk_option_default 0x00000 # define pcmk_option_hidden 0x00001 # define pcmk_option_paragraph 0x00002 # define pcmk_option_example 0x00004 struct crm_option { /* Fields from 'struct option' in getopt.h */ /* name of long option */ const char *name; /* * one of no_argument, required_argument, and optional_argument: * whether option takes an argument */ int has_arg; /* if not NULL, set *flag to val when option found */ int *flag; /* if flag not NULL, value to set *flag to; else return value */ int val; /* Custom fields */ const char *desc; long flags; }; void crm_set_options(const char *short_options, const char *usage, struct crm_option *long_options, const char *app_desc); int crm_get_option(int argc, char **argv, int *index); int crm_get_option_long(int argc, char **argv, int *index, const char **longname); int crm_help(char cmd, int exit_code); /* Cluster Option Processing */ typedef struct pe_cluster_option_s { const char *name; const char *alt_name; const char *type; const char *values; const char *default_value; gboolean(*is_valid) (const char *); const char *description_short; const char *description_long; } pe_cluster_option; const char *cluster_option(GHashTable * options, gboolean(*validate) (const char *), const char *name, const char *old_name, const char *def_value); const char *get_cluster_pref(GHashTable * options, pe_cluster_option * option_list, int len, const char *name); void config_metadata(const char *name, const char *version, const char *desc_short, const char *desc_long, pe_cluster_option * option_list, int len); void verify_all_options(GHashTable * options, pe_cluster_option * option_list, int len); gboolean check_time(const char *value); gboolean check_timer(const char *value); gboolean check_boolean(const char *value); gboolean check_number(const char *value); gboolean check_quorum(const char *value); gboolean check_script(const char *value); gboolean check_utilization(const char *value); long crm_get_sbd_timeout(void); gboolean check_sbd_timeout(const char *value); /* Shared PE/crmd functionality */ void filter_action_parameters(xmlNode * param_set, const char *version); /* Resource operation updates */ xmlNode *create_operation_update(xmlNode * parent, lrmd_event_data_t * event, const char * caller_version, int target_rc, const char * node, const char * origin, int level); /* char2score */ extern int node_score_red; extern int node_score_green; extern int node_score_yellow; extern int node_score_infinity; /* Assorted convenience functions */ int crm_pid_active(long pid, const char *daemon); void crm_make_daemon(const char *name, gboolean daemonize, const char *pidfile); char *generate_op_key(const char *rsc_id, const char *op_type, int interval); char *generate_notify_key(const char *rsc_id, const char *notify_type, const char *op_type); char *generate_transition_magic_v202(const char *transition_key, int op_status); char *generate_transition_magic(const char *transition_key, int op_status, int op_rc); char *generate_transition_key(int action, int transition_id, int target_rc, const char *node); static inline long long crm_clear_bit(const char *function, const char *target, long long word, long long bit) { long long rc = (word & ~bit); if (rc == word) { /* Unchanged */ } else if (target) { crm_trace("Bit 0x%.8llx for %s cleared by %s", bit, target, function); } else { crm_trace("Bit 0x%.8llx cleared by %s", bit, function); } return rc; } static inline long long crm_set_bit(const char *function, const char *target, long long word, long long bit) { long long rc = (word | bit); if (rc == word) { /* Unchanged */ } else if (target) { crm_trace("Bit 0x%.8llx for %s set by %s", bit, target, function); } else { crm_trace("Bit 0x%.8llx set by %s", bit, function); } return rc; } # define set_bit(word, bit) word = crm_set_bit(__FUNCTION__, NULL, word, bit) # define clear_bit(word, bit) word = crm_clear_bit(__FUNCTION__, NULL, word, bit) char *generate_hash_key(const char *crm_msg_reference, const char *sys); /*! remote tcp/tls helper functions */ typedef struct crm_remote_s crm_remote_t; int crm_remote_send(crm_remote_t * remote, xmlNode * msg); int crm_remote_ready(crm_remote_t * remote, int total_timeout /*ms */ ); gboolean crm_remote_recv(crm_remote_t * remote, int total_timeout /*ms */ , int *disconnected); xmlNode *crm_remote_parse_buffer(crm_remote_t * remote); int crm_remote_tcp_connect(const char *host, int port); int crm_remote_tcp_connect_async(const char *host, int port, int timeout, /*ms */ int *timer_id, void *userdata, void (*callback) (void *userdata, int sock)); int crm_remote_accept(int ssock); # ifdef HAVE_GNUTLS_GNUTLS_H /*! * \internal * \brief Initiate the client handshake after establishing the tcp socket. * \note This is a blocking function, it will block until the entire handshake * is complete or until the timeout period is reached. * \retval 0 success * \retval negative, failure */ int crm_initiate_client_tls_handshake(crm_remote_t * remote, int timeout_ms); /*! * \internal * \brief Create client or server session for anon DH encryption credentials * \param sock, the socket the session will use for transport * \param type, GNUTLS_SERVER or GNUTLS_CLIENT * \param credentials, gnutls_anon_server_credentials_t or gnutls_anon_client_credentials_t * * \retval gnutls_session_t * on success * \retval NULL on failure */ void *crm_create_anon_tls_session(int sock, int type, void *credentials); /*! * \internal * \brief Create client or server session for PSK credentials * \param sock, the socket the session will use for transport * \param type, GNUTLS_SERVER or GNUTLS_CLIENT * \param credentials, gnutls_psk_server_credentials_t or gnutls_osk_client_credentials_t * * \retval gnutls_session_t * on success * \retval NULL on failure */ void *create_psk_tls_session(int csock, int type, void *credentials); # endif # define REMOTE_MSG_TERMINATOR "\r\n\r\n" const char *daemon_option(const char *option); void set_daemon_option(const char *option, const char *value); gboolean daemon_option_enabled(const char *daemon, const char *option); void strip_text_nodes(xmlNode * xml); void pcmk_panic(const char *origin); void sysrq_init(void); pid_t pcmk_locate_sbd(void); long crm_pidfile_inuse(const char *filename, long mypid, const char *daemon); long crm_read_pidfile(const char *filename); # define crm_config_err(fmt...) { crm_config_error = TRUE; crm_err(fmt); } # define crm_config_warn(fmt...) { crm_config_warning = TRUE; crm_warn(fmt); } # define attrd_channel T_ATTRD # define F_ATTRD_KEY "attr_key" # define F_ATTRD_ATTRIBUTE "attr_name" # define F_ATTRD_REGEX "attr_regex" # define F_ATTRD_TASK "task" # define F_ATTRD_VALUE "attr_value" # define F_ATTRD_SET "attr_set" # define F_ATTRD_IS_REMOTE "attr_is_remote" # define F_ATTRD_IS_PRIVATE "attr_is_private" # define F_ATTRD_SECTION "attr_section" # define F_ATTRD_DAMPEN "attr_dampening" # define F_ATTRD_IGNORE_LOCALLY "attr_ignore_locally" # define F_ATTRD_HOST "attr_host" # define F_ATTRD_HOST_ID "attr_host_id" # define F_ATTRD_USER "attr_user" # define F_ATTRD_WRITER "attr_writer" # define F_ATTRD_VERSION "attr_version" /* attrd operations */ # define ATTRD_OP_PEER_REMOVE "peer-remove" # define ATTRD_OP_UPDATE "update" # define ATTRD_OP_UPDATE_BOTH "update-both" # define ATTRD_OP_UPDATE_DELAY "update-delay" # define ATTRD_OP_QUERY "query" # define ATTRD_OP_REFRESH "refresh" # define ATTRD_OP_FLUSH "flush" # define ATTRD_OP_SYNC "sync" # define ATTRD_OP_SYNC_RESPONSE "sync-response" # if SUPPORT_COROSYNC # if CS_USES_LIBQB # include # include typedef struct qb_ipc_request_header cs_ipc_header_request_t; typedef struct qb_ipc_response_header cs_ipc_header_response_t; # else # include # include # include typedef coroipc_request_header_t cs_ipc_header_request_t; typedef coroipc_response_header_t cs_ipc_header_response_t; # endif # else typedef struct { int size __attribute__ ((aligned(8))); int id __attribute__ ((aligned(8))); } __attribute__ ((aligned(8))) cs_ipc_header_request_t; typedef struct { int size __attribute__ ((aligned(8))); int id __attribute__ ((aligned(8))); int error __attribute__ ((aligned(8))); } __attribute__ ((aligned(8))) cs_ipc_header_response_t; # endif void attrd_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb); void stonith_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb); qb_ipcs_service_t * crmd_ipc_server_init(struct qb_ipcs_service_handlers *cb); void cib_ipc_servers_init(qb_ipcs_service_t **ipcs_ro, qb_ipcs_service_t **ipcs_rw, qb_ipcs_service_t **ipcs_shm, struct qb_ipcs_service_handlers *ro_cb, struct qb_ipcs_service_handlers *rw_cb); void cib_ipc_servers_destroy(qb_ipcs_service_t *ipcs_ro, qb_ipcs_service_t *ipcs_rw, qb_ipcs_service_t *ipcs_shm); static inline void *realloc_safe(void *ptr, size_t size) { void *ret = realloc(ptr, size); if (ret == NULL) { free(ptr); /* make coverity happy */ abort(); } return ret; } const char *crm_xml_add_last_written(xmlNode *xml_node); void crm_xml_dump(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth); void crm_buffer_add_char(char **buffer, int *offset, int *max, char c); gboolean crm_digest_verify(xmlNode *input, const char *expected); /* cross-platform compatibility functions */ char *crm_compat_realpath(const char *path); /* IPC Proxy Backend Shared Functions */ typedef struct remote_proxy_s { char *node_name; char *session_id; gboolean is_local; crm_ipc_t *ipc; mainloop_io_t *source; uint32_t last_request_id; + lrmd_t *lrm; } remote_proxy_t; -void remote_proxy_notify_destroy(lrmd_t *lrmd, const char *session_id); + +remote_proxy_t *remote_proxy_new( + lrmd_t *lrmd, struct ipc_client_callbacks proxy_callbacks, + const char *node_name, const char *session_id, const char *channel); + +int remote_proxy_check(lrmd_t *lrmd, GHashTable *hash); +void remote_proxy_cb(lrmd_t *lrmd, const char *node_name, xmlNode *msg); void remote_proxy_ack_shutdown(lrmd_t *lrmd); -void remote_proxy_relay_event(lrmd_t *lrmd, const char *session_id, xmlNode *msg); -void remote_proxy_relay_response(lrmd_t *lrmd, const char *session_id, xmlNode *msg, int msg_id); -void remote_proxy_end_session(const char *session); + +int remote_proxy_dispatch(const char *buffer, ssize_t length, gpointer userdata); +void remote_proxy_disconnected(gpointer data); void remote_proxy_free(gpointer data); -int remote_proxy_check(lrmd_t * lrmd, GHashTable *hash); + +void remote_proxy_relay_event(remote_proxy_t *proxy, xmlNode *msg); +void remote_proxy_relay_response(remote_proxy_t *proxy, xmlNode *msg, int msg_id); + char* crm_versioned_param_summary(xmlNode *versioned_params, const char *name); void crm_summarize_versioned_params(xmlNode *param_set, xmlNode *versioned_params); #endif /* CRM_INTERNAL__H */ diff --git a/lib/lrmd/proxy_common.c b/lib/lrmd/proxy_common.c index eb17e4e267..a250bae800 100644 --- a/lib/lrmd/proxy_common.c +++ b/lib/lrmd/proxy_common.c @@ -1,114 +1,293 @@ /* * Copyright (c) 2015 David Vossel * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser 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 library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include #include #include #include #include #include int lrmd_internal_proxy_send(lrmd_t * lrmd, xmlNode *msg); GHashTable *proxy_table = NULL; -void +static void remote_proxy_notify_destroy(lrmd_t *lrmd, const char *session_id) { /* sending to the remote node that an ipc connection has been destroyed */ xmlNode *msg = create_xml_node(NULL, T_LRMD_IPC_PROXY); crm_xml_add(msg, F_LRMD_IPC_OP, LRMD_IPC_OP_DESTROY); crm_xml_add(msg, F_LRMD_IPC_SESSION, session_id); lrmd_internal_proxy_send(lrmd, msg); free_xml(msg); } /*! * \brief Send an acknowledgment of a remote proxy shutdown request. * * \param[in] lrmd Connection to proxy */ void remote_proxy_ack_shutdown(lrmd_t *lrmd) { xmlNode *msg = create_xml_node(NULL, T_LRMD_IPC_PROXY); crm_xml_add(msg, F_LRMD_IPC_OP, LRMD_IPC_OP_SHUTDOWN_ACK); lrmd_internal_proxy_send(lrmd, msg); free_xml(msg); } void -remote_proxy_relay_event(lrmd_t *lrmd, const char *session_id, xmlNode *msg) +remote_proxy_relay_event(remote_proxy_t *proxy, xmlNode *msg) { /* sending to the remote node an event msg. */ xmlNode *event = create_xml_node(NULL, T_LRMD_IPC_PROXY); crm_xml_add(event, F_LRMD_IPC_OP, LRMD_IPC_OP_EVENT); - crm_xml_add(event, F_LRMD_IPC_SESSION, session_id); + crm_xml_add(event, F_LRMD_IPC_SESSION, proxy->session_id); add_message_xml(event, F_LRMD_IPC_MSG, msg); crm_log_xml_explicit(event, "EventForProxy"); - lrmd_internal_proxy_send(lrmd, event); + lrmd_internal_proxy_send(proxy->lrm, event); free_xml(event); } void -remote_proxy_relay_response(lrmd_t *lrmd, const char *session_id, xmlNode *msg, int msg_id) +remote_proxy_relay_response(remote_proxy_t *proxy, xmlNode *msg, int msg_id) { /* sending to the remote node a response msg. */ xmlNode *response = create_xml_node(NULL, T_LRMD_IPC_PROXY); crm_xml_add(response, F_LRMD_IPC_OP, LRMD_IPC_OP_RESPONSE); - crm_xml_add(response, F_LRMD_IPC_SESSION, session_id); + crm_xml_add(response, F_LRMD_IPC_SESSION, proxy->session_id); crm_xml_add_int(response, F_LRMD_IPC_MSG_ID, msg_id); add_message_xml(response, F_LRMD_IPC_MSG, msg); - lrmd_internal_proxy_send(lrmd, response); + lrmd_internal_proxy_send(proxy->lrm, response); free_xml(response); } -void -remote_proxy_end_session(const char *session) +static void +remote_proxy_end_session(remote_proxy_t *proxy) { - remote_proxy_t *proxy = g_hash_table_lookup(proxy_table, session); - if (proxy == NULL) { return; } crm_trace("ending session ID %s", proxy->session_id); if (proxy->source) { mainloop_del_ipc_client(proxy->source); } } void remote_proxy_free(gpointer data) { remote_proxy_t *proxy = data; crm_trace("freed proxy session ID %s", proxy->session_id); free(proxy->node_name); free(proxy->session_id); free(proxy); } +int +remote_proxy_dispatch(const char *buffer, ssize_t length, gpointer userdata) +{ + /* Async responses from cib and friends back to clients via pacemaker_remoted */ + xmlNode *xml = NULL; + uint32_t flags = 0; + remote_proxy_t *proxy = userdata; + + xml = string2xml(buffer); + if (xml == NULL) { + crm_warn("Received a NULL msg from IPC service."); + return 1; + } + + flags = crm_ipc_buffer_flags(proxy->ipc); + if (flags & crm_ipc_proxied_relay_response) { + crm_trace("Passing response back to %.8s on %s: %.200s - request id: %d", proxy->session_id, proxy->node_name, buffer, proxy->last_request_id); + remote_proxy_relay_response(proxy, xml, proxy->last_request_id); + proxy->last_request_id = 0; + + } else { + crm_trace("Passing event back to %.8s on %s: %.200s", proxy->session_id, proxy->node_name, buffer); + remote_proxy_relay_event(proxy, xml); + } + free_xml(xml); + return 1; +} + + +void +remote_proxy_disconnected(gpointer userdata) +{ + remote_proxy_t *proxy = userdata; + crm_trace("destroying %p", proxy); + + proxy->source = NULL; + proxy->ipc = NULL; + + if(proxy->lrm) { + remote_proxy_notify_destroy(proxy->lrm, proxy->session_id); + proxy->lrm = NULL; + } + + g_hash_table_remove(proxy_table, proxy->session_id); +} + +remote_proxy_t * +remote_proxy_new(lrmd_t *lrmd, struct ipc_client_callbacks proxy_callbacks, + const char *node_name, const char *session_id, const char *channel) +{ + remote_proxy_t *proxy = NULL; + + if(channel == NULL) { + crm_err("No channel specified to proxy"); + remote_proxy_notify_destroy(lrmd, session_id); + return NULL; + } + + proxy = calloc(1, sizeof(remote_proxy_t)); + + proxy->node_name = strdup(node_name); + proxy->session_id = strdup(session_id); + + proxy->source = mainloop_add_ipc_client(channel, G_PRIORITY_LOW, 0, proxy, &proxy_callbacks); + proxy->ipc = mainloop_get_ipc_client(proxy->source); + proxy->lrm = lrmd; + + if (proxy->source == NULL) { + remote_proxy_free(proxy); + remote_proxy_notify_destroy(lrmd, session_id); + return NULL; + } + + crm_trace("new remote proxy client established to %s on %s, session id %s", + channel, node_name, session_id); + g_hash_table_insert(proxy_table, proxy->session_id, proxy); + + return proxy; +} + +void +remote_proxy_cb(lrmd_t *lrmd, const char *node_name, xmlNode *msg) +{ + const char *op = crm_element_value(msg, F_LRMD_IPC_OP); + const char *session = crm_element_value(msg, F_LRMD_IPC_SESSION); + remote_proxy_t *proxy = g_hash_table_lookup(proxy_table, session); + int msg_id = 0; + + /* sessions are raw ipc connections to IPC, + * all we do is proxy requests/responses exactly + * like they are given to us at the ipc level. */ + + CRM_CHECK(op != NULL, return); + CRM_CHECK(session != NULL, return); + + crm_element_value_int(msg, F_LRMD_IPC_MSG_ID, &msg_id); + /* This is msg from remote ipc client going to real ipc server */ + + if (safe_str_eq(op, LRMD_IPC_OP_DESTROY)) { + remote_proxy_end_session(proxy); + + } else if (safe_str_eq(op, LRMD_IPC_OP_REQUEST)) { + int flags = 0; + xmlNode *request = get_message_xml(msg, F_LRMD_IPC_MSG); + const char *name = crm_element_value(msg, F_LRMD_IPC_CLIENT); + + CRM_CHECK(request != NULL, return); + + if (proxy == NULL) { + /* proxy connection no longer exists */ + remote_proxy_notify_destroy(lrmd, session); + return; + } else if ((proxy->is_local == FALSE) && (crm_ipc_connected(proxy->ipc) == FALSE)) { + remote_proxy_end_session(proxy); + return; + } + proxy->last_request_id = 0; + crm_element_value_int(msg, F_LRMD_IPC_MSG_FLAGS, &flags); + crm_xml_add(request, XML_ACL_TAG_ROLE, "pacemaker-remote"); + +#if ENABLE_ACL + CRM_ASSERT(node_name); + crm_acl_get_set_user(request, F_LRMD_IPC_USER, node_name); +#endif + + if(is_set(flags, crm_ipc_proxied)) { + const char *type = crm_element_value(request, F_TYPE); + int rc = 0; + + if (safe_str_eq(type, T_ATTRD) + && crm_element_value(request, F_ATTRD_HOST) == NULL) { + crm_xml_add(request, F_ATTRD_HOST, proxy->node_name); + } + + rc = crm_ipc_send(proxy->ipc, request, flags, 5000, NULL); + + if(rc < 0) { + xmlNode *op_reply = create_xml_node(NULL, "nack"); + + crm_err("Could not relay %s request %d from %s to %s for %s: %s (%d)", + op, msg_id, proxy->node_name, crm_ipc_name(proxy->ipc), name, pcmk_strerror(rc), rc); + + /* Send a n'ack so the caller doesn't block */ + crm_xml_add(op_reply, "function", __FUNCTION__); + crm_xml_add_int(op_reply, "line", __LINE__); + crm_xml_add_int(op_reply, "rc", rc); + remote_proxy_relay_response(proxy, op_reply, msg_id); + free_xml(op_reply); + + } else { + crm_trace("Relayed %s request %d from %s to %s for %s", + op, msg_id, proxy->node_name, crm_ipc_name(proxy->ipc), name); + proxy->last_request_id = msg_id; + } + + } else { + int rc = pcmk_ok; + xmlNode *op_reply = NULL; + /* For backwards compatibility with pacemaker_remoted <= 1.1.10 */ + + crm_trace("Relaying %s request %d from %s to %s for %s", + op, msg_id, proxy->node_name, crm_ipc_name(proxy->ipc), name); + + rc = crm_ipc_send(proxy->ipc, request, flags, 10000, &op_reply); + if(rc < 0) { + crm_err("Could not relay %s request %d from %s to %s for %s: %s (%d)", + op, msg_id, proxy->node_name, crm_ipc_name(proxy->ipc), name, pcmk_strerror(rc), rc); + } else { + crm_trace("Relayed %s request %d from %s to %s for %s", + op, msg_id, proxy->node_name, crm_ipc_name(proxy->ipc), name); + } + + if(op_reply) { + remote_proxy_relay_response(proxy, op_reply, msg_id); + free_xml(op_reply); + } + } + } else { + crm_err("Unknown proxy operation: %s", op); + } +} diff --git a/lrmd/remote_ctl.c b/lrmd/remote_ctl.c index 41cab661ea..f3fc24f6ca 100644 --- a/lrmd/remote_ctl.c +++ b/lrmd/remote_ctl.c @@ -1,526 +1,395 @@ /* * Copyright (c) 2015 David Vossel * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser 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 library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include #include #include #include #include #include extern GHashTable *proxy_table; void lrmd_internal_set_proxy_callback(lrmd_t * lrmd, void *userdata, void (*callback)(lrmd_t *lrmd, void *userdata, xmlNode *msg)); /* *INDENT-OFF* */ static struct crm_option long_options[] = { {"help", 0, 0, '?'}, {"verbose", 0, 0, 'V', "\t\tPrint out logs and events to screen"}, {"quiet", 0, 0, 'Q', "\t\tSuppress all output to screen"}, {"tls", 1, 0, 'S', "\t\tSet tls host to contact"}, {"tls-port", 1, 0, 'p', "\t\tUse custom tls port"}, {"node", 1, 0, 'n', "\tNode name to use for ipc proxy"}, {"api-call", 1, 0, 'c', "\tDirectly relates to lrmd api functions"}, {"-spacer-", 1, 0, '-', "\nParameters for api-call option"}, {"action", 1, 0, 'a'}, {"rsc-id", 1, 0, 'r'}, {"provider", 1, 0, 'P'}, {"class", 1, 0, 'C'}, {"type", 1, 0, 'T'}, {"timeout", 1, 0, 't'}, {"param-key", 1, 0, 'k'}, {"param-val", 1, 0, 'v'}, {"-spacer-", 1, 0, '-'}, {0, 0, 0, 0} }; /* *INDENT-ON* */ static int wait_poke = 0; static int exec_call_id = 0; static gboolean client_start(gpointer user_data); static void try_connect(void); static struct { int verbose; int quiet; int print; int interval; int timeout; int port; const char *node_name; const char *api_call; const char *rsc_id; const char *provider; const char *class; const char *type; const char *action; const char *listen; const char *tls_host; lrmd_key_value_t *params; } options; GMainLoop *mainloop = NULL; lrmd_t *lrmd_conn = NULL; static void client_exit(int rc) { lrmd_api_delete(lrmd_conn); if (proxy_table) { g_hash_table_destroy(proxy_table); proxy_table = NULL; } exit(rc); } static void client_shutdown(int nsig) { lrmd_api_delete(lrmd_conn); lrmd_conn = NULL; } static void read_events(lrmd_event_data_t * event) { if (wait_poke && event->type == lrmd_event_poke) { client_exit(PCMK_OCF_OK); } if ((event->call_id == exec_call_id) && (event->type == lrmd_event_exec_complete)) { if (event->output) { crm_info("%s", event->output); } if (event->exit_reason) { fprintf(stderr, "%s%s\n", PCMK_OCF_REASON_PREFIX, event->exit_reason); } client_exit(event->rc); } } static gboolean timeout_err(gpointer data) { crm_err("timed out in remote_client"); client_exit(PCMK_OCF_TIMEOUT); return FALSE; } static void connection_events(lrmd_event_data_t * event) { int rc = event->connection_rc; if (event->type != lrmd_event_connect) { /* ignore */ return; } if (!rc) { client_start(NULL); return; } else { sleep(1); try_connect(); } } static void try_connect(void) { int tries = 10; static int num_tries = 0; int rc = 0; lrmd_conn->cmds->set_callback(lrmd_conn, connection_events); for (; num_tries < tries; num_tries++) { rc = lrmd_conn->cmds->connect_async(lrmd_conn, "lrmd", 10000); if (!rc) { num_tries++; return; /* we'll hear back in async callback */ } sleep(1); } crm_err("Failed to connect to pacemaker remote."); client_exit(PCMK_OCF_UNKNOWN_ERROR); } static gboolean client_start(gpointer user_data) { int rc = 0; if (!lrmd_conn->cmds->is_connected(lrmd_conn)) { try_connect(); /* async connect -- this function will get called back into */ return 0; } lrmd_conn->cmds->set_callback(lrmd_conn, read_events); if (safe_str_eq(options.api_call, "ipc_debug")) { /* Do nothing, leave connection up just for debugging ipc proxy */ return 0; } if (options.timeout) { g_timeout_add(options.timeout, timeout_err, NULL); } if (safe_str_eq(options.api_call, "metadata")) { char *output = NULL; rc = lrmd_conn->cmds->get_metadata(lrmd_conn, options.class, options.provider, options.type, &output, 0); if (rc == pcmk_ok) { printf("%s", output); free(output); client_exit(PCMK_OCF_OK); } client_exit(PCMK_OCF_UNKNOWN_ERROR); } else if (safe_str_eq(options.api_call, "poke")) { rc = lrmd_conn->cmds->poke_connection(lrmd_conn); if (rc != pcmk_ok) { client_exit(PCMK_OCF_UNKNOWN_ERROR); } wait_poke = 1; + } else { lrmd_rsc_info_t *rsc_info = NULL; rsc_info = lrmd_conn->cmds->get_rsc_info(lrmd_conn, options.rsc_id, 0); if (rsc_info == NULL) { rc = lrmd_conn->cmds->register_rsc(lrmd_conn, options.rsc_id, options.class, options.provider, options.type, 0); if (rc != 0){ crm_err("failed to register resource %s with pacemaker_remote. rc: %d", options.rsc_id, rc); client_exit(1); } } lrmd_free_rsc_info(rsc_info); rc = lrmd_conn->cmds->exec(lrmd_conn, options.rsc_id, options.action, NULL, options.interval, options.timeout, 0, 0, options.params); if (rc > 0) { exec_call_id = rc; } else { crm_err("execution of rsc %s failed. rc = %d", options.rsc_id, rc); client_exit(PCMK_OCF_UNKNOWN_ERROR); } } return 0; } -static int -remote_proxy_dispatch_internal(const char *buffer, ssize_t length, gpointer userdata) -{ - /* Async responses from cib and friends back to clients via pacemaker_remoted */ - xmlNode *xml = NULL; - remote_proxy_t *proxy = userdata; - uint32_t flags; - - xml = string2xml(buffer); - if (xml == NULL) { - crm_warn("Received a NULL msg from IPC service."); - return 1; - } - - flags = crm_ipc_buffer_flags(proxy->ipc); - if (flags & crm_ipc_proxied_relay_response) { - crm_trace("Passing response back to %.8s on %s: %.200s - request id: %d", proxy->session_id, proxy->node_name, buffer, proxy->last_request_id); - remote_proxy_relay_response(lrmd_conn, proxy->session_id, xml, proxy->last_request_id); - proxy->last_request_id = 0; - - } else { - crm_trace("Passing event back to %.8s on %s: %.200s", proxy->session_id, proxy->node_name, buffer); - remote_proxy_relay_event(lrmd_conn, proxy->session_id, xml); - } - free_xml(xml); - return 1; -} - static void -remote_proxy_disconnected(void *userdata) -{ - remote_proxy_t *proxy = userdata; - - crm_trace("destroying %p", userdata); - - proxy->source = NULL; - proxy->ipc = NULL; - - remote_proxy_notify_destroy(lrmd_conn, proxy->session_id); - g_hash_table_remove(proxy_table, proxy->session_id); -} - -static remote_proxy_t * -remote_proxy_new(const char *node_name, const char *session_id, const char *channel) -{ - static struct ipc_client_callbacks proxy_callbacks = { - .dispatch = remote_proxy_dispatch_internal, - .destroy = remote_proxy_disconnected - }; - remote_proxy_t *proxy = calloc(1, sizeof(remote_proxy_t)); - - proxy->node_name = strdup(node_name); - proxy->session_id = strdup(session_id); - - if (safe_str_eq(channel, CRM_SYSTEM_CRMD)) { - proxy->is_local = TRUE; - } else { - proxy->source = mainloop_add_ipc_client(channel, G_PRIORITY_LOW, 0, proxy, &proxy_callbacks); - proxy->ipc = mainloop_get_ipc_client(proxy->source); - - if (proxy->source == NULL) { - remote_proxy_free(proxy); - return NULL; - } - } - - crm_trace("created proxy session ID %s", proxy->session_id); - g_hash_table_insert(proxy_table, proxy->session_id, proxy); - - return proxy; -} - -static void -remote_proxy_cb(lrmd_t *lrmd, void *userdata, xmlNode *msg) +ctl_remote_proxy_cb(lrmd_t *lrmd, void *userdata, xmlNode *msg) { const char *op = crm_element_value(msg, F_LRMD_IPC_OP); const char *session = crm_element_value(msg, F_LRMD_IPC_SESSION); - int msg_id = 0; - /* sessions are raw ipc connections to IPC, - * all we do is proxy requests/responses exactly - * like they are given to us at the ipc level. */ - - CRM_CHECK(op != NULL, return); - CRM_CHECK(session != NULL, return); - - crm_element_value_int(msg, F_LRMD_IPC_MSG_ID, &msg_id); - - /* This is msg from remote ipc client going to real ipc server */ if (safe_str_eq(op, LRMD_IPC_OP_NEW)) { const char *channel = crm_element_value(msg, F_LRMD_IPC_IPC_SERVER); - CRM_CHECK(channel != NULL, return); + static struct ipc_client_callbacks proxy_callbacks = { + .dispatch = remote_proxy_dispatch, + .destroy = remote_proxy_disconnected + }; + + remote_proxy_new(lrmd, proxy_callbacks, options.node_name, session, channel); - if (remote_proxy_new(options.node_name, session, channel) == NULL) { - remote_proxy_notify_destroy(lrmd, session); - } - crm_info("new remote proxy client established to %s, session id %s", channel, session); - } else if (safe_str_eq(op, LRMD_IPC_OP_DESTROY)) { - remote_proxy_end_session(session); - - } else if (safe_str_eq(op, LRMD_IPC_OP_REQUEST)) { - int flags = 0; - xmlNode *request = get_message_xml(msg, F_LRMD_IPC_MSG); - const char *name = crm_element_value(msg, F_LRMD_IPC_CLIENT); - remote_proxy_t *proxy = g_hash_table_lookup(proxy_table, session); - - CRM_CHECK(request != NULL, return); - - if (proxy == NULL) { - /* proxy connection no longer exists */ - remote_proxy_notify_destroy(lrmd, session); - return; - } else if ((proxy->is_local == FALSE) && (crm_ipc_connected(proxy->ipc) == FALSE)) { - remote_proxy_end_session(session); - return; - } - proxy->last_request_id = 0; - crm_element_value_int(msg, F_LRMD_IPC_MSG_FLAGS, &flags); - crm_xml_add(request, XML_ACL_TAG_ROLE, "pacemaker-remote"); - -#if ENABLE_ACL - CRM_ASSERT(options.node_name); - crm_acl_get_set_user(request, F_LRMD_IPC_USER, options.node_name); -#endif - - if (is_set(flags, crm_ipc_proxied)) { - int rc = crm_ipc_send(proxy->ipc, request, flags, 5000, NULL); - - if(rc < 0) { - xmlNode *op_reply = create_xml_node(NULL, "nack"); - - crm_err("Could not relay %s request %d from %s to %s for %s: %s (%d)", - op, msg_id, proxy->node_name, crm_ipc_name(proxy->ipc), name, pcmk_strerror(rc), rc); - - /* Send a n'ack so the caller doesn't block */ - crm_xml_add(op_reply, "function", __FUNCTION__); - crm_xml_add_int(op_reply, "line", __LINE__); - crm_xml_add_int(op_reply, "rc", rc); - remote_proxy_relay_response(lrmd, session, op_reply, msg_id); - free_xml(op_reply); - - } else { - crm_trace("Relayed %s request %d from %s to %s for %s", - op, msg_id, proxy->node_name, crm_ipc_name(proxy->ipc), name); - proxy->last_request_id = msg_id; - } - } } else { - crm_err("Unknown proxy operation: %s", op); + remote_proxy_cb(lrmd, options.node_name, msg); } } int main(int argc, char **argv) { int option_index = 0; int argerr = 0; int flag; char *key = NULL; char *val = NULL; gboolean use_tls = FALSE; crm_trigger_t *trig; crm_set_options(NULL, "mode [options]", long_options, "Inject commands into the lrmd and watch for events\n"); while (1) { flag = crm_get_option(argc, argv, &option_index); if (flag == -1) break; switch (flag) { case '?': crm_help(flag, EX_OK); break; case 'V': options.verbose = 1; break; case 'Q': options.quiet = 1; options.verbose = 0; break; case 'n': options.node_name = optarg; break; case 'c': options.api_call = optarg; break; case 'a': options.action = optarg; break; case 'r': options.rsc_id = optarg; break; case 'P': options.provider = optarg; break; case 'C': options.class = optarg; break; case 'T': options.type = optarg; break; case 't': if(optarg) { options.timeout = atoi(optarg); } break; case 'k': key = optarg; if (key && val) { options.params = lrmd_key_value_add(options.params, key, val); key = val = NULL; } break; case 'v': val = optarg; if (key && val) { options.params = lrmd_key_value_add(options.params, key, val); key = val = NULL; } break; case 'S': options.tls_host = optarg; use_tls = TRUE; break; case 'p': if(optarg) { options.port = atoi(optarg); } use_tls = TRUE; break; default: ++argerr; break; } } if (argerr) { crm_help('?', EX_USAGE); } if (optind > argc) { ++argerr; } crm_log_init("remote_client", LOG_INFO, FALSE, options.verbose ? TRUE : FALSE, argc, argv, FALSE); /* if we can't perform an api_call or listen for events, * there is nothing to do */ if (!options.api_call ) { crm_err("Nothing to be done. Please specify 'api-call'"); return PCMK_OCF_UNKNOWN_ERROR; } if (!options.timeout ) { options.timeout = 20000; } if (use_tls) { if (options.node_name == NULL) { crm_err("\"node\" option required when tls is in use."); return PCMK_OCF_UNKNOWN_ERROR; } proxy_table = g_hash_table_new_full(crm_strcase_hash, crm_strcase_equal, NULL, remote_proxy_free); lrmd_conn = lrmd_remote_api_new(NULL, options.tls_host ? options.tls_host : "localhost", options.port); - lrmd_internal_set_proxy_callback(lrmd_conn, NULL, remote_proxy_cb); + lrmd_internal_set_proxy_callback(lrmd_conn, NULL, ctl_remote_proxy_cb); } else { lrmd_conn = lrmd_api_new(); } trig = mainloop_add_trigger(G_PRIORITY_HIGH, client_start, NULL); mainloop_set_trigger(trig); mainloop_add_signal(SIGTERM, client_shutdown); mainloop = g_main_new(FALSE); g_main_run(mainloop); client_exit(0); return 0; }