diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index abdca54..1f590b5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,22 +31,6 @@ jobs: with: python-version: '3.x' - # - name: Install dependencies (Ubuntu) - # if: startsWith(matrix.config.os, 'ubuntu') - # run: | - # sudo apt-get update - # sudo apt-get install -y libboost-all-dev - - # - name: Install dependencies (macOS) - # if: startsWith(matrix.config.os, 'macos') - # run: | - # brew install boost - - - name: Install dependencies (Windows) - if: startsWith(matrix.config.os, 'windows') - run: | - choco install boost-msvc-14.2 - - name: Set up CMake (All platforms) uses: lukka/get-cmake@latest @@ -60,15 +44,17 @@ jobs: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_ARCHITECTURE_ID=${{ matrix.config.arch }} cmake --build build --config Release - - name: Rename binary + - name: Rename binary using sed and set env run: | - mv build/ser2net2ser build/ser2net2ser-${{ env.VERSION }}-${{ matrix.config.os }}-${{ matrix.config.arch }} + OS_NAME=$(echo "${{ matrix.config.os }}" | sed 's/ubuntu-latest/linux/; s/macos-latest/darwin/') + echo "OS_NAME=$OS_NAME" >> $GITHUB_ENV + mv build/ser2net2ser build/ser2net2ser-${{ env.VERSION }}-$OS_NAME-${{ matrix.config.arch }} - name: Upload Artifacts uses: actions/upload-artifact@v3 with: - name: ser2net2ser-${{ env.VERSION }}-${{ matrix.config.os }}-${{ matrix.config.arch }} - path: build/ser2net2ser-${{ env.VERSION }}-${{ matrix.config.os }}-${{ matrix.config.arch }} + name: ser2net2ser-${{ env.VERSION }}-${{ env.OS_NAME }}-${{ matrix.config.arch }} + path: build/ser2net2ser-${{ env.VERSION }}-${{ env.OS_NAME }}-${{ matrix.config.arch }} create-release: needs: build-and-release @@ -85,27 +71,15 @@ jobs: - name: List all files in artifacts directory run: ls -R artifacts - # - name: Create Release - # id: create_release - # uses: actions/create-release@v1 - # env: - # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # with: - # tag_name: ${{ github.ref }} - # release_name: Release ${{ github.ref_name }} - # draft: false - # prerelease: false - - - name: debug + - name: Debug file locations run: | - pwd - ls -la artifacts/ + find artifacts/ -type f - name: Upload binaries to release uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ser2net2ser* + file: artifacts/*/* # Changed this line to include subdirectories tag: ${{ github.ref }} release_name: Release ${{ github.ref_name }} overwrite: true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f7a01ea..2031792 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,7 +7,44 @@ on: branches: [ master ] jobs: - build: + statick-checks: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Set up CMake (All platforms) + uses: lukka/get-cmake@latest + + - name: Set up Clang + uses: egor-tensin/setup-clang@v1 + with: + version: latest + + - name: Install dependencies + run: sudo apt-get install -y cppcheck clang-format + + - name: Run Cppcheck + run: cppcheck --enable=all --std=c++17 --language=c++ --error-exitcode=1 --output-file=cppcheck_report.txt ./src + continue-on-error: true + + - name: Check code format (Clang-Format) + run: clang-format -i -style=file $(find . -name '*.cpp' -or -name '*.h') + + - name: Upload Cppcheck Results + uses: actions/upload-artifact@v2 + if: always() + with: + name: cppcheck-results + path: cppcheck_report.txt + + - name: Upload Clang-Format Results + uses: actions/upload-artifact@v2 + if: always() + with: + name: format-results + path: $(find . -name '*.cpp' -or -name '*.h') + + tests: runs-on: ${{ matrix.config.os }} strategy: fail-fast: false @@ -29,22 +66,6 @@ jobs: with: python-version: '3.x' - # - name: Install dependencies (Ubuntu) - # if: startsWith(matrix.config.os, 'ubuntu') - # run: | - # sudo apt-get update - # sudo apt-get install -y libboost-all-dev - - # - name: Install dependencies (macOS) - # if: startsWith(matrix.config.os, 'macos') - # run: | - # brew install boost - - # - name: Install dependencies (Windows) - # if: startsWith(matrix.config.os, 'windows') - # run: | - # choco install boost-msvc-14.2 - - name: Set up CMake (All platforms) uses: lukka/get-cmake@latest diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a8a735..12386c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,6 @@ include_directories(${CMAKE_SOURCE_DIR}/include) # Create core library without Logger.cpp, which is now in the logging library add_library(core_lib src/Logger.cpp - src/SerialServer.cpp src/SerialClient.cpp src/VirtualSerialPort.cpp src/SerialPort.cpp diff --git a/README.md b/README.md index a1b5811..ae69a32 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Motivation -I've been using `socat` + `ser2net` for sharing serial devices over network for some time, mainly in my other project -> [kubeserial]. I've run into few limitations so I created this project to recreate the behaviour of `socat` + `ser2net` and then solve connection issues I've been having. +I've been using `socat` + `ser2net` for sharing serial devices over network for some time, mainly in my other project [kubeserial]. I've run into few limitations so I created this project to recreate the behaviour of `socat` + `ser2net` and then solve connection issues I've been having. ## Requirements diff --git a/docs/connect.png b/docs/connect.png index 8b23794..69d3e23 100644 Binary files a/docs/connect.png and b/docs/connect.png differ diff --git a/docs/serve.png b/docs/serve.png index 815f732..449dd96 100644 Binary files a/docs/serve.png and b/docs/serve.png differ diff --git a/include/Logger.h b/include/Logger.h index a217781..d6d4c1c 100644 --- a/include/Logger.h +++ b/include/Logger.h @@ -4,6 +4,9 @@ #include #include #include +#include +#include +#include class Logger { public: diff --git a/include/SerialPort.h b/include/SerialPort.h index e7fd541..9a73346 100644 --- a/include/SerialPort.h +++ b/include/SerialPort.h @@ -1,6 +1,11 @@ #ifndef SERIALPORT_H #define SERIALPORT_H +#include +#include +#include +#include +#include #include #include #include diff --git a/include/SerialServer.h b/include/SerialServer.h deleted file mode 100644 index 4827c38..0000000 --- a/include/SerialServer.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef SERIALSERVER_H -#define SERIALSERVER_H - -#include -#include -#include -#include -#include - -#include "SerialPort.h" -#include "TCPServer.h" - -class SerialServer { -public: - SerialServer(const std::string& device, unsigned int baudRate, unsigned int port); - ~SerialServer(); - - void run(); - -private: - SerialPort serial_port_; - TcpServer server_; -}; - -#endif // SERIALSERVER_H diff --git a/include/SocketClient.h b/include/SocketClient.h index 15eec3a..0b6ccf3 100644 --- a/include/SocketClient.h +++ b/include/SocketClient.h @@ -24,24 +24,24 @@ class SocketClient { Logger(Logger::Level::Info) << "Start connecting to socet server:" << server_ip << ":" << server_port; sock_fd = socket(AF_INET, SOCK_STREAM, 0); if (sock_fd == -1) { - Logger(Logger::Level::Error) << "Error creating socket: " << strerror(errno) << std::endl; + Logger(Logger::Level::Error) << "Error creating socket: " << strerror(errno); return false; } server_addr.sin_family = AF_INET; server_addr.sin_port = htons(server_port); if (inet_pton(AF_INET, server_ip, &server_addr.sin_addr) <= 0) { - Logger(Logger::Level::Error) << "Invalid address/ Address not supported" << std::endl; + Logger(Logger::Level::Error) << "Invalid address/ Address not supported"; return false; } if (connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { - Logger(Logger::Level::Error) << "Connection Failed: " << strerror(errno) << std::endl; + Logger(Logger::Level::Error) << "Connection Failed: " << strerror(errno); exit(1); return false; } - Logger(Logger::Level::Info) << "Connected to server at " << server_ip << ":" << server_port << std::endl; + Logger(Logger::Level::Info) << "Connected to server at " << server_ip << ":" << server_port; return true; } diff --git a/include/TCPServer.h b/include/TCPServer.h index 8495a99..c731290 100644 --- a/include/TCPServer.h +++ b/include/TCPServer.h @@ -5,6 +5,10 @@ #include #include #include +#include +#include +#include + #include "SerialPort.h" #include "Logger.h" diff --git a/src/Logger.cpp b/src/Logger.cpp index ef9166d..03d618f 100644 --- a/src/Logger.cpp +++ b/src/Logger.cpp @@ -1,7 +1,4 @@ #include "Logger.h" -#include -#include -#include std::mutex Logger::mtx; diff --git a/src/SerialClient.cpp b/src/SerialClient.cpp index 73f1f8f..2041fe5 100644 --- a/src/SerialClient.cpp +++ b/src/SerialClient.cpp @@ -3,7 +3,6 @@ using std::string; using std::cout; using std::cerr; -using std::endl; SerialClient::SerialClient(const std::string& server_ip, unsigned short server_port, const std::string& vsp_name) : vsp_(vsp_name) { @@ -25,7 +24,7 @@ void SerialClient::run() { FD_SET(socketClient_.sock_fd, &read_fds); if (select(max_fd, &read_fds, NULL, NULL, NULL) < 0 && errno != EINTR) { - Logger(Logger::Level::Error) << "Select error: " << strerror(errno) << std::endl; + Logger(Logger::Level::Error) << "Select error: " << strerror(errno); break; } @@ -36,10 +35,10 @@ void SerialClient::run() { Logger(Logger::Level::Info) << "From PTY: " << buffer; socketClient_.sendToServer(buffer, bytes_read); } else if (bytes_read == 0) { - Logger(Logger::Level::Info) << "PTY closed." << std::endl; + Logger(Logger::Level::Error) << "PTY closed."; break; } else { - Logger(Logger::Level::Error) << "Error reading from PTY: " << strerror(errno) << std::endl; + Logger(Logger::Level::Error) << "Error reading from PTY: " << strerror(errno); } } @@ -50,10 +49,10 @@ void SerialClient::run() { Logger(Logger::Level::Info) << "From Server: " << buffer; vsp_.async_write(buffer, bytes_read); } else if (bytes_read == 0) { - Logger(Logger::Level::Info) << "Server closed connection." << std::endl; + Logger(Logger::Level::Info) << "Server closed connection."; break; } else { - Logger(Logger::Level::Error) << "Error reading from socket: " << strerror(errno) << std::endl; + Logger(Logger::Level::Error) << "Error reading from socket: " << strerror(errno); } } } diff --git a/src/SerialPort.cpp b/src/SerialPort.cpp index b52f4e4..b984810 100644 --- a/src/SerialPort.cpp +++ b/src/SerialPort.cpp @@ -1,20 +1,15 @@ #include "SerialPort.h" -#include -#include -#include -#include -#include SerialPort::SerialPort(const std::string& device, int baud_rate) { - Logger(Logger::Level::Info) << "SerialPort init start - device " << device << " - baudRate - " << baud_rate << std::endl; + Logger(Logger::Level::Info) << "Initializing serial port connection to " << device << " baudRate " << baud_rate; serial_fd = open(device.c_str(), O_RDWR | O_NOCTTY); if (serial_fd < 0) { - Logger(Logger::Level::Error) << "Error opening serial port: " << strerror(errno) << std::endl; + Logger(Logger::Level::Error) << "Error opening serial port: " << strerror(errno); throw std::runtime_error("Error opening serial port"); } configurePort(baud_rate); - Logger(Logger::Level::Info) << "SerialPort init finish" << std::endl; + Logger(Logger::Level::Info) << "SerialPort init finish"; } SerialPort::~SerialPort() { @@ -26,7 +21,7 @@ void SerialPort::configurePort(int baud_rate) { struct termios tty; memset(&tty, 0, sizeof tty); if (tcgetattr(serial_fd, &tty) != 0) { - Logger(Logger::Level::Error) << "tcgetattr failed: " << strerror(errno) << std::endl; + Logger(Logger::Level::Error) << "tcgetattr failed: " << strerror(errno); throw std::runtime_error("tcgetattr failed"); } @@ -49,25 +44,25 @@ void SerialPort::configurePort(int baud_rate) { tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout if (tcsetattr(serial_fd, TCSANOW, &tty) != 0) { - Logger(Logger::Level::Error) << "tcsetattr failed: " << strerror(errno) << std::endl; + Logger(Logger::Level::Error) << "tcsetattr failed: " << strerror(errno); throw std::runtime_error("tcsetattr failed"); } } void SerialPort::writeData(const std::string& data) { - Logger(Logger::Level::Info) << "SerialPort write data - " << data; + Logger(Logger::Level::Info) << "SerialPort - write: " << data; write(serial_fd, data.c_str(), data.size()); } void SerialPort::readLoop() { - Logger(Logger::Level::Info) << "SerialPort start reading loop"; + Logger(Logger::Level::Info) << "SerialPort - start reading loop."; while (keep_reading) { char buf[1024]; int n = read(serial_fd, buf, sizeof(buf) - 1); if (n > 0) { buf[n] = '\0'; // Ensure null-termination - Logger(Logger::Level::Info) << "SerialPort read - " << buf; + Logger(Logger::Level::Info) << "SerialPort - read:" << buf; std::lock_guard lock(mtx); read_buffer.push(std::string(buf)); cv.notify_one(); @@ -87,11 +82,9 @@ void SerialPort::stopReading() { } std::string SerialPort::readData() { - Logger(Logger::Level::Info) << "SerialPort read data"; std::unique_lock lock(mtx); cv.wait(lock, [this]{ return !read_buffer.empty(); }); std::string data = read_buffer.front(); read_buffer.pop(); - Logger(Logger::Level::Info) << "SerialPort read data - got data - " << data; return data; } diff --git a/src/SerialServer.cpp b/src/SerialServer.cpp deleted file mode 100644 index cee3644..0000000 --- a/src/SerialServer.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include -#include -#include -#include - -#include "SerialServer.h" - -SerialServer::SerialServer(const std::string& device, unsigned int baud_rate, unsigned int port) - : serial_port_(device, baud_rate), server_(port, serial_port_) {} - -SerialServer::~SerialServer() { - server_.stop(); -} - -void SerialServer::run() { - Logger(Logger::Level::Info) << "SerialServer::run"; - try { - server_.run(); - } catch (const std::exception& e) { - std::cerr << "Error: " << e.what() << std::endl; - } -} diff --git a/src/TCPServer.cpp b/src/TCPServer.cpp index 7d0ac5e..96e0beb 100644 --- a/src/TCPServer.cpp +++ b/src/TCPServer.cpp @@ -1,10 +1,7 @@ #include "TCPServer.h" -#include -#include -#include TcpServer::TcpServer(int port, SerialPort& serial) : port_(port), serial_(serial), is_running_(false) { - Logger(Logger::Level::Info) << "TCPServer init start"; + Logger(Logger::Level::Info) << "TCPServer init start, creating server on port " << port_; if ((server_fd_ = socket(AF_INET, SOCK_STREAM, 0)) == 0) { throw std::runtime_error("Socket creation failed"); } @@ -46,7 +43,7 @@ void TcpServer::run() { } std::thread clientThread(&TcpServer::handleClient, this, clientSocket); - clientThread.detach(); // Detach the thread to handle multiple clients + clientThread.detach(); } } @@ -57,26 +54,26 @@ void TcpServer::stop() { void TcpServer::handleClient(int client_socket) { char buffer[1024]; - Logger(Logger::Level::Info) << "HandleClient start - read thread from serial"; + Logger(Logger::Level::Info) << "HandleClient - start serial reading thread."; serial_.startReading(); std::thread readThread([&]() { while (true) { - std::string response = serial_.readData(); // This will block until data is available + std::string response = serial_.readData(); + Logger(Logger::Level::Info) << "HandleClient - write: " << response; if (!response.empty()) { - Logger(Logger::Level::Info) << "HandleClient start - write to client " << response.c_str(); write(client_socket, response.c_str(), response.size()); } } }); - Logger(Logger::Level::Info) << "HandleClient start - read data from client"; + Logger(Logger::Level::Info) << "HandleClient - start read data from client"; while (true) { memset(buffer, 0, sizeof(buffer)); ssize_t bytesReceived = read(client_socket, buffer, sizeof(buffer)); if (bytesReceived <= 0) { break; } - Logger(Logger::Level::Info) << "HandleClient start - write to serial " << buffer; + Logger(Logger::Level::Info) << "HandleClient - read: " << buffer; serial_.writeData(std::string(buffer, bytesReceived)); } diff --git a/src/VirtualSerialPort.cpp b/src/VirtualSerialPort.cpp index 2410a8c..5aa4d2e 100644 --- a/src/VirtualSerialPort.cpp +++ b/src/VirtualSerialPort.cpp @@ -16,7 +16,7 @@ VirtualSerialPort::VirtualSerialPort(const std::string& device) throw std::runtime_error("Failed to grant or unlock PTY"); } Logger(Logger::Level::Info) << "PTY grant and unlock successful"; - Logger(Logger::Level::Info) << "Slave PTY name: " << slave_name << std::endl; + Logger(Logger::Level::Info) << "Slave PTY name: " << slave_name; // Attempt to create a symbolic link from slave_name to "/dev/ttyUSB0" if (symlink(slave_name, device_name_.c_str()) == -1) { diff --git a/src/alternative_server_main.cpp b/src/alternative_server_main.cpp deleted file mode 100644 index 09f3666..0000000 --- a/src/alternative_server_main.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -void handleClient(int clientSocket) { - char buffer[1024]; - while (true) { - memset(buffer, 0, sizeof(buffer)); - ssize_t bytesReceived = read(clientSocket, buffer, sizeof(buffer)); - if (bytesReceived <= 0) { - std::cerr << "Read error or connection closed by client" << std::endl; - break; - } - - std::cout << "Received: " << buffer << std::endl; - write(clientSocket, buffer, bytesReceived); - } - - close(clientSocket); -} - -int main() { - const int port = 3333; - int serverFd = socket(AF_INET, SOCK_STREAM, 0); - if (serverFd < 0) { - std::cerr << "Cannot open socket" << std::endl; - return 1; - } - - struct sockaddr_in serverAddr; - memset(&serverAddr, 0, sizeof(serverAddr)); - serverAddr.sin_family = AF_INET; - serverAddr.sin_addr.s_addr = INADDR_ANY; // Listen on all network interfaces - serverAddr.sin_port = htons(port); - - if (bind(serverFd, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0) { - std::cerr << "Cannot bind to port " << port << std::endl; - return 1; - } - - if (listen(serverFd, 10) < 0) { // Listen for up to 10 connections - std::cerr << "Listen failed" << std::endl; - return 1; - } - - std::cout << "Server is listening on port " << port << std::endl; - - while (true) { - struct sockaddr_in clientAddr; - socklen_t clientLen = sizeof(clientAddr); - int clientSocket = accept(serverFd, (struct sockaddr *) &clientAddr, &clientLen); - if (clientSocket < 0) { - std::cerr << "Cannot accept connection" << std::endl; - continue; - } - - std::thread clientThread(handleClient, clientSocket); - clientThread.detach(); // Detach the thread to handle multiple clients - } - - close(serverFd); - return 0; -} diff --git a/src/main.cpp b/src/main.cpp index 299a9f9..84e5da0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,18 +1,22 @@ #include #include #include + #include "Logger.h" #include "SerialClient.h" -#include "SerialServer.h" +#include "SerialPort.h" +#include "TCPServer.h" using std::string; -using std::cout; -using std::cerr; -using std::endl; void setup_and_run_server(const string& device, unsigned int baud_rate, unsigned int port) { - SerialServer server(device, baud_rate, port); - server.run(); + SerialPort serialDevice(device, baud_rate); + TcpServer server(port, serialDevice); + try { + server.run(); + } catch (const std::exception& e) { + Logger(Logger::Level::Error) << "Error: " << e.what(); + } } void setup_and_run_client(const string& server_ip, unsigned short port, const string& vsp) { @@ -22,7 +26,7 @@ void setup_and_run_client(const string& server_ip, unsigned short port, const st int main(int argc, char* argv[]) { if (argc < 2) { - cerr << "Usage: ser2net2ser [options]\n"; + Logger(Logger::Level::Error) << "Usage: ser2net2ser [options]\n"; return 1; } @@ -43,7 +47,7 @@ int main(int argc, char* argv[]) { } else if (arg == "--port" || arg == "-p") { if (i + 1 < argc) port = std::stoi(argv[i + 1]); } else if (arg == "--help" || arg == "-h") { - cout << "Usage: ser2net2ser serve [--device ] [--baud ] [--port ]\n"; + Logger(Logger::Level::Info) << "Usage: ser2net2ser serve [--device ] [--baud ] [--port ]\n"; return 0; } } @@ -63,13 +67,13 @@ int main(int argc, char* argv[]) { } else if (arg == "--vsp" || arg == "-v") { if (i + 1 < argc) vsp = argv[i + 1]; } else if (arg == "--help" || arg == "-h") { - cout << "Usage: ser2net2ser connect [--server ] [--port ] --vsp \n"; + Logger(Logger::Level::Info) << "Usage: ser2net2ser connect [--server ] [--port ] --vsp \n"; return 0; } } if (vsp.empty()) { - cerr << "Virtual serial port name must be specified with --vsp.\n"; + Logger(Logger::Level::Error) << "Virtual serial port name must be specified with --vsp.\n"; return 1; } @@ -78,7 +82,7 @@ int main(int argc, char* argv[]) { throw std::invalid_argument("Invalid command provided. Use 'serve' or 'connect'."); } } catch (const std::exception& e) { - cerr << "Error: " << e.what() << endl; + Logger(Logger::Level::Error) << "Error: " << e.what(); return 1; }