Add a LegacySSHStore that uses nix-store --serve
This is useful for nix-copy-closure.
This commit is contained in:
		
							parent
							
								
									f38224e924
								
							
						
					
					
						commit
						caa5793b4a
					
				
					 2 changed files with 248 additions and 0 deletions
				
			
		
							
								
								
									
										247
									
								
								src/libstore/legacy-ssh-store.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								src/libstore/legacy-ssh-store.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,247 @@ | ||||||
|  | #include "archive.hh" | ||||||
|  | #include "pool.hh" | ||||||
|  | #include "remote-store.hh" | ||||||
|  | #include "serve-protocol.hh" | ||||||
|  | #include "store-api.hh" | ||||||
|  | #include "worker-protocol.hh" | ||||||
|  | 
 | ||||||
|  | namespace nix { | ||||||
|  | 
 | ||||||
|  | static std::string uriScheme = "legacy-ssh://"; | ||||||
|  | 
 | ||||||
|  | struct LegacySSHStore : public Store | ||||||
|  | { | ||||||
|  |     string host; | ||||||
|  | 
 | ||||||
|  |     struct Connection | ||||||
|  |     { | ||||||
|  |         Pid sshPid; | ||||||
|  |         AutoCloseFD out; | ||||||
|  |         AutoCloseFD in; | ||||||
|  |         FdSink to; | ||||||
|  |         FdSource from; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     AutoDelete tmpDir; | ||||||
|  | 
 | ||||||
|  |     Path socketPath; | ||||||
|  | 
 | ||||||
|  |     Pid sshMaster; | ||||||
|  | 
 | ||||||
|  |     ref<Pool<Connection>> connections; | ||||||
|  | 
 | ||||||
|  |     Path key; | ||||||
|  | 
 | ||||||
|  |     LegacySSHStore(const string & host, const Params & params, | ||||||
|  |         size_t maxConnections = std::numeric_limits<size_t>::max()) | ||||||
|  |         : Store(params) | ||||||
|  |         , host(host) | ||||||
|  |         , tmpDir(createTempDir("", "nix", true, true, 0700)) | ||||||
|  |         , socketPath((Path) tmpDir + "/ssh.sock") | ||||||
|  |         , connections(make_ref<Pool<Connection>>( | ||||||
|  |             maxConnections, | ||||||
|  |             [this]() { return openConnection(); }, | ||||||
|  |             [](const ref<Connection> & r) { return true; } | ||||||
|  |             )) | ||||||
|  |         , key(get(params, "ssh-key", "")) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ref<Connection> openConnection() | ||||||
|  |     { | ||||||
|  |         if ((pid_t) sshMaster == -1) { | ||||||
|  |             sshMaster = startProcess([&]() { | ||||||
|  |                 restoreSignals(); | ||||||
|  |                 Strings args{ "ssh", "-M", "-S", socketPath, "-N", "-x", "-a", host }; | ||||||
|  |                 if (!key.empty()) | ||||||
|  |                     args.insert(args.end(), {"-i", key}); | ||||||
|  |                 execvp("ssh", stringsToCharPtrs(args).data()); | ||||||
|  |                 throw SysError("starting SSH master connection to host ‘%s’", host); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         auto conn = make_ref<Connection>(); | ||||||
|  |         Pipe in, out; | ||||||
|  |         in.create(); | ||||||
|  |         out.create(); | ||||||
|  |         conn->sshPid = startProcess([&]() { | ||||||
|  |             if (dup2(in.readSide.get(), STDIN_FILENO) == -1) | ||||||
|  |                 throw SysError("duping over STDIN"); | ||||||
|  |             if (dup2(out.writeSide.get(), STDOUT_FILENO) == -1) | ||||||
|  |                 throw SysError("duping over STDOUT"); | ||||||
|  |             execlp("ssh", "ssh", "-S", socketPath.c_str(), host.c_str(), "nix-store", "--serve", "--write", nullptr); | ||||||
|  |             throw SysError("executing ‘nix-store --serve’ on remote host ‘%s’", host); | ||||||
|  |         }); | ||||||
|  |         in.readSide = -1; | ||||||
|  |         out.writeSide = -1; | ||||||
|  |         conn->out = std::move(out.readSide); | ||||||
|  |         conn->in = std::move(in.writeSide); | ||||||
|  |         conn->to = FdSink(conn->in.get()); | ||||||
|  |         conn->from = FdSource(conn->out.get()); | ||||||
|  | 
 | ||||||
|  |         int remoteVersion; | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             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); | ||||||
|  |             remoteVersion = readInt(conn->from); | ||||||
|  |             if (GET_PROTOCOL_MAJOR(remoteVersion) != 0x200) | ||||||
|  |                 throw Error("unsupported ‘nix-store --serve’ protocol version on ‘%s’", host); | ||||||
|  | 
 | ||||||
|  |         } catch (EndOfFile & e) { | ||||||
|  |             throw Error("cannot connect to ‘%1%’", host); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return conn; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     string getUri() override | ||||||
|  |     { | ||||||
|  |         return uriScheme + host; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void queryPathInfoUncached(const Path & path, | ||||||
|  |         std::function<void(std::shared_ptr<ValidPathInfo>)> success, | ||||||
|  |         std::function<void(std::exception_ptr exc)> failure) override | ||||||
|  |     { | ||||||
|  |         sync2async<std::shared_ptr<ValidPathInfo>>(success, failure, [&]() -> std::shared_ptr<ValidPathInfo> { | ||||||
|  |             auto conn(connections->get()); | ||||||
|  | 
 | ||||||
|  |             debug("querying remote host ‘%s’ for info on ‘%s’", host, path); | ||||||
|  | 
 | ||||||
|  |             conn->to << cmdQueryPathInfos << PathSet{path}; | ||||||
|  |             conn->to.flush(); | ||||||
|  | 
 | ||||||
|  |             auto info = std::make_shared<ValidPathInfo>(); | ||||||
|  |             conn->from >> info->path; | ||||||
|  |             if (info->path.empty()) return nullptr; | ||||||
|  |             assert(path == info->path); | ||||||
|  | 
 | ||||||
|  |             PathSet references; | ||||||
|  |             conn->from >> info->deriver; | ||||||
|  |             info->references = readStorePaths<PathSet>(*this, conn->from); | ||||||
|  |             readLongLong(conn->from); // download size
 | ||||||
|  |             info->narSize = readLongLong(conn->from); | ||||||
|  | 
 | ||||||
|  |             auto s = readString(conn->from); | ||||||
|  |             assert(s == ""); | ||||||
|  | 
 | ||||||
|  |             return info; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void addToStore(const ValidPathInfo & info, const ref<std::string> & nar, | ||||||
|  |         bool repair, bool dontCheckSigs, | ||||||
|  |         std::shared_ptr<FSAccessor> accessor) override | ||||||
|  |     { | ||||||
|  |         debug("adding path ‘%s’ to remote host ‘%s’", info.path, host); | ||||||
|  | 
 | ||||||
|  |         auto conn(connections->get()); | ||||||
|  | 
 | ||||||
|  |         conn->to | ||||||
|  |             << cmdImportPaths | ||||||
|  |             << 1; | ||||||
|  |         conn->to(*nar); | ||||||
|  |         conn->to | ||||||
|  |             << exportMagic | ||||||
|  |             << info.path | ||||||
|  |             << info.references | ||||||
|  |             << info.deriver | ||||||
|  |             << 0 | ||||||
|  |             << 0; | ||||||
|  |         conn->to.flush(); | ||||||
|  | 
 | ||||||
|  |         if (readInt(conn->from) != 1) | ||||||
|  |             throw Error("failed to add path ‘%s’ to remote host ‘%s’, info.path, host"); | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void narFromPath(const Path & path, Sink & sink) override | ||||||
|  |     { | ||||||
|  |         auto conn(connections->get()); | ||||||
|  | 
 | ||||||
|  |         conn->to << cmdDumpStorePath << path; | ||||||
|  |         conn->to.flush(); | ||||||
|  | 
 | ||||||
|  |         /* FIXME: inefficient. */ | ||||||
|  |         ParseSink parseSink; /* null sink; just parse the NAR */ | ||||||
|  |         SavingSourceAdapter savedNAR(conn->from); | ||||||
|  |         parseDump(parseSink, savedNAR); | ||||||
|  |         sink(savedNAR.s); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Unsupported methods. */ | ||||||
|  |     [[noreturn]] void unsupported() | ||||||
|  |     { | ||||||
|  |         throw Error("operation not supported on SSH stores"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     PathSet queryAllValidPaths() override { unsupported(); } | ||||||
|  | 
 | ||||||
|  |     void queryReferrers(const Path & path, PathSet & referrers) override | ||||||
|  |     { unsupported(); } | ||||||
|  | 
 | ||||||
|  |     PathSet queryDerivationOutputs(const Path & path) override | ||||||
|  |     { unsupported(); } | ||||||
|  | 
 | ||||||
|  |     StringSet queryDerivationOutputNames(const Path & path) override | ||||||
|  |     { unsupported(); } | ||||||
|  | 
 | ||||||
|  |     Path queryPathFromHashPart(const string & hashPart) override | ||||||
|  |     { unsupported(); } | ||||||
|  | 
 | ||||||
|  |     Path addToStore(const string & name, const Path & srcPath, | ||||||
|  |         bool recursive, HashType hashAlgo, | ||||||
|  |         PathFilter & filter, bool repair) override | ||||||
|  |     { unsupported(); } | ||||||
|  | 
 | ||||||
|  |     Path addTextToStore(const string & name, const string & s, | ||||||
|  |         const PathSet & references, bool repair) override | ||||||
|  |     { unsupported(); } | ||||||
|  | 
 | ||||||
|  |     void buildPaths(const PathSet & paths, BuildMode buildMode) override | ||||||
|  |     { unsupported(); } | ||||||
|  | 
 | ||||||
|  |     BuildResult buildDerivation(const Path & drvPath, const BasicDerivation & drv, | ||||||
|  |         BuildMode buildMode) override | ||||||
|  |     { unsupported(); } | ||||||
|  | 
 | ||||||
|  |     void ensurePath(const Path & path) override | ||||||
|  |     { unsupported(); } | ||||||
|  | 
 | ||||||
|  |     void addTempRoot(const Path & path) override | ||||||
|  |     { unsupported(); } | ||||||
|  | 
 | ||||||
|  |     void addIndirectRoot(const Path & path) override | ||||||
|  |     { unsupported(); } | ||||||
|  | 
 | ||||||
|  |     Roots findRoots() override | ||||||
|  |     { unsupported(); } | ||||||
|  | 
 | ||||||
|  |     void collectGarbage(const GCOptions & options, GCResults & results) override | ||||||
|  |     { unsupported(); } | ||||||
|  | 
 | ||||||
|  |     ref<FSAccessor> getFSAccessor() | ||||||
|  |     { unsupported(); } | ||||||
|  | 
 | ||||||
|  |     void addSignatures(const Path & storePath, const StringSet & sigs) override | ||||||
|  |     { unsupported(); } | ||||||
|  | 
 | ||||||
|  |     bool isTrusted() override | ||||||
|  |     { return true; } | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static RegisterStoreImplementation regStore([]( | ||||||
|  |     const std::string & uri, const Store::Params & params) | ||||||
|  |     -> std::shared_ptr<Store> | ||||||
|  | { | ||||||
|  |     if (std::string(uri, 0, uriScheme.size()) != uriScheme) return 0; | ||||||
|  |     return std::make_shared<LegacySSHStore>(std::string(uri, uriScheme.size()), params); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -37,6 +37,7 @@ template<class T> T readStorePaths(Store & store, Source & from) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template PathSet readStorePaths(Store & store, Source & from); | template PathSet readStorePaths(Store & store, Source & from); | ||||||
|  | template Paths readStorePaths(Store & store, Source & from); | ||||||
| 
 | 
 | ||||||
| /* TODO: Separate these store impls into different files, give them better names */ | /* TODO: Separate these store impls into different files, give them better names */ | ||||||
| RemoteStore::RemoteStore(const Params & params, size_t maxConnections) | RemoteStore::RemoteStore(const Params & params, size_t maxConnections) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue