Further progress toward working linux version
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -1,22 +1,57 @@
|
||||
|
||||
#include "driver.hpp"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <ws2tcpip.h>
|
||||
#include <winsock2.h>
|
||||
#include <synchapi.h>
|
||||
#include <sysinfoapi.h>
|
||||
#include <poll.h>
|
||||
#include <sys/time.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
#include <netdb.h>
|
||||
|
||||
|
||||
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<struct pollfd>;
|
||||
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<int> 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"));
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user