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:
parent
dfccc24da5
commit
2c6cc762dc
3 changed files with 114 additions and 15 deletions
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue