diff --git a/cib/remote.c b/cib/remote.c index 3061d26e1d..bc65c10870 100644 --- a/cib/remote.c +++ b/cib/remote.c @@ -1,440 +1,430 @@ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "callbacks.h" /* #undef HAVE_PAM_PAM_APPL_H */ /* #undef HAVE_GNUTLS_GNUTLS_H */ #ifdef HAVE_GNUTLS_GNUTLS_H # undef KEYFILE # include #endif #include #include #if HAVE_SECURITY_PAM_APPL_H # include # define HAVE_PAM 1 #else # if HAVE_PAM_PAM_APPL_H # include # define HAVE_PAM 1 # endif #endif int init_remote_listener(int port); #ifdef HAVE_GNUTLS_GNUTLS_H # define DH_BITS 1024 gnutls_dh_params dh_params; extern gnutls_anon_server_credentials anon_cred_s; static void debug_log(int level, const char *str) { fputs (str, stderr); } extern gnutls_session *create_tls_session(int csock, int type); #endif extern int num_clients; int authenticate_user(const char* user, const char* passwd); gboolean cib_remote_listen(int ssock, gpointer data); gboolean cib_remote_msg(int csock, gpointer data); extern void cib_process_request( xmlNode *request, gboolean privileged, gboolean force_synchronous, gboolean from_peer, cib_client_t *cib_client); #define ERROR_SUFFIX " Shutting down remote listener" int init_remote_listener(int port) { int ssock; struct sockaddr_in saddr; int optval; if(port <= 0) { /* dont start it */ return 0; } #ifdef HAVE_GNUTLS_GNUTLS_H crm_notice("Starting a tls listener on port %d.", port); gnutls_global_init(); /* gnutls_global_set_log_level (10); */ gnutls_global_set_log_function (debug_log); gnutls_dh_params_init(&dh_params); gnutls_dh_params_generate2(dh_params, DH_BITS); gnutls_anon_allocate_server_credentials (&anon_cred_s); gnutls_anon_set_server_dh_params (anon_cred_s, dh_params); #else crm_warn("Starting a _plain_text_ listener on port %d.", port); #endif #ifndef HAVE_PAM crm_warn("PAM is _not_ enabled!"); #endif /* create server socket */ ssock = socket(AF_INET, SOCK_STREAM, 0); if (ssock == -1) { cl_perror("Can not create server socket."ERROR_SUFFIX); return -1; } /* reuse address */ optval = 1; setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); /* bind server socket*/ memset(&saddr, '\0', sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = INADDR_ANY; saddr.sin_port = htons(port); if (bind(ssock, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) { cl_perror("Can not bind server socket."ERROR_SUFFIX); return -2; } if (listen(ssock, 10) == -1) { cl_perror("Can not start listen."ERROR_SUFFIX); return -3; } G_main_add_fd(G_PRIORITY_HIGH, ssock, FALSE, cib_remote_listen, NULL, default_ipc_connection_destroy); return 0; } static int check_group_membership(const char* usr, const char* grp) { int index = 0; struct group *group = NULL; CRM_CHECK(usr != NULL, return FALSE); CRM_CHECK(grp != NULL, return FALSE); group = getgrnam(grp); if (group == NULL) { crm_err("No group named '%s' exists!", grp); return FALSE; } while (TRUE) { char* member = group->gr_mem[index++]; if(member == NULL) { break; } else if (crm_str_eq(usr, member, TRUE)) { return TRUE; } }; return FALSE; } #define WELCOME "" gboolean cib_remote_listen(int ssock, gpointer data) { int lpc = 0; int csock; unsigned laddr; - char *msg = NULL; struct sockaddr_in addr; #ifdef HAVE_GNUTLS_GNUTLS_H gnutls_session *session = NULL; #endif cib_client_t *new_client = NULL; xmlNode *login = NULL; const char *user = NULL; const char *pass = NULL; const char *tmp = NULL; cl_uuid_t client_id; char uuid_str[UU_UNPARSE_SIZEOF]; crm_debug("New connection"); /* accept the connection */ laddr = sizeof(addr); csock = accept(ssock, (struct sockaddr*)&addr, &laddr); if (csock == -1) { crm_err("accept socket failed"); return TRUE; } #ifdef HAVE_GNUTLS_GNUTLS_H /* create gnutls session for the server socket */ session = create_tls_session(csock, GNUTLS_SERVER); if (session == NULL) { crm_err("TLS session creation failed"); close(csock); return TRUE; } #endif do { crm_debug_2("Iter: %d", lpc++); #ifdef HAVE_GNUTLS_GNUTLS_H - msg = cib_recv_remote_msg(session); + login = cib_recv_remote_msg(session); #else - msg = cib_recv_remote_msg(GINT_TO_POINTER(csock)); + login = cib_recv_remote_msg(GINT_TO_POINTER(csock)); #endif sleep(1); - } while(msg == NULL && lpc < 10); + } while(login == NULL && lpc < 10); - /* convert to xml */ - login = string2xml(msg); - crm_log_xml_info(login, "Login: "); if(login == NULL) { goto bail; } tmp = crm_element_name(login); if(safe_str_neq(tmp, "cib_command")) { crm_err("Wrong tag: %s", tmp); goto bail; } tmp = crm_element_value(login, "op"); if(safe_str_neq(tmp, "authenticate")) { crm_err("Wrong operation: %s", tmp); goto bail; } user = crm_element_value(login, "user"); pass = crm_element_value(login, "password"); if(check_group_membership(user, HA_APIGROUP) == FALSE) { crm_err("User is not a member of the required group"); goto bail; } else if (authenticate_user(user, pass) == FALSE) { crm_err("PAM auth failed"); goto bail; } /* send ACK */ crm_malloc0(new_client, sizeof(cib_client_t)); num_clients++; new_client->channel_name = "remote"; cl_uuid_generate(&client_id); cl_uuid_unparse(&client_id, uuid_str); CRM_CHECK(new_client->id == NULL, crm_free(new_client->id)); new_client->id = crm_strdup(uuid_str); new_client->callback_id = NULL; #ifdef HAVE_GNUTLS_GNUTLS_H new_client->channel = (void*)session; gnutls_record_send (*session, WELCOME, sizeof (WELCOME)); #else new_client->channel = GINT_TO_POINTER(csock); write(csock, WELCOME, sizeof (WELCOME)); #endif new_client->source = (void*)G_main_add_fd( G_PRIORITY_HIGH, csock, FALSE, cib_remote_msg, new_client, default_ipc_connection_destroy); g_hash_table_insert(client_list, new_client->id, new_client); return TRUE; bail: #ifdef HAVE_GNUTLS_GNUTLS_H gnutls_bye(*session, GNUTLS_SHUT_RDWR); gnutls_deinit(*session); gnutls_free(session); #endif close(csock); return TRUE; } gboolean cib_remote_msg(int csock, gpointer data) { cl_uuid_t call_id; char call_uuid[UU_UNPARSE_SIZEOF]; const char *value = NULL; xmlNode *command = NULL; cib_client_t *client = data; - char* msg = cib_recv_remote_msg(client->channel); - if(msg == NULL) { - return FALSE; - } - - command = string2xml(msg); + command = cib_recv_remote_msg(client->channel); if(command == NULL) { - crm_info("Could not parse command: %s", msg); - goto bail; + crm_info("Could not parse command"); + return FALSE; } crm_log_xml(LOG_MSG+1, "Command: ", command); value = crm_element_name(command); if(safe_str_neq(value, "cib_command")) { goto bail; } cl_uuid_generate(&call_id); cl_uuid_unparse(&call_id, call_uuid); crm_xml_add(command, F_TYPE, T_CIB); crm_xml_add(command, F_CIB_CLIENTID, client->id); crm_xml_add(command, F_CIB_CLIENTNAME, client->name); crm_xml_add(command, F_CIB_CALLID, call_uuid); if(crm_element_value(command, F_CIB_CALLOPTS) == NULL) { crm_xml_add_int(command, F_CIB_CALLOPTS, 0); } crm_log_xml(LOG_MSG, "Fixed Command: ", command); /* unset dangerous options */ xml_remove_prop(command, F_ORIG); xml_remove_prop(command, F_CIB_HOST); xml_remove_prop(command, F_CIB_GLOBAL_UPDATE); value = crm_element_value(command, F_CIB_OPERATION); if(safe_str_eq(value, T_CIB_NOTIFY) ) { /* Update the notify filters for this client */ int on_off = 0; const char *on_off_s = crm_element_value(command, F_CIB_NOTIFY_ACTIVATE); value = crm_element_value(command, F_CIB_NOTIFY_TYPE); on_off = crm_parse_int(on_off_s, "0"); crm_info("Setting %s callbacks for %s: %s", value, client->name, on_off?"on":"off"); if(safe_str_eq(value, T_CIB_POST_NOTIFY)) { client->post_notify = on_off; } else if(safe_str_eq(value, T_CIB_PRE_NOTIFY)) { client->pre_notify = on_off; } else if(safe_str_eq(value, T_CIB_UPDATE_CONFIRM)) { client->confirmations = on_off; } else if(safe_str_eq(value, T_CIB_DIFF_NOTIFY)) { client->diffs = on_off; } else if(safe_str_eq(value, T_CIB_REPLACE_NOTIFY)) { client->replace = on_off; } goto bail; } cib_process_request(command, TRUE, TRUE, FALSE, client); bail: free_xml(command); - crm_free(msg); return TRUE; } #ifdef HAVE_PAM /* * Useful Examples: * http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html * http://developer.apple.com/samplecode/CryptNoMore/index.html */ static int construct_pam_passwd(int n, const struct pam_message **msg, struct pam_response **resp, void *data) { int i; char* passwd = (char*)data; struct pam_response *reply = NULL; crm_malloc0(reply, n * sizeof(*reply)); CRM_ASSERT(reply != NULL); /* Construct a PAM password message */ for (i = 0; i < n; ++i) { switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_OFF: case PAM_PROMPT_ECHO_ON: reply[i].resp = passwd; break; case PAM_ERROR_MSG: crm_err("PAM error: %s", msg[i]->msg); break; case PAM_TEXT_INFO: crm_info("PAM info: %s", msg[i]->msg); break; default: crm_err("Unhandled message type: %d", msg[i]->msg_style); goto bail; break; } } *resp = reply; return PAM_SUCCESS; bail: crm_free(reply); return PAM_CONV_ERR; } #endif int authenticate_user(const char* user, const char* passwd) { #ifndef HAVE_PAM gboolean pass = TRUE; #else gboolean pass = FALSE; int rc = 0; struct pam_handle *handle = NULL; struct pam_conv passwd_data; passwd_data.conv = construct_pam_passwd; passwd_data.appdata_ptr = strdup(passwd); rc = pam_start ("cib", user, &passwd_data, &handle); if (rc != PAM_SUCCESS) { goto bail; } rc = pam_authenticate (handle, 0); if(rc != PAM_SUCCESS) { crm_err("pam_authenticate: %s (%d)", pam_strerror(handle, rc), rc); goto bail; } rc = pam_acct_mgmt(handle, 0); /* permitted access? */ if(rc != PAM_SUCCESS) { crm_err("pam_acct: %s (%d)", pam_strerror(handle, rc), rc); goto bail; } pass = TRUE; bail: rc = pam_end (handle, rc); #endif return pass; } diff --git a/include/crm/common/util.h b/include/crm/common/util.h index f0ad9702aa..43485fd1f8 100644 --- a/include/crm/common/util.h +++ b/include/crm/common/util.h @@ -1,194 +1,194 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef CRM_COMMON_UTIL__H #define CRM_COMMON_UTIL__H #include #include #if SUPPORT_HEARTBEAT # include # include #endif #include #include #include #include #define DEBUG_INC SIGUSR1 #define DEBUG_DEC SIGUSR2 extern unsigned int crm_log_level; extern gboolean crm_config_error; extern gboolean crm_config_warning; #define crm_config_err(fmt...) { crm_config_error = TRUE; crm_err(fmt); } #define crm_config_warn(fmt...) { crm_config_warning = TRUE; crm_warn(fmt); } extern gboolean crm_log_init( const char *entity, int level, gboolean coredir, gboolean to_stderr, int argc, char **argv); /* returns the old value */ extern unsigned int set_crm_log_level(unsigned int level); extern unsigned int get_crm_log_level(void); extern char *crm_itoa(int an_int); extern char *crm_strdup_fn(const char *a, const char *file, const char *fn, int line); extern char *generate_hash_key(const char *crm_msg_reference, const char *sys); extern char *generate_hash_value(const char *src_node, const char *src_subsys); extern gboolean decodeNVpair(const char *srcstring, char separator, char **name, char **value); extern int compare_version(const char *version1, const char *version2); extern char *generateReference(const char *custom1, const char *custom2); extern void alter_debug(int nsig); extern void g_hash_destroy_str(gpointer data); extern gboolean crm_is_true(const char * s); extern int crm_str_to_boolean(const char * s, int * ret); extern long crm_get_msec(const char * input); extern const char *op_status2text(op_status_t status); extern char *generate_op_key( const char *rsc_id, const char *op_type, int interval); extern gboolean parse_op_key( const char *key, char **rsc_id, char **op_type, int *interval); extern char *generate_notify_key( const char *rsc_id, const char *notify_type, const char *op_type); extern char *generate_transition_magic_v202( const char *transition_key, int op_status); extern char *generate_transition_magic( const char *transition_key, int op_status, int op_rc); extern gboolean decode_transition_magic( const char *magic, char **uuid, int *transition_id, int *action_id, int *op_status, int *op_rc, int *target_rc); extern char *generate_transition_key(int action, int transition_id, int target_rc, const char *node); extern gboolean decode_transition_key( const char *key, char **uuid, int *action, int *transition_id, int *target_rc); extern char *crm_concat(const char *prefix, const char *suffix, char join); extern gboolean decode_op_key( const char *key, char **rsc_id, char **op_type, int *interval); extern void filter_action_parameters(xmlNode *param_set, const char *version); extern void filter_reload_parameters(xmlNode *param_set, const char *restart_string); #define safe_str_eq(a, b) crm_str_eq(a, b, FALSE) extern gboolean crm_str_eq(const char *a, const char *b, gboolean use_case); extern gboolean safe_str_neq(const char *a, const char *b); extern int crm_parse_int(const char *text, const char *default_text); extern long crm_int_helper(const char *text, char **end_text); #define crm_atoi(text, default_text) crm_parse_int(text, default_text) extern void crm_abort(const char *file, const char *function, int line, const char *condition, gboolean do_core, gboolean do_fork); extern char *generate_series_filename( const char *directory, const char *series, int sequence, gboolean bzip); extern int get_last_sequence(const char *directory, const char *series); extern void write_last_sequence( const char *directory, const char *series, int sequence, int max); extern void crm_make_daemon( const char *name, gboolean daemonize, const char *pidfile); 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; extern const char *cluster_option( GHashTable* options, gboolean(*validate)(const char*), const char *name, const char *old_name, const char *def_value); extern const char *get_cluster_pref( GHashTable *options, pe_cluster_option *option_list, int len, const char *name); extern void config_metadata( const char *name, const char *version, const char *desc_short, const char *desc_long, pe_cluster_option *option_list, int len); extern void verify_all_options(GHashTable *options, pe_cluster_option *option_list, int len); extern gboolean check_time(const char *value); extern gboolean check_timer(const char *value); extern gboolean check_boolean(const char *value); extern gboolean check_number(const char *value); extern int char2score(const char *score); extern char *score2char(int score); extern gboolean crm_is_writable( const char *dir, const char *file, const char *user, const char *group, gboolean need_both); extern long long crm_set_bit(const char *function, long long word, long long bit); extern long long crm_clear_bit(const char *function, long long word, long long bit); #define set_bit(word, bit) word = crm_set_bit(__PRETTY_FUNCTION__, word, bit) #define clear_bit(word, bit) word = crm_clear_bit(__PRETTY_FUNCTION__, word, bit) #define set_bit_inplace(word, bit) word |= bit #define clear_bit_inplace(word, bit) word &= ~bit extern gboolean is_set(long long action_list, long long action); extern gboolean is_not_set(long long action_list, long long action); extern gboolean is_set_any(long long action_list, long long action); extern gboolean is_openais_cluster(void); extern gboolean is_heartbeat_cluster(void); -extern char *cib_recv_remote_msg(void *session); +extern xmlNode *cib_recv_remote_msg(void *session); extern void cib_send_remote_msg(void *session, xmlNode *msg); #endif diff --git a/lib/crm/cib/cib_native.c b/lib/crm/cib/cib_native.c index e959f99701..6f01e3e29e 100644 --- a/lib/crm/cib/cib_native.c +++ b/lib/crm/cib/cib_native.c @@ -1,880 +1,874 @@ /* * 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 #include #include #include #include typedef struct cib_native_opaque_s { IPC_Channel *command_channel; IPC_Channel *callback_channel; GCHSource *callback_source; } 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); 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)); void cib_native_callback(cib_t *cib, xmlNode *msg); void cib_native_notify(gpointer data, gpointer user_data); int cib_native_register_callback(cib_t* cib, const char *callback, int enabled); 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->signoff = cib_native_signoff; cib->cmds->free = cib_native_free; cib->cmds->channel = cib_native_channel; cib->cmds->inputfd = cib_native_inputfd; cib->cmds->msgready = cib_native_msgready; cib->cmds->rcvmsg = cib_native_rcvmsg; cib->cmds->dispatch = cib_native_dispatch; cib->cmds->register_callback = cib_native_register_callback; 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) { int rc = cib_ok; char *uuid_ticket = NULL; xmlNode *reg_msg = 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 if(type == cib_query_synchronous) { cib->state = cib_connected_query; native->command_channel = init_client_ipc_comms_nodispatch( cib_channel_ro_synchronous); } else if(type == cib_command_synchronous) { cib->state = cib_connected_query; native->command_channel = init_client_ipc_comms_nodispatch( cib_channel_rw_synchronous); } 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(type == cib_query_synchronous || type == cib_command_synchronous) { return rc; } if(rc == cib_ok) { crm_debug_4("Connecting callback channel"); native->callback_source = init_client_ipc_comms( cib_channel_callback, cib_native_dispatch, cib, &(native->callback_channel)); 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 callback channel failed"); rc = cib_authentication; } else if(native->callback_source == NULL) { crm_err("Callback source not recorded"); rc = cib_connection; } else { native->callback_channel->send_queue->max_qlen = 500; } } if(rc == cib_ok) { crm_debug_4("Waiting for msg on command channel"); reg_msg = xmlfromIPC(native->command_channel, 0); if(native->command_channel->ops->get_chan_status( native->command_channel) != IPC_CONNECT) { crm_err("No reply message - disconnected - %d", rc); rc = cib_not_connected; } else if(rc != IPC_OK) { crm_err("No reply message - failed - %d", rc); rc = cib_reply_failed; } else if(reg_msg == NULL) { crm_err("No reply message - empty - %d", rc); rc = cib_reply_failed; } } if(rc == cib_ok) { const char *msg_type = NULL; msg_type = crm_element_value(reg_msg, F_CIB_OPERATION); if(safe_str_neq(msg_type, CRM_OP_REGISTER) ) { crm_err("Invalid registration message: %s", msg_type); rc = cib_registration_msg; } else { const char *tmp_ticket = NULL; crm_debug_4("Retrieving callback channel ticket"); tmp_ticket = crm_element_value( reg_msg, F_CIB_CALLBACK_TOKEN); if(tmp_ticket == NULL) { rc = cib_callback_token; } else { uuid_ticket = crm_strdup(tmp_ticket); } } } if(reg_msg != NULL) { free_xml(reg_msg); reg_msg = NULL; } if(rc == cib_ok) { crm_debug_4("Registering callback channel with ticket %s", crm_str(uuid_ticket)); reg_msg = create_xml_node(NULL, __FUNCTION__); crm_xml_add(reg_msg, F_CIB_OPERATION, CRM_OP_REGISTER); crm_xml_add(reg_msg, F_CIB_CALLBACK_TOKEN, uuid_ticket); crm_xml_add(reg_msg, F_CIB_CLIENTNAME, name); if(send_ipc_message( native->callback_channel, reg_msg) == FALSE) { rc = cib_callback_register; } free_xml(reg_msg); crm_free(uuid_ticket); } if(rc == cib_ok) { /* In theory IPC_INTR could trip us up here */ crm_debug_4("wait for the callback channel setup to complete"); reg_msg = xmlfromIPC(native->callback_channel, 0); if(native->callback_channel->ops->get_chan_status( native->callback_channel) != IPC_CONNECT) { crm_err("No reply message - disconnected - %d", rc); rc = cib_not_connected; } else if(reg_msg == NULL) { crm_err("No reply message - empty - %d", rc); rc = cib_reply_failed; } free_xml(reg_msg); } if(rc == cib_ok) { 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; crm_warn("Freeing CIB"); if(cib->state != cib_disconnected) { rc = cib_native_signoff(cib); if(rc == cib_ok) { 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 xmlNode * +xmlNode * cib_create_op( int call_id, const char *op, const char *host, const char *section, xmlNode *data, int call_options) { int rc = HA_OK; - xmlNode *op_msg = create_xml_node(NULL, "cib-op"); + xmlNode *op_msg = create_xml_node(NULL, "cib_command"); CRM_CHECK(op_msg != NULL, return NULL); crm_xml_add(op_msg, F_XML_TAGNAME, "cib_command"); crm_xml_add(op_msg, F_TYPE, T_CIB); crm_xml_add(op_msg, F_CIB_OPERATION, op); crm_xml_add(op_msg, F_CIB_HOST, host); crm_xml_add(op_msg, F_CIB_SECTION, section); crm_xml_add_int(op_msg, F_CIB_CALLID, call_id); crm_debug_4("Sending call options: %.8lx, %d", (long)call_options, call_options); crm_xml_add_int(op_msg, F_CIB_CALLOPTS, call_options); if(data != NULL) { #if 0 const char *tag = crm_element_name(data); xmlNode *cib = data; if(safe_str_neq(tag, XML_TAG_CIB)) { cib = find_xml_node(data, XML_TAG_CIB, FALSE); if(cib != NULL) { tag = XML_TAG_CIB; } } if(safe_str_eq(tag, XML_TAG_CIB)) { const char *version = feature_set(cib); crm_xml_add(cib, XML_ATTR_CIB_REVISION, version); } else { crm_info("Skipping feature check for %s tag", tag); } #endif add_message_xml(op_msg, F_CIB_CALLDATA, data); } if (rc != HA_OK) { crm_err("Failed to create CIB operation message"); crm_log_xml(LOG_ERR, "op", op_msg); free_xml(op_msg); return NULL; } if(call_options & cib_inhibit_bcast) { CRM_CHECK((call_options & cib_scope_local), return NULL); } return op_msg; } static gboolean timer_expired = FALSE; static struct timer_rec_s *sync_timer = NULL; 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; } 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(sync_timer == NULL) { crm_malloc0(sync_timer, sizeof(struct timer_rec_s)); } 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; } op_msg = cib_create_op( cib->call_id, 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"); 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 = Gmain_timeout_add( sync_timer->timeout, cib_timeout_handler, sync_timer); } 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; } crm_element_value_int(op_reply, F_CIB_CALLID, &reply_id); CRM_CHECK(reply_id > 0, 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; } else if(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); } 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(op_reply == NULL) { - if(IPC_ISRCONN(native->command_channel) == FALSE) { - crm_err("No reply message - disconnected - %d", - native->command_channel->ch_status); - cib->state = cib_disconnected; - return cib_not_connected; - } - crm_err("No reply message - empty - %d", rc); - return cib_reply_failed; - } - if(IPC_ISRCONN(native->command_channel) == FALSE) { 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; + } + 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; } if(rc == cib_diff_resync) { /* This is an internal value that clients do not and should not care about */ rc = cib_ok; } if(rc == cib_ok || rc == cib_not_master || rc == cib_master_timeout) { crm_log_xml(LOG_MSG, "passed", op_reply); } else { /* } else if(rc == cib_remote_timeout) { */ crm_err("Call failed: %s", cib_error2string(rc)); crm_log_xml(LOG_WARNING, "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); } } 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, 0); 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 0; } else if(native->callback_channel->ch_status != IPC_CONNECT) { return 0; } else if(native->command_channel && native->command_channel->ch_status != IPC_CONNECT){ return 0; } native->callback_channel->ops->waitin(native->callback_channel); } /* IPC_INTR is not a factor here */ msg = xmlfromIPC(native->callback_channel, 0); 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); } 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; } void cib_native_callback(cib_t *cib, xmlNode *msg) { int rc = 0; int call_id = 0; xmlNode *output = NULL; cib_callback_client_t *blob = NULL; cib_callback_client_t local_blob; /* gcc4 had a point... make sure (at least) local_blob.callback * is initialized before use */ local_blob.callback = NULL; local_blob.user_data = NULL; local_blob.only_success = FALSE; crm_element_value_int(msg, F_CIB_CALLID, &call_id); blob = g_hash_table_lookup( cib_op_callback_table, GINT_TO_POINTER(call_id)); if(blob != NULL) { crm_debug_3("Callback found for call %d", call_id); /* local_blob.callback = blob->callback; */ /* local_blob.user_data = blob->user_data; */ /* local_blob.only_success = blob->only_success; */ local_blob = *blob; blob = NULL; remove_cib_op_callback(call_id, FALSE); } else { crm_debug_3("No callback found for call %d", call_id); local_blob.callback = NULL; } crm_element_value_int(msg, F_CIB_RC, &rc); if(rc == cib_diff_resync) { /* This is an internal value that clients do not and should not care about */ rc = cib_ok; } output = get_message_xml(msg, F_CIB_CALLDATA); if(local_blob.callback != NULL && (rc == cib_ok || local_blob.only_success == FALSE)) { local_blob.callback( msg, call_id, rc, output, local_blob.user_data); } else if(cib->op_callback == NULL && rc != cib_ok) { crm_warn("CIB command failed: %s", cib_error2string(rc)); crm_log_xml(LOG_DEBUG, "Failed CIB Update", msg); } if(cib->op_callback == NULL) { crm_debug_3("No OP callback set, ignoring reply"); } else { cib->op_callback(msg, call_id, rc, output); } crm_debug_4("OP callback activated."); } void cib_native_notify(gpointer data, gpointer user_data) { xmlNode *msg = user_data; cib_notify_client_t *entry = data; const char *event = NULL; if(msg == NULL) { crm_warn("Skipping callback - NULL message"); return; } event = crm_element_value(msg, F_SUBTYPE); if(entry == NULL) { crm_warn("Skipping callback - NULL callback client"); return; } else if(entry->callback == NULL) { crm_warn("Skipping callback - NULL callback"); return; } else if(safe_str_neq(entry->event, event)) { crm_debug_4("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event); return; } crm_debug_4("Invoking callback for %p/%s event...", entry, event); entry->callback(event, msg); crm_debug_4("Callback invoked..."); } gboolean cib_native_dispatch(IPC_Channel *channel, gpointer user_data) { int lpc = 0; cib_t *cib = user_data; cib_native_opaque_t *native = NULL; crm_debug_3("Received callback"); if(user_data == NULL){ crm_err("user_data field must contain the CIB struct"); return FALSE; } native = cib->variant_opaque; while(cib_native_msgready(cib)) { lpc++; /* invoke the callbacks but dont block */ if(cib_native_rcvmsg(cib, 0) < 1) { break; } } crm_debug_3("%d CIB messages dispatched", lpc); if(native->callback_channel && native->callback_channel->ch_status != IPC_CONNECT) { crm_crit("Lost connection to the CIB service [%d/callback].", channel->farside_pid); if(native->callback_source != NULL) { G_main_del_IPC_Channel(native->callback_source); native->callback_source = NULL; } return FALSE; } else if(native->command_channel && native->command_channel->ch_status != IPC_CONNECT) { crm_crit("Lost connection to the CIB service [%d/command].", channel->farside_pid); return FALSE; } return TRUE; } 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_ipc_connection_destroy); } else { crm_debug_3("Setting dnotify"); set_IPC_Channel_dnotify(native->callback_source, dnotify); } return cib_ok; } int cib_native_register_callback(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; /* short term hack - should make this generic somehow */ 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/crm/cib/cib_private.h b/lib/crm/cib/cib_private.h index cdd3a31585..5f3fe2f87e 100644 --- a/lib/crm/cib/cib_private.h +++ b/lib/crm/cib/cib_private.h @@ -1,61 +1,64 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef CIB_PRIVATE__H #define CIB_PRIVATE__H #include extern GHashTable *cib_op_callback_table; typedef struct cib_notify_client_s { const char *event; const char *obj_id; /* implement one day */ const char *obj_type; /* implement one day */ void (*callback)(const char *event, xmlNode *msg); } cib_notify_client_t; typedef struct cib_callback_client_s { void (*callback)(xmlNode*, int, int, xmlNode*, void*); void *user_data; gboolean only_success; struct timer_rec_s *timer; } cib_callback_client_t; struct timer_rec_s { int call_id; int timeout; guint ref; }; typedef enum cib_errors (*cib_op_t)(const char *, int, const char *, xmlNode *, xmlNode*, xmlNode*, xmlNode**, xmlNode**); extern cib_t *cib_new_variant(void); enum cib_errors cib_perform_op(const char *op, int call_options, cib_op_t *fn, gboolean is_query, const char *section, xmlNode *req, xmlNode *input, gboolean manage_counters, gboolean *config_changed, xmlNode *current_cib, xmlNode **result_cib, xmlNode **output); +extern xmlNode *cib_create_op( + int call_id, const char *op, const char *host, const char *section, + xmlNode *data, int call_options); #endif diff --git a/lib/crm/cib/cib_remote.c b/lib/crm/cib/cib_remote.c new file mode 100644 index 0000000000..2c3fe2d8b5 --- /dev/null +++ b/lib/crm/cib/cib_remote.c @@ -0,0 +1,513 @@ +/* + * Copyright (c) 2008 Andrew Beekhof + * + * 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 +#include +#include +#include +#include + +#ifdef HAVE_GNUTLS_GNUTLS_H +# undef KEYFILE +# include +#endif + +#include + +#define DH_BITS 1024 +extern gnutls_anon_client_credentials anon_cred_c; + +const int kx_prio[] = +{ + GNUTLS_KX_ANON_DH, + 0 +}; + + +typedef struct cib_remote_opaque_s +{ + int flags; + int socket; + int port; + char *server; + char *user; + char *passwd; + gnutls_session* session; + +} cib_remote_opaque_t; + +int cib_remote_signon(cib_t* cib, const char *name, enum cib_conn_type type); +int cib_remote_signoff(cib_t* cib); +int cib_remote_free(cib_t* cib); + +int cib_remote_perform_op( + cib_t *cib, const char *op, const char *host, const char *section, + xmlNode *data, xmlNode **output_data, int call_options); + +static gboolean cib_remote_msgready(cib_t* cib) { return FALSE; } +static IPC_Channel *cib_remote_channel(cib_t* cib) { return NULL; } +static int cib_remote_inputfd(cib_t* cib) { return cib_NOTSUPPORTED; } +static int cib_remote_rcvmsg(cib_t* cib, int blocking) { return cib_NOTSUPPORTED; } +static gboolean cib_remote_dispatch(IPC_Channel *channel, gpointer user_data) { return FALSE; } + +static int cib_remote_set_connection_dnotify( + cib_t *cib, void (*dnotify)(gpointer user_data)) +{ + return cib_NOTSUPPORTED; +} + + +static int cib_remote_register_callback(cib_t* cib, const char *callback, int enabled) +{ + return cib_NOTSUPPORTED; +} + +cib_t* +cib_remote_new (const char *server, const char *user, const char *passwd, int port) +{ + cib_remote_opaque_t *private = NULL; + cib_t *cib = cib_new_variant(); + + crm_malloc0(private, sizeof(cib_remote_opaque_t)); + + cib->variant = cib_file; + cib->variant_opaque = private; + + if(server) { + private->server = crm_strdup(server); + } + + if(user) { + private->user = crm_strdup(user); + } + + if(passwd) { + private->passwd = crm_strdup(passwd); + } + + private->port = port; + + /* assign variant specific ops*/ + cib->cmds->variant_op = cib_remote_perform_op; + cib->cmds->signon = cib_remote_signon; + cib->cmds->signoff = cib_remote_signoff; + cib->cmds->free = cib_remote_free; + cib->cmds->channel = cib_remote_channel; + cib->cmds->inputfd = cib_remote_inputfd; + cib->cmds->msgready = cib_remote_msgready; + cib->cmds->rcvmsg = cib_remote_rcvmsg; + cib->cmds->dispatch = cib_remote_dispatch; + + cib->cmds->register_callback = cib_remote_register_callback; + cib->cmds->set_connection_dnotify = cib_remote_set_connection_dnotify; + + return cib; +} + +static int +cib_tls_close(cib_t *cib) +{ + cib_remote_opaque_t *private = cib->variant_opaque; + close(private->socket); + gnutls_anon_free_client_credentials (anon_cred_c); + gnutls_global_deinit(); + return 0; +} + +extern gnutls_session *create_tls_session(int csock, int type); + +static int +cib_tls_signon(cib_t *cib) +{ + int sock; + cib_remote_opaque_t *private = cib->variant_opaque; + struct sockaddr_in addr; + int rc = 0; + char *server = private->server; + + int ret_ga; + struct addrinfo *res; + struct addrinfo hints; + + /* create socket */ + sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == -1 ) { + cl_perror("Socket creation failed"); + return -1; + } + + /* getaddrinfo */ + bzero(&hints, sizeof(struct addrinfo)); + hints.ai_flags = AI_CANONNAME; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_RAW; + + if(hints.ai_family == AF_INET6) { + hints.ai_protocol = IPPROTO_ICMPV6; + } else { + hints.ai_protocol = IPPROTO_ICMP; + } + + crm_debug("Looking up %s", server); + ret_ga = getaddrinfo(server, NULL, &hints, &res); + if (ret_ga) { + crm_err("getaddrinfo: %s", gai_strerror(ret_ga)); + return -1; + } + + if (res->ai_canonname) { + server = res->ai_canonname; + } + + crm_debug("Got address %s for %s", server, private->server); + + if (!res->ai_addr) { + fprintf(stderr, "getaddrinfo failed"); + exit(1); + } + +#if 1 + memcpy(&addr, res->ai_addr, res->ai_addrlen); +#else + /* connect to server*/ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr(server); +#endif + addr.sin_port = htons(private->port); + + if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) { + cl_perror("Connection to %s:%d failed", server, private->port); + close(sock); + return -1; + } + + /* initialize GnuTls lib*/ + gnutls_global_init(); + gnutls_anon_allocate_client_credentials(&anon_cred_c); + + /* bind the socket to GnuTls lib */ + private->session = create_tls_session(sock, GNUTLS_CLIENT); + if (private->session == NULL) { + cl_perror("Session creation for %s:%d failed", server, private->port); + close(sock); + cib_tls_close(cib); + return -1; + } + + /* login to server */ + crm_err("Do login..."); + { + xmlNode *answer = NULL; + xmlNode *login = create_xml_node(NULL, "cib_command"); + crm_xml_add(login, "op", "authenticate"); + crm_xml_add(login, "user", private->user); + crm_xml_add(login, "password", private->passwd); + crm_xml_add(login, "hidden", "password"); + + cib_send_remote_msg(private->session, login); + answer = cib_recv_remote_msg(private->session); + + crm_log_xml_err(answer, "Reply"); + if(answer == NULL) { + rc = 1; + } else { + /* create a second connection for notifications? */ + +/* GFDSource* */ +/* G_main_add_fd(int priority, int fd, gboolean can_recurse */ +/* , gboolean (*dispatch)(int fd, gpointer user_data) */ +/* , gpointer userdata */ +/* , GDestroyNotify notify) */ + /* ais_source = G_main_add_fd( */ + /* G_PRIORITY_HIGH, sock, FALSE, ais_dispatch, dispatch, destroy); */ + private->socket = sock; + } + + free_xml(login); + } + + if (rc != 0) { + cib_tls_close(cib); + } + return rc; +} + +int +cib_remote_signon(cib_t* cib, const char *name, enum cib_conn_type type) +{ + int rc = cib_ok; + cib_remote_opaque_t *private = cib->variant_opaque; + + if(private->server == NULL || private->user == NULL) { + rc = cib_missing; + } else { + rc = cib_tls_signon(cib); + } + + if(rc == cib_ok) { + fprintf(stderr, "%s: Opened connection to %s:%d\n", name, private->server, private->port); + cib->state = cib_connected_command; + cib->type = cib_command; + + } else { + fprintf(stderr, "%s: Connection to %s:%d failed: %s\n", + name, private->server, private->port, cib_error2string(rc)); + } + + return rc; +} + +int +cib_remote_signoff(cib_t* cib) +{ + int rc = cib_ok; + /* cib_remote_opaque_t *private = cib->variant_opaque; */ + + crm_debug("Signing out of the CIB Service"); + cib_tls_close(cib); + + cib->state = cib_disconnected; + cib->type = cib_none; + + return rc; +} + +int +cib_remote_free (cib_t* cib) +{ + int rc = cib_ok; + + crm_warn("Freeing CIB"); + if(cib->state != cib_disconnected) { + rc = cib_remote_signoff(cib); + if(rc == cib_ok) { + cib_remote_opaque_t *private = cib->variant_opaque; + crm_free(private->server); + crm_free(private->user); + crm_free(private->passwd); + crm_free(cib->cmds); + crm_free(private); + crm_free(cib); + } + } + + return rc; +} + +static gboolean timer_expired = FALSE; +static struct timer_rec_s *sync_timer = NULL; +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; +} + +int +cib_remote_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_remote_opaque_t *private = cib->variant_opaque; + if(sync_timer == NULL) { + crm_malloc0(sync_timer, sizeof(struct timer_rec_s)); + } + + 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; + } + + op_msg = cib_create_op( + cib->call_id, op, host, section, data, call_options); + if(op_msg == NULL) { + return cib_create_msg; + } + + crm_debug_3("Sending %s message to CIB service", op); + cib_send_remote_msg(private->session, op_msg); + 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_err("Async calls are not yet supported"); + /* return cib->call_id; */ + } + + rc = IPC_OK; + crm_debug_3("Waiting for a syncronous reply"); + + 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 = Gmain_timeout_add( + sync_timer->timeout, cib_timeout_handler, sync_timer); + } + + while(timer_expired == FALSE) { + + op_reply = cib_recv_remote_msg(private->session); + if(op_reply == NULL) { + break; + } + +#if 1 + break; +#else + int reply_id = -1; + int msg_id = cib->call_id; + crm_element_value_int(op_reply, F_CIB_CALLID, &reply_id); + CRM_CHECK(reply_id > 0, + 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; + + } else if(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); + } else { + crm_err("Received a __future__ reply:" + " %d (wanted %d)", reply_id, msg_id); + } +#endif + 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; */ + /* } */ + + if(op_reply == NULL) { + crm_err("No reply message - empty - %d", rc); + return cib_reply_failed; + } + + 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; + } + + if(rc == cib_diff_resync) { + /* This is an internal value that clients do not and should not care about */ + rc = cib_ok; + } + + if(rc == cib_ok || rc == cib_not_master || rc == cib_master_timeout) { + crm_log_xml(LOG_ERR, "passed", op_reply); + + } else { +/* } else if(rc == cib_remote_timeout) { */ + crm_err("Call failed: %s", cib_error2string(rc)); + crm_log_xml(LOG_WARNING, "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); + } + } + + free_xml(op_reply); + + return rc; +} diff --git a/lib/crm/common/remote.c b/lib/crm/common/remote.c new file mode 100644 index 0000000000..ea74d929fd --- /dev/null +++ b/lib/crm/common/remote.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2008 Andrew Beekhof + * + * 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 +#include + +#include +#include + +#ifdef HAVE_GNUTLS_GNUTLS_H +# undef KEYFILE +# include +#endif + +#ifdef HAVE_GNUTLS_GNUTLS_H +const int tls_kx_order[] = { + GNUTLS_KX_ANON_DH, + GNUTLS_KX_DHE_RSA, + GNUTLS_KX_DHE_DSS, + GNUTLS_KX_RSA, + 0 +}; +gnutls_anon_client_credentials anon_cred_c; +gnutls_anon_server_credentials anon_cred_s; +static char *cib_send_tls(gnutls_session *session, xmlNode *msg); +static char *cib_recv_tls(gnutls_session *session); +#endif + +static char *cib_send_plaintext(int sock, xmlNode *msg); +static char *cib_recv_plaintext(int sock); + +#ifdef HAVE_GNUTLS_GNUTLS_H +gnutls_session * +create_tls_session(int csock, int type /* GNUTLS_SERVER, GNUTLS_CLIENT */) +{ + int rc = 0; + gnutls_session *session = gnutls_malloc(sizeof(gnutls_session)); + + gnutls_init(session, type); + gnutls_set_default_priority(*session); + gnutls_kx_set_priority (*session, tls_kx_order); + gnutls_transport_set_ptr(*session, + (gnutls_transport_ptr) GINT_TO_POINTER(csock)); + switch(type) { + case GNUTLS_SERVER: + gnutls_credentials_set(*session, GNUTLS_CRD_ANON, anon_cred_s); + break; + case GNUTLS_CLIENT: + gnutls_credentials_set(*session, GNUTLS_CRD_ANON, anon_cred_c); + break; + } + + do { + rc = gnutls_handshake (*session); + } while (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN); + + if (rc < 0) { + crm_err("Handshake failed: %s", gnutls_strerror(rc)); + gnutls_deinit(*session); + gnutls_free(session); + return NULL; + } + return session; +} + +static char* +cib_send_tls(gnutls_session *session, xmlNode *msg) +{ + char *xml_text = NULL; + const char *name = crm_element_name(msg); +#if 0 + if(safe_str_neq(name, "cib_command")) { + msg->name = xmlCharStrdup("cib_result"); + } +#endif + xml_text = dump_xml_unformatted(msg); + if(xml_text != NULL) { + int len = strlen(xml_text); + len++; /* null char */ + crm_debug_3("Message size: %d", len); + gnutls_record_send (*session, xml_text, len); + } + crm_free(xml_text); + return NULL; + +} + +static char* +cib_recv_tls(gnutls_session *session) +{ + int rc = 0; + int last = 0; + char* tls_buf = NULL; + int chunk_size = 1024; + int len = chunk_size; + + if (session == NULL) { + return NULL; + } + + crm_malloc0(tls_buf, chunk_size); + + while(1) { + crm_debug("Creating more space: %d += %d: %.60s", len, chunk_size, tls_buf); + rc = gnutls_record_recv(*session, tls_buf+last, chunk_size); + if (rc == 0) { + if(len == 0) { + goto bail; + } + return tls_buf; + + } else if(rc > 0 && rc < chunk_size) { + return tls_buf; + + } else if(rc == chunk_size) { + crm_debug("Creating more space: %d += %d: %.60s", len, chunk_size, tls_buf); + last = len; + len += chunk_size; + crm_realloc(tls_buf, len); + CRM_ASSERT(tls_buf != NULL); + crm_debug("New size: %d: %.60s", len, tls_buf); + } + + if(rc < 0 + && rc != GNUTLS_E_INTERRUPTED + && rc != GNUTLS_E_AGAIN) { + cl_perror("Error receiving message: %d", rc); + goto bail; + } + } + bail: + crm_free(tls_buf); + return NULL; + +} +#endif + +static char* +cib_send_plaintext(int sock, xmlNode *msg) +{ + char *xml_text = NULL; + msg->name = xmlCharStrdup("cib_result"); + xml_text = dump_xml_unformatted(msg); + if(xml_text != NULL) { + int rc = 0; + int len = strlen(xml_text); + len++; /* null char */ + crm_debug_3("Message size: %d", len); + rc = write (sock, xml_text, len); + CRM_CHECK(len == rc, + crm_warn("Wrote %d of %d bytes", rc, len)); + } + crm_free(xml_text); + return NULL; + +} + +static char* +cib_recv_plaintext(int sock) +{ + int last = 0; + char* buf = NULL; + int chunk_size = 512; + int len = chunk_size; + + crm_malloc0(buf, chunk_size); + + while(1) { + int rc = recv(sock, buf+last, chunk_size, 0); + if (rc == 0) { + if(len == 0) { + goto bail; + } + return buf; + + } else if(rc > 0 && rc < chunk_size) { + return buf; + + } else if(rc == chunk_size) { + last = len; + len += chunk_size; + crm_realloc(buf, len); + CRM_ASSERT(buf != NULL); + } + + if(rc < 0 && errno != EINTR) { + cl_perror("Error receiving message: %d", rc); + goto bail; + } + } + bail: + crm_free(buf); + return NULL; + +} + +void +cib_send_remote_msg(void *session, xmlNode *msg) +{ +#ifdef HAVE_GNUTLS_GNUTLS_H + cib_send_tls(session, msg); +#else + cib_send_plaintext(GPOINTER_TO_INT(session), msg); +#endif +} + +xmlNode* +cib_recv_remote_msg(void *session) +{ + char *reply = NULL; + xmlNode *xml = NULL; +#ifdef HAVE_GNUTLS_GNUTLS_H + + reply = cib_recv_tls(session); +#else + reply = cib_recv_plaintext(GPOINTER_TO_INT(session)); +#endif + crm_debug("Got: '%.120s'", reply); + if(reply == NULL || strlen(reply) == 0) { + crm_err("Empty reply"); + } else { + xml = string2xml(reply); + if(xml == NULL) { + crm_err("Couldn't parse: '%.120s'", reply); + } + } + + crm_free(reply); + return xml; +} +