From 591ad390a7b28a868d87680a69003a4fd2404dfb Mon Sep 17 00:00:00 2001 From: Jason Hiser <jdhiser@gmail.com> Date: Tue, 10 Dec 2019 19:44:10 -0500 Subject: [PATCH] updated to use posix_spawn instead of system/popen to avoid using 2x the memory --- irdb-libs/libIRDB-core/src/cmdstr.hpp | 78 +++++++++++++++++++++++++++ irdb-libs/libIRDB-core/src/fileir.cpp | 24 ++------- 2 files changed, 83 insertions(+), 19 deletions(-) create mode 100644 irdb-libs/libIRDB-core/src/cmdstr.hpp diff --git a/irdb-libs/libIRDB-core/src/cmdstr.hpp b/irdb-libs/libIRDB-core/src/cmdstr.hpp new file mode 100644 index 000000000..1cc08ae61 --- /dev/null +++ b/irdb-libs/libIRDB-core/src/cmdstr.hpp @@ -0,0 +1,78 @@ +#ifndef cmdstr_hpp +#define cmdstr_hpp + +/*BINFMTCXX: -std=c++11 -Wall -Werror +*/ + +#include <spawn.h> // see manpages-posix-dev +#include <poll.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/wait.h> +#include <iostream> +#include <string> +#include <vector> +using namespace std; + +static inline pair<string,int> command_to_string( const string& command) +{ + auto ret=string(); + int exit_code; + int cout_pipe[2]; + int cerr_pipe[2]; + posix_spawn_file_actions_t action; + + if(pipe(cout_pipe) || pipe(cerr_pipe)) + cout << "pipe returned an error.\n"; + + posix_spawn_file_actions_init(&action); + posix_spawn_file_actions_addclose(&action, cout_pipe[0]); + posix_spawn_file_actions_addclose(&action, cerr_pipe[0]); + posix_spawn_file_actions_adddup2(&action, cout_pipe[1], 1); + posix_spawn_file_actions_adddup2(&action, cerr_pipe[1], 2); + + posix_spawn_file_actions_addclose(&action, cout_pipe[1]); + posix_spawn_file_actions_addclose(&action, cerr_pipe[1]); + + // string command = "echo bla"; // example #1 + // string command = "pgmcrater -width 64 -height 9 |pgmtopbm |pnmtoplainpnm"; + vector<char> argsmem[] = {{'s', 'h', '\0'}, {'-', 'c', '\0'}}; // allows non-const access to literals + char *args[] = {&argsmem[0][0], &argsmem[1][0],const_cast<char*>(command.c_str()),nullptr}; + + pid_t pid; + if(posix_spawnp(&pid, args[0], &action, NULL, args, environ) != 0) + cout << "posix_spawnp failed with error: " << strerror(errno) << "\n"; + + close(cout_pipe[1]), close(cerr_pipe[1]); // close child-side of pipes + + // Read from pipes + string buffer(1024,' '); + std::vector<pollfd> plist = { {cout_pipe[0],POLLIN}, {cerr_pipe[0],POLLIN} }; + for ( int rval; (rval=poll(&plist[0],plist.size(),/*timeout*/-1))>0; ) + { + if ( plist[0].revents&POLLIN) { + const auto bytes_read = read(cout_pipe[0], &buffer[0], buffer.length()); + cout << "read " << bytes_read << " bytes from stdout.\n"; + cout << buffer.substr(0, static_cast<size_t>(bytes_read)) << "\n"; + ret += buffer.substr(0, static_cast<size_t>(bytes_read)); + } + else if ( plist[1].revents&POLLIN ) { + const auto bytes_read = read(cerr_pipe[0], &buffer[0], buffer.length()); + cout << "read " << bytes_read << " bytes from stderr.\n"; + cout << buffer.substr(0, static_cast<size_t>(bytes_read)) << "\n"; + ret += buffer.substr(0, static_cast<size_t>(bytes_read)); + } + else + break; // nothing left to read + } + + waitpid(pid,&exit_code,0); + cout << "exit code: " << exit_code << "\n"; + + posix_spawn_file_actions_destroy(&action); + return {ret,exit_code}; +} + +#endif diff --git a/irdb-libs/libIRDB-core/src/fileir.cpp b/irdb-libs/libIRDB-core/src/fileir.cpp index f4bc10dbe..4d9a65fde 100644 --- a/irdb-libs/libIRDB-core/src/fileir.cpp +++ b/irdb-libs/libIRDB-core/src/fileir.cpp @@ -32,6 +32,8 @@ #include <irdb-util> #include <endian.h> +#include "cmdstr.hpp" + using namespace libIRDB; using namespace std; @@ -44,26 +46,10 @@ using namespace std; int command_to_stream(const string& command, ostream& stream) { - auto redirect_command=command+" 2>&1 "; - auto buffer=array<char,128>(); - - std::cout << "Issuing subcommand: "<< command << std::endl; - auto pipe = popen(redirect_command.c_str(), "r"); - if (!pipe) - { - stream << "Couldn't start command:"<< strerror(errno) << endl; - return 1; - } - while (fgets(buffer.data(), 128, pipe) != NULL) - { - stream<<buffer.data(); - } - auto returnCode = pclose(pipe); - if(returnCode==-1) - stream << "Could not close pipe: "<< strerror(errno) << endl; + const auto res = command_to_string(command); - std::cout << "Return code = "<<returnCode << std::endl; - return returnCode; + stream << res.first << endl; + return res.second; } static void UpdateEntryPoints( -- GitLab