blob: 87be52cc68cd69702d0c9bc8df95ec9f922a2c8f [file]
/*
* trace-msg.c : define message protocol for communication between clients and
* a server
*
* Copyright (C) 2013 Hitachi, Ltd.
* Created by Yoshihiro YUNOMAE <yoshihiro.yunomae.ez@hitachi.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <errno.h>
#include <poll.h>
#include <fcntl.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <linux/types.h>
#include "trace-cmd-local.h"
#include "trace-local.h"
#include "trace-msg.h"
typedef __u32 u32;
typedef __be32 be32;
static inline void dprint(const char *fmt, ...)
{
va_list ap;
if (!debug)
return;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
/* Two (4k) pages is the max transfer for now */
#define MSG_MAX_LEN 8192
#define MSG_HDR_LEN sizeof(struct tracecmd_msg_header)
#define MSG_DATA_LEN (MSG_MAX_LEN - MSG_HDR_LEN)
/* - header size for error msg */
#define MSG_META_MAX_LEN (MSG_MAX_LEN - MIN_DATA_SIZE)
#define MIN_TINIT_SIZE (sizeof(struct tracecmd_msg_header) + \
sizeof(struct tracecmd_msg_tinit))
/* Not really the minimum, but I couldn't think of a better name */
#define MIN_RINIT_SIZE (sizeof(struct tracecmd_msg_header) + \
sizeof(struct tracecmd_msg_rinit))
#define MIN_DATA_SIZE (sizeof(struct tracecmd_msg_header) + \
sizeof(struct tracecmd_msg_data))
#define MIN_CONNECT_SIZE (sizeof(struct tracecmd_msg_header) + \
sizeof(struct tracecmd_msg_connect))
#define MIN_DOMAIN_SIZE (sizeof(struct tracecmd_msg_header) + \
sizeof(struct tracecmd_msg_domain))
#define MIN_CINIT_SIZE (sizeof(struct tracecmd_msg_header) + \
sizeof(struct tracecmd_msg_cinit))
#define MIN_TRACE_SIZE (sizeof(struct tracecmd_msg_header) + \
sizeof(struct tracecmd_msg_trace))
/* use CONNECTION_MSG as a protocol version of trace-msg */
#define MSG_VERSION "V2"
#define CONNECTION_MSG "tracecmd-" MSG_VERSION
#define CONNECTION_MSGSIZE sizeof(CONNECTION_MSG)
unsigned int page_size;
struct tracecmd_msg_server {
struct tracecmd_msg_handle handle;
int done;
int *cpu_fds;
};
static struct tracecmd_msg_server *
make_server(struct tracecmd_msg_handle *msg_handle)
{
if (!(msg_handle->flags & TRACECMD_MSG_FL_SERVER)) {
plog("Message handle not of type server\n");
return NULL;
}
return (struct tracecmd_msg_server *)msg_handle;
}
struct tracecmd_msg_opt {
be32 size;
be32 opt_cmd;
be32 padding; /* for backward compatibility */
};
struct tracecmd_msg_tinit {
be32 cpus;
be32 page_size;
be32 opt_num;
} __attribute__((packed));
struct tracecmd_msg_rinit {
be32 cpus;
} __attribute__((packed));
struct tracecmd_msg_connect {
union {
be32 cpus;
be32 argc;
};
} __attribute__((packed));
struct tracecmd_msg_domain {
be32 cpus;
} __attribute__((packed));
struct tracecmd_msg_cinit {
be32 cpus;
} __attribute__((packed));
struct tracecmd_msg_trace {
be32 pid;
} __attribute__((packed));
struct tracecmd_msg_data {
be32 size;
} __attribute__((packed));
struct tracecmd_msg_header {
be32 size;
be32 cmd;
};
#define MSG_MAP \
C(ERROR, 0, 0), \
C(CLOSE, 1, 0), \
C(TCONNECT, 2, 0), \
C(RCONNECT, 3, MIN_DATA_SIZE), \
C(TINIT, 4, MIN_TINIT_SIZE), \
C(RINIT, 5, MIN_RINIT_SIZE), \
C(SENDMETA, 6, MIN_DATA_SIZE), \
C(FINMETA, 7, 0), \
C(CONNECT, 8, MIN_CONNECT_SIZE), \
C(ACK, 9, 0), \
C(GLIST, 10, 0), \
C(DOMAIN, 11, MIN_DOMAIN_SIZE), \
C(FINISH, 12, 0), \
C(CINIT, 13, MIN_CINIT_SIZE), \
C(CRINIT, 14, 0), \
C(ALIST, 15, 0), \
C(TRACE, 16, MIN_TRACE_SIZE), \
C(MAX, 17, -1)
#undef C
#define C(a,b,c) MSG_##a = b
enum tracecmd_msg_cmd {
MSG_MAP
};
#undef C
#define C(a,b,c) c
static be32 msg_min_sizes[] = { MSG_MAP };
#undef C
#define C(a,b,c) #a
static const char *msg_names[] = { MSG_MAP };
static const char *cmd_to_name(int cmd)
{
if (cmd < MSG_MAX)
return msg_names[cmd];
return "Unkown";
}
struct tracecmd_msg_error {
struct tracecmd_msg_header hdr;
union {
struct tracecmd_msg_tinit tinit;
struct tracecmd_msg_rinit rinit;
struct tracecmd_msg_data data;
};
} __attribute__((packed));
struct tracecmd_msg {
struct tracecmd_msg_header hdr;
union {
struct tracecmd_msg_tinit tinit;
struct tracecmd_msg_rinit rinit;
struct tracecmd_msg_connect connect;
struct tracecmd_msg_connect domain;
struct tracecmd_msg_cinit cinit;
struct tracecmd_msg_trace trace;
struct tracecmd_msg_data data;
struct tracecmd_msg_error err;
};
union {
struct tracecmd_msg_opt *opt;
be32 *port_array;
void *buf;
};
} __attribute__((packed));
struct tracecmd_msg *errmsg;
static int read_fd(int sfd)
{
struct msghdr msg;
struct iovec iov[1];
char buf[2];
ssize_t n;
int recvfd;
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmptr;
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov[0].iov_base = buf;
iov[0].iov_len = 2;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
if ( (n = recvmsg(sfd, &msg, 0)) <= 0)
return(n);
if ( (cmptr = CMSG_FIRSTHDR(&msg)) != NULL &&
cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
if (cmptr->cmsg_level != SOL_SOCKET) {
perror("control level != SOL_SOCKET");
return -1;
}
if (cmptr->cmsg_type != SCM_RIGHTS) {
perror("control type != SCM_RIGHTS");
return -1;
}
recvfd = *((int *) CMSG_DATA(cmptr));
} else
recvfd = -1; /* descriptor was not passed */
return recvfd;
}
static ssize_t write_fd(int fd, int sendfd)
{
struct msghdr msg;
struct iovec iov[1];
char buf[2];
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmptr;
memset(&msg, 0, sizeof(msg));
memset(&control_un, 0, sizeof(control_un));
memset(&iov, 0, sizeof(iov));
memset(&buf, 0, sizeof(buf));
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
cmptr = CMSG_FIRSTHDR(&msg);
cmptr->cmsg_len = CMSG_LEN(sizeof(int));
cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS;
*((int *) CMSG_DATA(cmptr)) = sendfd;
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov[0].iov_base = buf;
iov[0].iov_len = 2;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
return sendmsg(fd, &msg, 0);
}
static int msg_write(int fd, struct tracecmd_msg *msg)
{
int cmd = ntohl(msg->hdr.cmd);
int size;
int ret;
if (cmd >= MSG_MAX) {
plog("Unsupported command: %d\n", cmd);
return -EINVAL;
}
dprint("msg send: %d (%s)\n", cmd, cmd_to_name(cmd));
size = msg_min_sizes[cmd];
if (!size)
size = ntohl(msg->hdr.size);
ret = __do_write_check(fd, msg, size);
if (ret < 0)
return ret;
if (ntohl(msg->hdr.size) <= size)
return 0;
return __do_write_check(fd, msg->buf, ntohl(msg->hdr.size) - size);
}
static int make_data(const char *buf, int buflen, struct tracecmd_msg *msg)
{
msg->data.size = htonl(buflen);
msg->buf = malloc(buflen);
if (!msg->buf)
return -ENOMEM;
memcpy(msg->buf, buf, buflen);
msg->hdr.size = htonl(MIN_DATA_SIZE + buflen);
return 0;
}
enum msg_opt_command {
MSGOPT_USETCP = 1,
};
static int make_tinit(struct tracecmd_msg_handle *msg_handle,
struct tracecmd_msg *msg)
{
struct tracecmd_msg_opt *opt;
int cpu_count = msg_handle->cpu_count;
int opt_num = 0;
int size = MIN_TINIT_SIZE;
if (msg_handle->flags & TRACECMD_MSG_FL_USE_TCP) {
opt_num++;
opt = malloc(sizeof(*opt));
if (!opt)
return -ENOMEM;
opt->size = htonl(sizeof(*opt));
opt->opt_cmd = htonl(MSGOPT_USETCP);
msg->opt = opt;
size += sizeof(*opt);
}
msg->tinit.cpus = htonl(cpu_count);
msg->tinit.page_size = htonl(page_size);
msg->tinit.opt_num = htonl(opt_num);
msg->hdr.size = htonl(size);
return 0;
}
static int make_rinit(struct tracecmd_msg *msg, int total_cpus, int *ports)
{
int size = MIN_RINIT_SIZE;
be32 *ptr;
be32 port;
int i;
msg->rinit.cpus = htonl(total_cpus);
if (ports) {
msg->port_array = malloc(sizeof(*ports) * total_cpus);
if (!msg->port_array)
return -ENOMEM;
size += sizeof(*ports) * total_cpus;
ptr = msg->port_array;
for (i = 0; i < total_cpus; i++) {
/* + rrqports->cpus or rrqports->port_array[i] */
port = htonl(ports[i]);
*ptr = port;
ptr++;
}
}
msg->hdr.size = htonl(size);
return 0;
}
static void tracecmd_msg_init(u32 cmd, struct tracecmd_msg *msg)
{
memset(msg, 0, sizeof(*msg));
msg->hdr.cmd = htonl(cmd);
if (!msg_min_sizes[cmd])
msg->hdr.size = htonl(MSG_HDR_LEN);
else
msg->hdr.size = htonl(msg_min_sizes[cmd]);
}
static int make_error_msg(struct tracecmd_msg *msg, struct tracecmd_msg *errmsg)
{
msg->err.hdr.size = errmsg->hdr.size;
msg->err.hdr.cmd = errmsg->hdr.cmd;
switch (ntohl(errmsg->hdr.cmd)) {
case MSG_TINIT:
msg->err.tinit = errmsg->tinit;
break;
case MSG_RINIT:
msg->err.rinit = errmsg->rinit;
break;
case MSG_SENDMETA:
case MSG_RCONNECT:
msg->err.data = errmsg->data;
break;
}
msg->hdr.size = htonl(sizeof(*msg));
return 0;
}
static void msg_free(struct tracecmd_msg *msg)
{
int cmd = ntohl(msg->hdr.cmd);
/* If a min size is defined, then the buf needs to be freed */
if (cmd < MSG_MAX && (msg_min_sizes[cmd] > 0))
free(msg->buf);
memset(msg, 0, sizeof(*msg));
}
static int tracecmd_msg_send(int fd, struct tracecmd_msg *msg)
{
int ret = 0;
ret = msg_write(fd, msg);
if (ret < 0)
ret = -ECOMM;
msg_free(msg);
return ret;
}
static void tracecmd_msg_send_error(int fd, struct tracecmd_msg *errmsg)
{
struct tracecmd_msg msg;
tracecmd_msg_init(MSG_ERROR, &msg);
make_error_msg(&msg, errmsg);
tracecmd_msg_send(fd, &msg);
}
static int msg_read(int fd, void *buf, u32 size, int *n)
{
ssize_t r;
while (size) {
r = read(fd, buf + *n, size);
if (r < 0) {
if (errno == EINTR)
continue;
return -errno;
} else if (!r)
return -ENOTCONN;
size -= r;
*n += r;
}
return 0;
}
static int msg_read_extra(int fd, struct tracecmd_msg *msg,
int *n, int size)
{
u32 cmd;
int rsize;
int ret;
cmd = ntohl(msg->hdr.cmd);
if (cmd > MSG_MAX)
return -EINVAL;
rsize = msg_min_sizes[cmd] - *n;
if (rsize <= 0)
return 0;
ret = msg_read(fd, msg, rsize, n);
if (ret < 0)
return ret;
if (size > *n) {
size -= *n;
msg->buf = malloc(size);
if (!msg->buf)
return -ENOMEM;
*n = 0;
return msg_read(fd, msg->buf, size, n);
}
return 0;
}
/*
* Read header information of msg first, then read all data
*/
static int tracecmd_msg_recv(int fd, struct tracecmd_msg *msg)
{
u32 size = 0;
int n = 0;
int ret;
ret = msg_read(fd, msg, MSG_HDR_LEN, &n);
if (ret < 0)
return ret;
dprint("msg received: %d (%s)\n",
ntohl(msg->hdr.cmd), cmd_to_name(ntohl(msg->hdr.cmd)));
size = ntohl(msg->hdr.size);
if (size > MSG_MAX_LEN)
/* too big */
goto error;
else if (size < MSG_HDR_LEN)
/* too small */
goto error;
else if (size > MSG_HDR_LEN)
return msg_read_extra(fd, msg, &n, size);
return 0;
error:
plog("Receive an invalid message(size=%d)\n", size);
return -ENOMSG;
}
#define MSG_WAIT_MSEC 5000
static int msg_wait_to = MSG_WAIT_MSEC;
bool tracecmd_msg_done(struct tracecmd_msg_handle *msg_handle)
{
struct tracecmd_msg_server *msg_server = make_server(msg_handle);
return (volatile int)msg_server->done;
}
void tracecmd_msg_set_done(struct tracecmd_msg_handle *msg_handle)
{
struct tracecmd_msg_server *msg_server = make_server(msg_handle);
msg_server->done = true;
}
int *tracecmd_msg_cpu_fds(struct tracecmd_msg_handle *msg_handle)
{
struct tracecmd_msg_server *msg_server = make_server(msg_handle);
return msg_server->cpu_fds;
}
void tracecmd_msg_set_cpu_fds(struct tracecmd_msg_handle *msg_handle, int *fds)
{
struct tracecmd_msg_server *msg_server = make_server(msg_handle);
msg_server->cpu_fds = fds;
}
/*
* A return value of 0 indicates time-out
*/
static int tracecmd_msg_recv_wait(int fd, struct tracecmd_msg *msg)
{
struct pollfd pfd;
int ret;
pfd.fd = fd;
pfd.events = POLLIN;
ret = poll(&pfd, 1, debug ? -1 : msg_wait_to);
if (ret < 0)
return -errno;
else if (ret == 0)
return -ETIMEDOUT;
return tracecmd_msg_recv(fd, msg);
}
static int tracecmd_msg_wait_for_msg(int fd, struct tracecmd_msg *msg)
{
u32 cmd;
int ret;
ret = tracecmd_msg_recv_wait(fd, msg);
if (ret < 0) {
if (ret == -ETIMEDOUT)
warning("Connection timed out\n");
return ret;
}
cmd = ntohl(msg->hdr.cmd);
switch (cmd) {
case MSG_RCONNECT:
/* Make sure the server is the tracecmd server */
if (memcmp(msg->buf, CONNECTION_MSG,
ntohl(msg->data.size) - 1) != 0) {
warning("server not tracecmd server");
return -EPROTONOSUPPORT;
}
break;
case MSG_CLOSE:
return -ECONNABORTED;
}
return 0;
}
int tracecmd_open_virt_ports(int *ports, int cpus)
{
char path[PATH_MAX];
int i;
for (i = 0; i < cpus; i++) {
snprintf(path, PATH_MAX, TRACE_PATH_CPU, i);
ports[i] = open(path, O_WRONLY);
if (ports[i] < 0) {
warning("Cannot open %s", TRACE_PATH_CPU, i);
return -errno;
}
}
return 0;
}
int tracecmd_msg_send_init_data(struct tracecmd_msg_handle *msg_handle,
int **array)
{
struct tracecmd_msg send_msg;
struct tracecmd_msg recv_msg;
int fd = msg_handle->fd;
int *ports;
int i, cpus;
int ret;
*array = NULL;
tracecmd_msg_init(MSG_TINIT, &send_msg);
ret = make_tinit(msg_handle, &send_msg);
if (ret < 0)
return ret;
ret = tracecmd_msg_send(fd, &send_msg);
if (ret < 0)
return ret;
ret = tracecmd_msg_wait_for_msg(fd, &recv_msg);
if (ret < 0)
return ret;
if (ntohl(recv_msg.hdr.cmd) != MSG_RINIT)
return -EINVAL;
cpus = ntohl(recv_msg.rinit.cpus);
ports = malloc_or_die(sizeof(int) * cpus);
if (msg_handle->flags & TRACECMD_MSG_FL_NETWORK) {
for (i = 0; i < cpus; i++)
ports[i] = ntohl(recv_msg.port_array[i]);
} else if (msg_handle->flags & (TRACECMD_MSG_FL_VIRT | TRACECMD_MSG_FL_AGENT)) {
/* Open data paths of virtio-serial */
ret = tracecmd_open_virt_ports(ports, cpus);
if (ret < 0)
return ret;
} else {
plog("Neither virt or network specified");
return -EINVAL;
}
*array = ports;
return 0;
}
static int send_string(struct tracecmd_msg_handle *msg_handle, const char *str);
int tracecmd_msg_connect_to_agent(struct tracecmd_msg_handle *msg_handle,
int argc, char * const *argv)
{
struct tracecmd_msg msg;
int fd = msg_handle->fd;
int ret;
int i;
tracecmd_msg_init(MSG_CONNECT, &msg);
msg.connect.argc = htonl(argc);
ret = tracecmd_msg_send(fd, &msg);
if (ret < 0)
return ret;
for (i = 0; i < argc; i++) {
ret = send_string(msg_handle, argv[i]);
if (ret < 0)
return ret;
}
/* Wait for ACK */
ret = tracecmd_msg_recv(msg_handle->fd, &msg);
if (ret < 0)
return ret;
ret = ntohl(msg.hdr.cmd);
msg_free(&msg);
if (ret != MSG_ACK) {
warning("Expected ACK and received %d\n", ret);
return -EINVAL;
}
return 0;
}
int tracecmd_msg_agent_connect(struct tracecmd_msg_handle *msg_handle, int cpu_count)
{
struct tracecmd_msg msg;
int fd = msg_handle->fd;
int ret;
u32 cmd;
tracecmd_msg_init(MSG_CINIT, &msg);
msg.cinit.cpus = cpu_count;
ret = tracecmd_msg_send(fd, &msg);
if (ret < 0)
return ret;
ret = tracecmd_msg_recv_wait(fd, &msg);
if (ret < 0) {
if (ret == -ETIMEDOUT)
warning("Connection timed out\n");
return ret;
}
cmd = ntohl(msg.hdr.cmd);
if (cmd != MSG_CRINIT) {
warning("Expected CRINIT and received %d\n", cmd);
return -EINVAL;
}
/* Now we just sit and wait for connection */
ret = tracecmd_msg_recv(fd, &msg);
if (ret < 0)
return ret;
/*
* TODO, At this point we are waiting for a connection
* from a manager to perform a record.
*/
cmd = ntohl(msg.hdr.cmd);
if (cmd != MSG_CONNECT) {
warning("Expected CONNECT and received %d\n", cmd);
return -EINVAL;
}
return ntohl(msg.connect.argc);
}
int tracecmd_msg_connect_to_server(struct tracecmd_msg_handle *msg_handle)
{
struct tracecmd_msg send_msg, recv_msg;
int fd = msg_handle->fd;
int ret;
/* connect to a server */
tracecmd_msg_init(MSG_TCONNECT, &send_msg);
ret = tracecmd_msg_send(fd, &send_msg);
if (ret < 0)
return ret;
ret = tracecmd_msg_recv_wait(fd, &recv_msg);
if (ret < 0) {
if (ret == -EPROTONOSUPPORT)
goto error;
}
msg_free(&recv_msg);
return ret;
error:
tracecmd_msg_send_error(fd, &recv_msg);
return ret;
}
int tracecmd_msg_transfer_fd(struct tracecmd_msg_handle *msg_handle, int fd, int cpus)
{
struct tracecmd_msg msg;
int cmd;
int ret;
/* Tell the agent that we are about to send a fd */
tracecmd_msg_init(MSG_CONNECT, &msg);
msg.connect.cpus = htonl(cpus);
ret = tracecmd_msg_send(msg_handle->fd, &msg);
if (ret < 0)
return ret;
ret = tracecmd_msg_recv(msg_handle->fd, &msg);
if (ret < 0)
return ret;
/* A connect is made by the listener. */
cmd = ntohl(msg.hdr.cmd);
msg_free(&msg);
if (cmd != MSG_ACK) {
warning("Expected ACK and received %d\n", cmd);
return -EINVAL;
}
return write_fd(msg_handle->fd, fd);
}
int receive_fd(int fd, int *cpus)
{
struct tracecmd_msg msg;
u32 cmd;
int ret;
ret = tracecmd_msg_recv(fd, &msg);
if (ret < 0)
return ret;
cmd = ntohl(msg.hdr.cmd);
if (cmd != MSG_CONNECT)
return -EINVAL;
*cpus = ntohl(msg.connect.cpus);
tracecmd_msg_init(MSG_ACK, &msg);
ret = tracecmd_msg_send(fd, &msg);
if (ret < 0)
return ret;
return read_fd(fd);
}
int tracecmd_msg_get_fds(struct tracecmd_msg_handle *msg_handle,
int cpu_count, int *fds)
{
int main_fd;
int fd;
int cpus;
int left;
int i;
main_fd = receive_fd(msg_handle->fd, &cpus);
if (main_fd < 0)
return main_fd;
for (i = 0; i < cpu_count; i++)
fds[i] = -1;
for (i = 0; i < cpus; i++) {
fd = receive_fd(msg_handle->fd, &left);
if (fd < 0)
return fd;
if (i < cpu_count)
fds[i] = fd;
}
/*
* The fd used to connect to the server is no longer used to transfer
* data. But it is now being used by the server to know when the
* manager exits (which closes all file descriptors).
* The msg_handle is now used to connect to the agent. Just
* replace the current fd with the agent fd and let the old one
* be orphaned. It will be closed on exit.
*/
msg_handle->fd = main_fd;
return 0;
}
enum tracecmd_msg_mngr_type
tracecmd_msg_read_manager(struct tracecmd_msg_handle *msg_handle)
{
enum tracecmd_msg_mngr_type type = TRACECMD_MSG_MNG_ERR;
struct tracecmd_msg msg;
int fd = msg_handle->fd;
u32 cmd;
int ret;
memset(&msg, 0, sizeof(msg));
ret = tracecmd_msg_recv_wait(fd, &msg);
if (ret < 0)
goto out;
cmd = ntohl(msg.hdr.cmd);
switch (cmd) {
case MSG_CONNECT:
msg_handle->cpu_count = ntohl(msg.connect.cpus);
type = TRACECMD_MSG_MNG_CONNECT;
break;
case MSG_GLIST:
type = TRACECMD_MSG_MNG_GLIST;
break;
case MSG_ALIST:
type = TRACECMD_MSG_MNG_ALIST;
break;
case MSG_TRACE:
type = TRACECMD_MSG_MNG_TRACE;
msg_handle->pid = ntohl(msg.trace.pid);
break;
}
msg_free(&msg);
out:
return type;
}
static char *receive_string(struct tracecmd_msg_handle *msg_handle)
{
struct tracecmd_msg msg;
int fd = msg_handle->fd;
char *str = NULL;
char *new;
int size;
int len = 0;
int ret;
for (;;) {
ret = tracecmd_msg_recv_wait(fd, &msg);
if (ret < 0) {
free(str);
str = NULL;
break;
}
if (ntohl(msg.hdr.cmd) == MSG_FINMETA)
break;
if (ntohl(msg.hdr.cmd) != MSG_SENDMETA) {
free(str);
str = NULL;
break;
}
size = ntohl(msg.data.size);
new = realloc(str, len + size + 1);
if (!new) {
free(str);
str = NULL;
break;
}
str = new;
memcpy(str + len, msg.buf, size);
len += size;
msg_free(&msg);
}
if (str)
str[len] = 0;
msg_free(&msg);
return str;
}
static int send_string(struct tracecmd_msg_handle *msg_handle,
const char *str)
{
struct tracecmd_msg msg;
int fd = msg_handle->fd;
int len = strlen(str);
int ret;
ret = tracecmd_msg_metadata_send(msg_handle, str, len);
if (ret < 0)
return ret;
tracecmd_msg_init(MSG_FINMETA, &msg);
return tracecmd_msg_send(fd, &msg);
}
int tracecmd_msg_agent_parameters(struct tracecmd_msg_handle *msg_handle,
int argc, char **argv)
{
struct tracecmd_msg msg;
int ret;
int i;
for (i = 0; i < argc; i++) {
argv[i] = receive_string(msg_handle);
if (!argv[i])
return -1;
}
tracecmd_msg_init(MSG_ACK, &msg);
ret = tracecmd_msg_send(msg_handle->fd, &msg);
if (ret < 0)
return ret;
return 0;
}
int tracecmd_msg_list_domains(struct tracecmd_msg_handle *msg_handle)
{
struct tracecmd_msg msg;
int fd = msg_handle->fd;
char *domain;
u32 cmd;
int cpus;
int ret;
int i;
for (i = 0; ; i++) {
ret = tracecmd_msg_recv_wait(fd, &msg);
if (ret < 0) {
if (ret == -ETIMEDOUT)
warning("Connection timed out\n");
return ret;
}
cmd = ntohl(msg.hdr.cmd);
if (cmd == MSG_FINISH)
break;
if (cmd != MSG_DOMAIN) {
warning("Unknown response %d\n", cmd);
return -EINVAL;
}
cpus = ntohl(msg.domain.cpus);
domain = receive_string(msg_handle);
if (!domain)
return -EINVAL;
printf("%s with %d cpus\n", domain, cpus);
free(domain);
}
return i;
}
int tracecmd_msg_list_guests(struct tracecmd_msg_handle *msg_handle)
{
struct tracecmd_msg msg;
int fd = msg_handle->fd;
int ret;
tracecmd_msg_init(MSG_GLIST, &msg);
ret = tracecmd_msg_send(fd, &msg);
if (ret < 0)
return ret;
ret = tracecmd_msg_list_domains(msg_handle);
if (!ret)
printf("No guests registered\n");
return ret;
}
int tracecmd_msg_list_agents(struct tracecmd_msg_handle *msg_handle)
{
struct tracecmd_msg msg;
int fd = msg_handle->fd;
int ret;
tracecmd_msg_init(MSG_ALIST, &msg);
ret = tracecmd_msg_send(fd, &msg);
if (ret < 0)
return ret;
ret = tracecmd_msg_list_domains(msg_handle);
if (!ret)
printf("No agents registered\n");
return ret;
}
int tracecmd_msg_get_connect(struct tracecmd_msg_handle *msg_handle,
char **domain, char **agent_fifo,
char ***cpu_fifos)
{
struct tracecmd_msg msg;
char *str;
int fd = msg_handle->fd;
int ret;
int i;
tracecmd_msg_init(MSG_ACK, &msg);
ret = tracecmd_msg_send(fd, &msg);
if (ret < 0)
return ret;
*domain = receive_string(msg_handle);
if (!domain)
return -EINVAL;
*agent_fifo = receive_string(msg_handle);
if (!agent_fifo)
return -EINVAL;
*cpu_fifos = calloc(msg_handle->cpu_count + 1, sizeof(**cpu_fifos));
if (!*cpu_fifos)
goto free;
for (i = 0; i < msg_handle->cpu_count; i++) {
str = receive_string(msg_handle);
if (!str)
goto free;
(*cpu_fifos)[i] = str;
}
(*cpu_fifos)[i] = NULL;
return 0;
free:
free(*agent_fifo);
if (*cpu_fifos) {
for (i = 0; i < msg_handle->cpu_count; i++)
free((*cpu_fifos)[i]);
}
return -ENOMEM;
}
int tracecmd_msg_send_cpus(struct tracecmd_msg_handle *msg_handle, int cpus)
{
struct tracecmd_msg msg;
int fd = msg_handle->fd;
tracecmd_msg_init(MSG_DOMAIN, &msg);
msg.domain.cpus = htonl(cpus);
return tracecmd_msg_send(fd, &msg);
}
int tracecmd_msg_send_domain(struct tracecmd_msg_handle *msg_handle,
char *domain, int cpus)
{
int ret;
ret = tracecmd_msg_send_cpus(msg_handle, cpus);
if (ret < 0)
return ret;
return send_string(msg_handle, domain);
}
int tracecmd_msg_get_domain(struct tracecmd_msg_handle *msg_handle, char **domain)
{
struct tracecmd_msg msg;
int fd = msg_handle->fd;
int ret;
tracecmd_msg_init(MSG_ACK, &msg);
ret = tracecmd_msg_send(fd, &msg);
if (ret < 0)
return ret;
*domain = receive_string(msg_handle);
if (!*domain)
return -EINVAL;
return 0;
}
int tracecmd_msg_send_finish(struct tracecmd_msg_handle *msg_handle)
{
struct tracecmd_msg msg;
int fd = msg_handle->fd;
tracecmd_msg_init(MSG_FINISH, &msg);
return tracecmd_msg_send(fd, &msg);
}
int tracecmd_msg_recv_finish(struct tracecmd_msg_handle *msg_handle)
{
struct tracecmd_msg msg;
int ret;
/* Wait till we reciev finish */
ret = tracecmd_msg_recv(msg_handle->fd, &msg);
if (ret < 0)
return ret;
if (ntohl(msg.hdr.cmd) != MSG_FINISH)
warning("Did not receive FINISH\n");
return 0;
}
int tracecmd_msg_connect_guest(struct tracecmd_msg_handle *msg_handle,
const char *domain, const char *agent,
int nr_cpus, char * const *cpu_list)
{
struct tracecmd_msg msg;
int fd = msg_handle->fd;
int i;
int ret;
tracecmd_msg_init(MSG_CONNECT, &msg);
msg.connect.cpus = htonl(nr_cpus);
ret = tracecmd_msg_send(fd, &msg);
if (ret < 0)
return ret;
ret = tracecmd_msg_recv_wait(fd, &msg);
if (ret < 0)
return ret;
if (ntohl(msg.hdr.cmd) != MSG_ACK)
return -1;
/*
* Now it is expecting the following strings:
* domain, agent
* followed by a list of cpu paths (nr_cpus amount)
*/
ret = send_string(msg_handle, domain);
if (ret < 0)
return ret;
ret = send_string(msg_handle, agent);
if (ret < 0)
return ret;
for (i = 0; i < nr_cpus; i++) {
ret = send_string(msg_handle, cpu_list[i]);
if (ret < 0)
return ret;
}
return 0;
}
int tracecmd_msg_connect_agent(struct tracecmd_msg_handle *msg_handle,
const char *guest, int pid, int *cpu_count)
{
struct tracecmd_msg msg;
int fd = msg_handle->fd;
int ret;
u32 cmd;
tracecmd_msg_init(MSG_TRACE, &msg);
msg.trace.pid = htonl(pid);
ret = tracecmd_msg_send(fd, &msg);
if (ret < 0)
return ret;
ret = tracecmd_msg_recv_wait(fd, &msg);
if (ret < 0)
return ret;
cmd = ntohl(msg.hdr.cmd);
/*
* If the server finds the PID it returns DOMAIN, with
* the number of CPUs the guest has. Otherwise it simply
* returns ACK and expects to receive the domain name.
*/
if (cmd == MSG_DOMAIN) {
*cpu_count = ntohl(msg.domain.cpus);
return 0;
}
if (cmd != MSG_ACK) {
if (pid)
return -ENODEV;
else
return -EINVAL;
}
ret = send_string(msg_handle, guest);
if (ret < 0)
return ret;
ret = tracecmd_msg_recv_wait(fd, &msg);
if (ret < 0)
return ret;
cmd = ntohl(msg.hdr.cmd);
if (cmd != MSG_DOMAIN)
return -ENODEV;
*cpu_count = ntohl(msg.domain.cpus);
return 0;
}
static bool process_option(struct tracecmd_msg_handle *msg_handle,
struct tracecmd_msg_opt *opt)
{
/* currently the only option we have is to us TCP */
if (ntohl(opt->opt_cmd) == MSGOPT_USETCP) {
msg_handle->flags |= TRACECMD_MSG_FL_USE_TCP;
return true;
}
return false;
}
static void error_operation_for_server(struct tracecmd_msg *msg)
{
u32 cmd;
cmd = ntohl(msg->hdr.cmd);
if (cmd == MSG_ERROR)
plog("Receive error message: cmd=%d size=%d\n",
ntohl(msg->err.hdr.cmd), ntohl(msg->err.hdr.size));
else
warning("Message: cmd=%d size=%d\n", cmd, ntohl(msg->hdr.size));
}
struct tracecmd_msg_handle *
tracecmd_msg_handle_alloc(int fd, unsigned long flags)
{
struct tracecmd_msg_handle *handle;
int size;
if (flags & TRACECMD_MSG_FL_SERVER)
size = sizeof(struct tracecmd_msg_server);
else
size = sizeof(struct tracecmd_msg_handle);
handle = calloc(1, size);
if (!handle)
return NULL;
handle->fd = fd;
handle->flags = flags;
return handle;
}
void tracecmd_msg_handle_close(struct tracecmd_msg_handle *msg_handle)
{
if (msg_handle->fd >= 0)
close(msg_handle->fd);
free(msg_handle);
}
int tracecmd_msg_set_connection(struct tracecmd_msg_handle *msg_handle,
const char *domain)
{
struct tracecmd_msg msg;
u32 cmd;
int ret;
memset(&msg, 0, sizeof(msg));
/*
* Wait for connection msg by a client first.
* If a client uses virtio-serial, a connection message will
* not be sent immediately after accept(). connect() is called
* in QEMU, so the client can send the connection message
* after guest boots. Therefore, the virt-server patiently
* waits for the connection request of a client.
*/
ret = tracecmd_msg_recv(msg_handle->fd, &msg);
if (ret < 0) {
if (!msg.hdr.cmd) {
/* No data means QEMU has already died. */
tracecmd_msg_handle_close(msg_handle);
die("Connection refused: %s", domain);
}
return -ENOMSG;
}
cmd = ntohl(msg.hdr.cmd);
if (cmd == MSG_CLOSE)
return -ECONNABORTED;
else if (cmd != MSG_TCONNECT)
return -EINVAL;
tracecmd_msg_init(MSG_RCONNECT, &msg);
ret = make_data(CONNECTION_MSG, CONNECTION_MSGSIZE, &msg);
if (ret < 0)
goto error;
ret = tracecmd_msg_send(msg_handle->fd, &msg);
if (ret < 0)
goto error;
return 0;
error:
error_operation_for_server(&msg);
return ret;
}
#define MAX_OPTION_SIZE 4096
int tracecmd_msg_initial_setting(struct tracecmd_msg_handle *msg_handle)
{
struct tracecmd_msg_opt *opt;
struct tracecmd_msg msg;
int pagesize;
int options, i, s;
int cpus;
int ret;
int offset = 0;
u32 size = MIN_TINIT_SIZE;
u32 cmd;
ret = tracecmd_msg_recv_wait(msg_handle->fd, &msg);
if (ret < 0) {
if (ret == -ETIMEDOUT)
warning("Connection timed out\n");
return ret;
}
cmd = ntohl(msg.hdr.cmd);
if (cmd == MSG_CINIT) {
/* This is a client agent */
msg_handle->flags |= TRACECMD_MSG_FL_AGENT;
msg_handle->cpu_count = msg.cinit.cpus;
tracecmd_msg_init(MSG_CRINIT, &msg);
return tracecmd_msg_send(msg_handle->fd, &msg);
}
if (cmd != MSG_TINIT) {
ret = -EINVAL;
goto error;
}
cpus = ntohl(msg.tinit.cpus);
plog("cpus=%d\n", cpus);
if (cpus < 0) {
ret = -EINVAL;
goto error;
}
msg_handle->cpu_count = cpus;
pagesize = ntohl(msg.tinit.page_size);
plog("pagesize=%d\n", pagesize);
if (pagesize <= 0) {
ret = -EINVAL;
goto error;
}
options = ntohl(msg.tinit.opt_num);
for (i = 0; i < options; i++) {
if (size + sizeof(*opt) > ntohl(msg.hdr.size)) {
plog("Not enough message for options\n");
ret = -EINVAL;
goto error;
}
opt = (void *)msg.opt + offset;
offset += ntohl(opt->size);
size += ntohl(opt->size);
if (ntohl(msg.hdr.size) < size) {
plog("Not enough message for options\n");
ret = -EINVAL;
goto error;
}
/* prevent a client from killing us */
if (ntohl(opt->size) > MAX_OPTION_SIZE) {
plog("Exceed MAX_OPTION_SIZE\n");
ret = -EINVAL;
goto error;
}
s = process_option(msg_handle, opt);
/* do we understand this option? */
if (!s) {
plog("Cannot understand(%d:%d:%d)\n",
i, ntohl(opt->size), ntohl(opt->opt_cmd));
ret = -EINVAL;
goto error;
}
}
return pagesize;
error:
error_operation_for_server(&msg);
return ret;
}
int tracecmd_msg_send_port_array(struct tracecmd_msg_handle *msg_handle,
int *ports)
{
struct tracecmd_msg msg;
int ret;
tracecmd_msg_init(MSG_RINIT, &msg);
ret = make_rinit(&msg, msg_handle->cpu_count, ports);
if (ret < 0)
return ret;
ret = tracecmd_msg_send(msg_handle->fd, &msg);
if (ret < 0)
return ret;
return 0;
}
void tracecmd_msg_send_close_msg(struct tracecmd_msg_handle *msg_handle)
{
struct tracecmd_msg msg;
tracecmd_msg_init(MSG_CLOSE, &msg);
tracecmd_msg_send(msg_handle->fd, &msg);
}
int tracecmd_msg_metadata_send(struct tracecmd_msg_handle *msg_handle,
const char *buf, int size)
{
struct tracecmd_msg msg;
int fd = msg_handle->fd;
int n;
int ret;
int count = 0;
tracecmd_msg_init(MSG_SENDMETA, &msg);
n = size;
do {
if (n > MSG_META_MAX_LEN) {
ret = make_data(buf+count, MSG_META_MAX_LEN, &msg);
if (ret < 0)
return -ENOMEM;
n -= MSG_META_MAX_LEN;
count += MSG_META_MAX_LEN;
} else {
ret = make_data(buf+count, n, &msg);
n = 0;
}
ret = msg_write(fd, &msg);
if (ret < 0)
break;
} while (n);
msg_free(&msg);
return ret;
}
int tracecmd_msg_finish_sending_metadata(struct tracecmd_msg_handle *msg_handle)
{
struct tracecmd_msg msg;
int ret;
tracecmd_msg_init(MSG_FINMETA, &msg);
ret = tracecmd_msg_send(msg_handle->fd, &msg);
if (ret < 0)
return ret;
return 0;
}
int tracecmd_msg_collect_metadata(struct tracecmd_msg_handle *msg_handle, int ofd)
{
struct tracecmd_msg msg;
u32 t, n, cmd;
ssize_t s;
int ret;
do {
ret = tracecmd_msg_recv_wait(msg_handle->fd, &msg);
if (ret < 0) {
if (ret == -ETIMEDOUT)
warning("Connection timed out\n");
else
warning("reading client");
msg_free(&msg);
return ret;
}
cmd = ntohl(msg.hdr.cmd);
if (cmd == MSG_FINMETA) {
/* Finish receiving meta data */
break;
} else if (cmd != MSG_SENDMETA)
goto error;
n = ntohl(msg.data.size);
t = n;
s = 0;
do {
s = write(ofd, msg.buf+s, t);
if (s < 0) {
msg_free(&msg);
if (errno == EINTR)
continue;
warning("writing to file");
return -errno;
}
t -= s;
s = n - t;
} while (t);
msg_free(&msg);
} while (cmd == MSG_SENDMETA);
return 0;
error:
error_operation_for_server(&msg);
return ret;
}
int tracecmd_msg_wait_for_close(struct tracecmd_msg_handle *msg_handle)
{
struct tracecmd_msg msg;
u32 cmd;
int ret;
/* check the finish message of the client */
while (!tracecmd_msg_done(msg_handle)) {
ret = tracecmd_msg_recv(msg_handle->fd, &msg);
if (ret < 0) {
warning("reading client");
return ret;
}
cmd = ntohl(msg.hdr.cmd);
if (cmd == MSG_CLOSE)
/* Finish this connection */
break;
else {
warning("Not accept the message %d", ntohl(msg.hdr.cmd));
ret = -EINVAL;
goto error;
}
}
return 0;
error:
error_operation_for_server(&msg);
return ret;
}