mirror of https://github.com/sipwise/heartbeat.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1042 lines
27 KiB
1042 lines
27 KiB
/* File: stonithd_lib.c
|
|
* Description: Client library to STONITH deamon.
|
|
*
|
|
* Author: Sun Jiang Dong <sunjd@cn.ibm.com>
|
|
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <lha_internal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/wait.h>
|
|
#include <errno.h>
|
|
#include <glib.h>
|
|
#include <ha_msg.h>
|
|
#include <clplumbing/cl_signal.h>
|
|
#include <clplumbing/GSource.h>
|
|
#include <clplumbing/uids.h>
|
|
#include <clplumbing/cl_log.h>
|
|
#include <clplumbing/lsb_exitcodes.h>
|
|
#include <clplumbing/proctrack.h>
|
|
#include <fencing/stonithd_api.h>
|
|
#include <fencing/stonithd_msg.h>
|
|
#include <string.h>
|
|
|
|
#include <assert.h>
|
|
|
|
static const char * CLIENT_NAME = NULL;
|
|
static pid_t CLIENT_PID = 0;
|
|
static char CLIENT_PID_STR[16];
|
|
static gboolean DEBUG_MODE = FALSE;
|
|
static IPC_Channel * chan = NULL;
|
|
static IPC_Channel * cbchan = NULL;
|
|
|
|
static gboolean INT_BY_ALARM = FALSE;
|
|
static unsigned int DEFAULT_TIMEOUT = 60;
|
|
|
|
/* Must correspond to stonith_type_t */
|
|
/* Not use it yet
|
|
static const char * stonith_op_strname[] =
|
|
{
|
|
"QUERY", "RESET", "POWERON", "POWEROFF"
|
|
};
|
|
*/
|
|
|
|
static stonith_ops_callback_t stonith_ops_cb = NULL;
|
|
static stonithRA_ops_callback_t stonithRA_ops_cb = NULL;
|
|
static void * stonithRA_ops_cb_private_data = NULL;
|
|
|
|
static struct ha_msg * create_basic_reqmsg_fields(const char * apitype);
|
|
static gboolean cmp_field(const struct ha_msg * msg,
|
|
const char * field_name, const char * field_content,
|
|
gboolean mandatory);
|
|
static gboolean is_expected_msg(const struct ha_msg * msg,
|
|
const char * field_name1,
|
|
const char * field_content1,
|
|
const char * field_name2,
|
|
const char * field_content2,
|
|
gboolean mandatory);
|
|
static int chan_wait_timeout(IPC_Channel * chan,
|
|
int (*waitfun)(IPC_Channel * chan), unsigned int timeout);
|
|
static int chan_waitin_timeout(IPC_Channel * chan, unsigned int timeout);
|
|
static int chan_waitout_timeout(IPC_Channel * chan, unsigned int timeout);
|
|
static void sigalarm_handler(int signum);
|
|
static void free_stonith_ops_t(stonith_ops_t * st_op);
|
|
static void free_stonithRA_ops_t(stonithRA_ops_t * ra_op);
|
|
|
|
#define stdlib_log(priority, fmt...); \
|
|
if ( ( priority != LOG_DEBUG ) || ( debug_level > 0 ) ) { \
|
|
cl_log(priority, fmt); \
|
|
}
|
|
#define signed_on(ch) (ch && ch->ch_status != IPC_DISCONNECT)
|
|
|
|
#define LOG_FAILED_TO_GET_FIELD(field) \
|
|
stdlib_log(LOG_ERR \
|
|
, "%s:%d: cannot get field %s from message." \
|
|
,__FUNCTION__,__LINE__,field)
|
|
|
|
#define st_get_int_value(msg,fld,i) do { \
|
|
if (HA_OK != ha_msg_value_int(msg,fld,i)) { \
|
|
LOG_FAILED_TO_GET_FIELD(fld); \
|
|
rc = ST_FAIL; \
|
|
} \
|
|
} while(0)
|
|
#define st_save_string(msg,fld,v) do { \
|
|
const char *tmp; \
|
|
tmp = cl_get_string(msg,fld); \
|
|
if (!tmp) { \
|
|
LOG_FAILED_TO_GET_FIELD(fld); \
|
|
rc = ST_FAIL; \
|
|
} else { \
|
|
if( !(v = g_strdup(tmp)) ) { \
|
|
rc = ST_FAIL; \
|
|
stdlib_log(LOG_ERR,"%s:%d: out of memory" \
|
|
,__FUNCTION__,__LINE__); \
|
|
} \
|
|
} \
|
|
} while(0)
|
|
#define st_get_hashtable(msg,fld,v) do { \
|
|
v = cl_get_hashtable(msg,fld); \
|
|
if (!v) { \
|
|
LOG_FAILED_TO_GET_FIELD(fld); \
|
|
rc = ST_FAIL; \
|
|
} \
|
|
} while(0)
|
|
|
|
static int
|
|
send_request(IPC_Channel *chan, struct ha_msg *request, int timeout)
|
|
{
|
|
int rc;
|
|
|
|
assert(chan != NULL);
|
|
assert(request != NULL);
|
|
|
|
/* Send the request message */
|
|
if (msg2ipcchan(request, chan) != HA_OK) {
|
|
stdlib_log(LOG_ERR, "can't send signon message to IPC");
|
|
return ST_FAIL;
|
|
}
|
|
|
|
/* wait for the output to finish */
|
|
/* XXX: the time ellapsed should be substracted from timeout
|
|
* each time the call is resumed.
|
|
*/
|
|
do {
|
|
rc = chan_waitout_timeout(chan, timeout);
|
|
} while (rc == IPC_INTR);
|
|
|
|
if (rc != IPC_OK) {
|
|
stdlib_log(LOG_ERR, "waitout failed.");
|
|
return ST_FAIL;
|
|
}
|
|
return ST_OK;
|
|
}
|
|
|
|
static struct ha_msg *
|
|
recv_response(IPC_Channel *chan, int timeout)
|
|
{
|
|
struct ha_msg *reply;
|
|
if (IPC_OK != chan_waitin_timeout(chan, timeout)) {
|
|
stdlib_log(LOG_ERR, "waitin failed.");
|
|
return NULL;
|
|
}
|
|
if (!(reply = msgfromIPC_noauth(chan))) {
|
|
stdlib_log(LOG_ERR, "failed to recv response");
|
|
return NULL;
|
|
}
|
|
return reply;
|
|
}
|
|
|
|
static int
|
|
authenticate_with_cookie(IPC_Channel *chan, cl_uuid_t *cookie)
|
|
{
|
|
struct ha_msg * request;
|
|
struct ha_msg * reply;
|
|
|
|
assert(chan != NULL);
|
|
assert(cookie != NULL);
|
|
|
|
if (!(request = create_basic_reqmsg_fields(ST_SIGNON))) {
|
|
return ST_FAIL;
|
|
}
|
|
if (ha_msg_adduuid(request, F_STONITHD_COOKIE, cookie) != HA_OK) {
|
|
stdlib_log(LOG_ERR, "cannot add field to ha_msg.");
|
|
ZAPMSG(request);
|
|
return ST_FAIL;
|
|
}
|
|
|
|
/* Send request/read response */
|
|
if (send_request(chan, request, DEFAULT_TIMEOUT) != ST_OK) {
|
|
ZAPMSG(request);
|
|
return ST_FAIL;
|
|
}
|
|
if (!(reply = recv_response(chan, DEFAULT_TIMEOUT))) {
|
|
return ST_FAIL;
|
|
}
|
|
|
|
/* Are we signed on this time? */
|
|
if ( TRUE == is_expected_msg(reply, F_STONITHD_TYPE, ST_APIRPL,
|
|
F_STONITHD_APIRPL, ST_RSIGNON, TRUE) ) {
|
|
if ( !STRNCMP_CONST(
|
|
cl_get_string(reply,F_STONITHD_APIRET), ST_APIOK) ) {
|
|
ZAPMSG(reply);
|
|
return ST_OK;
|
|
}
|
|
}
|
|
|
|
ZAPMSG(reply);
|
|
return ST_FAIL;
|
|
}
|
|
|
|
int
|
|
stonithd_signon(const char * client_name)
|
|
{
|
|
int rc = ST_FAIL;
|
|
char path[] = IPC_PATH_ATTR;
|
|
char sock[] = STONITHD_SOCK;
|
|
char cbsock[] = STONITHD_CALLBACK_SOCK;
|
|
struct ha_msg * request;
|
|
struct ha_msg * reply;
|
|
GHashTable * wchanattrs;
|
|
uid_t my_euid;
|
|
gid_t my_egid;
|
|
const char * tmpstr;
|
|
int rc_tmp;
|
|
gboolean connected = TRUE;
|
|
cl_uuid_t cookie, *cptr = NULL;
|
|
|
|
if (chan == NULL || chan->ch_status != IPC_CONNECT) {
|
|
connected = FALSE;
|
|
} else if (cbchan == NULL || cbchan->ch_status != IPC_CONNECT) {
|
|
connected = FALSE;
|
|
}
|
|
|
|
if(!connected) {
|
|
/* cleanup */
|
|
if (NULL != chan) {
|
|
chan->ops->destroy(chan);
|
|
chan = NULL;
|
|
}
|
|
if (NULL != cbchan) {
|
|
cbchan->ops->destroy(cbchan);
|
|
cbchan = NULL;
|
|
}
|
|
stdlib_log(LOG_DEBUG, "stonithd_signon: creating connection");
|
|
wchanattrs = g_hash_table_new(g_str_hash, g_str_equal);
|
|
g_hash_table_insert(wchanattrs, path, sock);
|
|
/* Connect to the stonith deamon */
|
|
chan = ipc_channel_constructor(IPC_ANYTYPE, wchanattrs);
|
|
g_hash_table_destroy(wchanattrs);
|
|
|
|
if (chan == NULL) {
|
|
stdlib_log(LOG_ERR, "stonithd_signon: Can't connect "
|
|
" to stonithd");
|
|
rc = ST_FAIL;
|
|
goto end;
|
|
}
|
|
|
|
if (chan->ops->initiate_connection(chan) != IPC_OK) {
|
|
stdlib_log(LOG_ERR, "stonithd_signon: Can't initiate "
|
|
"connection to stonithd");
|
|
rc = ST_FAIL;
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
CLIENT_PID = getpid();
|
|
snprintf(CLIENT_PID_STR, sizeof(CLIENT_PID_STR), "%d", CLIENT_PID);
|
|
if ( client_name != NULL ) {
|
|
CLIENT_NAME = client_name;
|
|
} else {
|
|
CLIENT_NAME = CLIENT_PID_STR;
|
|
}
|
|
|
|
if ( (request = create_basic_reqmsg_fields(ST_SIGNON)) == NULL) {
|
|
rc = ST_FAIL;
|
|
goto end;
|
|
}
|
|
|
|
/* important error check client name length */
|
|
my_euid = geteuid();
|
|
my_egid = getegid();
|
|
if ( ( ha_msg_add_int(request, F_STONITHD_CEUID, my_euid) != HA_OK )
|
|
||( ha_msg_add_int(request, F_STONITHD_CEGID, my_egid) != HA_OK )
|
|
||( ha_msg_add(request, F_STONITHD_COOKIE, "") != HA_OK )
|
|
) {
|
|
stdlib_log(LOG_ERR, "stonithd_signon: "
|
|
"cannot add field to ha_msg.");
|
|
ZAPMSG(request);
|
|
rc = ST_FAIL;
|
|
goto end;
|
|
}
|
|
|
|
stdlib_log(LOG_DEBUG, "sending out the signon msg.");
|
|
/* Send the registration request message */
|
|
if (msg2ipcchan(request, chan) != HA_OK) {
|
|
ZAPMSG(request);
|
|
stdlib_log(LOG_ERR, "can't send signon message to IPC");
|
|
rc = ST_FAIL;
|
|
goto end;
|
|
}
|
|
|
|
/* waiting for the output to finish */
|
|
do {
|
|
rc_tmp= chan_waitout_timeout(chan, DEFAULT_TIMEOUT);
|
|
} while (rc_tmp == IPC_INTR);
|
|
|
|
ZAPMSG(request);
|
|
if (IPC_OK != rc_tmp) {
|
|
stdlib_log(LOG_ERR, "%s:%d: waitout failed."
|
|
, __FUNCTION__, __LINE__);
|
|
rc = ST_FAIL;
|
|
goto end;
|
|
}
|
|
|
|
/* Read the reply... */
|
|
if ( IPC_OK != chan_waitin_timeout(chan, DEFAULT_TIMEOUT) ) {
|
|
stdlib_log(LOG_ERR, "%s:%d: waitin failed."
|
|
, __FUNCTION__, __LINE__);
|
|
rc = ST_FAIL;
|
|
goto end;
|
|
}
|
|
|
|
if ( (reply = msgfromIPC_noauth(chan)) == NULL ) {
|
|
stdlib_log(LOG_ERR, "stonithd_signon: failed to fetch reply.");
|
|
rc = ST_FAIL;
|
|
goto end;
|
|
}
|
|
|
|
if ( TRUE == is_expected_msg(reply, F_STONITHD_TYPE, ST_APIRPL,
|
|
F_STONITHD_APIRPL, ST_RSIGNON, TRUE) ) {
|
|
if ( ((tmpstr=cl_get_string(reply, F_STONITHD_APIRET)) != NULL)
|
|
&& (STRNCMP_CONST(tmpstr, ST_APIOK) == 0) ) {
|
|
rc = ST_OK;
|
|
stdlib_log(LOG_DEBUG, "signed on to stonithd.");
|
|
/* get cookie if any */
|
|
if( cl_get_uuid(reply, F_STONITHD_COOKIE, &cookie) == HA_OK ) {
|
|
cptr = &cookie;
|
|
}
|
|
} else {
|
|
stdlib_log(LOG_WARNING, "failed to signon to the "
|
|
"stonithd.");
|
|
}
|
|
} else {
|
|
stdlib_log(LOG_ERR, "stonithd_signon: "
|
|
"Got an unexpected message.");
|
|
}
|
|
ZAPMSG(reply);
|
|
|
|
if (ST_OK != rc) { /* Something wrong when try to sign on to stonithd */
|
|
goto end;
|
|
}
|
|
|
|
/* Connect to the stonith deamon via callback channel */
|
|
wchanattrs = g_hash_table_new(g_str_hash, g_str_equal);
|
|
g_hash_table_insert(wchanattrs, path, cbsock);
|
|
cbchan = ipc_channel_constructor(IPC_ANYTYPE, wchanattrs);
|
|
g_hash_table_destroy(wchanattrs);
|
|
|
|
if (cbchan == NULL) {
|
|
stdlib_log(LOG_ERR, "stonithd_signon: Can't construct "
|
|
"callback channel to stonithd.");
|
|
rc = ST_FAIL;
|
|
goto end;
|
|
}
|
|
|
|
if (cbchan->ops->initiate_connection(cbchan) != IPC_OK) {
|
|
stdlib_log(LOG_ERR, "stonithd_signon: Can't initiate "
|
|
"connection with the callback channel");
|
|
rc = ST_FAIL;
|
|
goto end;
|
|
}
|
|
|
|
if ( (reply = msgfromIPC_noauth(cbchan)) == NULL ) {
|
|
stdlib_log(LOG_ERR, "%s:%d: failed to fetch reply via the "
|
|
" callback channel"
|
|
, __FUNCTION__, __LINE__);
|
|
rc = ST_FAIL;
|
|
goto end;
|
|
}
|
|
|
|
if ( TRUE == is_expected_msg(reply, F_STONITHD_TYPE, ST_APIRPL,
|
|
F_STONITHD_APIRPL, ST_RSIGNON, TRUE) ) {
|
|
tmpstr=cl_get_string(reply, F_STONITHD_APIRET);
|
|
if ( !STRNCMP_CONST(tmpstr, ST_APIOK) ) {
|
|
/*
|
|
* If the server directly authenticates us (probably
|
|
* via pid-auth), go ahead.
|
|
*/
|
|
stdlib_log(LOG_DEBUG, "%s:%d: Got a good signon reply "
|
|
"via the callback channel."
|
|
, __FUNCTION__, __LINE__);
|
|
} else if ( !STRNCMP_CONST(tmpstr, ST_COOKIE) ) {
|
|
/*
|
|
* If the server asks for a cookie to identify myself,
|
|
* initiate cookie authentication.
|
|
*/
|
|
if (cptr == NULL) {
|
|
stdlib_log(LOG_ERR, "server requested cookie auth on "
|
|
"the callback channel, but it didn't "
|
|
"provide the cookie on the main channel.");
|
|
rc = ST_FAIL;
|
|
} else {
|
|
rc = authenticate_with_cookie(cbchan, cptr);
|
|
}
|
|
} else {
|
|
/* Unknown response. */
|
|
rc = ST_FAIL;
|
|
stdlib_log(LOG_ERR, "%s:%d: Got a bad signon reply "
|
|
"via the callback channel."
|
|
, __FUNCTION__, __LINE__);
|
|
}
|
|
} else {
|
|
rc = ST_FAIL;
|
|
stdlib_log(LOG_ERR, "stonithd_signon: "
|
|
"Got an unexpected message via the callback chan.");
|
|
}
|
|
ZAPMSG(reply);
|
|
|
|
end:
|
|
if (ST_OK != rc) {
|
|
/* Something wrong when confirm via callback channel */
|
|
stonithd_signoff();
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
stonithd_signoff(void)
|
|
{
|
|
struct ha_msg * request;
|
|
gboolean connected = TRUE;
|
|
|
|
if (chan == NULL || chan->ch_status != IPC_CONNECT) {
|
|
connected = FALSE;
|
|
} else if (cbchan == NULL || cbchan->ch_status != IPC_CONNECT) {
|
|
connected = FALSE;
|
|
}
|
|
|
|
if (!connected) {
|
|
stdlib_log(LOG_NOTICE, "Not currently connected.");
|
|
goto bail;
|
|
}
|
|
|
|
if ( (request = create_basic_reqmsg_fields(ST_SIGNOFF)) == NULL) {
|
|
stdlib_log(LOG_ERR, "Couldn't create signoff message!");
|
|
goto bail;
|
|
}
|
|
|
|
/* Send the signoff request message */
|
|
if (msg2ipcchan(request, chan) != HA_OK) {
|
|
ZAPMSG(request);
|
|
stdlib_log(LOG_ERR, "Control channel dead - can't send signoff message");
|
|
goto bail;
|
|
}
|
|
|
|
/* waiting for the output to finish */
|
|
chan_waitout_timeout(chan, DEFAULT_TIMEOUT);
|
|
ZAPMSG(request);
|
|
|
|
bail:
|
|
if (NULL != chan) {
|
|
chan->ops->destroy(chan);
|
|
chan = NULL;
|
|
}
|
|
if (NULL != cbchan) {
|
|
cbchan->ops->destroy(cbchan);
|
|
cbchan = NULL;
|
|
}
|
|
|
|
return ST_OK;
|
|
}
|
|
|
|
IPC_Channel *
|
|
stonithd_input_IPC_channel(void)
|
|
{
|
|
if ( !signed_on(cbchan) ) {
|
|
stdlib_log(LOG_ERR, "stonithd_input_IPC_channel: not signed on");
|
|
return NULL;
|
|
} else {
|
|
return cbchan;
|
|
}
|
|
}
|
|
|
|
void
|
|
set_stonithd_input_IPC_channel_NULL(void)
|
|
{
|
|
cbchan = NULL;
|
|
}
|
|
|
|
int
|
|
stonithd_node_fence(stonith_ops_t * op)
|
|
{
|
|
int rc = ST_FAIL;
|
|
struct ha_msg * request, * reply;
|
|
|
|
if (op == NULL) {
|
|
stdlib_log(LOG_ERR, "stonithd_node_fence: op==NULL");
|
|
return ST_FAIL;
|
|
}
|
|
|
|
if ( !signed_on(chan) ) {
|
|
stdlib_log(LOG_NOTICE, "not signed on");
|
|
return ST_FAIL;
|
|
}
|
|
|
|
if ( (request = create_basic_reqmsg_fields(ST_STONITH)) == NULL) {
|
|
return ST_FAIL;
|
|
}
|
|
|
|
if ( (ha_msg_add_int(request, F_STONITHD_OPTYPE, op->optype) != HA_OK )
|
|
||(ha_msg_add(request, F_STONITHD_NODE, op->node_name ) != HA_OK)
|
|
||( op->node_uuid == NULL
|
|
|| ha_msg_add(request, F_STONITHD_NODE_UUID
|
|
, op->node_uuid) != HA_OK )
|
|
||(ha_msg_add_int(request, F_STONITHD_TIMEOUT, op->timeout) != HA_OK) ) {
|
|
stdlib_log(LOG_ERR, "stonithd_node_fence: "
|
|
"cannot add field to ha_msg.");
|
|
ZAPMSG(request);
|
|
return ST_FAIL;
|
|
}
|
|
if (op->private_data != NULL) {
|
|
if ( ha_msg_add(request, F_STONITHD_PDATA, op->private_data) != HA_OK) {
|
|
stdlib_log(LOG_ERR, "stonithd_node_fence: "
|
|
"Failed to add F_STONITHD_PDATA field to ha_msg.");
|
|
ZAPMSG(request);
|
|
return ST_FAIL;
|
|
}
|
|
}
|
|
|
|
/* Send the stonith request message */
|
|
if (msg2ipcchan(request, chan) != HA_OK) {
|
|
ZAPMSG(request);
|
|
stdlib_log(LOG_ERR
|
|
, "failed to send stonith request to the stonithd");
|
|
return ST_FAIL;
|
|
}
|
|
|
|
/* waiting for the output to finish */
|
|
chan_waitout_timeout(chan, DEFAULT_TIMEOUT);
|
|
ZAPMSG(request);
|
|
|
|
/* Read the reply... */
|
|
stdlib_log(LOG_DEBUG, "waiting for the stonith reply msg.");
|
|
if ( IPC_OK != chan_waitin_timeout(chan, DEFAULT_TIMEOUT) ) {
|
|
stdlib_log(LOG_ERR, "%s:%d: waitin failed."
|
|
, __FUNCTION__, __LINE__);
|
|
return ST_FAIL;
|
|
}
|
|
|
|
if ( (reply = msgfromIPC_noauth(chan)) == NULL ) {
|
|
stdlib_log(LOG_ERR, "stonithd_node_fence: fail to fetch reply");
|
|
return ST_FAIL;
|
|
}
|
|
|
|
if ( TRUE == is_expected_msg(reply, F_STONITHD_TYPE, ST_APIRPL,
|
|
F_STONITHD_APIRPL, ST_RSTONITH, TRUE) ) {
|
|
if( !STRNCMP_CONST(
|
|
cl_get_string(reply,F_STONITHD_APIRET), ST_APIOK) ) {
|
|
rc = ST_OK;
|
|
stdlib_log(LOG_DEBUG, "%s:%d: %s"
|
|
, __FUNCTION__, __LINE__
|
|
, "Stonithd's synchronous answer is ST_APIOK");
|
|
} else {
|
|
stdlib_log(LOG_ERR, "%s:%d: %s"
|
|
, __FUNCTION__, __LINE__
|
|
, "Stonithd's synchronous answer is ST_APIFAIL");
|
|
}
|
|
} else {
|
|
stdlib_log(LOG_ERR, "stonithd_node_fence: "
|
|
"Got an unexpected message.");
|
|
/* Need to handle in other way? */
|
|
}
|
|
|
|
ZAPMSG(reply);
|
|
return rc;
|
|
}
|
|
|
|
gboolean
|
|
stonithd_op_result_ready(void)
|
|
{
|
|
if ( !signed_on(cbchan) ) {
|
|
stdlib_log(LOG_ERR, "stonithd_op_result_ready: "
|
|
"not signed on");
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Regards IPC_DISCONNECT as a special result, so to prevent the caller
|
|
* from the possible endless waiting. That can be caused by the way
|
|
* in which the caller uses it.
|
|
*/
|
|
return (cbchan->ops->is_message_pending(cbchan)
|
|
|| cbchan->ch_status == IPC_DISCONNECT);
|
|
}
|
|
|
|
int
|
|
stonithd_receive_ops_result(gboolean blocking)
|
|
{
|
|
struct ha_msg* reply = NULL;
|
|
const char *reply_type;
|
|
int rc = ST_OK;
|
|
|
|
stdlib_log(LOG_DEBUG, "stonithd_receive_ops_result: begin");
|
|
|
|
/* If there is no msg ready and none blocking mode, then return */
|
|
if ((stonithd_op_result_ready() == FALSE) && (blocking == FALSE)) {
|
|
stdlib_log(LOG_DEBUG, "stonithd_receive_ops_result: "
|
|
"no result available.");
|
|
return ST_OK;
|
|
}
|
|
|
|
if (stonithd_op_result_ready() == FALSE) {
|
|
/* at that time, blocking must be TRUE */
|
|
if (IPC_OK != cbchan->ops->waitin(cbchan)) {
|
|
return ST_FAIL;
|
|
}
|
|
}
|
|
|
|
reply = msgfromIPC_noauth(cbchan);
|
|
reply_type = cl_get_string(reply, F_STONITHD_APIRPL);
|
|
if ( !is_expected_msg(reply, F_STONITHD_TYPE, ST_APIRPL,
|
|
F_STONITHD_APIRPL, reply_type, TRUE)) {
|
|
ZAPMSG(reply);
|
|
stdlib_log(LOG_DEBUG, "%s:%d: "
|
|
"got an unexpected message", __FUNCTION__, __LINE__);
|
|
return ST_FAIL;
|
|
}
|
|
if( !strcmp(reply_type, ST_STRET) ) {
|
|
stonith_ops_t *st_op = NULL;
|
|
/* handle the stonith op result message */
|
|
if( !(st_op = g_new(stonith_ops_t, 1)) ) {
|
|
stdlib_log(LOG_ERR, "out of memory");
|
|
return ST_FAIL;
|
|
}
|
|
st_op->node_uuid = NULL;
|
|
st_op->private_data = NULL;
|
|
|
|
st_get_int_value(reply, F_STONITHD_OPTYPE, (int*)&st_op->optype);
|
|
st_save_string(reply, F_STONITHD_NODE, st_op->node_name);
|
|
st_save_string(reply, F_STONITHD_NODE_UUID, st_op->node_uuid);
|
|
st_get_int_value(reply, F_STONITHD_TIMEOUT, &st_op->timeout);
|
|
st_get_int_value(reply, F_STONITHD_CALLID, &st_op->call_id);
|
|
st_get_int_value(reply, F_STONITHD_FRC, (int*)&st_op->op_result);
|
|
st_save_string(reply, F_STONITHD_NLIST, st_op->node_list);
|
|
st_save_string(reply, F_STONITHD_PDATA, st_op->private_data);
|
|
|
|
if (stonith_ops_cb != NULL) {
|
|
stonith_ops_cb(st_op);
|
|
}
|
|
|
|
free_stonith_ops_t(st_op);
|
|
}
|
|
else if( !strcmp(reply_type, ST_RAOPRET) ) {
|
|
stonithRA_ops_t *ra_op = NULL;
|
|
/* handle the stonithRA op result message */
|
|
if( !(ra_op = g_new(stonithRA_ops_t, 1)) ) {
|
|
stdlib_log(LOG_ERR, "out of memory");
|
|
return ST_FAIL;
|
|
}
|
|
|
|
st_save_string(reply, F_STONITHD_RSCID, ra_op->rsc_id);
|
|
st_save_string(reply, F_STONITHD_RAOPTYPE, ra_op->op_type);
|
|
st_save_string(reply, F_STONITHD_RANAME, ra_op->ra_name);
|
|
st_get_hashtable(reply, F_STONITHD_PARAMS, ra_op->params);
|
|
st_get_int_value(reply, F_STONITHD_CALLID, &ra_op->call_id);
|
|
st_get_int_value(reply, F_STONITHD_FRC, &ra_op->op_result);
|
|
|
|
/* if ( rc == ST_OK && stonithRA_ops_cb != NULL) */
|
|
if ( stonithRA_ops_cb ) {
|
|
stonithRA_ops_cb(ra_op, stonithRA_ops_cb_private_data);
|
|
}
|
|
|
|
free_stonithRA_ops_t(ra_op);
|
|
}
|
|
else {
|
|
stdlib_log(LOG_DEBUG, "%s:%d: "
|
|
"got an unexpected message", __FUNCTION__, __LINE__);
|
|
rc = ST_FAIL;
|
|
}
|
|
ZAPMSG(reply);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
stonithd_set_stonith_ops_callback(stonith_ops_callback_t callback)
|
|
{
|
|
if ( !signed_on(cbchan) ) {
|
|
stdlib_log(LOG_ERR, "stonithd_set_stonith_ops_callback: "
|
|
"not signed on");
|
|
return ST_FAIL;
|
|
}
|
|
stonith_ops_cb = callback;
|
|
return ST_OK;
|
|
}
|
|
|
|
int
|
|
stonithd_virtual_stonithRA_ops( stonithRA_ops_t * op, int * call_id)
|
|
{
|
|
int rc = ST_FAIL;
|
|
struct ha_msg * request, * reply;
|
|
const char * tmpstr;
|
|
|
|
if (op == NULL) {
|
|
stdlib_log(LOG_ERR, "stonithd_virtual_stonithRA_ops: op==NULL");
|
|
return ST_FAIL;
|
|
}
|
|
|
|
if (call_id == NULL) {
|
|
stdlib_log(LOG_ERR, "stonithd_stonithd_stonithRA_ops: "
|
|
"call_id==NULL");
|
|
return ST_FAIL;
|
|
}
|
|
|
|
if ( !signed_on(chan) ) {
|
|
stdlib_log(LOG_ERR, "not signed on");
|
|
return ST_FAIL;
|
|
}
|
|
|
|
if ( (request = create_basic_reqmsg_fields(ST_RAOP)) == NULL) {
|
|
return ST_FAIL;
|
|
}
|
|
|
|
if ( (ha_msg_add(request, F_STONITHD_RSCID, op->rsc_id) != HA_OK)
|
|
||(ha_msg_add(request, F_STONITHD_RAOPTYPE, op->op_type) != HA_OK)
|
|
||(ha_msg_add(request, F_STONITHD_RANAME, op->ra_name) != HA_OK)
|
|
||(ha_msg_addhash(request, F_STONITHD_PARAMS, op->params) != HA_OK)
|
|
) {
|
|
stdlib_log(LOG_ERR, "stonithd_virtual_stonithRA_ops: "
|
|
"cannot add field to ha_msg.");
|
|
ZAPMSG(request);
|
|
return ST_FAIL;
|
|
}
|
|
|
|
/* Send the request message */
|
|
if (msg2ipcchan(request, chan) != HA_OK) {
|
|
ZAPMSG(request);
|
|
stdlib_log(LOG_ERR, "can't send stonithRA message to IPC");
|
|
return ST_FAIL;
|
|
}
|
|
|
|
/* waiting for the output to finish */
|
|
chan_waitout_timeout(chan, DEFAULT_TIMEOUT);
|
|
ZAPMSG(request);
|
|
|
|
/* Read the reply... */
|
|
stdlib_log(LOG_DEBUG, "waiting for the stonithRA reply msg.");
|
|
if ( IPC_OK != chan_waitin_timeout(chan, DEFAULT_TIMEOUT) ) {
|
|
stdlib_log(LOG_ERR, "%s:%d: waitin failed."
|
|
, __FUNCTION__, __LINE__);
|
|
return ST_FAIL;
|
|
}
|
|
|
|
if ( (reply = msgfromIPC_noauth(chan)) == NULL ) {
|
|
stdlib_log(LOG_ERR, "stonithd_virtual_stonithRA_ops: "
|
|
"failed to fetch reply");
|
|
return ST_FAIL;
|
|
}
|
|
|
|
if ( FALSE == is_expected_msg(reply, F_STONITHD_TYPE, ST_APIRPL,
|
|
F_STONITHD_APIRPL, ST_RRAOP, TRUE) ) {
|
|
ZAPMSG(reply); /* avoid to zap the msg ? */
|
|
stdlib_log(LOG_WARNING, "stonithd_virtual_stonithRA_ops: "
|
|
"got an unexpected message");
|
|
return ST_FAIL;
|
|
}
|
|
|
|
if ( ((tmpstr = cl_get_string(reply, F_STONITHD_APIRET)) != NULL)
|
|
&& (STRNCMP_CONST(tmpstr, ST_APIOK) == 0) ) {
|
|
int tmpint;
|
|
|
|
if ( ha_msg_value_int(reply, F_STONITHD_CALLID, &tmpint)
|
|
== HA_OK ) {
|
|
*call_id = tmpint;
|
|
rc = ST_OK;
|
|
stdlib_log(LOG_DEBUG, "a stonith RA operation queue " \
|
|
"to run, call_id=%d.", *call_id);
|
|
} else {
|
|
stdlib_log(LOG_ERR, "no return call_id in reply");
|
|
rc = ST_FAIL;
|
|
}
|
|
} else {
|
|
stdlib_log(LOG_WARNING, "failed to do the RA op.");
|
|
rc = ST_FAIL;
|
|
* call_id = -1;
|
|
}
|
|
|
|
ZAPMSG(reply);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
stonithd_set_stonithRA_ops_callback(stonithRA_ops_callback_t callback,
|
|
void * private_data)
|
|
{
|
|
if ( !signed_on(cbchan) ) {
|
|
stdlib_log(LOG_ERR, "stonithd_set_stonithRA_ops_callback: "
|
|
"not signed on");
|
|
return ST_FAIL;
|
|
}
|
|
stonithRA_ops_cb = callback;
|
|
stonithRA_ops_cb_private_data = private_data;
|
|
return ST_OK;
|
|
}
|
|
|
|
int stonithd_list_stonith_types(GList ** types)
|
|
{
|
|
int rc = ST_FAIL;
|
|
struct ha_msg * request, * reply;
|
|
const char * tmpstr;
|
|
|
|
if ( !signed_on(chan) ) {
|
|
stdlib_log(LOG_ERR, "not signed on");
|
|
return ST_FAIL;
|
|
}
|
|
|
|
if ( (request = create_basic_reqmsg_fields(ST_LTYPES)) == NULL) {
|
|
return ST_FAIL;
|
|
}
|
|
|
|
/* Send the request message */
|
|
if (msg2ipcchan(request, chan) != HA_OK) {
|
|
ZAPMSG(request);
|
|
stdlib_log(LOG_ERR, "can't send stonithRA message to IPC");
|
|
return ST_FAIL;
|
|
}
|
|
|
|
/* waiting for the output to finish */
|
|
chan_waitout_timeout(chan, DEFAULT_TIMEOUT);
|
|
ZAPMSG(request);
|
|
|
|
/* Read the reply... */
|
|
stdlib_log(LOG_DEBUG, "waiting for the reply to list stonith types.");
|
|
if ( IPC_OK != chan_waitin_timeout(chan, DEFAULT_TIMEOUT) ) {
|
|
stdlib_log(LOG_ERR, "%s:%d: chan_waitin failed."
|
|
, __FUNCTION__, __LINE__);
|
|
return ST_FAIL;
|
|
}
|
|
|
|
if ( (reply = msgfromIPC_noauth(chan)) == NULL ) {
|
|
stdlib_log(LOG_ERR, "stonithd_list_stonith_types: "
|
|
"failed to fetch reply.");
|
|
return ST_FAIL;
|
|
}
|
|
|
|
*types = NULL;
|
|
if ( TRUE == is_expected_msg(reply, F_STONITHD_TYPE, ST_APIRPL,
|
|
F_STONITHD_APIRPL, ST_RLTYPES, TRUE) ) {
|
|
if ( ((tmpstr = cl_get_string(reply, F_STONITHD_APIRET)) != NULL)
|
|
&& (STRNCMP_CONST(tmpstr, ST_APIOK) == 0) ) {
|
|
int i;
|
|
int len=cl_msg_list_length(reply, F_STONITHD_STTYPES);
|
|
if ( len < 0 ) {
|
|
stdlib_log(LOG_ERR, "Not field to list stonith "
|
|
"types.");
|
|
} else {
|
|
for (i = 0; i < len; i++) {
|
|
tmpstr = cl_msg_list_nth_data(reply,
|
|
F_STONITHD_STTYPES, i);
|
|
if( tmpstr ) {
|
|
*types = g_list_append(*types,
|
|
g_strdup(tmpstr));
|
|
}
|
|
}
|
|
stdlib_log(LOG_DEBUG, "got stonith types.");
|
|
rc = ST_OK;
|
|
}
|
|
} else {
|
|
stdlib_log(LOG_DEBUG, "failed to get stonith types.");
|
|
}
|
|
} else {
|
|
stdlib_log(LOG_DEBUG, "stonithd_list_stonith_types: "
|
|
"Got an unexpected message.");
|
|
}
|
|
|
|
ZAPMSG(reply);
|
|
return rc;
|
|
}
|
|
|
|
static struct ha_msg *
|
|
create_basic_reqmsg_fields(const char * apitype)
|
|
{
|
|
struct ha_msg * msg = NULL;
|
|
|
|
if ((msg = ha_msg_new(4)) == NULL) {
|
|
stdlib_log(LOG_ERR, "create_basic_msg_fields:out of memory.");
|
|
return NULL;
|
|
}
|
|
|
|
/* important error check client name length */
|
|
if ( (ha_msg_add(msg, F_STONITHD_TYPE, ST_APIREQ ) != HA_OK )
|
|
||( ha_msg_add(msg, F_STONITHD_APIREQ, apitype) != HA_OK)
|
|
||( ha_msg_add(msg, F_STONITHD_CNAME, CLIENT_NAME) != HA_OK)
|
|
||( ha_msg_add_int(msg, F_STONITHD_CPID, CLIENT_PID) != HA_OK)
|
|
) {
|
|
stdlib_log(LOG_ERR, "create_basic_msg_fields: "
|
|
"cannot add field to ha_msg.");
|
|
ZAPMSG(msg);
|
|
}
|
|
|
|
return msg;
|
|
}
|
|
|
|
static gboolean
|
|
cmp_field(const struct ha_msg * msg,
|
|
const char * field_name, const char * field_content,
|
|
gboolean mandatory)
|
|
{
|
|
const char * tmpstr;
|
|
|
|
tmpstr = cl_get_string(msg, field_name);
|
|
if ( tmpstr && strncmp(tmpstr, field_content, 80) == 0 ) {
|
|
return TRUE;
|
|
} else {
|
|
stdlib_log(mandatory ? LOG_ERR : LOG_NOTICE
|
|
, "field <%s> content is "
|
|
" <%s>, expected content is: <%s>"
|
|
, field_name
|
|
, (NULL == tmpstr) ? "NULL" : tmpstr
|
|
, field_content);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
is_expected_msg(const struct ha_msg * msg,
|
|
const char * field_name1, const char * field_content1,
|
|
const char * field_name2, const char * field_content2,
|
|
gboolean mandatory)
|
|
{
|
|
if ( msg == NULL ) {
|
|
stdlib_log(LOG_ERR, "%s:%d: null message",
|
|
__FUNCTION__, __LINE__);
|
|
return FALSE;
|
|
}
|
|
return cmp_field(msg, field_name1, field_content1, mandatory)
|
|
&& cmp_field(msg, field_name2, field_content2, mandatory);
|
|
}
|
|
|
|
static void
|
|
sigalarm_handler(int signum)
|
|
{
|
|
if ( signum == SIGALRM ) {
|
|
INT_BY_ALARM = TRUE;
|
|
}
|
|
}
|
|
|
|
static int
|
|
chan_wait_timeout(IPC_Channel * chan,
|
|
int (*waitfun)(IPC_Channel * chan), unsigned int timeout)
|
|
{
|
|
int ret = IPC_FAIL;
|
|
unsigned int remaining;
|
|
struct sigaction old_action;
|
|
|
|
remaining = alarm(0);
|
|
if ( remaining > 0 ) {
|
|
alarm(remaining);
|
|
stdlib_log(LOG_NOTICE, "%s:%d: others "
|
|
"using alarm, can't set timeout",
|
|
__FUNCTION__, __LINE__);
|
|
ret = waitfun(chan);
|
|
} else {
|
|
memset(&old_action, 0, sizeof(old_action));
|
|
cl_signal_set_simple_handler(SIGALRM, sigalarm_handler
|
|
, &old_action);
|
|
|
|
INT_BY_ALARM = FALSE;
|
|
remaining = timeout;
|
|
|
|
while ( remaining > 0 ) {
|
|
alarm(remaining);
|
|
ret = waitfun(chan);
|
|
if ( ret == IPC_INTR ) {
|
|
if ( TRUE == INT_BY_ALARM ) {
|
|
stdlib_log(LOG_ERR, "%s:%d: timed out"
|
|
, __FUNCTION__, __LINE__);
|
|
ret = IPC_FAIL;
|
|
break;
|
|
} else {
|
|
stdlib_log(LOG_NOTICE, "%s:%d: interrupted"
|
|
, __FUNCTION__, __LINE__);
|
|
remaining = alarm(0);
|
|
}
|
|
} else {
|
|
alarm(0);
|
|
break;
|
|
}
|
|
}
|
|
cl_signal_set_simple_handler(SIGALRM, old_action.sa_handler
|
|
, &old_action);
|
|
if( ret != IPC_OK ) {
|
|
stdlib_log(LOG_DEBUG, "%s:%d: ret=%d"
|
|
, __FUNCTION__, __LINE__, ret);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
chan_waitin_timeout(IPC_Channel * chan, unsigned int timeout)
|
|
{
|
|
return chan_wait_timeout(chan,chan->ops->waitin,timeout);
|
|
}
|
|
|
|
static int
|
|
chan_waitout_timeout(IPC_Channel * chan, unsigned int timeout)
|
|
{
|
|
return chan_wait_timeout(chan,chan->ops->waitout,timeout);
|
|
}
|
|
|
|
void stdlib_enable_debug_mode(void)
|
|
{
|
|
DEBUG_MODE = TRUE;
|
|
}
|
|
|
|
static void
|
|
free_stonithRA_ops_t(stonithRA_ops_t * ra_op)
|
|
{
|
|
ZAPGDOBJ(ra_op->rsc_id);
|
|
ZAPGDOBJ(ra_op->ra_name);
|
|
ZAPGDOBJ(ra_op->op_type);
|
|
/* Has used g_hash_table_new_full to create params */
|
|
g_hash_table_destroy(ra_op->params);
|
|
ZAPGDOBJ(ra_op);
|
|
}
|
|
|
|
static void
|
|
free_stonith_ops_t(stonith_ops_t * st_op)
|
|
{
|
|
if (st_op == NULL) {
|
|
stdlib_log(LOG_DEBUG, "free_stonith_ops_t: st_op==NULL");
|
|
return;
|
|
}
|
|
|
|
ZAPGDOBJ(st_op->node_name);
|
|
ZAPGDOBJ(st_op->node_list);
|
|
ZAPGDOBJ(st_op->node_uuid);
|
|
ZAPGDOBJ(st_op->private_data);
|
|
ZAPGDOBJ(st_op);
|
|
}
|