2012-05-03 09:25:13 +02:00
|
|
|
#include "shell_manager.hh"
|
|
|
|
|
2012-12-10 18:41:01 +01:00
|
|
|
#include "context.hh"
|
2015-06-06 12:54:48 +02:00
|
|
|
#include "buffer_utils.hh"
|
2014-11-25 02:00:18 +01:00
|
|
|
#include "event_manager.hh"
|
2014-10-30 01:50:40 +01:00
|
|
|
#include "file.hh"
|
2012-09-06 14:28:07 +02:00
|
|
|
|
2012-05-03 09:25:13 +02:00
|
|
|
#include <cstring>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
2014-10-13 20:28:02 +02:00
|
|
|
#include <unistd.h>
|
2012-05-03 09:25:13 +02:00
|
|
|
|
|
|
|
namespace Kakoune
|
|
|
|
{
|
2013-04-17 19:26:44 +02:00
|
|
|
|
2014-03-25 20:43:56 +01:00
|
|
|
static const Regex env_var_regex(R"(\bkak_(\w+)\b)");
|
2012-05-03 09:25:13 +02:00
|
|
|
|
|
|
|
ShellManager::ShellManager()
|
|
|
|
{
|
2014-10-30 01:50:40 +01:00
|
|
|
const char* path = getenv("PATH");
|
2015-06-01 20:06:35 +02:00
|
|
|
auto new_path = format("{}:{}", path, split_path(get_kak_binary_path()).first);
|
2014-10-30 01:50:40 +01:00
|
|
|
setenv("PATH", new_path.c_str(), 1);
|
2012-05-03 09:25:13 +02:00
|
|
|
}
|
|
|
|
|
2015-03-13 14:39:18 +01:00
|
|
|
std::pair<String, int> ShellManager::eval(
|
|
|
|
StringView cmdline, const Context& context, StringView input,
|
2015-06-08 14:34:08 +02:00
|
|
|
Flags flags, ConstArrayView<String> params, const EnvVarMap& env_vars)
|
2012-05-03 09:25:13 +02:00
|
|
|
{
|
2012-09-06 14:28:07 +02:00
|
|
|
int write_pipe[2]; // child stdin
|
|
|
|
int read_pipe[2]; // child stdout
|
|
|
|
int error_pipe[2]; // child stderr
|
2012-05-03 09:25:13 +02:00
|
|
|
|
2012-05-29 12:39:03 +02:00
|
|
|
::pipe(write_pipe);
|
|
|
|
::pipe(read_pipe);
|
2012-09-06 14:28:07 +02:00
|
|
|
::pipe(error_pipe);
|
2012-05-03 09:25:13 +02:00
|
|
|
|
|
|
|
if (pid_t pid = fork())
|
|
|
|
{
|
|
|
|
close(write_pipe[0]);
|
|
|
|
close(read_pipe[1]);
|
2012-09-06 14:28:07 +02:00
|
|
|
close(error_pipe[1]);
|
2012-05-29 12:39:03 +02:00
|
|
|
|
2014-04-20 13:15:31 +02:00
|
|
|
write(write_pipe[1], input.data(), (int)input.length());
|
2012-05-03 09:25:13 +02:00
|
|
|
close(write_pipe[1]);
|
|
|
|
|
2015-03-14 02:53:17 +01:00
|
|
|
String child_stdout, child_stderr;
|
2015-06-08 14:34:08 +02:00
|
|
|
int status = 0;
|
2012-05-03 09:25:13 +02:00
|
|
|
{
|
2014-12-03 14:56:02 +01:00
|
|
|
auto pipe_reader = [](String& output) {
|
|
|
|
return [&output](FDWatcher& watcher, EventMode) {
|
2014-11-25 02:00:18 +01:00
|
|
|
char buffer[1024];
|
2014-12-03 14:56:02 +01:00
|
|
|
size_t size = read(watcher.fd(), buffer, 1024);
|
2014-11-25 02:00:18 +01:00
|
|
|
if (size <= 0)
|
2014-12-03 14:56:02 +01:00
|
|
|
watcher.close_fd();
|
2015-04-27 17:39:51 +02:00
|
|
|
output += StringView{buffer, buffer+size};
|
2014-11-25 02:00:18 +01:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2015-03-14 02:53:17 +01:00
|
|
|
FDWatcher stdout_watcher{read_pipe[0], pipe_reader(child_stdout)};
|
|
|
|
FDWatcher stderr_watcher{error_pipe[0], pipe_reader(child_stderr)};
|
2014-11-25 02:00:18 +01:00
|
|
|
|
2015-06-09 21:28:24 +02:00
|
|
|
// block SIGCHLD to make sure we wont receive it before
|
|
|
|
// our call to pselect, that will end up blocking indefinitly.
|
|
|
|
sigset_t mask, orig_mask;
|
|
|
|
sigemptyset(&mask);
|
|
|
|
sigaddset(&mask, SIGCHLD);
|
|
|
|
sigprocmask(SIG_BLOCK, &mask, &orig_mask);
|
|
|
|
|
|
|
|
// check for termination now that SIGCHLD is blocked
|
|
|
|
bool terminated = waitpid(pid, &status, WNOHANG);
|
|
|
|
|
2015-06-08 23:42:51 +02:00
|
|
|
while (not terminated or
|
|
|
|
((flags & Flags::WaitForStdout) and
|
|
|
|
(not stdout_watcher.closed() or
|
|
|
|
not stderr_watcher.closed())))
|
2015-06-08 14:34:08 +02:00
|
|
|
{
|
2015-06-09 21:28:24 +02:00
|
|
|
EventManager::instance().handle_next_events(EventMode::Urgent, &orig_mask);
|
2015-06-08 14:34:08 +02:00
|
|
|
if (not terminated)
|
|
|
|
terminated = waitpid(pid, &status, WNOHANG);
|
|
|
|
}
|
2015-06-08 23:42:51 +02:00
|
|
|
|
|
|
|
stdout_watcher.close_fd();
|
|
|
|
stderr_watcher.close_fd();
|
2015-06-09 21:28:24 +02:00
|
|
|
|
|
|
|
sigprocmask(SIG_SETMASK, &orig_mask, nullptr);
|
2012-05-03 09:25:13 +02:00
|
|
|
}
|
2012-09-06 14:28:07 +02:00
|
|
|
|
2015-03-14 02:53:17 +01:00
|
|
|
if (not child_stderr.empty())
|
2015-06-06 12:54:48 +02:00
|
|
|
write_to_debug_buffer(format("shell stderr: <<<\n{}>>>", child_stderr));
|
2012-09-06 14:28:07 +02:00
|
|
|
|
2015-03-14 02:53:17 +01:00
|
|
|
return { child_stdout, WIFEXITED(status) ? WEXITSTATUS(status) : - 1 };
|
2012-05-03 09:25:13 +02:00
|
|
|
}
|
2012-07-31 14:21:25 +02:00
|
|
|
else try
|
2012-05-03 09:25:13 +02:00
|
|
|
{
|
|
|
|
close(write_pipe[1]);
|
|
|
|
close(read_pipe[0]);
|
2012-09-06 14:28:07 +02:00
|
|
|
close(error_pipe[0]);
|
2012-05-03 09:25:13 +02:00
|
|
|
|
2012-08-29 00:08:39 +02:00
|
|
|
dup2(read_pipe[1], 1); close(read_pipe[1]);
|
2012-09-06 14:28:07 +02:00
|
|
|
dup2(error_pipe[1], 2); close(error_pipe[1]);
|
2012-08-29 00:08:39 +02:00
|
|
|
dup2(write_pipe[0], 0); close(write_pipe[0]);
|
2012-05-03 09:25:13 +02:00
|
|
|
|
2015-03-13 14:25:20 +01:00
|
|
|
using RegexIt = RegexIterator<StringView::iterator>;
|
|
|
|
for (RegexIt it{cmdline.begin(), cmdline.end(), env_var_regex}, end;
|
|
|
|
it != end; ++it)
|
2012-05-03 09:25:13 +02:00
|
|
|
{
|
|
|
|
auto& match = *it;
|
|
|
|
|
2015-03-13 14:25:20 +01:00
|
|
|
StringView name{match[1].first, match[1].second};
|
2013-04-09 20:04:11 +02:00
|
|
|
kak_assert(name.length() > 0);
|
2012-05-03 09:25:13 +02:00
|
|
|
|
2015-03-10 20:33:46 +01:00
|
|
|
auto local_var = env_vars.find(name.str());
|
2012-05-29 07:19:27 +02:00
|
|
|
if (local_var != env_vars.end())
|
|
|
|
setenv(("kak_" + name).c_str(), local_var->second.c_str(), 1);
|
2015-03-13 14:25:20 +01:00
|
|
|
else try
|
2012-05-03 09:25:13 +02:00
|
|
|
{
|
2015-03-13 14:25:20 +01:00
|
|
|
String value = get_val(name, context);
|
2015-06-01 20:06:35 +02:00
|
|
|
setenv(format("kak_{}", name).c_str(), value.c_str(), 1);
|
2012-05-03 09:25:13 +02:00
|
|
|
}
|
2015-03-13 14:25:20 +01:00
|
|
|
catch (runtime_error&) {}
|
2012-05-03 09:25:13 +02:00
|
|
|
}
|
2014-04-20 13:15:31 +02:00
|
|
|
const char* shell = "/bin/sh";
|
|
|
|
auto cmdlinezstr = cmdline.zstr();
|
2015-01-12 14:58:41 +01:00
|
|
|
Vector<const char*> execparams = { shell, "-c", cmdlinezstr };
|
2012-09-09 17:10:53 +02:00
|
|
|
if (not params.empty())
|
2014-04-20 13:15:31 +02:00
|
|
|
execparams.push_back(shell);
|
2012-09-09 17:10:53 +02:00
|
|
|
for (auto& param : params)
|
|
|
|
execparams.push_back(param.c_str());
|
2013-07-28 16:40:02 +02:00
|
|
|
execparams.push_back(nullptr);
|
2012-09-09 17:10:53 +02:00
|
|
|
|
2014-04-20 13:15:31 +02:00
|
|
|
execvp(shell, (char* const*)execparams.data());
|
2012-07-31 14:21:25 +02:00
|
|
|
exit(-1);
|
2012-05-03 09:25:13 +02:00
|
|
|
}
|
2012-07-31 14:21:25 +02:00
|
|
|
catch (...) { exit(-1); }
|
2015-03-13 14:39:18 +01:00
|
|
|
return {};
|
2012-05-03 09:25:13 +02:00
|
|
|
}
|
|
|
|
|
2014-04-20 13:15:31 +02:00
|
|
|
void ShellManager::register_env_var(StringView regex,
|
2012-05-03 09:25:13 +02:00
|
|
|
EnvVarRetriever retriever)
|
|
|
|
{
|
2012-06-25 19:40:18 +02:00
|
|
|
m_env_vars.push_back({ Regex(regex.begin(), regex.end()), std::move(retriever) });
|
2012-05-03 09:25:13 +02:00
|
|
|
}
|
|
|
|
|
2014-06-18 20:28:48 +02:00
|
|
|
String ShellManager::get_val(StringView name, const Context& context) const
|
|
|
|
{
|
|
|
|
auto env_var = std::find_if(
|
|
|
|
m_env_vars.begin(), m_env_vars.end(),
|
2015-05-26 19:40:39 +02:00
|
|
|
[name](const std::pair<Regex, EnvVarRetriever>& pair)
|
2014-10-13 14:12:33 +02:00
|
|
|
{ return regex_match(name.begin(), name.end(), pair.first); });
|
2014-06-18 20:28:48 +02:00
|
|
|
|
|
|
|
if (env_var == m_env_vars.end())
|
|
|
|
throw runtime_error("no such env var: " + name);
|
|
|
|
return env_var->second(name, context);
|
|
|
|
}
|
|
|
|
|
2012-05-03 09:25:13 +02:00
|
|
|
}
|