From 20801395e6ed9681fd0a384da17e4aec3c0b5fa6 Mon Sep 17 00:00:00 2001 From: Joshua Yelon Date: Wed, 12 Jan 2022 15:53:49 -0500 Subject: [PATCH] windows version compiles, but won't link because of bad archive --- luprex/core/cpp/driver-common.cpp | 14 +- luprex/core/cpp/driver-mingw.cpp | 583 +++++++++++------------------- 2 files changed, 228 insertions(+), 369 deletions(-) diff --git a/luprex/core/cpp/driver-common.cpp b/luprex/core/cpp/driver-common.cpp index 24fe6ab5..801ce63e 100644 --- a/luprex/core/cpp/driver-common.cpp +++ b/luprex/core/cpp/driver-common.cpp @@ -158,7 +158,7 @@ public: driven_->drv_peek_outgoing(0, &nbytes, &bytes); if (nbytes == 0) break; int nwrote = console_write(bytes, nbytes); - assert(nwrote > 0); + if (nwrote <= 0) break; driven_->drv_sent_outgoing(0, nwrote); } } @@ -168,8 +168,7 @@ public: read_console_recently_ = false; while (true) { int nread = console_read(buffer, 256); - if (nread == 0) break; - assert(nread > 0); + if (nread <= 0) break; read_console_recently_ = true; driven_->drv_recv_incoming(0, nread, buffer); } @@ -360,6 +359,7 @@ public: void handle_socket_input_output() { + std::string err; int mstimeout = 1000; // Peek output buffers and determine channel release flags. @@ -372,9 +372,9 @@ public: } } - // Construct the pollfd vector. + // Construct the struct pollfd vector. int pollsize = listen_sockets_.size() + chans_.size() + 1; - std::vector pollvec(pollsize); + PollVector pollvec(pollsize); int index = 0; for (const auto &p : listen_sockets_) { struct pollfd &pfd = pollvec[index++]; @@ -396,8 +396,8 @@ public: fill_stdio_pollfd(pollvec, mstimeout, read_console_recently_); // Do the poll. - int status = poll(&pollvec[0], pollvec.size(), mstimeout); - assert(status >= 0); + socket_poll(pollvec, mstimeout, err); + if_error_print_and_exit(err); // Check listening sockets. index = 0; diff --git a/luprex/core/cpp/driver-mingw.cpp b/luprex/core/cpp/driver-mingw.cpp index 5a2986c7..c39a62e0 100644 --- a/luprex/core/cpp/driver-mingw.cpp +++ b/luprex/core/cpp/driver-mingw.cpp @@ -1,3 +1,5 @@ +#define WINVER 0x0600 +#define _WIN32_WINNT 0x0600 #include "driver.hpp" #include "util.hpp" @@ -8,18 +10,233 @@ #include #include #include -#include #include +#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +using PollVector = std::vector; +static void set_nonblocking(SOCKET sock) { + u_long mode = 1; // 1 to enable non-blocking socket + int status = ioctlsocket(sock, FIONBIO, &mode); + assert(status == 0); +} -class monotonic_clock { +static void enable_tty_raw() { + // Do nothing on windows. +} + +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; +} + +static SOCKET open_connection(const std::string &target, std::string &err) { + PADDRINFOA addrs = nullptr; + PADDRINFOA goodaddr = nullptr; + SOCKET sock = INVALID_SOCKET; + std::string host, port; + + err.clear(); + 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); + } + if (status == WSAHOST_NOT_FOUND) { + err = "host not 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); + if (sock == INVALID_SOCKET) { + err = "could not create a socket"; + goto error; + } + set_nonblocking(sock); + status = connect(sock, goodaddr->ai_addr, goodaddr->ai_addrlen); + if (status != 0) { + int errcode = WSAGetLastError(); + if (errcode != WSAEWOULDBLOCK) { + err = "connect failure"; + goto error; + } + } + freeaddrinfo(addrs); + return sock; + +error: + if (sock != INVALID_SOCKET) closesocket(sock); + if (addrs != nullptr) freeaddrinfo(addrs); + return SOCKET_ERROR; +} + +SOCKET listen_on_port(int port, std::string &err) { + int status; + err.clear(); + SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == INVALID_SOCKET) { + err = "could not create a socket"; + goto error; + } + + struct sockaddr_in server; + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + server.sin_port = htons(port); + + status = bind(sock, (struct sockaddr *)&server, sizeof(server)); + if (status < 0) { + err = "could not bind port"; + goto error; + } + status = listen(sock, 10); + if (status < 0) { + err = "could not listen on socket"; + goto error; + } + set_nonblocking(sock); + return sock; + +error: + if (sock != INVALID_SOCKET) closesocket(sock); + return SOCKET_ERROR; +} + +static SOCKET accept_on_socket(SOCKET listen_socket, std::string &err) { + SOCKET chsock = accept(listen_socket, nullptr, nullptr); + if (chsock != INVALID_SOCKET) { + set_nonblocking(chsock); + return chsock; + } else { + int errcode = WSAGetLastError(); + if ((errcode == WSAEWOULDBLOCK) || (errcode == WSAECONNRESET)) { + return INVALID_SOCKET; + } else { + err = "accept failed"; + return INVALID_SOCKET; + } + } +} + +static int socket_send(SOCKET socket, const char *bytes, int nbytes, std::string &err) { + err.clear(); + int wbytes = send(socket, bytes, nbytes, 0); + if (wbytes == SOCKET_ERROR) { + int errcode = WSAGetLastError(); + if (errcode == WSAEWOULDBLOCK) { + return 0; + } else { + err = "send failure"; + return -1; + } + } else { + assert(wbytes > 0); + return wbytes; + } +} + +static int socket_recv(SOCKET socket, char *bytes, int nbytes, std::string &err) { + err.clear(); + int nrecv = recv(socket, bytes, nbytes, 0); + if (nrecv < 0) { + int errcode = WSAGetLastError(); + if (errcode == WSAEWOULDBLOCK) { + return 0; + } else { + err = "recv failure"; + return -1; + } + } else if (nrecv == 0) { + return -1; + } else { + return nrecv; + } +} + +static int socket_close(SOCKET socket) { + return closesocket(socket); +} + +static int socket_poll(PollVector &pollvec, int mstimeout, std::string &err) { + int status = WSAPoll(&pollvec[0], pollvec.size(), mstimeout); + if (status < 0) { + WSAGetLastError(); + err = "poll failed"; + return -1; + } + return status; +} + +static int console_write(const char *bytes, int nbytes) { + if (nbytes == 0) return 0; + 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); + return nwrote; +} + +static int console_read(char *bytes, int nbytes) { + HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE); + assert(hstdin != INVALID_HANDLE_VALUE); + INPUT_RECORD inrecords[512]; + DWORD nread, nevents; + int nascii = 0; + if (GetNumberOfConsoleInputEvents(hstdin, &nevents)) { + if (int(nevents) > nbytes) nevents = nbytes; + 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; + bytes[nascii++] = c; + } + return nascii; + } else { + return 0; + } +} + +// The last element in the vector is supposed to be +// for polling stdio. But on windows, you can't poll +// stdio, so on windows, we remove the last element from +// the vector and we reduce mstimeout instead. +static void fill_stdio_pollfd(PollVector &pollvec, int &mstimeout, bool read_console_recently) { + pollvec.pop_back(); + if (mstimeout > 100) mstimeout = 100; +} + +class MonoClock { public: double freq_; - monotonic_clock() { + MonoClock() { LARGE_INTEGER x; BOOL status = QueryPerformanceFrequency(&x); assert(status != 0); @@ -32,364 +249,6 @@ public: assert(status != 0); return double(x.QuadPart) * freq_; } -); -#endif - -static monotonic_clock monoclock; - -namespace util { - double profiling_clock() { - return monoclock.get(); - } -} - -class Driver { -public: - static const int MAX_CHAN = DrivenEngine::MAX_CHAN; - static const int CONSOLE_MAX = 512; - DrivenEngine *driven_; - SOCKET socket_[MAX_CHAN]; - bool connected_[MAX_CHAN]; - bool short_sleep_; - std::map listen_sockets_; - std::unique_ptr chbuf; - int64_t basetime_; - - int64_t get_now() { - FILETIME ft_now; - GetSystemTimeAsFileTime(&ft_now); - 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) { - return addr; - } - } - return nullptr; - } - - SOCKET open_connection(const std::string &target, std::string &err) { - PADDRINFOA addrs = nullptr; - PADDRINFOA goodaddr = nullptr; - SOCKET sock = INVALID_SOCKET; - std::string host, port; - - 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); - } - if (status == WSAHOST_NOT_FOUND) { - err = "host not 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); - 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; - } - } - freeaddrinfo(addrs); - return sock; - - error: - if (sock != INVALID_SOCKET) closesocket(sock); - if (addrs != nullptr) freeaddrinfo(addrs); - return SOCKET_ERROR; - } - - SOCKET listen_on_port(int port, std::string &err) { - int status; - err = ""; - SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); - assert(sock != INVALID_SOCKET); - - struct sockaddr_in server; - server.sin_family = AF_INET; - server.sin_addr.s_addr = INADDR_ANY; - server.sin_port = htons(port); - - status = bind(sock, (struct sockaddr *)&server, sizeof(server)); - assert(status == 0); - status = listen(sock, 10); - assert(status == 0); - set_nonblocking(sock); - return sock; - } - - void init(DrivenEngine *de) { - driven_ = de; - for (int i = 0; i < MAX_CHAN; i++) { - socket_[i] = INVALID_SOCKET; - connected_[i] = false; - } - short_sleep_ = false; - chbuf.reset(new char[65536]); - } - - void handle_listen_ports() { - std::set listenports; - driven_->drv_get_listen_ports(listenports); - for (int port : listenports) { - if (listen_sockets_.find(port) == listen_sockets_.end()) { - std::string err; - SOCKET sock = listen_on_port(port, err); - if (sock != INVALID_SOCKET) { - listen_sockets_[port] = sock; - } - } - } - } - - void handle_lua_source() { - if (driven_->drv_get_rescan_lua_source()) { - driven_->drv_set_lua_source(util::read_lua_source("lua")); - 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); - socket_[chid] = INVALID_SOCKET; - connected_[chid] = false; - } - } - } - - void handle_new_outgoing_sockets() { - std::set chans; - driven_->drv_get_new_outgoing(chans); - for (int chid : chans) { - assert(socket_[chid] == INVALID_SOCKET); - std::string err; - SOCKET sock = open_connection(driven_->drv_get_target(chid), err); - if (sock == INVALID_SOCKET) { - driven_->drv_notify_close(chid, err); - short_sleep_ = true; - } else { - socket_[chid] = sock; - connected_[chid] = false; - Sleep(1000); - } - } - } - - void handle_console_output() { - int nbytes; const char *bytes; - 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); - } - } - - // 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; - } - } - } - - void handle_clock() { - int64_t now = get_now() - basetime_; - driven_->drv_set_clock(double(now) / 10000000.0); - } - - void close_socket(int chid, const std::string err) { - assert(socket_[chid] != INVALID_SOCKET); - assert(closesocket(socket_[chid]) == 0); - driven_->drv_notify_close(chid, err); - socket_[chid] = INVALID_SOCKET; - connected_[chid] = false; - 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); - 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 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; - 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); - - for (auto &p : listen_sockets_) { - if (FD_ISSET(p.second, &rfds) || FD_ISSET(p.second, &efds)) { - accept_connections(p.first, p.second); - } - } - - for (int chid = 1; chid < MAX_CHAN; chid++) { - SOCKET sock = socket_[chid]; - if (sock == INVALID_SOCKET) continue; - if (FD_ISSET(sock, &wfds)) { - connected_[chid] = true; - driven_->drv_peek_outgoing(chid, &nbytes, &bytes); - if (nbytes > 0) { - int wbytes = send(sock, bytes, nbytes, 0); - if (wbytes == SOCKET_ERROR) { - close_socket(chid, "send failure"); - continue; - } else { - driven_->drv_sent_outgoing(chid, wbytes); - } - } - } - if (FD_ISSET(sock, &rfds) || FD_ISSET(sock, &efds)) { - int nrecv = recv(sock, chbuf.get(), 65536, 0); - if ((nrecv == SOCKET_ERROR) || (nrecv == 0)) { - close_socket(chid, "recv failure"); - continue; - } else { - driven_->drv_recv_incoming(chid, nrecv, chbuf.get()); - short_sleep_ = true; - } - } - } - } - - 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); - DrivenEngine::set(de); - basetime_ = get_now(); - driven_->drv_set_lua_source(util::read_lua_source("lua")); - driven_->drv_invoke_event_init(argc, argv); - handle_listen_ports(); - while (!de->drv_get_stop_driver()) { - short_sleep_ = false; - handle_lua_source(); - handle_new_closed_sockets(); - handle_new_outgoing_sockets(); - handle_console_output(); - handle_console_input(); - handle_console_output(); - int mstimeout = short_sleep_ ? 0 : 100; - handle_socket_input_output(mstimeout); - handle_clock(); - de->drv_invoke_event_update(); - } - DrivenEngine::set(nullptr); - } }; - -void driver_drive(DrivenEngine *de, int argc, char *argv[]) { - Driver driver; - driver.drive(de, argc, argv); -} - +#include "driver-common.cpp" \ No newline at end of file