diff --git a/source/SPoverSLIP/Listener.cpp b/source/SPoverSLIP/Listener.cpp index d48b37911..b43f9e7e2 100644 --- a/source/SPoverSLIP/Listener.cpp +++ b/source/SPoverSLIP/Listener.cpp @@ -15,52 +15,47 @@ #include "Log.h" #include "Requestor.h" -// clang-format off #ifdef WIN32 - #include - #include - #pragma comment(lib, "ws2_32.lib") - #define CLOSE_SOCKET closesocket - #define SOCKET_ERROR_CODE WSAGetLastError() + #include + #include + #pragma comment(lib, "ws2_32.lib") + #define CLOSE_SOCKET closesocket + #define SOCKET_ERROR_CODE WSAGetLastError() #else - #include - #include - #include - #include - #include - #include -#include "StatusRequest.h" -#include "StatusRequest.h" - #define CLOSE_SOCKET close - #define SOCKET_ERROR_CODE errno - #define INVALID_SOCKET -1 - #define SOCKET_ERROR -1 + #include + #include + #include + #include + #include + #include + #include "StatusRequest.h" + #include "StatusRequest.h" + #define CLOSE_SOCKET close + #define SOCKET_ERROR_CODE errno + #define INVALID_SOCKET -1 + #define SOCKET_ERROR -1 #endif -// clang-format off uint8_t Listener::next_device_id_ = 1; const std::regex Listener::ipPattern("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"); -Listener& GetSPoverSLIPListener(void) +Listener &GetSPoverSLIPListener(void) { - static Listener listener; - return listener; + static Listener listener; + return listener; } Listener::Listener() : is_listening_(false) {} void Listener::Initialize(std::string ip_address, const uint16_t port) { - ip_address_ = std::move(ip_address); - port_ = port; + ip_address_ = std::move(ip_address); + port_ = port; } bool Listener::get_is_listening() const { return is_listening_; } -void Listener::insert_connection(uint8_t start_id, uint8_t end_id, const std::shared_ptr &conn) -{ - connection_map_[{start_id, end_id}] = conn; -} +void Listener::insert_connection(uint8_t start_id, uint8_t end_id, const std::shared_ptr &conn) { connection_map_[{start_id, end_id}] = conn; } uint8_t Listener::get_total_device_count() { return next_device_id_ - 1; } @@ -70,189 +65,190 @@ std::thread Listener::create_listener_thread() { return std::thread(&Listener::l void Listener::listener_function() { - LogFileOutput("Listener::listener_function - RUNNING\n"); - int server_fd, new_socket; - struct sockaddr_in address; + LogFileOutput("Listener::listener_function - RUNNING\n"); + int server_fd, new_socket; + struct sockaddr_in address; #ifdef WIN32 - int address_length = sizeof(address); + int address_length = sizeof(address); #else - socklen_t address_length = sizeof(address); + socklen_t address_length = sizeof(address); #endif #ifdef WIN32 - WSADATA wsa_data; - if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) { - LogFileOutput("WSAStartup failed: %d\n", WSAGetLastError()); - } + WSADATA wsa_data; + if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) + { + LogFileOutput("WSAStartup failed: %d\n", WSAGetLastError()); + } #endif - if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) - { - LogFileOutput("Listener::listener_function - Socket creation failed\n"); - return; - } + if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) + { + LogFileOutput("Listener::listener_function - Socket creation failed\n"); + return; + } - address.sin_family = AF_INET; - address.sin_port = htons(port_); - inet_pton(AF_INET, ip_address_.c_str(), &(address.sin_addr)); + address.sin_family = AF_INET; + address.sin_port = htons(port_); + inet_pton(AF_INET, ip_address_.c_str(), &(address.sin_addr)); #ifdef WIN32 - char opt = 1; + char opt = 1; #else - int opt = 1; + int opt = 1; #endif - if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == SOCKET_ERROR) - { - LogFileOutput("Listener::listener_function - setsockopt failed\n"); - return; - } + if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == SOCKET_ERROR) + { + LogFileOutput("Listener::listener_function - setsockopt failed\n"); + return; + } #ifdef WIN32 - if (bind(server_fd, reinterpret_cast(&address), sizeof(address)) == SOCKET_ERROR) + if (bind(server_fd, reinterpret_cast(&address), sizeof(address)) == SOCKET_ERROR) #else - if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) == SOCKET_ERROR) + if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) == SOCKET_ERROR) #endif - { - LogFileOutput("Listener::listener_function - bind failed\n"); - return; - } - - if (listen(server_fd, 3) < 0) - { - LogFileOutput("Listener::listener_function - listen failed\n"); - return; - } - - while (is_listening_) - { - fd_set sock_set; - FD_ZERO(&sock_set); - FD_SET(server_fd, &sock_set); - - timeval timeout; - timeout.tv_sec = 2; - timeout.tv_usec = 0; - - const int activity = select(server_fd + 1, &sock_set, nullptr, nullptr, &timeout); - - if (activity == SOCKET_ERROR) - { - LogFileOutput("Listener::listener_function - select failed\n"); - is_listening_ = false; - break; - } - - if (activity == 0) - { - // timeout occurred, no client connection. Still need to check is_listening_ - continue; - } + { + LogFileOutput("Listener::listener_function - bind failed\n"); + return; + } + + if (listen(server_fd, 3) < 0) + { + LogFileOutput("Listener::listener_function - listen failed\n"); + return; + } + + while (is_listening_) + { + fd_set sock_set; + FD_ZERO(&sock_set); + FD_SET(server_fd, &sock_set); + + timeval timeout; + timeout.tv_sec = 2; + timeout.tv_usec = 0; + + const int activity = select(server_fd + 1, &sock_set, nullptr, nullptr, &timeout); + + if (activity == SOCKET_ERROR) + { + LogFileOutput("Listener::listener_function - select failed\n"); + is_listening_ = false; + break; + } + + if (activity == 0) + { + // timeout occurred, no client connection. Still need to check is_listening_ + continue; + } #ifdef WIN32 - if ((new_socket = accept(server_fd, reinterpret_cast(&address), &address_length)) == INVALID_SOCKET) + if ((new_socket = accept(server_fd, reinterpret_cast(&address), &address_length)) == INVALID_SOCKET) #else - if ((new_socket = accept(server_fd, (struct sockaddr *)&address, &address_length)) == INVALID_SOCKET) + if ((new_socket = accept(server_fd, (struct sockaddr *)&address, &address_length)) == INVALID_SOCKET) #endif - { - LogFileOutput("Listener::listener_function - accept failed\n"); - is_listening_ = false; - break; - } + { + LogFileOutput("Listener::listener_function - accept failed\n"); + is_listening_ = false; + break; + } - create_connection(new_socket); - } + create_connection(new_socket); + } - LogFileOutput("Listener::listener_function - listener closing down\n"); + LogFileOutput("Listener::listener_function - listener closing down\n"); - CLOSE_SOCKET(server_fd); + CLOSE_SOCKET(server_fd); } // Creates a Connection object, which is how SP device(s) will register itself with our listener. void Listener::create_connection(unsigned int socket) { - // Create a connection, give it some time to settle, else exit without creating listener to connection - const std::shared_ptr conn = std::make_shared(socket); - conn->create_read_channel(); - - const auto start = std::chrono::steady_clock::now(); - // Give the connection a generous 10 seconds to work. - constexpr auto timeout = std::chrono::seconds(10); - - while (!conn->is_connected()) - { - auto now = std::chrono::steady_clock::now(); - if (std::chrono::duration_cast(now - start) > timeout) - { - LogFileOutput("Listener::create_connection() - Failed to establish " - "connection, timed out.\n"); - return; - } - } - - // We need to send an INIT to device 01 for this connection, then 02, ... - // until we get an error back This will determine the number of devices - // attached. - - bool still_scanning = true; - uint8_t unit_id = 1; - - // send init requests to find all the devices on this connection, or we have too many devices. - while (still_scanning && (unit_id + next_device_id_) < 255) - { - LogFileOutput("SmartPortOverSlip listener sending request for unit_id: %d\n", unit_id); - InitRequest request(Requestor::next_request_number(), unit_id); - const auto response = Requestor::send_request(request, conn.get()); - const auto init_response = dynamic_cast(response.get()); - if (init_response == nullptr) - { - LogFileOutput("SmartPortOverSlip listener ERROR, no response data found\n"); - break; - } - still_scanning = init_response->get_status() == 0; - if (still_scanning) - unit_id++; - } - - const auto start_id = next_device_id_; - const auto end_id = static_cast(start_id + unit_id - 1); - next_device_id_ = end_id + 1; - // track the connection and device ranges it reported. Further connections can add to the devices we can target. - LogFileOutput("SmartPortOverSlip listener creating connection for start: %d, end: %d\n", start_id, end_id); - insert_connection(start_id, end_id, conn); + // Create a connection, give it some time to settle, else exit without creating listener to connection + const std::shared_ptr conn = std::make_shared(socket); + conn->create_read_channel(); + + const auto start = std::chrono::steady_clock::now(); + // Give the connection a generous 10 seconds to work. + constexpr auto timeout = std::chrono::seconds(10); + + while (!conn->is_connected()) + { + auto now = std::chrono::steady_clock::now(); + if (std::chrono::duration_cast(now - start) > timeout) + { + LogFileOutput("Listener::create_connection() - Failed to establish " + "connection, timed out.\n"); + return; + } + } + + // We need to send an INIT to device 01 for this connection, then 02, ... + // until we get an error back This will determine the number of devices + // attached. + + bool still_scanning = true; + uint8_t unit_id = 1; + + // send init requests to find all the devices on this connection, or we have too many devices. + while (still_scanning && (unit_id + next_device_id_) < 255) + { + LogFileOutput("SmartPortOverSlip listener sending request for unit_id: %d\n", unit_id); + InitRequest request(Requestor::next_request_number(), unit_id); + const auto response = Requestor::send_request(request, conn.get()); + const auto init_response = dynamic_cast(response.get()); + if (init_response == nullptr) + { + LogFileOutput("SmartPortOverSlip listener ERROR, no response data found\n"); + break; + } + still_scanning = init_response->get_status() == 0; + if (still_scanning) + unit_id++; + } + + const auto start_id = next_device_id_; + const auto end_id = static_cast(start_id + unit_id - 1); + next_device_id_ = end_id + 1; + // track the connection and device ranges it reported. Further connections can add to the devices we can target. + LogFileOutput("SmartPortOverSlip listener creating connection for start: %d, end: %d\n", start_id, end_id); + insert_connection(start_id, end_id, conn); } void Listener::start() { - is_listening_ = true; - listening_thread_ = std::thread(&Listener::listener_function, this); + is_listening_ = true; + listening_thread_ = std::thread(&Listener::listener_function, this); } void Listener::stop() { - LogFileOutput("Listener::stop()\n"); - if (is_listening_) - { - // Stop listener first, otherwise the PC might reboot too fast and be picked up - is_listening_ = false; - LogFileOutput("Listener::stop() ... joining listener until it stops\n"); - listening_thread_.join(); - - LogFileOutput("Listener::stop() - closing %ld connections\n", connection_map_.size()); - for (auto &pair : connection_map_) - { - const auto &connection = pair.second; - connection->set_is_connected(false); - connection->close_connection(); - connection->join(); - } - } - next_device_id_ = 1; - connection_map_.clear(); + LogFileOutput("Listener::stop()\n"); + if (is_listening_) + { + // Stop listener first, otherwise the PC might reboot too fast and be picked up + is_listening_ = false; + LogFileOutput("Listener::stop() ... joining listener until it stops\n"); + listening_thread_.join(); + + LogFileOutput("Listener::stop() - closing %ld connections\n", connection_map_.size()); + for (auto &pair : connection_map_) + { + const auto &connection = pair.second; + connection->set_is_connected(false); + connection->close_connection(); + connection->join(); + } + } + next_device_id_ = 1; + connection_map_.clear(); #ifdef WIN32 - WSACleanup(); + WSACleanup(); #endif - LogFileOutput("Listener::stop() ... finished\n"); + LogFileOutput("Listener::stop() ... finished\n"); } // Returns the ADJUSTED lower bound of the device id, and connection. @@ -261,72 +257,78 @@ void Listener::stop() // However the apple side sees 1-8, and so we have to convert 6, 7, 8 into the target's 1, 2, 3 std::pair> Listener::find_connection_with_device(const uint8_t device_id) const { - std::pair> result; - for (const auto &kv : connection_map_) - { - if (device_id >= kv.first.first && device_id <= kv.first.second) - { - result = std::make_pair(device_id - kv.first.first + 1, kv.second); - break; - } - } - return result; + std::pair> result; + for (const auto &kv : connection_map_) + { + if (device_id >= kv.first.first && device_id <= kv.first.second) + { + result = std::make_pair(device_id - kv.first.first + 1, kv.second); + break; + } + } + return result; } std::vector> Listener::get_all_connections() const { - std::vector> connections; - for (const auto &kv : connection_map_) - { - for (uint8_t id = kv.first.first; id <= kv.first.second; ++id) - { - connections.emplace_back(id, kv.second.get()); - } - } - return connections; + std::vector> connections; + for (const auto &kv : connection_map_) + { + for (uint8_t id = kv.first.first; id <= kv.first.second; ++id) + { + connections.emplace_back(id, kv.second.get()); + } + } + return connections; } -std::pair Listener::first_two_disk_devices() const { - if (cache_valid) { - return cached_disk_devices; - } - - cached_disk_devices = {-1, -1}; // Initialize with invalid device ids - - for (uint8_t unit_number = 1; unit_number < next_device_id_; ++unit_number) { - const auto id_and_connection = GetSPoverSLIPListener().find_connection_with_device(unit_number); - if (id_and_connection.second == nullptr) continue; - - // ids from the map need adjusting down to their real ids on the device - - - // DIB request to get information block - const StatusRequest request(Requestor::next_request_number(), unit_number, 3); - - - std::unique_ptr response = Requestor::send_request(request, id_and_connection.second.get()); - - // Cast the Response to a StatusResponse - StatusResponse* statusResponse = dynamic_cast(response.get()); - - if (statusResponse) { - const std::vector& data = statusResponse->get_data(); - - // Check if data size is at least 22 and the device type is a disk, and that the disk status is ONLINE (status[0] bit 4) - if (data.size() >= 22 && (data[21] == 0x01 || data[21] == 0x02 || data[21] == 0x0A) && ((data[0] & 0x10) == 0x10)) { - // If first disk device id is not set, set it - if (cached_disk_devices.first == -1) { - cached_disk_devices.first = unit_number; - } - // Else if second disk device id is not set, set it and break the loop - else if (cached_disk_devices.second == -1) { - cached_disk_devices.second = unit_number; - break; - } - } - } - } - - cache_valid = true; - return cached_disk_devices; +std::pair Listener::first_two_disk_devices() const +{ + if (cache_valid) + { + return cached_disk_devices; + } + + cached_disk_devices = {-1, -1}; // Initialize with invalid device ids + + for (uint8_t unit_number = 1; unit_number < next_device_id_; ++unit_number) + { + const auto id_and_connection = GetSPoverSLIPListener().find_connection_with_device(unit_number); + if (id_and_connection.second == nullptr) + continue; + + // ids from the map need adjusting down to their real ids on the device + + // DIB request to get information block + const StatusRequest request(Requestor::next_request_number(), unit_number, 3); + + std::unique_ptr response = Requestor::send_request(request, id_and_connection.second.get()); + + // Cast the Response to a StatusResponse + StatusResponse *statusResponse = dynamic_cast(response.get()); + + if (statusResponse) + { + const std::vector &data = statusResponse->get_data(); + + // Check if data size is at least 22 and the device type is a disk, and that the disk status is ONLINE (status[0] bit 4) + if (data.size() >= 22 && (data[21] == 0x01 || data[21] == 0x02 || data[21] == 0x0A) && ((data[0] & 0x10) == 0x10)) + { + // If first disk device id is not set, set it + if (cached_disk_devices.first == -1) + { + cached_disk_devices.first = unit_number; + } + // Else if second disk device id is not set, set it and break the loop + else if (cached_disk_devices.second == -1) + { + cached_disk_devices.second = unit_number; + break; + } + } + } + } + + cache_valid = true; + return cached_disk_devices; } diff --git a/source/SPoverSLIP/TCPConnection.cpp b/source/SPoverSLIP/TCPConnection.cpp index 939032275..e5d886f71 100644 --- a/source/SPoverSLIP/TCPConnection.cpp +++ b/source/SPoverSLIP/TCPConnection.cpp @@ -4,133 +4,128 @@ #include #include -// clang-format off #ifdef WIN32 - #include - #pragma comment(lib, "ws2_32.lib") - #define CLOSE_SOCKET closesocket - #define SHUTDOWN_SOCKET(s) shutdown(s, SD_SEND) - #define SOCKET_ERROR_CODE WSAGetLastError() + #include + #pragma comment(lib, "ws2_32.lib") + #define CLOSE_SOCKET closesocket + #define SHUTDOWN_SOCKET(s) shutdown(s, SD_SEND) + #define SOCKET_ERROR_CODE WSAGetLastError() #else - #include - #include - #include - #define CLOSE_SOCKET close - #define SHUTDOWN_SOCKET(s) shutdown(s, SHUT_WR) - #define SOCKET_ERROR_CODE errno - #define INVALID_SOCKET -1 - #define SOCKET_ERROR -1 + #include + #include + #include + #define CLOSE_SOCKET close + #define SHUTDOWN_SOCKET(s) shutdown(s, SHUT_WR) + #define SOCKET_ERROR_CODE errno + #define INVALID_SOCKET -1 + #define SOCKET_ERROR -1 #endif -// clang-format off #include "Log.h" #include "SLIP.h" void TCPConnection::close_connection() { - if (socket_ != 0) - { - LogFileOutput("Closing TCPConnection socket\n"); - if (SHUTDOWN_SOCKET(socket_) == SOCKET_ERROR) - { - LogFileOutput("Error shutting down socket, error code: %d\n", SOCKET_ERROR_CODE); - } - if (CLOSE_SOCKET(socket_) == SOCKET_ERROR) - { - LogFileOutput("Error closing socket, error code: %d\n", SOCKET_ERROR_CODE); - } - } - socket_ = 0; + if (socket_ != 0) + { + LogFileOutput("Closing TCPConnection socket\n"); + if (SHUTDOWN_SOCKET(socket_) == SOCKET_ERROR) + { + LogFileOutput("Error shutting down socket, error code: %d\n", SOCKET_ERROR_CODE); + } + if (CLOSE_SOCKET(socket_) == SOCKET_ERROR) + { + LogFileOutput("Error closing socket, error code: %d\n", SOCKET_ERROR_CODE); + } + } + socket_ = 0; } void TCPConnection::send_data(const std::vector &data) { - if (data.empty()) - { - return; - } + if (data.empty()) + { + return; + } - const auto slip_data = SLIP::encode(data); - send(socket_, reinterpret_cast(slip_data.data()), slip_data.size(), 0); + const auto slip_data = SLIP::encode(data); + send(socket_, reinterpret_cast(slip_data.data()), slip_data.size(), 0); } void TCPConnection::create_read_channel() { - // Start a new thread to listen for incoming data - reading_thread_ = std::thread( - [self = shared_from_this()]() - { - std::vector complete_data; - std::vector buffer(1024); - bool is_initialising = true; + // Start a new thread to listen for incoming data + reading_thread_ = std::thread([self = shared_from_this()]() { + std::vector complete_data; + std::vector buffer(1024); + bool is_initialising = true; - // Set a timeout on the socket - timeval timeout; - timeout.tv_sec = 1; - timeout.tv_usec = 0; - setsockopt(self->get_socket(), SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast(&timeout), sizeof(timeout)); + // Set a timeout on the socket + timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 0; + setsockopt(self->get_socket(), SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast(&timeout), sizeof(timeout)); - while (self->is_connected() || is_initialising) - { - int valread = 0; - do - { - if (is_initialising) - { - is_initialising = false; - LogFileOutput("SmartPortOverSlip TCPConnection: connected\n"); - self->set_is_connected(true); - } + while (self->is_connected() || is_initialising) + { + int valread = 0; + do + { + if (is_initialising) + { + is_initialising = false; + LogFileOutput("SmartPortOverSlip TCPConnection: connected\n"); + self->set_is_connected(true); + } - valread = recv(self->get_socket(), reinterpret_cast(buffer.data()), static_cast(buffer.size()), 0); - const int errsv = errno; - if (valread < 0) - { - // timeout is fine, just reloop. - if (errno == EAGAIN || errno == EWOULDBLOCK || errno == 0) - { - continue; - } - // otherwise it was a genuine error. - LogFileOutput("Error in read thread for connection, errno: %d = %s\n", errsv, strerror(errsv)); - self->set_is_connected(false); - } - if (valread == 0) - { - // disconnected, close connection, should remove it too: TODO - LogFileOutput("TCPConnection: recv == 0, disconnecting\n"); - self->set_is_connected(false); - } - if (valread > 0) - { - // LogFileOutput("SmartPortOverSlip TCPConnection, inserting data, valread: %d\n", valread); - complete_data.insert(complete_data.end(), buffer.begin(), buffer.begin() + valread); - } - } while (valread == 1024); + valread = recv(self->get_socket(), reinterpret_cast(buffer.data()), static_cast(buffer.size()), 0); + const int errsv = errno; + if (valread < 0) + { + // timeout is fine, just reloop. + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == 0) + { + continue; + } + // otherwise it was a genuine error. + LogFileOutput("Error in read thread for connection, errno: %d = %s\n", errsv, strerror(errsv)); + self->set_is_connected(false); + } + if (valread == 0) + { + // disconnected, close connection, should remove it too: TODO + LogFileOutput("TCPConnection: recv == 0, disconnecting\n"); + self->set_is_connected(false); + } + if (valread > 0) + { + // LogFileOutput("SmartPortOverSlip TCPConnection, inserting data, valread: %d\n", valread); + complete_data.insert(complete_data.end(), buffer.begin(), buffer.begin() + valread); + } + } while (valread == 1024); - if (!complete_data.empty()) - { - std::vector> decoded_packets = - SLIP::split_into_packets(complete_data.data(), complete_data.size()); - // LogFileOutput("SmartPortOverSlip TCPConnection, packets decoded: %d\n", decoded_packets.size()); + if (!complete_data.empty()) + { + std::vector> decoded_packets = SLIP::split_into_packets(complete_data.data(), complete_data.size()); + // LogFileOutput("SmartPortOverSlip TCPConnection, packets decoded: %d\n", decoded_packets.size()); - if (!decoded_packets.empty()) - { - for (const auto &packet : decoded_packets) - { - if (!packet.empty()) - { - { - std::lock_guard lock(self->data_mutex_); - self->data_map_[packet[0]] = packet; - } - self->data_cv_.notify_all(); - } - } - } - complete_data.clear(); - } - } - LogFileOutput("TCPConnection::create_read_channel - thread is EXITING\n"); - }); + if (!decoded_packets.empty()) + { + for (const auto &packet : decoded_packets) + { + if (!packet.empty()) + { + { + std::lock_guard lock(self->data_mutex_); + self->data_map_[packet[0]] = packet; + } + self->data_cv_.notify_all(); + } + } + } + complete_data.clear(); + } + } + LogFileOutput("TCPConnection::create_read_channel - thread is EXITING\n"); + }); }