X-Git-Url: http://git.liburcu.org/?a=blobdiff_plain;f=libustcomm%2Fustcomm.c;h=50038cba157cdc964a553c718a22a6e38b6217c6;hb=4723ca096d740ff93da400df304c9902e9834e5f;hp=6047b6012bc5f5abb98c45c2e28a4038b6b438a6;hpb=688760ef257bee85e3841e0d27ecf4e2f921ef00;p=ust.git diff --git a/libustcomm/ustcomm.c b/libustcomm/ustcomm.c index 6047b60..50038cb 100644 --- a/libustcomm/ustcomm.c +++ b/libustcomm/ustcomm.c @@ -1,3 +1,22 @@ +/* Copyright (C) 2009 Pierre-Marc Fournier + * + * 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 + */ + +/* API used by UST components to communicate with each other via sockets. */ + #define _GNU_SOURCE #include #include @@ -6,6 +25,8 @@ #include #include #include +#include +#include #include #include @@ -13,418 +34,637 @@ #include #include "ustcomm.h" -#include "localerr.h" - -#define UNIX_PATH_MAX 108 -#define SOCK_DIR "/tmp/socks" -#define UST_SIGNAL SIGIO - -#define MSG_MAX 1000 +#include "usterr.h" +#include "share.h" -/* FIXME: ustcomm blocks on message sending, which might be problematic in - * some cases. Fix the poll() usage so sends are buffered until they don't - * block. - */ - -//static void bt(void) -//{ -// void *buffer[100]; -// int result; -// -// result = backtrace(&buffer, 100); -// backtrace_symbols_fd(buffer, result, STDERR_FILENO); -//} - -char *strdup_malloc(const char *s) +static int mkdir_p(const char *path, mode_t mode) { - char *retval; + const char *path_p; + char *tmp; - if(s == NULL) - return NULL; + int retval = 0; + int result; + mode_t old_umask; + + tmp = zmalloc(strlen(path) + 1); + if (tmp == NULL) + return -1; - retval = (char *) malloc(strlen(s)+1); + /* skip first / */ + path_p = path+1; - strcpy(retval, s); + old_umask = umask(0); + for(;;) { + while (*path_p != '/') { + if(*path_p == 0) + break; + ++path_p; + } + if (*path_p == '/') { + strncpy(tmp, path, path_p - path); + tmp[path_p-path] = '\0'; + if (tmp[path_p - path - 1] != '/') { + result = mkdir(tmp, mode); + if(result == -1) { + if (!(errno == EEXIST || errno == EACCES || errno == EROFS)) { + /* Then this is a real error */ + retval = -1; + break; + } + } + } + /* pass / */ + path_p++; + } else { + /* last component */ + result = mkdir(path, mode); + if (result == -1) + retval = -1; + break; + } + } + free(tmp); + umask(old_umask); return retval; } -static void signal_process(pid_t pid) +static struct sockaddr_un * create_sock_addr(const char *name, + size_t *sock_addr_size) { - int result; + struct sockaddr_un * addr; + size_t alloc_size; - result = kill(pid, UST_SIGNAL); - if(result == -1) { - PERROR("kill"); - return; + 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; } - sleep(1); + addr->sun_family = AF_UNIX; + strcpy(addr->sun_path, name); + + *sock_addr_size = alloc_size; + + return addr; } -int send_message_fd(int fd, const char *msg, char **reply) +struct ustcomm_sock * ustcomm_init_sock(int fd, int epoll_fd, + struct list_head *list) { - int result; + struct epoll_event ev; + struct ustcomm_sock *sock; - result = send(fd, msg, strlen(msg), 0); - if(result == -1) { - PERROR("send"); - return -1; - } - else if(result == 0) { - return 0; + sock = malloc(sizeof(struct ustcomm_sock)); + if (!sock) { + perror("malloc: couldn't allocate ustcomm_sock"); + return NULL; } - if(!reply) - return 1; + ev.events = EPOLLIN; + ev.data.ptr = sock; + sock->fd = fd; - *reply = (char *) malloc(MSG_MAX+1); - result = recv(fd, *reply, MSG_MAX, 0); - if(result == -1) { - PERROR("recv"); - return -1; + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock->fd, &ev) == -1) { + perror("epoll_ctl: failed to add socket\n"); + free(sock); + return NULL; } - else if(result == 0) { - return 0; + + sock->epoll_fd = epoll_fd; + if (list) { + list_add(&sock->list, list); + } else { + INIT_LIST_HEAD(&sock->list); } - - (*reply)[result] = '\0'; - return 1; + return sock; } -int send_message_path(const char *path, const char *msg, char **reply, int signalpid) +void ustcomm_del_sock(struct ustcomm_sock *sock, int keep_in_epoll) +{ + 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); +} + +struct ustcomm_sock * ustcomm_init_named_socket(const char *name, + int epoll_fd) { - int fd; int result; - struct sockaddr_un addr; + int fd; + size_t sock_addr_size; + struct sockaddr_un * addr; + struct ustcomm_sock *sock; - result = fd = socket(PF_UNIX, SOCK_STREAM, 0); - if(result == -1) { + fd = socket(PF_UNIX, SOCK_STREAM, 0); + if(fd == -1) { PERROR("socket"); - return -1; + return NULL; } - addr.sun_family = AF_UNIX; + addr = create_sock_addr(name, &sock_addr_size); + if (addr == NULL) { + ERR("allocating addr, UST thread bailing"); + goto close_sock; + } - result = snprintf(addr.sun_path, UNIX_PATH_MAX, "%s", path); - if(result >= UNIX_PATH_MAX) { - ERR("string overflow allocating socket name"); - return -1; + 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"); } - if(signalpid >= 0) - signal_process(signalpid); + result = bind(fd, (struct sockaddr *)addr, sock_addr_size); + if(result == -1) { + PERROR("bind"); + goto free_addr; + } - result = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); + result = listen(fd, 1); if(result == -1) { - PERROR("connect"); - return -1; + PERROR("listen"); + goto free_addr; } - return send_message_fd(fd, msg, reply); -} + sock = ustcomm_init_sock(fd, epoll_fd, + NULL); + if (!sock) { + ERR("failed to create ustcomm_sock"); + goto free_addr; + } -/* pid: the pid of the trace process that must receive the msg - msg: pointer to a null-terminated message to send - reply: location where to put the null-terminated string of the reply; - it must be free'd after usage - */ + free(addr); + + return sock; + +free_addr: + free(addr); +close_sock: + close(fd); + + return NULL; +} -int send_message(pid_t pid, const char *msg, char **reply) +void ustcomm_del_named_sock(struct ustcomm_sock *sock, + int keep_socket_file) { - int result; - char path[UNIX_PATH_MAX]; + int result, fd; + struct stat st; + struct sockaddr dummy; + struct sockaddr_un *sockaddr = NULL; + int alloc_size; - result = snprintf(path, UNIX_PATH_MAX, "%s/%d", SOCK_DIR, pid); - if(result >= UNIX_PATH_MAX) { - fprintf(stderr, "string overflow allocating socket name"); - return -1; + 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 = zmalloc(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) { + PERROR("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"); + } } - send_message_path(path, msg, reply, pid); +free_sockaddr: + free(sockaddr); - return 0; +del_sock: + ustcomm_del_sock(sock, keep_socket_file); } + /* Called by an app to ask the consumer daemon to connect to it. */ int ustcomm_request_consumer(pid_t pid, const char *channel) { - char path[UNIX_PATH_MAX]; - int result; - char *msg; - - result = snprintf(path, UNIX_PATH_MAX, "%s/ustd", SOCK_DIR); - if(result >= UNIX_PATH_MAX) { - fprintf(stderr, "string overflow allocating socket name"); + int result, daemon_fd; + int retval = 0; + char *msg=NULL; + char *explicit_daemon_socket_path, *daemon_path; + + explicit_daemon_socket_path = getenv("UST_DAEMON_SOCKET"); + if (explicit_daemon_socket_path) { + /* user specified explicitly a socket path */ + result = asprintf(&daemon_path, "%s", explicit_daemon_socket_path); + } else { + /* just use the default path */ + result = asprintf(&daemon_path, "%s/ustd", SOCK_DIR); + } + if (result < 0) { + ERR("string overflow allocating socket name"); return -1; } - asprintf(&msg, "collect %d %s", pid, channel); + if (asprintf(&msg, "collect %d %s", pid, channel) < 0) { + ERR("ustcomm_request_consumer : asprintf failed (collect %d/%s)", + pid, channel); + retval = -1; + goto free_daemon_path; + } + + result = ustcomm_connect_path(daemon_path, &daemon_fd); + if (result < 0) { + WARN("ustcomm_connect_path failed, daemon_path: %s", + daemon_path); + retval = -1; + goto del_string; + } - send_message_path(path, msg, NULL, -1); + result = ustcomm_send_request(daemon_fd, msg, NULL); + if (result < 0) { + WARN("ustcomm_send_request failed, daemon path: %s", + daemon_path); + retval = -1; + } + + close(daemon_fd); +del_string: free(msg); +free_daemon_path: + free(daemon_path); - return 0; + return retval; } /* returns 1 to indicate a message was received - * returns 0 to indicate no message was received (cannot happen) + * returns 0 to indicate no message was received (end of stream) * returns -1 to indicate an error */ - -static int recv_message_fd(int fd, char **msg, struct ustcomm_source *src) +int ustcomm_recv_fd(int sock, + struct ustcomm_header *header, + char **data, int *fd) { int result; + int retval; + 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; + } - *msg = (char *) malloc(MSG_MAX+1); + memset(&msg, 0, sizeof(msg)); - result = recv(fd, *msg, MSG_MAX, 0); - if(result == -1) { - PERROR("recv"); - return -1; + 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) { + if (peek_header.size < 0 || peek_header.size > 100) { + WARN("big peek header! %d", peek_header.size); + } + *data = malloc(peek_header.size); + if (!*data) { + ERR("failed to allocate space for message"); + } + + iov[1].iov_base = (char *)*data; + iov[1].iov_len = peek_header.size; + + msg.msg_iovlen++; } - (*msg)[result] = '\0'; - - DBG("ustcomm_app_recv_message: result is %d, message is %s", result, (*msg)); + if (fd && peek_header.fd_included) { + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + } - if(src) - src->fd = fd; + result = recvmsg(sock, &msg, + MSG_WAITALL); + + if (result <= 0) { + if(errno == ECONNRESET) { + retval = 0; + } else if (errno == EINTR) { + retval = -1; + } else if (result < 0) { + PERROR("recv"); + retval = -1; + } else { + retval = 0; + } + free(*data); + return retval; + } + + 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; } -int ustcomm_send_reply(struct ustcomm_server *server, char *msg, struct ustcomm_source *src) +int ustcomm_recv(int sock, + struct ustcomm_header *header, + char **data) { - int result; + return ustcomm_recv_fd(sock, header, data, NULL); +} - result = send_message_fd(src->fd, msg, NULL); - if(result) { - ERR("error in send_message_fd"); - return -1; - } - return 0; -} +int recv_message_conn(int sock, char **msg) +{ + struct ustcomm_header header; -/* @timeout: max blocking time in milliseconds, -1 means infinity - * - * returns 1 to indicate a message was received - * returns 0 to indicate no message was received - * returns -1 to indicate an error - */ + return ustcomm_recv(sock, &header, msg); +} -int ustcomm_recv_message(struct ustcomm_server *server, char **msg, struct ustcomm_source *src, int timeout) +int ustcomm_send_fd(int sock, + const struct ustcomm_header *header, + const char *data, + int *fd) { - struct pollfd *fds; - struct ustcomm_connection *conn; + struct iovec iov[2]; + struct msghdr msg; int result; - int retval; + struct cmsghdr *cmsg; + char buf[CMSG_SPACE(sizeof(int))]; - for(;;) { - int idx = 0; - int n_fds = 1; + memset(&msg, 0, sizeof(msg)); - list_for_each_entry(conn, &server->connections, list) { - n_fds++; - } + iov[0].iov_base = (char *)header; + iov[0].iov_len = sizeof(struct ustcomm_header); - fds = (struct pollfd *) malloc(n_fds * sizeof(struct pollfd)); - if(fds == NULL) { - ERR("malloc returned NULL"); - return -1; - } + msg.msg_iov = iov; + msg.msg_iovlen = 1; - /* special idx 0 is for listening socket */ - fds[idx].fd = server->listen_fd; - fds[idx].events = POLLIN; - idx++; + if (header->size) { + iov[1].iov_base = (char *)data; + iov[1].iov_len = header->size; - list_for_each_entry(conn, &server->connections, list) { - fds[idx].fd = conn->fd; - fds[idx].events = POLLIN; - idx++; - } + msg.msg_iovlen++; - result = poll(fds, n_fds, timeout); - if(result == -1) { - PERROR("poll"); - return -1; - } + } - if(result == 0) - return 0; + 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; + } - if(fds[0].revents) { - struct ustcomm_connection *newconn; - int newfd; + result = sendmsg(sock, &msg, MSG_NOSIGNAL); + if (result < 0 && errno != EPIPE) { + PERROR("sendmsg failed"); + } + return result; +} - result = newfd = accept(server->listen_fd, NULL, NULL); - if(result == -1) { - PERROR("accept"); - return -1; - } +int ustcomm_send(int sock, + const struct ustcomm_header *header, + const char *data) +{ + return ustcomm_send_fd(sock, header, data, NULL); +} - newconn = (struct ustcomm_connection *) malloc(sizeof(struct ustcomm_connection)); - if(newconn == NULL) { - ERR("malloc returned NULL"); - return -1; - } +int ustcomm_send_reply(char *msg, int sock) +{ + int result; + struct ustcomm_header header; - newconn->fd = newfd; + memset(&header, 0, sizeof(header)); - list_add(&newconn->list, &server->connections); - } + header.size = strlen(msg) + 1; - for(idx=1; idxconnections, list) { - if(conn->fd == fds[idx].fd) { - list_del(&conn->list); - break; - } - } - } - else { - goto free_fds_return; - } - } - } - - free(fds); + result = ustcomm_send(sock, &header, msg); + if(result < 0) { + ERR("error in ustcomm_send"); + return result; } -free_fds_return: - free(fds); - return retval; + return 0; } -int ustcomm_ustd_recv_message(struct ustcomm_ustd *ustd, char **msg, struct ustcomm_source *src, int timeout) +int ustcomm_send_req(int sock, + const struct ustcomm_header *req_header, + const char *data, + char **response) { - return ustcomm_recv_message(&ustd->server, msg, src, timeout); + int result; + struct ustcomm_header res_header; + + result = ustcomm_send(sock, req_header, data); + if ( result <= 0) { + return result; + } + + if (!response) { + return 1; + } + + return ustcomm_recv(sock, + &res_header, + response); + } -int ustcomm_app_recv_message(struct ustcomm_app *app, char **msg, struct ustcomm_source *src, int timeout) +/* + * Return value: + * 0: Success, but no reply because recv() returned 0 + * 1: Success + * -1: Error + * + * On error, the error message is printed, except on + * ECONNRESET, which is normal when the application dies. + */ + +int ustcomm_send_request(int sock, const char *req, char **reply) { - return ustcomm_recv_message(&app->server, msg, src, timeout); + struct ustcomm_header req_header; + + req_header.size = strlen(req) + 1; + + return ustcomm_send_req(sock, + &req_header, + req, + reply); + } -static int init_named_socket(char *name, char **path_out) +/* Return value: + * 0: success + * -1: error + */ + +int ustcomm_connect_path(const char *name, int *connection_fd) { - int result; - int fd; + int result, fd; + size_t sock_addr_size; + struct sockaddr_un *addr; - struct sockaddr_un addr; - - result = fd = socket(PF_UNIX, SOCK_STREAM, 0); - if(result == -1) { + fd = socket(PF_UNIX, SOCK_STREAM, 0); + if(fd == -1) { PERROR("socket"); return -1; } - addr.sun_family = AF_UNIX; - - strncpy(addr.sun_path, name, UNIX_PATH_MAX); - addr.sun_path[UNIX_PATH_MAX-1] = '\0'; - - result = access(name, F_OK); - if(result == 0) { - /* file exists */ - result = unlink(name); - if(result == -1) { - PERROR("unlink of socket file"); - goto close_sock; - } - WARN("socket already exists; overwriting"); - } - - result = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); - if(result == -1) { - PERROR("bind"); + addr = create_sock_addr(name, &sock_addr_size); + if (addr == NULL) { + ERR("allocating addr failed"); goto close_sock; } - result = listen(fd, 1); + result = connect(fd, (struct sockaddr *)addr, sock_addr_size); if(result == -1) { - PERROR("listen"); - goto close_sock; + PERROR("connect (path=%s)", name); + goto free_sock_addr; } - if(path_out) { - *path_out = ""; - *path_out = strdupa(addr.sun_path); - } + *connection_fd = fd; - return fd; + free(addr); - close_sock: + return 0; + +free_sock_addr: + free(addr); +close_sock: close(fd); return -1; } -int ustcomm_init_app(pid_t pid, struct ustcomm_app *handle) + +/* Open a connection to a traceable app. + * + * Return value: + * 0: success + * -1: error + */ + +int ustcomm_connect_app(pid_t pid, int *app_fd) { int result; + int retval = 0; char *name; - result = asprintf(&name, "%s/%d", SOCK_DIR, (int)pid); - if(result >= UNIX_PATH_MAX) { - ERR("string overflow allocating socket name"); + result = asprintf(&name, "%s/%d", SOCK_DIR, pid); + if (result < 0) { + ERR("failed to allocate socket name"); return -1; } - handle->server.listen_fd = init_named_socket(name, &(handle->server.socketpath)); - if(handle->server.listen_fd < 0) { - ERR("error initializing named socket"); - goto free_name; + result = ustcomm_connect_path(name, app_fd); + if (result < 0) { + ERR("failed to connect to app"); + retval = -1; } - free(name); - - INIT_LIST_HEAD(&handle->server.connections); - - return 0; -free_name: free(name); - return -1; + + return retval; } -int ustcomm_init_ustd(struct ustcomm_ustd *handle) +int ensure_dir_exists(const char *dir) { + struct stat st; int result; - char *name; - result = asprintf(&name, "%s/%s", SOCK_DIR, "ustd"); - if(result >= UNIX_PATH_MAX) { - ERR("string overflow allocating socket name"); + if(!strcmp(dir, "")) return -1; - } - handle->server.listen_fd = init_named_socket(name, &handle->server.socketpath); - if(handle->server.listen_fd < 0) { - ERR("error initializing named socket"); - goto free_name; + result = stat(dir, &st); + if(result == -1 && errno != ENOENT) { + return -1; + } + else if(result == -1) { + /* ENOENT */ + int result; + + /* mkdir mode to 0777 */ + result = mkdir_p(dir, S_IRWXU | S_IRWXG | S_IRWXO); + if(result != 0) { + ERR("executing in recursive creation of directory %s", dir); + return -1; + } } - free(name); - - INIT_LIST_HEAD(&handle->server.connections); return 0; - -free_name: - free(name); - return -1; } -static char *find_tok(char *str) +/* Used by the daemon to initialize its server so applications + * can connect to it. + */ + + +static const char *find_tok(const char *str) { while(*str == ' ') { str++; @@ -436,7 +676,7 @@ static char *find_tok(char *str) return str; } -static char *find_sep(char *str) +static const char *find_sep(const char *str) { while(*str != ' ') { str++; @@ -448,11 +688,11 @@ static char *find_sep(char *str) return str; } -int nth_token_is(char *str, char *token, int tok_no) +int nth_token_is(const char *str, const char *token, int tok_no) { int i; - char *start; - char *end; + const char *start; + const char *end; for(i=0; i<=tok_no; i++) { str = find_tok(str); @@ -477,12 +717,12 @@ int nth_token_is(char *str, char *token, int tok_no) return 1; } -char *nth_token(char *str, int tok_no) +char *nth_token(const char *str, int tok_no) { static char *retval = NULL; int i; - char *start; - char *end; + const char *start; + const char *end; for(i=0; i<=tok_no; i++) { str = find_tok(str); @@ -503,8 +743,11 @@ char *nth_token(char *str, int tok_no) retval = NULL; } - asprintf(&retval, "%.*s", (int)(end-start), start); + if (asprintf(&retval, "%.*s", (int)(end-start), start) < 0) { + ERR("nth_token : asprintf failed (%.*s)", + (int)(end-start), start); + return NULL; + } return retval; } -