From f7a4107c6f2e6c8231b606e38af5adb31178a342 Mon Sep 17 00:00:00 2001 From: Chris Johns Date: Mon, 28 Nov 2016 14:23:15 +1100 Subject: Add a RTEMS Debugger TCP remote transport. The patch also adds support to libbsd's build system making source conditional on a configure check. The debugger support is not available on all architectures and this feature lets us test if is avaliable. --- rtemsbsd/debugger/rtems-debugger-remote-tcp.c | 342 ++++++++++++++++++++++++++ 1 file changed, 342 insertions(+) create mode 100644 rtemsbsd/debugger/rtems-debugger-remote-tcp.c (limited to 'rtemsbsd/debugger/rtems-debugger-remote-tcp.c') diff --git a/rtemsbsd/debugger/rtems-debugger-remote-tcp.c b/rtemsbsd/debugger/rtems-debugger-remote-tcp.c new file mode 100644 index 00000000..af222e00 --- /dev/null +++ b/rtemsbsd/debugger/rtems-debugger-remote-tcp.c @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2016 Chris Johns . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/** + * Debugger default server port. 'RT' as ASCII. + */ +#define RTEMS_DB_PORT_DEFAULT (8284) + +/** + * TCP Remote data. + */ +typedef struct +{ + int fd; + int port; +} rtems_debugger_remote_tcp; + +static rtems_debugger_remote_tcp* +tcp_remote(rtems_debugger_remote* remote) +{ + rtems_debugger_remote_tcp* tcp = NULL; + rtems_debugger_lock(); + if (remote != NULL && remote->data != NULL) + tcp = (rtems_debugger_remote_tcp*) remote->data; + rtems_debugger_unlock(); + return tcp; +} + +static int +tcp_remote_begin(rtems_debugger_remote* remote, const char* device) +{ + rtems_debugger_remote_tcp* tcp; + int port; + char* end; + + rtems_debugger_lock(); + + /* + * Parse the port number. + */ + port = strtoul(device, &end, 10); + if (port == 0 || *end != '\0') { + rtems_debugger_printf("error: rtems-db: tcp remote: invalid port: %s\n", device); + return -1; + } + + tcp = malloc(sizeof(rtems_debugger_remote_tcp)); + if (tcp == NULL) { + errno = ENOMEM; + return -1; + } + + remote->data = tcp; + + tcp->fd = -1; + tcp->port = port; + + rtems_debugger_unlock(); + + return 0; +} + +static int +tcp_remote_end(rtems_debugger_remote* remote) +{ + rtems_debugger_lock(); + + if (remote != NULL && remote->data != NULL) { + rtems_debugger_remote_tcp* tcp = (rtems_debugger_remote_tcp*) remote->data; + if (tcp != NULL) { + if (tcp->fd >= 0) + close(tcp->fd); + free(tcp); + remote->data = NULL; + } + } + + rtems_debugger_unlock(); + + return 0; +} + +static int +tcp_remote_connect(rtems_debugger_remote* remote) +{ + int ld; + struct sockaddr_in addr; + socklen_t opt; + socklen_t len; + bool running; + struct timeval timeout; + rtems_debugger_remote_tcp* tcp = tcp_remote(remote); + int r; + + if (rtems_debugger_verbose()) + rtems_debugger_printf("error: rtems-db: tcp remote: connect\n"); + + ld = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (ld < 0) { + rtems_debugger_printf("error: rtems-db: tcp remote: socket: (%d) %s\n", + errno, strerror(errno)); + return -1; + } + + opt = 1; + r = setsockopt(ld, + SOL_SOCKET, + SO_REUSEADDR, + (char *) &opt, + sizeof(opt)); + if (r < 0) { + close(ld); + rtems_debugger_printf("error: rtems-db: tcp remote: setsocket: reuseaddr: (%d) %s\n", + errno, strerror(errno)); + return -1; + } + + addr.sin_family = PF_INET; + addr.sin_port = htons(tcp->port); + addr.sin_addr.s_addr = INADDR_ANY; + + r = bind(ld, (struct sockaddr *) &addr, sizeof(addr)); + if (r < 0) { + close(ld); + rtems_debugger_printf("error: rtems-db: tcp remote: bind: (%d) %s\n", + errno, strerror(errno)); + return -1; + } + + /* + * Backlog of 1 connection. + */ + r = listen(ld, 1); + if (r < 0) { + close(ld); + rtems_debugger_printf("error: rtems-db: tcp remote: listen: (%d) %s\n", + errno, strerror(errno)); + return -1; + } + + /* + * Use a random port if the port is 0. + */ + if (tcp->port == 0) { + len = sizeof(addr); + r = getsockname(ld, (struct sockaddr *) &addr, &len); + if (r < 0 || len < sizeof(addr)) { + close(ld); + rtems_debugger_printf("error: rtems-db: tcp remote: getsockname: (%d) %s\n", + errno, strerror(errno)); + return -1; + } + tcp->port = ntohs(addr.sin_port); + } + + rtems_debugger_printf("rtems-db: tcp remote: listing on port: %d\n", + tcp->port); + + len = sizeof(addr); + tcp->fd = accept(ld, (struct sockaddr *) &addr, &len); + + running = rtems_debugger_server_running(); + + close(ld); + + if (tcp->fd < 0) { + /* + * EBADF means the socket has been closed, ignore it. + */ + if (errno != EBADF) + rtems_debugger_printf("error: rtems-db: accept: (%d) %s\n", + errno, strerror(errno)); + return -1; + } + + if (!running) { + close(tcp->fd); + errno = EIO; + return -1; + } + + opt = 1; + r = setsockopt(tcp->fd, + SOL_SOCKET, SO_KEEPALIVE, + (char*) &opt, + sizeof(opt)); + if (r < 0) { + int errno_ = errno; + close(tcp->fd); + rtems_debugger_printf("error: rtems-db: tcp remote: set keepalive: (%d) %s\n", + errno, strerror(errno)); + errno = errno_; + return -1; + } + + opt = 1; + r = setsockopt(tcp->fd, + IPPROTO_TCP, TCP_NODELAY, + (char*) &opt, sizeof(opt)); + if (r < 0) { + int errno_ = errno; + close(tcp->fd); + rtems_debugger_printf("error: rtems-db: tcp remote: set no-delay: (%d) %s\n", + errno, strerror(errno)); + errno = errno_; + return -1; + } + + timeout.tv_sec = rtems_debugger->timeout; + timeout.tv_usec = 0; + + r = setsockopt(tcp->fd, + SOL_SOCKET, SO_RCVTIMEO, + (char*) &timeout, sizeof(timeout)); + if (r < 0) { + int errno_ = errno; + close(tcp->fd); + rtems_debugger_printf("error: rtems-db: tcp remote: set rcv-timeout: (%d) %s\n", + errno, strerror(errno)); + errno = errno_; + return -1; + } + + rtems_debugger_printf("rtems-db: tcp remote: connect host: %s\n", + inet_ntoa(addr.sin_addr)); + + return 0; +} + +static int +tcp_remote_disconnect(rtems_debugger_remote* remote) +{ + rtems_debugger_remote_tcp* tcp; + + rtems_debugger_lock(); + + rtems_debugger_printf("rtems-db: tcp remote: disconnect host\n"); + + tcp = (rtems_debugger_remote_tcp*) remote->data; + close(tcp->fd); + + rtems_debugger_unlock(); + + return 0; +} + +static bool +tcp_remote_isconnected(rtems_debugger_remote* remote) +{ + rtems_debugger_remote_tcp* tcp = tcp_remote(remote); + return tcp != NULL && tcp->fd >= 0; +} + +static ssize_t +tcp_remote_receive(rtems_debugger_remote* remote, + void* buf, + size_t nbytes) +{ + rtems_debugger_remote_tcp* tcp = tcp_remote(remote); + ssize_t len; + if (tcp != NULL) { + len = read(tcp->fd, buf, nbytes); + } + else { + errno = EIO; + len = -1; + } + return len; +} + +static ssize_t +tcp_remote_send(rtems_debugger_remote* remote, + const void* buf, + size_t nbytes) +{ + rtems_debugger_remote_tcp* tcp = tcp_remote(remote); + ssize_t len; + if (tcp != NULL) { + len = write(tcp->fd, buf, nbytes); + } + else { + errno = EIO; + len = -1; + } + return len; +} + +static rtems_debugger_remote remote_tcp = +{ + .name = "tcp", + .begin = tcp_remote_begin, + .end = tcp_remote_end, + .connect = tcp_remote_connect, + .disconnect = tcp_remote_disconnect, + .isconnected = tcp_remote_isconnected, + .read = tcp_remote_receive, + .write = tcp_remote_send +}; + +int +rtems_debugger_register_tcp_remote(void) +{ + return rtems_debugger_remote_register(&remote_tcp); +} -- cgit v1.2.3