1213 lines
46 KiB
C
Executable File
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, ¬ification_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, ¬ification_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;
|
|
}
|
|
// ****************************************************************************
|