aboutsummaryrefslogtreecommitdiff
path: root/lib/trace-cmd/trace-msg.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/trace-cmd/trace-msg.c')
-rw-r--r--lib/trace-cmd/trace-msg.c1404
1 files changed, 1404 insertions, 0 deletions
diff --git a/lib/trace-cmd/trace-msg.c b/lib/trace-cmd/trace-msg.c
new file mode 100644
index 00000000..39465ade
--- /dev/null
+++ b/lib/trace-cmd/trace-msg.c
@@ -0,0 +1,1404 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * 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>
+ *
+ */
+
+#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 <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <linux/types.h>
+
+#include "trace-write-local.h"
+#include "trace-cmd-local.h"
+#include "trace-local.h"
+#include "trace-msg.h"
+#include "trace-cmd.h"
+
+typedef __u32 u32;
+typedef __be32 be32;
+
+#define dprint(fmt, ...) tracecmd_debug(fmt, ##__VA_ARGS__)
+
+/* 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_MAX_DATA_LEN (MSG_MAX_LEN - MSG_HDR_LEN)
+
+unsigned int page_size;
+
+struct tracecmd_msg_tinit {
+ be32 cpus;
+ be32 page_size;
+ be32 opt_num;
+} __packed;
+
+struct tracecmd_msg_rinit {
+ be32 cpus;
+} __packed;
+
+#define TRACE_REQ_PARAM_SIZE (2 * sizeof(int))
+enum trace_req_params {
+ TRACE_REQUEST_ARGS,
+ TRACE_REQUEST_TSYNC_PROTOS,
+};
+
+struct tracecmd_msg_trace_req_param {
+ int id;
+ int length;
+ char *value;
+};
+
+struct tracecmd_msg_trace_req {
+ be32 flags;
+ be32 argc;
+ u64 trace_id;
+} __packed;
+
+struct tracecmd_msg_trace_resp {
+ be32 flags;
+ be32 cpus;
+ be32 page_size;
+ u64 trace_id;
+ char tsync_proto_name[TRACECMD_TSYNC_PNAME_LENGTH];
+ be32 tsync_port;
+} __packed;
+
+struct tracecmd_msg_tsync {
+ char sync_protocol_name[TRACECMD_TSYNC_PNAME_LENGTH];
+ be32 sync_msg_id;
+} __packed;
+
+struct tracecmd_msg_header {
+ be32 size;
+ be32 cmd;
+ be32 cmd_size;
+} __packed;
+
+#define MSG_MAP \
+ C(CLOSE, 0, 0), \
+ C(TINIT, 1, sizeof(struct tracecmd_msg_tinit)), \
+ C(RINIT, 2, sizeof(struct tracecmd_msg_rinit)), \
+ C(SEND_DATA, 3, 0), \
+ C(FIN_DATA, 4, 0), \
+ C(NOT_SUPP, 5, 0), \
+ C(TRACE_REQ, 6, sizeof(struct tracecmd_msg_trace_req)), \
+ C(TRACE_RESP, 7, sizeof(struct tracecmd_msg_trace_resp)),\
+ C(CLOSE_RESP, 8, 0), \
+ C(TIME_SYNC, 9, sizeof(struct tracecmd_msg_tsync)),
+
+#undef C
+#define C(a,b,c) MSG_##a = b
+
+enum tracecmd_msg_cmd {
+ MSG_MAP
+ MSG_NR_COMMANDS
+};
+
+#undef C
+#define C(a,b,c) c
+
+static be32 msg_cmd_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 < 0 || cmd >= MSG_NR_COMMANDS)
+ return "Unknown";
+ return msg_names[cmd];
+}
+
+struct tracecmd_msg {
+ struct tracecmd_msg_header hdr;
+ union {
+ struct tracecmd_msg_tinit tinit;
+ struct tracecmd_msg_rinit rinit;
+ struct tracecmd_msg_trace_req trace_req;
+ struct tracecmd_msg_trace_resp trace_resp;
+ struct tracecmd_msg_tsync tsync;
+ };
+ char *buf;
+} __packed;
+
+static inline int msg_buf_len(struct tracecmd_msg *msg)
+{
+ return ntohl(msg->hdr.size) - MSG_HDR_LEN - ntohl(msg->hdr.cmd_size);
+}
+
+static int __msg_write(int fd, struct tracecmd_msg *msg, bool network)
+{
+ int msg_size, data_size;
+ int ret;
+ int cmd;
+
+ if (network) {
+ cmd = ntohl(msg->hdr.cmd);
+ if (cmd < 0 || cmd >= MSG_NR_COMMANDS)
+ return -EINVAL;
+ dprint("msg send: %d (%s) [%d]\n",
+ cmd, cmd_to_name(cmd), ntohl(msg->hdr.size));
+ }
+ msg_size = MSG_HDR_LEN + ntohl(msg->hdr.cmd_size);
+ data_size = ntohl(msg->hdr.size) - msg_size;
+ if (data_size < 0)
+ return -EINVAL;
+
+ if (network) {
+ ret = __do_write_check(fd, msg, msg_size);
+ if (ret < 0)
+ return ret;
+ }
+ if (!data_size)
+ return 0;
+
+ return __do_write_check(fd, msg->buf, data_size);
+}
+
+__hidden off64_t msg_lseek(struct tracecmd_msg_handle *msg_handle, off64_t offset, int whence)
+{
+ /*
+ * lseek works only if the handle is in cache mode,
+ * cannot seek on a network socket
+ */
+ if (!msg_handle->cache || msg_handle->cfd < 0)
+ return (off64_t)-1;
+ return lseek64(msg_handle->cfd, offset, whence);
+}
+
+static int msg_write(struct tracecmd_msg_handle *msg_handle, struct tracecmd_msg *msg)
+{
+ if (msg_handle->cache && msg_handle->cfd >= 0)
+ return __msg_write(msg_handle->cfd, msg, false);
+
+
+ return __msg_write(msg_handle->fd, msg, true);
+}
+
+enum msg_trace_flags {
+ MSG_TRACE_USE_FIFOS = 1 << 0,
+};
+
+static int make_tinit(struct tracecmd_msg_handle *msg_handle,
+ struct tracecmd_msg *msg)
+{
+ int cpu_count = msg_handle->cpu_count;
+ int opt_num = 0;
+ int data_size = 0;
+
+ if (msg_handle->flags & (TRACECMD_MSG_FL_USE_TCP |
+ TRACECMD_MSG_FL_USE_VSOCK)) {
+ msg->buf = msg_handle->flags & TRACECMD_MSG_FL_USE_TCP ?
+ strdup("tcp") : strdup("vsock");
+ if (!msg->buf)
+ return -1;
+ opt_num++;
+ data_size += strlen(msg->buf) + 1;
+ }
+
+ msg->tinit.cpus = htonl(cpu_count);
+ msg->tinit.page_size = htonl(page_size);
+ msg->tinit.opt_num = htonl(opt_num);
+
+ msg->hdr.size = htonl(ntohl(msg->hdr.size) + data_size);
+
+ return 0;
+}
+
+/* test a to u */
+static int tatou(const char *s, unsigned int *res)
+{
+ long r;
+
+ r = atol(s);
+ if (r >= 0 && r <= UINT_MAX) {
+ *res = (unsigned int)r;
+ return 0;
+ }
+ return -1;
+}
+
+static int write_uints(char *buf, size_t buf_len,
+ unsigned int *arr, int arr_len)
+{
+ int i, ret, tot = 0;
+
+ for (i = 0; i < arr_len; i++) {
+ ret = snprintf(buf, buf_len, "%u", arr[i]);
+ if (ret < 0)
+ return ret;
+
+ /* Count the '\0' byte */
+ ret++;
+ tot += ret;
+ if (buf)
+ buf += ret;
+ if (buf_len >= ret)
+ buf_len -= ret;
+ else
+ buf_len = 0;
+ }
+
+ return tot;
+}
+
+static int make_rinit(struct tracecmd_msg *msg, int cpus, unsigned int *ports)
+{
+ int data_size;
+
+ data_size = write_uints(NULL, 0, ports, cpus);
+ msg->buf = malloc(data_size);
+ if (!msg->buf)
+ return -ENOMEM;
+ write_uints(msg->buf, data_size, ports, cpus);
+
+ msg->rinit.cpus = htonl(cpus);
+ msg->hdr.size = htonl(ntohl(msg->hdr.size) + data_size);
+
+ return 0;
+}
+
+static void tracecmd_msg_init(u32 cmd, struct tracecmd_msg *msg)
+{
+ memset(msg, 0, sizeof(*msg));
+ msg->hdr.size = htonl(MSG_HDR_LEN + msg_cmd_sizes[cmd]);
+ msg->hdr.cmd = htonl(cmd);
+ msg->hdr.cmd_size = htonl(msg_cmd_sizes[cmd]);
+}
+
+static void msg_free(struct tracecmd_msg *msg)
+{
+ free(msg->buf);
+ memset(msg, 0, sizeof(*msg));
+}
+
+static int tracecmd_msg_send(struct tracecmd_msg_handle *msg_handle, struct tracecmd_msg *msg)
+{
+ int ret = 0;
+
+ ret = msg_write(msg_handle, msg);
+ if (ret < 0)
+ ret = -ECOMM;
+
+ msg_free(msg);
+
+ return ret;
+}
+
+static int msg_send_nofree(struct tracecmd_msg_handle *msg_handle, struct tracecmd_msg *msg)
+{
+ int ret = 0;
+
+ ret = msg_write(msg_handle, msg);
+ if (ret < 0)
+ ret = -ECOMM;
+
+ return ret;
+}
+
+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 char scratch_buf[MSG_MAX_LEN];
+
+static int msg_read_extra(int fd, struct tracecmd_msg *msg,
+ int *n, int size)
+{
+ int cmd, cmd_size, rsize;
+ int ret;
+
+ cmd = ntohl(msg->hdr.cmd);
+ if (cmd < 0 || cmd >= MSG_NR_COMMANDS)
+ return -EINVAL;
+
+ cmd_size = ntohl(msg->hdr.cmd_size);
+ if (cmd_size < 0)
+ return -EINVAL;
+
+ if (cmd_size > 0) {
+ rsize = cmd_size;
+ if (rsize > msg_cmd_sizes[cmd])
+ rsize = msg_cmd_sizes[cmd];
+
+ ret = msg_read(fd, msg, rsize, n);
+ if (ret < 0)
+ return ret;
+
+ ret = msg_read(fd, scratch_buf, cmd_size - 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) [%d]\n",
+ ntohl(msg->hdr.cmd), cmd_to_name(ntohl(msg->hdr.cmd)),
+ ntohl(msg->hdr.size));
+
+ 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:
+ tracecmd_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)
+{
+ return (volatile int)msg_handle->done;
+}
+
+void tracecmd_msg_set_done(struct tracecmd_msg_handle *msg_handle)
+{
+ msg_handle->done = true;
+}
+
+static void error_operation(struct tracecmd_msg *msg)
+{
+ tracecmd_warning("Message: cmd=%d size=%d", ntohl(msg->hdr.cmd), ntohl(msg->hdr.size));
+}
+
+/*
+ * 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, tracecmd_get_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)
+ tracecmd_warning("Connection timed out");
+ return ret;
+ }
+
+ cmd = ntohl(msg->hdr.cmd);
+ if (cmd == MSG_CLOSE)
+ return -ECONNABORTED;
+
+ return 0;
+}
+
+static int tracecmd_msg_send_notsupp(struct tracecmd_msg_handle *msg_handle)
+{
+ struct tracecmd_msg msg;
+
+ tracecmd_msg_init(MSG_NOT_SUPP, &msg);
+ return tracecmd_msg_send(msg_handle, &msg);
+}
+
+static int handle_unexpected_msg(struct tracecmd_msg_handle *msg_handle,
+ struct tracecmd_msg *msg)
+{
+ /* Don't send MSG_NOT_SUPP back if we just received one */
+ if (ntohl(msg->hdr.cmd) == MSG_NOT_SUPP)
+ return 0;
+
+ return tracecmd_msg_send_notsupp(msg_handle);
+
+}
+
+int tracecmd_msg_send_init_data(struct tracecmd_msg_handle *msg_handle,
+ unsigned int **client_ports)
+{
+ struct tracecmd_msg msg;
+ unsigned int *ports;
+ int i, cpus, ret;
+ char *p, *buf_end;
+ ssize_t buf_len;
+
+ *client_ports = NULL;
+
+ tracecmd_msg_init(MSG_TINIT, &msg);
+ ret = make_tinit(msg_handle, &msg);
+ if (ret < 0)
+ goto out;
+
+ ret = tracecmd_msg_send(msg_handle, &msg);
+ if (ret < 0)
+ goto out;
+
+ msg_free(&msg);
+
+ ret = tracecmd_msg_wait_for_msg(msg_handle->fd, &msg);
+ if (ret < 0)
+ goto out;
+
+ if (ntohl(msg.hdr.cmd) != MSG_RINIT) {
+ ret = -EOPNOTSUPP;
+ goto error;
+ }
+
+ buf_len = msg_buf_len(&msg);
+ if (buf_len <= 0) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (msg.buf[buf_len-1] != '\0') {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ cpus = ntohl(msg.rinit.cpus);
+ ports = malloc(sizeof(*ports) * cpus);
+ if (!ports) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ buf_end = msg.buf + buf_len;
+ for (i = 0, p = msg.buf; i < cpus; i++, p++) {
+ if (p >= buf_end || tatou(p, &ports[i])) {
+ free(ports);
+ ret = -EINVAL;
+ goto error;
+ }
+ p = strchr(p, '\0');
+ }
+
+ *client_ports = ports;
+
+ msg_free(&msg);
+ return 0;
+
+error:
+ error_operation(&msg);
+ if (ret == -EOPNOTSUPP)
+ handle_unexpected_msg(msg_handle, &msg);
+out:
+ msg_free(&msg);
+ return ret;
+}
+
+static bool process_option(struct tracecmd_msg_handle *msg_handle,
+ const char *opt)
+{
+ if (strcmp(opt, "tcp") == 0) {
+ msg_handle->flags |= TRACECMD_MSG_FL_USE_TCP;
+ return true;
+ }
+ if (strcmp(opt, "vsock") == 0) {
+ msg_handle->flags |= TRACECMD_MSG_FL_USE_VSOCK;
+ return true;
+ }
+ return false;
+}
+
+struct tracecmd_msg_handle *
+tracecmd_msg_handle_alloc(int fd, unsigned long flags)
+{
+ struct tracecmd_msg_handle *handle;
+
+ handle = calloc(1, sizeof(struct tracecmd_msg_handle));
+ if (!handle)
+ return NULL;
+
+ handle->fd = fd;
+ handle->flags = flags;
+ handle->cfd = -1;
+ handle->cache = false;
+ return handle;
+}
+
+int tracecmd_msg_handle_cache(struct tracecmd_msg_handle *msg_handle)
+{
+ if (msg_handle->cfd < 0) {
+ strcpy(msg_handle->cfile, MSG_CACHE_FILE);
+ msg_handle->cfd = mkstemp(msg_handle->cfile);
+ if (msg_handle->cfd < 0)
+ return -1;
+ unlink(msg_handle->cfile);
+ }
+ msg_handle->cache = true;
+ return 0;
+}
+
+static int flush_cache(struct tracecmd_msg_handle *msg_handle)
+{
+ char buf[MSG_MAX_DATA_LEN];
+ int ret;
+
+ if (!msg_handle->cache || msg_handle->cfd < 0)
+ return 0;
+ msg_handle->cache = false;
+ if (lseek64(msg_handle->cfd, 0, SEEK_SET) == (off64_t)-1)
+ return -1;
+ do {
+ ret = read(msg_handle->cfd, buf, MSG_MAX_DATA_LEN);
+ if (ret <= 0)
+ break;
+ ret = tracecmd_msg_data_send(msg_handle, buf, ret);
+ if (ret < 0)
+ break;
+ } while (ret >= 0);
+
+ close(msg_handle->cfd);
+ msg_handle->cfd = -1;
+ return ret;
+}
+
+void tracecmd_msg_handle_close(struct tracecmd_msg_handle *msg_handle)
+{
+ if (msg_handle->fd >= 0)
+ close(msg_handle->fd);
+ if (msg_handle->cfd >= 0)
+ close(msg_handle->cfd);
+ free(msg_handle);
+}
+
+#define MAX_OPTION_SIZE 4096
+
+int tracecmd_msg_initial_setting(struct tracecmd_msg_handle *msg_handle)
+{
+ struct tracecmd_msg msg;
+ char *p, *buf_end;
+ ssize_t buf_len;
+ int pagesize;
+ int options, i;
+ int cpus;
+ int ret;
+
+ memset(&msg, 0, sizeof(msg));
+ ret = tracecmd_msg_recv_wait(msg_handle->fd, &msg);
+ if (ret < 0) {
+ if (ret == -ETIMEDOUT)
+ tracecmd_warning("Connection timed out");
+ return ret;
+ }
+
+ if (ntohl(msg.hdr.cmd) != MSG_TINIT) {
+ ret = -EOPNOTSUPP;
+ goto error;
+ }
+
+ cpus = ntohl(msg.tinit.cpus);
+ tracecmd_plog("cpus=%d\n", cpus);
+ if (cpus < 0) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ msg_handle->cpu_count = cpus;
+
+ pagesize = ntohl(msg.tinit.page_size);
+ tracecmd_plog("pagesize=%d\n", pagesize);
+ if (pagesize <= 0) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ buf_len = msg_buf_len(&msg);
+ if (buf_len < 0) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (buf_len == 0)
+ goto no_options;
+
+ if (msg.buf[buf_len-1] != '\0') {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ buf_end = msg.buf + buf_len;
+ options = ntohl(msg.tinit.opt_num);
+ for (i = 0, p = msg.buf; i < options; i++, p++) {
+ if (p >= buf_end) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* do we understand this option? */
+ if (!process_option(msg_handle, p))
+ tracecmd_plog("Cannot understand option '%s'\n", p);
+
+ p = strchr(p, '\0');
+ }
+
+no_options:
+ msg_free(&msg);
+ return pagesize;
+
+error:
+ error_operation(&msg);
+ if (ret == -EOPNOTSUPP)
+ handle_unexpected_msg(msg_handle, &msg);
+ msg_free(&msg);
+ return ret;
+}
+
+int tracecmd_msg_send_port_array(struct tracecmd_msg_handle *msg_handle,
+ unsigned 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, &msg);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int tracecmd_msg_send_close_msg(struct tracecmd_msg_handle *msg_handle)
+{
+ struct tracecmd_msg msg;
+
+ tracecmd_msg_init(MSG_CLOSE, &msg);
+ return tracecmd_msg_send(msg_handle, &msg);
+}
+
+int tracecmd_msg_send_close_resp_msg(struct tracecmd_msg_handle *msg_handle)
+{
+ struct tracecmd_msg msg;
+
+ tracecmd_msg_init(MSG_CLOSE_RESP, &msg);
+ return tracecmd_msg_send(msg_handle, &msg);
+}
+
+int tracecmd_msg_data_send(struct tracecmd_msg_handle *msg_handle,
+ const char *buf, int size)
+{
+ struct tracecmd_msg msg;
+ int n;
+ int ret;
+ int count = 0;
+
+ /* Don't bother doing anything if there's nothing to do */
+ if (!size)
+ return 0;
+
+ tracecmd_msg_init(MSG_SEND_DATA, &msg);
+
+ msg.buf = malloc(MSG_MAX_DATA_LEN);
+ if (!msg.buf)
+ return -ENOMEM;
+
+ msg.hdr.size = htonl(MSG_MAX_LEN);
+
+ n = size;
+ while (n) {
+ if (n > MSG_MAX_DATA_LEN) {
+ memcpy(msg.buf, buf + count, MSG_MAX_DATA_LEN);
+ n -= MSG_MAX_DATA_LEN;
+ count += MSG_MAX_DATA_LEN;
+ } else {
+ msg.hdr.size = htonl(MSG_HDR_LEN + n);
+ memcpy(msg.buf, buf + count, n);
+ n = 0;
+ }
+ ret = msg_write(msg_handle, &msg);
+ if (ret < 0)
+ break;
+ }
+
+ msg_free(&msg);
+ return ret;
+}
+
+int tracecmd_msg_finish_sending_data(struct tracecmd_msg_handle *msg_handle)
+{
+ struct tracecmd_msg msg;
+ int ret;
+
+ flush_cache(msg_handle);
+ tracecmd_msg_init(MSG_FIN_DATA, &msg);
+ ret = tracecmd_msg_send(msg_handle, &msg);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+int tracecmd_msg_read_data(struct tracecmd_msg_handle *msg_handle, int ofd)
+{
+ struct tracecmd_msg msg;
+ int t, n, cmd;
+ ssize_t s;
+ int ret;
+
+ while (!tracecmd_msg_done(msg_handle)) {
+ ret = tracecmd_msg_recv_wait(msg_handle->fd, &msg);
+ if (ret < 0) {
+ tracecmd_warning("reading client %d (%s)", ret, strerror(ret));
+ return ret;
+ }
+
+ cmd = ntohl(msg.hdr.cmd);
+ if (cmd == MSG_FIN_DATA) {
+ /* Finish receiving data */
+ break;
+ } else if (cmd != MSG_SEND_DATA) {
+ ret = handle_unexpected_msg(msg_handle, &msg);
+ if (ret < 0)
+ goto error;
+ goto next;
+ }
+
+ n = msg_buf_len(&msg);
+ t = n;
+ s = 0;
+ while (t > 0) {
+ s = write(ofd, msg.buf+s, t);
+ if (s < 0) {
+ if (errno == EINTR)
+ continue;
+ tracecmd_warning("writing to file");
+ ret = -errno;
+ goto error;
+ }
+ t -= s;
+ s = n - t;
+ }
+
+next:
+ msg_free(&msg);
+ }
+
+ return 0;
+
+error:
+ error_operation(&msg);
+ msg_free(&msg);
+ return ret;
+}
+
+int tracecmd_msg_collect_data(struct tracecmd_msg_handle *msg_handle, int ofd)
+{
+ int ret;
+
+ ret = tracecmd_msg_read_data(msg_handle, ofd);
+ if (ret)
+ return ret;
+
+ return tracecmd_msg_wait_close(msg_handle);
+}
+
+static int tracecmd_msg_wait_for_cmd(struct tracecmd_msg_handle *msg_handle, enum tracecmd_msg_cmd cmd)
+{
+ struct tracecmd_msg msg;
+ int ret = -1;
+
+ memset(&msg, 0, sizeof(msg));
+ while (!tracecmd_msg_done(msg_handle)) {
+ ret = tracecmd_msg_recv(msg_handle->fd, &msg);
+ if (ret < 0)
+ goto error;
+
+ if (ntohl(msg.hdr.cmd) == cmd)
+ return 0;
+
+ error_operation(&msg);
+ ret = handle_unexpected_msg(msg_handle, &msg);
+ if (ret < 0)
+ goto error;
+
+ msg_free(&msg);
+ }
+
+error:
+ msg_free(&msg);
+ return ret;
+}
+
+int tracecmd_msg_wait_close(struct tracecmd_msg_handle *msg_handle)
+{
+ return tracecmd_msg_wait_for_cmd(msg_handle, MSG_CLOSE);
+}
+
+int tracecmd_msg_wait_close_resp(struct tracecmd_msg_handle *msg_handle)
+{
+ return tracecmd_msg_wait_for_cmd(msg_handle, MSG_CLOSE_RESP);
+}
+
+static int make_trace_req_protos(char **buf, int *size,
+ struct tracecmd_tsync_protos *protos)
+{
+ int protos_size = 1;
+ size_t buf_size;
+ char **names;
+ char *nbuf;
+ char *p;
+
+ names = protos->names;
+ while (*names) {
+ protos_size += strlen(*names) + 1;
+ names++;
+ }
+
+ buf_size = TRACE_REQ_PARAM_SIZE + protos_size;
+ nbuf = realloc(*buf, *size + buf_size);
+ if (!nbuf)
+ return -1;
+
+ p = nbuf + *size;
+ memset(p, 0, buf_size);
+
+ *(unsigned int *)p = htonl(TRACE_REQUEST_TSYNC_PROTOS);
+ p += sizeof(int);
+ *(unsigned int *)p = htonl(protos_size);
+ p += sizeof(int);
+
+ names = protos->names;
+ while (*names) {
+ strcpy(p, *names);
+ p += strlen(*names) + 1;
+ names++;
+ }
+ p = NULL;
+
+ *size += buf_size;
+ *buf = nbuf;
+ return 0;
+}
+
+static int make_trace_req_args(char **buf, int *size, int argc, char **argv)
+{
+ size_t args_size;
+ size_t buf_size;
+ char *nbuf;
+ char *p;
+ int i;
+
+ args_size = sizeof(int);
+ for (i = 0; i < argc; i++)
+ args_size += strlen(argv[i]) + 1;
+
+ buf_size = TRACE_REQ_PARAM_SIZE + args_size;
+ nbuf = realloc(*buf, *size + buf_size);
+ if (!nbuf)
+ return -1;
+
+ p = nbuf + *size;
+ memset(p, 0, buf_size);
+
+ *(unsigned int *)p = htonl(TRACE_REQUEST_ARGS);
+ p += sizeof(int);
+ *(unsigned int *)p = htonl(args_size);
+ p += sizeof(int);
+
+ *(unsigned int *)p = htonl(argc);
+ p += sizeof(int);
+ for (i = 0; i < argc; i++)
+ p = stpcpy(p, argv[i]) + 1;
+
+ *size += buf_size;
+ *buf = nbuf;
+ return 0;
+}
+
+static int make_trace_req(struct tracecmd_msg *msg, int argc, char **argv,
+ bool use_fifos, unsigned long long trace_id,
+ struct tracecmd_tsync_protos *protos)
+{
+ int size = 0;
+ char *buf = NULL;
+
+ msg->trace_req.flags = 0;
+ if (use_fifos)
+ msg->trace_req.flags |= MSG_TRACE_USE_FIFOS;
+ msg->trace_req.flags = htonl(msg->trace_req.flags);
+ msg->trace_req.trace_id = htonll(trace_id);
+
+ if (argc && argv)
+ make_trace_req_args(&buf, &size, argc, argv);
+ if (protos && protos->names)
+ make_trace_req_protos(&buf, &size, protos);
+
+ msg->buf = buf;
+ msg->hdr.size = htonl(ntohl(msg->hdr.size) + size);
+
+ return 0;
+}
+
+int tracecmd_msg_send_trace_req(struct tracecmd_msg_handle *msg_handle,
+ int argc, char **argv, bool use_fifos,
+ unsigned long long trace_id,
+ struct tracecmd_tsync_protos *protos)
+{
+ struct tracecmd_msg msg;
+ int ret;
+
+ tracecmd_msg_init(MSG_TRACE_REQ, &msg);
+ ret = make_trace_req(&msg, argc, argv, use_fifos, trace_id, protos);
+ if (ret < 0)
+ return ret;
+
+ return tracecmd_msg_send(msg_handle, &msg);
+}
+
+static int get_trace_req_protos(char *buf, int length,
+ struct tracecmd_tsync_protos **protos)
+{
+ struct tracecmd_tsync_protos *plist = NULL;
+ int count = 0;
+ char *p;
+ int i, j;
+
+ i = length;
+ p = buf;
+ while (i > 0) {
+ i -= strlen(p) + 1;
+ count++;
+ p += strlen(p) + 1;
+ }
+
+ plist = calloc(1, sizeof(struct tracecmd_tsync_protos));
+ if (!plist)
+ goto error;
+ plist->names = calloc(count + 1, sizeof(char *));
+ if (!plist->names)
+ goto error;
+ i = length;
+ p = buf;
+ j = 0;
+ while (i > 0 && j < (count - 1)) {
+ i -= strlen(p) + 1;
+ plist->names[j++] = strdup(p);
+ p += strlen(p) + 1;
+ }
+
+ *protos = plist;
+ return 0;
+error:
+ if (plist) {
+ free(plist->names);
+ free(plist);
+ }
+ return -1;
+}
+
+static int get_trace_req_args(char *buf, int length, int *argc, char ***argv)
+{
+ unsigned int nr_args;
+ char *p, *buf_end;
+ char **args = NULL;
+ char *vagrs = NULL;
+ int ret;
+ int i;
+
+ if (length <= sizeof(int) || buf[length - 1] != '\0') {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ nr_args = ntohl(*(unsigned int *)buf);
+ buf += sizeof(int);
+ length -= sizeof(int);
+
+ args = calloc(nr_args, sizeof(*args));
+ if (!args) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ vagrs = calloc(length, sizeof(char));
+ if (!vagrs) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(vagrs, buf, length);
+ buf_end = vagrs + length;
+ for (i = 0, p = vagrs; i < nr_args; i++, p++) {
+ if (p >= buf_end) {
+ ret = -EINVAL;
+ goto out;
+ }
+ args[i] = p;
+ p = strchr(p, '\0');
+ }
+
+ *argc = nr_args;
+ *argv = args;
+ return 0;
+
+out:
+ free(args);
+ free(vagrs);
+ return ret;
+
+}
+
+/*
+ * NOTE: On success, the returned `argv` should be freed with:
+ * free(argv[0]);
+ * free(argv);
+ * and `tsync_protos` with free(tsync_protos);
+ */
+int tracecmd_msg_recv_trace_req(struct tracecmd_msg_handle *msg_handle,
+ int *argc, char ***argv, bool *use_fifos,
+ unsigned long long *trace_id,
+ struct tracecmd_tsync_protos **protos)
+{
+ struct tracecmd_msg msg;
+ unsigned int param_id;
+ int param_length;
+ ssize_t buf_len;
+ char *p;
+ int ret;
+
+ ret = tracecmd_msg_recv(msg_handle->fd, &msg);
+ if (ret < 0)
+ return ret;
+
+ if (ntohl(msg.hdr.cmd) != MSG_TRACE_REQ) {
+ ret = -ENOTSUP;
+ goto out;
+ }
+
+ buf_len = ntohl(msg.hdr.size) - MSG_HDR_LEN - ntohl(msg.hdr.cmd_size);
+ if (buf_len < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ *use_fifos = ntohl(msg.trace_req.flags) & MSG_TRACE_USE_FIFOS;
+ *trace_id = ntohll(msg.trace_req.trace_id);
+ p = msg.buf;
+ while (buf_len > 2 * sizeof(int)) {
+ param_id = ntohl(*((unsigned int *)p));
+ p += sizeof(int);
+ buf_len -= sizeof(int);
+ param_length = ntohl(*((unsigned int *)p));
+ p += sizeof(int);
+ buf_len -= sizeof(int);
+ if (buf_len < param_length)
+ break;
+ ret = 0;
+ switch (param_id) {
+ case TRACE_REQUEST_ARGS:
+ ret = get_trace_req_args(p, param_length, argc, argv);
+ break;
+ case TRACE_REQUEST_TSYNC_PROTOS:
+ ret = get_trace_req_protos(p, param_length, protos);
+ break;
+ default:
+ break;
+ }
+ if (ret)
+ break;
+ buf_len -= param_length;
+ p += param_length;
+ }
+
+ msg_free(&msg);
+ return 0;
+
+out:
+ error_operation(&msg);
+ if (ret == -EOPNOTSUPP)
+ handle_unexpected_msg(msg_handle, &msg);
+ msg_free(&msg);
+ return ret;
+}
+
+/**
+ * tracecmd_msg_send_time_sync - Send a time sync packet
+ * @msg_handle: message handle, holding the communication context
+ * @sync_protocol: name of the time synch protocol, string up to
+ * TRACECMD_TSYNC_PNAME_LENGTH characters length.
+ * @sync_msg_id: id if the time synch message, protocol dependent
+ * @payload_size: size of the packet payload, 0 in case of no payload
+ * @payload: pointer to the packet payload, or NULL in case of no payload
+ *
+ * Returns 0 if packet is sent successfully, or negative error otherwise.
+ */
+int tracecmd_msg_send_time_sync(struct tracecmd_msg_handle *msg_handle,
+ char *sync_protocol, unsigned int sync_msg_id,
+ unsigned int payload_size, char *payload)
+{
+ struct tracecmd_msg msg;
+
+ tracecmd_msg_init(MSG_TIME_SYNC, &msg);
+ strncpy(msg.tsync.sync_protocol_name, sync_protocol, TRACECMD_TSYNC_PNAME_LENGTH);
+ msg.tsync.sync_msg_id = htonl(sync_msg_id);
+ msg.hdr.size = htonl(ntohl(msg.hdr.size) + payload_size);
+
+ msg.buf = payload;
+ return msg_send_nofree(msg_handle, &msg);
+}
+
+/**
+ * tracecmd_msg_recv_time_sync - Receive a time sync packet
+ * @msg_handle: message handle, holding the communication context
+ * @sync_protocol: return the name of the packet's time synch protocol.
+ * It must point to a prealocated buffer with size
+ * TRACECMD_TSYNC_PNAME_LENGTH
+ * @sync_msg_id: return the id of the packet's time synch message
+ * @payload_size: size of the packet's payload, can be:
+ * NULL - the payload is not interested and should be ignored
+ * pointer to int, with value 0 - update with the size of the payload
+ * allocate memory and cpy the payload
+ * into it
+ * pointer to int, with value greater than 0 - expected size of the
+ * payload, preallocated
+ * memory is passed to the API
+ * with that size
+ *@payload: pointer to the packet payload, can be:
+ * NULL - the payload is not interested and should be ignored
+ * pointer to char *, with value NULL - a new memory is allocated and returned
+ * here, containing the packet's payload
+ * the @payload_size is updated with the
+ * size of the allocated memory. It must be
+ * freed by free()
+ * pointer to char *, with no-NULL value - A prealocated array is passed, with size
+ * @payload_size. If payload's size is equal
+ * or less, it will be copied here.
+ *
+ * Returns 0 if packet is received successfully, or negative error otherwise.
+ */
+int tracecmd_msg_recv_time_sync(struct tracecmd_msg_handle *msg_handle,
+ char *sync_protocol,
+ unsigned int *sync_msg_id,
+ unsigned int *payload_size, char **payload)
+{
+ struct tracecmd_msg msg;
+ int ret = -1;
+ int buf_size;
+
+ memset(&msg, 0, sizeof(msg));
+ ret = tracecmd_msg_recv(msg_handle->fd, &msg);
+ if (ret < 0)
+ goto out;
+
+ if (ntohl(msg.hdr.cmd) != MSG_TIME_SYNC) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (sync_protocol)
+ strncpy(sync_protocol, msg.tsync.sync_protocol_name,
+ TRACECMD_TSYNC_PNAME_LENGTH);
+ if (sync_msg_id)
+ *sync_msg_id = ntohl(msg.tsync.sync_msg_id);
+
+ buf_size = msg_buf_len(&msg);
+ if (buf_size < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (buf_size && payload && payload_size) {
+ if (*payload_size) {
+ if (*payload_size < buf_size || *payload == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ memcpy(*payload, msg.buf, buf_size);
+ goto out;
+ }
+
+ *payload = malloc(buf_size);
+ if (*payload == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ *payload_size = buf_size;
+ memcpy(*payload, msg.buf, buf_size);
+ }
+
+out:
+ msg_free(&msg);
+ return ret;
+}
+
+static int make_trace_resp(struct tracecmd_msg *msg, int page_size, int nr_cpus,
+ unsigned int *ports, bool use_fifos,
+ unsigned long long trace_id,
+ const char *tsync_proto,
+ unsigned int tsync_port)
+{
+ int data_size;
+
+ if (!tsync_proto)
+ tsync_proto = "";
+
+ data_size = write_uints(NULL, 0, ports, nr_cpus);
+ msg->buf = malloc(data_size);
+ if (!msg->buf)
+ return -ENOMEM;
+ write_uints(msg->buf, data_size, ports, nr_cpus);
+
+ msg->hdr.size = htonl(ntohl(msg->hdr.size) + data_size);
+ msg->trace_resp.flags = use_fifos ? MSG_TRACE_USE_FIFOS : 0;
+ msg->trace_resp.flags = htonl(msg->trace_resp.flags);
+ strncpy(msg->trace_resp.tsync_proto_name, tsync_proto, TRACECMD_TSYNC_PNAME_LENGTH);
+ msg->trace_resp.tsync_port = htonl(tsync_port);
+
+ msg->trace_resp.cpus = htonl(nr_cpus);
+ msg->trace_resp.page_size = htonl(page_size);
+ msg->trace_resp.trace_id = htonll(trace_id);
+
+ return 0;
+}
+
+int tracecmd_msg_send_trace_resp(struct tracecmd_msg_handle *msg_handle,
+ int nr_cpus, int page_size,
+ unsigned int *ports, bool use_fifos,
+ unsigned long long trace_id,
+ const char *tsync_proto, unsigned int tsync_port)
+{
+ struct tracecmd_msg msg;
+ int ret;
+
+ tracecmd_msg_init(MSG_TRACE_RESP, &msg);
+ ret = make_trace_resp(&msg, page_size, nr_cpus, ports,
+ use_fifos, trace_id, tsync_proto, tsync_port);
+ if (ret < 0)
+ return ret;
+
+ return tracecmd_msg_send(msg_handle, &msg);
+}
+
+int tracecmd_msg_recv_trace_resp(struct tracecmd_msg_handle *msg_handle,
+ int *nr_cpus, int *page_size,
+ unsigned int **ports, bool *use_fifos,
+ unsigned long long *trace_id,
+ char **tsync_proto,
+ unsigned int *tsync_port)
+{
+ struct tracecmd_msg msg;
+ char *p, *buf_end;
+ ssize_t buf_len;
+ int i, ret;
+
+ ret = tracecmd_msg_recv(msg_handle->fd, &msg);
+ if (ret < 0)
+ return ret;
+
+ if (ntohl(msg.hdr.cmd) != MSG_TRACE_RESP) {
+ ret = -ENOTSUP;
+ goto out;
+ }
+
+ buf_len = msg_buf_len(&msg);
+ if (buf_len <= 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ *use_fifos = ntohl(msg.trace_resp.flags) & MSG_TRACE_USE_FIFOS;
+ *nr_cpus = ntohl(msg.trace_resp.cpus);
+ *page_size = ntohl(msg.trace_resp.page_size);
+ *trace_id = ntohll(msg.trace_resp.trace_id);
+ *tsync_proto = strdup(msg.trace_resp.tsync_proto_name);
+ *tsync_port = ntohl(msg.trace_resp.tsync_port);
+ *ports = calloc(*nr_cpus, sizeof(**ports));
+ if (!*ports) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ buf_end = msg.buf + buf_len;
+ for (i = 0, p = msg.buf; i < *nr_cpus; i++, p++) {
+ if (p >= buf_end || tatou(p, &(*ports)[i])) {
+ free(*ports);
+ ret = -EINVAL;
+ goto out;
+ }
+ p = strchr(p, '\0');
+ }
+
+ msg_free(&msg);
+ return 0;
+
+out:
+ error_operation(&msg);
+ if (ret == -EOPNOTSUPP)
+ handle_unexpected_msg(msg_handle, &msg);
+ msg_free(&msg);
+ return ret;
+}