1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-06-06 05:21:22 +03:00

emulation on host: fix nasty delay (#5840)

emulation with socket: fix nasty delay
        properly check all return values (read() returning 0 indicates closed peer)
        ClientContext::state() triggers read() to check for closed peer

uart emulation on tty: add cmdline -b debug option to disable raw tty

hide annoying MOCK messages, add cmdline -v option to show them

uart emulation on tty: check more return values

emulation on host: tcp/udp port shifting: new cmdline -s(port Shift) option
This commit is contained in:
david gauchard 2019-03-05 16:59:39 +01:00 committed by GitHub
parent 22eeb94587
commit 61a8a6b14e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 169 additions and 82 deletions

View File

@ -39,22 +39,43 @@
#include <stdarg.h>
#include <stdio.h>
#define MOCK_PORT_SHIFTER 9000
bool user_exit = false;
const char* host_interface = nullptr;
size_t spiffs_kb = 1024;
bool ignore_sigint = false;
bool restore_tty = false;
bool mockdebug = false;
int mock_port_shifter = MOCK_PORT_SHIFTER;
#define STDIN STDIN_FILENO
static struct termios initial_settings;
int mockverbose (const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (mockdebug)
return fprintf(stderr, MOCK) + vfprintf(stderr, fmt, ap);
return 0;
}
static int mock_start_uart(void)
{
struct termios settings;
if (!isatty(STDIN)) return 0;
if (tcgetattr(STDIN, &initial_settings) < 0) return -1;
if (!isatty(STDIN))
{
perror("setting tty in raw mode: isatty(STDIN)");
return -1;
}
if (tcgetattr(STDIN, &initial_settings) < 0)
{
perror("setting tty in raw mode: tcgetattr(STDIN)");
return -1;
}
settings = initial_settings;
settings.c_lflag &= ~(ignore_sigint ? ISIG : 0);
settings.c_lflag &= ~(ECHO | ICANON);
@ -62,7 +83,11 @@ static int mock_start_uart(void)
settings.c_oflag |= (ONLCR);
settings.c_cc[VMIN] = 0;
settings.c_cc[VTIME] = 0;
if (tcsetattr(STDIN, TCSANOW, &settings) < 0) return -2;
if (tcsetattr(STDIN, TCSANOW, &settings) < 0)
{
perror("setting tty in raw mode: tcsetattr(STDIN)");
return -1;
}
restore_tty = true;
return 0;
}
@ -71,11 +96,14 @@ static int mock_stop_uart(void)
{
if (!restore_tty) return 0;
if (!isatty(STDIN)) {
perror("isatty(STDIN)");
//system("stty sane"); <- same error message "Inappropriate ioctl for device"
return 0;
perror("restoring tty: isatty(STDIN)");
return -1;
}
if (tcsetattr(STDIN, TCSANOW, &initial_settings) < 0)
{
perror("restoring tty: tcsetattr(STDIN)");
return -1;
}
if (tcsetattr(STDIN, TCSANOW, &initial_settings) < 0) return -1;
printf("\e[?25h"); // show cursor
return (0);
}
@ -94,11 +122,14 @@ void help (const char* argv0, int exitcode)
" -h\n"
" -i <interface> - use this interface for IP address\n"
" -l - bind tcp/udp servers to interface only (not 0.0.0.0)\n"
" -s - port shifter (default: %d, when root: 0)\n"
" -c - ignore CTRL-C (send it via Serial)\n"
" -f - no throttle (possibly 100%%CPU)\n"
" -b - blocking tty/mocked-uart (default: not blocking tty)\n"
" -S - spiffs size in KBytes (default: %zd)\n"
" -v - mock verbose\n"
" (negative value will force mismatched size)\n"
, argv0, spiffs_kb);
, argv0, MOCK_PORT_SHIFTER, spiffs_kb);
exit(exitcode);
}
@ -108,8 +139,11 @@ static struct option options[] =
{ "fast", no_argument, NULL, 'f' },
{ "local", no_argument, NULL, 'l' },
{ "sigint", no_argument, NULL, 'c' },
{ "blockinguart", no_argument, NULL, 'b' },
{ "verbose", no_argument, NULL, 'v' },
{ "interface", required_argument, NULL, 'i' },
{ "spiffskb", required_argument, NULL, 'S' },
{ "portshifter", required_argument, NULL, 's' },
};
void cleanup ()
@ -133,13 +167,18 @@ void control_c (int sig)
int main (int argc, char* const argv [])
{
signal(SIGINT, control_c);
bool fast = false;
bool blocking_uart = false;
signal(SIGINT, control_c);
if (geteuid() == 0)
mock_port_shifter = 0;
else
mock_port_shifter = MOCK_PORT_SHIFTER;
for (;;)
{
int n = getopt_long(argc, argv, "hlcfi:S:", options, NULL);
int n = getopt_long(argc, argv, "hlcfbvi:S:s:", options, NULL);
if (n < 0)
break;
switch (n)
@ -153,6 +192,9 @@ int main (int argc, char* const argv [])
case 'l':
global_ipv4_netfmt = NO_GLOBAL_BINDING;
break;
case 's':
mock_port_shifter = atoi(optarg);
break;
case 'c':
ignore_sigint = true;
break;
@ -162,12 +204,19 @@ int main (int argc, char* const argv [])
case 'S':
spiffs_kb = atoi(optarg);
break;
case 'b':
blocking_uart = true;
break;
case 'v':
mockdebug = true;
break;
default:
fprintf(stderr, MOCK "bad option '%c'\n", n);
exit(EXIT_FAILURE);
help(argv[0], EXIT_FAILURE);
}
}
mockverbose("server port shifter: %d\n", mock_port_shifter);
if (spiffs_kb)
{
String name = argv[0];
@ -180,8 +229,11 @@ int main (int argc, char* const argv [])
// setup global global_ipv4_netfmt
wifi_get_ip_info(0, nullptr);
// set stdin to non blocking mode
mock_start_uart();
if (!blocking_uart)
{
// set stdin to non blocking mode
mock_start_uart();
}
// install exit handler in case Esp.restart() is called
atexit(cleanup);

View File

@ -58,7 +58,7 @@ void check_incoming_udp ()
p.events = POLLIN;
if (poll(&p, 1, 0) && p.revents == POLLIN)
{
fprintf(stderr, MOCK "UDP poll(%d) -> cb\r", p.fd);
mockverbose("UDP poll(%d) -> cb\r", p.fd);
udp.second->mock_cb();
}
}

View File

@ -43,7 +43,7 @@ int mockSockSetup (int sock)
{
if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1)
{
fprintf(stderr, MOCK "socket fcntl(O_NONBLOCK): %s\n", strerror(errno));
perror("socket fcntl(O_NONBLOCK)");
close(sock);
return -1;
}
@ -52,7 +52,8 @@ int mockSockSetup (int sock)
int i = 1;
if (setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof i) == -1)
{
fprintf(stderr, MOCK "sockopt( SO_NOSIGPIPE)(macOS): %s\n", strerror(errno));
perror("sockopt(SO_NOSIGPIPE)(macOS)");
close(sock);
return -1;
}
#endif
@ -84,6 +85,13 @@ ssize_t mockFillInBuf (int sock, char* ccinbuf, size_t& ccinbufsize)
{
size_t maxread = CCBUFSIZE - ccinbufsize;
ssize_t ret = ::read(sock, ccinbuf + ccinbufsize, maxread);
if (ret == 0)
{
// connection closed
return -1;
}
if (ret == -1)
{
if (errno != EAGAIN)
@ -100,7 +108,7 @@ ssize_t mockFillInBuf (int sock, char* ccinbuf, size_t& ccinbufsize)
ssize_t mockPeekBytes (int sock, char* dst, size_t usersize, int timeout_ms, char* ccinbuf, size_t& ccinbufsize)
{
if (usersize > CCBUFSIZE)
fprintf(stderr, MOCK "CCBUFSIZE(%d) should be increased by %zd bytes (-> %zd)\n", CCBUFSIZE, usersize - CCBUFSIZE, usersize);
mockverbose("CCBUFSIZE(%d) should be increased by %zd bytes (-> %zd)\n", CCBUFSIZE, usersize - CCBUFSIZE, usersize);
struct pollfd p;
size_t retsize = 0;
@ -163,12 +171,12 @@ ssize_t mockWrite (int sock, const uint8_t* data, size_t size, int timeout_ms)
#endif
if (ret == -1)
{
fprintf(stderr, MOCK "ClientContext::read: write(%d): %s\n", sock, strerror(errno));
fprintf(stderr, MOCK "ClientContext::write(%d): %s\n", sock, strerror(errno));
return -1;
}
if (ret != (int)size)
{
fprintf(stderr, MOCK "ClientContext::write: short write (%d < %zd) (TODO)\n", ret, size);
fprintf(stderr, MOCK "ClientContext::write: short write (%d < %zd) (FIXME poll loop TODO)\n", ret, size);
exit(EXIT_FAILURE);
}
}

View File

@ -31,6 +31,12 @@
#include <Arduino.h>
#ifdef DEBUG_ESP_CORE
#define VERBOSE(x...) fprintf(stderr, MOCK x)
#else
#define VERBOSE(x...) mockverbose(x)
#endif
void pinMode (uint8_t pin, uint8_t mode)
{
#define xxx(mode) case mode: m=STRHELPER(mode); break
@ -46,23 +52,17 @@ void pinMode (uint8_t pin, uint8_t mode)
case WAKEUP_PULLDOWN: m="WAKEUP_PULLDOWN"; break;
default: m="(special)";
}
#ifdef DEBUG_ESP_CORE
fprintf(stderr, MOCK "gpio%d: mode='%s'\n", pin, m);
#endif
VERBOSE("gpio%d: mode='%s'\n", pin, m);
}
void digitalWrite(uint8_t pin, uint8_t val)
{
#ifdef DEBUG_ESP_CORE
fprintf(stderr, MOCK "digitalWrite(pin=%d val=%d)\n", pin, val);
#endif
VERBOSE("digitalWrite(pin=%d val=%d)\n", pin, val);
}
void analogWrite(uint8_t pin, int val)
{
#ifdef DEBUG_ESP_CORE
fprintf(stderr, MOCK "analogWrite(pin=%d, val=%d\n", pin, val);
#endif
VERBOSE("analogWrite(pin=%d, val=%d\n", pin, val);
}
int analogRead(uint8_t pin)
@ -73,16 +73,12 @@ int analogRead(uint8_t pin)
void analogWriteRange(uint32_t range)
{
#ifdef DEBUG_ESP_CORE
fprintf(stderr, MOCK "analogWriteRange(range=%d)\n", range);
#endif
VERBOSE("analogWriteRange(range=%d)\n", range);
}
int digitalRead(uint8_t pin)
{
#ifdef DEBUG_ESP_CORE
fprintf(stderr, MOCK "digitalRead(%d)\n", pin);
#endif
VERBOSE("digitalRead(%d)\n", pin);
// pin 0 is most likely a low active input
return pin ? 0 : 1;

View File

@ -81,7 +81,7 @@ EspClass ESP;
void EspClass::restart ()
{
fprintf(stderr, MOCK "Esp.restart(): exiting\n");
mockverbose("Esp.restart(): exiting\n");
exit(EXIT_SUCCESS);
}

View File

@ -59,7 +59,7 @@ extern "C" void configTime(long timezone, int daylightOffset_sec,
(void)server2;
(void)server3;
fprintf(stderr, MOCK "configTime: TODO (tz=%ldH offset=%dS) (time will be host's)\n", timezone, daylightOffset_sec);
mockverbose("configTime: TODO (tz=%ldH offset=%dS) (time will be host's)\n", timezone, daylightOffset_sec);
}
void stack_thunk_add_ref() { }

View File

@ -47,23 +47,11 @@ extern "C" const ip_addr_t ip_addr_any = IPADDR4_INIT(IPADDR_ANY);
WiFiServer::WiFiServer (const IPAddress& addr, uint16_t port)
{
(void)addr;
if (port < 1024)
{
int newport = port + 9000;
fprintf(stderr, MOCK "WiFiServer port: %d -> %d\n", port, newport);
port = newport;
}
_port = port;
}
WiFiServer::WiFiServer (uint16_t port)
{
if (port < 1024)
{
int newport = port + 9000;
fprintf(stderr, MOCK "WiFiServer port: %d -> %d\n", port, newport);
port = newport;
}
_port = port;
}

View File

@ -67,8 +67,18 @@ void WiFiServer::begin (uint16_t port)
void WiFiServer::begin ()
{
int sock;
int mockport;
struct sockaddr_in server;
mockport = _port;
if (mockport < 1024 && mock_port_shifter)
{
mockport += mock_port_shifter;
fprintf(stderr, MOCK "=====> WiFiServer port: %d shifted to %d (use option -s) <=====\n", _port, mockport);
}
else
fprintf(stderr, MOCK "=====> WiFiServer port: %d <=====\n", mockport);
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror(MOCK "socket()");
@ -83,7 +93,7 @@ void WiFiServer::begin ()
}
server.sin_family = AF_INET;
server.sin_port = htons(_port);
server.sin_port = htons(mockport);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sock, (struct sockaddr*)&server, sizeof(server)) == -1)
{
@ -118,6 +128,7 @@ size_t WiFiServer::write (uint8_t c)
size_t WiFiServer::write (const uint8_t *buf, size_t size)
{
fprintf(stderr, MOCK "todo: WiFiServer::write(%p, %zd)\n", buf, size);
exit(EXIT_FAILURE);
return 0;
}

View File

@ -52,7 +52,19 @@ int mockUDPSocket ()
bool mockUDPListen (int sock, uint32_t dstaddr, uint16_t port, uint32_t mcast)
{
int optval = 1;
int optval;
int mockport;
mockport = port;
if (mockport < 1024 && mock_port_shifter)
{
mockport += mock_port_shifter;
fprintf(stderr, MOCK "=====> UdpServer port: %d shifted to %d (use option -s) <=====\n", port, mockport);
}
else
fprintf(stderr, MOCK "=====> UdpServer port: %d <=====\n", mockport);
optval = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) == -1)
fprintf(stderr, MOCK "SO_REUSEPORT failed\n");
optval = 1;
@ -66,16 +78,16 @@ bool mockUDPListen (int sock, uint32_t dstaddr, uint16_t port, uint32_t mcast)
servaddr.sin_family = AF_INET;
//servaddr.sin_addr.s_addr = global_ipv4_netfmt?: dstaddr;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
servaddr.sin_port = htons(mockport);
// Bind the socket with the server address
if (bind(sock, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
{
fprintf(stderr, MOCK "UDP bind on port %d failed: %s\n", port, strerror(errno));
fprintf(stderr, MOCK "UDP bind on port %d failed: %s\n", mockport, strerror(errno));
return false;
}
else
fprintf(stderr, MOCK "UDP server on port %d (sock=%d)\n", (int)port, sock);
mockverbose("UDP server on port %d (sock=%d)\n", mockport, sock);
if (mcast)
{

View File

@ -52,7 +52,10 @@ public:
err_t abort()
{
if (_sock >= 0)
{
::close(_sock);
mockverbose("socket %d closed\n", _sock);
}
_sock = -1;
return ERR_ABRT;
}
@ -111,12 +114,12 @@ public:
void setNoDelay(bool nodelay)
{
fprintf(stderr, MOCK "TODO setNoDelay(%d)\n", (int)nodelay);
mockverbose("TODO setNoDelay(%d)\n", (int)nodelay);
}
bool getNoDelay() const
{
fprintf(stderr, MOCK "TODO getNoDelay()\n");
mockverbose("TODO getNoDelay()\n");
return false;
}
@ -132,25 +135,25 @@ public:
uint32_t getRemoteAddress() const
{
fprintf(stderr, MOCK "TODO getRemoteAddress()\n");
mockverbose("TODO getRemoteAddress()\n");
return 0;
}
uint16_t getRemotePort() const
{
fprintf(stderr, MOCK "TODO getRemotePort()\n");
mockverbose("TODO getRemotePort()\n");
return 0;
}
uint32_t getLocalAddress() const
{
fprintf(stderr, MOCK "TODO getLocalAddress()\n");
mockverbose("TODO getLocalAddress()\n");
return 0;
}
uint16_t getLocalPort() const
{
fprintf(stderr, MOCK "TODO getLocalPort()\n");
mockverbose("TODO getLocalPort()\n");
return 0;
}
@ -160,7 +163,13 @@ public:
return 0;
if (_inbufsize)
return _inbufsize;
return mockFillInBuf(_sock, _inbuf, _inbufsize);
ssize_t ret = mockFillInBuf(_sock, _inbuf, _inbufsize);
if (ret < 0)
{
abort();
return 0;
}
return ret;
}
int read()
@ -174,7 +183,7 @@ public:
ssize_t ret = mockRead(_sock, dst, size, 0, _inbuf, _inbufsize);
if (ret < 0)
{
abort(); // close, CLOSED
abort();
return 0;
}
return ret;
@ -188,12 +197,18 @@ public:
size_t peekBytes(char *dst, size_t size)
{
return mockPeekBytes(_sock, dst, size, _timeout_ms, _inbuf, _inbufsize);
ssize_t ret = mockPeekBytes(_sock, dst, size, _timeout_ms, _inbuf, _inbufsize);
if (ret < 0)
{
abort();
return 0;
}
return ret;
}
void discard_received()
{
fprintf(stderr, MOCK "TODO: ClientContext::discard_received()\n");
mockverbose("TODO: ClientContext::discard_received()\n");
}
bool wait_until_sent(int max_wait_ms = WIFICLIENT_MAX_FLUSH_WAIT_MS)
@ -202,8 +217,9 @@ public:
return true;
}
uint8_t state() const
uint8_t state()
{
(void)getSize(); // read on socket to force detect closed peer
return _sock >= 0? ESTABLISHED: CLOSED;
}
@ -212,7 +228,7 @@ public:
ssize_t ret = mockWrite(_sock, data, size, _timeout_ms);
if (ret < 0)
{
abort(); // close, CLOSED
abort();
return 0;
}
return ret;
@ -242,42 +258,42 @@ public:
void keepAlive (uint16_t idle_sec = TCP_DEFAULT_KEEPALIVE_IDLE_SEC, uint16_t intv_sec = TCP_DEFAULT_KEEPALIVE_INTERVAL_SEC, uint8_t count = TCP_DEFAULT_KEEPALIVE_COUNT)
{
fprintf(stderr, MOCK "TODO ClientContext::keepAlive()\n");
mockverbose("TODO ClientContext::keepAlive()\n");
}
bool isKeepAliveEnabled () const
{
fprintf(stderr, MOCK "TODO ClientContext::isKeepAliveEnabled()\n");
mockverbose("TODO ClientContext::isKeepAliveEnabled()\n");
return false;
}
uint16_t getKeepAliveIdle () const
{
fprintf(stderr, MOCK "TODO ClientContext::getKeepAliveIdle()\n");
mockverbose("TODO ClientContext::getKeepAliveIdle()\n");
return 0;
}
uint16_t getKeepAliveInterval () const
{
fprintf(stderr, MOCK "TODO ClientContext::getKeepAliveInternal()\n");
mockverbose("TODO ClientContext::getKeepAliveInternal()\n");
return 0;
}
uint8_t getKeepAliveCount () const
{
fprintf(stderr, MOCK "TODO ClientContext::getKeepAliveCount()\n");
mockverbose("TODO ClientContext::getKeepAliveCount()\n");
return 0;
}
bool getSync () const
{
fprintf(stderr, MOCK "TODO ClientContext::getSync()\n");
mockverbose("TODO ClientContext::getSync()\n");
return _sync;
}
void setSync (bool sync)
{
fprintf(stderr, MOCK "TODO ClientContext::setSync()\n");
mockverbose("TODO ClientContext::setSync()\n");
_sync = sync;
}

View File

@ -89,7 +89,7 @@ public:
void setMulticastTTL(int ttl)
{
(void)ttl;
//fprintf(stderr, MOCK "TODO: UdpContext::setMulticastTTL\n");
//mockverbose("TODO: UdpContext::setMulticastTTL\n");
}
// warning: handler is called from tcp stack context
@ -112,7 +112,7 @@ public:
{
if (!isValidOffset(pos))
{
fprintf(stderr, MOCK "UDPContext::seek too far (%zd >= %zd)\n", pos, _inbufsize);
mockverbose("UDPContext::seek too far (%zd >= %zd)\n", pos, _inbufsize);
exit(EXIT_FAILURE);
}
mockUDPSwallow(pos, _inbuf, _inbufsize);
@ -134,13 +134,13 @@ public:
uint32_t getDestAddress()
{
fprintf(stderr, MOCK "TODO: implement UDP getDestAddress\n");
mockverbose("TODO: implement UDP getDestAddress\n");
return 0; //ip_hdr* iphdr = GET_IP_HDR(_rx_buf);
}
uint16_t getLocalPort()
{
fprintf(stderr, MOCK "TODO: implement UDP getLocalPort\n");
mockverbose("TODO: implement UDP getLocalPort\n");
return 0; //
}
@ -175,7 +175,7 @@ public:
void flush()
{
//fprintf(stderr, MOCK "UdpContext::flush() does not follow arduino's flush concept\n");
//mockverbose("UdpContext::flush() does not follow arduino's flush concept\n");
//exit(EXIT_FAILURE);
// would be:
_inbufsize = 0;
@ -185,7 +185,7 @@ public:
{
if (size + _outbufsize > sizeof _outbuf)
{
fprintf(stderr, MOCK "UdpContext::append: increase CCBUFSIZE (%d -> %zd)\n", CCBUFSIZE, (size + _outbufsize));
mockverbose("UdpContext::append: increase CCBUFSIZE (%d -> %zd)\n", CCBUFSIZE, (size + _outbufsize));
exit(EXIT_FAILURE);
}
@ -225,7 +225,7 @@ private:
//ip4_addr_set_u32(&ip_2_ip4(_dst), *(uint32_t*)addr);
}
else
fprintf(stderr, MOCK "TODO unhandled udp address of size %d\n", (int)addrsize);
mockverbose("TODO unhandled udp address of size %d\n", (int)addrsize);
}
int _sock = -1;

View File

@ -77,8 +77,12 @@ extern "C" {
int ets_printf (const char* fmt, ...) __attribute__ ((format (printf, 1, 2)));
#define os_printf_plus printf
int mockverbose (const char* fmt, ...) __attribute__ ((format (printf, 1, 2)));
extern const char* host_interface; // cmdline parameter
extern int mock_port_shifter;
#define NO_GLOBAL_BINDING 0xffffffff
extern uint32_t global_ipv4_netfmt; // selected interface addresse to bind to

View File

@ -216,7 +216,7 @@ wifi_event_handler_cb_t wifi_event_handler_cb_emu = nullptr;
void wifi_set_event_handler_cb (wifi_event_handler_cb_t cb)
{
wifi_event_handler_cb_emu = cb;
fprintf(stderr, MOCK "TODO: wifi_set_event_handler_cb set\n");
mockverbose("TODO: wifi_set_event_handler_cb set\n");
}
bool wifi_set_ip_info (uint8 if_index, struct ip_info *info)