From 62d3069fe1d56bf9f95f9100f21758c3d99dccf6 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Wed, 8 Jun 2011 14:06:40 -0400 Subject: [PATCH] Initial import of libustctl and libustcomm Import and clean the libustctl and libustcomm code from the UST git tree. This import is done at the ust git head commit: 161239e0409832d65cb773a08d364b29945b18a7 Small modifications was needed but nothing major in the API and basic functionnalities. Signed-off-by: David Goulet --- Makefile.am | 2 + configure.ac | 7 +- include/lttng-share.h | 3 + libustcomm/Makefile.am | 10 + libustcomm/libustcomm.c | 1027 ++++++++++++++++++++++++++++++++++++++ libustcomm/libustcomm.h | 209 ++++++++ libustctl/Makefile.am | 9 + libustctl/libustctl.c | 775 ++++++++++++++++++++++++++++ libustctl/libustctl.h | 76 +++ ltt-sessiond/Makefile.am | 7 +- ltt-sessiond/main.c | 4 +- ltt-sessiond/ust-ctl.c | 3 +- 12 files changed, 2121 insertions(+), 11 deletions(-) create mode 100644 libustcomm/Makefile.am create mode 100644 libustcomm/libustcomm.c create mode 100644 libustcomm/libustcomm.h create mode 100644 libustctl/Makefile.am create mode 100644 libustctl/libustctl.c create mode 100644 libustctl/libustctl.h diff --git a/Makefile.am b/Makefile.am index 5c27eabe3..133579413 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,6 +3,8 @@ ACLOCAL_AMFLAGS = -I config SUBDIRS = liblttsessiondcomm \ libkernelctl \ liblttngctl \ + libustcomm \ + libustctl \ lttng \ ltt-sessiond \ kconsumerd \ diff --git a/configure.ac b/configure.ac index 5eb64a5f9..441230f88 100644 --- a/configure.ac +++ b/configure.ac @@ -25,11 +25,6 @@ AC_CHECK_LIB([uuid], [uuid_generate], [], [AC_MSG_ERROR([Cannot find libuuid. Use [LDFLAGS]=-Ldir to specify its location.])] ) -# Check libustctl -AC_CHECK_LIB([ustctl], [ustctl_connect_pid], [], - [AC_MSG_ERROR([Cannot find libustctl. Use [LDFLAGS]=-Ldir to specify its location.])] -) - # Check liburcu AC_CHECK_LIB([urcu], [synchronize_rcu], [], [AC_MSG_ERROR([Cannot find liburcu. Use [LDFLAGS]=-Ldir to specify its location.])] @@ -49,6 +44,8 @@ AC_CONFIG_FILES([ libkernelctl/Makefile liblttngctl/Makefile liblttsessiondcomm/Makefile + libustctl/Makefile + libustcomm/Makefile ltt-sessiond/Makefile lttng/Makefile kconsumerd/Makefile diff --git a/include/lttng-share.h b/include/lttng-share.h index f61e24dbb..812a0f05c 100644 --- a/include/lttng-share.h +++ b/include/lttng-share.h @@ -19,11 +19,14 @@ #ifndef _LTTNG_SHARE_H #define _LTTNG_SHARE_H +#include #include typedef uint32_t u32; typedef uint64_t u64; +typedef __s64 s64; + #define LTTNG_RUNDIR "/var/run/lttng" #endif /* _LTTNG_SHARE_H */ diff --git a/libustcomm/Makefile.am b/libustcomm/Makefile.am new file mode 100644 index 000000000..a4052c852 --- /dev/null +++ b/libustcomm/Makefile.am @@ -0,0 +1,10 @@ +AM_CPPFLAGS = -I$(top_srcdir)/include +AM_CFLAGS = -fno-strict-aliasing + +noinst_LTLIBRARIES = libustcomm.la + +libustcomm_la_SOURCES = libustcomm.c + +libustcomm_la_LDFLAGS = -no-undefined -static + +libustcomm_la_CFLAGS = -fPIC -fno-strict-aliasing diff --git a/libustcomm/libustcomm.c b/libustcomm/libustcomm.c new file mode 100644 index 000000000..bbf4ef9c1 --- /dev/null +++ b/libustcomm/libustcomm.c @@ -0,0 +1,1027 @@ +/* + * Copyright (C) 2009 Pierre-Marc Fournier + * 2010 Nils Carlson + * 2011 David Goulet + * + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libustcomm.h" +#include "lttngerr.h" + +#define MAX_SOCK_PATH_BASE_LEN 100 + +/* + * get_sock_name + * + * Set sock_name to the libust socket path using the pid + * and the base directory dir_name. + */ +static int get_sock_name(char *dir_name, pid_t pid, char *sock_name) +{ + struct dirent *dirent; + char sock_path_base[MAX_SOCK_PATH_BASE_LEN]; + int len; + DIR *dir; + + dir = opendir(dir_name); + if (dir == NULL) { + ERR("ustcomm sock name opendir %s", dir_name); + goto out_err; + } + + snprintf(sock_path_base, MAX_SOCK_PATH_BASE_LEN - 1, "%ld", (long) pid); + len = strlen(sock_path_base); + + DBG("Socket directory %s", sock_path_base); + + while ((dirent = readdir(dir))) { + if (!strcmp(dirent->d_name, ".") || + !strcmp(dirent->d_name, "..") || + !strcmp(dirent->d_name, "ust-consumer") || + dirent->d_type == DT_DIR || + strncmp(dirent->d_name, sock_path_base, len) != 0) { + continue; + } + + if (snprintf(sock_name, PATH_MAX - 1, "%s/%s", dir_name, dirent->d_name) < 0) { + perror("path longer than PATH_MAX?"); + goto out_err; + } + + break; + } + + return 0; + +out_err: + closedir(dir); + return -1; +} + +/* + * create_sock_addr + * + * Create a sockaddr structure for unic socket. + */ +static struct sockaddr_un *create_sock_addr(const char *name, size_t *sock_addr_size) +{ + struct sockaddr_un * addr; + size_t alloc_size; + + alloc_size = (size_t) (((struct sockaddr_un *) 0)->sun_path) + + strlen(name) + 1; + + addr = malloc(alloc_size); + if (addr < 0) { + ERR("allocating addr failed"); + return NULL; + } + + addr->sun_family = AF_UNIX; + strcpy(addr->sun_path, name); + + *sock_addr_size = alloc_size; + + return addr; +} + +/* + * connect_app_non_root + * + * Open a connection to a non root traceable app. + * On error, return -1 else 0 + */ +static int connect_app_non_root(pid_t pid, int *app_fd) +{ + int result; + int retval = 0; + char *dir_name; + char sock_name[PATH_MAX]; + + dir_name = ustcomm_user_sock_dir(); + if (!dir_name) { + return -ENOMEM; + } + + if (get_sock_name(dir_name, pid, sock_name)) { + retval = -ENOENT; + goto free_dir_name; + } + + result = ustcomm_connect_path(sock_name, app_fd); + if (result < 0) { + ERR("failed to connect to app"); + retval = -1; + goto free_dir_name; + } + +free_dir_name: + free(dir_name); + + return retval; +} + +/* + * connect_app_root + * + * Open a connection to a root traceable app. + * On error, return -1 else 0 + */ +static int connect_app_root(pid_t pid, int *app_fd) +{ + DIR *tmp_dir; + struct dirent *dirent; + char dir_name[PATH_MAX], sock_name[PATH_MAX]; + int result = -1; + + tmp_dir = opendir(USER_TMP_DIR); + if (!tmp_dir) { + return -1; + } + + while ((dirent = readdir(tmp_dir))) { + if (!strncmp(dirent->d_name, USER_SOCK_DIR_BASE, + strlen(USER_SOCK_DIR_BASE))) { + + if (snprintf(dir_name, PATH_MAX - 1, "%s/%s", USER_TMP_DIR, + dirent->d_name) < 0) { + continue; + } + + if (get_sock_name(dir_name, pid, sock_name)) { + continue; + } + + result = ustcomm_connect_path(sock_name, app_fd); + + if (result == 0) { + goto close_tmp_dir; + } + } + } + +close_tmp_dir: + closedir(tmp_dir); + + return result; +} + +/* + * time_and_pid_from_socket_name + * + * Get time from socket name + */ +static int time_and_pid_from_socket_name(char *sock_name, unsigned long *time, + pid_t *pid) +{ + char *saveptr, *pid_m_time_str; + char *sock_basename = strdup(basename(sock_name)); + + if (!sock_basename) { + return -1; + } + + /* This is the pid */ + pid_m_time_str = strtok_r(sock_basename, ".", &saveptr); + if (!pid_m_time_str) { + goto out_err; + } + + errno = 0; + *pid = (pid_t)strtoul(pid_m_time_str, NULL, 10); + if (errno) { + goto out_err; + } + + /* This should be the time-stamp */ + pid_m_time_str = strtok_r(NULL, ".", &saveptr); + if (!pid_m_time_str) { + goto out_err; + } + + errno = 0; + *time = strtoul(pid_m_time_str, NULL, 10); + if (errno) { + goto out_err; + } + + return 0; + +out_err: + free(sock_basename); + return -1; +} + +/* + * ustcomm_is_socket_live + * + * Check if socket is available and "living". + */ +int ustcomm_is_socket_live(char *sock_name, pid_t *read_pid) +{ + time_t time_from_pid; + unsigned long time_from_sock; + pid_t pid; + + if (time_and_pid_from_socket_name(sock_name, &time_from_sock, &pid)) { + return 0; + } + + if (read_pid) { + *read_pid = pid; + } + + time_from_pid = ustcomm_pid_st_mtime(pid); + if (!time_from_pid) { + return 0; + } + + if ((unsigned long) time_from_pid == time_from_sock) { + return 1; + } + + return 0; +} + +/* + * ustcomm_init_sock + * + * Init socket for communication between libust and libustcomm. + */ +struct ustcomm_sock *ustcomm_init_sock(int fd, int epoll_fd, + struct cds_list_head *list) +{ + struct epoll_event ev; + struct ustcomm_sock *sock; + + sock = malloc(sizeof(struct ustcomm_sock)); + if (!sock) { + perror("malloc: couldn't allocate ustcomm_sock"); + return NULL; + } + + ev.events = EPOLLIN; + ev.data.ptr = sock; + sock->fd = fd; + + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock->fd, &ev) == -1) { + perror("epoll_ctl: failed to add socket\n"); + free(sock); + return NULL; + } + + sock->epoll_fd = epoll_fd; + if (list) { + cds_list_add(&sock->list, list); + } else { + CDS_INIT_LIST_HEAD(&sock->list); + } + + return sock; +} + +/* + * ustcomm_del_sock + * + * Delete socket and remove it from the epoll set. + */ +void ustcomm_del_sock(struct ustcomm_sock *sock, int keep_in_epoll) +{ + cds_list_del(&sock->list); + if (!keep_in_epoll) { + if (epoll_ctl(sock->epoll_fd, EPOLL_CTL_DEL, sock->fd, NULL) == -1) { + perror("epoll_ctl: failed to delete socket"); + } + } + close(sock->fd); + free(sock); +} + +/* + * ustcomm_init_named_socket + * + * Init unix named socket + */ +struct ustcomm_sock *ustcomm_init_named_socket(const char *name, int epoll_fd) +{ + int result; + int fd; + size_t sock_addr_size; + struct sockaddr_un * addr; + struct ustcomm_sock *sock; + + fd = socket(PF_UNIX, SOCK_STREAM, 0); + if(fd == -1) { + perror("socket"); + return NULL; + } + + addr = create_sock_addr(name, &sock_addr_size); + if (addr == NULL) { + ERR("allocating addr, UST thread bailing"); + goto close_sock; + } + + result = access(name, F_OK); + if(result == 0) { + /* file exists */ + result = unlink(name); + if(result == -1) { + perror("unlink of socket file"); + goto free_addr; + } + DBG("socket already exists; overwriting"); + } + + result = bind(fd, (struct sockaddr *)addr, sock_addr_size); + if(result == -1) { + perror("bind"); + goto free_addr; + } + + result = listen(fd, 1); + if(result == -1) { + perror("listen"); + goto free_addr; + } + + sock = ustcomm_init_sock(fd, epoll_fd, NULL); + if (!sock) { + ERR("failed to create ustcomm_sock"); + goto free_addr; + } + + free(addr); + + return sock; + +free_addr: + free(addr); +close_sock: + close(fd); + + return NULL; +} + +/* + * ustcomm_del_named_sock + * + * Delete named socket + */ +void ustcomm_del_named_sock(struct ustcomm_sock *sock, int keep_socket_file) +{ + int result, fd; + struct stat st; + struct sockaddr dummy; + struct sockaddr_un *sockaddr = NULL; + int alloc_size; + + fd = sock->fd; + + if(!keep_socket_file) { + + /* Get the socket name */ + alloc_size = sizeof(dummy); + if (getsockname(fd, &dummy, (socklen_t *)&alloc_size) < 0) { + perror("getsockname failed"); + goto del_sock; + } + + sockaddr = malloc(alloc_size); + if (!sockaddr) { + ERR("failed to allocate sockaddr"); + goto del_sock; + } + + if (getsockname(fd, sockaddr, (socklen_t *)&alloc_size) < 0) { + perror("getsockname failed"); + goto free_sockaddr; + } + + /* Destroy socket */ + result = stat(sockaddr->sun_path, &st); + if(result < 0) { + ERR("stat (%s)", sockaddr->sun_path); + goto free_sockaddr; + } + + /* Paranoid check before deleting. */ + result = S_ISSOCK(st.st_mode); + if(!result) { + ERR("The socket we are about to delete is not a socket."); + goto free_sockaddr; + } + + result = unlink(sockaddr->sun_path); + if(result < 0) { + perror("unlink"); + } + } + +free_sockaddr: + free(sockaddr); + +del_sock: + ustcomm_del_sock(sock, keep_socket_file); +} + +/* + * ustcomm_recv_alloc + * + * Receive data and allocate data reception buffers. + */ +int ustcomm_recv_alloc(int sock, struct ustcomm_header *header, char **data) +{ + int result; + struct ustcomm_header peek_header; + struct iovec iov[2]; + struct msghdr msg; + + /* Just to make the caller fail hard */ + *data = NULL; + + result = recv(sock, &peek_header, sizeof(peek_header), MSG_PEEK | MSG_WAITALL); + if (result <= 0) { + if(errno == ECONNRESET) { + return 0; + } else if (errno == EINTR) { + return -1; + } else if (result < 0) { + perror("recv"); + return -1; + } + return 0; + } + + memset(&msg, 0, sizeof(msg)); + + iov[0].iov_base = (char *)header; + iov[0].iov_len = sizeof(struct ustcomm_header); + + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + if (peek_header.size) { + *data = malloc(peek_header.size); + if (!*data) { + return -ENOMEM; + } + + iov[1].iov_base = *data; + iov[1].iov_len = peek_header.size; + + msg.msg_iovlen++; + } + + result = recvmsg(sock, &msg, MSG_WAITALL); + if (result < 0) { + free(*data); + perror("recvmsg failed"); + } + + return result; +} + +/* + * ustcomm_recv_fd + * + * Receive data from fd. + * returns 1 to indicate a message was received + * returns 0 to indicate no message was received (end of stream) + * returns -1 to indicate an error + */ +int ustcomm_recv_fd(int sock, struct ustcomm_header *header, char *data, int *fd) +{ + int result; + struct ustcomm_header peek_header; + struct iovec iov[2]; + struct msghdr msg; + struct cmsghdr *cmsg; + char buf[CMSG_SPACE(sizeof(int))]; + + result = recv(sock, &peek_header, sizeof(peek_header), MSG_PEEK | MSG_WAITALL); + if (result <= 0) { + if(errno == ECONNRESET) { + return 0; + } else if (errno == EINTR) { + return -1; + } else if (result < 0) { + perror("recv"); + return -1; + } + return 0; + } + + memset(&msg, 0, sizeof(msg)); + + iov[0].iov_base = (char *)header; + iov[0].iov_len = sizeof(struct ustcomm_header); + + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + if (peek_header.size && data) { + if (peek_header.size < 0 || + peek_header.size > USTCOMM_DATA_SIZE) { + ERR("big peek header! %ld", peek_header.size); + return 0; + } + + iov[1].iov_base = data; + iov[1].iov_len = peek_header.size; + + msg.msg_iovlen++; + } + + if (fd && peek_header.fd_included) { + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + } + + result = recvmsg(sock, &msg, MSG_WAITALL); + if (result <= 0) { + if (result < 0) { + perror("recvmsg failed"); + } + return result; + } + + if (fd && peek_header.fd_included) { + cmsg = CMSG_FIRSTHDR(&msg); + result = 0; + while (cmsg != NULL) { + if (cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == SCM_RIGHTS) { + *fd = *(int *) CMSG_DATA(cmsg); + result = 1; + break; + } + cmsg = CMSG_NXTHDR(&msg, cmsg); + } + if (!result) { + ERR("Failed to receive file descriptor\n"); + } + } + + return 1; +} + +/* + * ustcomm_recv + * + * Recv wrapper + */ +int ustcomm_recv(int sock, struct ustcomm_header *header, char *data) +{ + return ustcomm_recv_fd(sock, header, data, NULL); +} + +/* + * ustcomm_send_fd + * + * Send data to fd. + */ +int ustcomm_send_fd(int sock, const struct ustcomm_header *header, + const char *data, int *fd) +{ + struct iovec iov[2]; + struct msghdr msg; + int result; + struct cmsghdr *cmsg; + char buf[CMSG_SPACE(sizeof(int))]; + + memset(&msg, 0, sizeof(msg)); + + iov[0].iov_base = (char *)header; + iov[0].iov_len = sizeof(struct ustcomm_header); + + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + if (header->size && data) { + iov[1].iov_base = (char *)data; + iov[1].iov_len = header->size; + + msg.msg_iovlen++; + + } + + if (fd && header->fd_included) { + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + *(int *) CMSG_DATA(cmsg) = *fd; + msg.msg_controllen = cmsg->cmsg_len; + } + + result = sendmsg(sock, &msg, MSG_NOSIGNAL); + if (result < 0 && errno != EPIPE) { + perror("sendmsg failed"); + } + return result; +} + +/* + * ustcomm_send + * + * Send wrapper + */ +int ustcomm_send(int sock, const struct ustcomm_header *header, const char *data) +{ + return ustcomm_send_fd(sock, header, data, NULL); +} + +/* + * ustcomm_req + * + * Do a request to the libust socket which means sending data and + * receiving data. + */ +int ustcomm_req(int sock, + const struct ustcomm_header *req_header, + const char *req_data, + struct ustcomm_header *res_header, + char *res_data) +{ + int result; + + result = ustcomm_send(sock, req_header, req_data); + if ( result <= 0) { + return result; + } + + return ustcomm_recv(sock, res_header, res_data); +} + +/* + * ustcomm_connect_path + * + * Connect to an fd. + * Return value: + * 0: success + * -1: error + */ +int ustcomm_connect_path(const char *name, int *connection_fd) +{ + int result, fd; + size_t sock_addr_size; + struct sockaddr_un *addr; + + fd = socket(PF_UNIX, SOCK_STREAM, 0); + if(fd == -1) { + perror("socket"); + return -1; + } + + addr = create_sock_addr(name, &sock_addr_size); + if (addr == NULL) { + ERR("allocating addr failed"); + goto close_sock; + } + + result = connect(fd, (struct sockaddr *)addr, sock_addr_size); + if(result == -1) { + perror("ustcomm connect path"); + goto free_sock_addr; + } + + *connection_fd = fd; + + free(addr); + + return 0; + +free_sock_addr: + free(addr); +close_sock: + close(fd); + + return -1; +} + +/* + * ustcomm_user_sock_dir + * + * Returns the current users socket directory, must be freed. + */ +char *ustcomm_user_sock_dir(void) +{ + int result; + char *sock_dir = NULL; + + result = asprintf(&sock_dir, "/tmp/ust-app-socks/"); + if (result < 0) { + ERR("string overflow allocating directory name"); + return NULL; + } + + return sock_dir; +} + +/* + * ustcomm_pid_st_mtime + * + * Return time of the process pid creation. + */ +time_t ustcomm_pid_st_mtime(pid_t pid) +{ + struct stat proc_stat; + char proc_name[PATH_MAX]; + + if (snprintf(proc_name, PATH_MAX - 1, "/proc/%ld", (long) pid) < 0) { + return 0; + } + + if (stat(proc_name, &proc_stat)) { + return 0; + } + + return proc_stat.st_mtime; +} + +/* + * ustcomm_connect_app + * + * Connect to libust application using the pid. + */ +int ustcomm_connect_app(pid_t pid, int *app_fd) +{ + *app_fd = 0; + + if (geteuid()) { + return connect_app_non_root(pid, app_fd); + } else { + return connect_app_root(pid, app_fd); + } + +} + +/* + * ustcomm_print_data + * + * Print data in a data_field. + */ +char *ustcomm_print_data(char *data_field, int field_size, + int *offset, const char *format, ...) +{ + va_list args; + int count, limit; + char *ptr = USTCOMM_POISON_PTR; + + limit = field_size - *offset; + va_start(args, format); + count = vsnprintf(&data_field[*offset], limit, format, args); + va_end(args); + + if (count < limit && count > -1) { + ptr = NULL + *offset; + *offset = *offset + count + 1; + } + + return ptr; +} + +/* + * ustcomm_restore_ptr + */ +char *ustcomm_restore_ptr(char *ptr, char *data_field, int data_field_size) +{ + if ((unsigned long)ptr > data_field_size || ptr == USTCOMM_POISON_PTR) { + return NULL; + } + + return data_field + (long)ptr; +} + +/* + * ustcomm_pack_single_field + */ +int ustcomm_pack_single_field(struct ustcomm_header *header, + struct ustcomm_single_field *single_field, + const char *string) +{ + int offset = 0; + + single_field->field = ustcomm_print_data(single_field->data, + sizeof(single_field->data), + &offset, + string); + + if (single_field->field == USTCOMM_POISON_PTR) { + return -ENOMEM; + } + + header->size = COMPUTE_MSG_SIZE(single_field, offset); + + return 0; +} + +/* + * ustcomm_unpack_single_field + */ +int ustcomm_unpack_single_field(struct ustcomm_single_field *single_field) +{ + single_field->field = ustcomm_restore_ptr(single_field->field, + single_field->data, + sizeof(single_field->data)); + if (!single_field->field) { + return -EINVAL; + } + + return 0; +} + +/* + * ustcomm_pack_channel_info + */ +int ustcomm_pack_channel_info(struct ustcomm_header *header, + struct ustcomm_channel_info *ch_inf, const char *trace, const char *channel) +{ + int offset = 0; + + ch_inf->trace = ustcomm_print_data(ch_inf->data, + sizeof(ch_inf->data), &offset, trace); + + if (ch_inf->trace == USTCOMM_POISON_PTR) { + return -ENOMEM; + } + + ch_inf->channel = ustcomm_print_data(ch_inf->data, + sizeof(ch_inf->data), &offset, channel); + + if (ch_inf->channel == USTCOMM_POISON_PTR) { + return -ENOMEM; + } + + header->size = COMPUTE_MSG_SIZE(ch_inf, offset); + + return 0; +} + +/* + * ustcomm_unpack_channel_info + */ +int ustcomm_unpack_channel_info(struct ustcomm_channel_info *ch_inf) +{ + ch_inf->trace = ustcomm_restore_ptr(ch_inf->trace, + ch_inf->data, sizeof(ch_inf->data)); + if (!ch_inf->trace) { + return -EINVAL; + } + + ch_inf->channel = ustcomm_restore_ptr(ch_inf->channel, + ch_inf->data, sizeof(ch_inf->data)); + if (!ch_inf->channel) { + return -EINVAL; + } + + return 0; +} + +/* + * ustcomm_pack_buffer_info + */ +int ustcomm_pack_buffer_info(struct ustcomm_header *header, + struct ustcomm_buffer_info *buf_inf, + const char *trace, const char *channel, int channel_cpu) +{ + int offset = 0; + + buf_inf->trace = ustcomm_print_data(buf_inf->data, + sizeof(buf_inf->data), &offset, trace); + + if (buf_inf->trace == USTCOMM_POISON_PTR) { + return -ENOMEM; + } + + buf_inf->channel = ustcomm_print_data(buf_inf->data, + sizeof(buf_inf->data), &offset, channel); + + if (buf_inf->channel == USTCOMM_POISON_PTR) { + return -ENOMEM; + } + + buf_inf->ch_cpu = channel_cpu; + + header->size = COMPUTE_MSG_SIZE(buf_inf, offset); + + return 0; +} + +/* + * ustcomm_unpack_buffer_info + */ +int ustcomm_unpack_buffer_info(struct ustcomm_buffer_info *buf_inf) +{ + buf_inf->trace = ustcomm_restore_ptr(buf_inf->trace, + buf_inf->data, sizeof(buf_inf->data)); + if (!buf_inf->trace) { + return -EINVAL; + } + + buf_inf->channel = ustcomm_restore_ptr(buf_inf->channel, + buf_inf->data, sizeof(buf_inf->data)); + if (!buf_inf->channel) { + return -EINVAL; + } + + return 0; +} + +/* + * ustcomm_pack_ust_marker_info + */ +int ustcomm_pack_ust_marker_info(struct ustcomm_header *header, + struct ustcomm_ust_marker_info *ust_marker_inf, + const char *trace, const char *channel, const char *ust_marker) +{ + int offset = 0; + + ust_marker_inf->trace = ustcomm_print_data(ust_marker_inf->data, + sizeof(ust_marker_inf->data), &offset, trace); + + if (ust_marker_inf->trace == USTCOMM_POISON_PTR) { + return -ENOMEM; + } + + + ust_marker_inf->channel = ustcomm_print_data(ust_marker_inf->data, + sizeof(ust_marker_inf->data), &offset, channel); + + if (ust_marker_inf->channel == USTCOMM_POISON_PTR) { + return -ENOMEM; + } + + + ust_marker_inf->ust_marker = ustcomm_print_data(ust_marker_inf->data, + sizeof(ust_marker_inf->data), &offset, ust_marker); + + if (ust_marker_inf->ust_marker == USTCOMM_POISON_PTR) { + return -ENOMEM; + } + + header->size = COMPUTE_MSG_SIZE(ust_marker_inf, offset); + + return 0; +} + +/* + * ustcomm_unpack_ust_marker_info + */ +int ustcomm_unpack_ust_marker_info(struct ustcomm_ust_marker_info *ust_marker_inf) +{ + ust_marker_inf->trace = ustcomm_restore_ptr(ust_marker_inf->trace, + ust_marker_inf->data, + sizeof(ust_marker_inf->data)); + if (!ust_marker_inf->trace) { + return -EINVAL; + } + + ust_marker_inf->channel = ustcomm_restore_ptr(ust_marker_inf->channel, + ust_marker_inf->data, sizeof(ust_marker_inf->data)); + if (!ust_marker_inf->channel) { + return -EINVAL; + } + + ust_marker_inf->ust_marker = ustcomm_restore_ptr(ust_marker_inf->ust_marker, + ust_marker_inf->data, sizeof(ust_marker_inf->data)); + if (!ust_marker_inf->ust_marker) { + return -EINVAL; + } + + return 0; +} + diff --git a/libustcomm/libustcomm.h b/libustcomm/libustcomm.h new file mode 100644 index 000000000..0b7f4fd11 --- /dev/null +++ b/libustcomm/libustcomm.h @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2009 Pierre-Marc Fournier + * 2010 Nils Carlson + * 2011 David Goulet + * + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _LTT_LIBUSTCOMM_H +#define _LTT_LIBUSTCOMM_H + +#include +#include +#include + +#include "lttng-share.h" + +#define UST_SOCK_DIR "/tmp/ust-app-socks" +#define USER_TMP_DIR "/tmp" +#define USER_SOCK_DIR_BASE "ust-socks-" +#define USER_SOCK_DIR USER_TMP_DIR "/" USER_SOCK_DIR_BASE + +struct ustcomm_sock { + struct cds_list_head list; + int fd; + int epoll_fd; +}; + +struct ustcomm_header { + int command; + long size; + int result; + int fd_included; +}; + +#define USTCOMM_BUFFER_SIZE ((1 << 12) - sizeof(struct ustcomm_header)) + +/* + * Specify a sata size that leaves margin at the end of a buffer + * in order to make sure that we never have more data than + * will fit in the buffer AND that the last chars (due to a + * pre-receive memset) will always be 0, terminating any string + */ +#define USTCOMM_DATA_SIZE (USTCOMM_BUFFER_SIZE - 20 * sizeof(void *)) + +enum ustcomm_tracectl_commands { + ALLOC_TRACE, + CONSUME_BUFFER, + CREATE_TRACE, + DESTROY_TRACE, + DISABLE_MARKER, + ENABLE_MARKER, + EXIT, + FORCE_SUBBUF_SWITCH, + GET_BUF_SHMID_PIPE_FD, + GET_PIDUNIQUE, + GET_SOCK_PATH, + GET_SUBBUFFER, + GET_SUBBUF_NUM_SIZE, + LIST_MARKERS, + LIST_TRACE_EVENTS, + LOAD_PROBE_LIB, + NOTIFY_BUF_MAPPED, + PRINT_MARKERS, + PRINT_TRACE_EVENTS, + PUT_SUBBUFFER, + SETUP_TRACE, + SET_SOCK_PATH, + SET_SUBBUF_NUM, + SET_SUBBUF_SIZE, + START, + START_TRACE, + STOP_TRACE, +}; + +struct ustcomm_single_field { + char *field; + char data[USTCOMM_DATA_SIZE]; +}; + +struct ustcomm_channel_info { + char *trace; + char *channel; + unsigned int subbuf_size; + unsigned int subbuf_num; + char data[USTCOMM_DATA_SIZE]; +}; + +struct ustcomm_buffer_info { + char *trace; + char *channel; + int ch_cpu; + pid_t pid; + int buf_shmid; + int buf_struct_shmid; + long consumed_old; + char data[USTCOMM_DATA_SIZE]; +}; + +struct ustcomm_ust_marker_info { + char *trace; + char *channel; + char *ust_marker; + char data[USTCOMM_DATA_SIZE]; +}; + +struct ustcomm_pidunique { + s64 pidunique; +}; + +struct ustcomm_notify_buf_mapped { + char data[USTCOMM_DATA_SIZE]; +}; + +/* Create and delete sockets */ +struct ustcomm_sock *ustcomm_init_sock(int fd, int epoll_fd, struct cds_list_head *list); +void ustcomm_del_sock(struct ustcomm_sock *sock, int keep_in_epoll); + +/* Create and delete named sockets */ +struct ustcomm_sock *ustcomm_init_named_socket(const char *name, int epoll_fd); +void ustcomm_del_named_sock(struct ustcomm_sock *sock, int keep_socket_file); + +/* Send and receive functions for file descriptors */ +int ustcomm_send_fd(int sock, const struct ustcomm_header *header, + const char *data, int *fd); +int ustcomm_recv_fd(int sock, struct ustcomm_header *header, + char *data, int *fd); + +/* Normal send and receive functions */ +int ustcomm_send(int sock, const struct ustcomm_header *header, + const char *data); +int ustcomm_recv(int sock, struct ustcomm_header *header, + char *data); + +/* Receive and allocate data, not to be used inside libust */ +int ustcomm_recv_alloc(int sock, struct ustcomm_header *header, char **data); + +/* Request function, send and receive */ +int ustcomm_req(int sock, + const struct ustcomm_header *req_header, + const char *req_data, + struct ustcomm_header *res_header, + char *res_data); + +int ustcomm_request_consumer(pid_t pid, const char *channel); + +/* Returns the current users socket directory, must be freed */ +char *ustcomm_user_sock_dir(void); + +/* Get the st_m_time from proc*/ +time_t ustcomm_pid_st_mtime(pid_t pid); + +/* Check that a socket is live */ +int ustcomm_is_socket_live(char *sock_name, pid_t *read_pid); + +int ustcomm_connect_app(pid_t pid, int *app_fd); +int ustcomm_connect_path(const char *path, int *connection_fd); + +/* String serialising functions, printf straight into a buffer */ +#define USTCOMM_POISON_PTR (void *)0x19831018 + +char *ustcomm_print_data(char *data_field, int field_size, + int *offset, const char *format, ...); +char *ustcomm_restore_ptr(char *ptr, char *data_field, int data_field_size); + +#define COMPUTE_MSG_SIZE(struct_ptr, offset) \ + (size_t) (long)(struct_ptr)->data - (long)(struct_ptr) + (offset) + +/* Packing and unpacking functions, making life easier */ +int ustcomm_pack_single_field(struct ustcomm_header *header, + struct ustcomm_single_field *sf, const char *trace); + +int ustcomm_unpack_single_field(struct ustcomm_single_field *sf); + +int ustcomm_pack_channel_info(struct ustcomm_header *header, + struct ustcomm_channel_info *ch_inf, + const char *trace, const char *channel); + +int ustcomm_unpack_channel_info(struct ustcomm_channel_info *ch_inf); + +int ustcomm_pack_buffer_info(struct ustcomm_header *header, + struct ustcomm_buffer_info *buf_inf, + const char *trace, + const char *channel, + int channel_cpu); + +int ustcomm_unpack_buffer_info(struct ustcomm_buffer_info *buf_inf); + +int ustcomm_pack_ust_marker_info(struct ustcomm_header *header, + struct ustcomm_ust_marker_info *ust_marker_inf, + const char *trace, + const char *channel, + const char *ust_marker); + +int ustcomm_unpack_ust_marker_info(struct ustcomm_ust_marker_info *ust_marker_inf); + +#endif /* _LTT_LIBUSTCOMM_H */ diff --git a/libustctl/Makefile.am b/libustctl/Makefile.am new file mode 100644 index 000000000..82c6c4002 --- /dev/null +++ b/libustctl/Makefile.am @@ -0,0 +1,9 @@ +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libustcomm +AM_CFLAGS = -fno-strict-aliasing + +lib_LTLIBRARIES = libustctl.la + +libustctl_la_SOURCES = libustctl.c + +libustctl_la_LIBADD = \ + $(top_builddir)/libustcomm/libustcomm.la diff --git a/libustctl/libustctl.c b/libustctl/libustctl.c new file mode 100644 index 000000000..f109eb4e8 --- /dev/null +++ b/libustctl/libustctl.c @@ -0,0 +1,775 @@ +/* + * Copyright (C) 2009 Pierre-Marc Fournier, Philippe Proulx-Barrette + * Copyright (C) 2011 Ericsson AB + * David Goulet + * + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include "libustcomm.h" +#include "libustctl.h" +#include "lttngerr.h" + +/* + * do_cmd + * + * Execute command using ustcomm API. + */ +static int do_cmd(int sock, const struct ustcomm_header *req_header, + const char *req_data, struct ustcomm_header *res_header, char **res_data) +{ + int result, saved_errno = 0; + char *recv_buf; + + recv_buf = malloc(USTCOMM_BUFFER_SIZE); + if (!recv_buf) { + saved_errno = ENOMEM; + goto out; + } + + result = ustcomm_req(sock, req_header, req_data, res_header, recv_buf); + if (result > 0) { + saved_errno = -res_header->result; + if (res_header->size == 0 || saved_errno > 0) { + free(recv_buf); + } else { + if (res_data) { + *res_data = recv_buf; + } else { + free(recv_buf); + } + } + } else { + ERR("ustcomm req failed"); + if (result == 0) { + saved_errno = ENOTCONN; + } else { + saved_errno = -result; + } + free(recv_buf); + } + +out: + errno = saved_errno; + if (errno) { + return -1; + } + + return 0; +} + +/* + * realloc_pid_list + */ +static int realloc_pid_list(pid_t **pid_list, unsigned int *pid_list_size) +{ + pid_t *new_pid_list; + unsigned int new_pid_list_size = 2 * (*pid_list_size); + + new_pid_list = realloc(*pid_list, new_pid_list_size * sizeof(pid_t)); + if (!*new_pid_list) { + return -1; + } + + *pid_list = new_pid_list; + *pid_list_size = new_pid_list_size; + + return 0; +} + +/* + * get_pids_in_dir + * + * Set pid_list with all pid in the dir. + */ +static int get_pids_in_dir(DIR *dir, pid_t **pid_list, + unsigned int *pid_list_index, unsigned int *pid_list_size) +{ + struct dirent *dirent; + pid_t read_pid; + + while ((dirent = readdir(dir))) { + if (!strcmp(dirent->d_name, ".") || + !strcmp(dirent->d_name, "..") || + !strcmp(dirent->d_name, "ust-consumer") || + dirent->d_type == DT_DIR) { + continue; + } + + if (ustcomm_is_socket_live(dirent->d_name, &read_pid)) { + (*pid_list)[(*pid_list_index)++] = (long) read_pid; + if (*pid_list_index == *pid_list_size) { + if (realloc_pid_list(pid_list, pid_list_size)) { + return -1; + } + } + } + } + + (*pid_list)[*pid_list_index] = 0; /* Array end */ + + return 0; +} + +/* + * get_pids_non_root + */ +static pid_t *get_pids_non_root(void) +{ + char *dir_name; + DIR *dir; + unsigned int pid_list_index = 0, pid_list_size = 1; + pid_t *pid_list = NULL; + + dir_name = ustcomm_user_sock_dir(); + if (!dir_name) { + return NULL; + } + + dir = opendir(dir_name); + if (!dir) { + goto free_dir_name; + } + + pid_list = malloc(pid_list_size * sizeof(pid_t)); + if (!pid_list) { + goto close_dir; + } + + if (get_pids_in_dir(dir, &pid_list, &pid_list_index, &pid_list_size)) { + /* if any errors are encountered, force freeing of the list */ + pid_list[0] = 0; + } + +close_dir: + closedir(dir); + +free_dir_name: + free(dir_name); + + return pid_list; +} + +/* + * get_pids_root + */ +static pid_t *get_pids_root(void) +{ + char *dir_name; + DIR *tmp_dir, *dir; + unsigned int pid_list_index = 0, pid_list_size = 1; + pid_t *pid_list = NULL; + struct dirent *dirent; + int result; + + tmp_dir = opendir(USER_TMP_DIR); + if (!tmp_dir) { + return NULL; + } + + pid_list = malloc(pid_list_size * sizeof(pid_t)); + if (!pid_list) { + goto close_tmp_dir; + } + + while ((dirent = readdir(tmp_dir))) { + /* Compare the dir to check for the USER_SOCK_DIR_BASE prefix */ + if (!strncmp(dirent->d_name, USER_SOCK_DIR_BASE, + strlen(USER_SOCK_DIR_BASE))) { + if (asprintf(&dir_name, USER_TMP_DIR "/%s", dirent->d_name) < 0) { + goto close_tmp_dir; + } + + dir = opendir(dir_name); + free(dir_name); + if (!dir) { + continue; + } + + result = get_pids_in_dir(dir, &pid_list, &pid_list_index, &pid_list_size); + closedir(dir); + if (result) { + /* + * if any errors are encountered, + * force freeing of the list + */ + pid_list[0] = 0; + break; + } + } + } + +close_tmp_dir: + closedir(tmp_dir); + return pid_list; +} + +/* + * ustctl_get_subbuf_num_size + */ +static int ustctl_get_subbuf_num_size(int sock, const char *trace, + const char *channel, int *num, int *size) +{ + struct ustcomm_header req_header, res_header; + struct ustcomm_channel_info ch_inf, *ch_inf_res; + int result; + + + result = ustcomm_pack_channel_info(&req_header, + &ch_inf, trace, channel); + if (result < 0) { + errno = -result; + return -1; + } + + req_header.command = GET_SUBBUF_NUM_SIZE; + + result = do_cmd(sock, &req_header, (char *)&ch_inf, + &res_header, (char **)&ch_inf_res); + if (result < 0) { + return -1; + } + + *num = ch_inf_res->subbuf_num; + *size = ch_inf_res->subbuf_size; + + free(ch_inf_res); + + return 0; +} + +/* + * do_trace_cmd + * + * Do all trace action command + */ +static int do_trace_cmd(int sock, const char *trace, int command) +{ + struct ustcomm_header req_header, res_header; + struct ustcomm_single_field trace_inf; + int result; + + result = ustcomm_pack_single_field(&req_header, &trace_inf, trace); + if (result < 0) { + errno = -result; + return -1; + } + + req_header.command = command; + + return do_cmd(sock, &req_header, (char *)&trace_inf, &res_header, NULL); +} + +/* + * ustctl_connect_pid + * + * Connect to PID + */ +int ustctl_connect_pid(pid_t pid) +{ + int sock; + + if (ustcomm_connect_app(pid, &sock)) { + ERR("could not connect to PID %u", (unsigned int) pid); + errno = ENOTCONN; + return -1; + } + + return sock; +} + +/* + * ustctl_get_online_pids + * + * Return list of online pids. + */ +pid_t *ustctl_get_online_pids(void) +{ + pid_t *pid_list; + + if (geteuid()) { + pid_list = get_pids_non_root(); + } else { + pid_list = get_pids_root(); + } + + if (pid_list && pid_list[0] == 0) { + /* No PID at all */ + free(pid_list); + pid_list = NULL; + } + + return pid_list; +} + +/* + * Sets ust_marker state (USTCTL_MS_ON or USTCTL_MS_OFF). + * + * @param mn Marker name + * @param state Marker's new state + * @param pid Traced process ID + * @return 0 if successful, or errors {USTCTL_ERR_GEN, USTCTL_ERR_ARG} + */ +int ustctl_set_marker_state(int sock, const char *trace, const char *channel, + const char *ust_marker, int state) +{ + struct ustcomm_header req_header, res_header; + struct ustcomm_ust_marker_info ust_marker_inf; + int result; + + result = ustcomm_pack_ust_marker_info(&req_header, &ust_marker_inf, + trace, channel, ust_marker); + if (result < 0) { + errno = -result; + return -1; + } + + req_header.command = state ? ENABLE_MARKER : DISABLE_MARKER; + + return do_cmd(sock, &req_header, (char *)&ust_marker_inf, &res_header, NULL); +} + +/* + * Set subbuffer size. + * + * @param channel_size Channel name and size + * @param pid Traced process ID + * @return 0 if successful, or error + */ +int ustctl_set_subbuf_size(int sock, const char *trace, const char *channel, + unsigned int subbuf_size) +{ + struct ustcomm_header req_header, res_header; + struct ustcomm_channel_info ch_inf; + int result; + + result = ustcomm_pack_channel_info(&req_header, &ch_inf, + trace, channel); + if (result < 0) { + errno = -result; + return -1; + } + + req_header.command = SET_SUBBUF_SIZE; + ch_inf.subbuf_size = subbuf_size; + + return do_cmd(sock, &req_header, (char *)&ch_inf, &res_header, NULL); +} + +/* + * Set subbuffer num. + * + * @param channel_num Channel name and num + * @param pid Traced process ID + * @return 0 if successful, or error + */ +int ustctl_set_subbuf_num(int sock, const char *trace, const char *channel, + unsigned int num) +{ + struct ustcomm_header req_header, res_header; + struct ustcomm_channel_info ch_inf; + int result; + + result = ustcomm_pack_channel_info(&req_header, + &ch_inf, trace, channel); + if (result < 0) { + errno = -result; + return -1; + } + + req_header.command = SET_SUBBUF_NUM; + ch_inf.subbuf_num = num; + + return do_cmd(sock, &req_header, (char *)&ch_inf, &res_header, NULL); + +} + +/* + * Get subbuffer num. + * + * @param channel Channel name + * @param pid Traced process ID + * @return subbuf cnf if successful, or error + */ +int ustctl_get_subbuf_num(int sock, const char *trace, const char *channel) +{ + int num, size, result; + + result = ustctl_get_subbuf_num_size(sock, trace, channel, &num, &size); + if (result < 0) { + errno = -result; + return -1; + } + + return num; +} + +/* + * Get subbuffer size. + * + * @param channel Channel name + * @param pid Traced process ID + * @return subbuf size if successful, or error + */ +int ustctl_get_subbuf_size(int sock, const char *trace, const char *channel) +{ + int num, size, result; + + result = ustctl_get_subbuf_num_size(sock, trace, channel, &num, &size); + if (result < 0) { + errno = -result; + return -1; + } + + return size; +} + +/* + * Destroys an UST trace according to a PID. + * + * @param pid Traced process ID + * @return 0 if successful, or error USTCTL_ERR_GEN + */ +int ustctl_destroy_trace(int sock, const char *trace) +{ + return do_trace_cmd(sock, trace, DESTROY_TRACE); +} + +/* + * Starts an UST trace (and setups it) according to a PID. + * + * @param pid Traced process ID + * @return 0 if successful, or error USTCTL_ERR_GEN + */ +int ustctl_setup_and_start(int sock, const char *trace) +{ + return do_trace_cmd(sock, trace, START); +} + +/* + * Creates an UST trace according to a PID. + * + * @param pid Traced process ID + * @return 0 if successful, or error USTCTL_ERR_GEN + */ +int ustctl_create_trace(int sock, const char *trace) +{ + return do_trace_cmd(sock, trace, CREATE_TRACE); +} + +/* + * Starts an UST trace according to a PID. + * + * @param pid Traced process ID + * @return 0 if successful, or error USTCTL_ERR_GEN + */ +int ustctl_start_trace(int sock, const char *trace) +{ + return do_trace_cmd(sock, trace, START_TRACE); +} + +/* + * Alloc an UST trace according to a PID. + * + * @param pid Traced process ID + * @return 0 if successful, or error USTCTL_ERR_GEN + */ +int ustctl_alloc_trace(int sock, const char *trace) +{ + return do_trace_cmd(sock, trace, ALLOC_TRACE); +} + +/* + * ustctl_force_switch + * + * Force switch buffer in libust + */ +int ustctl_force_switch(int sock, const char *trace) +{ + return do_trace_cmd(sock, trace, FORCE_SUBBUF_SWITCH); +} + +/* + * Stops an UST trace according to a PID. + * + * @param pid Traced process ID + * @return 0 if successful, or error USTCTL_ERR_GEN + */ +int ustctl_stop_trace(int sock, const char *trace) +{ + return do_trace_cmd(sock, trace, STOP_TRACE); +} + +/* + * Counts newlines ('\n') in a string. + * + * @param str String to search in + * @return Total newlines count + */ +unsigned int ustctl_count_nl(const char *str) +{ + unsigned int i = 0, tot = 0; + + while (str[i] != '\0') { + if (str[i] == '\n') { + ++tot; + } + ++i; + } + + return tot; +} + +/* + * Frees a CMSF array. + * + * @param cmsf CMSF array to free + * @return 0 if successful, or error USTCTL_ERR_ARG + */ +int ustctl_free_cmsf(struct ustctl_marker_status *cmsf) +{ + if (cmsf == NULL) { + return -1; + } + + unsigned int i = 0; + while (cmsf[i].channel != NULL) { + free(cmsf[i].channel); + free(cmsf[i].ust_marker); + free(cmsf[i].fs); + ++i; + } + free(cmsf); + + return 0; +} + +/* + * Gets channel/ust_marker/state/format string for a given PID. + * + * @param cmsf Pointer to CMSF array to be filled (callee allocates, caller + * frees with `ustctl_free_cmsf') + * @param pid Targeted PID + * @return 0 if successful, or -1 on error + */ +int ustctl_get_cmsf(int sock, struct ustctl_marker_status **cmsf) +{ + struct ustcomm_header req_header, res_header; + char *big_str = NULL; + int result; + struct ustctl_marker_status *tmp_cmsf = NULL; + unsigned int i = 0, cmsf_ind = 0; + + if (cmsf == NULL) { + return -1; + } + + req_header.command = LIST_MARKERS; + req_header.size = 0; + + result = ustcomm_send(sock, &req_header, NULL); + if (result <= 0) { + ERR("error while requesting ust_marker list"); + return -1; + } + + result = ustcomm_recv_alloc(sock, &res_header, &big_str); + if (result <= 0) { + ERR("error while receiving ust_marker list"); + return -1; + } + + tmp_cmsf = (struct ustctl_marker_status *) malloc(sizeof(struct ustctl_marker_status) * + (ustctl_count_nl(big_str) + 1)); + if (tmp_cmsf == NULL) { + ERR("Failed to allocate CMSF array"); + return -1; + } + + /* Parse received reply string (format: "[chan]/[mark] [st] [fs]"): */ + while (big_str[i] != '\0') { + char state; + + sscanf(big_str + i, "ust_marker: %a[^/]/%a[^ ] %c %a[^\n]", + &tmp_cmsf[cmsf_ind].channel, + &tmp_cmsf[cmsf_ind].ust_marker, + &state, + &tmp_cmsf[cmsf_ind].fs); + tmp_cmsf[cmsf_ind].state = (state == USTCTL_MS_CHR_ON ? + USTCTL_MS_ON : USTCTL_MS_OFF); /* Marker state */ + + while (big_str[i] != '\n') { + ++i; /* Go to next '\n' */ + } + ++i; /* Skip current pointed '\n' */ + ++cmsf_ind; + } + tmp_cmsf[cmsf_ind].channel = NULL; + tmp_cmsf[cmsf_ind].ust_marker = NULL; + tmp_cmsf[cmsf_ind].fs = NULL; + + *cmsf = tmp_cmsf; + + free(big_str); + return 0; +} + +/* + * Frees a TES array. + * + * @param tes TES array to free + * @return 0 if successful, or error USTCTL_ERR_ARG + */ +int ustctl_free_tes(struct ustctl_trace_event_status *tes) +{ + if (tes == NULL) { + return USTCTL_ERR_ARG; + } + + unsigned int i = 0; + while (tes[i].name != NULL) { + free(tes[i].name); + ++i; + } + free(tes); + + return 0; +} + +/* + * Gets trace_events string for a given PID. + * + * @param tes Pointer to TES array to be filled (callee allocates, caller + * frees with `ustctl_free_tes') + * @param pid Targeted PID + * @return 0 if successful, or -1 on error + */ +int ustctl_get_tes(int sock, struct ustctl_trace_event_status **tes) +{ + struct ustcomm_header req_header, res_header; + char *big_str = NULL; + int result; + struct ustctl_trace_event_status *tmp_tes = NULL; + unsigned int i = 0, tes_ind = 0; + + if (tes == NULL) { + return -1; + } + + req_header.command = LIST_TRACE_EVENTS; + req_header.size = 0; + + result = ustcomm_send(sock, &req_header, NULL); + if (result != 1) { + ERR("error while requesting trace_event list"); + return -1; + } + + result = ustcomm_recv_alloc(sock, &res_header, &big_str); + if (result != 1) { + ERR("error while receiving ust_marker list"); + return -1; + } + + tmp_tes = (struct ustctl_trace_event_status *) + malloc(sizeof(struct ustctl_trace_event_status) * + (ustctl_count_nl(big_str) + 1)); + if (tmp_tes == NULL) { + ERR("Failed to allocate TES array"); + return -1; + } + + /* Parse received reply string (format: "[name]"): */ + while (big_str[i] != '\0') { + sscanf(big_str + i, "trace_event: %a[^\n]", &tmp_tes[tes_ind].name); + while (big_str[i] != '\n') { + ++i; /* Go to next '\n' */ + } + ++i; /* Skip current pointed '\n' */ + ++tes_ind; + } + tmp_tes[tes_ind].name = NULL; + + *tes = tmp_tes; + + free(big_str); + return 0; +} + +/* + * Set sock path + * + * @param sock_path Sock path + * @param pid Traced process ID + * @return 0 if successful, or error + */ +int ustctl_set_sock_path(int sock, const char *sock_path) +{ + int result; + struct ustcomm_header req_header, res_header; + struct ustcomm_single_field sock_path_msg; + + result = ustcomm_pack_single_field(&req_header, &sock_path_msg, sock_path); + if (result < 0) { + errno = -result; + return -1; + } + + req_header.command = SET_SOCK_PATH; + + return do_cmd(sock, &req_header, (char *)&sock_path_msg, &res_header, NULL); +} + +/* + * Get sock path + * + * @param sock_path Pointer to where the sock path will be returned + * @param pid Traced process ID + * @return 0 if successful, or error + */ +int ustctl_get_sock_path(int sock, char **sock_path) +{ + int result; + struct ustcomm_header req_header, res_header; + struct ustcomm_single_field *sock_path_msg; + + req_header.command = GET_SOCK_PATH; + req_header.size = 0; + + result = do_cmd(sock, &req_header, NULL, &res_header, + (char **)&sock_path_msg); + if (result < 0) { + return -1; + } + + result = ustcomm_unpack_single_field(sock_path_msg); + if (result < 0) { + return result; + } + + *sock_path = strdup(sock_path_msg->field); + + free(sock_path_msg); + + return 0; +} diff --git a/libustctl/libustctl.h b/libustctl/libustctl.h new file mode 100644 index 000000000..420d55409 --- /dev/null +++ b/libustctl/libustctl.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2009 Pierre-Marc Fournier + * 2011 David Goulet + * + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _LTT_LIBUSTCTL_H +#define _LTT_LIBUSTCTL_H + +#define USTCTL_ERR_CONN 1 /* Process connection error */ +#define USTCTL_ERR_ARG 2 /* Invalid function argument */ +#define USTCTL_ERR_GEN 3 /* General ustctl error */ + +#define USTCTL_MS_CHR_OFF '0' /* Marker state 'on' character */ +#define USTCTL_MS_CHR_ON '1' /* Marker state 'on' character */ +#define USTCTL_MS_OFF 0 /* Marker state 'on' value */ +#define USTCTL_MS_ON 1 /* Marker state 'on' value */ + +/* + * Channel/marker/state/format string (cmsf) data structure + */ +struct ustctl_marker_status { + char *channel; /* Channel name (end of ustctl_marker_status array if NULL) */ + char *ust_marker; /* Marker name (end of ustctl_marker_status array if NULL) */ + int state; /* State (0 := marker disabled, 1 := marker enabled) */ + char *fs; /* Format string (end of ustctl_marker_status array if NULL) */ +}; + +struct ustctl_trace_event_status { + char *name; +}; + +pid_t *ustctl_get_online_pids(void); + +int ustctl_alloc_trace(int sock, const char *trace); +int ustctl_connect_pid(pid_t pid); +int ustctl_create_trace(int sock, const char *trace); +int ustctl_destroy_trace(int sock, const char *trace); +int ustctl_force_switch(int sock, const char *trace); +int ustctl_free_cmsf(struct ustctl_marker_status *cmsf); +int ustctl_free_tes(struct ustctl_trace_event_status *tes); +int ustctl_get_cmsf(int sock, struct ustctl_marker_status **cmsf); +int ustctl_get_sock_path(int sock, char **sock_path); +int ustctl_get_subbuf_num(pid_t pid, const char *trace, const char *channel); +int ustctl_get_subbuf_size(int sock, const char *trace, const char *channel); +int ustctl_get_tes(int sock, struct ustctl_trace_event_status **tes); +int ustctl_set_sock_path(int sock, const char *sock_path); + +int ustctl_set_marker_state(int sock, const char *trace, + const char *channel, const char *ust_marker, int state); + +int ustctl_set_subbuf_size(int sock, const char *trace, + const char *channel, unsigned int subbuf_size); + +int ustctl_set_subbuf_num(int sock, const char *trace, + const char *channel, unsigned int num); + +int ustctl_setup_and_start(int sock, const char *trace); +int ustctl_start_trace(int sock, const char *trace); +int ustctl_stop_trace(int sock, const char *trace); +unsigned int ustctl_count_nl(const char *str); + +#endif /* _LTT_LIBUSTCTL_H */ diff --git a/ltt-sessiond/Makefile.am b/ltt-sessiond/Makefile.am index b7200064f..b655380d3 100644 --- a/ltt-sessiond/Makefile.am +++ b/ltt-sessiond/Makefile.am @@ -1,4 +1,6 @@ -AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/liblttsessiondcomm -I$(top_srcdir)/libkernelctl +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/liblttsessiondcomm \ + -I$(top_srcdir)/libkernelctl -I$(top_srcdir)/libustctl + AM_CFLAGS = -fno-strict-aliasing bin_PROGRAMS = ltt-sessiond @@ -7,4 +9,5 @@ ltt_sessiond_SOURCES = trace.c session.c traceable-app.c ust-ctl.c kernel-ctl.c ltt_sessiond_LDADD = \ $(top_builddir)/liblttsessiondcomm/liblttsessiondcomm.la \ - $(top_builddir)/libkernelctl/libkernelctl.la + $(top_builddir)/libkernelctl/libkernelctl.la \ + $(top_builddir)/libustctl/libustctl.la diff --git a/ltt-sessiond/main.c b/ltt-sessiond/main.c index e7ad6f9b1..8c113fab3 100644 --- a/ltt-sessiond/main.c +++ b/ltt-sessiond/main.c @@ -35,7 +35,6 @@ #include #include /* URCU list library (-lurcu) */ -#include /* UST control lib (-lust) */ #include #include "liblttsessiondcomm.h" @@ -46,6 +45,7 @@ #include "session.h" #include "traceable-app.h" #include "lttng-kconsumerd.h" +#include "libustctl.h" /* * TODO: @@ -224,7 +224,7 @@ static int ust_connect_app(pid_t pid) sock = ustctl_connect_pid(lta->pid); if (sock < 0) { - ERR("Fail connecting to the PID %d\n", pid); + ERR("Fail connecting to the PID %d", pid); } return sock; diff --git a/ltt-sessiond/ust-ctl.c b/ltt-sessiond/ust-ctl.c index 2ec168e93..a23d3c5ac 100644 --- a/ltt-sessiond/ust-ctl.c +++ b/ltt-sessiond/ust-ctl.c @@ -23,8 +23,7 @@ #include #include -#include - +#include "libustctl.h" #include "liblttsessiondcomm.h" #include "lttngerr.h" #include "ust-ctl.h" -- 2.34.1