diff --git a/lib/cib/cib_native.c b/lib/cib/cib_native.c index 7c97ddc013..2bcde23da9 100644 --- a/lib/cib/cib_native.c +++ b/lib/cib/cib_native.c @@ -1,680 +1,673 @@ /* * Copyright (c) 2004 International Business Machines * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include typedef struct cib_native_opaque_s { IPC_Channel *command_channel; IPC_Channel *callback_channel; GCHSource *callback_source; char *token; } cib_native_opaque_t; int cib_native_perform_op( cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, xmlNode **output_data, int call_options); int cib_native_free(cib_t* cib); int cib_native_signoff(cib_t* cib); int cib_native_signon(cib_t* cib, const char *name, enum cib_conn_type type); int cib_native_signon_raw(cib_t* cib, const char *name, enum cib_conn_type type, int *async_fd, int *sync_fd); IPC_Channel *cib_native_channel(cib_t* cib); gboolean cib_native_msgready(cib_t* cib); gboolean cib_native_dispatch(IPC_Channel *channel, gpointer user_data); int cib_native_inputfd(cib_t* cib); int cib_native_rcvmsg(cib_t* cib, int blocking); int cib_native_set_connection_dnotify(cib_t *cib, void (*dnotify)(gpointer user_data)); cib_t* cib_native_new (void) { cib_native_opaque_t *native = NULL; cib_t *cib = cib_new_variant(); crm_malloc0(native, sizeof(cib_native_opaque_t)); cib->variant = cib_native; cib->variant_opaque = native; native->command_channel = NULL; native->callback_channel = NULL; /* assign variant specific ops*/ cib->cmds->variant_op = cib_native_perform_op; cib->cmds->signon = cib_native_signon; cib->cmds->signon_raw = cib_native_signon_raw; cib->cmds->signoff = cib_native_signoff; cib->cmds->free = cib_native_free; cib->cmds->inputfd = cib_native_inputfd; cib->cmds->register_notification = cib_native_register_notification; cib->cmds->set_connection_dnotify = cib_native_set_connection_dnotify; return cib; } int cib_native_signon(cib_t* cib, const char *name, enum cib_conn_type type) { return cib_native_signon_raw(cib, name, type, NULL, NULL); } int cib_native_signon_raw(cib_t* cib, const char *name, enum cib_conn_type type, int *async_fd, int *sync_fd) { int rc = cib_ok; xmlNode *hello = NULL; char *uuid_ticket = NULL; cib_native_opaque_t *native = cib->variant_opaque; crm_debug_4("Connecting command channel"); if(type == cib_command) { cib->state = cib_connected_command; native->command_channel = init_client_ipc_comms_nodispatch( cib_channel_rw); } else if(type == cib_query) { cib->state = cib_connected_query; native->command_channel = init_client_ipc_comms_nodispatch( cib_channel_ro); } else { return cib_not_connected; } if(native->command_channel == NULL) { crm_debug("Connection to command channel failed"); rc = cib_connection; } else if(native->command_channel->ch_status != IPC_CONNECT) { crm_err("Connection may have succeeded," " but authentication to command channel failed"); rc = cib_authentication; } if(rc == cib_ok) { rc = get_channel_token(native->command_channel, &uuid_ticket); if(rc == cib_ok) { native->token = uuid_ticket; uuid_ticket = NULL; } } native->callback_channel = init_client_ipc_comms_nodispatch(cib_channel_callback); if(native->callback_channel == NULL) { crm_debug("Connection to callback channel failed"); rc = cib_connection; } else if(native->callback_channel->ch_status != IPC_CONNECT) { crm_err("Connection may have succeeded," " but authentication to command channel failed"); rc = cib_authentication; } if(rc == cib_ok) { native->callback_channel->send_queue->max_qlen = 500; rc = get_channel_token(native->callback_channel, &uuid_ticket); if(rc == cib_ok) { crm_free(native->token); native->token = uuid_ticket; } } if(rc == cib_ok) { CRM_CHECK(native->token != NULL, ;); hello = cib_create_op(0, native->token, CRM_OP_REGISTER, NULL, NULL, NULL, 0); crm_xml_add(hello, F_CIB_CLIENTNAME, name); if(send_ipc_message(native->command_channel, hello) == FALSE) { rc = cib_callback_register; } free_xml(hello); } if(rc == cib_ok) { gboolean do_mainloop = TRUE; if(async_fd != NULL) { do_mainloop = FALSE; *async_fd = native->callback_channel->ops->get_recv_select_fd(native->callback_channel); } if(sync_fd != NULL) { do_mainloop = FALSE; *sync_fd = native->callback_channel->ops->get_send_select_fd(native->callback_channel); } if(do_mainloop) { crm_debug_4("Connecting callback channel"); native->callback_source = G_main_add_IPC_Channel( G_PRIORITY_HIGH, native->callback_channel, FALSE, cib_native_dispatch, cib, default_ipc_connection_destroy); if(native->callback_source == NULL) { crm_err("Callback source not recorded"); rc = cib_connection; } } } if(rc == cib_ok) { #if HAVE_MSGFROMIPC_TIMEOUT - cib->call_timeout = 30; /* Default to 30s */ + cib->call_timeout = MAX_IPC_DELAY; #endif crm_debug("Connection to CIB successful"); return cib_ok; } crm_debug("Connection to CIB failed: %s", cib_error2string(rc)); cib_native_signoff(cib); return rc; } int cib_native_signoff(cib_t* cib) { cib_native_opaque_t *native = cib->variant_opaque; crm_debug("Signing out of the CIB Service"); /* close channels */ if (native->command_channel != NULL) { native->command_channel->ops->destroy( native->command_channel); native->command_channel = NULL; } if (native->callback_source != NULL) { G_main_del_IPC_Channel(native->callback_source); native->callback_source = NULL; } if (native->callback_channel != NULL) { #ifdef BUG native->callback_channel->ops->destroy( native->callback_channel); #endif native->callback_channel = NULL; } cib->state = cib_disconnected; cib->type = cib_none; return cib_ok; } int cib_native_free (cib_t* cib) { int rc = cib_ok; if(cib->state != cib_disconnected) { rc = cib_native_signoff(cib); } if(cib->state == cib_disconnected) { cib_native_opaque_t *native = cib->variant_opaque; crm_free(native->token); crm_free(cib->variant_opaque); crm_free(cib->cmds); crm_free(cib); } return rc; } IPC_Channel * cib_native_channel(cib_t* cib) { cib_native_opaque_t *native = NULL; if(cib == NULL) { crm_err("Missing cib object"); return NULL; } native = cib->variant_opaque; if(native != NULL) { return native->callback_channel; } crm_err("couldnt find variant specific data in %p", cib); return NULL; } int cib_native_inputfd(cib_t* cib) { IPC_Channel *ch = cib_native_channel(cib); return ch->ops->get_recv_select_fd(ch); } static gboolean timer_expired = FALSE; +#ifndef HAVE_MSGFROMIPC_TIMEOUT static struct timer_rec_s sync_timer; static gboolean cib_timeout_handler(gpointer data) { struct timer_rec_s *timer = data; timer_expired = TRUE; crm_err("Call %d timed out after %ds", timer->call_id, timer->timeout); /* Always return TRUE, never remove the handler * We do that after the while-loop in cib_native_perform_op() */ return TRUE; } +#endif int cib_native_perform_op( cib_t *cib, const char *op, const char *host, const char *section, xmlNode *data, xmlNode **output_data, int call_options) { int rc = HA_OK; xmlNode *op_msg = NULL; xmlNode *op_reply = NULL; cib_native_opaque_t *native = cib->variant_opaque; if(cib->state == cib_disconnected) { return cib_not_connected; } if(output_data != NULL) { *output_data = NULL; } if(op == NULL) { crm_err("No operation specified"); return cib_operation; } cib->call_id++; /* prevent call_id from being negative (or zero) and conflicting * with the cib_errors enum * use 2 because we use it as (cib->call_id - 1) below */ if(cib->call_id < 1) { cib->call_id = 1; } CRM_CHECK(native->token != NULL, ;); op_msg = cib_create_op( cib->call_id, native->token, op, host, section, data, call_options); if(op_msg == NULL) { return cib_create_msg; } crm_debug_3("Sending %s message to CIB service", op); if(send_ipc_message(native->command_channel, op_msg) == FALSE) { crm_err("Sending message to CIB service FAILED"); free_xml(op_msg); return cib_send_failed; } else { crm_debug_3("Message sent"); } free_xml(op_msg); if((call_options & cib_discard_reply)) { crm_debug_3("Discarding reply"); return cib_ok; } else if(!(call_options & cib_sync_call)) { crm_debug_3("Async call, returning"); CRM_CHECK(cib->call_id != 0, return cib_reply_failed); return cib->call_id; } rc = IPC_OK; crm_debug_3("Waiting for a syncronous reply"); +#ifndef HAVE_MSGFROMIPC_TIMEOUT sync_timer.ref = 0; if(cib->call_timeout > 0) { - /* We need this, even with msgfromIPC_timeout(), because we might - * get other/older replies that don't match the active request - */ timer_expired = FALSE; sync_timer.call_id = cib->call_id; sync_timer.timeout = cib->call_timeout*1000; sync_timer.ref = g_timeout_add( sync_timer.timeout, cib_timeout_handler, &sync_timer); } - +#endif + rc = cib_ok; while(timer_expired == FALSE && IPC_ISRCONN(native->command_channel)) { int reply_id = -1; int msg_id = cib->call_id; op_reply = xmlfromIPC(native->command_channel, cib->call_timeout); if(op_reply == NULL) { - break; + rc = cib_remote_timeout; + break; } crm_element_value_int(op_reply, F_CIB_CALLID, &reply_id); - CRM_CHECK(reply_id > 0, - crm_log_xml(LOG_ERR, "Invalid call id", op_reply); - free_xml(op_reply); - if(sync_timer.ref > 0) { - g_source_remove(sync_timer.ref); - sync_timer.ref = 0; - } - return cib_reply_failed); - - if(reply_id == msg_id) { - break; + if(reply_id <= 0) { + rc = cib_reply_failed; + break; + + } else if(reply_id == msg_id) { + crm_debug_3("Syncronous reply received"); + if(crm_element_value_int(op_reply, F_CIB_RC, &rc) != 0) { + rc = cib_return_code; + } + + if(output_data != NULL && is_not_set(call_options, cib_discard_reply)) { + xmlNode *tmp = get_message_xml(op_reply, F_CIB_CALLDATA); + if(tmp != NULL) { + *output_data = copy_xml(tmp); + } + } + + break; } else if(reply_id < msg_id) { - crm_debug("Recieved old reply: %d (wanted %d)", - reply_id, msg_id); + crm_debug("Recieved old reply: %d (wanted %d)", reply_id, msg_id); crm_log_xml(LOG_MSG, "Old reply", op_reply); } else if((reply_id - 10000) > msg_id) { /* wrap-around case */ - crm_debug("Recieved old reply: %d (wanted %d)", - reply_id, msg_id); - crm_log_xml( - LOG_MSG, "Old reply", op_reply); + crm_debug("Recieved old reply: %d (wanted %d)", reply_id, msg_id); + crm_log_xml(LOG_MSG, "Old reply", op_reply); + } else { crm_err("Received a __future__ reply:" " %d (wanted %d)", reply_id, msg_id); } free_xml(op_reply); op_reply = NULL; } - - if(sync_timer.ref > 0) { - g_source_remove(sync_timer.ref); - sync_timer.ref = 0; - } - if(timer_expired) { - return cib_remote_timeout; - } - if(IPC_ISRCONN(native->command_channel) == FALSE) { - crm_err("CIB disconnected: %d", - native->command_channel->ch_status); - cib->state = cib_disconnected; + crm_err("CIB disconnected: %d", native->command_channel->ch_status); + cib->state = cib_disconnected; } - - if(op_reply == NULL) { - crm_err("No reply message - empty - %d", rc); - return cib_reply_failed; + + if(op_reply == NULL && cib->state == cib_disconnected) { + rc = cib_not_connected; + + } else if(rc == cib_ok && op_reply == NULL) { + rc = cib_remote_timeout; } - - crm_debug_3("Syncronous reply recieved"); - rc = cib_ok; - - /* Start processing the reply... */ - if(crm_element_value_int(op_reply, F_CIB_RC, &rc) != 0) { - rc = cib_return_code; - } switch(rc) { case cib_ok: - case cib_diff_resync: + case cib_not_master: + break; + /* This is an internal value that clients do not and should not care about */ + case cib_diff_resync: rc = cib_ok; - /* fall through */ - case cib_not_master: - case cib_master_timeout: - crm_log_xml(LOG_MSG, "passed", op_reply); break; + + /* These indicate internal problems */ + case cib_return_code: + case cib_reply_failed: + case cib_master_timeout: + crm_err("Call failed: %s", cib_error2string(rc)); + if(op_reply) { + crm_log_xml(LOG_ERR, "Invalid reply", op_reply); + } + break; + default: if(safe_str_neq(op, CIB_OP_QUERY)) { crm_warn("Call failed: %s", cib_error2string(rc)); - crm_log_xml(LOG_DEBUG_2, "failed", op_reply); } } - if(output_data == NULL) { - /* do nothing more */ - - } else if(!(call_options & cib_discard_reply)) { - xmlNode *tmp = get_message_xml(op_reply, F_CIB_CALLDATA); - if(tmp == NULL) { - crm_debug_3("No output in reply to \"%s\" command %d", - op, cib->call_id - 1); - } else { - *output_data = copy_xml(tmp); - } +#ifndef HAVE_MSGFROMIPC_TIMEOUT + if(sync_timer.ref > 0) { + g_source_remove(sync_timer.ref); + sync_timer.ref = 0; } +#endif free_xml(op_reply); - return rc; } gboolean cib_native_msgready(cib_t* cib) { cib_native_opaque_t *native = NULL; if (cib == NULL) { crm_err("No CIB!"); return FALSE; } native = cib->variant_opaque; if(native->command_channel != NULL) { /* drain the channel */ IPC_Channel *cmd_ch = native->command_channel; xmlNode *cmd_msg = NULL; while(cmd_ch->ch_status != IPC_DISCONNECT && cmd_ch->ops->is_message_pending(cmd_ch)) { /* this will happen when the CIB exited from beneath us */ cmd_msg = xmlfromIPC(cmd_ch, MAX_IPC_DELAY); free_xml(cmd_msg); } } else { crm_err("No command channel"); } if(native->callback_channel == NULL) { crm_err("No callback channel"); return FALSE; } else if(native->callback_channel->ch_status == IPC_DISCONNECT) { crm_info("Lost connection to the CIB service [%d].", native->callback_channel->farside_pid); return FALSE; } else if(native->callback_channel->ops->is_message_pending( native->callback_channel)) { crm_debug_4("Message pending on command channel [%d]", native->callback_channel->farside_pid); return TRUE; } crm_debug_3("No message pending"); return FALSE; } int cib_native_rcvmsg(cib_t* cib, int blocking) { const char *type = NULL; xmlNode* msg = NULL; cib_native_opaque_t *native = NULL; if (cib == NULL) { crm_err("No CIB!"); return FALSE; } native = cib->variant_opaque; /* if it is not blocking mode and no message in the channel, return */ if (blocking == 0 && cib_native_msgready(cib) == FALSE) { crm_debug_3("No message ready and non-blocking..."); return 0; } else if (cib_native_msgready(cib) == FALSE) { crm_debug("Waiting for message from CIB service..."); if(native->callback_channel == NULL) { return -1; } else if(native->callback_channel->ch_status != IPC_CONNECT) { return -2; } else if(native->command_channel && native->command_channel->ch_status != IPC_CONNECT){ return -3; } native->callback_channel->ops->waitin(native->callback_channel); } /* IPC_INTR is not a factor here */ msg = xmlfromIPC(native->callback_channel, MAX_IPC_DELAY); if (msg == NULL) { crm_warn("Received a NULL msg from CIB service."); return 0; } /* do callbacks */ type = crm_element_value(msg, F_TYPE); crm_debug_4("Activating %s callbacks...", type); if(safe_str_eq(type, T_CIB)) { cib_native_callback(cib, msg, 0, 0); } else if(safe_str_eq(type, T_CIB_NOTIFY)) { g_list_foreach(cib->notify_list, cib_native_notify, msg); } else { crm_err("Unknown message type: %s", type); } free_xml(msg); return 1; } gboolean cib_native_dispatch(IPC_Channel *channel, gpointer user_data) { cib_t *cib = user_data; cib_native_opaque_t *native = NULL; gboolean stay_connected = TRUE; CRM_CHECK(cib != NULL, return FALSE); native = cib->variant_opaque; CRM_CHECK(native->callback_channel == channel, return FALSE); while(cib_native_msgready(cib)) { /* invoke the callbacks but dont block */ int rc = cib_native_rcvmsg(cib, 0); if( rc < 0) { crm_err("Message acquisition failed: %d", rc); break; } else if(rc == 0) { break; } } if(native->callback_channel && native->callback_channel->ch_status != IPC_CONNECT) { crm_crit("Lost connection to the CIB service [%d/callback].", channel->farside_pid); native->callback_source = NULL; stay_connected = FALSE; } if(native->command_channel && native->command_channel->ch_status != IPC_CONNECT) { crm_crit("Lost connection to the CIB service [%d/command].", channel->farside_pid); native->callback_source = NULL; stay_connected = FALSE; } return stay_connected; } static void default_cib_connection_destroy(gpointer user_data) { cib_t *cib = user_data; cib->state = cib_disconnected; } int cib_native_set_connection_dnotify( cib_t *cib, void (*dnotify)(gpointer user_data)) { cib_native_opaque_t *native = NULL; if (cib == NULL) { crm_err("No CIB!"); return FALSE; } native = cib->variant_opaque; if(dnotify == NULL) { crm_warn("Setting dnotify back to default value"); set_IPC_Channel_dnotify( native->callback_source, default_cib_connection_destroy); } else { crm_debug_3("Setting dnotify"); set_IPC_Channel_dnotify(native->callback_source, dnotify); } return cib_ok; } int cib_native_register_notification(cib_t* cib, const char *callback, int enabled) { xmlNode *notify_msg = create_xml_node(NULL, "cib-callback"); cib_native_opaque_t *native = cib->variant_opaque; if(cib->state != cib_disconnected) { crm_xml_add(notify_msg, F_CIB_OPERATION, T_CIB_NOTIFY); crm_xml_add(notify_msg, F_CIB_NOTIFY_TYPE, callback); crm_xml_add_int(notify_msg, F_CIB_NOTIFY_ACTIVATE, enabled); send_ipc_message(native->callback_channel, notify_msg); } free_xml(notify_msg); return cib_ok; } diff --git a/lib/common/ipc.c b/lib/common/ipc.c index 2a08b40f74..e28edda53d 100644 --- a/lib/common/ipc.c +++ b/lib/common/ipc.c @@ -1,685 +1,685 @@ /* * 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 #include #include #include xmlNode *xmlfromIPC(IPC_Channel *ch, int timeout) { xmlNode *xml = NULL; HA_Message *msg = NULL; #if HAVE_MSGFROMIPC_TIMEOUT int ipc_rc = IPC_OK; msg = msgfromIPC_timeout(ch, MSG_ALLOWINTR, timeout, &ipc_rc); if(ipc_rc == IPC_TIMEOUT) { crm_warn("No message received in the required interval (%ds)", timeout); return NULL; } else if(ipc_rc == IPC_BROKEN) { crm_debug("Peer disconnected"); return NULL; } else if(ipc_rc != IPC_OK) { crm_err("msgfromIPC_timeout failed: rc=%d", ipc_rc); return NULL; } else if(msg == NULL) { crm_err("Empty reply from msgfromIPC_timeout"); return NULL; } #else static gboolean do_show_error = TRUE; if(timeout && do_show_error) { crm_err("Timeouts are not supported by the current heartbeat libraries"); do_show_error = FALSE; } msg = msgfromIPC_noauth(ch); if(msg == NULL) { crm_debug("Empty reply from msgfromIPC_noauth"); return NULL; } #endif xml = convert_ha_message(NULL, msg, __FUNCTION__); + CRM_CHECK(xml != NULL, crm_err("Invalid ipc message")); crm_msg_del(msg); - return xml; } static int xml2ipcchan(xmlNode *m, IPC_Channel *ch) { HA_Message *msg = NULL; IPC_Message *imsg = NULL; if (m == NULL || ch == NULL) { cl_log(LOG_ERR, "Invalid msg2ipcchan argument"); errno = EINVAL; return HA_FAIL; } msg = convert_xml_message(m); if ((imsg = hamsg2ipcmsg(msg, ch)) == NULL) { cl_log(LOG_ERR, "hamsg2ipcmsg() failure"); crm_msg_del(msg); return HA_FAIL; } crm_msg_del(msg); if (ch->ops->send(ch, imsg) != IPC_OK) { if (ch->ch_status == IPC_CONNECT) { snprintf(ch->failreason,MAXFAILREASON, "send failed,farside_pid=%d, sendq length=%ld(max is %ld)", ch->farside_pid, (long)ch->send_queue->current_qlen, (long)ch->send_queue->max_qlen); } imsg->msg_done(imsg); return HA_FAIL; } return HA_OK; } /* frees msg */ gboolean send_ipc_message(IPC_Channel *ipc_client, xmlNode *msg) { gboolean all_is_good = TRUE; int fail_level = LOG_WARNING; if(ipc_client != NULL && ipc_client->conntype == IPC_CLIENT) { fail_level = LOG_ERR; } if (msg == NULL) { crm_err("cant send NULL message"); all_is_good = FALSE; } else if (ipc_client == NULL) { crm_err("cant send message without an IPC Channel"); all_is_good = FALSE; } else if(ipc_client->ops->get_chan_status(ipc_client) != IPC_CONNECT) { do_crm_log(fail_level, "IPC Channel to %d is not connected", (int)ipc_client->farside_pid); all_is_good = FALSE; } if(all_is_good && xml2ipcchan(msg, ipc_client) != HA_OK) { do_crm_log(fail_level, "Could not send IPC message to %d", (int)ipc_client->farside_pid); all_is_good = FALSE; if(ipc_client->ops->get_chan_status(ipc_client) != IPC_CONNECT) { do_crm_log(fail_level, "IPC Channel to %d is no longer connected", (int)ipc_client->farside_pid); } else if(ipc_client->conntype == IPC_CLIENT) { if(ipc_client->send_queue->current_qlen >= ipc_client->send_queue->max_qlen) { crm_err("Send queue to %d (size=%d) full.", ipc_client->farside_pid, (int)ipc_client->send_queue->max_qlen); } } } /* crm_log_xml(all_is_good?LOG_MSG:LOG_WARNING,"IPC[outbound]",msg); */ return all_is_good; } void default_ipc_connection_destroy(gpointer user_data) { return; } int init_server_ipc_comms( char *channel_name, gboolean (*channel_client_connect)(IPC_Channel *newclient,gpointer user_data), void (*channel_connection_destroy)(gpointer user_data)) { /* the clients wait channel is the other source of events. * This source delivers the clients connection events. * listen to this source at a relatively lower priority. */ char commpath[SOCKET_LEN]; IPC_WaitConnection *wait_ch; sprintf(commpath, CRM_STATE_DIR "/%s", channel_name); wait_ch = wait_channel_init(commpath); if (wait_ch == NULL) { return 1; } G_main_add_IPC_WaitConnection( G_PRIORITY_LOW, wait_ch, NULL, FALSE, channel_client_connect, channel_name, channel_connection_destroy); crm_debug_3("Listening on: %s", commpath); return 0; } GCHSource* init_client_ipc_comms(const char *channel_name, gboolean (*dispatch)( IPC_Channel* source_data, gpointer user_data), void *client_data, IPC_Channel **ch) { IPC_Channel *a_ch = NULL; GCHSource *the_source = NULL; void *callback_data = client_data; a_ch = init_client_ipc_comms_nodispatch(channel_name); if(ch != NULL) { *ch = a_ch; if(callback_data == NULL) { callback_data = a_ch; } } if(a_ch == NULL) { crm_warn("Setup of client connection failed," " not adding channel to mainloop"); return NULL; } if(dispatch == NULL) { crm_warn("No dispatch method specified..." "maybe you meant init_client_ipc_comms_nodispatch()?"); } else { crm_debug_3("Adding dispatch method to channel"); the_source = G_main_add_IPC_Channel( G_PRIORITY_HIGH, a_ch, FALSE, dispatch, callback_data, default_ipc_connection_destroy); } return the_source; } IPC_Channel * init_client_ipc_comms_nodispatch(const char *channel_name) { IPC_Channel *ch; GHashTable *attrs; static char path[] = IPC_PATH_ATTR; char *commpath = NULL; int local_socket_len = 2; /* 2 = '/' + '\0' */ local_socket_len += strlen(channel_name); local_socket_len += strlen(CRM_STATE_DIR); crm_malloc0(commpath, local_socket_len); sprintf(commpath, CRM_STATE_DIR "/%s", channel_name); commpath[local_socket_len - 1] = '\0'; crm_debug("Attempting to talk on: %s", commpath); attrs = g_hash_table_new(g_str_hash,g_str_equal); g_hash_table_insert(attrs, path, commpath); ch = ipc_channel_constructor(IPC_ANYTYPE, attrs); g_hash_table_destroy(attrs); if (ch == NULL) { crm_err("Could not access channel on: %s", commpath); crm_free(commpath); return NULL; } else if (ch->ops->initiate_connection(ch) != IPC_OK) { crm_debug("Could not init comms on: %s", commpath); ch->ops->destroy(ch); crm_free(commpath); return NULL; } ch->ops->set_recv_qlen(ch, 512); ch->ops->set_send_qlen(ch, 512); ch->should_send_block = TRUE; crm_debug_3("Processing of %s complete", commpath); crm_free(commpath); return ch; } IPC_WaitConnection * wait_channel_init(char daemonsocket[]) { IPC_WaitConnection *wait_ch; mode_t mask; char path[] = IPC_PATH_ATTR; GHashTable * attrs; attrs = g_hash_table_new(g_str_hash,g_str_equal); g_hash_table_insert(attrs, path, daemonsocket); mask = umask(0); wait_ch = ipc_wait_conn_constructor(IPC_ANYTYPE, attrs); if (wait_ch == NULL) { crm_perror(LOG_ERR,"Can't create wait channel of type %s", IPC_ANYTYPE); exit(1); } mask = umask(mask); g_hash_table_destroy(attrs); return wait_ch; } gboolean subsystem_msg_dispatch(IPC_Channel *sender, void *user_data) { int lpc = 0; xmlNode *msg = NULL; xmlNode *data = NULL; gboolean all_is_well = TRUE; const char *sys_to; const char *task; time_t ipc_call_start = 0; time_t ipc_call_stop = 0; time_t ipc_call_diff = 0; gboolean (*process_function) (xmlNode *msg, xmlNode *data, IPC_Channel *sender) = NULL; while(IPC_ISRCONN(sender)) { gboolean process = FALSE; if(sender->ops->is_message_pending(sender) == 0) { break; } msg = xmlfromIPC(sender, MAX_IPC_DELAY); if (msg == NULL) { break; } lpc++; crm_log_xml(LOG_MSG, __FUNCTION__, msg); sys_to = crm_element_value(msg, F_CRM_SYS_TO); task = crm_element_value(msg, F_CRM_TASK); if(safe_str_eq(task, CRM_OP_HELLO)) { process = TRUE; } else if(sys_to == NULL) { crm_err("Value of %s was NULL!!", F_CRM_SYS_TO); } else if(task == NULL) { crm_err("Value of %s was NULL!!", F_CRM_TASK); } else { process = TRUE; } if(process == FALSE) { free_xml(msg); msg = NULL; continue; } data = get_message_xml(msg, F_CRM_DATA); process_function = user_data; if(ipc_call_diff_max > 0) { ipc_call_start = time(NULL); } if(FALSE == process_function(msg, data, sender)) { crm_warn("Received a message destined for %s" " by mistake", sys_to); } if(ipc_call_diff_max > 0) { ipc_call_stop = time(NULL); ipc_call_diff = ipc_call_stop - ipc_call_start; if(ipc_call_diff > ipc_call_diff_max) { crm_err("%s took %ds to complete", sys_to, (int)ipc_call_diff); } } free_xml(msg); msg = NULL; if(sender->ch_status == IPC_CONNECT) { break; } } crm_debug_2("Processed %d messages", lpc); if (sender->ch_status != IPC_CONNECT) { crm_err("The server %d has left us: Shutting down...NOW", sender->farside_pid); exit(1); /* shutdown properly later */ return !all_is_well; } return all_is_well; } gboolean is_ipc_empty(IPC_Channel *ch) { if(ch == NULL) { return TRUE; } else if(ch->send_queue->current_qlen == 0 && ch->recv_queue->current_qlen == 0) { return TRUE; } return FALSE; } void send_hello_message(IPC_Channel *ipc_client, const char *uuid, const char *client_name, const char *major_version, const char *minor_version) { xmlNode *hello_node = NULL; xmlNode *hello = NULL; if (uuid == NULL || strlen(uuid) == 0 || client_name == NULL || strlen(client_name) == 0 || major_version == NULL || strlen(major_version) == 0 || minor_version == NULL || strlen(minor_version) == 0) { crm_err("Missing fields, Hello message will not be valid."); return; } hello_node = create_xml_node(NULL, XML_TAG_OPTIONS); crm_xml_add(hello_node, "major_version", major_version); crm_xml_add(hello_node, "minor_version", minor_version); crm_xml_add(hello_node, "client_name", client_name); crm_xml_add(hello_node, "client_uuid", uuid); crm_debug_4("creating hello message"); hello = create_request( CRM_OP_HELLO, hello_node, NULL, NULL, client_name, uuid); send_ipc_message(ipc_client, hello); crm_debug_4("hello message sent"); free_xml(hello_node); free_xml(hello); } gboolean process_hello_message(xmlNode *hello, char **uuid, char **client_name, char **major_version, char **minor_version) { const char *local_uuid; const char *local_client_name; const char *local_major_version; const char *local_minor_version; *uuid = NULL; *client_name = NULL; *major_version = NULL; *minor_version = NULL; if(hello == NULL) { return FALSE; } local_uuid = crm_element_value(hello, "client_uuid"); local_client_name = crm_element_value(hello, "client_name"); local_major_version = crm_element_value(hello, "major_version"); local_minor_version = crm_element_value(hello, "minor_version"); if (local_uuid == NULL || strlen(local_uuid) == 0) { crm_err("Hello message was not valid (field %s not found)", "uuid"); return FALSE; } else if (local_client_name==NULL || strlen(local_client_name)==0){ crm_err("Hello message was not valid (field %s not found)", "client name"); return FALSE; } else if(local_major_version == NULL || strlen(local_major_version) == 0){ crm_err("Hello message was not valid (field %s not found)", "major version"); return FALSE; } else if (local_minor_version == NULL || strlen(local_minor_version) == 0){ crm_err("Hello message was not valid (field %s not found)", "minor version"); return FALSE; } *uuid = crm_strdup(local_uuid); *client_name = crm_strdup(local_client_name); *major_version = crm_strdup(local_major_version); *minor_version = crm_strdup(local_minor_version); crm_debug_3("Hello message ok"); return TRUE; } xmlNode * create_request_adv(const char *task, xmlNode *msg_data, const char *host_to, const char *sys_to, const char *sys_from, const char *uuid_from, const char *origin) { char *true_from = NULL; xmlNode *request = NULL; char *reference = generateReference(task, sys_from); if (uuid_from != NULL) { true_from = generate_hash_key(sys_from, uuid_from); } else if(sys_from != NULL) { true_from = crm_strdup(sys_from); } else { crm_err("No sys from specified"); } /* host_from will get set for us if necessary by CRMd when routed */ request = create_xml_node(NULL, __FUNCTION__); crm_xml_add(request, F_CRM_ORIGIN, origin); crm_xml_add(request, F_TYPE, T_CRM); crm_xml_add(request, F_CRM_VERSION, CRM_FEATURE_SET); crm_xml_add(request, F_CRM_MSG_TYPE, XML_ATTR_REQUEST); crm_xml_add(request, XML_ATTR_REFERENCE, reference); crm_xml_add(request, F_CRM_TASK, task); crm_xml_add(request, F_CRM_SYS_TO, sys_to); crm_xml_add(request, F_CRM_SYS_FROM, true_from); /* HOSTTO will be ignored if it is to the DC anyway. */ if(host_to != NULL && strlen(host_to) > 0) { crm_xml_add(request, F_CRM_HOST_TO, host_to); } if (msg_data != NULL) { add_message_xml(request, F_CRM_DATA, msg_data); } crm_free(reference); crm_free(true_from); return request; } ha_msg_input_t * new_ha_msg_input(xmlNode *orig) { ha_msg_input_t *input_copy = NULL; crm_malloc0(input_copy, sizeof(ha_msg_input_t)); input_copy->msg = orig; input_copy->xml = get_message_xml(input_copy->msg, F_CRM_DATA); return input_copy; } void delete_ha_msg_input(ha_msg_input_t *orig) { if(orig == NULL) { return; } free_xml(orig->msg); crm_free(orig); } xmlNode * validate_crm_message( xmlNode *msg, const char *sys, const char *uuid, const char *msg_type) { const char *from = NULL; const char *to = NULL; const char *type = NULL; const char *crm_msg_reference = NULL; xmlNode *action = NULL; const char *true_sys; char *local_sys = NULL; if (msg == NULL) { return NULL; } from = crm_element_value(msg, F_CRM_SYS_FROM); to = crm_element_value(msg, F_CRM_SYS_TO); type = crm_element_value(msg, F_CRM_MSG_TYPE); crm_msg_reference = crm_element_value(msg, XML_ATTR_REFERENCE); action = msg; true_sys = sys; if (uuid != NULL) { local_sys = generate_hash_key(sys, uuid); true_sys = local_sys; } if (to == NULL) { crm_info("No sub-system defined."); action = NULL; } else if (true_sys != NULL && strcasecmp(to, true_sys) != 0) { crm_debug_3("The message is not for this sub-system (%s != %s).", to, true_sys); action = NULL; } crm_free(local_sys); if (type == NULL) { crm_info("No message type defined."); return NULL; } else if (msg_type != NULL && strcasecmp(msg_type, type) != 0) { crm_info("Expecting a (%s) message but received a (%s).", msg_type, type); action = NULL; } if (crm_msg_reference == NULL) { crm_info("No message crm_msg_reference defined."); action = NULL; } /* if(action != NULL) crm_debug_3( "XML is valid and node with message type (%s) found.", type); crm_debug_3("Returning node (%s)", crm_element_name(action)); */ return action; } /* * This method adds a copy of xml_response_data */ xmlNode * create_reply_adv(xmlNode *original_request, xmlNode *xml_response_data, const char *origin) { xmlNode *reply = NULL; const char *host_from= crm_element_value(original_request, F_CRM_HOST_FROM); const char *sys_from = crm_element_value(original_request, F_CRM_SYS_FROM); const char *sys_to = crm_element_value(original_request, F_CRM_SYS_TO); const char *type = crm_element_value(original_request, F_CRM_MSG_TYPE); const char *operation= crm_element_value(original_request, F_CRM_TASK); const char *crm_msg_reference = crm_element_value( original_request, XML_ATTR_REFERENCE); if (type == NULL) { crm_err("Cannot create new_message," " no message type in original message"); CRM_ASSERT(type != NULL); return NULL; #if 0 } else if (strcasecmp(XML_ATTR_REQUEST, type) != 0) { crm_err("Cannot create new_message," " original message was not a request"); return NULL; #endif } reply = create_xml_node(NULL, __FUNCTION__); crm_xml_add(reply, F_CRM_ORIGIN, origin); crm_xml_add(reply, F_TYPE, T_CRM); crm_xml_add(reply, F_CRM_VERSION, CRM_FEATURE_SET); crm_xml_add(reply, F_CRM_MSG_TYPE, XML_ATTR_RESPONSE); crm_xml_add(reply, XML_ATTR_REFERENCE, crm_msg_reference); crm_xml_add(reply, F_CRM_TASK, operation); /* since this is a reply, we reverse the from and to */ crm_xml_add(reply, F_CRM_SYS_TO, sys_from); crm_xml_add(reply, F_CRM_SYS_FROM, sys_to); /* HOSTTO will be ignored if it is to the DC anyway. */ if(host_from != NULL && strlen(host_from) > 0) { crm_xml_add(reply, F_CRM_HOST_TO, host_from); } if (xml_response_data != NULL) { add_message_xml(reply, F_CRM_DATA, xml_response_data); } return reply; }