Commit 19eed0bc authored by phlo's avatar phlo

updated shell

parent 5f9223d7
......@@ -11,9 +11,10 @@ namespace ConcuBinE {
// Boolector::command ----------------------------------------------------------
std::string Boolector::command () const
const std::vector<std::string> & Boolector::command () const
{
return "boolector --model-gen"; // --output-number-format=dec
static const std::vector<std::string> cmd({name(), "--model-gen"});
return cmd;
}
// Boolector::parse ------------------------------------------------------------
......@@ -91,8 +92,12 @@ std::string Boolector::name () const { return "boolector"; }
std::string Boolector::version () const
{
std::string version;
Shell().run(name() + " --version") >> version;
static const std::vector<std::string> cmd({name(), "--version"});
static std::string version;
if (version.empty())
shell::run(cmd).stdout >> version;
return version;
}
......
......@@ -19,7 +19,7 @@ private: //---------------------------------------------------------------------
// build command line for running boolector
//
virtual std::string command () const;
virtual const std::vector<std::string> & command () const;
protected: //-------------------------------------------------------------------
......
......@@ -14,9 +14,18 @@ std::string BtorMC::name () const { return "btormc"; }
// BtorMC::command -------------------------------------------------------------
std::string BtorMC::command () const
const std::vector<std::string> & BtorMC::command () const
{
return "btormc --trace-gen-full -kmax " + std::to_string(bound);
static std::vector<std::string> cmd({
name(),
"--trace-gen-full",
"-kmax",
""});
// TODO: improve
cmd.back() = std::to_string(bound);
return cmd;
}
// BtorMC::parse ---------------------------------------------------------------
......
......@@ -23,7 +23,7 @@ struct BtorMC : public Boolector
// build command line for running btormc
//
virtual std::string command () const;
virtual const std::vector<std::string> & command () const;
// parse current variable's symbol
//
......
......@@ -17,10 +17,16 @@ std::string CVC4::name () const { return "cvc4"; }
std::string CVC4::version () const
{
std::string version;
auto ss = Shell().run(name() + " --version");
do ss >> version; while (ss && version != "version");
ss >> version;
static const std::vector<std::string> cmd({name(), "--version"});
static std::string version;
if (version.empty())
{
auto out = shell::run(cmd);
do out.stdout >> version; while (out.stdout && version != "version");
out.stdout >> version;
}
return version;
}
......@@ -37,9 +43,15 @@ std::string CVC4::formula (Encoder & encoder) const
// CVC4::command ---------------------------------------------------------------
std::string CVC4::command () const
const std::vector<std::string> & CVC4::command () const
{
return "cvc4 -L smt2 -m --output-lang=cvc4";
static std::vector<std::string> cmd({
name(),
"-L", "smt2",
"-m",
"--output-lang=cvc4"});
return cmd;
}
// CVC4::parse -----------------------------------------------------------------
......
......@@ -31,7 +31,7 @@ struct CVC4 : public External
// build command line for the specific solver
//
virtual std::string command () const;
virtual const std::vector<std::string> & command () const;
// parse variable
//
......
......@@ -4,65 +4,62 @@
#include <unistd.h>
#include <sys/wait.h>
namespace ConcuBinE {
namespace ConcuBinE::shell {
//==============================================================================
// constants
//==============================================================================
#define PIPE_READ 0
#define PIPE_WRITE 1
#define BUFFER_SIZE 128
#define READ 0
#define WRITE 1
//==============================================================================
// functions
//==============================================================================
// sys_error -------------------------------------------------------------------
// errmsg ----------------------------------------------------------------------
inline std::string sys_error ()
inline std::string errmsg (const std::string & msg)
{
return "[" + std::string(strerror(errno)) + "]";
return "error " + msg + " [" + strerror(errno) + ']';
}
//==============================================================================
// Shell
//==============================================================================
// Shell::last_exit_code -------------------------------------------------------
int Shell::last_exit_code () { return exit_code; }
// Shell::run ------------------------------------------------------------------
std::stringstream Shell::run (const std::string & cmd)
{
return run(cmd, "");
}
// run -------------------------------------------------------------------------
std::stringstream Shell::run (const std::string & cmd,
const std::string & input)
Output run (const std::vector<std::string> & command, const std::string & input)
{
// stdout read from cmd
std::stringstream output;
shell::Output output;
// stdin pipe file descriptors
// stdin pipe
int std_in[2];
// stdout pipe file descriptors
// stdout pipe
int std_out[2];
// stderr pipe
int std_err[2];
// exec error pipe
int exec_err[2];
// pid returned by fork (0 == child)
int pid;
// open stdin pipe
if (pipe(std_in) < 0)
throw std::runtime_error("creating input pipe " + sys_error());
if (pipe(std_in))
throw std::runtime_error(errmsg("creating stdin pipe"));
// open stdout pipe
if (pipe(std_out) < 0)
throw std::runtime_error("creating output pipe " + sys_error());
if (pipe(std_out))
throw std::runtime_error(errmsg("creating stdout pipe"));
// open stderr pipe
if (pipe(std_err))
throw std::runtime_error(errmsg("creating stderr pipe"));
// open error pipe
if (pipe(exec_err))
throw std::runtime_error(errmsg("creating error pipe"));
// fork process
pid = fork();
......@@ -71,57 +68,97 @@ std::stringstream Shell::run (const std::string & cmd,
if (pid == 0)
{
// redirect stdin
if (dup2(std_in[PIPE_READ], STDIN_FILENO) < 0)
throw std::runtime_error("redirecting stdin " + sys_error());
if (dup2(std_in[READ], STDIN_FILENO) < 0)
throw std::runtime_error(errmsg("redirecting stdin"));
// redirect stdout
if (dup2(std_out[PIPE_WRITE], STDOUT_FILENO) < 0)
throw std::runtime_error("redirecting stdout " + sys_error());
if (dup2(std_out[WRITE], STDOUT_FILENO) < 0)
throw std::runtime_error(errmsg("redirecting stdout"));
// redirect stderr
if (dup2(std_out[PIPE_WRITE], STDERR_FILENO) < 0)
throw std::runtime_error("redirecting stderr " + sys_error());
if (dup2(std_err[WRITE], STDERR_FILENO) < 0)
throw std::runtime_error(errmsg("redirecting stderr"));
// close file descriptors - only used by parent
close(std_in[PIPE_READ]);
close(std_in[PIPE_WRITE]);
close(std_out[PIPE_READ]);
close(std_out[PIPE_WRITE]);
// run shell command as child process
execlp("bash", "bash", "-c", cmd.c_str(), static_cast<char *>(0));
// exec should not return - if we get here, an error must have happened
throw std::runtime_error("executing shell command " + sys_error());
close(std_in[READ]);
close(std_in[WRITE]);
close(std_out[READ]);
close(std_out[WRITE]);
close(std_err[READ]);
close(std_err[WRITE]);
close(exec_err[READ]);
// build arguments vector
std::vector<char *> argv;
argv.reserve(command.size() + 1);
for (const auto & a : command)
argv.push_back(const_cast<char *>(a.c_str()));
argv.push_back(nullptr);
// run executable
execvp(argv[0], argv.data());
// exec should not return - if we get here, something must have happened
const int ret = errno;
const std::string msg = errmsg("calling exec");
// write reason to error pipe
write(exec_err[WRITE], msg.c_str(), msg.length());
// close remaining file descriptor
close(exec_err[WRITE]);
exit(ret);
}
// parent process
else if (pid > 0)
{
// read buffer
char buffer[BUFFER_SIZE];
// close unused file descriptors
close(std_in[PIPE_READ]);
close(std_out[PIPE_WRITE]);
close(std_in[READ]);
close(std_out[WRITE]);
close(std_err[WRITE]);
close(exec_err[WRITE]);
// write given input to stdin of child
if (!input.empty())
if (write(std_in[PIPE_WRITE], input.c_str(), input.length()) < 0)
throw std::runtime_error("writing to stdin " + sys_error());
if (write(std_in[WRITE], input.c_str(), input.length()) < 0)
throw std::runtime_error(errmsg("writing to stdin pipe"));
// close stdin pipe file descriptor
close(std_in[PIPE_WRITE]);
close(std_in[WRITE]);
// read stdout from child
int num_read = 0;
while ((num_read = read(std_out[PIPE_READ], buffer, BUFFER_SIZE - 1)) > 0)
// read buffer
constexpr size_t nbuf = 128;
constexpr size_t nbyte = nbuf - 1;
char buf[nbuf];
int n;
// read stdout pipe
while ((n = read(std_out[READ], buf, nbyte)) > 0)
{
buffer[num_read] = '\0';
output << buffer;
buf[n] = 0;
output.stdout << buf;
}
// read stderr pipe
while ((n = read(std_err[READ], buf, nbyte)) > 0)
{
buf[n] = 0;
output.stderr << buf;
}
// read error pipe
std::string error;
while ((n = read(exec_err[READ], buf, nbyte)) > 0)
{
buf[n] = 0;
error += buf;
}
// close remaining file descriptors
close(std_out[PIPE_READ]);
close(std_out[READ]);
close(std_err[READ]);
close(exec_err[READ]);
// wait for child to finish and assign exit code
int wstatus;
......@@ -129,24 +166,31 @@ std::stringstream Shell::run (const std::string & cmd,
waitpid(pid, &wstatus, 0);
if (WIFEXITED(wstatus))
exit_code = WEXITSTATUS(wstatus);
output.exit = WEXITSTATUS(wstatus);
else
throw
std::runtime_error("child process exited prematurely " + sys_error());
throw std::runtime_error(errmsg("child process exited prematurely"));
// check for exec errors
if (!error.empty())
throw std::runtime_error(error);
}
// fork failed
else
{
// close file descriptors
close(std_in[PIPE_READ]);
close(std_in[PIPE_WRITE]);
close(std_out[PIPE_READ]);
close(std_out[PIPE_WRITE]);
throw std::runtime_error("fork failed " + sys_error());
close(std_in[READ]);
close(std_in[WRITE]);
close(std_out[READ]);
close(std_out[WRITE]);
close(std_err[READ]);
close(std_err[WRITE]);
close(exec_err[READ]);
close(exec_err[WRITE]);
throw std::runtime_error(errmsg("fork failed"));
}
return output;
}
} // namespace ConcuBinE
} // namespace ConcuBinE::shell
......@@ -2,42 +2,21 @@
#define SHELL_HH_
#include <sstream>
#include <vector>
namespace ConcuBinE {
namespace ConcuBinE::shell {
//==============================================================================
// Shell
//==============================================================================
class Shell
struct Output
{
//----------------------------------------------------------------------------
// members
//----------------------------------------------------------------------------
// last exit code ($?)
//
int exit_code;
//----------------------------------------------------------------------------
// public member functions
//----------------------------------------------------------------------------
public:
// returns last exit code (like $?)
//
int last_exit_code ();
// runs shell command and returns it's output
//
std::stringstream run (const std::string & input);
// pipes input into shell command and returns it's output
//
std::stringstream run (const std::string & cmd, const std::string & input);
int exit;
std::stringstream stdout, stderr;
};
} // namespace ConcuBinE
// run command with given input (via stdin)
//
Output run (const std::vector<std::string> & command,
const std::string & input = "");
} // namespace ConcuBinE::shell
#endif
......@@ -253,11 +253,9 @@ bool External::sat (const std::string & input)
{
using namespace std::chrono;
Shell shell;
high_resolution_clock::time_point t = high_resolution_clock::now();
std_out = shell.run(command(), input);
std_out = std::move(shell::run(command(), input).stdout);
time = duration_cast<milliseconds>(high_resolution_clock::now() - t).count();
......
......@@ -115,7 +115,7 @@ struct External : public Solver
// build command line for running the specific solver
//
virtual std::string command () const = 0;
virtual const std::vector<std::string> & command () const = 0;
// parse integer attribute of current symbol
//
......
This diff is collapsed.
......@@ -8,73 +8,54 @@ namespace ConcuBinE::test {
// Shell tests
//==============================================================================
struct Shell : public ::testing::Test
{
ConcuBinE::Shell shell;
};
// Shell::last_exit_code =======================================================
// Shell::run ==================================================================
TEST_F(Shell, return_code)
TEST(Shell, exit)
{
shell.run("exit 0");
ASSERT_EQ(0, shell.last_exit_code());
shell.run("exit 1");
ASSERT_EQ(1, shell.last_exit_code());
shell.run("exit -1");
ASSERT_EQ(255, shell.last_exit_code());
ASSERT_EQ(0, shell::run({"bash"}, "exit 0").exit);
ASSERT_EQ(1, shell::run({"bash"}, "exit 1").exit);
ASSERT_EQ(255, shell::run({"bash"}, "exit -1").exit);
}
// Shell::run ==================================================================
TEST_F(Shell, ouput)
TEST(Shell, stdout)
{
std::string expected = "hello shell";
std::string actual = shell.run("echo -n " + expected).str();
const std::string expected = "hello world";
const auto out = shell::run({"echo", "-n", expected});
ASSERT_EQ(0, shell.last_exit_code());
ASSERT_EQ(expected, actual);
ASSERT_EQ(0, out.exit);
ASSERT_EQ(expected, out.stdout.str());
ASSERT_EQ("", out.stderr.str());
}
TEST_F(Shell, input_output)
TEST(Shell, stderr)
{
std::string expected = "hello shell";
std::string actual = shell.run("cat", expected).str();
const std::string expected = "hello world";
const auto out = shell::run({"bash"}, "echo " + expected + " 1>&2");
ASSERT_EQ(0, shell.last_exit_code());
ASSERT_EQ(expected, actual);
ASSERT_EQ(0, out.exit);
ASSERT_EQ("", out.stdout.str());
ASSERT_EQ(expected + '\n', out.stderr.str());
}
TEST_F(Shell, pipe_in_pipe)
TEST(Shell, input)
{
std::string input = "3\n2\n4\n5\n1\n3\n2\n4\n5\n1\n";
std::string expected = "1\n2\n3\n4\n5\n";
const std::string expected = "hello world";
const auto out = shell::run({"cat"}, expected);
std::string actual = shell.run("sort | uniq", input).str();
ASSERT_EQ(0, shell.last_exit_code());
ASSERT_EQ(expected, actual);
ASSERT_EQ(0, out.exit);
ASSERT_EQ(expected, out.stdout.str());
ASSERT_EQ("", out.stderr.str());
}
TEST_F(Shell, abuse)
TEST(Shell, abuse)
{
std::string expected = "bash: unknown: command not found\n";
std::string actual = shell.run("unknown").str();
ASSERT_EQ(127, shell.last_exit_code());
ASSERT_EQ(expected, actual);
const char * expected = "error calling exec [No such file or directory]";
actual = shell.run("").str();
ASSERT_EQ("", actual);
try { shell::run({"unknown"}); }
catch (const std::exception & e) { ASSERT_STREQ(expected, e.what()); }
std::string input;
actual= shell.run("echo ", input).str();
ASSERT_EQ(0, shell.last_exit_code());
ASSERT_EQ("\n", actual);
try { shell::run({""}); }
catch (const std::exception & e) { ASSERT_STREQ(expected, e.what()); }
}
} // namespace ConcuBinE::test
......@@ -55,7 +55,7 @@ struct Env : public ::testing::Environment
{
Timetable totals;
const std::string titel = "\n## Runtime\n";
const std::string host = Shell().run("uname -p").str();
const std::string host = shell::run({"uname", "-p"}).stdout.str();
const std::string section = titel + "\n> " + host + eol;
const std::vector<std::string> header = {"Solver", "Runtime [ms]"};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment