Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/include/crm_internal.h b/include/crm_internal.h
index 388af5931f..664e3e0b7f 100644
--- a/include/crm_internal.h
+++ b/include/crm_internal.h
@@ -1,290 +1,301 @@
/* crm_internal.h */
/*
* Copyright (C) 2006 - 2008
* Andrew Beekhof <andrew@beekhof.net>
*
* 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 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 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
*/
#ifndef CRM_INTERNAL__H
# define CRM_INTERNAL__H
# include <config.h>
# include <portability.h>
# include <glib.h>
# include <stdbool.h>
# include <libxml/tree.h>
# include <crm/lrmd.h>
# include <crm/common/logging.h>
/* 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(char *user, xmlNode * request, const char *field);
# if ENABLE_ACL
# include <string.h>
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 <getopt.h>
# 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);
void 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);
/* Shared PE/crmd functionality */
void filter_action_parameters(xmlNode * param_set, const char *version);
void filter_reload_parameters(xmlNode * param_set, const char *restart_string);
/* Resource operation updates */
xmlNode *create_operation_update(xmlNode * parent, lrmd_event_data_t *event, const char *caller_version,
int target_rc, 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 */
static inline int
crm_strlen_zero(const char *s)
{
return !s || *s == '\0';
}
char *add_list_element(char *list, const char *value);
char *generate_series_filename(const char *directory, const char *series, int sequence, gboolean bzip);
int get_last_sequence(const char *directory, const char *series);
void write_last_sequence(const char *directory, const char *series, int sequence, int max);
int crm_pid_active(long pid);
void crm_make_daemon(const char *name, gboolean daemonize, const char *pidfile);
gboolean crm_is_writable(const char *dir, const char *file, const char *user, const char *group, gboolean need_both);
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(__PRETTY_FUNCTION__, NULL, word, bit)
# define clear_bit(word, bit) word = crm_clear_bit(__PRETTY_FUNCTION__, NULL, word, bit)
void g_hash_destroy_str(gpointer data);
long long crm_int_helper(const char *text, char **end_text);
char *crm_concat(const char *prefix, const char *suffix, char join);
char *generate_hash_key(const char *crm_msg_reference, const char *sys);
/*! remote tcp/tls helper functions */
gboolean crm_recv_remote_msg(void *session, char **recv_buf, gboolean encrypted, int total_timeout_ms, int *disconnected);
char *crm_recv_remote_raw(void *data, gboolean encrypted, size_t max_recv, size_t *recv_len, int *disconnected);
int crm_send_remote_msg(void *session, xmlNode * msg, gboolean encrypted);
int crm_recv_remote_ready(void *session, gboolean encrypted, int timeout_ms);
xmlNode *crm_parse_remote_buffer(char **msg_buf);
int crm_remote_tcp_connect(const char *host, int port);
#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(void *session_data, 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 * 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 * 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);
# 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_TASK "task"
# define F_ATTRD_VALUE "attr_value"
# define F_ATTRD_SET "attr_set"
# 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_USER "attr_user"
# if SUPPORT_COROSYNC
# if CS_USES_LIBQB
# include <qb/qbipc_common.h>
# include <corosync/corotypes.h>
typedef struct qb_ipc_request_header cs_ipc_header_request_t;
typedef struct qb_ipc_response_header cs_ipc_header_response_t;
# else
# include <corosync/corodefs.h>
# include <corosync/coroipcc.h>
# include <corosync/coroipc_types.h>
static inline int
qb_to_cs_error(int a)
{
return a;
}
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
#endif /* CRM_INTERNAL__H */
diff --git a/lib/common/remote.c b/lib/common/remote.c
index ae61481bc5..9f05a5e682 100644
--- a/lib/common/remote.c
+++ b/lib/common/remote.c
@@ -1,705 +1,735 @@
/*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <crm_internal.h>
#include <crm/crm.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netdb.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <glib.h>
#include <crm/common/ipc.h>
#include <crm/common/xml.h>
#ifdef HAVE_GNUTLS_GNUTLS_H
# undef KEYFILE
# include <gnutls/gnutls.h>
#endif
#ifdef HAVE_GNUTLS_GNUTLS_H
+const int psk_tls_kx_order[] = {
+ GNUTLS_KX_DHE_PSK,
+ GNUTLS_KX_PSK,
+};
const int anon_tls_kx_order[] = {
GNUTLS_KX_ANON_DH,
GNUTLS_KX_DHE_RSA,
GNUTLS_KX_DHE_DSS,
GNUTLS_KX_RSA,
0
};
int
crm_initiate_client_tls_handshake(void *session_data, int timeout_ms)
{
int rc = 0;
int pollrc = 0;
time_t start = time(NULL);
gnutls_session *session = session_data;
do {
rc = gnutls_handshake(*session);
if (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN) {
pollrc = crm_recv_remote_ready(session, TRUE, 1000);
if (pollrc < 0) {
/* poll returned error, there is no hope */
rc = -1;
}
}
} while (((time(NULL) - start) < (timeout_ms/1000)) &&
(rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN));
return rc;
}
void *
crm_create_anon_tls_session(int csock, int type /* GNUTLS_SERVER, GNUTLS_CLIENT */, void *credentials)
{
gnutls_session *session = gnutls_malloc(sizeof(gnutls_session));
gnutls_init(session, type);
# ifdef HAVE_GNUTLS_PRIORITY_SET_DIRECT
/* http://www.manpagez.com/info/gnutls/gnutls-2.10.4/gnutls_81.php#Echo-Server-with-anonymous-authentication */
gnutls_priority_set_direct(*session, "NORMAL:+ANON-DH", NULL);
/* gnutls_priority_set_direct (*session, "NONE:+VERS-TLS-ALL:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ANON-DH", NULL); */
# else
gnutls_set_default_priority(*session);
gnutls_kx_set_priority(*session, anon_tls_kx_order);
# endif
gnutls_transport_set_ptr(*session, (gnutls_transport_ptr) GINT_TO_POINTER(csock));
switch (type) {
case GNUTLS_SERVER:
gnutls_credentials_set(*session, GNUTLS_CRD_ANON, (gnutls_anon_server_credentials_t) credentials);
break;
case GNUTLS_CLIENT:
gnutls_credentials_set(*session, GNUTLS_CRD_ANON, (gnutls_anon_client_credentials_t) credentials);
break;
}
return session;
}
+void *
+create_psk_tls_session(int csock, int type /* GNUTLS_SERVER, GNUTLS_CLIENT */, void *credentials)
+{
+ gnutls_session *session = gnutls_malloc(sizeof(gnutls_session));
+
+ gnutls_init(session, type);
+# ifdef HAVE_GNUTLS_PRIORITY_SET_DIRECT
+ gnutls_priority_set_direct(*session, "NORMAL:+DHE-PSK:+PSK", NULL);
+# else
+ gnutls_set_default_priority(*session);
+ gnutls_kx_set_priority(*session, psk_tls_kx_order);
+# endif
+ gnutls_transport_set_ptr(*session, (gnutls_transport_ptr) GINT_TO_POINTER(csock));
+ switch (type) {
+ case GNUTLS_SERVER:
+ gnutls_credentials_set(*session, GNUTLS_CRD_PSK, (gnutls_psk_server_credentials_t) credentials);
+ break;
+ case GNUTLS_CLIENT:
+ gnutls_credentials_set(*session, GNUTLS_CRD_PSK, (gnutls_psk_client_credentials_t) credentials);
+ break;
+ }
+
+ return session;
+}
+
+
static int
crm_send_tls(gnutls_session * session, const char *buf, size_t len)
{
const char *unsent = buf;
int rc = 0;
int total_send;
if (buf == NULL) {
return -1;
}
total_send = len;
crm_trace("Message size: %d", len);
while (TRUE) {
rc = gnutls_record_send(*session, unsent, len);
if (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN) {
crm_debug("Retry");
} else if (rc < 0) {
crm_err("Connection terminated rc = %d", rc);
break;
} else if (rc < len) {
crm_debug("Only sent %d of %d bytes", rc, len);
len -= rc;
unsent += rc;
} else {
crm_debug("Sent %d bytes", rc);
break;
}
}
return rc < 0 ? rc : total_send;
}
/*!
* \internal
* \brief Read bytes off non blocking tls session.
*
* \param session - tls session to read
* \param max_size - max bytes allowed to read for buffer. 0 assumes no limit
*
* \note only use with NON-Blocking sockets. Should only be used after polling socket.
* This function will return once max_size is met, the socket read buffer
* is empty, or an error is encountered.
*
* \retval '\0' terminated buffer on success
*/
static char *
crm_recv_tls(gnutls_session * session, size_t max_size, size_t *recv_len, int *disconnected)
{
char *buf = NULL;
int rc = 0;
size_t len = 0;
size_t chunk_size = max_size ? max_size : 1024;
size_t buf_size = 0;
size_t read_size = 0;
if (session == NULL) {
if (disconnected) {
*disconnected = 1;
}
goto done;
}
buf = calloc(1, chunk_size + 1);
buf_size = chunk_size;
while (TRUE) {
read_size = buf_size - len;
/* automatically grow the buffer when needed if max_size is not set.*/
if (!max_size && (read_size < (chunk_size / 2))) {
buf_size += chunk_size;
crm_trace("Grow buffer by %d more bytes. buf is now %d bytes", (int)chunk_size, buf_size);
buf = realloc(buf, buf_size + 1);
CRM_ASSERT(buf != NULL);
read_size = buf_size - len;
}
rc = gnutls_record_recv(*session, buf + len, read_size);
if (rc > 0) {
crm_trace("Got %d more bytes.", rc);
len += rc;
/* always null terminate buffer, the +1 to alloc always allows for this.*/
buf[len] = '\0';
}
if (max_size && (max_size == read_size)) {
crm_trace("Buffer max read size %d met" , max_size);
goto done;
}
/* process any errors. */
if (rc == GNUTLS_E_INTERRUPTED) {
crm_trace("EINTR encoutered, retry tls read");
} else if (rc == GNUTLS_E_AGAIN) {
crm_trace("non-blocking, exiting read on rc = %d", rc);
goto done;
} else if (rc <= 0) {
if (rc == 0) {
crm_debug("EOF encoutered during TLS read");
} else {
crm_debug("Error receiving message: %s (%d)", gnutls_strerror(rc), rc);
}
if (disconnected) {
*disconnected = 1;
}
goto done;
}
}
done:
if (recv_len) {
*recv_len = len;
}
if (!len) {
free(buf);
buf = NULL;
}
return buf;
}
#endif
static int
crm_send_plaintext(int sock, const char *buf, size_t len)
{
int rc = 0;
const char *unsent = buf;
int total_send;
if (buf == NULL) {
return -1;
}
total_send = len;
crm_trace("Message on socket %d: size=%d", sock, len);
retry:
rc = write(sock, unsent, len);
if (rc < 0) {
switch (errno) {
case EINTR:
case EAGAIN:
crm_trace("Retry");
goto retry;
default:
crm_perror(LOG_ERR, "Could only write %d of the remaining %d bytes", rc, (int) len);
break;
}
} else if (rc < len) {
crm_trace("Only sent %d of %d remaining bytes", rc, len);
len -= rc;
unsent += rc;
goto retry;
} else {
crm_trace("Sent %d bytes: %.100s", rc, buf);
}
return rc < 0 ? rc : total_send;
}
/*!
* \internal
* \brief Read bytes off non blocking socket.
*
* \param session - tls session to read
* \param max_size - max bytes allowed to read for buffer. 0 assumes no limit
*
* \note only use with NON-Blocking sockets. Should only be used after polling socket.
* This function will return once max_size is met, the socket read buffer
* is empty, or an error is encountered.
*
* \retval '\0' terminated buffer on success
*/
static char *
crm_recv_plaintext(int sock, size_t max_size, size_t *recv_len, int *disconnected)
{
char *buf = NULL;
ssize_t rc = 0;
ssize_t len = 0;
ssize_t chunk_size = max_size ? max_size : 1024;
size_t buf_size = 0;
size_t read_size = 0;
if (sock <= 0) {
if (disconnected) {
*disconnected = 1;
}
goto done;
}
buf = calloc(1, chunk_size + 1);
buf_size = chunk_size;
while (TRUE) {
errno = 0;
read_size = buf_size - len;
/* automatically grow the buffer when needed if max_size is not set.*/
if (!max_size && (read_size < (chunk_size / 2))) {
buf_size += chunk_size;
crm_trace("Grow buffer by %d more bytes. buf is now %d bytes", (int)chunk_size, buf_size);
buf = realloc(buf, buf_size + 1);
CRM_ASSERT(buf != NULL);
read_size = buf_size - len;
}
rc = read(sock, buf + len, chunk_size);
if (rc > 0) {
crm_trace("Got %d more bytes. errno=%d", (int)rc, errno);
len += rc;
/* always null terminate buffer, the +1 to alloc always allows for this.*/
buf[len] = '\0';
}
if (max_size && (max_size == read_size)) {
crm_trace("Buffer max read size %d met" , max_size);
goto done;
}
if (rc > 0) {
continue;
} else if (rc == 0) {
if (disconnected) {
*disconnected = 1;
}
crm_trace("EOF encoutered during read");
goto done;
}
/* process errors */
if (errno == EINTR) {
crm_trace("EINTER encoutered, retry socket read.");
} else if (errno == EAGAIN) {
crm_trace("non-blocking, exiting read on rc = %d", rc);
goto done;
} else if (errno <= 0) {
if (disconnected) {
*disconnected = 1;
}
crm_debug("Error receiving message: %d", (int)rc);
goto done;
}
}
done:
if (recv_len) {
*recv_len = len;
}
if (!len) {
free(buf);
buf = NULL;
}
return buf;
}
static int
crm_send_remote_msg_raw(void *session, const char *buf, size_t len, gboolean encrypted)
{
int rc = -1;
if (encrypted) {
#ifdef HAVE_GNUTLS_GNUTLS_H
rc = crm_send_tls(session, buf, len);
#else
CRM_ASSERT(encrypted == FALSE);
#endif
} else {
rc = crm_send_plaintext(GPOINTER_TO_INT(session), buf, len);
}
return rc;
}
int
crm_send_remote_msg(void *session, xmlNode * msg, gboolean encrypted)
{
int rc = -1;
char *xml_text = NULL;
int len = 0;
xml_text = dump_xml_unformatted(msg);
if (xml_text) {
len = strlen(xml_text);
} else {
crm_err("Invalid XML, can not send msg");
return -1;
}
rc = crm_send_remote_msg_raw(session, xml_text, len, encrypted);
if (rc < 0) {
goto done;
}
rc = crm_send_remote_msg_raw(session, REMOTE_MSG_TERMINATOR, strlen(REMOTE_MSG_TERMINATOR), encrypted);
done:
if (rc < 0) {
crm_err("Failed to send remote msg, rc = %d", rc);
}
free(xml_text);
return rc;
}
/*!
* \internal
* \brief handles the recv buffer and parsing out msgs.
* \note new_data is owned by this function once it is passed in.
*/
xmlNode *
crm_parse_remote_buffer(char **msg_buf)
{
char *buf = NULL;
char *start = NULL;
char *end = NULL;
xmlNode *xml = NULL;
if (*msg_buf == NULL) {
return NULL;
}
/* take ownership of the buffer */
buf = *msg_buf;
*msg_buf = NULL;
/* MSGS are separated by a '\r\n\r\n'. Split a message off the buffer and return it. */
start = buf;
end = strstr(start, REMOTE_MSG_TERMINATOR);
while (!xml && end) {
/* grab the message */
end[0] = '\0';
end += strlen(REMOTE_MSG_TERMINATOR);
xml = string2xml(start);
if (xml == NULL) {
crm_err("Couldn't parse: '%.120s'", start);
}
start = end;
end = strstr(start, REMOTE_MSG_TERMINATOR);
}
if (xml && start) {
/* we have msgs left over, save it until next time */
*msg_buf = strdup(start);
free(buf);
} else if (!xml) {
/* no msg present */
*msg_buf = buf;
}
return xml;
}
/*!
* \internal
* \brief Determine if a remote session has data to read
*
* \retval 0, timeout occured.
* \retval positive, data is ready to be read
* \retval negative, session has ended
*/
int
crm_recv_remote_ready(void *session, gboolean encrypted, int timeout /* ms */)
{
struct pollfd fds = { 0, };
int sock = 0;
void *sock_ptr = NULL;
int rc = 0;
time_t start;
if (encrypted) {
#ifdef HAVE_GNUTLS_GNUTLS_H
gnutls_session *tls_session = session;
sock_ptr = gnutls_transport_get_ptr(*tls_session);
#else
CRM_ASSERT(encrypted == FALSE);
#endif
} else {
sock_ptr = session;
}
sock = GPOINTER_TO_INT(sock_ptr);
if (sock <= 0) {
return -ENOTCONN;
}
start = time(NULL);
errno = 0;
do {
fds.fd = sock;
fds.events = POLLIN;
/* If we got an EINTR while polling, and we have a
* specific timeout we are trying to honor, attempt
* to adjust the timeout to the closest second. */
if (errno == EINTR && (timeout > 0)) {
timeout = timeout - ((time(NULL) - start) * 1000);
if (timeout < 1000) {
timeout = 1000;
}
}
rc = poll(&fds, 1, timeout);
} while (rc < 0 && errno == EINTR);
return rc;
}
char *
crm_recv_remote_raw(void *session, gboolean encrypted, size_t max_recv, size_t *recv_len, int *disconnected)
{
char *reply = NULL;
if (recv_len) {
*recv_len = 0;
}
if (disconnected) {
*disconnected = 0;
}
if (encrypted) {
#ifdef HAVE_GNUTLS_GNUTLS_H
reply = crm_recv_tls(session, max_recv, recv_len, disconnected);
#else
CRM_ASSERT(encrypted == FALSE);
#endif
} else {
reply = crm_recv_plaintext(GPOINTER_TO_INT(session), max_recv, recv_len, disconnected);
}
if (reply == NULL || strlen(reply) == 0) {
crm_trace("Empty reply");
}
return reply;
}
/*!
* \internal
* \brief Read data off the socket until at least one full message is present or timeout occures.
* \retval TRUE message read
* \retval FALSE full message not read
*/
gboolean
crm_recv_remote_msg(void *session, char **recv_buf, gboolean encrypted, int total_timeout /*ms */, int *disconnected)
{
int ret;
size_t request_len = 0;
time_t start = time(NULL);
char *raw_request = NULL;
int remaining_timeout = 0;
if (total_timeout == 0) {
total_timeout = 10000;
} else if (total_timeout < 0) {
total_timeout = 60000;
}
*disconnected = 0;
remaining_timeout = total_timeout;
while ((remaining_timeout > 0) && !(*disconnected)) {
/* read some more off the tls buffer if we still have time left. */
crm_trace("waiting to receive remote msg, starting timeout %d, remaining_timeout %d", total_timeout, remaining_timeout);
ret = crm_recv_remote_ready(session, encrypted, remaining_timeout);
raw_request = NULL;
if (ret == 0) {
crm_err("poll timed out (%d ms) while waiting to receive msg", remaining_timeout);
return FALSE;
} else if (ret < 0) {
if (errno != EINTR) {
crm_debug("poll returned error while waiting for msg, rc: %d, errno: %d", ret, errno);
*disconnected = 1;
return FALSE;
}
crm_debug("poll EINTR encountered during poll, retrying");
} else {
raw_request = crm_recv_remote_raw(session, encrypted, 0, &request_len, disconnected);
}
remaining_timeout = remaining_timeout - ((time(NULL) - start) * 1000);
if (!raw_request) {
crm_debug("Empty msg received after poll");
continue;
}
if (*recv_buf) {
int old_len = strlen(*recv_buf);
crm_trace("Expanding recv buffer from %d to %d", old_len, old_len+request_len);
*recv_buf = realloc(*recv_buf, old_len + request_len + 1);
memcpy(*recv_buf + old_len, raw_request, request_len);
*(*recv_buf+old_len+request_len) = '\0';
free(raw_request);
} else {
*recv_buf = raw_request;
}
if (strstr(*recv_buf, REMOTE_MSG_TERMINATOR)) {
return TRUE;
}
}
return FALSE;
}
/*!
* \internal
* \brief tcp connection to server at specified port
* \retval positive, socket fd.
* \retval negative, failed to connect.
*/
int
crm_remote_tcp_connect(const char *host, int port)
{
struct addrinfo *res;
struct addrinfo *rp;
struct addrinfo hints;
const char *server = host;
int ret_ga;
int sock;
/* getaddrinfo */
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_CANONNAME;
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 || !res->ai_addr) {
crm_err("getaddrinfo failed");
return -1;
}
for (rp = res; rp != NULL; rp = rp->ai_next) {
struct sockaddr *addr = rp->ai_addr;
int flag = 0;
if (!addr) {
continue;
}
if (rp->ai_canonname) {
server = res->ai_canonname;
}
crm_debug("Got address %s for %s", server, host);
/* create socket */
sock = socket(rp->ai_family, SOCK_STREAM, IPPROTO_TCP);
if (sock == -1) {
crm_err("Socket creation failed for remote client connection.");
continue;
}
if (addr->sa_family == AF_INET6) {
struct sockaddr_in6 *addr_in = (struct sockaddr_in6 *) addr;
addr_in->sin6_port = htons(port);
} else {
struct sockaddr_in *addr_in = (struct sockaddr_in *) addr;
addr_in->sin_port = htons(port);
crm_info("Attempting to connect to remote server at %s:%d", inet_ntoa(addr_in->sin_addr), port);
}
if (connect(sock, rp->ai_addr, rp->ai_addrlen) == 0) {
if ((flag = fcntl(sock, F_GETFL)) >= 0) {
if (fcntl(sock, F_SETFL, flag | O_NONBLOCK) < 0) {
crm_err( "fcntl() write failed");
close(sock);
sock = -1;
continue;
}
}
break; /* Success */
}
close(sock);
sock = -1;
}
freeaddrinfo(res);
return sock;
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Oct 16, 12:11 AM (1 d, 6 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2530734
Default Alt Text
(30 KB)

Event Timeline