/* * remote TCP control connection * * Copyright (c) 2006 Daniel Veillard * * 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "vl.h" #ifdef CONFIG_TCPCTRL #include #include #include #include /* #define DEBUG_TCPCTRL */ static int tcpctrl_fd = -1; static char tcpctrl_socket[150] = ""; typedef struct _tcp_client { int fd; CharDriverState drv; } tcp_client; static int chrctr_write(struct CharDriverState *s, const uint8_t *buf, int len) { tcp_client *info; int written, sent = 0; if ((s == NULL) || (buf == NULL) || (len < 0) || (s->opaque == NULL)) return(-1); if (tcpctrl_fd == -1) return(-1); info = s->opaque; #ifdef DEBUG_TCPCTRL fprintf(stderr, "chrctr_write(fd = %d, len = %d)\n", info->fd, len); #endif retry_write: written = write(info->fd, buf + sent, len - sent); if (written < 0) { if ((errno == EINTR) || (errno == EAGAIN)) goto retry_write; close(info->fd); qemu_set_fd_handler(info->fd, NULL, NULL, NULL); info->fd = -1; return(-1); } if (written + sent < len) { sent += written; goto retry_write; } return(written); } static void tcpctrl_disconnect(tcp_client *info) { if ((info == NULL) || (info->fd < 0)) return; #ifdef DEBUG_TCPCTRL fprintf(stderr, "tcpctrl_disconnect(fd = %d)\n", info->fd); #endif /* * this seems the best way to unregister this should work * even in the loop over iov in the main loop */ if (info->fd > 0) { qemu_set_fd_handler(info->fd, NULL, NULL, NULL); close(info->fd); } qemu_free(info); } static int tcpctrl_process(tcp_client *info, const char *command) { if (tcpctrl_fd == -1) return(-1); #ifdef DEBUG_TCPCTRL fprintf(stderr, "tcpctrl_process(command = '%s')\n", command); #endif monitor_process_command(&info->drv, command); /* writing back to tcp connection may have failed */ if (info->fd < 0) tcpctrl_disconnect(info); } static void tcpctrl_read(void *opaque) { char buffer[4096 + 1]; int ret; tcp_client *info = opaque; #ifdef DEBUG_TCPCTRL fprintf(stderr, "tcpctrl_read(fd = %d)\n", info->fd); #endif if (tcpctrl_fd == -1) { tcpctrl_disconnect(info); return; } retry_read: ret = read(info->fd, &buffer[0], sizeof(buffer) - 1); if (ret < 0) { if ((errno == EINTR) || (errno == EAGAIN)) goto retry_read; tcpctrl_disconnect(info); return; } if (ret == 0) { tcpctrl_disconnect(info); return; } buffer[ret] = 0; tcpctrl_process(info, &buffer[0]); } static void tcpctrl_accept(void *opaque) { struct sockaddr_in sockaddr; socklen_t len; int val, fd; tcp_client *info; #ifdef DEBUG_TCPCTRL fprintf(stderr, "tcpctrl_accept()\n"); #endif for(;;) { len = sizeof(sockaddr); fd = accept(tcpctrl_fd, (struct sockaddr *)&sockaddr, &len); if (fd < 0 && errno != EINTR) { perror("accept"); return; } else if (fd >= 0) { break; } } /* set short latency */ val = 1; setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); info = qemu_mallocz(sizeof(tcp_client)); if (!info) { close(fd); return; } info->fd = fd; info->drv.chr_write = chrctr_write; info->drv.opaque = info; fcntl(fd, F_SETFL, O_NONBLOCK); qemu_set_fd_handler(fd, tcpctrl_read, NULL, info); #ifdef DEBUG_TCPCTRL fprintf(stderr, "tcpctrl_accept(): fd = %d\n", fd); #endif } static int tcpctrl_open() { int fd = -1; int pid = getpid(); mode_t oldmask; struct sockaddr_un addr; snprintf(tcpctrl_socket, sizeof(tcpctrl_socket) - 1, "%s/qemu-%d-socket", "/tmp", pid); tcpctrl_socket[sizeof(tcpctrl_socket) - 1] = 0; unlink(tcpctrl_socket); fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd < 0) { fprintf(stderr, "Failed to create unix socket"); return(-1); } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(&addr.sun_path[0], tcpctrl_socket, (sizeof(addr) - 4) - 1); oldmask = umask(0077); if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { fprintf(stderr, "Failed to bind to socket %s\n", tcpctrl_socket); close(fd); umask(oldmask); return(-1); } if (listen(fd, 3) < 0) { fprintf(stderr, "Failed to listen to socket %s\n", tcpctrl_socket); close(fd); umask(oldmask); return(-1); } umask(oldmask); #ifdef DEBUG_TCPCTRL fprintf(stderr, "Listening on TCP control socket %s\n", tcpctrl_socket); #endif return fd; } int tcpctrl_start() { #ifdef DEBUG_TCPCTRL fprintf(stderr, "tcpctrl_start()\n"); #endif tcpctrl_fd = tcpctrl_open(); if (tcpctrl_fd < 0) return -1; qemu_set_fd_handler(tcpctrl_fd, tcpctrl_accept, NULL, NULL); return 0; } void tcpctrl_stop() { if (tcpctrl_fd < 0) return; #ifdef DEBUG_TCPCTRL fprintf(stderr, "tcpctrl_stop()\n"); #endif unlink(tcpctrl_socket); qemu_set_fd_handler(tcpctrl_fd, NULL, NULL, NULL); close(tcpctrl_fd); tcpctrl_fd = -1; return; } #endif /* CONFIG_TCPCTRL */