1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-09 00:00:00 +01:00

popen: allow passing of new env variables, working directory and non-shell execution

This commit is contained in:
Steffen Vogel 2019-06-23 10:50:35 +02:00
parent dfccc24da5
commit 2c6cc762dc
3 changed files with 114 additions and 15 deletions

View file

@ -27,6 +27,7 @@
#include <ext/stdio_filebuf.h>
#include <map>
#include <string>
#include <istream>
#include <ostream>
@ -38,10 +39,17 @@ namespace utils {
class Popen {
public:
using arg_list = std::vector<std::string>;
using env_map = std::map<std::string, std::string>;
using char_type = char;
using stdio_buf = __gnu_cxx::stdio_filebuf<char_type>;
Popen(const std::string &command);
Popen(const std::string &cmd,
const arg_list &args = arg_list(),
const env_map &env = env_map(),
const std::string &wd = std::string(),
bool shell = false);
~Popen();
void open();
@ -70,6 +78,10 @@ public:
protected:
std::string command;
std::string working_dir;
arg_list arguments;
env_map environment;
bool shell;
pid_t pid;
struct {
@ -81,7 +93,6 @@ protected:
std::unique_ptr<std::ostream> stream;
std::unique_ptr<stdio_buf> buffer;
} output;
};
} /* namespace utils */

View file

@ -20,26 +20,40 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <signal.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <paths.h>
#include <dirent.h>
#include <cstring>
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <fmt/core.h>
#include <villas/utils.hpp>
#include <villas/exceptions.hpp>
#include <villas/popen.hpp>
namespace villas {
namespace utils {
Popen::Popen(const std::string &cmd) :
command(cmd)
Popen::Popen(const std::string &cmd,
const arg_list &args,
const env_map &env,
const std::string &wd,
bool sh) :
command(cmd),
working_dir(wd),
arguments(args),
environment(env),
shell(sh)
{
open();
}
@ -56,13 +70,14 @@ void Popen::kill(int signal)
void Popen::open()
{
std::vector<char *> argv, envp;
const int READ = 0;
const int WRITE = 1;
int ret;
int inpipe[2];
int outpipe[2];
char *argv[4];
ret = pipe(inpipe);
if (ret)
@ -83,6 +98,35 @@ void Popen::open()
goto clean_outpipe_out;
if (pid == 0) {
/* Prepare arguments */
if (shell) {
argv.push_back((char *) "sh");
argv.push_back((char *) "-c");
argv.push_back((char *) command.c_str());
}
else {
for (auto arg: arguments) {
auto s = strdup(arg.c_str());
argv.push_back(s);
}
}
/* Prepare environment */
for (char **p = environ; *p; p++)
envp.push_back(*p);
for (auto env: environment) {
auto e = fmt::format("{}={}", env.first, env.second);
auto s = strdup(e.c_str());
envp.push_back(s);
}
argv.push_back(nullptr);
envp.push_back(nullptr);
/* Redirect IO */
::close(outpipe[READ]);
::close(inpipe[WRITE]);
@ -96,12 +140,11 @@ void Popen::open()
::close(outpipe[WRITE]);
}
argv[0] = (char *) "sh";
argv[1] = (char *) "-c";
argv[2] = (char *) command.c_str();
argv[3] = nullptr;
/* Change working directory */
if (!working_dir.empty())
chdir(working_dir.c_str());
execv(_PATH_BSHELL, (char * const *) argv);
execvpe(shell ? _PATH_BSHELL : command.c_str(), (char * const *) argv.data(), (char * const *) envp.data());
exit(127);
}

View file

@ -29,9 +29,9 @@ using namespace villas::utils;
TestSuite(popen, .description = "Bi-directional popen");
Test(popen, cat)
Test(popen, no_shell)
{
Popen proc("cat");
Popen proc("/usr/bin/tee", {"tee", "test"});
proc.cout() << "Hello World" << std::endl;
proc.cout().flush();
@ -40,9 +40,54 @@ Test(popen, cat)
proc.cin() >> str >> str2;
std::cout << str << str2 << std::endl;
cr_assert_eq(str, "Hello");
cr_assert_eq(str2, "World");
proc.kill();
proc.close();
}
Test(popen, shell)
{
Popen proc("echo \"Hello World\"", {}, {}, std::string(), true);
std::string str, str2;
proc.cin() >> str >> str2;
cr_assert_eq(str, "Hello");
cr_assert_eq(str2, "World");
proc.kill();
proc.close();
}
Test(popen, wd)
{
Popen proc("/usr/bin/pwd", {"pwd"}, {}, "/usr/lib");
std::string wd;
proc.cin() >> wd;
cr_assert_eq(wd, "/usr/lib");
proc.kill();
proc.close();
}
Test(popen, env)
{
Popen proc("echo $MYVAR", {}, {{"MYVAR", "TESTVAL"}}, std::string(), true);
std::string var;
proc.cin() >> var;
cr_assert_eq(var, "TESTVAL");
proc.kill();
proc.close();
}