/* * 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 host clients to interface with * non-standard functionality on the guest using the backchannel * communications pathway. * * 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. * * This is almost identical to lib_guest_host_msg.c but some data structures * are different. Might be possible to extract some common stuff. */ #include #include #include #include #include #include #include #include #include #include #include "host_guest_msg_type.h" #include "host_guest_msg.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 hg_send_msg(hg_info_t *info, const char *dest_addr, const char *dest_instance, 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)); json_object_object_add(jobj_outmsg, DEST_INSTANCE, json_object_new_string(dest_instance)); 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 hg_process_msg(hg_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"); 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 source instance struct json_object *jobj_source_instance; if (!json_object_object_get_ex(jobj_msg, SOURCE_INSTANCE, &jobj_source_instance)) { snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "failed to parse source_instance"); goto failed; } const char *source_instance = json_object_get_string(jobj_source_instance); // 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->hg_msg_handler) info->hg_msg_handler(source_addr, source_instance, 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 hg_get_error() to get an error message. */ int hg_init(hg_msg_handler_t msg_handler, char *addr, hg_info_t **in_info) { int flags; int addrlen; struct sockaddr_un cliaddr; hg_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->hg_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 *hg_get_error(hg_info_t *info) { return info->errorbuf; }