From 994348e9e070a07d9f86ce876415f5484e5b7e66 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 23 Sep 2021 17:48:52 +0200 Subject: [PATCH 1/2] SSHStore / LegacySSHStore: Show a better error message if the remote is "nologin" Instead of error: serialised integer 7161674624452356180 is too large for type 'j' we now get error: 'nix-store --serve' protocol mismatch from 'sshtest@localhost', got 'This account is currently not available.' Fixes https://github.com/NixOS/nixpkgs/issues/37287. --- src/libstore/legacy-ssh-store.cc | 15 ++++++++++++--- src/libstore/remote-store.cc | 12 ++++++++++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index 45eed5707..5d006f6e2 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -82,9 +82,18 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor conn->to << SERVE_MAGIC_1 << SERVE_PROTOCOL_VERSION; conn->to.flush(); - unsigned int magic = readInt(conn->from); - if (magic != SERVE_MAGIC_2) - throw Error("protocol mismatch with 'nix-store --serve' on '%s'", host); + StringSink saved; + try { + TeeSource tee(conn->from, saved); + unsigned int magic = readInt(tee); + if (magic != SERVE_MAGIC_2) + throw Error("'nix-store --serve' protocol mismatch from '%s'", host); + } catch (SerialisationError & e) { + conn->sshConn->in.close(); + auto msg = conn->from.drain(); + throw Error("'nix-store --serve' protocol mismatch from '%s', got '%s'", + host, chomp(*saved.s + msg)); + } conn->remoteVersion = readInt(conn->from); if (GET_PROTOCOL_MAJOR(conn->remoteVersion) != 0x200) throw Error("unsupported 'nix-store --serve' protocol version on '%s'", host); diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 73f590e7b..c71eb4631 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -162,8 +162,16 @@ void RemoteStore::initConnection(Connection & conn) try { conn.to << WORKER_MAGIC_1; conn.to.flush(); - unsigned int magic = readInt(conn.from); - if (magic != WORKER_MAGIC_2) throw Error("protocol mismatch"); + StringSink saved; + try { + TeeSource tee(conn.from, saved); + unsigned int magic = readInt(tee); + if (magic != WORKER_MAGIC_2) + throw Error("protocol mismatch"); + } catch (SerialisationError & e) { + auto msg = conn.from.drain(); + throw Error("protocol mismatch, got '%s'", chomp(*saved.s + msg)); + } conn.from >> conn.daemonVersion; if (GET_PROTOCOL_MAJOR(conn.daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION)) From ea9df6fe51cf52a8e9106cd62a195bf185e6c5f6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 23 Sep 2021 18:01:04 +0200 Subject: [PATCH 2/2] Shut down write side before draining the read side This is important if the remote side *does* execute nix-store/nix-daemon successfully, but stdout is polluted (e.g. because the remote user's bashrc script prints something to stdout). In that case we have to shutdown the write side to force the remote nix process to exit. --- src/libstore/legacy-ssh-store.cc | 2 ++ src/libstore/remote-store.cc | 3 +++ src/libstore/remote-store.hh | 3 ++- src/libstore/ssh-store.cc | 5 +++++ src/libstore/uds-remote-store.cc | 6 ++++++ src/libstore/uds-remote-store.hh | 6 ++++++ 6 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index 5d006f6e2..c660d7f5b 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -89,6 +89,8 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor if (magic != SERVE_MAGIC_2) throw Error("'nix-store --serve' protocol mismatch from '%s'", host); } catch (SerialisationError & e) { + /* In case the other side is waiting for our input, + close it. */ conn->sshConn->in.close(); auto msg = conn->from.drain(); throw Error("'nix-store --serve' protocol mismatch from '%s', got '%s'", diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index c71eb4631..b57a4cb05 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -169,6 +169,9 @@ void RemoteStore::initConnection(Connection & conn) if (magic != WORKER_MAGIC_2) throw Error("protocol mismatch"); } catch (SerialisationError & e) { + /* In case the other side is waiting for our input, close + it. */ + conn.closeWrite(); auto msg = conn.from.drain(); throw Error("protocol mismatch, got '%s'", chomp(*saved.s + msg)); } diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index 8901c79fc..ac1eaa19e 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -125,7 +125,6 @@ public: struct Connection { - AutoCloseFD fd; FdSink to; FdSource from; unsigned int daemonVersion; @@ -133,6 +132,8 @@ public: virtual ~Connection(); + virtual void closeWrite() = 0; + std::exception_ptr processStderr(Sink * sink = 0, Source * source = 0, bool flush = true); }; diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index f2caf2aeb..bb03daef4 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -57,6 +57,11 @@ private: struct Connection : RemoteStore::Connection { std::unique_ptr sshConn; + + void closeWrite() override + { + sshConn->in.close(); + } }; ref openConnection() override; diff --git a/src/libstore/uds-remote-store.cc b/src/libstore/uds-remote-store.cc index cac4fa036..cfadccf68 100644 --- a/src/libstore/uds-remote-store.cc +++ b/src/libstore/uds-remote-store.cc @@ -45,6 +45,12 @@ std::string UDSRemoteStore::getUri() } +void UDSRemoteStore::Connection::closeWrite() +{ + shutdown(fd.get(), SHUT_WR); +} + + ref UDSRemoteStore::openConnection() { auto conn = make_ref(); diff --git a/src/libstore/uds-remote-store.hh b/src/libstore/uds-remote-store.hh index ddc7716cd..f8dfcca70 100644 --- a/src/libstore/uds-remote-store.hh +++ b/src/libstore/uds-remote-store.hh @@ -40,6 +40,12 @@ public: private: + struct Connection : RemoteStore::Connection + { + AutoCloseFD fd; + void closeWrite() override; + }; + ref openConnection() override; std::optional path; };