diff --git a/demos/client_demo.cpp b/demos/client_demo.cpp index e1b54eb..2ef1768 100644 --- a/demos/client_demo.cpp +++ b/demos/client_demo.cpp @@ -26,10 +26,10 @@ int main(int argc, char *argv[]) { } usleep(1000); } - std::cerr << "On termination error code = " << stc.GetErrorCode().err_no << " Error detail = " << stc.GetErrorCode().detail << std::endl; + std::cerr << "On termination error code = " << stc.GetErrorCode().err_no << " Error detail = " << stc.GetErrorCode().err_detail << std::endl; sleep(5); // Delay to read the message before terminal emulator window closes. } else { - std::cerr << "On termination error code = " << stc.GetErrorCode().err_no << " Error detail = " << stc.GetErrorCode().detail << std::endl; + std::cerr << "On termination error code = " << stc.GetErrorCode().err_no << " Error detail = " << stc.GetErrorCode().err_detail << std::endl; sleep(5); // Delay to read the message before terminal emulator window closes. } return 0; diff --git a/demos/server_demo.cpp b/demos/server_demo.cpp index c15e78f..80cc86d 100644 --- a/demos/server_demo.cpp +++ b/demos/server_demo.cpp @@ -7,7 +7,7 @@ int main (void) { - SatTerm_Server sts("test_server", "./client_demo"); + SatTerm_Server sts("test_server", "./client_demo", true, {"com_1", "com_2"}); if (sts.IsConnected()) { size_t message_count = 10; @@ -27,11 +27,11 @@ int main (void) { usleep(1000); } - std::cerr << "On termination error code = " << sts.GetErrorCode().err_no << " Error detail = " << sts.GetErrorCode().detail << std::endl; + std::cerr << "On termination error code = " << sts.GetErrorCode().err_no << " Error detail = " << sts.GetErrorCode().err_detail << std::endl; } else { - std::cerr << "On termination error code = " << sts.GetErrorCode().err_no << " Error detail = " << sts.GetErrorCode().detail << std::endl; + std::cerr << "On termination error code = " << sts.GetErrorCode().err_no << " Error detail = " << sts.GetErrorCode().err_detail << std::endl; } return 0; diff --git a/src/satellite_terminal.h b/src/satellite_terminal.h index 8e73f65..c9fde14 100644 --- a/src/satellite_terminal.h +++ b/src/satellite_terminal.h @@ -12,8 +12,8 @@ #include // std::string. #include // std::vector. -#include -#include +#include // std::map. +#include // std::unique_ptr. #include "satterm_port.h" @@ -36,15 +36,12 @@ class SatTerm_Agent { void SetConnectedFlag(bool is_connected); protected: - bool CreatePorts(bool is_server, bool is_in_port, std::string const& working_path, std::vector port_identifiers, - bool display_messages, char end_char, std::map>& ports); bool OpenPorts(std::map>& ports, unsigned long timeout_seconds = 5); error_descriptor m_error_code = {0, ""}; bool m_display_messages = false; - std::map> m_in_ports = {}; - std::map> m_out_ports = {}; - default_port m_default_port = {"", ""}; + std::map> m_ports = {}; + std::string m_default_port_identifier = ""; std::string m_stop_port_identifier = ""; std::string m_working_path = ""; std::string m_identifier = ""; @@ -56,26 +53,28 @@ class SatTerm_Agent { class SatTerm_Server : public SatTerm_Agent { public: SatTerm_Server(std::string const& identifier, std::string const& path_to_client_binary, bool display_messages = true, + std::vector port_identifiers = {"comms"}, std::string const& stop_message = "q", std::string const& path_to_terminal_emulator_paths = "./terminal_emulator_paths.txt", - std::vector out_port_identifiers = {"server_out"}, std::vector in_port_identifiers = {"server_in"}, - char end_char = 3, std::string const& stop_port_identifier = "", std::string const& stop_message = "q"); + char end_char = 3, std::string const& stop_port_identifier = "", unsigned long timeout_seconds = 5); ~SatTerm_Server(); - + private: std::string GetWorkingPath(void); + bool CreateServerPorts(std::string const& working_path, std::vector port_identifiers, + bool display_messages, char end_char, std::map>& ports); std::vector LoadTerminalEmulatorPaths(std::string const& file_path); pid_t StartClient(std::string const& path_to_terminal_emulator_paths, std::string const& path_to_client_binary, std::string const& working_path, - char end_char, std::string const& stop_message, std::vector in_port_identifiers, - std::vector out_port_identifiers); - + char end_char, std::string const& stop_message, std::vector port_identifiers); }; class SatTerm_Client : public SatTerm_Agent { public: SatTerm_Client(std::string const& identifier, int argc, char* argv[], bool display_messages = true); ~SatTerm_Client(); - + private: size_t GetArgStartIndex(std::string const& arg_start_delimiter, int argc, char* argv[]); std::vector ParseFifoPaths(size_t argv_start_index, size_t argv_count, char* argv[]); + void CreateClientPorts(std::string const& working_path, std::vector port_identifiers, + bool display_messages, char end_char, std::map>& ports); }; diff --git a/src/satterm_agent.cpp b/src/satterm_agent.cpp index 35f2e92..7fc04a6 100644 --- a/src/satterm_agent.cpp +++ b/src/satterm_agent.cpp @@ -10,37 +10,20 @@ // For a copy, see . // ----------------------------------------------------------------------------------------------------- -#include +#include // std::cout, std::cerr, std::endl. #include // std::ifstream. -#include -#include -#include -#include -#include +#include // std::out_of_range. +#include // std::string, std::to_string. +#include // std::map. +#include // std::vector. +#include // std::unique_ptr. #include "satellite_terminal.h" -bool SatTerm_Agent::CreatePorts(bool is_server, bool is_in_port, std::string const& working_path, std::vector port_identifiers, - bool display_messages, char end_char, std::map>& ports) { - bool success = true; - for (auto const& port_identifier : port_identifiers) { - ports.emplace(port_identifier, std::make_unique(is_server, is_in_port, working_path, port_identifier, display_messages, end_char)); - if (is_server) { - if (!(ports.at(port_identifier)->IsCreated())) { - success = false; - m_error_code = ports.at(port_identifier)->GetErrorCode(); - break; - } - } - } - return success; -} - bool SatTerm_Agent::OpenPorts(std::map>& ports, unsigned long timeout_seconds) { bool success = true; for (auto const& current_port : ports) { - current_port.second->Open(timeout_seconds); - if (!(current_port.second->IsOpened())) { + if (!(current_port.second->Open(timeout_seconds))) { success = false; m_error_code = current_port.second->GetErrorCode(); break; @@ -50,72 +33,72 @@ bool SatTerm_Agent::OpenPorts(std::map>& port } std::string SatTerm_Agent::GetMessage(bool capture_end_char, unsigned long timeout_seconds) { - return GetMessage(m_default_port.in, capture_end_char, timeout_seconds); + return GetMessage(m_default_port_identifier, capture_end_char, timeout_seconds); } -std::string SatTerm_Agent::GetMessage(std::string const& in_port_identifier, bool capture_end_char, unsigned long timeout_seconds) { +std::string SatTerm_Agent::GetMessage(std::string const& port_identifier, bool capture_end_char, unsigned long timeout_seconds) { m_error_code = {0, ""}; std::string received_message = ""; try { - received_message = m_in_ports.at(in_port_identifier)->GetMessage(capture_end_char, timeout_seconds); - m_error_code = m_in_ports.at(in_port_identifier)->GetErrorCode(); + received_message = m_ports.at(port_identifier)->GetMessage(capture_end_char, timeout_seconds); + m_error_code = m_ports.at(port_identifier)->GetErrorCode(); if (m_error_code.err_no != 0) { - SetConnectedFlag(m_in_ports.at(in_port_identifier)->IsOpened()); + SetConnectedFlag(m_ports.at(port_identifier)->IsOpened()); } } catch (const std::out_of_range& oor) { m_error_code = {-1, "GetMessage()_OOR_port_id"}; - std::string error_message = "GetMessage() error - No port matches identifier " + in_port_identifier; + std::string error_message = "GetMessage() error - No port matches identifier " + port_identifier; std::cerr << error_message << std::endl; } return received_message; } std::string SatTerm_Agent::SendMessage(std::string const& message, unsigned long timeout_seconds) { - return SendMessage(message, m_default_port.out, timeout_seconds); + return SendMessage(message, m_default_port_identifier, timeout_seconds); } -std::string SatTerm_Agent::SendMessage(std::string const& message, std::string const& out_port_identifier, unsigned long timeout_seconds) { +std::string SatTerm_Agent::SendMessage(std::string const& message, std::string const& port_identifier, unsigned long timeout_seconds) { m_error_code = {0, ""}; std::string remaining_message = ""; try { - remaining_message = m_out_ports.at(out_port_identifier)->SendMessage(message, timeout_seconds); - m_error_code = m_out_ports.at(out_port_identifier)->GetErrorCode(); + remaining_message = m_ports.at(port_identifier)->SendMessage(message, timeout_seconds); + m_error_code = m_ports.at(port_identifier)->GetErrorCode(); if (m_error_code.err_no != 0) { - SetConnectedFlag(m_out_ports.at(out_port_identifier)->IsOpened()); + SetConnectedFlag(m_ports.at(port_identifier)->IsOpened()); } } catch (const std::out_of_range& oor) { m_error_code = {-1, "SendMessage()_OOR_port_id"}; - std::string error_message = "SendMessage() error - No port matches identifier " + out_port_identifier; + std::string error_message = "SendMessage() error - No port matches identifier " + port_identifier; std::cerr << error_message << std::endl; } return remaining_message; } size_t SatTerm_Agent::SendBytes(const char* bytes, size_t byte_count, unsigned long timeout_seconds) { - return SendBytes(bytes, byte_count, m_default_port.out, timeout_seconds); + return SendBytes(bytes, byte_count, m_default_port_identifier, timeout_seconds); } -size_t SatTerm_Agent::SendBytes(const char* bytes, size_t byte_count, std::string const& out_port_identifier, unsigned long timeout_seconds) { +size_t SatTerm_Agent::SendBytes(const char* bytes, size_t byte_count, std::string const& port_identifier, unsigned long timeout_seconds) { m_error_code = {0, ""}; size_t sent_bytes = 0; try { - sent_bytes = m_out_ports.at(out_port_identifier)->SendBytes(bytes, byte_count, timeout_seconds); - m_error_code = m_out_ports.at(out_port_identifier)->GetErrorCode(); + sent_bytes = m_ports.at(port_identifier)->SendBytes(bytes, byte_count, timeout_seconds); + m_error_code = m_ports.at(port_identifier)->GetErrorCode(); if (m_error_code.err_no != 0) { - SetConnectedFlag(m_out_ports.at(out_port_identifier)->IsOpened()); + SetConnectedFlag(m_ports.at(port_identifier)->IsOpened()); } } catch (const std::out_of_range& oor) { m_error_code = {-1, "SendBytes()_OOR_port_id"}; - std::string error_message = "SendBytes() error - No port matches identifier " + out_port_identifier; + std::string error_message = "SendBytes() error - No port matches identifier " + port_identifier; std::cerr << error_message << std::endl; } return sent_bytes; diff --git a/src/satterm_client.cpp b/src/satterm_client.cpp index d361130..cde9a8b 100644 --- a/src/satterm_client.cpp +++ b/src/satterm_client.cpp @@ -10,10 +10,10 @@ // For a copy, see . // ----------------------------------------------------------------------------------------------------- -#include -#include -#include -#include +#include // std::cout, std::cerr, std::endl. +#include // std::string, std::stoi. +#include // std::map. +#include // std::vector. #include "satellite_terminal.h" @@ -21,43 +21,33 @@ SatTerm_Client::SatTerm_Client(std::string const& identifier, int argc, char* ar m_identifier = identifier; m_display_messages = display_messages; - size_t in_port_count = 0; - size_t out_port_count = 0; - std::vector out_port_identifiers = {}; - std::vector in_port_identifiers = {}; + size_t port_count = 0; + std::vector port_identifiers = {}; size_t argv_start_index = GetArgStartIndex("client_args", argc, argv); - + bool success = false; if (argv_start_index != 0) { + success = true; m_working_path = std::string(argv[argv_start_index]); m_end_char = (char)(std::stoi(std::string(argv[argv_start_index + 1]))); m_stop_message = std::string(argv[argv_start_index + 2]); - out_port_count = std::stoi(std::string(argv[argv_start_index + 3])); - in_port_count = std::stoi(std::string(argv[argv_start_index + 4])); - out_port_identifiers = ParseFifoPaths(argv_start_index + 5, out_port_count, argv); - in_port_identifiers = ParseFifoPaths(argv_start_index + 5 + out_port_count, in_port_count, argv); + port_count = std::stoi(std::string(argv[argv_start_index + 3])); + port_identifiers = ParseFifoPaths(argv_start_index + 4, port_count, argv); - m_default_port.out = out_port_identifiers[0]; - m_default_port.in = in_port_identifiers[0]; + m_default_port_identifier = port_identifiers[0]; if (m_display_messages) { std::string message = "Client working path is " + m_working_path; std::cerr << message << std::endl; } - success = CreatePorts(false, false, m_working_path, out_port_identifiers, m_display_messages, m_end_char, m_out_ports); - if (success) { - success = CreatePorts(false, true, m_working_path, in_port_identifiers, m_display_messages, m_end_char, m_in_ports); - } + CreateClientPorts(m_working_path, port_identifiers, m_display_messages, m_end_char, m_ports); unsigned long timeout_seconds = 5; - if (success) { - success = OpenPorts(m_out_ports, timeout_seconds); - } if (success) { - success = OpenPorts(m_in_ports, timeout_seconds); + success = OpenPorts(m_ports, timeout_seconds); } if (success) { @@ -84,15 +74,12 @@ SatTerm_Client::SatTerm_Client(std::string const& identifier, int argc, char* ar SatTerm_Client::~SatTerm_Client() { // If shutdown is occurring, the server will poll m_default_port.in until it gets EOF, then everything will be closed and unlinked at the // server end. To make sure that unlink() deletes the files, we close any that are open here, saving m_default_port.out at this end for last. - for (const auto& port : m_in_ports) { - port.second->Close(); - } - for (const auto& port : m_out_ports) { - if (port.first != m_default_port.out) { + for (const auto& port : m_ports) { + if (port.first != m_default_port_identifier) { port.second->Close(); } } - m_out_ports.at(m_default_port.out)->Close(); + m_ports.at(m_default_port_identifier)->Close(); // There should not be any more to do. Pointers in m_in_ports and m_out_ports are stored as unique_ptr, so when the maps // are destroyed now the destructors for the Ports should be called automatically. In these the fifos will be unlink()ed if m_is_server is set. } @@ -122,3 +109,10 @@ std::vector SatTerm_Client::ParseFifoPaths(size_t argv_start_index, } return paths_container; } + +void SatTerm_Client::CreateClientPorts(std::string const& working_path, std::vector port_identifiers, + bool display_messages, char end_char, std::map>& ports) { + for (auto const& port_identifier : port_identifiers) { + ports.emplace(port_identifier, std::make_unique(working_path, port_identifier, display_messages, end_char)); + } +} diff --git a/src/satterm_port.cpp b/src/satterm_port.cpp index 00b40ed..4b1770a 100644 --- a/src/satterm_port.cpp +++ b/src/satterm_port.cpp @@ -10,10 +10,10 @@ // For a copy, see . // ----------------------------------------------------------------------------------------------------- -#include -#include -#include -#include +#include // std::cout, std::cerr, std::endl; +#include // std::string, std::to_string. +#include // std::map. +#include // std::vector. #include // time(). #include // perror(). @@ -21,99 +21,32 @@ #include // write(), read(), close(), unlink(). #include // mkfifo(). #include // errno. -#include // SIGPIPE, SIG_IGN. #include "satterm_port.h" -Port::Port(bool is_server, bool is_in_port, std::string const& working_path, std::string const& identifier, bool display_messages, char end_char) { - m_is_server = is_server; - m_is_in_port = is_in_port; - m_working_path = working_path; - m_identifier = identifier; - m_display_messages = display_messages; - m_end_char = end_char; - - signal(SIGPIPE, SIG_IGN); - - if (m_is_server) { - Create(); - } -} - -Port::~Port() { - if (m_opened) { - close(m_fifo_descriptor); - } - if ((m_is_server) && (m_created)) { - Close(); - // Unlink. - std::string fifo_path = m_working_path + m_identifier; - int status = unlink(fifo_path.c_str()); - if ((status < 0) && m_display_messages) { - std::string error_message = "Unable to unlink fifo at path " + fifo_path; - perror(error_message.c_str()); - } - } -} - -void Port::Close(void) { - close(m_fifo_descriptor); - m_opened = false; -} - -void Port::Create(void) { - m_error_code = {0, ""}; - - std::string fifo_path = m_working_path + m_identifier; - - remove(fifo_path.c_str()); // If temporary file already exists, delete it. - - int status = mkfifo(fifo_path.c_str(), S_IFIFO|0666); - - if (status < 0) { - // Info on possible errors for mkfifo() - https://pubs.opengroup.org/onlinepubs/009696799/functions/mkfifo.html - m_error_code = {errno, "mkfifo()"}; - if (m_display_messages) { - std::string error_message = "Server mkfifo() error trying to create fifo at path " + fifo_path; - perror(error_message.c_str()); - } - m_created = false; - } else { - m_created = true; - } -} - -void Port::Open(unsigned long timeout_seconds) { - if (m_is_in_port) { - m_opened = OpenRxFifo(m_working_path + m_identifier, timeout_seconds); - } else { - m_opened = OpenTxFifo(m_working_path + m_identifier, timeout_seconds); - } -} - bool Port::OpenRxFifo(std::string const& fifo_path, unsigned long timeout_seconds) { m_error_code = {0, ""}; - int fifo = open(fifo_path.c_str(), O_RDONLY | O_NONBLOCK); + int fifo_descriptor = open(fifo_path.c_str(), O_RDONLY | O_NONBLOCK); bool success = false; - if (!(fifo < 0)) { - m_fifo_descriptor = fifo; + if (!(fifo_descriptor < 0)) { + m_fifos.in.descriptor = fifo_descriptor; m_current_message = ""; std::string init_message = GetMessage(false, timeout_seconds); if (init_message == "init") { if (m_display_messages) { - std::string message = "In Port " + m_identifier + " opened fifo " + fifo_path + " for reading on descriptor " + std::to_string(fifo); + std::string message = "Port " + m_identifier + " opened fifo " + fifo_path + " for reading on descriptor " + std::to_string(fifo_descriptor); std::cerr << message << std::endl; } success = true; } else { - m_fifo_descriptor = 0; + m_fifos.in.descriptor = 0; if (m_error_code == error_descriptor{-1, "GetMessage()_tx_unconn_timeout"}) { if (m_display_messages) { - std::string error_message = "In Port " + m_identifier + " opened fifo " + fifo_path + " for reading on descriptor " + std::to_string(fifo) + " but timed-out waiting for an init message."; + std::string error_message = "Port " + m_identifier + " opened fifo " + fifo_path + " for reading on descriptor " + std::to_string(fifo_descriptor) + " but timed-out waiting for an init message."; std::cerr << error_message << std::endl; } } @@ -122,7 +55,7 @@ bool Port::OpenRxFifo(std::string const& fifo_path, unsigned long timeout_second } else { m_error_code = {errno, "open()_rx"}; if (m_display_messages) { - std::string error_message = "In Port " + m_identifier + " unable to open fifo " + fifo_path + " for reading."; + std::string error_message = "Port " + m_identifier + " unable to open fifo at " + fifo_path + " for reading."; perror(error_message.c_str()); } success = false; @@ -133,22 +66,22 @@ bool Port::OpenRxFifo(std::string const& fifo_path, unsigned long timeout_second bool Port::OpenTxFifo(std::string const& fifo_path, unsigned long timeout_seconds) { m_error_code = {0, ""}; - int fifo = PollToOpenTxFifo(fifo_path, timeout_seconds); + int fifo_descriptor = PollToOpenTxFifo(fifo_path, timeout_seconds); bool success = false; - if (fifo >= 0) { + if (fifo_descriptor >= 0) { if (m_display_messages) { - std::string message = "Out Port " + m_identifier + " opened fifo " + fifo_path + " for writing on descriptor " + std::to_string(fifo); + std::string message = "Port " + m_identifier + " opened fifo " + fifo_path + " for writing on descriptor " + std::to_string(fifo_descriptor); std::cerr << message << std::endl; } - m_fifo_descriptor = fifo; + m_fifos.out.descriptor = fifo_descriptor; std::string init_message = "init"; init_message = SendMessage(init_message, timeout_seconds); if (GetErrorCode().err_no != 0) { - m_fifo_descriptor = 0; + m_fifos.out.descriptor = 0; success = false; } else { success = true; @@ -163,14 +96,14 @@ int Port::PollToOpenTxFifo(std::string const& fifo_path, unsigned long timeout_s m_error_code = {0, ""}; unsigned long start_time = time(0); - int fifo = -1; + int fifo_descriptor = -1; bool finished = false; while (!finished) { - fifo = open(fifo_path.c_str(), O_WRONLY | O_NONBLOCK); + fifo_descriptor = open(fifo_path.c_str(), O_WRONLY | O_NONBLOCK); - if (fifo >= 0) { + if (fifo_descriptor >= 0) { finished = true; } else { switch (errno) { @@ -183,14 +116,14 @@ int Port::PollToOpenTxFifo(std::string const& fifo_path, unsigned long timeout_s default: m_error_code = {errno, "open()_tx"}; if (m_display_messages) { - std::string error_message = "Out Port " + m_identifier + " unable to open fifo " + fifo_path + " for writing."; + std::string error_message = "Port " + m_identifier + " unable to open() fifo at" + fifo_path + " for writing."; perror(error_message.c_str()); } finished = true; } } } - return fifo; + return fifo_descriptor; } std::string Port::SendMessage(std::string const& message, unsigned long timeout_seconds) { @@ -214,132 +147,114 @@ std::string Port::SendMessage(std::string const& message, unsigned long timeout_ size_t Port::SendBytes(const char* bytes, size_t byte_count, unsigned long timeout_seconds) { m_error_code = {0, ""}; - if (!m_is_in_port) { - unsigned long start_time = time(0); - bool finished = false; - size_t bytes_remaining = byte_count; - size_t offset = 0; + unsigned long start_time = time(0); + bool finished = false; + size_t bytes_remaining = byte_count; + size_t offset = 0; - while (!finished) { - - int status = write(m_fifo_descriptor, bytes + offset, bytes_remaining); - - if (status >= 0) { - if ((size_t)(status) == bytes_remaining) { - finished = true; - } else { - offset += (size_t)(status); - } - bytes_remaining -= (size_t)(status); + while (!finished) { + + int status = write(m_fifos.out.descriptor, bytes + offset, bytes_remaining); + + if (status >= 0) { + if ((size_t)(status) == bytes_remaining) { + finished = true; } else { - switch (errno) { - case EAGAIN: // Erro - thread would block (reader currently reading, etc). Try again unless timeout. - finished = ((time(0) - start_time) > timeout_seconds); - if ((finished) && (timeout_seconds == 0)) { - m_error_code = {errno, "write()_thread_block"}; - } else if ((finished) && (timeout_seconds > 0)) { - m_error_code = {errno, "write()_thread_block_timeout"}; - } - break; - default: // Trap all other write() errors here. - m_error_code = {errno, "write()"}; - if (m_display_messages) { - std::string error_message = "Error on write() for Out_Port " + m_identifier; - perror(error_message.c_str()); - } - m_opened = false; - finished = true; - } + offset += (size_t)(status); + } + bytes_remaining -= (size_t)(status); + } else { + switch (errno) { + case EAGAIN: // Erro - thread would block (reader currently reading, etc). Try again unless timeout. + finished = ((time(0) - start_time) > timeout_seconds); + if ((finished) && (timeout_seconds == 0)) { + m_error_code = {errno, "write()_thread_block"}; + } else if ((finished) && (timeout_seconds > 0)) { + m_error_code = {errno, "write()_thread_block_timeout"}; + } + break; + default: // Trap all other write() errors here. + m_error_code = {errno, "write()"}; + if (m_display_messages) { + std::string error_message = "Port " + m_identifier + " unable to write() to fifo at" + m_fifos.out.identifier; + perror(error_message.c_str()); + } + m_fifos.out.opened = false; + finished = true; } } - return byte_count - bytes_remaining; - } else { - m_error_code = {-1, "SendBytes()_in_port"}; - if (m_display_messages) { - std::string error_message = "In Port " + m_identifier + " unable to SendBytes()."; - std::cerr << error_message << std::endl; - } - return 0; } + return byte_count - bytes_remaining; } std::string Port::GetMessage(bool capture_end_char, unsigned long timeout_seconds) { m_error_code = {0, ""}; - if (m_is_in_port) { - unsigned long start_time = 0; - if (timeout_seconds > 0) { - start_time = time(0); - } + unsigned long start_time = 0; + if (timeout_seconds > 0) { + start_time = time(0); + } + + bool finished = false; + bool end_char_received = false; + char char_in; + std::string message; + + while (!finished) { - bool finished = false; - bool end_char_received = false; - char char_in; - std::string message; + int status = read(m_fifos.in.descriptor, &char_in, 1); - while (!finished) { - - int status = read(m_fifo_descriptor, &char_in, 1); - - if (status > 0) { // read() read-in a character to the char buffer. - if (char_in != m_end_char) { + if (status > 0) { // read() read-in a character to the char buffer. + if (char_in != m_end_char) { + m_current_message.push_back(char_in); + } else { + if (capture_end_char) { m_current_message.push_back(char_in); - } else { - if (capture_end_char) { - m_current_message.push_back(char_in); + } + message = m_current_message; + m_current_message = ""; + end_char_received = true; + finished = true; + } + + } else if (status == 0) { // EOF. read() will return this if no process has the pipe open for writing. + if (!m_fifos.in.opened) { + finished = ((time(0) - start_time) > timeout_seconds); // If the Component has not finished initialising, assume that the partner + if (finished) { // component hasn't opened the fifo for writing yet so continue to poll + m_error_code = {-1, "GetMessage()_tx_unconn_timeout"}; // until timeout. + } + } else { // If the Component is initialised, the partner component no-longer has the + m_error_code = {-1, "read()_EOF"}; // fifo open for writing (has become disconnected). + if (m_display_messages) { + std::string error_message = "EOF error on GetMessage() for Port " + m_identifier + " suggests counterpart terminated."; + std::cerr << error_message << std::endl; } - message = m_current_message; - m_current_message = ""; - end_char_received = true; + m_fifos.in.opened = false; finished = true; } - - } else if (status == 0) { // EOF. read() will return this if no process has the pipe open for writing. - if (!m_opened) { - finished = ((time(0) - start_time) > timeout_seconds); // If the Component has not finished initialising, assume that the partner - if (finished) { // component hasn't opened the fifo for writing yet so continue to poll - m_error_code = {-1, "GetMessage()_tx_unconn_timeout"}; // until timeout. - } - } else { // If the Component is initialised, the partner component no-longer has the - m_error_code = {-1, "read()_EOF"}; // fifo open for writing (has become disconnected). - if (m_display_messages) { - std::string error_message = "EOF error on GetMessage() for In Port " + m_identifier + " suggests counterpart terminated."; - std::cerr << error_message << std::endl; - } - m_opened = false; - finished = true; + + } else if (status < 0) { // read() indicates an error. + switch (errno) { // See under errors here - https://pubs.opengroup.org/onlinepubs/009604599/functions/read.html + case EAGAIN: // Non-blocking read on empty fifo with connected writer will return -1 with error EAGAIN, + finished = ((time(0) - start_time) > timeout_seconds); // so we continue to poll unless timeout. + if (finished && (timeout_seconds > 0)) { // Only set m_error_code to EAGAIN if we have been waiting on a timeout. + m_error_code = {errno, "GetMessage()_tx_conn_timeout"}; } - - } else if (status < 0) { // read() indicates an error. - switch (errno) { // See under errors here - https://pubs.opengroup.org/onlinepubs/009604599/functions/read.html - case EAGAIN: // Non-blocking read on empty fifo with connected writer will return -1 with error EAGAIN, - finished = ((time(0) - start_time) > timeout_seconds); // so we continue to poll unless timeout. - if (finished && (timeout_seconds > 0)) { // Only set m_error_code to EAGAIN if we have been waiting on a timeout. - m_error_code = {errno, "GetMessage()_tx_conn_timeout"}; - } - break; - default: // Trap all other read() errors here. - m_error_code = {errno, "read()"}; - if (m_display_messages) { - std::string error_message = "Error on read() for In Port " + m_identifier; - perror(error_message.c_str()); - } - m_opened = false; - finished = true; - } + break; + default: // Trap all other read() errors here. + m_error_code = {errno, "read()"}; + if (m_display_messages) { + std::string error_message = "Port " + m_identifier + " unable to read() from fifo at " + m_fifos.in.identifier; + perror(error_message.c_str()); + } + m_fifos.in.opened = false; + finished = true; } } - if (end_char_received) { - return message; - } else { - return ""; - } + } + if (end_char_received) { + return message; } else { - m_error_code = {-1, "GetMessage()_out_port"}; - if (m_display_messages) { - std::string error_message = "Out Port " + m_identifier + " unable to GetMessage()."; - std::cerr << error_message << std::endl; - } return ""; } } @@ -348,14 +263,6 @@ error_descriptor Port::GetErrorCode(void) { return m_error_code; } -bool Port::IsCreated(void) { - return m_created; -} - bool Port::IsOpened(void) { - return m_opened; -} - -bool Port::IsInPort(void) { - return m_is_in_port; + return (m_fifos.in.opened && m_fifos.out.opened); } diff --git a/src/satterm_port.h b/src/satterm_port.h index a581d2a..03220f2 100644 --- a/src/satterm_port.h +++ b/src/satterm_port.h @@ -13,65 +13,58 @@ #include // std::string. #include // std::vector. -struct default_port { - std::string out; - std::string in; - default_port& operator=(default_port const& rhs) { - out = rhs.out; - in = rhs.in; - return *this; - } -}; - -struct error_descriptor { - int err_no; - std::string detail; - error_descriptor& operator=(error_descriptor const& rhs) { - err_no = rhs.err_no; - detail = rhs.detail; - return *this; - } - bool operator==(error_descriptor const& rhs) const { - return ((this->err_no == rhs.err_no) && (this->detail == rhs.detail)); - } - bool operator!=(error_descriptor const& rhs) const { - return ((this->err_no != rhs.err_no) || (this->detail != rhs.detail)); - } -}; +#include "satterm_struct.h" class Port { public: - Port(bool is_server, bool is_in_port, std::string const& working_path, std::string const& identifier, bool display_messages, char end_char); - ~Port(); + Port() {} + virtual ~Port() {} - void Create(void); - void Open(unsigned long timeout_seconds); - void Close(void); + virtual bool Open(unsigned long timeout_seconds) = 0; + virtual void Close(void) = 0; std::string GetMessage(bool capture_end_char, unsigned long timeout_seconds); std::string SendMessage(std::string const& message, unsigned long timeout_seconds); size_t SendBytes(const char* bytes, size_t byte_count, unsigned long timeout_seconds); - error_descriptor GetErrorCode(void); - bool IsCreated(void); bool IsOpened(void); - bool IsInPort(void); - - private: + + protected: bool OpenRxFifo(std::string const& fifo_path, unsigned long timeout_seconds); bool OpenTxFifo(std::string const& fifo_path, unsigned long timeout_seconds); int PollToOpenTxFifo(std::string const& fifo_path, unsigned long timeout_seconds); error_descriptor m_error_code = {0, ""}; bool m_display_messages = false; - bool m_is_server = false; - bool m_is_in_port = false; - bool m_created = false; - bool m_opened = false; - std::string m_identifier = ""; std::string m_working_path = ""; char m_end_char = 0; - int m_fifo_descriptor = 0; + fifo_pair m_fifos = {{"", false, false, 0}, {"", false, false, 0}}; std::string m_current_message = ""; + +}; + +class Port_Server : public Port { + public: + Port_Server(std::string const& working_path, std::string const& identifier, bool display_messages, char end_char); + ~Port_Server(); + + bool Open(unsigned long timeout_seconds) override; + void Close(void) override; + + bool IsCreated(void); + + private: + bool CreateFifo(std::string const& fifo_path); + +}; + +class Port_Client : public Port { + public: + Port_Client(std::string const& working_path, std::string const& identifier, bool display_messages, char end_char); + ~Port_Client(); + + bool Open(unsigned long timeout_seconds) override; + void Close(void) override; + }; diff --git a/src/satterm_port_client.cpp b/src/satterm_port_client.cpp new file mode 100644 index 0000000..8a2016b --- /dev/null +++ b/src/satterm_port_client.cpp @@ -0,0 +1,61 @@ +// ----------------------------------------------------------------------------------------------------- +// satellite_terminal - Easily spawn and communicate bidirectionally with client processes in separate +// terminal emulator instances. +// ----------------------------------------------------------------------------------------------------- +// seb.nf.sikora@protonmail.com +// +// Copyright © 2021 Dr Seb N.F. Sikora. +// +// This work is licensed under the terms of the MIT license. +// For a copy, see . +// ----------------------------------------------------------------------------------------------------- + +#include // std::cout, std::cerr, std::endl; +#include // std::string, std::to_string. +#include // std::map. +#include // std::vector. +#include // time(). + +#include // SIGPIPE, SIG_IGN. +#include // perror(). +#include // open() and O_RDONLY, O_WRONLY, etc. +#include // write(), read(), close(), unlink(). +#include // mkfifo(). + +#include "satterm_port.h" + +Port_Client::Port_Client(std::string const& working_path, std::string const& identifier, bool display_messages, char end_char) { + m_working_path = working_path; + m_identifier = identifier; + m_fifos.in.identifier = identifier + "_sout"; + m_fifos.out.identifier = identifier + "_sin"; + m_display_messages = display_messages; + m_end_char = end_char; + + signal(SIGPIPE, SIG_IGN); + +} + +Port_Client::~Port_Client() { + Close(); +} + +bool Port_Client::Open(unsigned long timeout_seconds) { + m_fifos.out.opened = OpenTxFifo(m_working_path + m_fifos.out.identifier, timeout_seconds); + if (m_fifos.out.opened) { + m_fifos.in.opened = OpenRxFifo(m_working_path + m_fifos.in.identifier, timeout_seconds); + } + return (m_fifos.out.opened && m_fifos.in.opened); +} + +void Port_Client::Close(void) { + if (m_fifos.in.opened) { + close(m_fifos.in.descriptor); + m_fifos.in.opened = false; + } + if (m_fifos.out.opened) { + close(m_fifos.out.descriptor); + m_fifos.out.opened = false; + } +} + diff --git a/src/satterm_port_server.cpp b/src/satterm_port_server.cpp new file mode 100644 index 0000000..0017bb1 --- /dev/null +++ b/src/satterm_port_server.cpp @@ -0,0 +1,109 @@ +// ----------------------------------------------------------------------------------------------------- +// satellite_terminal - Easily spawn and communicate bidirectionally with client processes in separate +// terminal emulator instances. +// ----------------------------------------------------------------------------------------------------- +// seb.nf.sikora@protonmail.com +// +// Copyright © 2021 Dr Seb N.F. Sikora. +// +// This work is licensed under the terms of the MIT license. +// For a copy, see . +// ----------------------------------------------------------------------------------------------------- + +#include // std::cout, std::cerr, std::endl; +#include // std::string, std::to_string. +#include // std::map. +#include // std::vector. +#include // time(). + +#include // SIGPIPE, SIG_IGN. +#include // perror(). +#include // open() and O_RDONLY, O_WRONLY, etc. +#include // write(), read(), close(), unlink(). +#include // mkfifo(). + +#include "satterm_port.h" + +Port_Server::Port_Server(std::string const& working_path, std::string const& identifier, bool display_messages, char end_char) { + m_working_path = working_path; + m_identifier = identifier; + m_fifos.in.identifier = identifier + "_sin"; + m_fifos.out.identifier = identifier + "_sout"; + m_display_messages = display_messages; + m_end_char = end_char; + + signal(SIGPIPE, SIG_IGN); + + m_fifos.in.created = CreateFifo(m_working_path + m_fifos.in.identifier); + if (m_fifos.in.created) { + m_fifos.out.created = CreateFifo(m_working_path + m_fifos.out.identifier); + } +} + +Port_Server::~Port_Server() { + Close(); + if (m_fifos.in.created) { + // Unlink. + std::string fifo_path = m_working_path + m_fifos.in.identifier; + int status = unlink(fifo_path.c_str()); + if ((status < 0) && m_display_messages) { + std::string error_message = "Unable to unlink() fifo at " + fifo_path; + perror(error_message.c_str()); + } + } + if (m_fifos.out.created) { + // Unlink. + std::string fifo_path = m_working_path + m_fifos.out.identifier; + int status = unlink(fifo_path.c_str()); + if ((status < 0) && m_display_messages) { + std::string error_message = "Unable to unlink() fifo at " + fifo_path; + perror(error_message.c_str()); + } + } + +} + +bool Port_Server::CreateFifo(std::string const& fifo_path) { + m_error_code = {0, ""}; + bool success = false; + + remove(fifo_path.c_str()); // If temporary file already exists, delete it. + + int status = mkfifo(fifo_path.c_str(), S_IFIFO|0666); + + if (status < 0) { + // Info on possible errors for mkfifo() - https://pubs.opengroup.org/onlinepubs/009696799/functions/mkfifo.html + m_error_code = {errno, "mkfifo()"}; + if (m_display_messages) { + std::string error_message = "mkfifo() error trying to create fifo " + fifo_path; + perror(error_message.c_str()); + } + success = false; + } else { + success = true; + } + return success; +} + +bool Port_Server::Open(unsigned long timeout_seconds) { + m_fifos.in.opened = OpenRxFifo(m_working_path + m_fifos.in.identifier, timeout_seconds); + if (m_fifos.in.opened) { + m_fifos.out.opened = OpenTxFifo(m_working_path + m_fifos.out.identifier, timeout_seconds); + } + return (m_fifos.in.opened && m_fifos.out.opened); +} + +void Port_Server::Close(void) { + if (m_fifos.out.opened) { + close(m_fifos.out.descriptor); + m_fifos.out.opened = false; + } + if (m_fifos.in.opened) { + close(m_fifos.in.descriptor); + m_fifos.in.opened = false; + } +} + +bool Port_Server::IsCreated(void) { + return (m_fifos.in.created && m_fifos.out.created); +} diff --git a/src/satterm_server.cpp b/src/satterm_server.cpp index 642164e..6c858a9 100644 --- a/src/satterm_server.cpp +++ b/src/satterm_server.cpp @@ -10,67 +10,53 @@ // For a copy, see . // ----------------------------------------------------------------------------------------------------- -#include +#include // std::cout, std::cerr, std::endl. #include // std::ifstream. -#include -#include -#include -#include -#include +#include // std::string, std::to_string. +#include // std::map. +#include // std::vector. +#include // std::unique_ptr. #include // errno. #include // fork(), execl(), getcwd(). #include "satellite_terminal.h" -SatTerm_Server::SatTerm_Server(std::string const& identifier, std::string const& path_to_client_binary, bool display_messages, - std::string const& path_to_terminal_emulator_paths, std::vector out_port_identifiers, - std::vector in_port_identifiers, char end_char, std::string const& stop_port_identifier, - std::string const& stop_message) { +SatTerm_Server::SatTerm_Server(std::string const& identifier, std::string const& path_to_client_binary, bool display_messages, + std::vector port_identifiers, std::string const& stop_message, + std::string const& path_to_terminal_emulator_paths, char end_char, std::string const& stop_port_identifier, + unsigned long timeout_seconds) { m_identifier = identifier; m_display_messages = display_messages; m_end_char = end_char; m_stop_message = stop_message; - if (out_port_identifiers.size() == 0) { - out_port_identifiers.push_back("server_out"); + if (port_identifiers.size() == 0) { + port_identifiers.push_back("comms"); } - if (in_port_identifiers.size() == 0) { - in_port_identifiers.push_back("server_in"); - } - m_default_port.out = out_port_identifiers[0]; - m_default_port.in = in_port_identifiers[0]; + m_default_port_identifier = port_identifiers[0]; if (stop_port_identifier == "") { - m_stop_port_identifier = m_default_port.out; + m_stop_port_identifier = m_default_port_identifier; } m_working_path = GetWorkingPath(); - + bool success = true; if (m_working_path != "") { - success = CreatePorts(true, true, m_working_path, in_port_identifiers, m_display_messages, m_end_char, m_in_ports); - if (success) { - success = CreatePorts(true, false, m_working_path, out_port_identifiers, m_display_messages, m_end_char, m_out_ports); - } + success = CreateServerPorts(m_working_path, port_identifiers, m_display_messages, m_end_char, m_ports); if (success) { - pid_t client_pid = StartClient(path_to_terminal_emulator_paths, path_to_client_binary, m_working_path, m_end_char, m_stop_message, - in_port_identifiers, out_port_identifiers); + pid_t client_pid = StartClient(path_to_terminal_emulator_paths, path_to_client_binary, m_working_path, m_end_char, m_stop_message, port_identifiers); if (client_pid < 0) { success = false; } } - unsigned long timeout_seconds = 5; - if (success) { - success = OpenPorts(m_in_ports, timeout_seconds); - } - if (success) { - success = OpenPorts(m_out_ports, timeout_seconds); + success = OpenPorts(m_ports, timeout_seconds); } if (success) { @@ -96,7 +82,7 @@ SatTerm_Server::~SatTerm_Server() { if (m_display_messages) { std::cerr << "Waiting for client process to terminate..." << std::endl; } - // Poll for EOF on read on m_default_port.in (tells us that client write end has closed). + // Poll for EOF on read on m_default_port_identifier (tells us that client write end has closed). while(IsConnected()) { GetMessage(false, 0); } @@ -113,7 +99,7 @@ std::string SatTerm_Server::GetWorkingPath(void) { if (retval == NULL) { m_error_code = {errno, "getcwd()"}; if (m_display_messages) { - perror("getcwd() unable to obtain current working path."); + perror("getcwd() unable to obtain current working path"); } return ""; } else { @@ -128,8 +114,22 @@ std::string SatTerm_Server::GetWorkingPath(void) { } } +bool SatTerm_Server::CreateServerPorts(std::string const& working_path, std::vector port_identifiers, + bool display_messages, char end_char, std::map>& ports) { + bool success = true; + for (auto const& port_identifier : port_identifiers) { + ports.emplace(port_identifier, std::make_unique(working_path, port_identifier, display_messages, end_char)); + if (!(static_cast(ports.at(port_identifier).get())->IsCreated())) { + success = false; + m_error_code = ports.at(port_identifier)->GetErrorCode(); + break; + } + } + return success; +} + pid_t SatTerm_Server::StartClient(std::string const& path_to_terminal_emulator_paths, std::string const& path_to_client_binary, std::string const& working_path, - char end_char, std::string const& stop_message, std::vector in_port_identifiers, std::vector out_port_identifiers) { + char end_char, std::string const& stop_message, std::vector port_identifiers) { m_error_code = {0, ""}; std::vector terminal_emulator_paths = LoadTerminalEmulatorPaths(path_to_terminal_emulator_paths); @@ -141,7 +141,7 @@ pid_t SatTerm_Server::StartClient(std::string const& path_to_terminal_emulator_p // Info on possible fork() errors - https://pubs.opengroup.org/onlinepubs/009696799/functions/fork.html m_error_code = {errno, "fork()"}; if (m_display_messages) { - perror("fork() to client process failed."); + perror("fork() to client process failed"); } return process; } @@ -157,13 +157,9 @@ pid_t SatTerm_Server::StartClient(std::string const& path_to_terminal_emulator_p arg_string += " " + working_path; arg_string += " " + std::to_string((int)(end_char)); arg_string += " " + stop_message; - arg_string += " " + std::to_string(in_port_identifiers.size()); - arg_string += " " + std::to_string(out_port_identifiers.size()); + arg_string += " " + std::to_string(port_identifiers.size()); - for (const auto& identifier : in_port_identifiers) { - arg_string += " " + identifier; - } - for (const auto& identifier : out_port_identifiers) { + for (const auto& identifier : port_identifiers) { arg_string += " " + identifier; } diff --git a/src/satterm_struct.h b/src/satterm_struct.h new file mode 100644 index 0000000..5e2178c --- /dev/null +++ b/src/satterm_struct.h @@ -0,0 +1,53 @@ +// ----------------------------------------------------------------------------------------------------- +// satellite_terminal - Easily spawn and communicate bidirectionally with client processes in separate +// terminal emulator instances. +// ----------------------------------------------------------------------------------------------------- +// seb.nf.sikora@protonmail.com +// +// Copyright © 2021 Dr Seb N.F. Sikora. +// +// This work is licensed under the terms of the MIT license. +// For a copy, see . +// ----------------------------------------------------------------------------------------------------- + +struct fifo { + std::string identifier; + bool created; + bool opened; + int descriptor; + + fifo& operator=(fifo const& rhs) { + identifier = rhs.identifier; + created = rhs.created; + opened = rhs.opened; + descriptor = rhs.descriptor; + return *this; + } +}; + +struct fifo_pair { + fifo in; + fifo out; + + fifo_pair& operator=(fifo_pair const& rhs) { + in = rhs.in; + out = rhs.out; + return *this; + } +}; + +struct error_descriptor { + int err_no; + std::string err_detail; + error_descriptor& operator=(error_descriptor const& rhs) { + err_no = rhs.err_no; + err_detail = rhs.err_detail; + return *this; + } + bool operator==(error_descriptor const& rhs) const { + return ((this->err_no == rhs.err_no) && (this->err_detail == rhs.err_detail)); + } + bool operator!=(error_descriptor const& rhs) const { + return ((this->err_no != rhs.err_no) || (this->err_detail != rhs.err_detail)); + } +};