From a133fb48eb74c204100f57e04bf0ec7df8965b8d Mon Sep 17 00:00:00 2001 From: Dulana Peiris Date: Mon, 23 Oct 2023 23:11:05 +0530 Subject: [PATCH] Added HPSH init, deinit and serve. Fixed socket communication issues --- CMakeLists.txt | 1 + src/hpsh/hpsh.cpp | 92 ++++++++++++++++++++++++++++++++++++++++++++++ src/hpsh/hpsh.hpp | 14 +++++++ src/main.cpp | 5 ++- src/usr/usr.cpp | 89 ++------------------------------------------ test/bin/hpsh | Bin 18496 -> 19592 bytes 6 files changed, 115 insertions(+), 86 deletions(-) create mode 100644 src/hpsh/hpsh.cpp create mode 100644 src/hpsh/hpsh.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0319c611..8d75b111 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,6 +62,7 @@ add_executable(hpcore src/status.cpp src/consensus.cpp src/main.cpp + src/hpsh/hpsh.cpp ) target_link_libraries(hpcore killswitch diff --git a/src/hpsh/hpsh.cpp b/src/hpsh/hpsh.cpp new file mode 100644 index 00000000..e7b5e30b --- /dev/null +++ b/src/hpsh/hpsh.cpp @@ -0,0 +1,92 @@ +#include "../conf.hpp" +#include "../util/util.hpp" + +namespace hpsh +{ + pid_t hpsh_pid; + static int fd1[2]; + static int fd2[2]; + + int deinit() + { + //kill(hpsh_pid, SIGTERM); + close(fd1[0]); + close(fd1[1]); + close(fd2[0]); + close(fd1[1]); + + LOG_INFO << "HPSH stopped."; + + return 0; + } + int init() + { + LOG_INFO << "Initializing HPSH"; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd1) == -1 || socketpair(AF_UNIX, SOCK_STREAM, 0, fd2) == -1) + { + return -1; + } + + pid_t pid = fork(); + if (pid == -1) + { + return -1; + } + + if (pid == 0) + { + hpsh_pid = getpid(); + char cfd1[10], cfd2[10]; + snprintf(cfd1, 10, "%d", fd1[0]); + snprintf(cfd2, 10, "%d", fd2[1]); + + char *argv[] = {const_cast(conf::ctx.hpsh_exe_path.c_str()), (char *)("-s1"), cfd1, (char *)("-s2"), cfd2, NULL}; + LOG_DEBUG << "Starting HPSH Executable"; + execv(argv[0], argv); + LOG_DEBUG << "Failed to execute hpsh"; + exit(EXIT_FAILURE); + } + else + { + close(fd1[0]); + close(fd2[1]); + + return 0; + } + } + + std::string serve(const char *message) + { + char buffer[1024]; + + ssize_t bytes_written = write(fd1[1], message, strlen(message)); + if (bytes_written == -1) { + perror("Error when writing to HPSH socket"); + } + + LOG_DEBUG << "\nMessage sent from hpcore: " << message; + + while (true) + { + int bytesRead; + bytesRead = read(fd2[0], buffer, sizeof(buffer)); + if (bytesRead < 0) + { + // Handle read error + perror("read"); + return "error when reading"; + } + + + buffer[bytesRead] = '\0'; // Null-terminate the buffer + + LOG_DEBUG << "\nMessage received in hpcore: " << buffer; + if(bytesRead<1024){ + break; + } + + } + return buffer; + } +} \ No newline at end of file diff --git a/src/hpsh/hpsh.hpp b/src/hpsh/hpsh.hpp new file mode 100644 index 00000000..4d9bad19 --- /dev/null +++ b/src/hpsh/hpsh.hpp @@ -0,0 +1,14 @@ +#ifndef HPSH_H +#define HPSH_H + +#include "../conf.hpp" +#include "../util/util.hpp" + +namespace hpsh +{ + int deinit(); + int init(); + std::string serve(const char* command); +} + +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index bc711b27..4f50fae1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,6 +16,7 @@ #include "ledger/ledger.hpp" #include "unl.hpp" #include "killswitch/killswitch.h" +#include "hpsh/hpsh.hpp" /** * Parses CLI args and extracts HotPocket command and parameters given. @@ -75,6 +76,7 @@ void deinit() sc::deinit(); ledger::deinit(); conf::deinit(); + hpsh::deinit(); } void sig_exit_handler(int signum) @@ -213,7 +215,8 @@ int main(int argc, char **argv) consensus::init() == -1 || read_req::init() == -1 || p2p::init() == -1 || - usr::init() == -1) + usr::init() == -1 || + hpsh::init() == -1) { deinit(); return -1; diff --git a/src/usr/usr.cpp b/src/usr/usr.cpp index 8a514ee2..87655008 100644 --- a/src/usr/usr.cpp +++ b/src/usr/usr.cpp @@ -17,6 +17,7 @@ #include "user_input.hpp" #include "read_req.hpp" #include "input_nonce_map.hpp" +#include "../hpsh/hpsh.hpp" namespace usr { @@ -278,91 +279,9 @@ namespace usr if (parser.extract_shell_input(id, content) != -1) { - int p1[2] ; // 0,1 are hpcore to hphs - int p2[2] ; // 2,3 are hpsh to hpcore - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, p1)) - perror("could not create unix domain socket pair"); - - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, p2)) - perror("could not create unix domain socket pair"); - - pid_t pid = -1; - - pid = fork(); - - if (pid == -1) - { - perror("Error occurred when forking"); - return -1; - } - if (pid != 0) - { - // parent process - close(p1[0]); - close(p2[1]); - LOG_INFO << "parent: Received Shell Input.\n"; - LOG_INFO << "parent: User PubKey:" << user.pubkey << "\n"; - LOG_INFO << "parent: ID:" << id << "\n"; - LOG_INFO << "parent: Content:" << content << "\n"; - LOG_INFO << "parent: writing content to pipe\n"; - ssize_t bytes_written = write(p1[1], content.c_str(), content.size()); - if (bytes_written == -1) - { - perror("write to pipe failed"); - // handle the error appropriately - } - char buffer[1024] = {0}; - if(read(p2[0], &buffer, sizeof(buffer))==-1){ - perror("Error in reading to the fd"); - } - std::cout <<"bufferread"<< buffer << std::endl; - - LOG_INFO << "parent: closing pipe\n"; - close(p1[1]); - close(p2[0]); - LOG_INFO << "parent: closed\n"; - } - else - { - - // child process - close(p1[1]); - close(p2[0]); - char read_end[16]; - snprintf(read_end, sizeof(read_end), "%d", p1[0]); - char write_end[16]; - snprintf(write_end, sizeof(write_end), "%d", p2[1]); - std::string receivedContent; - LOG_INFO << "child: reading from pipe:\n"; - const int BUFFER_SIZE = 1024; - char buffer[BUFFER_SIZE]; - - ssize_t bytes_read = read(p1[0], buffer, BUFFER_SIZE); - if (bytes_read == -1) - { - perror("read from pipe failed"); - // handle the error appropriately - } - else - { - buffer[bytes_read] = '\0'; // Null-terminate the string - receivedContent = buffer; - LOG_INFO << "child: Content:" << receivedContent << "\n"; - } - - receivedContent = "\"" + receivedContent + "\""; - - char *passed_command = new char[receivedContent.length() + 1]; - std::strcpy(passed_command, receivedContent.c_str()); - - char *args[] = {const_cast(conf::ctx.hpsh_exe_path.c_str()), passed_command,read_end, write_end, NULL}; - if (execv(conf::ctx.hpsh_exe_path.c_str(), args) == -1) - { - perror("Error when executing HPSH"); - } - close(p1[0]); - close(p2[1]); - } + LOG_INFO << "shell input received:" << content; + std::string response = hpsh::serve(content.c_str()); + LOG_INFO << "response: " << response; return 0; } diff --git a/test/bin/hpsh b/test/bin/hpsh index cffa8ef387521885579789b3568c67f9f81e85db..d58f320b1776018bb9269b44b6390f3965c3abaa 100755 GIT binary patch literal 19592 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 delta 4591 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 KCUbDRB;&uy0WF9C