1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-07-02 21:51:50 +02:00

Merge pull request #11021 from hercules-ci/issue-11010

Fix SSH invocation when local SHELL misbehaves
This commit is contained in:
tomberek 2024-08-26 10:40:51 -04:00 committed by GitHub
commit 440de80d34
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 67 additions and 4 deletions

View file

@ -3,6 +3,7 @@
#include "current-process.hh"
#include "environment-variables.hh"
#include "util.hh"
#include "exec.hh"
namespace nix {
@ -44,6 +45,10 @@ void SSHMaster::addCommonSSHOpts(Strings & args)
if (compress)
args.push_back("-C");
// We use this to make ssh signal back to us that the connection is established.
// It really does run locally; see createSSHEnv which sets up SHELL to make
// it launch more reliably. The local command runs synchronously, so presumably
// the remote session won't be garbled if the local command is slow.
args.push_back("-oPermitLocalCommand=yes");
args.push_back("-oLocalCommand=echo started");
}
@ -56,6 +61,27 @@ bool SSHMaster::isMasterRunning() {
return res.first == 0;
}
Strings createSSHEnv()
{
// Copy the environment and set SHELL=/bin/sh
std::map<std::string, std::string> env = getEnv();
// SSH will invoke the "user" shell for -oLocalCommand, but that means
// $SHELL. To keep things simple and avoid potential issues with other
// shells, we set it to /bin/sh.
// Technically, we don't need that, and we could reinvoke ourselves to print
// "started". Self-reinvocation is tricky with library consumers, but mostly
// solved; refer to the development history of nixExePath in libstore/globals.cc.
env.insert_or_assign("SHELL", "/bin/sh");
Strings r;
for (auto & [k, v] : env) {
r.push_back(k + "=" + v);
}
return r;
}
std::unique_ptr<SSHMaster::Connection> SSHMaster::startCommand(
Strings && command, Strings && extraSshArgs)
{
@ -104,8 +130,8 @@ std::unique_ptr<SSHMaster::Connection> SSHMaster::startCommand(
}
args.splice(args.end(), std::move(command));
execvp(args.begin()->c_str(), stringsToCharPtrs(args).data());
auto env = createSSHEnv();
nix::execvpe(args.begin()->c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(env).data());
// could not exec ssh/bash
throw SysError("unable to execute '%s'", args.front());
@ -172,7 +198,8 @@ Path SSHMaster::startMaster()
if (verbosity >= lvlChatty)
args.push_back("-v");
addCommonSSHOpts(args);
execvp(args.begin()->c_str(), stringsToCharPtrs(args).data());
auto env = createSSHEnv();
nix::execvpe(args.begin()->c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(env).data());
throw SysError("unable to execute '%s'", args.front());
}, options);