nfv/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_msg.c
Jack Ding 7a2aa8aaa2 vm_voting on migration action are sometimes not getting rejected
Due to race conditions, multiple messages might be received from a
single read by guestServer. guestServer in this case would only
handle the first message and discard the remaining ones.

In this particular issue, guestServer received a heartbeat challenge
response message and a vote notification response (reject) message
from a single read, and the latter message was discarded.

This fix re-writes message handler for virtio serial channel to
handle segmented and multiple messages, and removed any newline
characters in client log message.

It contains fix for both host and client, so both host load and
guest image need to be updated to test the fix.

Change-Id: I50ed2236ad24b61a79dce7773ace5b4b6ee33f28
Signed-off-by: Al Bailey <Al.Bailey@windriver.com>
2018-06-19 10:08:07 -05:00

1213 lines
46 KiB
C
Executable File

/*
* Copyright (c) 2013-2018, Wind River Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1) Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2) Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3) Neither the name of Wind River Systems nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "guest_heartbeat_msg_defs.h"
#include "guest_heartbeat_msg.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/eventfd.h>
#include <json-c/json.h>
#include "guest_limits.h"
#include "guest_types.h"
#include "guest_debug.h"
#include "guest_selobj.h"
#include "guest_channel.h"
#include "guest_signal.h"
#include "guest_utils.h"
#include "guest_heartbeat_types.h"
#define GUEST_HEARTBEAT_PROPAGATION_DELAY_IN_SECS 1
#define GUEST_HEARTBEAT_CHALLENGE_DEPTH 6
static int _signal_fd = -1;
static int _challenge_depth;
static uint32_t _last_tx_challenge[GUEST_HEARTBEAT_CHALLENGE_DEPTH];
static uint32_t _last_rx_challenge;
static uint32_t _msg_sequence;
static GuestChannelIdT _channel_id = GUEST_CHANNEL_ID_INVALID;
static GuestHeartbeatMsgCallbacksT _callbacks;
// Tokener serves as reassembly buffer for host connection.
static struct json_tokener* tok;
// ****************************************************************************
// Guest Heartbeat Message - Action (Host to Network)
// ==================================================
static const char *guest_heartbeat_msg_action_hton(
GuestHeartbeatActionT action )
{
switch (action)
{
case GUEST_HEARTBEAT_ACTION_NONE:
return GUEST_HEARTBEAT_MSG_ACTION_NONE;
case GUEST_HEARTBEAT_ACTION_REBOOT:
return GUEST_HEARTBEAT_MSG_ACTION_REBOOT;
case GUEST_HEARTBEAT_ACTION_STOP:
return GUEST_HEARTBEAT_MSG_ACTION_STOP;
case GUEST_HEARTBEAT_ACTION_LOG:
return GUEST_HEARTBEAT_MSG_ACTION_LOG;
default:
DPRINTFE("Unknown action %i.", action);
return GUEST_HEARTBEAT_MSG_ACTION_UNKNOWN;
}
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Action (Network to Host)
// ==================================================
static GuestHeartbeatActionT guest_heartbeat_msg_action_ntoh(
const char *action )
{
if (!strcmp(action, GUEST_HEARTBEAT_MSG_ACTION_REBOOT))
{
return GUEST_HEARTBEAT_ACTION_REBOOT;
}
else if (!strcmp(action, GUEST_HEARTBEAT_MSG_ACTION_STOP)) {
return GUEST_HEARTBEAT_ACTION_STOP;
}
else if (!strcmp(action, GUEST_HEARTBEAT_MSG_ACTION_LOG)) {
return GUEST_HEARTBEAT_ACTION_LOG;
}
else {
DPRINTFE("Unknown action %i.", action);
return GUEST_HEARTBEAT_ACTION_UNKNOWN;
}
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Event (Host to Network)
// ==================================================
static const char *guest_heartbeat_msg_event_hton(
GuestHeartbeatEventT event )
{
switch (event)
{
case GUEST_HEARTBEAT_EVENT_STOP:
return GUEST_HEARTBEAT_MSG_EVENT_STOP;
case GUEST_HEARTBEAT_EVENT_REBOOT:
return GUEST_HEARTBEAT_MSG_EVENT_REBOOT;
case GUEST_HEARTBEAT_EVENT_SUSPEND:
return GUEST_HEARTBEAT_MSG_EVENT_SUSPEND;
case GUEST_HEARTBEAT_EVENT_PAUSE:
return GUEST_HEARTBEAT_MSG_EVENT_PAUSE;
case GUEST_HEARTBEAT_EVENT_UNPAUSE:
return GUEST_HEARTBEAT_MSG_EVENT_UNPAUSE;
case GUEST_HEARTBEAT_EVENT_RESUME:
return GUEST_HEARTBEAT_MSG_EVENT_RESUME;
case GUEST_HEARTBEAT_EVENT_RESIZE_BEGIN:
return GUEST_HEARTBEAT_MSG_EVENT_RESIZE_BEGIN;
case GUEST_HEARTBEAT_EVENT_RESIZE_END:
return GUEST_HEARTBEAT_MSG_EVENT_RESIZE_END;
case GUEST_HEARTBEAT_EVENT_LIVE_MIGRATE_BEGIN:
return GUEST_HEARTBEAT_MSG_EVENT_LIVE_MIGRATE_BEGIN;
case GUEST_HEARTBEAT_EVENT_LIVE_MIGRATE_END:
return GUEST_HEARTBEAT_MSG_EVENT_LIVE_MIGRATE_END;
case GUEST_HEARTBEAT_EVENT_COLD_MIGRATE_BEGIN:
return GUEST_HEARTBEAT_MSG_EVENT_COLD_MIGRATE_BEGIN;
case GUEST_HEARTBEAT_EVENT_COLD_MIGRATE_END:
return GUEST_HEARTBEAT_MSG_EVENT_COLD_MIGRATE_END;
default:
DPRINTFE("Unknown event %i.", event);
return GUEST_HEARTBEAT_MSG_EVENT_UNKNOWN;
}
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Event (Network to Host)
// =================================================
static GuestHeartbeatEventT guest_heartbeat_msg_event_ntoh(
const char *event )
{
if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_STOP))
{
return GUEST_HEARTBEAT_EVENT_STOP;
}
else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_REBOOT)) {
return GUEST_HEARTBEAT_EVENT_REBOOT;
}
else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_SUSPEND)) {
return GUEST_HEARTBEAT_EVENT_SUSPEND;
}
else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_PAUSE)) {
return GUEST_HEARTBEAT_EVENT_PAUSE;
}
else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_UNPAUSE)) {
return GUEST_HEARTBEAT_EVENT_UNPAUSE;
}
else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_RESUME)) {
return GUEST_HEARTBEAT_EVENT_RESUME;
}
else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_RESIZE_BEGIN)) {
return GUEST_HEARTBEAT_EVENT_RESIZE_BEGIN;
}
else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_RESIZE_END)) {
return GUEST_HEARTBEAT_EVENT_RESIZE_END;
}
else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_LIVE_MIGRATE_BEGIN)) {
return GUEST_HEARTBEAT_EVENT_LIVE_MIGRATE_BEGIN;
}
else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_LIVE_MIGRATE_END)) {
return GUEST_HEARTBEAT_EVENT_LIVE_MIGRATE_END;
}
else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_COLD_MIGRATE_BEGIN)) {
return GUEST_HEARTBEAT_EVENT_COLD_MIGRATE_BEGIN;
}
else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_COLD_MIGRATE_END)) {
return GUEST_HEARTBEAT_EVENT_COLD_MIGRATE_END;
}
else {
DPRINTFE("Unknown event %i.", event);
return GUEST_HEARTBEAT_EVENT_UNKNOWN;
}
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Notify (Host to Network)
// ==================================================
static const char *guest_heartbeat_msg_notify_hton(
GuestHeartbeatNotifyT notify )
{
switch (notify)
{
case GUEST_HEARTBEAT_NOTIFY_REVOCABLE:
return GUEST_HEARTBEAT_MSG_NOTIFY_REVOCABLE;
case GUEST_HEARTBEAT_NOTIFY_IRREVOCABLE:
return GUEST_HEARTBEAT_MSG_NOTIFY_IRREVOCABLE;
default:
DPRINTFE("Unknown notify %i.", notify);
return GUEST_HEARTBEAT_MSG_NOTIFY_UNKNOWN;
}
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Notify (Network to Host)
// ==================================================
static GuestHeartbeatNotifyT guest_heartbeat_msg_notify_ntoh(
const char *notify )
{
if (!strcmp(notify, GUEST_HEARTBEAT_MSG_NOTIFY_REVOCABLE))
{
return GUEST_HEARTBEAT_NOTIFY_REVOCABLE;
}
else if (!strcmp(notify, GUEST_HEARTBEAT_MSG_NOTIFY_IRREVOCABLE)) {
return GUEST_HEARTBEAT_NOTIFY_IRREVOCABLE;
}
else {
DPRINTFE("Unknown notify %i.", notify);
return GUEST_HEARTBEAT_NOTIFY_UNKNOWN;
}
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Vote Result (Host to Network)
// =======================================================
static const char * guest_heartbeat_msg_vote_result_hton(
GuestHeartbeatVoteResultT vote_result )
{
switch (vote_result)
{
case GUEST_HEARTBEAT_VOTE_RESULT_ACCEPT:
return GUEST_HEARTBEAT_MSG_VOTE_RESULT_ACCEPT;
case GUEST_HEARTBEAT_VOTE_RESULT_REJECT:
return GUEST_HEARTBEAT_MSG_VOTE_RESULT_REJECT;
case GUEST_HEARTBEAT_VOTE_RESULT_COMPLETE:
return GUEST_HEARTBEAT_MSG_VOTE_RESULT_COMPLETE;
case GUEST_HEARTBEAT_VOTE_RESULT_TIMEOUT:
return GUEST_HEARTBEAT_MSG_VOTE_RESULT_TIMEOUT;
case GUEST_HEARTBEAT_VOTE_RESULT_ERROR:
return GUEST_HEARTBEAT_MSG_VOTE_RESULT_ERROR;
default:
DPRINTFE("Unknown vote result %i.", vote_result);
return GUEST_HEARTBEAT_MSG_VOTE_RESULT_UNKNOWN;
}
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Vote Result (Network to Host)
// =======================================================
static GuestHeartbeatVoteResultT guest_heartbeat_msg_vote_result_ntoh(
const char *vote_result )
{
if (!strcmp(vote_result, GUEST_HEARTBEAT_MSG_VOTE_RESULT_ACCEPT))
{
return GUEST_HEARTBEAT_VOTE_RESULT_ACCEPT;
}
else if (!strcmp(vote_result, GUEST_HEARTBEAT_MSG_VOTE_RESULT_REJECT)) {
return GUEST_HEARTBEAT_VOTE_RESULT_REJECT;
}
else if (!strcmp(vote_result, GUEST_HEARTBEAT_MSG_VOTE_RESULT_COMPLETE)) {
return GUEST_HEARTBEAT_VOTE_RESULT_COMPLETE;
}
else if (!strcmp(vote_result, GUEST_HEARTBEAT_MSG_VOTE_RESULT_TIMEOUT)) {
return GUEST_HEARTBEAT_VOTE_RESULT_TIMEOUT;
}
else if (!strcmp(vote_result, GUEST_HEARTBEAT_MSG_VOTE_RESULT_ERROR)) {
return GUEST_HEARTBEAT_VOTE_RESULT_ERROR;
}
else {
DPRINTFE("Unknown vote result %i.", vote_result);
return GUEST_HEARTBEAT_VOTE_RESULT_UNKNOWN;
}
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Send Init
// ===================================
GuestErrorT guest_heartbeat_msg_send_init(
int invocation_id, GuestHeartbeatMsgInitDataT* data )
{
GuestErrorT error;
char msg[GUEST_HEARTBEAT_MSG_MAX_MSG_SIZE];
snprintf(msg, sizeof(msg), "\n{\"%s\":%d,\"%s\":%d,\"%s\":\"%s\",\"%s\":%d,"
"\"%s\":%d,\"%s\":\"%s\","
"\"%s\":%d,\"%s\":%d,\"%s\":%d,\"%s\":%d,\"%s\":%d,\"%s\":%d,"
"\"%s\":\"%s\"}\n",
GUEST_HEARTBEAT_MSG_VERSION, GUEST_HEARTBEAT_MSG_VERSION_CURRENT,
GUEST_HEARTBEAT_MSG_REVISION, GUEST_HEARTBEAT_MSG_REVISION_CURRENT,
GUEST_HEARTBEAT_MSG_MSG_TYPE, GUEST_HEARTBEAT_MSG_INIT,
GUEST_HEARTBEAT_MSG_SEQUENCE, ++_msg_sequence,
GUEST_HEARTBEAT_MSG_INVOCATION_ID, invocation_id,
GUEST_HEARTBEAT_MSG_NAME, data->name,
GUEST_HEARTBEAT_MSG_HEARTBEAT_INTERVAL_MS,
data->heartbeat_interval_ms,
GUEST_HEARTBEAT_MSG_VOTE_SECS,
data->vote_ms/1000 + GUEST_HEARTBEAT_PROPAGATION_DELAY_IN_SECS,
GUEST_HEARTBEAT_MSG_SHUTDOWN_NOTICE_SECS,
data->shutdown_notice_ms/1000 + GUEST_HEARTBEAT_PROPAGATION_DELAY_IN_SECS,
GUEST_HEARTBEAT_MSG_SUSPEND_NOTICE_SECS,
data->suspend_notice_ms/1000 + GUEST_HEARTBEAT_PROPAGATION_DELAY_IN_SECS,
GUEST_HEARTBEAT_MSG_RESUME_NOTICE_SECS,
data->resume_notice_ms/1000 + GUEST_HEARTBEAT_PROPAGATION_DELAY_IN_SECS,
GUEST_HEARTBEAT_MSG_RESTART_SECS,
data->restart_ms/1000 + GUEST_HEARTBEAT_PROPAGATION_DELAY_IN_SECS,
GUEST_HEARTBEAT_MSG_CORRECTIVE_ACTION,
guest_heartbeat_msg_action_hton(data->corrective_action));
error = guest_channel_send(_channel_id, msg, strlen(msg));
if (GUEST_OKAY != error)
{
DPRINTFE("Failed to send guest heartbeat init message, error=%s.",
guest_error_str(error));
return error;
}
DPRINTFI("Sent heartbeat init message, invocation_id=%i.", invocation_id);
DPRINTFD("Sent heartbeat init message: %s", msg);
return GUEST_OKAY;
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Send Init Ack
// =======================================
GuestErrorT guest_heartbeat_msg_send_init_ack( int invocation_id )
{
GuestErrorT error;
char msg[GUEST_HEARTBEAT_MSG_MAX_MSG_SIZE];
snprintf(msg, sizeof(msg), "\n{\"%s\":%d,\"%s\":%d,\"%s\":\"%s\",\"%s\":%d,"
"\"%s\":%d}\n",
GUEST_HEARTBEAT_MSG_VERSION, GUEST_HEARTBEAT_MSG_VERSION_CURRENT,
GUEST_HEARTBEAT_MSG_REVISION, GUEST_HEARTBEAT_MSG_REVISION_CURRENT,
GUEST_HEARTBEAT_MSG_MSG_TYPE, GUEST_HEARTBEAT_MSG_INIT_ACK,
GUEST_HEARTBEAT_MSG_SEQUENCE, ++_msg_sequence,
GUEST_HEARTBEAT_MSG_INVOCATION_ID, invocation_id);
error = guest_channel_send(_channel_id, msg, strlen(msg));
if (GUEST_OKAY != error)
{
DPRINTFE("Failed to send guest heartbeat init ack message, error=%s.",
guest_error_str(error));
return error;
}
DPRINTFI("Sent heartbeat init ack message: %s", msg);
return GUEST_OKAY;
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Send Exit
// ===================================
GuestErrorT guest_heartbeat_msg_send_exit( char log_msg[] )
{
GuestErrorT error;
char log_msg_buf[GUEST_HEARTBEAT_MSG_MAX_LOG_SIZE];
snprintf(log_msg_buf, GUEST_HEARTBEAT_MSG_MAX_LOG_SIZE, "%s",
guest_utils_remove_newline(log_msg));
char msg[GUEST_HEARTBEAT_MSG_MAX_MSG_SIZE];
snprintf(msg, sizeof(msg), "\n{\"%s\":%d,\"%s\":%d,\"%s\":\"%s\",\"%s\":%d,"
"\"%s\":\"%s\"}\n",
GUEST_HEARTBEAT_MSG_VERSION, GUEST_HEARTBEAT_MSG_VERSION_CURRENT,
GUEST_HEARTBEAT_MSG_REVISION, GUEST_HEARTBEAT_MSG_REVISION_CURRENT,
GUEST_HEARTBEAT_MSG_MSG_TYPE, GUEST_HEARTBEAT_MSG_EXIT,
GUEST_HEARTBEAT_MSG_SEQUENCE, ++_msg_sequence,
GUEST_HEARTBEAT_MSG_LOG_MSG, log_msg_buf);
error = guest_channel_send(_channel_id, msg, strlen(msg));
if (GUEST_OKAY != error)
{
DPRINTFE("Failed to send guest heartbeat exit message, error=%s.",
guest_error_str(error));
return error;
}
DPRINTFI("Sent heartbeat exit message: %s", msg);
return GUEST_OKAY;
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Send Challenge
// ========================================
GuestErrorT guest_heartbeat_msg_send_challenge( void )
{
GuestErrorT error;
++_challenge_depth;
if (GUEST_HEARTBEAT_CHALLENGE_DEPTH <= _challenge_depth)
_challenge_depth = 0;
_last_tx_challenge[_challenge_depth] = rand();
char msg[GUEST_HEARTBEAT_MSG_MAX_MSG_SIZE];
snprintf(msg, sizeof(msg), "\n{\"%s\":%d,\"%s\":%d,\"%s\":\"%s\",\"%s\":%d,"
"\"%s\":%d}\n",
GUEST_HEARTBEAT_MSG_VERSION, GUEST_HEARTBEAT_MSG_VERSION_CURRENT,
GUEST_HEARTBEAT_MSG_REVISION, GUEST_HEARTBEAT_MSG_REVISION_CURRENT,
GUEST_HEARTBEAT_MSG_MSG_TYPE, GUEST_HEARTBEAT_MSG_CHALLENGE,
GUEST_HEARTBEAT_MSG_SEQUENCE, ++_msg_sequence,
GUEST_HEARTBEAT_MSG_HEARTBEAT_CHALLENGE, _last_tx_challenge[_challenge_depth]);
error = guest_channel_send(_channel_id, msg, strlen(msg));
if (GUEST_OKAY != error)
{
DPRINTFE("Failed to send guest heartbeat challenge message, "
"error=%s.", guest_error_str(error));
return error;
}
DPRINTFD("Sent heartbeat challenge message, challenge=%i.",
_last_tx_challenge[_challenge_depth]);
return GUEST_OKAY;
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Send Challenge Response
// =================================================
GuestErrorT guest_heartbeat_msg_send_challenge_response(
bool health, GuestHeartbeatActionT corrective_action, char log_msg[] )
{
GuestErrorT error;
char log_msg_buf[GUEST_HEARTBEAT_MSG_MAX_LOG_SIZE];
snprintf(log_msg_buf, GUEST_HEARTBEAT_MSG_MAX_LOG_SIZE, "%s",
guest_utils_remove_newline(log_msg));
char msg[GUEST_HEARTBEAT_MSG_MAX_MSG_SIZE];
snprintf(msg, sizeof(msg), "\n{\"%s\":%d,\"%s\":%d,\"%s\":\"%s\",\"%s\":%d,"
"\"%s\":%d,\"%s\":\"%s\",\"%s\":\"%s\",\"%s\":\"%s\"}\n",
GUEST_HEARTBEAT_MSG_VERSION, GUEST_HEARTBEAT_MSG_VERSION_CURRENT,
GUEST_HEARTBEAT_MSG_REVISION, GUEST_HEARTBEAT_MSG_REVISION_CURRENT,
GUEST_HEARTBEAT_MSG_MSG_TYPE, GUEST_HEARTBEAT_MSG_CHALLENGE_RESPONSE,
GUEST_HEARTBEAT_MSG_SEQUENCE, ++_msg_sequence,
GUEST_HEARTBEAT_MSG_HEARTBEAT_RESPONSE, _last_rx_challenge,
GUEST_HEARTBEAT_MSG_HEARTBEAT_HEALTH,
health ? GUEST_HEARTBEAT_MSG_HEALTHY : GUEST_HEARTBEAT_MSG_UNHEALTHY,
GUEST_HEARTBEAT_MSG_CORRECTIVE_ACTION,
guest_heartbeat_msg_action_hton(corrective_action),
GUEST_HEARTBEAT_MSG_LOG_MSG, log_msg_buf);
error = guest_channel_send(_channel_id, msg, strlen(msg));
if (GUEST_OKAY != error)
{
DPRINTFE("Failed to send guest heartbeat challenge response message, "
"error=%s.", guest_error_str(error));
return error;
}
// print info logs with message content only if not healthy
if (!health)
{
DPRINTFI("Unhealthy, sent heartbeat challenge response message: %s", msg);
}
else {
DPRINTFD("Sent heartbeat challenge response message, challenge=%i.",
_last_rx_challenge);
}
return GUEST_OKAY;
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Send Action Notify
// ============================================
GuestErrorT guest_heartbeat_msg_send_action_notify(
int invocation_id, GuestHeartbeatEventT event,
GuestHeartbeatNotifyT notify, int timeout_ms )
{
GuestErrorT error;
char msg[GUEST_HEARTBEAT_MSG_MAX_MSG_SIZE];
snprintf(msg, sizeof(msg), "\n{\"%s\":%d,\"%s\":%d,\"%s\":\"%s\",\"%s\":%d,"
"\"%s\":%d,\"%s\":\"%s\",\"%s\":\"%s\",\"%s\":%d}\n",
GUEST_HEARTBEAT_MSG_VERSION, GUEST_HEARTBEAT_MSG_VERSION_CURRENT,
GUEST_HEARTBEAT_MSG_REVISION, GUEST_HEARTBEAT_MSG_REVISION_CURRENT,
GUEST_HEARTBEAT_MSG_MSG_TYPE, GUEST_HEARTBEAT_MSG_ACTION_NOTIFY,
GUEST_HEARTBEAT_MSG_SEQUENCE, ++_msg_sequence,
GUEST_HEARTBEAT_MSG_INVOCATION_ID, invocation_id,
GUEST_HEARTBEAT_MSG_EVENT_TYPE, guest_heartbeat_msg_event_hton(event),
GUEST_HEARTBEAT_MSG_NOTIFICATION_TYPE, guest_heartbeat_msg_notify_hton(notify),
GUEST_HEARTBEAT_MSG_TIMEOUT_MS, timeout_ms);
error = guest_channel_send(_channel_id, msg, strlen(msg));
if (GUEST_OKAY != error)
{
DPRINTFE("Failed to send guest heartbeat action notify message, "
"error=%s.", guest_error_str(error));
return error;
}
DPRINTFI("Sent heartbeat action notify message, invocation_id=%i.",
invocation_id);
DPRINTFD("Sent heartbeat action notify message: %s", msg);
return GUEST_OKAY;
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Send Action Response
// ==============================================
GuestErrorT guest_heartbeat_msg_send_action_response(
int invocation_id, GuestHeartbeatEventT event,
GuestHeartbeatNotifyT notify, GuestHeartbeatVoteResultT vote_result,
char log_msg[] )
{
GuestErrorT error;
char log_msg_buf[GUEST_HEARTBEAT_MSG_MAX_LOG_SIZE];
snprintf(log_msg_buf, GUEST_HEARTBEAT_MSG_MAX_LOG_SIZE, "%s",
guest_utils_remove_newline(log_msg));
char msg[GUEST_HEARTBEAT_MSG_MAX_MSG_SIZE];
snprintf(msg, sizeof(msg), "\n{\"%s\":%d,\"%s\":%d,\"%s\":\"%s\",\"%s\":%d,"
"\"%s\":%d,\"%s\":\"%s\",\"%s\":\"%s\",\"%s\":\"%s\",\"%s\":\"%s\"}\n",
GUEST_HEARTBEAT_MSG_VERSION, GUEST_HEARTBEAT_MSG_VERSION_CURRENT,
GUEST_HEARTBEAT_MSG_REVISION, GUEST_HEARTBEAT_MSG_REVISION_CURRENT,
GUEST_HEARTBEAT_MSG_MSG_TYPE, GUEST_HEARTBEAT_MSG_ACTION_RESPONSE,
GUEST_HEARTBEAT_MSG_SEQUENCE, ++_msg_sequence,
GUEST_HEARTBEAT_MSG_INVOCATION_ID, invocation_id,
GUEST_HEARTBEAT_MSG_EVENT_TYPE, guest_heartbeat_msg_event_hton(event),
GUEST_HEARTBEAT_MSG_NOTIFICATION_TYPE, guest_heartbeat_msg_notify_hton(notify),
GUEST_HEARTBEAT_MSG_VOTE_RESULT, guest_heartbeat_msg_vote_result_hton(vote_result),
GUEST_HEARTBEAT_MSG_LOG_MSG, log_msg_buf);
error = guest_channel_send(_channel_id, msg, strlen(msg));
if (GUEST_OKAY != error)
{
DPRINTFE("Failed to send guest heartbeat action response message, "
"error=%s.", guest_error_str(error));
return error;
}
DPRINTFI("Sent heartbeat action response message: %s", msg);
return GUEST_OKAY;
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Receive Init
// ======================================
static void guest_heartbeat_msg_recv_init( struct json_object *jobj_msg )
{
char name[GUEST_HEARTBEAT_MSG_MAX_NAME_SIZE];
uint32_t invocation_id;
char corrective_action[GUEST_HEARTBEAT_MSG_MAX_VALUE_SIZE];
GuestHeartbeatMsgInitDataT data;
uint32_t vote_secs, shutdown_notice_secs, suspend_notice_secs;
uint32_t resume_notice_secs, restart_secs;
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_INVOCATION_ID, &invocation_id))
return;
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_NAME, &name))
return;
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_HEARTBEAT_INTERVAL_MS, &data.heartbeat_interval_ms))
return;
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_VOTE_SECS, &vote_secs))
return;
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_SHUTDOWN_NOTICE_SECS, &shutdown_notice_secs))
return;
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_SUSPEND_NOTICE_SECS, &suspend_notice_secs))
return;
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_RESUME_NOTICE_SECS, &resume_notice_secs))
return;
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_RESTART_SECS, &restart_secs))
return;
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_CORRECTIVE_ACTION, &corrective_action))
return;
data.vote_ms = vote_secs*1000;
data.shutdown_notice_ms = shutdown_notice_secs*1000;
data.suspend_notice_ms = suspend_notice_secs*1000;
data.resume_notice_ms= resume_notice_secs*1000;
data.restart_ms = restart_secs*1000;
snprintf(data.name, GUEST_NAME_MAX_CHAR, "%s", name);
data.corrective_action = guest_heartbeat_msg_action_ntoh(corrective_action);
DPRINTFI("Heartbeat Init received, invocation_id=%i", invocation_id);
const char *msg = json_object_to_json_string_ext(jobj_msg, JSON_C_TO_STRING_PLAIN);
DPRINTFD("Heartbeat Init message received: %s", msg);
if (NULL != _callbacks.recv_init)
_callbacks.recv_init(invocation_id, &data);
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Receive Init Ack
// ==========================================
static void guest_heartbeat_msg_recv_init_ack( struct json_object *jobj_msg )
{
uint32_t invocation_id;
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_INVOCATION_ID, &invocation_id))
return;
DPRINTFI("Heartbeat Init Ack received, invocation_id=%i.",
invocation_id);
const char *msg = json_object_to_json_string_ext(jobj_msg, JSON_C_TO_STRING_PLAIN);
DPRINTFD("Heartbeat Init Ack message received: %s", msg);
if (NULL != _callbacks.recv_init_ack)
_callbacks.recv_init_ack(invocation_id);
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Receive Exit
// ======================================
static void guest_heartbeat_msg_recv_exit( struct json_object *jobj_msg )
{
char log_msg[GUEST_HEARTBEAT_MSG_MAX_LOG_SIZE];
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_LOG_MSG, &log_msg))
return;
DPRINTFI("Heartbeat Exit received, msg=%s.", log_msg);
const char *msg = json_object_to_json_string_ext(jobj_msg, JSON_C_TO_STRING_PLAIN);
DPRINTFD("Heartbeat Exit message received: %s", msg);
if (NULL != _callbacks.recv_exit)
_callbacks.recv_exit(log_msg);
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Receive Challenge
// ===========================================
static void guest_heartbeat_msg_recv_challenge( struct json_object *jobj_msg )
{
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_HEARTBEAT_CHALLENGE, &_last_rx_challenge))
return;
DPRINTFD("Heartbeat Challenge received, challenge=%i.", _last_rx_challenge);
if (NULL != _callbacks.recv_challenge)
_callbacks.recv_challenge();
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Receive Challenge Ack
// ===============================================
static void guest_heartbeat_msg_recv_challenge_ack( struct json_object *jobj_msg )
{
char health[GUEST_HEARTBEAT_MSG_MAX_VALUE_SIZE];
char corrective_action_str[GUEST_HEARTBEAT_MSG_MAX_VALUE_SIZE];
GuestHeartbeatActionT corrective_action;
char log_msg[GUEST_HEARTBEAT_MSG_MAX_LOG_SIZE];
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_HEARTBEAT_RESPONSE, &_last_rx_challenge))
return;
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_HEARTBEAT_HEALTH, &health))
return;
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_CORRECTIVE_ACTION, &corrective_action_str))
return;
corrective_action = guest_heartbeat_msg_action_ntoh(corrective_action_str);
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_LOG_MSG, &log_msg))
return;
DPRINTFD("Heartbeat Challenge Response received, challenge=%i.",
_last_rx_challenge);
unsigned int challenge_i;
for (challenge_i=0; GUEST_HEARTBEAT_CHALLENGE_DEPTH > challenge_i;
++challenge_i)
{
if (_last_tx_challenge[challenge_i] == _last_rx_challenge)
break;
}
if (GUEST_HEARTBEAT_CHALLENGE_DEPTH == challenge_i)
{
DPRINTFE("Mismatch between last transmitted challenges and last "
"received challenge.");
return;
}
if (NULL != _callbacks.recv_challenge_ack)
_callbacks.recv_challenge_ack(!strcmp(health, GUEST_HEARTBEAT_MSG_HEALTHY),
corrective_action, log_msg);
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Receive Action Notify
// ===============================================
static void guest_heartbeat_msg_recv_action_notify( struct json_object *jobj_msg )
{
uint32_t invocation_id;
char event_type[GUEST_HEARTBEAT_MSG_MAX_VALUE_SIZE];
char notification_type[GUEST_HEARTBEAT_MSG_MAX_VALUE_SIZE];
uint32_t timeout_ms;
GuestHeartbeatEventT event;
GuestHeartbeatNotifyT notify;
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_INVOCATION_ID, &invocation_id))
return;
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_EVENT_TYPE, &event_type))
return;
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_NOTIFICATION_TYPE, &notification_type))
return;
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_TIMEOUT_MS, &timeout_ms))
return;
if (timeout_ms > (GUEST_HEARTBEAT_PROPAGATION_DELAY_IN_SECS*1000))
timeout_ms -= (GUEST_HEARTBEAT_PROPAGATION_DELAY_IN_SECS * 1000);
event = guest_heartbeat_msg_event_ntoh(event_type);
notify = guest_heartbeat_msg_notify_ntoh(notification_type);
DPRINTFI("Heartbeat Action Notify received, invocation_id=%i.",
invocation_id);
const char *msg = json_object_to_json_string_ext(jobj_msg, JSON_C_TO_STRING_PLAIN);
DPRINTFD("Heartbeat Action Notify message received: %s", msg);
if (NULL != _callbacks.recv_action_notify)
_callbacks.recv_action_notify(invocation_id, event, notify, timeout_ms);
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Receive Action Response
// =================================================
static void guest_heartbeat_msg_recv_action_response( struct json_object *jobj_msg )
{
uint32_t invocation_id;
char event_type[GUEST_HEARTBEAT_MSG_MAX_VALUE_SIZE];
char notification_type[GUEST_HEARTBEAT_MSG_MAX_VALUE_SIZE];
char vote_result[GUEST_HEARTBEAT_MSG_MAX_VALUE_SIZE];
GuestHeartbeatEventT event;
GuestHeartbeatNotifyT notify;
GuestHeartbeatVoteResultT result;
char log_msg[GUEST_HEARTBEAT_MSG_MAX_LOG_SIZE];
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_INVOCATION_ID, &invocation_id))
return;
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_EVENT_TYPE, &event_type))
return;
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_NOTIFICATION_TYPE, &notification_type))
return;
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_VOTE_RESULT, &vote_result))
return;
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_LOG_MSG, &log_msg))
return;
event = guest_heartbeat_msg_event_ntoh(event_type);
notify = guest_heartbeat_msg_notify_ntoh(notification_type);
result = guest_heartbeat_msg_vote_result_ntoh(vote_result);
DPRINTFI("Heartbeat Action Response received, invocation_id=%i.",
invocation_id);
const char *msg = json_object_to_json_string_ext(jobj_msg, JSON_C_TO_STRING_PLAIN);
DPRINTFD("Heartbeat Action Response message received: %s", msg);
if (NULL != _callbacks.recv_action_response)
_callbacks.recv_action_response(invocation_id, event, notify,
result, log_msg);
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Receive Nack
// =================================================
static void guest_heartbeat_msg_recv_nack( struct json_object *jobj_msg )
{
uint32_t invocation_id;
char log_msg[GUEST_HEARTBEAT_MSG_MAX_LOG_SIZE];
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_INVOCATION_ID, &invocation_id))
return;
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_LOG_MSG, &log_msg))
return;
DPRINTFE("Heartbeat Nack message received, invocation_id=%i, error msg: %s",
invocation_id, log_msg);
const char *msg = json_object_to_json_string_ext(jobj_msg, JSON_C_TO_STRING_PLAIN);
DPRINTFD("Heartbeat Nack message received: %s", msg);
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Dispatch
// ==================================
void guest_heartbeat_msg_dispatch(json_object *jobj_msg)
{
int version;
char msg_type[GUEST_HEARTBEAT_MSG_MAX_VALUE_SIZE];
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_VERSION, &version))
return;
if (GUEST_HEARTBEAT_MSG_VERSION_CURRENT > version)
{
DPRINTFI("message received version %d, expected %d, dropping\n",
version, GUEST_HEARTBEAT_MSG_VERSION_CURRENT);
return;
}
if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_MSG_TYPE, &msg_type))
return;
if (!strcmp(msg_type, GUEST_HEARTBEAT_MSG_INIT)) {
guest_heartbeat_msg_recv_init(jobj_msg);
} else if (!strcmp(msg_type, GUEST_HEARTBEAT_MSG_INIT_ACK)) {
guest_heartbeat_msg_recv_init_ack(jobj_msg);
} else if (!strcmp(msg_type, GUEST_HEARTBEAT_MSG_EXIT)) {
guest_heartbeat_msg_recv_exit(jobj_msg);
} else if (!strcmp(msg_type, GUEST_HEARTBEAT_MSG_CHALLENGE)) {
guest_heartbeat_msg_recv_challenge(jobj_msg);
} else if (!strcmp(msg_type, GUEST_HEARTBEAT_MSG_CHALLENGE_RESPONSE)) {
guest_heartbeat_msg_recv_challenge_ack(jobj_msg);
} else if (!strcmp(msg_type, GUEST_HEARTBEAT_MSG_ACTION_NOTIFY)) {
guest_heartbeat_msg_recv_action_notify(jobj_msg);
} else if (!strcmp(msg_type, GUEST_HEARTBEAT_MSG_ACTION_RESPONSE)) {
guest_heartbeat_msg_recv_action_response(jobj_msg);
} else if (!strcmp(msg_type, GUEST_HEARTBEAT_MSG_NACK)) {
guest_heartbeat_msg_recv_nack(jobj_msg);
} else {
DPRINTFV("Unknown message type %s.", msg_type);
}
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Parser
// ==================================
/**
Multiple messages from the host can be bundled together into a single "read"
so we need to check message boundaries and handle breaking the message apart.
Assume a valid message does not contain newline '\n', and newline is added to
the beginning and end of each message by the sender to delimit the boundaries.
*/
void guest_heartbeat_msg_parser(void *buf, ssize_t len, json_tokener* tok, int newline_found)
{
json_object *jobj = json_tokener_parse_ex(tok, buf, len);
enum json_tokener_error jerr = json_tokener_get_error(tok);
if (jerr == json_tokener_success) {
guest_heartbeat_msg_dispatch(jobj);
json_object_put(jobj);
return;
}
else if (jerr == json_tokener_continue) {
// partial JSON is parsed , continue to read from socket.
if (newline_found) {
// if newline was found in the middle of the buffer, the message
// should be completed at this point. Throw out incomplete message
// by resetting tokener.
json_tokener_reset(tok);
}
}
else
{
// parsing error
json_tokener_reset(tok);
}
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Handler
// ==================================
void guest_heartbeat_msg_handler(void *buf, ssize_t len,json_tokener* tok)
{
void *newline;
ssize_t len_head;
next:
if (len == 0)
return;
// search for newline as delimiter
newline = memchr(buf, '\n', len);
if (newline) {
// split buffer to head and tail at the location of newline.
// feed the head to the parser and recursively process the tail.
len_head = newline-buf;
// parse head
if (len_head > 0)
guest_heartbeat_msg_parser(buf, len_head, tok, 1);
// start of the tail: skip newline
buf += len_head+1;
// length of the tail: deduct 1 for the newline character
len -= len_head+1;
// continue to process the tail.
goto next;
}
else {
guest_heartbeat_msg_parser(buf, len, tok, 0);
}
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Receive
// ==================================
static void guest_heartbeat_msg_receiver( int selobj )
{
int bytes_received;
GuestErrorT error;
char buf[4096];
error = guest_channel_receive(_channel_id, buf, sizeof(buf),
&bytes_received);
if (GUEST_OKAY != error)
{
DPRINTFE("Failed to receive message, error=%s.",
guest_error_str(error));
return;
}
DPRINTFV("Bytes received is %i.", bytes_received);
guest_heartbeat_msg_handler(buf, bytes_received, tok);
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Signal Handler
// ========================================
static void guest_heartbeat_msg_signal_handler( int signum )
{
int64_t sigval = signum;
int result;
if ((SIGIO == signum) && (0 <= _signal_fd))
{
result = write(_signal_fd, &sigval, sizeof(sigval));
if (0 > result)
{
DPRINTFE("Failed to write signal, error=%s", strerror(errno));
return;
}
guest_signal_ignore(signum);
}
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Hangup
// ================================
static void guest_heartbeat_msg_hangup( int selobj )
{
DPRINTFI("Heartbeat messaging hangup.");
if (GUEST_CHANNEL_ID_INVALID != _channel_id)
{
int selobj;
selobj = guest_channel_get_selobj(_channel_id);
if (0 <= selobj)
{
GuestErrorT error;
error = guest_selobj_deregister(selobj);
if (GUEST_OKAY != error)
{
DPRINTFE("Failed to deregister selection object %i, "
"error=%s.", selobj, guest_error_str(error));
}
guest_signal_register_handler(SIGIO,
guest_heartbeat_msg_signal_handler);
if (NULL != _callbacks.channel_state_change)
_callbacks.channel_state_change(false);
}
}
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Signal Dispatch
// =========================================
static void guest_heartbeat_msg_signal_dispatch( int selobj )
{
int signum;
int64_t sigval = 0;
int result;
GuestSelObjCallbacksT callbacks;
GuestErrorT error;
result = read(_signal_fd, &sigval, sizeof(sigval));
if (0 > result)
{
if (EINTR == errno)
{
DPRINTFD("Interrupted on signal read, error=%s.", strerror(errno));
} else {
DPRINTFE("Failed to dispatch signal, error=%s.", strerror(errno));
}
return;
}
signum = sigval;
if (SIGIO == signum)
{
DPRINTFI("Heartbeat messaging available.");
if (GUEST_CHANNEL_ID_INVALID != _channel_id)
{
selobj = guest_channel_get_selobj(_channel_id);
if (0 <= selobj)
{
memset(&callbacks, 0, sizeof(callbacks));
callbacks.read_callback = guest_heartbeat_msg_receiver;
callbacks.hangup_callback = guest_heartbeat_msg_hangup;
error = guest_selobj_register(selobj, &callbacks);
if (GUEST_OKAY != error)
{
DPRINTFE("Failed to register selection object %i, "
"error=%s.", selobj, guest_error_str(error));
abort();
}
if (NULL != _callbacks.channel_state_change)
_callbacks.channel_state_change(true);
}
}
} else {
DPRINTFI("Ignoring signal %i.", signum);
}
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Initialize
// ====================================
GuestErrorT guest_heartbeat_msg_initialize(
char* comm_device, GuestHeartbeatMsgCallbacksT* callbacks )
{
int selobj;
GuestSelObjCallbacksT selobj_callbacks;
GuestErrorT error;
_channel_id = GUEST_CHANNEL_ID_INVALID;
error = guest_channel_open(comm_device, &_channel_id);
if (GUEST_OKAY != error)
{
DPRINTFE("Failed to open communication channel over device %s, "
"error=%s.", comm_device, guest_error_str(error));
return error;
}
selobj = guest_channel_get_selobj(_channel_id);
if (0 <= selobj)
{
memset(&selobj_callbacks, 0, sizeof(selobj_callbacks));
selobj_callbacks.read_callback = guest_heartbeat_msg_receiver;
selobj_callbacks.hangup_callback = guest_heartbeat_msg_hangup;
error = guest_selobj_register(selobj, &selobj_callbacks);
if (GUEST_OKAY != error)
{
DPRINTFE("Failed to register selection object %i, error=%s.",
selobj, guest_error_str(error));
return error;
}
}
_signal_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
if (0 > _signal_fd)
{
DPRINTFE("Failed to open signal file descriptor,error=%s.",
strerror(errno));
return GUEST_FAILED;
}
memset(&selobj_callbacks, 0, sizeof(selobj_callbacks));
selobj_callbacks.read_callback = guest_heartbeat_msg_signal_dispatch;
error = guest_selobj_register(_signal_fd, &selobj_callbacks);
if (GUEST_OKAY != error)
{
DPRINTFE("Failed to register selection object, error=%s.",
guest_error_str(error));
close(_signal_fd);
_signal_fd = -1;
return error;
}
memcpy(&_callbacks, callbacks, sizeof(GuestHeartbeatMsgCallbacksT));
tok = json_tokener_new();
return GUEST_OKAY;
}
// ****************************************************************************
// ****************************************************************************
// Guest Heartbeat Message - Finalize
// ==================================
GuestErrorT guest_heartbeat_msg_finalize( void )
{
int selobj;
GuestErrorT error;
memset(&_callbacks, 0, sizeof(GuestHeartbeatMsgCallbacksT));
free(tok);
if (0 <= _signal_fd)
{
error = guest_selobj_deregister(_signal_fd);
if (GUEST_OKAY != error)
DPRINTFE("Failed to deregister selection object, error=%s.",
guest_error_str(error));
close(_signal_fd);
_signal_fd = -1;
}
if (GUEST_CHANNEL_ID_INVALID != _channel_id)
{
selobj = guest_channel_get_selobj(_channel_id);
if (0 <= selobj)
{
error = guest_selobj_deregister(selobj);
if (GUEST_OKAY != error)
{
DPRINTFE("Failed to deregister selection object %i, error=%s.",
selobj, guest_error_str(error));
}
}
error = guest_channel_close(_channel_id);
if (GUEST_OKAY != error)
{
DPRINTFE("Failed close communication channel, error=%s.",
guest_error_str(error));
}
_channel_id = GUEST_CHANNEL_ID_INVALID;
}
return GUEST_OKAY;
}
// ****************************************************************************