2017-03-03 19:05:50 +01:00
|
|
|
#include "ssh.hh"
|
|
|
|
|
2020-05-20 04:33:07 +01:00
|
|
|
#include <utility>
|
|
|
|
|
2017-03-03 19:05:50 +01:00
|
|
|
namespace nix {
|
|
|
|
|
2020-05-20 04:33:07 +01:00
|
|
|
SSHMaster::SSHMaster(const std::string& host, std::string keyFile,
|
2017-08-11 13:55:41 +02:00
|
|
|
bool useMaster, bool compress, int logFD)
|
|
|
|
: host(host),
|
2018-08-03 18:12:28 +02:00
|
|
|
fakeSSH(host == "localhost"),
|
2020-05-20 04:33:07 +01:00
|
|
|
keyFile(std::move(keyFile)),
|
2018-08-03 18:12:28 +02:00
|
|
|
useMaster(useMaster && !fakeSSH),
|
2017-08-11 13:55:41 +02:00
|
|
|
compress(compress),
|
|
|
|
logFD(logFD) {
|
2020-05-20 22:27:37 +01:00
|
|
|
if (host.empty() || hasPrefix(host, "-")) {
|
2017-08-11 13:55:41 +02:00
|
|
|
throw Error("invalid SSH host name '%s'", host);
|
2020-05-19 20:47:23 +01:00
|
|
|
}
|
2017-08-11 13:55:41 +02:00
|
|
|
}
|
|
|
|
|
2017-03-21 14:35:50 +01:00
|
|
|
void SSHMaster::addCommonSSHOpts(Strings& args) {
|
|
|
|
for (auto& i : tokenizeString<Strings>(getEnv("NIX_SSHOPTS"))) {
|
|
|
|
args.push_back(i);
|
2020-05-19 20:47:23 +01:00
|
|
|
}
|
2017-03-21 14:35:50 +01:00
|
|
|
if (!keyFile.empty()) {
|
|
|
|
args.insert(args.end(), {"-i", keyFile});
|
2020-05-19 18:55:58 +01:00
|
|
|
}
|
2017-03-21 14:35:50 +01:00
|
|
|
if (compress) {
|
|
|
|
args.push_back("-C");
|
2020-05-19 18:55:58 +01:00
|
|
|
}
|
2017-03-21 14:35:50 +01:00
|
|
|
}
|
|
|
|
|
2017-03-03 19:05:50 +01:00
|
|
|
std::unique_ptr<SSHMaster::Connection> SSHMaster::startCommand(
|
|
|
|
const std::string& command) {
|
2017-03-03 19:28:27 +01:00
|
|
|
Path socketPath = startMaster();
|
2017-03-03 19:05:50 +01:00
|
|
|
|
2020-05-20 22:27:37 +01:00
|
|
|
Pipe in;
|
|
|
|
Pipe out;
|
2017-03-03 19:05:50 +01:00
|
|
|
in.create();
|
|
|
|
out.create();
|
|
|
|
|
|
|
|
auto conn = std::make_unique<Connection>();
|
2020-02-14 07:47:48 +01:00
|
|
|
ProcessOptions options;
|
|
|
|
options.dieWithParent = false;
|
|
|
|
|
2017-03-03 19:05:50 +01:00
|
|
|
conn->sshPid = startProcess(
|
|
|
|
[&]() {
|
|
|
|
restoreSignals();
|
|
|
|
|
|
|
|
close(in.writeSide.get());
|
|
|
|
close(out.readSide.get());
|
|
|
|
|
|
|
|
if (dup2(in.readSide.get(), STDIN_FILENO) == -1) {
|
|
|
|
throw SysError("duping over stdin");
|
2020-05-19 20:47:23 +01:00
|
|
|
}
|
2017-03-03 19:05:50 +01:00
|
|
|
if (dup2(out.writeSide.get(), STDOUT_FILENO) == -1) {
|
|
|
|
throw SysError("duping over stdout");
|
2020-05-19 20:47:23 +01:00
|
|
|
}
|
2017-05-02 12:01:46 +02:00
|
|
|
if (logFD != -1 && dup2(logFD, STDERR_FILENO) == -1) {
|
|
|
|
throw SysError("duping over stderr");
|
2020-05-19 20:47:23 +01:00
|
|
|
}
|
2017-03-03 19:05:50 +01:00
|
|
|
|
2018-08-03 18:12:28 +02:00
|
|
|
Strings args;
|
|
|
|
|
|
|
|
if (fakeSSH) {
|
|
|
|
args = {"bash", "-c"};
|
|
|
|
} else {
|
2020-05-20 22:27:37 +01:00
|
|
|
args = {"ssh", host, "-x", "-a"};
|
2018-08-03 18:12:28 +02:00
|
|
|
addCommonSSHOpts(args);
|
2020-05-20 22:27:37 +01:00
|
|
|
if (!socketPath.empty()) {
|
2020-05-19 01:02:44 +01:00
|
|
|
args.insert(args.end(), {"-S", socketPath});
|
|
|
|
}
|
|
|
|
// TODO(tazjin): Abseil verbosity flag
|
|
|
|
/*if (verbosity >= lvlChatty) {
|
|
|
|
args.push_back("-v");
|
|
|
|
}*/
|
2018-08-03 18:12:28 +02:00
|
|
|
}
|
|
|
|
|
2017-03-03 19:05:50 +01:00
|
|
|
args.push_back(command);
|
|
|
|
execvp(args.begin()->c_str(), stringsToCharPtrs(args).data());
|
|
|
|
|
2019-12-12 15:15:18 +01:00
|
|
|
// could not exec ssh/bash
|
2019-12-13 12:53:20 +01:00
|
|
|
throw SysError("unable to execute '%s'", args.front());
|
2020-02-14 07:47:48 +01:00
|
|
|
},
|
|
|
|
options);
|
2017-03-03 19:05:50 +01:00
|
|
|
|
|
|
|
in.readSide = -1;
|
|
|
|
out.writeSide = -1;
|
|
|
|
|
|
|
|
conn->out = std::move(out.readSide);
|
|
|
|
conn->in = std::move(in.writeSide);
|
|
|
|
|
|
|
|
return conn;
|
|
|
|
}
|
|
|
|
|
2017-03-03 19:28:27 +01:00
|
|
|
Path SSHMaster::startMaster() {
|
|
|
|
if (!useMaster) {
|
|
|
|
return "";
|
2020-05-19 18:55:58 +01:00
|
|
|
}
|
2017-03-03 19:05:50 +01:00
|
|
|
|
2017-03-03 19:28:27 +01:00
|
|
|
auto state(state_.lock());
|
2017-03-03 19:05:50 +01:00
|
|
|
|
2017-03-03 19:28:27 +01:00
|
|
|
if (state->sshMaster != -1) {
|
|
|
|
return state->socketPath;
|
2020-05-19 18:55:58 +01:00
|
|
|
}
|
2017-03-03 19:28:27 +01:00
|
|
|
|
|
|
|
state->tmpDir =
|
|
|
|
std::make_unique<AutoDelete>(createTempDir("", "nix", true, true, 0700));
|
|
|
|
|
|
|
|
state->socketPath = (Path)*state->tmpDir + "/ssh.sock";
|
2017-03-03 19:05:50 +01:00
|
|
|
|
|
|
|
Pipe out;
|
|
|
|
out.create();
|
|
|
|
|
2020-02-14 07:47:48 +01:00
|
|
|
ProcessOptions options;
|
|
|
|
options.dieWithParent = false;
|
|
|
|
|
2017-03-03 19:28:27 +01:00
|
|
|
state->sshMaster = startProcess(
|
|
|
|
[&]() {
|
2017-03-03 19:05:50 +01:00
|
|
|
restoreSignals();
|
|
|
|
|
|
|
|
close(out.readSide.get());
|
|
|
|
|
|
|
|
if (dup2(out.writeSide.get(), STDOUT_FILENO) == -1) {
|
|
|
|
throw SysError("duping over stdout");
|
2020-05-19 20:47:23 +01:00
|
|
|
}
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2020-05-20 22:27:37 +01:00
|
|
|
Strings args = {"ssh", host,
|
2017-03-03 19:28:27 +01:00
|
|
|
"-M", "-N",
|
|
|
|
"-S", state->socketPath,
|
2017-03-03 19:05:50 +01:00
|
|
|
"-o", "LocalCommand=echo started",
|
|
|
|
"-o", "PermitLocalCommand=yes"};
|
2020-05-19 01:02:44 +01:00
|
|
|
// if (verbosity >= lvlChatty) { args.push_back("-v"); }
|
2017-03-21 14:35:50 +01:00
|
|
|
addCommonSSHOpts(args);
|
2017-03-03 19:05:50 +01:00
|
|
|
execvp(args.begin()->c_str(), stringsToCharPtrs(args).data());
|
|
|
|
|
2019-12-13 12:53:20 +01:00
|
|
|
throw SysError("unable to execute '%s'", args.front());
|
2020-02-14 07:47:48 +01:00
|
|
|
},
|
|
|
|
options);
|
2017-03-03 19:05:50 +01:00
|
|
|
|
|
|
|
out.writeSide = -1;
|
|
|
|
|
|
|
|
std::string reply;
|
|
|
|
try {
|
|
|
|
reply = readLine(out.readSide.get());
|
|
|
|
} catch (EndOfFile& e) {
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reply != "started") {
|
2017-07-30 12:27:57 +01:00
|
|
|
throw Error("failed to start SSH master connection to '%s'", host);
|
2020-05-19 20:47:23 +01:00
|
|
|
}
|
2017-03-03 19:28:27 +01:00
|
|
|
|
|
|
|
return state->socketPath;
|
2017-03-03 19:05:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace nix
|