From 147c7f2df426796f767da5e640b9fe6201d6f9d2 Mon Sep 17 00:00:00 2001 From: jyelon Date: Tue, 7 Dec 2021 17:28:53 -0500 Subject: [PATCH] Further progress toward working linux version --- luprex/.gitignore | 1 + luprex/core/Makefile | 4 +- luprex/core/cpp/driver-linux.cpp | 267 +++++++++++++++---------------- luprex/core/cpp/driver-mingw.cpp | 68 ++++---- 4 files changed, 167 insertions(+), 173 deletions(-) diff --git a/luprex/.gitignore b/luprex/.gitignore index a4b4e858..2878e3de 100644 --- a/luprex/.gitignore +++ b/luprex/.gitignore @@ -5,6 +5,7 @@ *.dll *.exe *.a +core/main core/inc/** core/lib/** core/obj/** diff --git a/luprex/core/Makefile b/luprex/core/Makefile index eba9b281..2630fe57 100644 --- a/luprex/core/Makefile +++ b/luprex/core/Makefile @@ -2,8 +2,10 @@ OS=unspecified ifeq ($(OS),mingw) EXE=main.exe + LIBS=-lws2_32 else EXE=main + LIBS= endif CXX=g++ -std=c++17 -Wall -g -Iinc -Icpp @@ -48,7 +50,7 @@ obj/%.o: cpp/%.cpp $(CXX) -c -MMD $< -o $@ $(EXE): $(OBJ_FILES) - $(CXX) -o $< $(OBJ_FILES) -lws2_32 -Llib lib/liblua-dbg.a + $(CXX) -o $@ $(OBJ_FILES) $(LIBS) -Llib lib/liblua-dbg.a -include $(OBJ_FILES:%.o=%.d) diff --git a/luprex/core/cpp/driver-linux.cpp b/luprex/core/cpp/driver-linux.cpp index 88fe23ec..1c26e13b 100644 --- a/luprex/core/cpp/driver-linux.cpp +++ b/luprex/core/cpp/driver-linux.cpp @@ -1,22 +1,57 @@ #include "driver.hpp" #include +#include #include #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +struct termios orig_termios; + +void set_nonblocking(int fd) { + int flags = fcntl(fd, F_GETFL, 0); + assert(flags != -1); + int status = fcntl(fd, F_SETFL, flags | O_NONBLOCK); + assert(status != -1); +} + +static void disableRawMode() { + tcsetattr(0, TCSAFLUSH, &orig_termios); +} + +static void enableRawMode() { + int status = tcgetattr(0, &orig_termios); + assert(status >= 0); + atexit(disableRawMode); + struct termios raw = orig_termios; + raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + raw.c_lflag &= ~(ECHO | ICANON); + raw.c_oflag |= OPOST; + raw.c_cc[VMIN] = 0; + raw.c_cc[VTIME] = 0; + status = tcsetattr(0, TCSAFLUSH, &raw); + assert(status >= 0); +} class Driver { public: + using PollVec = std::vector; + using SOCKET=int; + const int INVALID_SOCKET = -1; static const int MAX_CHAN = DrivenEngine::MAX_CHAN; - static const int CONSOLE_MAX = 512; DrivenEngine *driven_; SOCKET socket_[MAX_CHAN]; bool connected_[MAX_CHAN]; @@ -26,80 +61,67 @@ public: int64_t basetime_; int64_t get_now() { - FILETIME ft_now; - GetSystemTimeAsFileTime(&ft_now); - return (LONGLONG)ft_now.dwLowDateTime + ((LONGLONG)(ft_now.dwHighDateTime) << 32LL); - } - - static PADDRINFOA find_good_addr(PADDRINFOA addrinfo) { - for (PADDRINFOA addr = addrinfo; addr != nullptr; addr = addr->ai_next) { - if (addr->ai_family == AF_INET) { - return addr; - } - } - return nullptr; - } - - void set_nonblocking(SOCKET sock) { - u_long mode = 1; // 1 to enable non-blocking socket - int status = ioctlsocket(sock, FIONBIO, &mode); - assert(status == 0); + struct timeval tv; + gettimeofday(&tv, nullptr); + int64_t tv_sec = tv.tv_sec; + int64_t tv_usec = tv.tv_usec; + return tv_sec * 1000000 + tv_usec; } SOCKET open_connection(const std::string &target, std::string &err) { - PADDRINFOA addrs = nullptr; - PADDRINFOA goodaddr = nullptr; - SOCKET sock = INVALID_SOCKET; + struct addrinfo *addrs = nullptr; + struct addrinfo *goodaddr = nullptr; + struct addrinfo hints; + SOCKET sock = INVALID_SOCKET; std::string host, port; - + char errbuf[1024]; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_NUMERICSERV; + err = ""; util::split_host_port(target, host, port); - int status = getaddrinfo(host.c_str(), port.c_str(), nullptr, &addrs); - while (status == WSATRY_AGAIN) { - status = getaddrinfo(host.c_str(), port.c_str(), nullptr, &addrs); + int status = getaddrinfo(host.c_str(), port.c_str(), &hints, &addrs); + if (status != 0) { + err = gai_strerror(status); goto error; } - if (status == WSAHOST_NOT_FOUND) { - err = "host not found"; + if (addrs == nullptr) { + err = "no such host found"; goto error; } - if (status != 0) { - err = "DNS resolution malfunction"; - goto error; - } - goodaddr = find_good_addr(addrs); - if (goodaddr == nullptr) { - err = "host not an internet host"; - goto error; - } - sock = socket(goodaddr->ai_family, SOCK_STREAM, IPPROTO_TCP); - assert(sock != INVALID_SOCKET); + goodaddr = addrs; + assert(goodaddr->ai_family == AF_INET); + assert(goodaddr->ai_socktype == SOCK_STREAM); + assert(goodaddr->ai_protocol == IPPROTO_TCP); + sock = socket(goodaddr->ai_family, goodaddr->ai_socktype, goodaddr->ai_protocol); + assert(sock > 0); set_nonblocking(sock); status = connect(sock, goodaddr->ai_addr, goodaddr->ai_addrlen); - if (status != 0) { - int errcode = WSAGetLastError(); - if (errcode != WSAEWOULDBLOCK) { - std::ostringstream oss; - oss << "connect error " << errcode; - err = oss.str(); - goto error; - } + if ((status != 0) && (errno != EINPROGRESS)) { + goto error_errno; } freeaddrinfo(addrs); return sock; - + + error_errno: + strerror_r(errno, errbuf, 1024); + err = errbuf; error: - if (sock != INVALID_SOCKET) closesocket(sock); + if (sock != INVALID_SOCKET) close(sock); if (addrs != nullptr) freeaddrinfo(addrs); - return SOCKET_ERROR; + return INVALID_SOCKET; } SOCKET listen_on_port(int port, std::string &err) { int status; err = ""; SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); - assert(sock != INVALID_SOCKET); - + assert(sock > 0); + struct sockaddr_in server; server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; @@ -143,13 +165,13 @@ public: short_sleep_ = true; } } - + void handle_new_closed_sockets() { std::set chans; driven_->drv_get_new_closed(chans); for (int chid : chans) { if (socket_[chid] != INVALID_SOCKET) { - assert(closesocket(socket_[chid]) == 0); + assert(close(socket_[chid]) == 0); socket_[chid] = INVALID_SOCKET; connected_[chid] = false; } @@ -169,7 +191,6 @@ public: } else { socket_[chid] = sock; connected_[chid] = false; - Sleep(1000); } } } @@ -179,96 +200,50 @@ public: while (true) { driven_->drv_peek_outgoing(0, &nbytes, &bytes); if (nbytes == 0) break; - HANDLE hstdout = GetStdHandle(STD_OUTPUT_HANDLE); - assert(hstdout != INVALID_HANDLE_VALUE); - DWORD nwrote; - if (nbytes > 10000) nbytes = 10000; - assert(WriteConsoleA(hstdout, bytes, nbytes, &nwrote, nullptr)); - assert(nwrote > 0); - driven_->drv_sent_outgoing(0, nwrote); + int nwrote = write(1, bytes, nbytes); + assert(nwrote > 0); + driven_->drv_sent_outgoing(0, nwrote); } } - // We're feeding raw console characters to the DrivenEngine layer. - // The DrivenEngine channel is expected to be in readline mode, - // which will handle echoing and line processing. void handle_console_input() { - HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE); - assert(hstdin != INVALID_HANDLE_VALUE); - INPUT_RECORD inrecords[512]; - DWORD nread, nevents; - char ascii[512]; - int nascii = 0; - if (GetNumberOfConsoleInputEvents(hstdin, &nevents)) { - if (nevents > 512) nevents = 512; - ReadConsoleInputA(hstdin, inrecords, nevents, &nread); - for (int i = 0; i < int(nread); i++) { - const INPUT_RECORD &inr = inrecords[i]; - if (inr.EventType != KEY_EVENT) continue; - const KEY_EVENT_RECORD &key = inr.Event.KeyEvent; - if (!key.bKeyDown) continue; - char c = key.uChar.AsciiChar; - ascii[nascii++] = c; - } - if (nascii > 0) { - driven_->drv_recv_incoming(0, nascii, ascii); - short_sleep_ = true; - } + char buffer[32]; + while (true) { + int nread = read(0, buffer, 32); + if (nread == 0) break; + assert(nread > 0); + driven_->drv_recv_incoming(0, nread, buffer); } } void handle_clock() { - int now = get_now() - basetime_; - driven_->drv_set_clock(double(now) / 10000000.0); + int64_t now = get_now() - basetime_; + driven_->drv_set_clock(double(now) / 1000000.0); } void close_socket(int chid, const std::string err) { assert(socket_[chid] != INVALID_SOCKET); - assert(closesocket(socket_[chid]) == 0); + assert(close(socket_[chid]) == 0); driven_->drv_notify_close(chid, err); socket_[chid] = INVALID_SOCKET; connected_[chid] = false; short_sleep_ = true; } - bool calc_select_sets(fd_set &rfds, fd_set &wfds, fd_set &efds) const { - FD_ZERO(&rfds); - FD_ZERO(&wfds); - FD_ZERO(&efds); - bool any = false; - for (const auto &p : listen_sockets_) { - FD_SET(p.second, &rfds); - FD_SET(p.second, &efds); - any = true; - } - for (int chid = 1; chid < MAX_CHAN; chid++) { - SOCKET sock = socket_[chid]; - if (sock == INVALID_SOCKET) continue; - any = true; - FD_SET(sock, &rfds); - FD_SET(sock, &efds); - if (!driven_->drv_outgoing_empty(chid)) { - FD_SET(sock, &wfds); - } - } - return any; - } - void accept_connections(int port, SOCKET sock) { while (true) { SOCKET chsock = accept(sock, nullptr, nullptr); - if (chsock != INVALID_SOCKET) { + if (chsock > 0) { int chid = driven_->drv_notify_accept(port); socket_[chid] = chsock; connected_[chid] = true; short_sleep_ = true; continue; } - int errcode = WSAGetLastError(); - if (errcode == WSAEWOULDBLOCK) { - return; - } - if (errcode == WSAECONNRESET) { + if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { + return; + } + if (errno == ECONNABORTED) { // The remote disconnected before we had a chance to accept. // Just pretend it never happened. continue; @@ -280,20 +255,39 @@ public: } } + int calc_select_sets(fd_set &rfds, fd_set &wfds, fd_set &efds) const { + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&efds); + int largest = -1; + for (const auto &p : listen_sockets_) { + FD_SET(p.second, &rfds); + FD_SET(p.second, &efds); + if (p.second > largest) largest = p.second; + } + for (int chid = 1; chid < MAX_CHAN; chid++) { + SOCKET sock = socket_[chid]; + if (sock == INVALID_SOCKET) continue; + FD_SET(sock, &rfds); + FD_SET(sock, &efds); + if (!driven_->drv_outgoing_empty(chid)) { + FD_SET(sock, &wfds); + } + if (sock > largest) largest = sock; + } + return largest + 1; + } + void handle_socket_input_output(int mstimeout) { fd_set rfds, wfds, efds; int nbytes; const char *bytes; - bool any = calc_select_sets(rfds, wfds, efds); - if (!any) { - if (mstimeout>0) Sleep(mstimeout); - return; - } - TIMEVAL timeout; + int nfds = calc_select_sets(rfds, wfds, efds); + struct timeval timeout; timeout.tv_sec = mstimeout / 1000; timeout.tv_usec = (mstimeout - (timeout.tv_sec*1000)) * 1000; - int status = select(1, &rfds, &wfds, &efds, &timeout); - assert(status != SOCKET_ERROR); - + int status = select(nfds, &rfds, &wfds, &efds, &timeout); + assert(status >= 0); + for (auto &p : listen_sockets_) { if (FD_ISSET(p.second, &rfds) || FD_ISSET(p.second, &efds)) { accept_connections(p.first, p.second); @@ -308,7 +302,7 @@ public: driven_->drv_peek_outgoing(chid, &nbytes, &bytes); if (nbytes > 0) { int wbytes = send(sock, bytes, nbytes, 0); - if (wbytes == SOCKET_ERROR) { + if (wbytes < 0) { close_socket(chid, "send failure"); continue; } else { @@ -318,7 +312,7 @@ public: } if (FD_ISSET(sock, &rfds) || FD_ISSET(sock, &efds)) { int nrecv = recv(sock, chbuf.get(), 65536, 0); - if ((nrecv == SOCKET_ERROR) || (nrecv == 0)) { + if (nrecv <= 0) { close_socket(chid, "recv failure"); continue; } else { @@ -330,11 +324,8 @@ public: } void drive(DrivenEngine *de, int argc, char *argv[]) { - WSADATA whocares; - assert(WSAStartup(MAKEWORD(2,2), &whocares) == 0); - HANDLE hconsole = GetStdHandle(STD_INPUT_HANDLE); - assert(hconsole != INVALID_HANDLE_VALUE); - init(de); + enableRawMode(); + init(de); DrivenEngine::set(de); basetime_ = get_now(); driven_->drv_set_lua_source(util::read_lua_source("lua")); diff --git a/luprex/core/cpp/driver-mingw.cpp b/luprex/core/cpp/driver-mingw.cpp index 88fe23ec..ddfdff31 100644 --- a/luprex/core/cpp/driver-mingw.cpp +++ b/luprex/core/cpp/driver-mingw.cpp @@ -31,6 +31,12 @@ public: return (LONGLONG)ft_now.dwLowDateTime + ((LONGLONG)(ft_now.dwHighDateTime) << 32LL); } + void set_nonblocking(SOCKET sock) { + u_long mode = 1; // 1 to enable non-blocking socket + int status = ioctlsocket(sock, FIONBIO, &mode); + assert(status == 0); + } + static PADDRINFOA find_good_addr(PADDRINFOA addrinfo) { for (PADDRINFOA addr = addrinfo; addr != nullptr; addr = addr->ai_next) { if (addr->ai_family == AF_INET) { @@ -40,12 +46,6 @@ public: return nullptr; } - void set_nonblocking(SOCKET sock) { - u_long mode = 1; // 1 to enable non-blocking socket - int status = ioctlsocket(sock, FIONBIO, &mode); - assert(status == 0); - } - SOCKET open_connection(const std::string &target, std::string &err) { PADDRINFOA addrs = nullptr; PADDRINFOA goodaddr = nullptr; @@ -57,7 +57,6 @@ public: int status = getaddrinfo(host.c_str(), port.c_str(), nullptr, &addrs); while (status == WSATRY_AGAIN) { status = getaddrinfo(host.c_str(), port.c_str(), nullptr, &addrs); - goto error; } if (status == WSAHOST_NOT_FOUND) { err = "host not found"; @@ -218,7 +217,7 @@ public: } void handle_clock() { - int now = get_now() - basetime_; + int64_t now = get_now() - basetime_; driven_->drv_set_clock(double(now) / 10000000.0); } @@ -231,6 +230,33 @@ public: short_sleep_ = true; } + void accept_connections(int port, SOCKET sock) { + while (true) { + SOCKET chsock = accept(sock, nullptr, nullptr); + if (chsock != INVALID_SOCKET) { + set_nonblocking(chsock); + int chid = driven_->drv_notify_accept(port); + socket_[chid] = chsock; + connected_[chid] = true; + short_sleep_ = true; + continue; + } + int errcode = WSAGetLastError(); + if (errcode == WSAEWOULDBLOCK) { + return; + } + if (errcode == WSAECONNRESET) { + // The remote disconnected before we had a chance to accept. + // Just pretend it never happened. + continue; + } + // If a listening port fails in a non-transient way, + // we don't really have any good way of handling + // that. + assert(false); + } + } + bool calc_select_sets(fd_set &rfds, fd_set &wfds, fd_set &efds) const { FD_ZERO(&rfds); FD_ZERO(&wfds); @@ -254,32 +280,6 @@ public: return any; } - void accept_connections(int port, SOCKET sock) { - while (true) { - SOCKET chsock = accept(sock, nullptr, nullptr); - if (chsock != INVALID_SOCKET) { - int chid = driven_->drv_notify_accept(port); - socket_[chid] = chsock; - connected_[chid] = true; - short_sleep_ = true; - continue; - } - int errcode = WSAGetLastError(); - if (errcode == WSAEWOULDBLOCK) { - return; - } - if (errcode == WSAECONNRESET) { - // The remote disconnected before we had a chance to accept. - // Just pretend it never happened. - continue; - } - // If a listening port fails in a non-transient way, - // we don't really have any good way of handling - // that. - assert(false); - } - } - void handle_socket_input_output(int mstimeout) { fd_set rfds, wfds, efds; int nbytes; const char *bytes;