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