nfv/guest-comm/host-guest-comm-2.0/lib_guest_host_msg.c

240 lines
8.0 KiB
C

/*
* Copyright (c) 2013-2016, 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.
*/
/* This implements a library to be used for guest/host clients to interface with
* non-standard functionality on the host/guest using the backchannel
* communications pathway. (The same library can be used for both directions.)
*
* The general idea is that everything that goes through this is multiplexed
* over a single unix socket so that the guest app only needs to monitor one
* socket for activity.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/un.h>
#include <errno.h>
#include <stdlib.h>
#include <json-c/json.h>
#include "guest_host_msg.h"
#include "host_guest_msg_type.h"
/* Send a message to an address on the host.
* Returns 0 on success.
* A negative return value indicates an error of some kind.
*/
int gh_send_msg(gh_info_t *info, const char *dest_addr, const char *msg)
{
int rc;
//parse msg data
struct json_object *jobj_data = json_tokener_parse(msg);
if (jobj_data == NULL) {
snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "failed to parse msg");
return -1;
}
struct json_object *jobj_outmsg = json_object_new_object();
if (jobj_outmsg == NULL) {
snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "failed to allocate json object for outmsg");
json_object_put(jobj_data);
return -1;
}
json_object_object_add(jobj_outmsg, DATA, jobj_data);
json_object_object_add(jobj_outmsg, VERSION, json_object_new_int(CUR_VERSION));
json_object_object_add(jobj_outmsg, DEST_ADDR, json_object_new_string(dest_addr));
const char *outmsg = json_object_to_json_string_ext(jobj_outmsg, JSON_C_TO_STRING_PLAIN);
int msglen = strlen(outmsg);
rc = sendto(info->sock, outmsg, msglen, 0, (struct sockaddr *) &info->svaddr,
info->svaddrlen);
if (rc != msglen) {
if (rc > 0) {
snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "sendto returned %d, expected %d",
rc, msglen);
} else
snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "sendto: %m");
goto failed;
}
json_object_put(jobj_outmsg);
return 0;
failed:
json_object_put(jobj_outmsg);
return -1;
}
/* Read a message from the socket and process it. */
int gh_process_msg(gh_info_t *info)
{
char buf[HOST_GUEST_BUFSIZE];
int len;
len = recv(info->sock, buf, sizeof(buf), 0);
if (len == -1) {
if (errno == EAGAIN)
return 0;
else {
snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "error receiving msg: %m");
return -1;
}
}
struct json_object *jobj_msg = json_tokener_parse(buf);
if (jobj_msg == NULL) {
snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "failed to parse msg");
return -1;
}
// parse version
struct json_object *jobj_version;
if (!json_object_object_get_ex(jobj_msg, VERSION, &jobj_version)) {
snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "failed to parse version");
goto failed;
}
int version = json_object_get_int(jobj_version);
if (version != CUR_VERSION) {
snprintf(info->errorbuf, sizeof(info->errorbuf)-1,
"invalid version %d, expecting %d", version, CUR_VERSION);
goto failed;
}
// parse source address
struct json_object *jobj_source_addr;
if (!json_object_object_get_ex(jobj_msg, SOURCE_ADDR, &jobj_source_addr)) {
snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "failed to parse source_addr");
goto failed;
}
const char *source_addr = json_object_get_string(jobj_source_addr);
// parse data. data is a json object that is nested inside the msg
struct json_object *jobj_data;
if (!json_object_object_get_ex(jobj_msg, DATA, &jobj_data)) {
snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "failed to parse data");
goto failed;
}
if (info->gh_msg_handler)
info->gh_msg_handler(source_addr, jobj_data);
json_object_put(jobj_msg);
return 0;
failed:
json_object_put(jobj_msg);
return -1;
}
/* Allocate socket, set up callbacks, etc. This must be called once before
* any other API calls. "addr" is a null-terminated string of 16 chars or less
* (including the null) that is unique within this guest. "info" is the address
* of a value-result pointer that will be updated during the call.
*
* On success returns a socket and "info" is updated to point to an allocated chunk of memory.
* On error will return -1. If it was unable to allocate memory then "info" will be
* NULL. If it was able to allocate memory but something else failed then "info" will
* be non-NULL and you can call gh_get_error() to get an error message.
*/
int gh_init(gh_msg_handler_t msg_handler, char *addr, gh_info_t **in_info)
{
int flags;
int addrlen;
struct sockaddr_un cliaddr;
gh_info_t *info;
*in_info = malloc(sizeof(**in_info));
if (!*in_info)
/* unable to allocate memory */
return -1;
info = *in_info;
/* socket for talking to guest agent */
info->sock = socket(AF_UNIX, SOCK_DGRAM, 0);
if (info->sock == -1) {
snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "unable to open socket: %m");
goto free_out;
}
flags = fcntl(info->sock, F_GETFL, 0);
fcntl(info->sock, F_SETFL, flags | O_NONBLOCK);
/* our address */
memset(&cliaddr, 0, sizeof(struct sockaddr_un));
cliaddr.sun_family = AF_UNIX;
cliaddr.sun_path[0] = '\0';
strncpy(cliaddr.sun_path+1, addr,
sizeof(cliaddr.sun_path) - 2);
addrlen = sizeof(sa_family_t) + strlen(addr) + 1;
if (bind(info->sock, (struct sockaddr *) &cliaddr, addrlen) == -1) {
snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "unable to bind socket: %m");
goto close_out;
}
/* guest agent address */
memset(&info->svaddr, 0, sizeof(struct sockaddr_un));
info->svaddr.sun_family = AF_UNIX;
info->svaddr.sun_path[0] = '\0';
strncpy(info->svaddr.sun_path+1, AGENT_ADDR, sizeof(info->svaddr.sun_path) - 2);
info->svaddrlen = sizeof(sa_family_t) + strlen(AGENT_ADDR) + 1;
/* set up callback pointers */
info->gh_msg_handler = msg_handler;
return info->sock;
close_out:
close(info->sock);
free_out:
free(info);
return -1;
}
/* Provide access to the error message if the most recent call failed. */
char *gh_get_error(gh_info_t *info)
{
return info->errorbuf;
}